Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
Loading...
Searching...
No Matches
qabstracttestlogger.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
/
qabstracttestlogger_p
.
h
>
5
#
include
<
QtTest
/
qtestassert
.
h
>
6
#
include
<
qbenchmark_p
.
h
>
7
#
include
<
qtestresult_p
.
h
>
8
9
#
include
<
QtCore
/
qbytearray
.
h
>
10
#
include
<
QtCore
/
qstring
.
h
>
11
12
#
include
<
cstdio
>
13
14
#
include
<
stdio
.
h
>
15
#
include
<
stdlib
.
h
>
16
#
include
<
stdarg
.
h
>
17
18
#
ifndef
Q_OS_WIN
19
#
include
<
unistd
.
h
>
20
#
endif
21
22
#
if
defined
(
Q_OS_WINDOWS
)
23
#
include
<
io
.
h
>
24
#
endif
25
26
#
ifdef
Q_OS_ANDROID
27
#
include
<
sys
/
stat
.
h
>
28
#
endif
29
30
QT_BEGIN_NAMESPACE
31
/*!
32
\internal
33
\class QAbstractTestLogger
34
\inmodule QtTest
35
\brief Base class for test loggers
36
37
Implementations of logging for QtTest should implement all pure virtual
38
methods of this class and may implement the other virtual methods. This
39
class's documentation of each virtual method sets out how those
40
implementations are invoked by the QtTest code and offers guidance on how
41
the logging class should use the data. Actual implementations may have
42
different requirements - such as a file format with a defined schema, or a
43
target audience to serve - that affect how it interprets that guidance.
44
*/
45
46
/*!
47
\enum QAbstractTestLogger::IncidentTypes
48
49
\value Pass The test ran to completion successfully.
50
\value XFail The test failed a check that is known to fail; this failure
51
does not prevent successful completion of the test and may be
52
followed by further checks.
53
\value Fail The test fails.
54
\value XPass A check which was expected to fail actually passed. This is
55
counted as a failure, as it means whatever caused the known failure
56
no longer does, so the test needs an update.
57
\value Skip The current test ended prematurely, skipping some checks.
58
\value BlacklistedPass As Pass but the test was blacklisted.
59
\value BlacklistedXFail As XFail but the test was blacklisted.
60
\value BlacklistedFail As Fail but the test was blacklisted.
61
\value BlacklistedXPass As XPass but the test was blacklisted.
62
63
A test may also skip (see \l {QAbstractTestLogger::}{MessageTypes}). The
64
first of skip, Fail, XPass or the blacklisted equivalents of the last two to
65
arise is decisive for the outcome of the test: loggers which should only
66
report one outcome should thus record that as the outcome and either ignore
67
later incidents (or skips) in the same run of the test function or map them
68
to some form of message.
69
70
\note tests can be "blacklisted" when they are known to fail
71
unreliably. When testing is used to decide whether a change to the code
72
under test is acceptable, such failures are not automatic grounds for
73
rejecting the change, if the unreliable failure was known before the
74
change. QTest::qExec(), as a result, only returns a failing status code if
75
some non-blacklisted test failed. Logging backends may reasonably report a
76
blacklisted result just as they would report the non-blacklisted equivalent,
77
optionally with some annotation to indicate that the result should not be
78
taken as damning evidence against recent changes to the code under test.
79
80
\sa QAbstractTestLogger::addIncident()
81
*/
82
83
/*!
84
\enum QAbstractTestLogger::MessageTypes
85
86
The members whose names begin with \c Q describe messages that originate in
87
calls, by the test or code under test, to Qt logging functions (implemented
88
as macros) whose names are similar, with a \c q in place of the leading \c
89
Q. The other members describe messages generated internally by QtTest.
90
91
\value QInfo An informational message from qInfo().
92
\value QWarning A warning from qWarning().
93
\value QDebug A debug message from qDebug().
94
\value QCritical A critical error from qCritical().
95
\value QFatal A fatal error from qFatal(), or an unrecognised message from
96
the Qt logging functions.
97
\value Info Messages QtTest generates as requested by the \c{-v1} or \c{-v2}
98
command-line option being specified when running the test.
99
\value Warn A warning generated internally by QtTest
100
101
\note For these purposes, some utilities provided by QtTestlib as helper
102
functions to facilitate testing - such as \l QSignalSpy, \l
103
QTestAccessibility, \l QTest::qExtractTestData(), and the facilities to
104
deliver artificial mouse and keyboard events - are treated as test code,
105
rather than internal to QtTest; they call \l qWarning() and friends rather
106
than using the internal logging infrastructure, so that \l
107
QTest::ignoreMessage() can be used to anticipate the messages.
108
109
\sa QAbstractTestLogger::addMessage()
110
*/
111
112
/*!
113
Constructs the base-class parts of the logger.
114
115
Derived classes should pass this base-constructor the \a filename of the
116
file to which they shall log test results, or \nullptr to write to standard
117
output. The protected member \c stream is set to the open file descriptor.
118
*/
119
QAbstractTestLogger::QAbstractTestLogger(
const
char
*filename)
120
{
121
if
(!filename) {
122
stream = stdout;
123
return
;
124
}
125
#
if
defined
(
_MSC_VER
)
126
// "N": fopen_s opens the file exclusively, so a child process that inherits the handle and
127
// outlives us (e.g. mspdbsrv.exe, spawned by cl.exe) would keep the log file unreadable.
128
if
(::fopen_s(&stream, filename,
"wtN"
)) {
129
#
else
130
stream = ::fopen(filename,
"wt"
);
131
if
(!stream) {
132
#
endif
133
fprintf(stderr,
"Unable to open file for logging: %s\n"
, filename);
134
::exit(1);
135
}
136
#
ifdef
Q_OS_ANDROID
137
else
{
138
// Make sure output is world-readable on Android
139
::chmod(filename, 0666);
140
}
141
#
endif
142
}
143
144
/*!
145
Destroys the logger object.
146
147
If the protected \c stream is not standard output, it is closed. In any
148
case it is cleared.
149
*/
150
QAbstractTestLogger::~QAbstractTestLogger()
151
{
152
QTEST_ASSERT(stream);
153
if
(stream != stdout)
154
fclose(stream);
155
stream =
nullptr
;
156
}
157
158
/*!
159
Returns true if the logger supports repeated test runs.
160
161
Repetition of test runs is disabled by default, and can be enabled only for
162
test loggers that support it. Even if the logger may create syntactically
163
correct test reports, log-file analyzers may assume that test names are
164
unique within one report file.
165
*/
166
bool
QAbstractTestLogger::isRepeatSupported()
const
167
{
168
return
false
;
169
}
170
171
/*!
172
Returns true if the \c output stream is standard output.
173
*/
174
bool
QAbstractTestLogger::isLoggingToStdout()
const
175
{
176
return
stream == stdout;
177
}
178
179
/*!
180
Helper utility to blot out unprintable characters in \a str.
181
182
Takes a \c{'\0'}-terminated mutable string and changes any characters of it
183
that are not suitable for printing to \c{'?'} characters.
184
*/
185
void
QAbstractTestLogger::filterUnprintable(
char
*str)
const
186
{
187
unsigned
char
*idx =
reinterpret_cast
<
unsigned
char
*>(str);
188
while
(*idx) {
189
if
(((*idx < 0x20 && *idx !=
'\n'
&& *idx !=
'\t'
) || *idx == 0x7f))
190
*idx =
'?'
;
191
++idx;
192
}
193
}
194
195
/*!
196
Convenience method to write \a msg to the output stream.
197
198
The output \a msg must be a \c{'\0'}-terminated string (and not \nullptr).
199
200
If the output \c stream is TTY the message is printed as is. If not, the
201
message is filtered via filterUnprintable() first. In both cases the output
202
\c stream is flushed after printing.
203
*/
204
void
QAbstractTestLogger::outputString(
const
char
*msg)
205
{
206
QTEST_ASSERT(stream);
207
QTEST_ASSERT(msg);
208
209
#
if
defined
(
Q_OS_WINDOWS
)
210
#
define
isatty
_isatty
211
#
define
fileno
_fileno
212
#
endif
213
214
if
(isatty(fileno(stream))) {
215
::fputs(msg, stream);
216
::fflush(stream);
217
}
else
{
218
char
*filtered =
new
char
[strlen(msg) + 1];
219
strcpy(filtered, msg);
220
filterUnprintable(filtered);
221
::fputs(filtered, stream);
222
::fflush(stream);
223
delete
[] filtered;
224
}
225
226
#
if
defined
(
Q_OS_WINDOWS
)
227
#
undef
isatty
228
#
undef
fileno
229
#
endif
230
}
231
232
/*!
233
Called before the start of a test run.
234
235
This virtual method is called before the first tests are run. A logging
236
implementation might open a file, write some preamble, or prepare in other
237
ways, such as setting up initial values of variables. It can use the usual
238
Qt logging infrastucture, since it is also called before QtTest installs its
239
own custom message handler.
240
241
\sa stopLogging()
242
*/
243
void
QAbstractTestLogger::startLogging()
244
{
245
}
246
247
/*!
248
Called after the end of a test run.
249
250
This virtual method is called after all tests have run. A logging
251
implementation might collate information gathered from the run, write a
252
summary, or close a file. It can use the usual Qt logging infrastucture,
253
since it is also called after QtTest has restored the default message
254
handler it replaced with its own custom message handler.
255
256
\sa startLogging()
257
*/
258
void
QAbstractTestLogger::stopLogging()
259
{
260
}
261
262
void
QAbstractTestLogger::addBenchmarkResults(
const
QList<QBenchmarkResult> &result)
263
{
264
for
(
const
auto
&m : result)
265
addBenchmarkResult(m);
266
}
267
268
/*!
269
\fn void QAbstractTestLogger::enterTestFunction(const char *function)
270
271
This virtual method is called before each test function is invoked. It is
272
passed the name of the test function (without its class prefix) as \a
273
function. It is likewise called for \c{initTestCase()} at the start of
274
testing, after \l startLogging(), and for \c{cleanupTestCase()} at the end
275
of testing, in each case passing the name of the function. It is also called
276
with \nullptr as \a function after the last of these functions, or in the
277
event of an early end to testing, before \l stopLogging().
278
279
For data-driven test functions, this is called only once, before the data
280
function is called to set up the table of datasets and the test is run with
281
its first dataset.
282
283
Every logging implementation must implement this method. It shall typically
284
need to record the name of the function for later use in log messages.
285
286
\sa leaveTestFunction(), enterTestData()
287
*/
288
/*!
289
\fn void QAbstractTestLogger::leaveTestFunction()
290
291
This virtual method is called after a test function has completed, to match
292
\l enterTestFunction(). For data-driven test functions, this is called only
293
once, after the test is run with its last dataset.
294
295
Every logging implementation must implement this method. In some cases it
296
may be called more than once without an intervening call to \l
297
enterTestFunction(). In such cases, the implementation should ignore these
298
later calls, until the next call to enterTestFunction().
299
300
\sa enterTestFunction(), enterTestData()
301
*/
302
/*!
303
\fn void QAbstractTestLogger::enterTestData(QTestData *)
304
305
This virtual method is called before and after each call to a test
306
function. For a data-driven test, the call before is passed the name of the
307
test data row. This may combine a global data row name with a local data row
308
name. For non-data-driven tests and for the call after a test function,
309
\nullptr is passed
310
311
A logging implementation might chose to record the data row name for
312
reporting of results from the test for that data row. It should, in such a
313
case, clear its record of the name when called with \nullptr.
314
315
\sa enterTestFunction(), leaveTestFunction()
316
*/
317
/*!
318
\fn void QAbstractTestLogger::addIncident(IncidentTypes type, const char *description, const char *file, int line)
319
320
This virtual method is called when an event occurs that relates to the
321
resolution of the test. The \a type indicates whether this was a pass, a
322
fail or a skip, whether a failure was expected, and whether the test being
323
run is blacklisted. The \a description may be empty (for a pass) or a
324
message describing the nature of the incident. Where the location in code of
325
the incident is known, it is indicated by \a file and \a line; otherwise,
326
these are \a nullptr and 0, respectively.
327
328
Every logging implementation must implement this method. Note that there are
329
circumstances where more than one incident may be reported, in this way, for
330
a single run of a test on a single dataset. It is the implementation's
331
responsibility to recognize such cases and decide what to do about them. For
332
purposes of counting resolutions of tests in the "Totals" report at the end
333
of a test run, QtTest considers the first incident (excluding XFail and its
334
blacklisted variant) decisive.
335
336
\sa addMessage(), addBenchmarkResult()
337
*/
338
/*!
339
\fn void QAbstractTestLogger::addBenchmarkResult(const QBenchmarkResult &result)
340
341
This virtual method is called after a benchmark has been run enough times to
342
produce usable data. It is passed the median \a result from all cycles of
343
the code controlled by the test's QBENCHMARK loop.
344
345
Every logging implementation must implement this method.
346
347
\sa addIncident(), addMessage()
348
*/
349
/*!
350
\overload
351
\fn void QAbstractTestLogger::addMessage(MessageTypes type, const QString &message, const char *file, int line)
352
353
This virtual method is called, via its \c QtMsgType overload, from the
354
custom message handler QtTest installs. It is also used to
355
warn about various situations detected by QtTest itself, such
356
as \e failure to see a message anticipated by QTest::ignoreMessage() and,
357
particularly when verbosity options have been enabled via the command-line,
358
to log some extra information.
359
360
Every logging implementation must implement this method. The \a type
361
indicates the category of message and the \a message is the content to be
362
reported. When the message is associated with specific code, the name of the
363
\a file and \a line number within it are also supplied; otherwise, these are
364
\nullptr and 0, respectively.
365
366
\sa QTest::ignoreMessage(), addIncident()
367
*/
368
369
/*!
370
\overload
371
372
This virtual method is called from the custom message handler QtTest
373
installs in place of Qt's default message handler for the duration of
374
testing, unless QTest::ignoreMessage() was used to ignore it, or too many
375
messages have previously been processed. (The limiting number of messages is
376
controlled by the -maxwarnings option to a test and defaults to 2002.)
377
378
Logging implementations should not normally need to override this method.
379
The base implementation converts \a type to the matching \l MessageType,
380
formats the given \a message suitably for the specified \a context, and
381
forwards the converted type and formatted message to the overload that takes
382
MessageType and QString.
383
384
\sa QTest::ignoreMessage(), addIncident()
385
*/
386
void
QAbstractTestLogger::addMessage(QtMsgType type,
const
QMessageLogContext &context,
387
const
QString &message)
388
{
389
QAbstractTestLogger::MessageTypes messageType = [=]() {
390
switch
(type) {
391
case
QtDebugMsg:
return
QAbstractTestLogger::QDebug;
392
case
QtInfoMsg:
return
QAbstractTestLogger::QInfo;
393
case
QtCriticalMsg:
return
QAbstractTestLogger::QCritical;
394
case
QtWarningMsg:
return
QAbstractTestLogger::QWarning;
395
case
QtFatalMsg:
return
QAbstractTestLogger::QFatal;
396
}
397
Q_UNREACHABLE_RETURN(QAbstractTestLogger::QFatal);
398
}();
399
400
QString formattedMessage = qFormatLogMessage(type, context, message);
401
402
// Note that we explicitly ignore the file and line of the context here,
403
// as that's what QTest::messageHandler used to do when calling the same
404
// overload directly.
405
addMessage(messageType, formattedMessage);
406
}
407
408
namespace
409
{
410
constexpr
int
MAXSIZE = 1024 * 1024 * 2;
411
}
412
413
namespace
QTest
414
{
415
416
/*!
417
\fn int QTest::qt_asprintf(QTestCharBuffer *buf, const char *format, ...);
418
\internal
419
*/
420
int
qt_asprintf
(QTestCharBuffer *str,
const
char
*format, ...)
421
{
422
Q_ASSERT(str);
423
int
size = str->size();
424
Q_ASSERT(size > 0);
425
426
va_list ap;
427
int
res = 0;
428
429
do
{
430
va_start(ap, format);
431
res = std::vsnprintf(str->data(), size, format, ap);
432
va_end(ap);
433
// vsnprintf() reliably '\0'-terminates
434
Q_ASSERT(res < 0 || str->data()[res < size ? res : size - 1] ==
'\0'
);
435
// Note, we're assuming that a result of -1 is always due to running out of space.
436
if
(res >= 0 && res < size)
// Success
437
break
;
438
439
// Buffer wasn't big enough, try again:
440
size *= 2;
441
// If too large or out of memory, take what we have:
442
}
while
(size <= MAXSIZE && str->reset(size));
443
444
return
res;
445
}
446
447
}
448
449
namespace
QTestPrivate
450
{
451
452
void
generateTestIdentifier
(QTestCharBuffer *identifier,
int
parts)
453
{
454
// We may run on any thread, while the main thread moves on to the next data row or test
455
// function. Hold the lock until we're done copying the strings.
456
const
QTestResult::IdentifierLocker locker;
457
458
const
char
*function = locker.testFunction() ? locker.testFunction() :
"UnknownTestFunc"
;
459
const
char
*testObject = parts & TestObject ? locker.objectName() :
""
;
460
const
char
*testFunction = parts & TestFunction ? function :
""
;
461
const
char
*objectFunctionFiller = parts & TestObject && parts & (TestFunction | TestDataTag) ?
"::"
:
""
;
462
const
char
*testFuctionStart = parts & TestFunction ?
"("
:
""
;
463
const
char
*testFuctionEnd = parts & TestFunction ?
")"
:
""
;
464
465
const
char
*dataTag = (parts & TestDataTag) && locker.dataTag() ? locker.dataTag() :
""
;
466
const
char
*globalDataTag =
467
(parts & TestDataTag) && locker.globalDataTag() ? locker.globalDataTag() :
""
;
468
const
char
*tagFiller = (dataTag[0] && globalDataTag[0]) ?
":"
:
""
;
469
470
QTest
::
qt_asprintf
(identifier,
"%s%s%s%s%s%s%s%s"
,
471
testObject, objectFunctionFiller, testFunction, testFuctionStart,
472
globalDataTag, tagFiller, dataTag, testFuctionEnd);
473
}
474
475
// strcat() for QTestCharBuffer objects:
476
bool
appendCharBuffer
(QTestCharBuffer *accumulator,
const
QTestCharBuffer &more)
477
{
478
const
auto
bufsize = [](
const
QTestCharBuffer &buf) ->
int
{
479
const
int
max = buf.size();
480
return
max > 0 ?
int
(qstrnlen(buf.constData(), max)) : 0;
481
};
482
const
int
extra = bufsize(more);
483
if
(extra <= 0)
484
return
true
;
// Nothing to do, fatuous success
485
486
const
int
oldsize = bufsize(*accumulator);
487
const
int
newsize = oldsize + extra + 1;
// 1 for final '\0'
488
if
(newsize > MAXSIZE || !accumulator->resize(newsize))
489
return
false
;
// too big or unable to grow
490
491
char
*tail = accumulator->data() + oldsize;
492
memcpy(tail, more.constData(), extra);
493
tail[extra] =
'\0'
;
494
return
true
;
495
}
496
497
}
498
499
QT_END_NAMESPACE
QTestPrivate
Definition
qabstractitemmodeltester.h:29
QTestPrivate::generateTestIdentifier
void generateTestIdentifier(QTestCharBuffer *identifier, int parts)
Definition
qabstracttestlogger.cpp:452
QTestPrivate::appendCharBuffer
bool appendCharBuffer(QTestCharBuffer *accumulator, const QTestCharBuffer &more)
Definition
qabstracttestlogger.cpp:476
QTest
[15]
Definition
qtestsupport_core.h:15
QTest::qt_asprintf
int qt_asprintf(QTestCharBuffer *str, const char *format,...)
Definition
qabstracttestlogger.cpp:420
qtbase
src
testlib
qabstracttestlogger.cpp
Generated on
for Qt by
1.18.0