summaryrefslogtreecommitdiffstats
path: root/src/tools/testrunner/testrunner.cpp
blob: 0f96ffb56b1bf701e97ce8d3b8b4b4806064e13d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// 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 <QGuiApplication>
#include <QEventLoop>
#include <QQmlEngine>
#include <QFileInfo>
#include <QVector>
#include <QScopeGuard>
#include <QWindow>

#include <qlogging.h>
#include <QtTest/qtestsystem.h>
#include <QtTest/private/qtestcrashhandler_p.h>
#include <QtQuickTest/private/quicktest_p.h>
#include <QtQuickTest/private/quicktestresult_p.h>
#include "configuration.h"
#include "amtest.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

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<char *> 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(testRunnerArguments.at(i).toLocal8Bit());
    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(sourceFile.toLocal8Bit());
    }
    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");

    // Register the test object and application manager test add-on
    qApp->setProperty("_am_buildConfig", cfg->buildConfig());

    // this is the only non-declarative registration, as this is only available for test runs
    qmlRegisterSingletonType<AmTest>("QtApplicationManager.Test", 2, 0, "AmTest",
                                     [](QQmlEngine *, QJSEngine *) {
        QQmlEngine::setObjectOwnership(AmTest::instance(), QQmlEngine::CppOwnership);
        return AmTest::instance();
    });

    qInfo().nospace().noquote() << "Verbose mode is " << (cfg->verbose() ? "on" : "off")
                                << " (change by (un)setting $AM_VERBOSE_TEST)\n 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<QTest::CrashHandler::FatalSignalHandler> handler;
    QTest::CrashHandler::prepareStackTrace();
    if (!QTest::Internal::noCrashHandler)
        handler.emplace();

    QEventLoop eventLoop;

    int typeId = qmlTypeId("QtTest", 1, 2, "QTestRootObject");
    QTestRootObject* inst = qmlEngine->singletonInstance<QTestRootObject*>(typeId);

    inst->setWindowShown(true);

    if (QTest::printAvailableFunctions)
        return 0;

    if (inst->hasTestCase())
        eventLoop.exec();

    QuickTestResult::setProgramName(nullptr);

    return QuickTestResult::exitCode();
}

QT_END_NAMESPACE_AM