// Copyright (C) 2021 The Qt Company Ltd. // Copyright (C) 2019 Luxoft Sweden AB // Copyright (C) 2018 Pelagicore AG // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "testrunner.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "configuration.h" #include "qml-utilities.h" QT_BEGIN_NAMESPACE namespace QTest { extern Q_TESTLIB_EXPORT bool printAvailableFunctions; extern Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, char *argv[], bool qml); } QT_END_NAMESPACE AM_QML_REGISTER_TYPES(QtApplicationManager_Test) QT_BEGIN_NAMESPACE_AM void TestRunner::setup(Configuration *cfg) { const QString testFile = cfg->yaml.ui.mainQml; const QString sourceFile = cfg->testRunnerSourceFile(); const QStringList testRunnerArguments = cfg->testRunnerArguments(); cfg->setForceVerbose(qEnvironmentVariableIsSet("AM_VERBOSE_TEST")); cfg->setForceDisableWatchdog(true); // this messes up test results on slow CI systems otherwise Q_ASSERT(!testRunnerArguments.isEmpty()); #if defined(Q_OS_WINDOWS) qputenv("QT_FORCE_STDERR_LOGGING", "1"); #endif // Convert all the arguments back into a char * array. // qtest_qParseArgs copies all data, so we can get rid of the array afterwards QVector argv; auto cleanup = qScopeGuard([&argv]() { std::for_each(argv.cbegin(), argv.cend(), [](char *arg) { delete [] arg; }); }); int argc = int(testRunnerArguments.size()); argv.resize(argc + 1); for (int i = 0; i < argc; ++i) argv[i] = qstrdup(qPrintable(testRunnerArguments.at(i))); argv[argc] = nullptr; if (!sourceFile.isEmpty()) { if (QFileInfo(sourceFile).fileName() != QFileInfo(testFile).fileName()) qWarning() << "appman-qmltestrunner: source file" << sourceFile << "does not match test file" << testFile; else QTest::setMainSourcePath(qPrintable(sourceFile)); } QuickTestResult::setCurrentAppname(argv.at(0)); QuickTestResult::setProgramName("qml"); // Allocate a QuickTestResult to create QBenchmarkGlobalData, otherwise the benchmark options don't work volatile QuickTestResult result; // We would call QuickTestResult::parseArgs here, but that would include qml options in the help // which we don't support QTest::qtest_qParseArgs(argc, argv.data(), false /*no qml options*/); qputenv("QT_QTESTLIB_RUNNING", "1"); if (QTestLog::verboseLevel() >= 1) { qputenv("AM_VERBOSE_TEST", "1"); cfg->setForceVerbose(true); } // Register the test object and application manager test add-on qApp->setProperty("_am_buildConfig", cfg->buildConfig()); qInfo() << "Verbose mode:" << (cfg->verbose() ? "on" : "off") << "(change with $AM_VERBOSE_TEST or the -v1/-v2 options)"; qInfo().nospace().noquote() << "Test: " << testFile << " in " << (cfg->yaml.flags.forceMultiProcess ? "multi" : "single") << "-process mode"; } int TestRunner::exec(QQmlEngine *qmlEngine) { if (qEnvironmentVariableIsSet("AM_BACKGROUND_TEST") && !qApp->topLevelWindows().isEmpty()) { QWindow *w = qApp->topLevelWindows().first(); w->setFlag(Qt::WindowStaysOnBottomHint); w->setFlag(Qt::WindowDoesNotAcceptFocus); } // install the QtTest crash handler std::optional handler; QTest::CrashHandler::prepareStackTrace(); if (!QTest::Internal::noCrashHandler) handler.emplace(); QEventLoop eventLoop; int typeId = qmlTypeId("QtTest", 1, 2, "QTestRootObject"); auto *inst = qmlEngine->singletonInstance(typeId); inst->setWindowShown(true); if (QTest::printAvailableFunctions) return 0; if (inst->hasTestCase()) eventLoop.exec(); QuickTestResult::setProgramName(nullptr); return QuickTestResult::exitCode(); } QT_END_NAMESPACE_AM