1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). |
4 | ** Contact: http://www.qt-project.org/legal |
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 Digia. For licensing terms and |
14 | ** conditions see http://qt.digia.com/licensing. For further information |
15 | ** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 2.1 requirements |
23 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
24 | ** |
25 | ** In addition, as a special exception, Digia gives you certain additional |
26 | ** rights. These rights are described in the Digia Qt LGPL Exception |
27 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
28 | ** |
29 | ** GNU General Public License Usage |
30 | ** Alternatively, this file may be used under the terms of the GNU |
31 | ** General Public License version 3.0 as published by the Free Software |
32 | ** Foundation and appearing in the file LICENSE.GPL included in the |
33 | ** packaging of this file. Please review the following information to |
34 | ** ensure the GNU General Public License version 3.0 requirements will be |
35 | ** met: http://www.gnu.org/copyleft/gpl.html. |
36 | ** |
37 | ** |
38 | ** $QT_END_LICENSE$ |
39 | ** |
40 | ****************************************************************************/ |
41 | |
42 | #include "QtTest/qtestassert.h" |
43 | |
44 | #include "QtTest/private/qtestlog_p.h" |
45 | #include "QtTest/private/qtestresult_p.h" |
46 | #include "QtTest/private/qabstracttestlogger_p.h" |
47 | #include "QtTest/private/qplaintestlogger_p.h" |
48 | #include "QtTest/private/qxmltestlogger_p.h" |
49 | #include <QtCore/qatomic.h> |
50 | #include <QtCore/qbytearray.h> |
51 | |
52 | #include <stdlib.h> |
53 | #include <string.h> |
54 | #include <limits.h> |
55 | |
56 | |
57 | #include "qtestlogger_p.h" |
58 | |
59 | QT_BEGIN_NAMESPACE |
60 | |
61 | namespace QTest { |
62 | |
63 | struct IgnoreResultList |
64 | { |
65 | inline IgnoreResultList(QtMsgType tp, const char *message) |
66 | : type(tp), next(0) |
67 | { msg = qstrdup(message); } |
68 | inline ~IgnoreResultList() |
69 | { delete [] msg; } |
70 | |
71 | static inline void clearList(IgnoreResultList *&list) |
72 | { |
73 | while (list) { |
74 | IgnoreResultList *current = list; |
75 | list = list->next; |
76 | delete current; |
77 | } |
78 | } |
79 | |
80 | QtMsgType type; |
81 | char *msg; |
82 | IgnoreResultList *next; |
83 | }; |
84 | |
85 | static IgnoreResultList *ignoreResultList = 0; |
86 | |
87 | static QTestLog::LogMode logMode = QTestLog::Plain; |
88 | static QTestLog::FlushMode flushMode = QTestLog::NoFlush; |
89 | static int verbosity = 0; |
90 | static int maxWarnings = 2002; |
91 | |
92 | static QAbstractTestLogger *testLogger = 0; |
93 | static const char *outFile = 0; |
94 | |
95 | static QtMsgHandler oldMessageHandler; |
96 | |
97 | static bool handleIgnoredMessage(QtMsgType type, const char *msg) |
98 | { |
99 | IgnoreResultList *last = 0; |
100 | IgnoreResultList *list = ignoreResultList; |
101 | while (list) { |
102 | if (list->type == type && strcmp(msg, list->msg) == 0) { |
103 | // remove the item from the list |
104 | if (last) |
105 | last->next = list->next; |
106 | else if (list->next) |
107 | ignoreResultList = list->next; |
108 | else |
109 | ignoreResultList = 0; |
110 | |
111 | delete list; |
112 | return true; |
113 | } |
114 | |
115 | last = list; |
116 | list = list->next; |
117 | } |
118 | return false; |
119 | } |
120 | |
121 | static void messageHandler(QtMsgType type, const char *msg) |
122 | { |
123 | static QBasicAtomicInt counter = Q_BASIC_ATOMIC_INITIALIZER(QTest::maxWarnings); |
124 | |
125 | if (!msg || !QTest::testLogger) { |
126 | // if this goes wrong, something is seriously broken. |
127 | qInstallMsgHandler(oldMessageHandler); |
128 | QTEST_ASSERT(msg); |
129 | QTEST_ASSERT(QTest::testLogger); |
130 | } |
131 | |
132 | if (handleIgnoredMessage(type, msg)) |
133 | // the message is expected, so just swallow it. |
134 | return; |
135 | |
136 | if (type != QtFatalMsg) { |
137 | if (counter <= 0) |
138 | return; |
139 | |
140 | if (!counter.deref()) { |
141 | QTest::testLogger->addMessage(QAbstractTestLogger::QSystem, |
142 | "Maximum amount of warnings exceeded. Use -maxwarnings to override." ); |
143 | return; |
144 | } |
145 | } |
146 | |
147 | switch (type) { |
148 | case QtDebugMsg: |
149 | QTest::testLogger->addMessage(QAbstractTestLogger::QDebug, msg); |
150 | break; |
151 | case QtCriticalMsg: |
152 | QTest::testLogger->addMessage(QAbstractTestLogger::QSystem, msg); |
153 | break; |
154 | case QtWarningMsg: |
155 | QTest::testLogger->addMessage(QAbstractTestLogger::QWarning, msg); |
156 | break; |
157 | case QtFatalMsg: |
158 | QTest::testLogger->addMessage(QAbstractTestLogger::QFatal, msg); |
159 | /* Right now, we're inside the custom message handler and we're |
160 | * being qt_message_output in qglobal.cpp. After we return from |
161 | * this function, it will proceed with calling exit() and abort() |
162 | * and hence crash. Therefore, we call these logging functions such |
163 | * that we wrap up nicely, and in particular produce well-formed XML. */ |
164 | QTestResult::addFailure("Received a fatal error." , "Unknown file" , 0); |
165 | QTestLog::leaveTestFunction(); |
166 | QTestLog::stopLogging(); |
167 | break; |
168 | } |
169 | } |
170 | |
171 | void initLogger() |
172 | { |
173 | switch (QTest::logMode) { |
174 | case QTestLog::Plain: |
175 | QTest::testLogger = new QPlainTestLogger; |
176 | break; |
177 | case QTestLog::XML:{ |
178 | if(QTest::flushMode == QTestLog::FLushOn) |
179 | QTest::testLogger = new QXmlTestLogger(QXmlTestLogger::Complete); |
180 | else |
181 | QTest::testLogger = new QTestLogger(QTestLogger::TLF_XML); |
182 | break; |
183 | }case QTestLog::LightXML:{ |
184 | if(QTest::flushMode == QTestLog::FLushOn) |
185 | QTest::testLogger = new QXmlTestLogger(QXmlTestLogger::Light); |
186 | else |
187 | QTest::testLogger = new QTestLogger(QTestLogger::TLF_LightXml); |
188 | break; |
189 | }case QTestLog::XunitXML: |
190 | QTest::testLogger = new QTestLogger(QTestLogger::TLF_XunitXml); |
191 | } |
192 | } |
193 | |
194 | extern Q_TESTLIB_EXPORT bool printAvailableTags; |
195 | |
196 | } |
197 | |
198 | QTestLog::QTestLog() |
199 | { |
200 | } |
201 | |
202 | QTestLog::~QTestLog() |
203 | { |
204 | } |
205 | |
206 | void QTestLog::enterTestFunction(const char* function) |
207 | { |
208 | if (QTest::printAvailableTags) |
209 | return; |
210 | |
211 | QTEST_ASSERT(QTest::testLogger); |
212 | QTEST_ASSERT(function); |
213 | |
214 | QTest::testLogger->enterTestFunction(function); |
215 | } |
216 | |
217 | int QTestLog::unhandledIgnoreMessages() |
218 | { |
219 | int i = 0; |
220 | QTest::IgnoreResultList *list = QTest::ignoreResultList; |
221 | while (list) { |
222 | ++i; |
223 | list = list->next; |
224 | } |
225 | return i; |
226 | } |
227 | |
228 | void QTestLog::leaveTestFunction() |
229 | { |
230 | if (QTest::printAvailableTags) |
231 | return; |
232 | |
233 | QTEST_ASSERT(QTest::testLogger); |
234 | |
235 | QTest::IgnoreResultList::clearList(QTest::ignoreResultList); |
236 | QTest::testLogger->leaveTestFunction(); |
237 | } |
238 | |
239 | void QTestLog::printUnhandledIgnoreMessages() |
240 | { |
241 | QTEST_ASSERT(QTest::testLogger); |
242 | |
243 | char msg[1024]; |
244 | QTest::IgnoreResultList *list = QTest::ignoreResultList; |
245 | while (list) { |
246 | QTest::qt_snprintf(msg, 1024, "Did not receive message: \"%s\"" , list->msg); |
247 | QTest::testLogger->addMessage(QAbstractTestLogger::Info, msg); |
248 | |
249 | list = list->next; |
250 | } |
251 | } |
252 | |
253 | void QTestLog::addPass(const char *msg) |
254 | { |
255 | if (QTest::printAvailableTags) |
256 | return; |
257 | |
258 | QTEST_ASSERT(QTest::testLogger); |
259 | QTEST_ASSERT(msg); |
260 | |
261 | QTest::testLogger->addIncident(QAbstractTestLogger::Pass, msg); |
262 | } |
263 | |
264 | void QTestLog::addFail(const char *msg, const char *file, int line) |
265 | { |
266 | QTEST_ASSERT(QTest::testLogger); |
267 | |
268 | QTest::testLogger->addIncident(QAbstractTestLogger::Fail, msg, file, line); |
269 | } |
270 | |
271 | void QTestLog::addXFail(const char *msg, const char *file, int line) |
272 | { |
273 | QTEST_ASSERT(QTest::testLogger); |
274 | QTEST_ASSERT(msg); |
275 | QTEST_ASSERT(file); |
276 | |
277 | QTest::testLogger->addIncident(QAbstractTestLogger::XFail, msg, file, line); |
278 | } |
279 | |
280 | void QTestLog::addXPass(const char *msg, const char *file, int line) |
281 | { |
282 | QTEST_ASSERT(QTest::testLogger); |
283 | QTEST_ASSERT(msg); |
284 | QTEST_ASSERT(file); |
285 | |
286 | QTest::testLogger->addIncident(QAbstractTestLogger::XPass, msg, file, line); |
287 | } |
288 | |
289 | void QTestLog::addSkip(const char *msg, QTest::SkipMode /*mode*/, |
290 | const char *file, int line) |
291 | { |
292 | QTEST_ASSERT(QTest::testLogger); |
293 | QTEST_ASSERT(msg); |
294 | QTEST_ASSERT(file); |
295 | |
296 | QTest::testLogger->addMessage(QAbstractTestLogger::Skip, msg, file, line); |
297 | } |
298 | |
299 | void QTestLog::addBenchmarkResult(const QBenchmarkResult &result) |
300 | { |
301 | QTEST_ASSERT(QTest::testLogger); |
302 | QTest::testLogger->addBenchmarkResult(result); |
303 | } |
304 | |
305 | void QTestLog::startLogging(unsigned int randomSeed) |
306 | { |
307 | QTEST_ASSERT(!QTest::testLogger); |
308 | QTest::initLogger(); |
309 | QTest::testLogger->registerRandomSeed(randomSeed); |
310 | QTest::testLogger->startLogging(); |
311 | QTest::oldMessageHandler = qInstallMsgHandler(QTest::messageHandler); |
312 | } |
313 | |
314 | void QTestLog::startLogging() |
315 | { |
316 | QTEST_ASSERT(!QTest::testLogger); |
317 | QTest::initLogger(); |
318 | QTest::testLogger->startLogging(); |
319 | QTest::oldMessageHandler = qInstallMsgHandler(QTest::messageHandler); |
320 | } |
321 | |
322 | void QTestLog::stopLogging() |
323 | { |
324 | qInstallMsgHandler(QTest::oldMessageHandler); |
325 | |
326 | QTEST_ASSERT(QTest::testLogger); |
327 | QTest::testLogger->stopLogging(); |
328 | delete QTest::testLogger; |
329 | QTest::testLogger = 0; |
330 | } |
331 | |
332 | void QTestLog::warn(const char *msg) |
333 | { |
334 | QTEST_ASSERT(QTest::testLogger); |
335 | QTEST_ASSERT(msg); |
336 | |
337 | QTest::testLogger->addMessage(QAbstractTestLogger::Warn, msg); |
338 | } |
339 | |
340 | void QTestLog::info(const char *msg, const char *file, int line) |
341 | { |
342 | QTEST_ASSERT(msg); |
343 | |
344 | if (QTest::testLogger) |
345 | QTest::testLogger->addMessage(QAbstractTestLogger::Info, msg, file, line); |
346 | } |
347 | |
348 | void QTestLog::setLogMode(LogMode mode) |
349 | { |
350 | QTest::logMode = mode; |
351 | } |
352 | |
353 | QTestLog::LogMode QTestLog::logMode() |
354 | { |
355 | return QTest::logMode; |
356 | } |
357 | |
358 | void QTestLog::setVerboseLevel(int level) |
359 | { |
360 | QTest::verbosity = level; |
361 | } |
362 | |
363 | int QTestLog::verboseLevel() |
364 | { |
365 | return QTest::verbosity; |
366 | } |
367 | |
368 | void QTestLog::addIgnoreMessage(QtMsgType type, const char *msg) |
369 | { |
370 | QTest::IgnoreResultList *item = new QTest::IgnoreResultList(type, msg); |
371 | |
372 | QTest::IgnoreResultList *list = QTest::ignoreResultList; |
373 | if (!list) { |
374 | QTest::ignoreResultList = item; |
375 | return; |
376 | } |
377 | while (list->next) |
378 | list = list->next; |
379 | list->next = item; |
380 | } |
381 | |
382 | void QTestLog::redirectOutput(const char *fileName) |
383 | { |
384 | QTEST_ASSERT(fileName); |
385 | |
386 | QTest::outFile = fileName; |
387 | } |
388 | |
389 | const char *QTestLog::outputFileName() |
390 | { |
391 | return QTest::outFile; |
392 | } |
393 | |
394 | void QTestLog::setMaxWarnings(int m) |
395 | { |
396 | QTest::maxWarnings = m <= 0 ? INT_MAX : m + 2; |
397 | } |
398 | |
399 | void QTestLog::setFlushMode(FlushMode mode) |
400 | { |
401 | QTest::flushMode = mode; |
402 | } |
403 | |
404 | QT_END_NAMESPACE |
405 | |