Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qjunittestlogger.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
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>
11
12#include <QtCore/qlibraryinfo.h>
13
14#include <string.h>
15
26// QTBUG-95424 links to further useful documentation.
27
29 : QAbstractTestLogger(filename)
30{
31}
32
34{
35 Q_ASSERT(!currentTestSuite);
36 delete logFormatter;
37}
38
39// We track test timing per test case, so we
40// need to maintain our own elapsed timer.
43{
44 return elapsedTestcaseTime.nsecsElapsed() / 1e9;
45}
46
48{
49 return QByteArray::number(ms / 1000, 'f', 3);
50}
51
53{
55
56 logFormatter = new QTestJUnitStreamer(this);
57
58 Q_ASSERT(!currentTestSuite);
59 currentTestSuite = new QTestElement(QTest::LET_TestSuite);
61
62 auto localTime = QDateTime::currentDateTime();
63 currentTestSuite->addAttribute(QTest::AI_Timestamp,
64 localTime.toString(Qt::ISODate).toUtf8().constData());
65
66 currentTestSuite->addAttribute(QTest::AI_Hostname,
67 QSysInfo::machineHostName().toUtf8().constData());
68
71
72 property = new QTestElement(QTest::LET_Property);
73 property->addAttribute(QTest::AI_Name, "QTestVersion");
74 property->addAttribute(QTest::AI_PropertyValue, QTEST_VERSION_STR);
75 properties->addChild(property);
76
77 property = new QTestElement(QTest::LET_Property);
78 property->addAttribute(QTest::AI_Name, "QtVersion");
79 property->addAttribute(QTest::AI_PropertyValue, qVersion());
80 properties->addChild(property);
81
82 property = new QTestElement(QTest::LET_Property);
83 property->addAttribute(QTest::AI_Name, "QtBuild");
84 property->addAttribute(QTest::AI_PropertyValue, QLibraryInfo::build());
85 properties->addChild(property);
86
87 currentTestSuite->addChild(properties);
88
90}
91
93{
94 char buf[10];
95
96 qsnprintf(buf, sizeof(buf), "%i", testCounter);
97 currentTestSuite->addAttribute(QTest::AI_Tests, buf);
98
99 qsnprintf(buf, sizeof(buf), "%i", failureCounter);
100 currentTestSuite->addAttribute(QTest::AI_Failures, buf);
101
102 qsnprintf(buf, sizeof(buf), "%i", errorCounter);
103 currentTestSuite->addAttribute(QTest::AI_Errors, buf);
104
105 qsnprintf(buf, sizeof(buf), "%i", QTestLog::skipCount());
106 currentTestSuite->addAttribute(QTest::AI_Skipped, buf);
107
108 currentTestSuite->addAttribute(QTest::AI_Time,
110
111 for (auto *testCase : listOfTestcases)
112 currentTestSuite->addChild(testCase);
113 listOfTestcases.clear();
114
115 logFormatter->output(currentTestSuite);
116
117 delete currentTestSuite;
118 currentTestSuite = nullptr;
119
121}
122
123void QJUnitTestLogger::enterTestFunction(const char *function)
124{
125 enterTestCase(function);
126}
127
128void QJUnitTestLogger::enterTestCase(const char *name)
129{
130 currentTestCase = new QTestElement(QTest::LET_TestCase);
131 currentTestCase->addAttribute(QTest::AI_Name, name);
133 listOfTestcases.push_back(currentTestCase);
134
135 Q_ASSERT(!systemOutputElement && !systemErrorElement);
136 systemOutputElement = new QTestElement(QTest::LET_SystemOutput);
137 systemErrorElement = new QTestElement(QTest::LET_SystemError);
138
139 // The element will be deleted when the suite is deleted
140
141 ++testCounter;
142
144}
145
147{
148 QTestCharBuffer testIdentifier;
151
152 static const char *lastTestFunction = nullptr;
153 if (QTestResult::currentTestFunction() != lastTestFunction) {
154 // Adopt existing testcase for the initial test data
155 auto *name = const_cast<QTestElementAttribute*>(
156 currentTestCase->attribute(QTest::AI_Name));
157 name->setPair(QTest::AI_Name, testIdentifier.data());
158 lastTestFunction = QTestResult::currentTestFunction();
160 } else {
161 // Create new test cases for remaining test data
162 leaveTestCase();
163 enterTestCase(testIdentifier.data());
164 }
165}
166
168{
169 leaveTestCase();
170}
171
172void QJUnitTestLogger::leaveTestCase()
173{
174 currentTestCase->addAttribute(QTest::AI_Time,
175 toSecondsFormat(elapsedTestCaseSeconds() * 1000).constData());
176
177 if (!systemOutputElement->childElements().empty())
178 currentTestCase->addChild(systemOutputElement);
179 else
180 delete systemOutputElement;
181
182 if (!systemErrorElement->childElements().empty())
183 currentTestCase->addChild(systemErrorElement);
184 else
185 delete systemErrorElement;
186
187 systemOutputElement = nullptr;
188 systemErrorElement = nullptr;
189}
190
192 const char *file, int line)
193{
194 if (type == Fail || type == XPass) {
195 auto failureType = [&]() {
196 switch (type) {
197 case QAbstractTestLogger::Fail: return "fail";
198 case QAbstractTestLogger::XPass: return "xpass";
199 default: Q_UNREACHABLE();
200 }
201 }();
202
203 addFailure(QTest::LET_Failure, failureType, QString::fromUtf8(description));
204 } else if (type == XFail) {
205 // Since XFAIL does not add a failure to the testlog in JUnit XML we add a
206 // message, so we still have some information about the expected failure.
207 addMessage(Info, QString::fromUtf8(description), file, line);
208 } else if (type == Skip) {
209 auto skippedElement = new QTestElement(QTest::LET_Skipped);
210 skippedElement->addAttribute(QTest::AI_Message, description);
211 currentTestCase->addChild(skippedElement);
212 }
213}
214
215void QJUnitTestLogger::addFailure(QTest::LogElementType elementType,
216 const char *failureType, const QString &failureDescription)
217{
218 if (elementType == QTest::LET_Failure) {
219 // Make sure we're not adding failure when we already have error,
220 // or adding additional failures when we already have a failure.
221 for (auto *childElement : currentTestCase->childElements()) {
222 if (childElement->elementType() == QTest::LET_Error ||
223 childElement->elementType() == QTest::LET_Failure)
224 return;
225 }
226 }
227
228 QTestElement *failureElement = new QTestElement(elementType);
229 failureElement->addAttribute(QTest::AI_Type, failureType);
230
231 // Assume the first line is the message, and the remainder are details
232 QString message = failureDescription.section(u'\n', 0, 0);
233 QString details = failureDescription.section(u'\n', 1);
234
235 failureElement->addAttribute(QTest::AI_Message, message.toUtf8().constData());
236
237 if (!details.isEmpty()) {
238 auto textNode = new QTestElement(QTest::LET_Text);
239 textNode->addAttribute(QTest::AI_Value, details.toUtf8().constData());
240 failureElement->addChild(textNode);
241 }
242
243 currentTestCase->addChild(failureElement);
244
245 switch (elementType) {
246 case QTest::LET_Failure: ++failureCounter; break;
247 case QTest::LET_Error: ++errorCounter; break;
248 default: Q_UNREACHABLE();
249 }
250}
251
253{
254 Q_UNUSED(file);
255 Q_UNUSED(line);
256
257 if (type == QFatal) {
258 addFailure(QTest::LET_Error, "qfatal", message);
259 return;
260 }
261
262 auto systemLogElement = [&]() {
263 switch (type) {
267 return systemOutputElement;
271 return systemErrorElement;
272 default:
273 Q_UNREACHABLE();
274 }
275 }();
276
277 if (!systemLogElement)
278 return; // FIXME: Handle messages outside of test functions
279
280 auto textNode = new QTestElement(QTest::LET_Text);
281 textNode->addAttribute(QTest::AI_Value, message.toUtf8().constData());
282 systemLogElement->addChild(textNode);
283}
284
286
Base class for test loggers.
IncidentTypes
\value Pass The test ran to completion successfully.
virtual void startLogging()
Called before the start of a test run.
MessageTypes
The members whose names begin with Q describe messages that originate in calls, by the test or code u...
virtual void stopLogging()
Called after the end of a test run.
\inmodule QtCore
Definition qbytearray.h:57
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
static QDateTime currentDateTime()
This is an overloaded member function, provided for convenience. It differs from the above function o...
\inmodule QtCore
qint64 restart() noexcept
Restarts the timer and returns the number of milliseconds elapsed since the previous start.
void start() noexcept
Starts this timer.
qint64 nsecsElapsed() const noexcept
void stopLogging() override
Called after the end of a test run.
void enterTestFunction(const char *function) override
This virtual method is called before each test function is invoked.
void startLogging() override
Called before the start of a test run.
void addMessage(MessageTypes type, const QString &message, const char *file=nullptr, int line=0) override
This is an overloaded member function, provided for convenience. It differs from the above function o...
void enterTestData(QTestData *) override
This virtual method is called before and after each call to a test function.
void addIncident(IncidentTypes type, const char *description, const char *file=nullptr, int line=0) override
This virtual method is called when an event occurs that relates to the resolution of the test.
QJUnitTestLogger(const char *filename)
void leaveTestFunction() override
This virtual method is called after a test function has completed, to match \l enterTestFunction().
static const char * build() noexcept
Returns a string describing how this version of Qt was built.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
QString section(QChar sep, qsizetype start, qsizetype end=-1, SectionFlags flags=SectionDefault) const
This function returns a section of the string.
Definition qstring.h:1139
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
QByteArray toUtf8() const &
Definition qstring.h:563
static QString machineHostName()
Definition qsysinfo.cpp:926
void addAttribute(const QTest::AttributeIndex index, const char *value)
const QTestElementAttribute * attribute(QTest::AttributeIndex index) const
bool setPair(QTest::AttributeIndex attributeIndex, const char *value)
const std::vector< QTestElement * > & childElements() const
bool addChild(QTestElement *element)
void output(QTestElement *element) const
static int skipCount()
Definition qtestlog.cpp:652
static qreal msecsTotalTime()
Definition qtestlog_p.h:115
static const char * currentTestObjectName()
static const char * currentTestFunction()
Combined button and popup list for selecting options.
void generateTestIdentifier(QTestCharBuffer *identifier, int parts)
@ ISODate
Q_CORE_EXPORT int qsnprintf(char *str, size_t n, const char *fmt,...)
static const QCssKnownValue properties[NumProperties - 1]
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction function
static QByteArray toSecondsFormat(qreal ms)
static Q_CONSTINIT QElapsedTimer elapsedTestcaseTime
static qreal elapsedTestCaseSeconds()
GLenum type
GLenum GLuint GLenum GLsizei const GLchar * buf
GLuint GLsizei const GLchar * message
GLuint name
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_UNUSED(x)
#define QTEST_VERSION_STR
QT_BEGIN_NAMESPACE Q_CORE_EXPORT Q_DECL_CONST_FUNCTION const char * qVersion(void) Q_DECL_NOEXCEPT
double qreal
Definition qtypes.h:92
const char property[13]
Definition qwizard.cpp:101
QFile file
[0]