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