Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
89 elapsedTestcaseTime.start();
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
143 elapsedTestcaseTime.restart();
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();
159 elapsedTestcaseTime.restart();
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:124
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
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:129
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
QString section(QChar sep, qsizetype start, qsizetype end=-1, SectionFlags flags=SectionDefault) const
This function returns a section of the string.
Definition qstring.h:1284
QByteArray toUtf8() const &
Definition qstring.h:634
static QString machineHostName()
Definition qsysinfo.cpp:929
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:673
static qreal msecsTotalTime()
Definition qtestlog_p.h:117
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]
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:187
const char property[13]
Definition qwizard.cpp:101
QFile file
[0]