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 "qtestlogger_p.h" |
43 | #include "qtestelement.h" |
44 | #include "qtestxunitstreamer.h" |
45 | #include "qtestxmlstreamer.h" |
46 | #include "qtestlightxmlstreamer.h" |
47 | #include "qtestfilelogger.h" |
48 | |
49 | #include "QtTest/qtestcase.h" |
50 | #include "QtTest/private/qtestresult_p.h" |
51 | #include "QtTest/private/qbenchmark_p.h" |
52 | |
53 | #include <string.h> |
54 | |
55 | QT_BEGIN_NAMESPACE |
56 | |
57 | QTestLogger::QTestLogger(int fm) |
58 | :listOfTestcases(0), currentLogElement(0), errorLogElement(0), |
59 | logFormatter(0), format( (TestLoggerFormat)fm ), filelogger(new QTestFileLogger), |
60 | testCounter(0), passCounter(0), |
61 | failureCounter(0), errorCounter(0), |
62 | warningCounter(0), skipCounter(0), |
63 | systemCounter(0), qdebugCounter(0), |
64 | qwarnCounter(0), qfatalCounter(0), |
65 | infoCounter(0), randomSeed_(0), |
66 | hasRandomSeed_(false) |
67 | { |
68 | } |
69 | |
70 | QTestLogger::~QTestLogger() |
71 | { |
72 | if(format == TLF_XunitXml) |
73 | delete currentLogElement; |
74 | else |
75 | delete listOfTestcases; |
76 | |
77 | delete logFormatter; |
78 | delete filelogger; |
79 | } |
80 | |
81 | void QTestLogger::startLogging() |
82 | { |
83 | switch(format){ |
84 | case TLF_LightXml:{ |
85 | logFormatter = new QTestLightXmlStreamer; |
86 | filelogger->init(); |
87 | break; |
88 | }case TLF_XML:{ |
89 | logFormatter = new QTestXmlStreamer; |
90 | filelogger->init(); |
91 | break; |
92 | }case TLF_XunitXml:{ |
93 | logFormatter = new QTestXunitStreamer; |
94 | delete errorLogElement; |
95 | errorLogElement = new QTestElement(QTest::LET_SystemError); |
96 | filelogger->init(); |
97 | break; |
98 | } |
99 | } |
100 | |
101 | logFormatter->setLogger(this); |
102 | logFormatter->startStreaming(); |
103 | } |
104 | |
105 | void QTestLogger::stopLogging() |
106 | { |
107 | QTestElement *iterator = listOfTestcases; |
108 | |
109 | if(format == TLF_XunitXml ){ |
110 | char buf[10]; |
111 | |
112 | currentLogElement = new QTestElement(QTest::LET_TestSuite); |
113 | currentLogElement->addAttribute(QTest::AI_Name, QTestResult::currentTestObjectName()); |
114 | |
115 | QTest::qt_snprintf(buf, sizeof(buf), "%i" , testCounter); |
116 | currentLogElement->addAttribute(QTest::AI_Tests, buf); |
117 | |
118 | QTest::qt_snprintf(buf, sizeof(buf), "%i" , failureCounter); |
119 | currentLogElement->addAttribute(QTest::AI_Failures, buf); |
120 | |
121 | QTest::qt_snprintf(buf, sizeof(buf), "%i" , errorCounter); |
122 | currentLogElement->addAttribute(QTest::AI_Errors, buf); |
123 | |
124 | QTestElement *property; |
125 | QTestElement *properties = new QTestElement(QTest::LET_Properties); |
126 | |
127 | property = new QTestElement(QTest::LET_Property); |
128 | property->addAttribute(QTest::AI_Name, "QTestVersion" ); |
129 | property->addAttribute(QTest::AI_PropertyValue, QTEST_VERSION_STR); |
130 | properties->addLogElement(property); |
131 | |
132 | property = new QTestElement(QTest::LET_Property); |
133 | property->addAttribute(QTest::AI_Name, "QtVersion" ); |
134 | property->addAttribute(QTest::AI_PropertyValue, qVersion()); |
135 | properties->addLogElement(property); |
136 | |
137 | if (hasRandomSeed()) { |
138 | property = new QTestElement(QTest::LET_Property); |
139 | property->addAttribute(QTest::AI_Name, "RandomSeed" ); |
140 | QTest::qt_snprintf(buf, sizeof(buf), "%i" , randomSeed()); |
141 | property->addAttribute(QTest::AI_PropertyValue, buf); |
142 | properties->addLogElement(property); |
143 | } |
144 | |
145 | currentLogElement->addLogElement(properties); |
146 | |
147 | currentLogElement->addLogElement(iterator); |
148 | |
149 | /* For correct indenting, make sure every testcase knows its parent */ |
150 | QTestElement* testcase = iterator; |
151 | while (testcase) { |
152 | testcase->setParent(currentLogElement); |
153 | testcase = testcase->nextElement(); |
154 | } |
155 | |
156 | currentLogElement->addLogElement(errorLogElement); |
157 | |
158 | QTestElement *it = currentLogElement; |
159 | logFormatter->output(it); |
160 | }else{ |
161 | logFormatter->output(iterator); |
162 | } |
163 | |
164 | logFormatter->stopStreaming(); |
165 | } |
166 | |
167 | void QTestLogger::enterTestFunction(const char *function) |
168 | { |
169 | char buf[1024]; |
170 | QTest::qt_snprintf(buf, sizeof(buf), "Entered test-function: %s\n" , function); |
171 | filelogger->flush(buf); |
172 | |
173 | currentLogElement = new QTestElement(QTest::LET_TestCase); |
174 | currentLogElement->addAttribute(QTest::AI_Name, function); |
175 | currentLogElement->addToList(&listOfTestcases); |
176 | |
177 | ++testCounter; |
178 | } |
179 | |
180 | void QTestLogger::leaveTestFunction() |
181 | { |
182 | } |
183 | |
184 | void QTestLogger::addIncident(IncidentTypes type, const char *description, |
185 | const char *file, int line) |
186 | { |
187 | const char *typeBuf = 0; |
188 | char buf[100]; |
189 | |
190 | switch (type) { |
191 | case QAbstractTestLogger::XPass: |
192 | ++failureCounter; |
193 | typeBuf = "xpass" ; |
194 | break; |
195 | case QAbstractTestLogger::Pass: |
196 | ++passCounter; |
197 | typeBuf = "pass" ; |
198 | break; |
199 | case QAbstractTestLogger::XFail: |
200 | ++passCounter; |
201 | typeBuf = "xfail" ; |
202 | break; |
203 | case QAbstractTestLogger::Fail: |
204 | ++failureCounter; |
205 | typeBuf = "fail" ; |
206 | break; |
207 | default: |
208 | typeBuf = "??????" ; |
209 | break; |
210 | } |
211 | |
212 | if (type == QAbstractTestLogger::Fail || type == QAbstractTestLogger::XPass |
213 | || ((format != TLF_XunitXml) && (type == QAbstractTestLogger::XFail))) { |
214 | QTestElement *failureElement = new QTestElement(QTest::LET_Failure); |
215 | failureElement->addAttribute(QTest::AI_Result, typeBuf); |
216 | if(file) |
217 | failureElement->addAttribute(QTest::AI_File, file); |
218 | else |
219 | failureElement->addAttribute(QTest::AI_File, "" ); |
220 | QTest::qt_snprintf(buf, sizeof(buf), "%i" , line); |
221 | failureElement->addAttribute(QTest::AI_Line, buf); |
222 | failureElement->addAttribute(QTest::AI_Description, description); |
223 | addTag(failureElement); |
224 | currentLogElement->addLogElement(failureElement); |
225 | } |
226 | |
227 | /* |
228 | Only one result can be shown for the whole testfunction. |
229 | Check if we currently have a result, and if so, overwrite it |
230 | iff the new result is worse. |
231 | */ |
232 | QTestElementAttribute* resultAttr = |
233 | const_cast<QTestElementAttribute*>(currentLogElement->attribute(QTest::AI_Result)); |
234 | if (resultAttr) { |
235 | const char* oldResult = resultAttr->value(); |
236 | bool overwrite = false; |
237 | if (!strcmp(oldResult, "pass" )) { |
238 | overwrite = true; |
239 | } |
240 | else if (!strcmp(oldResult, "xfail" )) { |
241 | overwrite = (type == QAbstractTestLogger::XPass || type == QAbstractTestLogger::Fail); |
242 | } |
243 | else if (!strcmp(oldResult, "xpass" )) { |
244 | overwrite = (type == QAbstractTestLogger::Fail); |
245 | } |
246 | if (overwrite) { |
247 | resultAttr->setPair(QTest::AI_Result, typeBuf); |
248 | } |
249 | } |
250 | else { |
251 | currentLogElement->addAttribute(QTest::AI_Result, typeBuf); |
252 | } |
253 | |
254 | if(file) |
255 | currentLogElement->addAttribute(QTest::AI_File, file); |
256 | else |
257 | currentLogElement->addAttribute(QTest::AI_File, "" ); |
258 | |
259 | QTest::qt_snprintf(buf, sizeof(buf), "%i" , line); |
260 | currentLogElement->addAttribute(QTest::AI_Line, buf); |
261 | |
262 | /* |
263 | Since XFAIL does not add a failure to the testlog in xunitxml, add a message, so we still |
264 | have some information about the expected failure. |
265 | */ |
266 | if (format == TLF_XunitXml && type == QAbstractTestLogger::XFail) { |
267 | QTestLogger::addMessage(QAbstractTestLogger::Info, description, file, line); |
268 | } |
269 | } |
270 | |
271 | void QTestLogger::addBenchmarkResult(const QBenchmarkResult &result) |
272 | { |
273 | QTestElement *benchmarkElement = new QTestElement(QTest::LET_Benchmark); |
274 | // printf("element %i", benchmarkElement->elementType()); |
275 | |
276 | benchmarkElement->addAttribute( |
277 | QTest::AI_Metric, |
278 | QTest::benchmarkMetricName(QBenchmarkTestMethodData::current->result.metric)); |
279 | benchmarkElement->addAttribute(QTest::AI_Tag, result.context.tag.toAscii().data()); |
280 | benchmarkElement->addAttribute(QTest::AI_Value, QByteArray::number(result.value).constData()); |
281 | |
282 | char buf[100]; |
283 | QTest::qt_snprintf(buf, sizeof(buf), "%i" , result.iterations); |
284 | benchmarkElement->addAttribute(QTest::AI_Iterations, buf); |
285 | currentLogElement->addLogElement(benchmarkElement); |
286 | } |
287 | |
288 | void QTestLogger::addTag(QTestElement* element) |
289 | { |
290 | const char *tag = QTestResult::currentDataTag(); |
291 | const char *gtag = QTestResult::currentGlobalDataTag(); |
292 | const char *filler = (tag && gtag) ? ":" : "" ; |
293 | if ((!tag || !tag[0]) && (!gtag || !gtag[0])) { |
294 | return; |
295 | } |
296 | |
297 | if (!tag) { |
298 | tag = "" ; |
299 | } |
300 | if (!gtag) { |
301 | gtag = "" ; |
302 | } |
303 | |
304 | QTestCharBuffer buf; |
305 | QTest::qt_asprintf(&buf, "%s%s%s" , gtag, filler, tag); |
306 | element->addAttribute(QTest::AI_Tag, buf.constData()); |
307 | } |
308 | |
309 | void QTestLogger::addMessage(MessageTypes type, const char *message, const char *file, int line) |
310 | { |
311 | QTestElement *errorElement = new QTestElement(QTest::LET_Error); |
312 | const char *typeBuf = 0; |
313 | |
314 | switch (type) { |
315 | case QAbstractTestLogger::Warn: |
316 | ++warningCounter; |
317 | typeBuf = "warn" ; |
318 | break; |
319 | case QAbstractTestLogger::QSystem: |
320 | ++systemCounter; |
321 | typeBuf = "system" ; |
322 | break; |
323 | case QAbstractTestLogger::QDebug: |
324 | ++qdebugCounter; |
325 | typeBuf = "qdebug" ; |
326 | break; |
327 | case QAbstractTestLogger::QWarning: |
328 | ++qwarnCounter; |
329 | typeBuf = "qwarn" ; |
330 | break; |
331 | case QAbstractTestLogger::QFatal: |
332 | ++qfatalCounter; |
333 | typeBuf = "qfatal" ; |
334 | break; |
335 | case QAbstractTestLogger::Skip: |
336 | ++skipCounter; |
337 | typeBuf = "skip" ; |
338 | break; |
339 | case QAbstractTestLogger::Info: |
340 | ++infoCounter; |
341 | typeBuf = "info" ; |
342 | break; |
343 | default: |
344 | typeBuf = "??????" ; |
345 | break; |
346 | } |
347 | |
348 | errorElement->addAttribute(QTest::AI_Type, typeBuf); |
349 | errorElement->addAttribute(QTest::AI_Description, message); |
350 | addTag(errorElement); |
351 | |
352 | if(file) |
353 | errorElement->addAttribute(QTest::AI_File, file); |
354 | else |
355 | errorElement->addAttribute(QTest::AI_File, "" ); |
356 | |
357 | char buf[100]; |
358 | QTest::qt_snprintf(buf, sizeof(buf), "%i" , line); |
359 | errorElement->addAttribute(QTest::AI_Line, buf); |
360 | |
361 | currentLogElement->addLogElement(errorElement); |
362 | ++errorCounter; |
363 | |
364 | // Also add the message to the system error log (i.e. stderr), if one exists |
365 | if (errorLogElement) { |
366 | QTestElement *systemErrorElement = new QTestElement(QTest::LET_Error); |
367 | systemErrorElement->addAttribute(QTest::AI_Description, message); |
368 | errorLogElement->addLogElement(systemErrorElement); |
369 | } |
370 | } |
371 | |
372 | void QTestLogger::setLogFormat(TestLoggerFormat fm) |
373 | { |
374 | format = fm; |
375 | } |
376 | |
377 | QTestLogger::TestLoggerFormat QTestLogger::logFormat() |
378 | { |
379 | return format; |
380 | } |
381 | |
382 | int QTestLogger::passCount() const |
383 | { |
384 | return passCounter; |
385 | } |
386 | |
387 | int QTestLogger::failureCount() const |
388 | { |
389 | return failureCounter; |
390 | } |
391 | |
392 | int QTestLogger::errorCount() const |
393 | { |
394 | return errorCounter; |
395 | } |
396 | |
397 | int QTestLogger::warningCount() const |
398 | { |
399 | return warningCounter; |
400 | } |
401 | |
402 | int QTestLogger::skipCount() const |
403 | { |
404 | return skipCounter; |
405 | } |
406 | |
407 | int QTestLogger::systemCount() const |
408 | { |
409 | return systemCounter; |
410 | } |
411 | |
412 | int QTestLogger::qdebugCount() const |
413 | { |
414 | return qdebugCounter; |
415 | } |
416 | |
417 | int QTestLogger::qwarnCount() const |
418 | { |
419 | return qwarnCounter; |
420 | } |
421 | |
422 | int QTestLogger::qfatalCount() const |
423 | { |
424 | return qfatalCounter; |
425 | } |
426 | |
427 | int QTestLogger::infoCount() const |
428 | { |
429 | return infoCounter; |
430 | } |
431 | |
432 | void QTestLogger::registerRandomSeed(unsigned int seed) |
433 | { |
434 | randomSeed_ = seed; |
435 | hasRandomSeed_ = true; |
436 | } |
437 | |
438 | unsigned int QTestLogger::randomSeed() const |
439 | { |
440 | return randomSeed_; |
441 | } |
442 | |
443 | bool QTestLogger::hasRandomSeed() const |
444 | { |
445 | return hasRandomSeed_; |
446 | } |
447 | |
448 | QT_END_NAMESPACE |
449 | |
450 | |