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_QMLTEST_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 if (QTestPrivate::checkBlackLists(slot: fullName.toUtf8().constData(), data: nullptr))
227 QTestResult::setBlacklistCurrentTest(true);
228 }
229 } else {
230 QTestResult::setCurrentTestFunction(nullptr);
231 }
232 d->functionName = name;
233 emit functionNameChanged();
234}
235
236/*!
237 \qmlproperty string TestResult::dataTag
238
239 This property defines the tag for the current row in a
240 data-driven test, or an empty string if not a data-driven test.
241*/
242QString QuickTestResult::dataTag() const
243{
244 const char *tag = QTestResult::currentDataTag();
245 if (tag)
246 return QString::fromUtf8(utf8: tag);
247 else
248 return QString();
249}
250
251void QuickTestResult::setDataTag(const QString &tag)
252{
253 if (!tag.isEmpty()) {
254 QTestData *data = &(QTest::newRow(dataTag: tag.toUtf8().constData()));
255 QTestResult::setCurrentTestData(data);
256 if (QTestPrivate::checkBlackLists(slot: (testCaseName() + QLatin1String("::")
257 + functionName()).toUtf8().constData(), data: tag.toUtf8().constData())) {
258 QTestResult::setBlacklistCurrentTest(true);
259 }
260 emit dataTagChanged();
261 } else {
262 QTestResult::setCurrentTestData(nullptr);
263 }
264}
265
266/*!
267 \qmlproperty bool TestResult::failed
268
269 This property returns true if the current test function (or
270 current test data row for a data-driven test) has failed;
271 false otherwise. The fail state is reset when functionName
272 is changed or finishTestDataCleanup() is called.
273
274 \sa skipped
275*/
276bool QuickTestResult::isFailed() const
277{
278 return QTestResult::currentTestFailed();
279}
280
281/*!
282 \qmlproperty bool TestResult::skipped
283
284 This property returns true if the current test function was
285 marked as skipped; false otherwise.
286
287 \sa failed
288*/
289bool QuickTestResult::isSkipped() const
290{
291 return QTestResult::skipCurrentTest();
292}
293
294void QuickTestResult::setSkipped(bool skip)
295{
296 QTestResult::setSkipCurrentTest(skip);
297 if (!skip)
298 QTestResult::setBlacklistCurrentTest(false);
299 emit skippedChanged();
300}
301
302/*!
303 \qmlproperty int TestResult::passCount
304
305 This property returns the number of tests that have passed.
306
307 \sa failCount, skipCount
308*/
309int QuickTestResult::passCount() const
310{
311 return QTestLog::passCount();
312}
313
314/*!
315 \qmlproperty int TestResult::failCount
316
317 This property returns the number of tests that have failed.
318
319 \sa passCount, skipCount
320*/
321int QuickTestResult::failCount() const
322{
323 return QTestLog::failCount();
324}
325
326/*!
327 \qmlproperty int TestResult::skipCount
328
329 This property returns the number of tests that have been skipped.
330
331 \sa passCount, failCount
332*/
333int QuickTestResult::skipCount() const
334{
335 return QTestLog::skipCount();
336}
337
338/*!
339 \qmlproperty list<string> TestResult::functionsToRun
340
341 This property returns the list of function names to be run.
342*/
343QStringList QuickTestResult::functionsToRun() const
344{
345 return QTest::testFunctions;
346}
347
348/*!
349 \qmlproperty list<string> TestResult::tagsToRun
350
351 This property returns the list of test function's data tags to be run
352*/
353QStringList QuickTestResult::tagsToRun() const
354{
355 return QTest::testTags;
356}
357
358/*!
359 \qmlmethod TestResult::reset()
360
361 Resets all pass/fail/skip counters and prepare for testing.
362*/
363void QuickTestResult::reset()
364{
365 if (!globalProgramName) // Only if run via qmlviewer.
366 QTestResult::reset();
367}
368
369/*!
370 \qmlmethod TestResult::startLogging()
371
372 Starts logging to the test output stream and writes the
373 test header.
374
375 \sa stopLogging()
376*/
377void QuickTestResult::startLogging()
378{
379 // The program name is used for logging headers and footers if it
380 // is set. Otherwise the test case name is used.
381 if (loggingStarted)
382 return;
383 QTestLog::startLogging();
384 loggingStarted = true;
385}
386
387/*!
388 \qmlmethod TestResult::stopLogging()
389
390 Writes the test footer to the test output stream and then stops logging.
391
392 \sa startLogging()
393*/
394void QuickTestResult::stopLogging()
395{
396 Q_D(QuickTestResult);
397 if (globalProgramName)
398 return; // Logging will be stopped by setProgramName(0).
399 QTestResult::setCurrentTestObject(d->intern(str: d->testCaseName).constData());
400 QTestLog::stopLogging();
401}
402
403void QuickTestResult::initTestTable()
404{
405 Q_D(QuickTestResult);
406 delete d->table;
407 d->table = new QTestTable;
408 //qmltest does not really need the column for data driven test
409 //add this to avoid warnings.
410 d->table->addColumn(elementType: qMetaTypeId<QString>(), elementName: "qmltest_dummy_data_column");
411}
412
413void QuickTestResult::clearTestTable()
414{
415 Q_D(QuickTestResult);
416 delete d->table;
417 d->table = nullptr;
418}
419
420void QuickTestResult::finishTestData()
421{
422 QTestResult::finishedCurrentTestData();
423}
424
425void QuickTestResult::finishTestDataCleanup()
426{
427 QTestResult::finishedCurrentTestDataCleanup();
428}
429
430void QuickTestResult::finishTestFunction()
431{
432 QTestResult::finishedCurrentTestFunction();
433}
434
435static QString qtestFixUrl(const QUrl &location)
436{
437 if (location.isLocalFile()) // Use QUrl's logic for Windows drive letters.
438 return QDir::toNativeSeparators(pathName: location.toLocalFile());
439 return location.toString();
440}
441
442void QuickTestResult::fail
443 (const QString &message, const QUrl &location, int line)
444{
445 QTestResult::addFailure(message: message.toUtf8().constData(),
446 file: qtestFixUrl(location).toLatin1().constData(), line);
447}
448
449bool QuickTestResult::verify
450 (bool success, const QString &message, const QUrl &location, int line)
451{
452 if (!success && message.isEmpty()) {
453 return QTestResult::verify
454 (statement: success, statementStr: "verify()", extraInfo: "",
455 file: qtestFixUrl(location).toLatin1().constData(), line);
456 } else {
457 return QTestResult::verify
458 (statement: success, statementStr: message.toUtf8().constData(), extraInfo: "",
459 file: qtestFixUrl(location).toLatin1().constData(), line);
460 }
461}
462
463bool QuickTestResult::fuzzyCompare(const QVariant &actual, const QVariant &expected, qreal delta)
464{
465 if (actual.userType() == QMetaType::QColor || expected.userType() == QMetaType::QColor) {
466 if (!actual.canConvert(targetType: QMetaType(QMetaType::QColor))
467 || !expected.canConvert(targetType: QMetaType(QMetaType::QColor))) {
468 return false;
469 }
470
471 //fuzzy color comparison
472 QColor act;
473 QColor exp;
474 bool ok(false);
475
476 QVariant var = QQml_colorProvider()->colorFromString(actual.toString(), &ok);
477 if (!ok)
478 return false;
479 act = var.value<QColor>();
480
481 var = QQml_colorProvider()->colorFromString(expected.toString(), &ok);
482 if (!ok)
483 return false;
484 exp = var.value<QColor>();
485
486 return ( qAbs(t: act.red() - exp.red()) <= delta
487 && qAbs(t: act.green() - exp.green()) <= delta
488 && qAbs(t: act.blue() - exp.blue()) <= delta
489 && qAbs(t: act.alpha() - exp.alpha()) <= delta);
490 } else {
491 //number comparison
492 bool ok = true;
493 qreal act = actual.toFloat(ok: &ok);
494 if (!ok)
495 return false;
496
497 qreal exp = expected.toFloat(ok: &ok);
498 if (!ok)
499 return false;
500
501 return (qAbs(t: act - exp) <= delta);
502 }
503
504 return false;
505}
506
507void QuickTestResult::stringify(QQmlV4FunctionPtr args)
508{
509 if (args->length() < 1)
510 args->setReturnValue(QV4::Encode::null());
511
512 QV4::Scope scope(args->v4engine());
513 QV4::ScopedValue value(scope, (*args)[0]);
514
515 QString result;
516
517 //Check for Object Type
518 if (value->isObject()
519 && !value->as<QV4::FunctionObject>()
520 && !value->as<QV4::ArrayObject>()) {
521 QVariant v = QV4::ExecutionEngine::toVariant(value, typeHint: QMetaType {});
522 if (v.isValid()) {
523 switch (v.userType()) {
524 case QMetaType::QVector3D:
525 {
526 QVector3D v3d = v.value<QVector3D>();
527 result = QString::fromLatin1(ba: "Qt.vector3d(%1, %2, %3)").arg(a: v3d.x()).arg(a: v3d.y()).arg(a: v3d.z());
528 break;
529 }
530 case QMetaType::QUrl:
531 {
532 QUrl url = v.value<QUrl>();
533 result = QString::fromLatin1(ba: "Qt.url(%1)").arg(a: url.toString());
534 break;
535 }
536 case QMetaType::QDateTime:
537 {
538 QDateTime dt = v.value<QDateTime>();
539 result = dt.toString(format: Qt::ISODateWithMs);
540 break;
541 }
542 default:
543 result = v.toString();
544 }
545
546 } else {
547 result = QLatin1String("Object");
548 }
549 }
550
551 if (result.isEmpty()) {
552 QString tmp = value->toQStringNoThrow();
553 if (value->as<QV4::ArrayObject>())
554 result += QLatin1Char('[') + tmp + QLatin1Char(']');
555 else
556 result.append(s: tmp);
557 }
558
559 args->setReturnValue(QV4::Encode(args->v4engine()->newString(s: result)));
560}
561
562bool QuickTestResult::compare
563 (bool success, const QString &message,
564 const QVariant &val1, const QVariant &val2,
565 const QUrl &location, int line)
566{
567 return QTestResult::compare
568 (success, failureMsg: message.toUtf8().constData(),
569 val1: QTest::toString(val1.toString().toLatin1().constData()),
570 val2: QTest::toString(val2.toString().toLatin1().constData()),
571 actual: "", expected: "",
572 file: qtestFixUrl(location).toLatin1().constData(), line);
573}
574
575void QuickTestResult::skip
576 (const QString &message, const QUrl &location, int line)
577{
578 QTestResult::addSkip(message: message.toUtf8().constData(),
579 file: qtestFixUrl(location).toLatin1().constData(), line);
580 QTestResult::setSkipCurrentTest(true);
581}
582
583bool QuickTestResult::expectFail
584 (const QString &tag, const QString &comment, const QUrl &location, int line)
585{
586 return QTestResult::expectFail
587 (dataIndex: tag.toLatin1().constData(),
588 comment: QTest::toString(comment.toLatin1().constData()),
589 mode: QTest::Abort, file: qtestFixUrl(location).toLatin1().constData(), line);
590}
591
592bool QuickTestResult::expectFailContinue
593 (const QString &tag, const QString &comment, const QUrl &location, int line)
594{
595 return QTestResult::expectFail
596 (dataIndex: tag.toLatin1().constData(),
597 comment: QTest::toString(comment.toUtf8().constData()),
598 mode: QTest::Continue, file: qtestFixUrl(location).toLatin1().constData(), line);
599}
600
601void QuickTestResult::warn(const QString &message, const QUrl &location, int line)
602{
603 QTestLog::warn(msg: message.toUtf8().constData(), file: qtestFixUrl(location).toLatin1().constData(), line);
604}
605
606void QuickTestResult::ignoreWarning(const QJSValue &message)
607{
608 if (message.isRegExp()) {
609#if QT_CONFIG(regularexpression)
610 QTestLog::ignoreMessage(type: QtWarningMsg, expression: qjsvalue_cast<QRegularExpression>(value: message));
611#endif
612 } else {
613 QTestLog::ignoreMessage(type: QtWarningMsg, msg: message.toString().toUtf8());
614 }
615}
616
617void QuickTestResult::failOnWarning(const QJSValue &message)
618{
619 if (message.isRegExp()) {
620#if QT_CONFIG(regularexpression)
621 QTestLog::failOnWarning(expression: qjsvalue_cast<QRegularExpression>(value: message));
622#endif
623 } else {
624 QTestLog::failOnWarning(msg: message.toString().toUtf8());
625 }
626}
627
628void QuickTestResult::wait(int ms)
629{
630 QTest::qWait(ms);
631}
632
633void QuickTestResult::sleep(int ms)
634{
635 QTest::qSleep(ms);
636}
637
638bool QuickTestResult::waitForRendering(QQuickItem *item, int timeout)
639{
640 Q_ASSERT(item);
641
642 return qWaitForSignal(obj: item->window(), SIGNAL(frameSwapped()), timeout);
643}
644
645void QuickTestResult::startMeasurement()
646{
647 Q_D(QuickTestResult);
648 delete d->benchmarkData;
649 d->benchmarkData = new QBenchmarkTestMethodData();
650 QBenchmarkTestMethodData::current = d->benchmarkData;
651 d->iterCount = (QBenchmarkGlobalData::current->measurer->needsWarmupIteration()) ? -1 : 0;
652 d->resultsList.clear();
653}
654
655void QuickTestResult::beginDataRun()
656{
657 QBenchmarkTestMethodData::current->beginDataRun();
658}
659
660void QuickTestResult::endDataRun()
661{
662 Q_D(QuickTestResult);
663 QBenchmarkTestMethodData::current->endDataRun();
664 const QList<QBenchmarkResult> &results = QBenchmarkTestMethodData::current->results;
665 if (results.isEmpty())
666 return; // shouldn't happen
667 if (d->iterCount > -1) // iteration -1 is the warmup iteration.
668 d->resultsList.append(t: results);
669
670 if (QBenchmarkGlobalData::current->verboseOutput) {
671 if (d->iterCount == -1) {
672 qDebug() << "warmup stage result :" << results.first().measurement.value;
673 } else {
674 qDebug() << "accumulation stage result:" << results.first().measurement.value;
675 }
676 }
677}
678
679bool QuickTestResult::measurementAccepted()
680{
681 return QBenchmarkTestMethodData::current->resultsAccepted();
682}
683
684static QList<QBenchmarkResult> qMedian(const QList<QList<QBenchmarkResult>> &container)
685{
686 const int count = container.size();
687 if (count == 0)
688 return {};
689
690 if (count == 1)
691 return container.at(i: 0);
692
693 QList<QList<QBenchmarkResult>> containerCopy = container;
694 std::sort(first: containerCopy.begin(), last: containerCopy.end(),
695 comp: [](const QList<QBenchmarkResult> &a, const QList<QBenchmarkResult> &b) {
696 return a.first() < b.first();
697 });
698
699 const int middle = count / 2;
700
701 // ### handle even-sized containers here by doing an aritmetic mean of the two middle items.
702 return containerCopy.at(i: middle);
703}
704
705bool QuickTestResult::needsMoreMeasurements()
706{
707 Q_D(QuickTestResult);
708 ++(d->iterCount);
709 if (d->iterCount < QBenchmarkGlobalData::current->adjustMedianIterationCount())
710 return true;
711 if (QBenchmarkTestMethodData::current->resultsAccepted())
712 QTestLog::addBenchmarkResults(result: qMedian(container: d->resultsList));
713 return false;
714}
715
716void QuickTestResult::startBenchmark(RunMode runMode, const QString &tag)
717{
718 QBenchmarkTestMethodData::current->results = {};
719 QBenchmarkTestMethodData::current->resultAccepted = false;
720 QBenchmarkGlobalData::current->context.tag = tag;
721 QBenchmarkGlobalData::current->context.slotName = functionName();
722
723 Q_D(QuickTestResult);
724 delete d->benchmarkIter;
725 d->benchmarkIter = new QTest::QBenchmarkIterationController
726 (QTest::QBenchmarkIterationController::RunMode(runMode));
727}
728
729bool QuickTestResult::isBenchmarkDone() const
730{
731 Q_D(const QuickTestResult);
732 if (d->benchmarkIter)
733 return d->benchmarkIter->isDone();
734 else
735 return true;
736}
737
738void QuickTestResult::nextBenchmark()
739{
740 Q_D(QuickTestResult);
741 if (d->benchmarkIter)
742 d->benchmarkIter->next();
743}
744
745void QuickTestResult::stopBenchmark()
746{
747 Q_D(QuickTestResult);
748 delete d->benchmarkIter;
749 d->benchmarkIter = nullptr;
750}
751
752QObject *QuickTestResult::grabImage(QQuickItem *item)
753{
754 if (item && item->window()) {
755 QQuickWindow *window = item->window();
756 QImage grabbed = window->grabWindow();
757 const auto dpi = grabbed.devicePixelRatio();
758 QRectF rf(item->x() * dpi, item->y() * dpi, item->width() * dpi, item->height() * dpi);
759 rf = rf.intersected(r: QRectF(0, 0, grabbed.width(), grabbed.height()));
760 QObject *o = new QuickTestImageObject(grabbed.copy(rect: rf.toAlignedRect()));
761 QQmlEngine::setContextForObject(o, qmlContext(this));
762 return o;
763 }
764 return nullptr;
765}
766
767QObject *QuickTestResult::findChild(QObject *parent, const QString &objectName)
768{
769 return parent ? parent->findChild<QObject*>(aName: objectName) : 0;
770}
771
772bool QuickTestResult::isPolishScheduled(QObject *itemOrWindow) const
773{
774 if (auto item = qobject_cast<QQuickItem*>(o: itemOrWindow))
775 return QQuickTest::qIsPolishScheduled(item);
776
777 if (auto window = qobject_cast<QQuickWindow*>(object: itemOrWindow))
778 return QQuickTest::qIsPolishScheduled(window);
779
780 qmlWarning(me: this) << "isPolishScheduled() expects either an Item or Window, but got"
781 << QDebug::toString(object: itemOrWindow);
782 return false;
783}
784
785bool QuickTestResult::waitForPolish(QObject *itemOrWindow, int timeout) const
786{
787 if (auto item = qobject_cast<QQuickItem*>(o: itemOrWindow))
788 return QQuickTest::qWaitForPolish(item, timeout);
789
790 if (auto window = qobject_cast<QQuickWindow*>(object: itemOrWindow))
791 return QQuickTest::qWaitForPolish(window, timeout);
792
793 qmlWarning(me: this) << "waitForItemPolish() expects either an Item or Window, but got"
794 << QDebug::toString(object: itemOrWindow);
795 return false;
796}
797
798namespace QTest {
799 void qtest_qParseArgs(int argc, char *argv[], bool qml);
800};
801
802void QuickTestResult::parseArgs(int argc, char *argv[])
803{
804 if (!QBenchmarkGlobalData::current)
805 QBenchmarkGlobalData::current = &globalBenchmarkData;
806 QTest::qtest_qParseArgs(argc, argv, qml: true);
807}
808
809void QuickTestResult::setProgramName(const char *name)
810{
811 if (name) {
812 QTestPrivate::parseBlackList();
813 QTestResult::reset();
814 } else if (!name && loggingStarted) {
815 QTestResult::setCurrentTestObject(globalProgramName);
816 QTestLog::stopLogging();
817 QTestResult::setCurrentTestObject(nullptr);
818 }
819 globalProgramName = name;
820 QTestResult::setCurrentTestObject(globalProgramName);
821}
822
823void QuickTestResult::setCurrentAppname(const char *appname)
824{
825 QTestResult::setCurrentAppName(appname);
826}
827
828int QuickTestResult::exitCode()
829{
830#if defined(QTEST_NOEXITCODE)
831 return 0;
832#else
833 // make sure our exit code is never going above 127
834 // since that could wrap and indicate 0 test fails
835 return qMin(a: QTestLog::failCount(), b: 127);
836#endif
837}
838
839QT_END_NAMESPACE
840
841#include "quicktestresult.moc"
842#include "moc_quicktestresult_p.cpp"
843

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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