1// Copyright (C) 2017 Crimson AS <info@crimson.no>
2// Copyright (C) 2016 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "quicktestresult_p.h"
6#include "quicktest.h"
7#include "quicktest_p.h"
8#include <QtTest/qtestcase.h>
9#include <QtTest/qtestsystem.h>
10#include <QtTest/private/qtestblacklist_p.h>
11#include <QtTest/private/qtestresult_p.h>
12#include <QtTest/private/qtesttable_p.h>
13#include <QtTest/private/qtestlog_p.h>
14#include "qtestoptions_p.h"
15#include <QtTest/qbenchmark.h>
16#include <QtTest/private/qbenchmark_p.h>
17#include <QtCore/qset.h>
18#include <QtCore/qmap.h>
19#include <QtCore/qbytearray.h>
20#include <QtCore/qcoreapplication.h>
21#include <QtCore/qdatetime.h>
22#include <QtCore/qdebug.h>
23#include <QtCore/QUrl>
24#include <QtCore/QDir>
25#if QT_CONFIG(regularexpression)
26#include <QtCore/qregularexpression.h>
27#endif
28#include <QtQuick/qquickwindow.h>
29#include <QtGui/qvector3d.h>
30#include <QtGui/qimagewriter.h>
31#include <QtQml/private/qqmlglobal_p.h>
32#include <QtQml/QQmlEngine>
33#include <QtQml/QQmlContext>
34#include <private/qv4qobjectwrapper_p.h>
35
36#include <algorithm>
37
38QT_BEGIN_NAMESPACE
39
40static const char *globalProgramName = nullptr;
41static bool loggingStarted = false;
42static QBenchmarkGlobalData globalBenchmarkData;
43
44class Q_QUICK_TEST_EXPORT QuickTestImageObject : public QObject
45{
46 Q_OBJECT
47
48 Q_PROPERTY(int width READ width CONSTANT)
49 Q_PROPERTY(int height READ height CONSTANT)
50 Q_PROPERTY(QSize size READ size CONSTANT)
51
52public:
53 QuickTestImageObject(const QImage& img, QObject *parent = nullptr)
54 : QObject(parent)
55 , m_image(img)
56 {
57 }
58
59 ~QuickTestImageObject() {}
60
61public Q_SLOTS:
62 int red(int x, int y) const
63 {
64 return pixel(x, y).value<QColor>().red();
65 }
66
67 int green(int x, int y) const
68 {
69 return pixel(x, y).value<QColor>().green();
70 }
71
72 int blue(int x, int y) const
73 {
74 return pixel(x, y).value<QColor>().blue();
75 }
76
77 int alpha(int x, int y) const
78 {
79 return pixel(x, y).value<QColor>().alpha();
80 }
81
82 QVariant pixel(int x, int y) const
83 {
84 if (m_image.isNull()
85 || x >= m_image.width()
86 || y >= m_image.height()
87 || x < 0
88 || y < 0
89 || x * y >= m_image.width() * m_image.height())
90 return QVariant();
91
92 return QColor::fromRgba(rgba: m_image.pixel(pt: QPoint(x, y)));
93 }
94
95 bool equals(QuickTestImageObject *other) const
96 {
97 if (!other)
98 return m_image.isNull();
99
100 return m_image == other->m_image;
101 }
102
103 void save(const QString &filePath)
104 {
105 QImageWriter writer(filePath);
106 if (!writer.write(image: m_image)) {
107 QQmlEngine *engine = qmlContext(this)->engine();
108 QV4::ExecutionEngine *v4 = engine->handle();
109 v4->throwError(QStringLiteral("Can't save to %1: %2").arg(args: filePath, args: writer.errorString()));
110 }
111 }
112
113public:
114 int width() const
115 {
116 return m_image.width();
117 }
118
119 int height() const
120 {
121 return m_image.height();
122 }
123
124 QSize size() const
125 {
126 return m_image.size();
127 }
128
129private:
130 QImage m_image;
131};
132
133class QuickTestResultPrivate
134{
135public:
136 QuickTestResultPrivate()
137 : table(nullptr)
138 , benchmarkIter(nullptr)
139 , benchmarkData(nullptr)
140 , iterCount(0)
141 {
142 }
143 ~QuickTestResultPrivate()
144 {
145 delete table;
146 delete benchmarkIter;
147 delete benchmarkData;
148 }
149
150 QByteArray intern(const QString &str);
151
152 QString testCaseName;
153 QString functionName;
154 QSet<QByteArray> internedStrings;
155 QTestTable *table;
156 QTest::QBenchmarkIterationController *benchmarkIter;
157 QBenchmarkTestMethodData *benchmarkData;
158 int iterCount;
159 QList<QList<QBenchmarkResult>> resultsList;
160};
161
162QByteArray QuickTestResultPrivate::intern(const QString &str)
163{
164 QByteArray bstr = str.toUtf8();
165 return *(internedStrings.insert(value: bstr));
166}
167
168QuickTestResult::QuickTestResult(QObject *parent)
169 : QObject(parent), d_ptr(new QuickTestResultPrivate)
170{
171 if (!QBenchmarkGlobalData::current)
172 QBenchmarkGlobalData::current = &globalBenchmarkData;
173}
174
175QuickTestResult::~QuickTestResult()
176{
177}
178
179/*!
180 \qmlproperty string TestResult::testCaseName
181
182 This property defines the name of current TestCase element
183 that is running test cases.
184
185 \sa functionName
186*/
187QString QuickTestResult::testCaseName() const
188{
189 Q_D(const QuickTestResult);
190 return d->testCaseName;
191}
192
193void QuickTestResult::setTestCaseName(const QString &name)
194{
195 Q_D(QuickTestResult);
196 d->testCaseName = name;
197 emit testCaseNameChanged();
198}
199
200/*!
201 \qmlproperty string TestResult::functionName
202
203 This property defines the name of current test function
204 within a TestCase element that is running. If this string is
205 empty, then no function is currently running.
206
207 \sa testCaseName
208*/
209QString QuickTestResult::functionName() const
210{
211 Q_D(const QuickTestResult);
212 return d->functionName;
213}
214
215void QuickTestResult::setFunctionName(const QString &name)
216{
217 Q_D(QuickTestResult);
218 if (!name.isEmpty()) {
219 if (d->testCaseName.isEmpty()) {
220 QTestResult::setCurrentTestFunction
221 (d->intern(str: name).constData());
222 } else {
223 QString fullName = d->testCaseName + QLatin1String("::") + name;
224 QTestResult::setCurrentTestFunction
225 (d->intern(str: fullName).constData());
226 QTestPrivate::checkBlackLists(slot: fullName.toUtf8().constData(), data: nullptr);
227 }
228 } else {
229 QTestResult::setCurrentTestFunction(nullptr);
230 }
231 d->functionName = name;
232 emit functionNameChanged();
233}
234
235/*!
236 \qmlproperty string TestResult::dataTag
237
238 This property defines the tag for the current row in a
239 data-driven test, or an empty string if not a data-driven test.
240*/
241QString QuickTestResult::dataTag() const
242{
243 const char *tag = QTestResult::currentDataTag();
244 if (tag)
245 return QString::fromUtf8(utf8: tag);
246 else
247 return QString();
248}
249
250void QuickTestResult::setDataTag(const QString &tag)
251{
252 if (!tag.isEmpty()) {
253 QTestData *data = &(QTest::newRow(dataTag: tag.toUtf8().constData()));
254 QTestResult::setCurrentTestData(data);
255 QTestPrivate::checkBlackLists(slot: (testCaseName() + QLatin1String("::") + functionName()).toUtf8().constData(), data: tag.toUtf8().constData());
256 emit dataTagChanged();
257 } else {
258 QTestResult::setCurrentTestData(nullptr);
259 }
260}
261
262/*!
263 \qmlproperty bool TestResult::failed
264
265 This property returns true if the current test function (or
266 current test data row for a data-driven test) has failed;
267 false otherwise. The fail state is reset when functionName
268 is changed or finishTestDataCleanup() is called.
269
270 \sa skipped
271*/
272bool QuickTestResult::isFailed() const
273{
274 return QTestResult::currentTestFailed();
275}
276
277/*!
278 \qmlproperty bool TestResult::skipped
279
280 This property returns true if the current test function was
281 marked as skipped; false otherwise.
282
283 \sa failed
284*/
285bool QuickTestResult::isSkipped() const
286{
287 return QTestResult::skipCurrentTest();
288}
289
290void QuickTestResult::setSkipped(bool skip)
291{
292 QTestResult::setSkipCurrentTest(skip);
293 if (!skip)
294 QTestResult::setBlacklistCurrentTest(false);
295 emit skippedChanged();
296}
297
298/*!
299 \qmlproperty int TestResult::passCount
300
301 This property returns the number of tests that have passed.
302
303 \sa failCount, skipCount
304*/
305int QuickTestResult::passCount() const
306{
307 return QTestLog::passCount();
308}
309
310/*!
311 \qmlproperty int TestResult::failCount
312
313 This property returns the number of tests that have failed.
314
315 \sa passCount, skipCount
316*/
317int QuickTestResult::failCount() const
318{
319 return QTestLog::failCount();
320}
321
322/*!
323 \qmlproperty int TestResult::skipCount
324
325 This property returns the number of tests that have been skipped.
326
327 \sa passCount, failCount
328*/
329int QuickTestResult::skipCount() const
330{
331 return QTestLog::skipCount();
332}
333
334/*!
335 \qmlproperty list<string> TestResult::functionsToRun
336
337 This property returns the list of function names to be run.
338*/
339QStringList QuickTestResult::functionsToRun() const
340{
341 return QTest::testFunctions;
342}
343
344/*!
345 \qmlproperty list<string> TestResult::tagsToRun
346
347 This property returns the list of test function's data tags to be run
348*/
349QStringList QuickTestResult::tagsToRun() const
350{
351 return QTest::testTags;
352}
353
354/*!
355 \qmlmethod TestResult::reset()
356
357 Resets all pass/fail/skip counters and prepare for testing.
358*/
359void QuickTestResult::reset()
360{
361 if (!globalProgramName) // Only if run via qmlviewer.
362 QTestResult::reset();
363}
364
365/*!
366 \qmlmethod TestResult::startLogging()
367
368 Starts logging to the test output stream and writes the
369 test header.
370
371 \sa stopLogging()
372*/
373void QuickTestResult::startLogging()
374{
375 // The program name is used for logging headers and footers if it
376 // is set. Otherwise the test case name is used.
377 if (loggingStarted)
378 return;
379 QTestLog::startLogging();
380 loggingStarted = true;
381}
382
383/*!
384 \qmlmethod TestResult::stopLogging()
385
386 Writes the test footer to the test output stream and then stops logging.
387
388 \sa startLogging()
389*/
390void QuickTestResult::stopLogging()
391{
392 Q_D(QuickTestResult);
393 if (globalProgramName)
394 return; // Logging will be stopped by setProgramName(0).
395 QTestResult::setCurrentTestObject(d->intern(str: d->testCaseName).constData());
396 QTestLog::stopLogging();
397}
398
399void QuickTestResult::initTestTable()
400{
401 Q_D(QuickTestResult);
402 delete d->table;
403 d->table = new QTestTable;
404 //qmltest does not really need the column for data driven test
405 //add this to avoid warnings.
406 d->table->addColumn(elementType: qMetaTypeId<QString>(), elementName: "qmltest_dummy_data_column");
407}
408
409void QuickTestResult::clearTestTable()
410{
411 Q_D(QuickTestResult);
412 delete d->table;
413 d->table = nullptr;
414}
415
416void QuickTestResult::finishTestData()
417{
418 QTestResult::finishedCurrentTestData();
419}
420
421void QuickTestResult::finishTestDataCleanup()
422{
423 QTestResult::finishedCurrentTestDataCleanup();
424}
425
426void QuickTestResult::finishTestFunction()
427{
428 QTestResult::finishedCurrentTestFunction();
429}
430
431static QString qtestFixUrl(const QUrl &location)
432{
433 if (location.isLocalFile()) // Use QUrl's logic for Windows drive letters.
434 return QDir::toNativeSeparators(pathName: location.toLocalFile());
435 return location.toString();
436}
437
438void QuickTestResult::fail
439 (const QString &message, const QUrl &location, int line)
440{
441 QTestResult::addFailure(message: message.toUtf8().constData(),
442 file: qtestFixUrl(location).toLatin1().constData(), line);
443}
444
445bool QuickTestResult::verify
446 (bool success, const QString &message, const QUrl &location, int line)
447{
448 if (!success && message.isEmpty()) {
449 return QTestResult::verify
450 (statement: success, statementStr: "verify()", extraInfo: "",
451 file: qtestFixUrl(location).toLatin1().constData(), line);
452 } else {
453 return QTestResult::verify
454 (statement: success, statementStr: message.toUtf8().constData(), extraInfo: "",
455 file: qtestFixUrl(location).toLatin1().constData(), line);
456 }
457}
458
459bool QuickTestResult::fuzzyCompare(const QVariant &actual, const QVariant &expected, qreal delta)
460{
461 if (actual.userType() == QMetaType::QColor || expected.userType() == QMetaType::QColor) {
462 if (!actual.canConvert(targetType: QMetaType(QMetaType::QColor))
463 || !expected.canConvert(targetType: QMetaType(QMetaType::QColor))) {
464 return false;
465 }
466
467 //fuzzy color comparison
468 QColor act;
469 QColor exp;
470 bool ok(false);
471
472 QVariant var = QQml_colorProvider()->colorFromString(actual.toString(), &ok);
473 if (!ok)
474 return false;
475 act = var.value<QColor>();
476
477 var = QQml_colorProvider()->colorFromString(expected.toString(), &ok);
478 if (!ok)
479 return false;
480 exp = var.value<QColor>();
481
482 return ( qAbs(t: act.red() - exp.red()) <= delta
483 && qAbs(t: act.green() - exp.green()) <= delta
484 && qAbs(t: act.blue() - exp.blue()) <= delta
485 && qAbs(t: act.alpha() - exp.alpha()) <= delta);
486 } else {
487 //number comparison
488 bool ok = true;
489 qreal act = actual.toFloat(ok: &ok);
490 if (!ok)
491 return false;
492
493 qreal exp = expected.toFloat(ok: &ok);
494 if (!ok)
495 return false;
496
497 return (qAbs(t: act - exp) <= delta);
498 }
499
500 return false;
501}
502
503void QuickTestResult::stringify(QQmlV4Function *args)
504{
505 if (args->length() < 1)
506 args->setReturnValue(QV4::Encode::null());
507
508 QV4::Scope scope(args->v4engine());
509 QV4::ScopedValue value(scope, (*args)[0]);
510
511 QString result;
512
513 //Check for Object Type
514 if (value->isObject()
515 && !value->as<QV4::FunctionObject>()
516 && !value->as<QV4::ArrayObject>()) {
517 QVariant v = QV4::ExecutionEngine::toVariant(value, typeHint: QMetaType {});
518 if (v.isValid()) {
519 switch (v.userType()) {
520 case QMetaType::QVector3D:
521 {
522 QVector3D v3d = v.value<QVector3D>();
523 result = QString::fromLatin1(ba: "Qt.vector3d(%1, %2, %3)").arg(a: v3d.x()).arg(a: v3d.y()).arg(a: v3d.z());
524 break;
525 }
526 case QMetaType::QUrl:
527 {
528 QUrl url = v.value<QUrl>();
529 result = QString::fromLatin1(ba: "Qt.url(%1)").arg(a: url.toString());
530 break;
531 }
532 case QMetaType::QDateTime:
533 {
534 QDateTime dt = v.value<QDateTime>();
535 result = dt.toString(format: Qt::ISODateWithMs);
536 break;
537 }
538 default:
539 result = v.toString();
540 }
541
542 } else {
543 result = QLatin1String("Object");
544 }
545 }
546
547 if (result.isEmpty()) {
548 QString tmp = value->toQStringNoThrow();
549 if (value->as<QV4::ArrayObject>())
550 result += QLatin1Char('[') + tmp + QLatin1Char(']');
551 else
552 result.append(s: tmp);
553 }
554
555 args->setReturnValue(QV4::Encode(args->v4engine()->newString(s: result)));
556}
557
558bool QuickTestResult::compare
559 (bool success, const QString &message,
560 const QVariant &val1, const QVariant &val2,
561 const QUrl &location, int line)
562{
563 return QTestResult::compare
564 (success, failureMsg: message.toUtf8().constData(),
565 val1: QTest::toString(val1.toString().toLatin1().constData()),
566 val2: QTest::toString(val2.toString().toLatin1().constData()),
567 actual: "", expected: "",
568 file: qtestFixUrl(location).toLatin1().constData(), line);
569}
570
571void QuickTestResult::skip
572 (const QString &message, const QUrl &location, int line)
573{
574 QTestResult::addSkip(message: message.toUtf8().constData(),
575 file: qtestFixUrl(location).toLatin1().constData(), line);
576 QTestResult::setSkipCurrentTest(true);
577}
578
579bool QuickTestResult::expectFail
580 (const QString &tag, const QString &comment, const QUrl &location, int line)
581{
582 return QTestResult::expectFail
583 (dataIndex: tag.toLatin1().constData(),
584 comment: QTest::toString(comment.toLatin1().constData()),
585 mode: QTest::Abort, file: qtestFixUrl(location).toLatin1().constData(), line);
586}
587
588bool QuickTestResult::expectFailContinue
589 (const QString &tag, const QString &comment, const QUrl &location, int line)
590{
591 return QTestResult::expectFail
592 (dataIndex: tag.toLatin1().constData(),
593 comment: QTest::toString(comment.toUtf8().constData()),
594 mode: QTest::Continue, file: qtestFixUrl(location).toLatin1().constData(), line);
595}
596
597void QuickTestResult::warn(const QString &message, const QUrl &location, int line)
598{
599 QTestLog::warn(msg: message.toUtf8().constData(), file: qtestFixUrl(location).toLatin1().constData(), line);
600}
601
602void QuickTestResult::ignoreWarning(const QJSValue &message)
603{
604 if (message.isRegExp()) {
605#if QT_CONFIG(regularexpression)
606 QTestLog::ignoreMessage(type: QtWarningMsg, expression: qjsvalue_cast<QRegularExpression>(value: message));
607#endif
608 } else {
609 QTestLog::ignoreMessage(type: QtWarningMsg, msg: message.toString().toUtf8());
610 }
611}
612
613void QuickTestResult::failOnWarning(const QJSValue &message)
614{
615 if (message.isRegExp()) {
616#if QT_CONFIG(regularexpression)
617 QTestLog::failOnWarning(expression: qjsvalue_cast<QRegularExpression>(value: message));
618#endif
619 } else {
620 QTestLog::failOnWarning(msg: message.toString().toUtf8());
621 }
622}
623
624void QuickTestResult::wait(int ms)
625{
626 QTest::qWait(ms);
627}
628
629void QuickTestResult::sleep(int ms)
630{
631 QTest::qSleep(ms);
632}
633
634bool QuickTestResult::waitForRendering(QQuickItem *item, int timeout)
635{
636 Q_ASSERT(item);
637
638 return qWaitForSignal(obj: item->window(), SIGNAL(frameSwapped()), timeout);
639}
640
641void QuickTestResult::startMeasurement()
642{
643 Q_D(QuickTestResult);
644 delete d->benchmarkData;
645 d->benchmarkData = new QBenchmarkTestMethodData();
646 QBenchmarkTestMethodData::current = d->benchmarkData;
647 d->iterCount = (QBenchmarkGlobalData::current->measurer->needsWarmupIteration()) ? -1 : 0;
648 d->resultsList.clear();
649}
650
651void QuickTestResult::beginDataRun()
652{
653 QBenchmarkTestMethodData::current->beginDataRun();
654}
655
656void QuickTestResult::endDataRun()
657{
658 Q_D(QuickTestResult);
659 QBenchmarkTestMethodData::current->endDataRun();
660 const QList<QBenchmarkResult> &results = QBenchmarkTestMethodData::current->results;
661 if (results.isEmpty())
662 return; // shouldn't happen
663 if (d->iterCount > -1) // iteration -1 is the warmup iteration.
664 d->resultsList.append(t: results);
665
666 if (QBenchmarkGlobalData::current->verboseOutput) {
667 if (d->iterCount == -1) {
668 qDebug() << "warmup stage result :" << results.first().measurement.value;
669 } else {
670 qDebug() << "accumulation stage result:" << results.first().measurement.value;
671 }
672 }
673}
674
675bool QuickTestResult::measurementAccepted()
676{
677 return QBenchmarkTestMethodData::current->resultsAccepted();
678}
679
680static QList<QBenchmarkResult> qMedian(const QList<QList<QBenchmarkResult>> &container)
681{
682 const int count = container.size();
683 if (count == 0)
684 return {};
685
686 if (count == 1)
687 return container.at(i: 0);
688
689 QList<QList<QBenchmarkResult>> containerCopy = container;
690 std::sort(first: containerCopy.begin(), last: containerCopy.end(),
691 comp: [](const QList<QBenchmarkResult> &a, const QList<QBenchmarkResult> &b) {
692 return a.first() < b.first();
693 });
694
695 const int middle = count / 2;
696
697 // ### handle even-sized containers here by doing an aritmetic mean of the two middle items.
698 return containerCopy.at(i: middle);
699}
700
701bool QuickTestResult::needsMoreMeasurements()
702{
703 Q_D(QuickTestResult);
704 ++(d->iterCount);
705 if (d->iterCount < QBenchmarkGlobalData::current->adjustMedianIterationCount())
706 return true;
707 if (QBenchmarkTestMethodData::current->resultsAccepted())
708 QTestLog::addBenchmarkResults(result: qMedian(container: d->resultsList));
709 return false;
710}
711
712void QuickTestResult::startBenchmark(RunMode runMode, const QString &tag)
713{
714 QBenchmarkTestMethodData::current->results = {};
715 QBenchmarkTestMethodData::current->resultAccepted = false;
716 QBenchmarkGlobalData::current->context.tag = tag;
717 QBenchmarkGlobalData::current->context.slotName = functionName();
718
719 Q_D(QuickTestResult);
720 delete d->benchmarkIter;
721 d->benchmarkIter = new QTest::QBenchmarkIterationController
722 (QTest::QBenchmarkIterationController::RunMode(runMode));
723}
724
725bool QuickTestResult::isBenchmarkDone() const
726{
727 Q_D(const QuickTestResult);
728 if (d->benchmarkIter)
729 return d->benchmarkIter->isDone();
730 else
731 return true;
732}
733
734void QuickTestResult::nextBenchmark()
735{
736 Q_D(QuickTestResult);
737 if (d->benchmarkIter)
738 d->benchmarkIter->next();
739}
740
741void QuickTestResult::stopBenchmark()
742{
743 Q_D(QuickTestResult);
744 delete d->benchmarkIter;
745 d->benchmarkIter = nullptr;
746}
747
748QObject *QuickTestResult::grabImage(QQuickItem *item)
749{
750 if (item && item->window()) {
751 QQuickWindow *window = item->window();
752 QImage grabbed = window->grabWindow();
753 const auto dpi = grabbed.devicePixelRatio();
754 QRectF rf(item->x() * dpi, item->y() * dpi, item->width() * dpi, item->height() * dpi);
755 rf = rf.intersected(r: QRectF(0, 0, grabbed.width(), grabbed.height()));
756 QObject *o = new QuickTestImageObject(grabbed.copy(rect: rf.toAlignedRect()));
757 QQmlEngine::setContextForObject(o, qmlContext(this));
758 return o;
759 }
760 return nullptr;
761}
762
763QObject *QuickTestResult::findChild(QObject *parent, const QString &objectName)
764{
765 return parent ? parent->findChild<QObject*>(aName: objectName) : 0;
766}
767
768bool QuickTestResult::isPolishScheduled(QObject *itemOrWindow) const
769{
770 if (auto item = qobject_cast<QQuickItem*>(o: itemOrWindow))
771 return QQuickTest::qIsPolishScheduled(item);
772
773 if (auto window = qobject_cast<QQuickWindow*>(object: itemOrWindow))
774 return QQuickTest::qIsPolishScheduled(window);
775
776 qmlWarning(me: this) << "isPolishScheduled() expects either an Item or Window, but got"
777 << QDebug::toString(object&: itemOrWindow);
778 return false;
779}
780
781bool QuickTestResult::waitForPolish(QObject *itemOrWindow, int timeout) const
782{
783 if (auto item = qobject_cast<QQuickItem*>(o: itemOrWindow))
784 return QQuickTest::qWaitForPolish(item, timeout);
785
786 if (auto window = qobject_cast<QQuickWindow*>(object: itemOrWindow))
787 return QQuickTest::qWaitForPolish(window, timeout);
788
789 qmlWarning(me: this) << "waitForItemPolish() expects either an Item or Window, but got"
790 << QDebug::toString(object&: itemOrWindow);
791 return false;
792}
793
794namespace QTest {
795 void qtest_qParseArgs(int argc, char *argv[], bool qml);
796};
797
798void QuickTestResult::parseArgs(int argc, char *argv[])
799{
800 if (!QBenchmarkGlobalData::current)
801 QBenchmarkGlobalData::current = &globalBenchmarkData;
802 QTest::qtest_qParseArgs(argc, argv, qml: true);
803}
804
805void QuickTestResult::setProgramName(const char *name)
806{
807 if (name) {
808 QTestPrivate::parseBlackList();
809 QTestResult::reset();
810 } else if (!name && loggingStarted) {
811 QTestResult::setCurrentTestObject(globalProgramName);
812 QTestLog::stopLogging();
813 QTestResult::setCurrentTestObject(nullptr);
814 }
815 globalProgramName = name;
816 QTestResult::setCurrentTestObject(globalProgramName);
817}
818
819void QuickTestResult::setCurrentAppname(const char *appname)
820{
821 QTestResult::setCurrentAppName(appname);
822}
823
824int QuickTestResult::exitCode()
825{
826#if defined(QTEST_NOEXITCODE)
827 return 0;
828#else
829 // make sure our exit code is never going above 127
830 // since that could wrap and indicate 0 test fails
831 return qMin(a: QTestLog::failCount(), b: 127);
832#endif
833}
834
835QT_END_NAMESPACE
836
837#include "quicktestresult.moc"
838#include "moc_quicktestresult_p.cpp"
839

source code of qtdeclarative/src/qmltest/quicktestresult.cpp