4#include <QtTest/private/qjunittestlogger_p.h>
5#include <QtTest/private/qtestelement_p.h>
6#include <QtTest/private/qtestjunitstreamer_p.h>
7#include <QtTest/qtestcase.h>
8#include <QtTest/private/qtestresult_p.h>
9#include <QtTest/private/qbenchmark_p.h>
10#include <QtTest/private/qtestlog_p.h>
12#include <QtCore/qelapsedtimer.h>
13#include <QtCore/qlibraryinfo.h>
21
22
23
24
25
26
27
28
31QJUnitTestLogger::QJUnitTestLogger(
const char *filename)
32 : QAbstractTestLogger(filename)
36QJUnitTestLogger::~QJUnitTestLogger()
38 Q_ASSERT(!currentTestSuite);
44Q_CONSTINIT
static QElapsedTimer elapsedTestcaseTime;
47 return elapsedTestcaseTime.nsecsElapsed() / 1e9;
52 return QByteArray::number(ms / 1000,
'f', 3);
55void QJUnitTestLogger::startLogging()
57 QAbstractTestLogger::startLogging();
59 logFormatter =
new QTestJUnitStreamer(
this);
61 Q_ASSERT(!currentTestSuite);
62 currentTestSuite =
new QTestElement(QTest::LET_TestSuite);
63 currentTestSuite->addAttribute(QTest::AI_Name, QTestResult::currentTestObjectName());
65 auto localTime = QDateTime::currentDateTime();
66 currentTestSuite->addAttribute(QTest::AI_Timestamp,
67 localTime.toString(Qt::ISODate).toUtf8().constData());
69 currentTestSuite->addAttribute(QTest::AI_Hostname,
70 QSysInfo::machineHostName().toUtf8().constData());
72 QTestElement *property;
73 QTestElement *properties =
new QTestElement(QTest::LET_Properties);
75 property =
new QTestElement(QTest::LET_Property);
76 property->addAttribute(QTest::AI_Name,
"QTestVersion");
77 property->addAttribute(QTest::AI_PropertyValue, QTEST_VERSION_STR);
78 properties->addChild(property);
80 property =
new QTestElement(QTest::LET_Property);
81 property->addAttribute(QTest::AI_Name,
"QtVersion");
82 property->addAttribute(QTest::AI_PropertyValue, qVersion());
83 properties->addChild(property);
85 property =
new QTestElement(QTest::LET_Property);
86 property->addAttribute(QTest::AI_Name,
"QtBuild");
87 property->addAttribute(QTest::AI_PropertyValue, QLibraryInfo::build());
88 properties->addChild(property);
90 currentTestSuite->addChild(properties);
92 elapsedTestcaseTime.start();
95void QJUnitTestLogger::stopLogging()
99 std::snprintf(buf,
sizeof(buf),
"%i", testCounter);
100 currentTestSuite->addAttribute(QTest::AI_Tests, buf);
102 std::snprintf(buf,
sizeof(buf),
"%i", failureCounter);
103 currentTestSuite->addAttribute(QTest::AI_Failures, buf);
105 std::snprintf(buf,
sizeof(buf),
"%i", errorCounter);
106 currentTestSuite->addAttribute(QTest::AI_Errors, buf);
108 std::snprintf(buf,
sizeof(buf),
"%i", QTestLog::skipCount());
109 currentTestSuite->addAttribute(QTest::AI_Skipped, buf);
111 currentTestSuite->addAttribute(QTest::AI_Time,
112 toSecondsFormat(QTestLog::msecsTotalTime()).constData());
114 for (
auto *testCase : listOfTestcases)
115 currentTestSuite->addChild(testCase);
116 listOfTestcases.clear();
118 logFormatter->output(currentTestSuite);
120 delete currentTestSuite;
121 currentTestSuite =
nullptr;
123 QAbstractTestLogger::stopLogging();
126void QJUnitTestLogger::enterTestFunction(
const char *function)
128 enterTestCase(function);
131void QJUnitTestLogger::enterTestCase(
const char *name)
134 QMutexLocker locker(&mutex);
135 currentTestCase =
new QTestElement(QTest::LET_TestCase);
136 currentTestCase->addAttribute(QTest::AI_Name, name);
137 currentTestCase->addAttribute(QTest::AI_Classname, QTestResult::currentTestObjectName());
138 listOfTestcases.push_back(currentTestCase);
140 Q_ASSERT(!systemOutputElement && !systemErrorElement);
141 systemOutputElement =
new QTestElement(QTest::LET_SystemOutput);
142 systemErrorElement =
new QTestElement(QTest::LET_SystemError);
149 elapsedTestcaseTime.start();
152void QJUnitTestLogger::enterTestData(QTestData *)
154 QTestCharBuffer testIdentifier;
155 QTestPrivate::generateTestIdentifier(&testIdentifier,
156 QTestPrivate::TestFunction | QTestPrivate::TestDataTag);
158 static const char *lastTestFunction =
nullptr;
159 if (QTestResult::currentTestFunction() != lastTestFunction) {
161 auto *name =
const_cast<QTestElementAttribute*>(
162 currentTestCase->attribute(QTest::AI_Name));
163 name->setPair(QTest::AI_Name, testIdentifier.data());
164 lastTestFunction = QTestResult::currentTestFunction();
165 elapsedTestcaseTime.start();
169 enterTestCase(testIdentifier.data());
173void QJUnitTestLogger::leaveTestFunction()
178void QJUnitTestLogger::leaveTestCase()
180 QMutexLocker locker(&mutex);
181 currentTestCase->addAttribute(QTest::AI_Time,
182 toSecondsFormat(elapsedTestCaseSeconds() * 1000).constData());
184 if (!systemOutputElement->childElements().empty())
185 currentTestCase->addChild(systemOutputElement);
187 delete systemOutputElement;
189 if (!systemErrorElement->childElements().empty())
190 currentTestCase->addChild(systemErrorElement);
192 delete systemErrorElement;
194 systemOutputElement =
nullptr;
195 systemErrorElement =
nullptr;
198void QJUnitTestLogger::addIncident(IncidentTypes type,
const char *description,
199 const char *file,
int line)
201 if (type == Fail || type == XPass) {
202 auto failureType = [&]() {
204 case QAbstractTestLogger::Fail:
return "fail";
205 case QAbstractTestLogger::XPass:
return "xpass";
206 default: Q_UNREACHABLE();
210 addFailure(QTest::LET_Failure, failureType, QString::fromUtf8(description));
211 }
else if (type == XFail) {
214 addMessage(Info, QString::fromUtf8(description), file, line);
215 }
else if (type == Skip) {
216 auto skippedElement =
new QTestElement(QTest::LET_Skipped);
217 skippedElement->addAttribute(QTest::AI_Message, description);
218 currentTestCase->addChild(skippedElement);
222void QJUnitTestLogger::addFailure(QTest::LogElementType elementType,
223 const char *failureType,
const QString &failureDescription)
225 if (elementType == QTest::LET_Failure) {
228 for (
auto *childElement : currentTestCase->childElements()) {
229 if (childElement->elementType() == QTest::LET_Error ||
230 childElement->elementType() == QTest::LET_Failure)
235 QTestElement *failureElement =
new QTestElement(elementType);
236 failureElement->addAttribute(QTest::AI_Type, failureType);
239 QString message = failureDescription.section(u'\n', 0, 0);
240 QString details = failureDescription.section(u'\n', 1);
242 failureElement->addAttribute(QTest::AI_Message, message.toUtf8().constData());
244 if (!details.isEmpty()) {
245 auto textNode =
new QTestElement(QTest::LET_Text);
246 textNode->addAttribute(QTest::AI_Value, details.toUtf8().constData());
247 failureElement->addChild(textNode);
250 currentTestCase->addChild(failureElement);
252 switch (elementType) {
253 case QTest::LET_Failure: ++failureCounter;
break;
254 case QTest::LET_Error: ++errorCounter;
break;
255 default: Q_UNREACHABLE();
259void QJUnitTestLogger::addMessage(MessageTypes type,
const QString &message,
const char *file,
int line)
264 QMutexLocker locker(&mutex);
265 if (type == QFatal) {
266 addFailure(QTest::LET_Error,
"qfatal", message);
270 auto systemLogElement = [&]() {
272 case QAbstractTestLogger::QDebug:
273 case QAbstractTestLogger::Info:
274 case QAbstractTestLogger::QInfo:
275 return systemOutputElement;
276 case QAbstractTestLogger::Warn:
277 case QAbstractTestLogger::QWarning:
278 case QAbstractTestLogger::QCritical:
279 return systemErrorElement;
285 if (!systemLogElement)
288 auto textNode =
new QTestElement(QTest::LET_Text);
289 textNode->addAttribute(QTest::AI_Value, message.toUtf8().constData());
290 systemLogElement->addChild(textNode);
static QByteArray toSecondsFormat(qreal ms)
static qreal elapsedTestCaseSeconds()