1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtTest module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include <QtTest/private/qabstracttestlogger_p.h>
41 #include <QtTest/qtestassert.h>
42 #include <qtestresult_p.h>
43 
44 #include <QtCore/qbytearray.h>
45 #include <QtCore/qstring.h>
46 
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <stdarg.h>
50 
51 #ifndef Q_OS_WIN
52 #include <unistd.h>
53 #endif
54 
55 #ifdef Q_OS_ANDROID
56 #include <sys/stat.h>
57 #endif
58 
59 QT_BEGIN_NAMESPACE
60 
QAbstractTestLogger(const char * filename)61 QAbstractTestLogger::QAbstractTestLogger(const char *filename)
62 {
63     if (!filename) {
64         stream = stdout;
65         return;
66     }
67 #if defined(_MSC_VER)
68     if (::fopen_s(&stream, filename, "wt")) {
69 #else
70     stream = ::fopen(filename, "wt");
71     if (!stream) {
72 #endif
73         fprintf(stderr, "Unable to open file for logging: %s\n", filename);
74         ::exit(1);
75     }
76 #ifdef Q_OS_ANDROID
77     else {
78         // Make sure output is world-readable on Android
79         ::chmod(filename, 0666);
80     }
81 #endif
82 }
83 
84 QAbstractTestLogger::~QAbstractTestLogger()
85 {
86     QTEST_ASSERT(stream);
87     if (stream != stdout) {
88         fclose(stream);
89     }
90     stream = nullptr;
91 }
92 
93 bool QAbstractTestLogger::isLoggingToStdout() const
94 {
95     return stream == stdout;
96 }
97 
98 void QAbstractTestLogger::filterUnprintable(char *str) const
99 {
100     unsigned char *idx = reinterpret_cast<unsigned char *>(str);
101     while (*idx) {
102         if (((*idx < 0x20 && *idx != '\n' && *idx != '\t') || *idx == 0x7f))
103             *idx = '?';
104         ++idx;
105     }
106 }
107 
108 void QAbstractTestLogger::outputString(const char *msg)
109 {
110     QTEST_ASSERT(stream);
111     QTEST_ASSERT(msg);
112 
113     char *filtered = new char[strlen(msg) + 1];
114     strcpy(filtered, msg);
115     filterUnprintable(filtered);
116 
117     ::fputs(filtered, stream);
118     ::fflush(stream);
119 
120     delete [] filtered;
121 }
122 
123 void QAbstractTestLogger::startLogging()
124 {
125 }
126 
127 void QAbstractTestLogger::stopLogging()
128 {
129 }
130 
131 void QAbstractTestLogger::addMessage(QtMsgType type, const QMessageLogContext &context,
132                                      const QString &message)
133 {
134     QAbstractTestLogger::MessageTypes messageType = [=]() {
135         switch (type) {
136         case QtDebugMsg: return QAbstractTestLogger::QDebug;
137         case QtInfoMsg: return QAbstractTestLogger::QInfo;
138         case QtCriticalMsg: return QAbstractTestLogger::QSystem;
139         case QtWarningMsg: return QAbstractTestLogger::QWarning;
140         case QtFatalMsg: return QAbstractTestLogger::QFatal;
141         }
142         Q_UNREACHABLE();
143         return QAbstractTestLogger::QFatal;
144     }();
145 
146     QString formattedMessage = qFormatLogMessage(type, context, message);
147 
148     // Note that we explicitly ignore the file and line of the context here,
149     // as that's what QTest::messageHandler used to do when calling the same
150     // overload directly.
151     addMessage(messageType, formattedMessage);
152 }
153 
154 namespace QTest
155 {
156 
157 extern void filter_unprintable(char *str);
158 
159 /*!
160     \fn int QTest::qt_asprintf(QTestCharBuffer *buf, const char *format, ...);
161     \internal
162  */
163 int qt_asprintf(QTestCharBuffer *str, const char *format, ...)
164 {
165     static const int MAXSIZE = 1024*1024*2;
166 
167     Q_ASSERT(str);
168 
169     int size = str->size();
170 
171     va_list ap;
172     int res = 0;
173 
174     for (;;) {
175         va_start(ap, format);
176         res = qvsnprintf(str->data(), size, format, ap);
177         va_end(ap);
178         str->data()[size - 1] = '\0';
179         if (res >= 0 && res < size) {
180             // We succeeded
181             break;
182         }
183         // buffer wasn't big enough, try again.
184         // Note, we're assuming that a result of -1 is always due to running out of space.
185         size *= 2;
186         if (size > MAXSIZE) {
187             break;
188         }
189         if (!str->reset(size))
190             break; // out of memory - take what we have
191     }
192 
193     return res;
194 }
195 
196 }
197 
198 namespace QTestPrivate
199 {
200 
201 void generateTestIdentifier(QTestCharBuffer *identifier, int parts)
202 {
203     const char *testObject = parts & TestObject ? QTestResult::currentTestObjectName() : "";
204     const char *testFunction = parts & TestFunction ? (QTestResult::currentTestFunction() ? QTestResult::currentTestFunction() : "UnknownTestFunc") : "";
205     const char *objectFunctionFiller = parts & TestObject && parts & (TestFunction | TestDataTag) ? "::" : "";
206     const char *testFuctionStart = parts & TestFunction ? "(" : "";
207     const char *testFuctionEnd = parts & TestFunction ? ")" : "";
208 
209     const char *dataTag = (parts & TestDataTag) && QTestResult::currentDataTag() ? QTestResult::currentDataTag() : "";
210     const char *globalDataTag = (parts & TestDataTag) && QTestResult::currentGlobalDataTag() ? QTestResult::currentGlobalDataTag() : "";
211     const char *tagFiller = (dataTag[0] && globalDataTag[0]) ? ":" : "";
212 
213     QTest::qt_asprintf(identifier, "%s%s%s%s%s%s%s%s",
214         testObject, objectFunctionFiller, testFunction, testFuctionStart,
215         globalDataTag, tagFiller, dataTag, testFuctionEnd);
216 }
217 
218 }
219 
220 QT_END_NAMESPACE
221