summaryrefslogtreecommitdiffstats
path: root/tests/auto/integration/multiapp/tst_multiapp.cpp
blob: 793a56e9d6db1f0494a463c8612295ab0b7926b8 (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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only

#include <QtTest/QtTest>
#include <QtCore/qdebug.h>
#include <QtCore/qprocess.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qstring.h>
#include <QtCore/qmetaobject.h>
#include <QtMultimedia/qsoundeffect.h>
#include <QtMultimedia/qmediadevices.h>
#include <QtMultimedia/qaudiodevice.h>

using namespace Qt::StringLiterals;

QT_USE_NAMESPACE

namespace {
bool executeTestOutOfProcess(const QString &testName);
void playSound();
} // namespace

class tst_multiapp : public QObject
{
    Q_OBJECT

public slots:
    void initTestCase()
    {
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
        QSKIP("Out-of-process testing does not behave correctly on mobile OS");
#endif
    }

private slots:
    void mediaDevices_doesNotCrash_whenRecreatingApplication()
    {
        QVERIFY(executeTestOutOfProcess(
                "mediaDevices_doesNotCrash_whenRecreatingApplication_impl"_L1));
    }

    bool mediaDevices_doesNotCrash_whenRecreatingApplication_impl(int argc, char ** argv)
    {
        {
            QCoreApplication app{ argc, argv };
            QMediaDevices::defaultAudioOutput();
        }
        {
            QCoreApplication app{ argc, argv };
            QMediaDevices::defaultAudioOutput();
        }

        return true;
    }

    void soundEffect_doesNotCrash_whenRecreatingApplication()
    {
        QVERIFY(executeTestOutOfProcess(
                "soundEffect_doesNotCrash_whenRecreatingApplication_impl"_L1));
    }

    bool soundEffect_doesNotCrash_whenRecreatingApplication_impl(int argc, char **argv)
    {
        Q_ASSERT(!qApp);

        // Play a sound twice under two different application objects
        // This verifies that QSoundEffect works in use cases where
        // client application recreates Qt application instances,
        // for example when the client application loads plugins
        // implemented using Qt.
        {
            QCoreApplication app{ argc, argv };
            playSound();
        }
        {
            QCoreApplication app{ argc, argv };
            playSound();
        }

        return true;
    }

};

namespace {

void playSound()
{
    const QUrl url{ "qrc:double-drop.wav"_L1 };

    QSoundEffect effect;
    effect.setSource(url);
    effect.play();

    QObject::connect(&effect, &QSoundEffect::playingChanged, qApp, [&]() {
        if (!effect.isPlaying())
            qApp->quit();
    });

    // In some CI configurations, we do not have any audio devices. We must therefore
    // close the qApp on error signal instead of on playingChanged.
    QObject::connect(&effect, &QSoundEffect::statusChanged, qApp, [&]() {
        if (effect.status() == QSoundEffect::Status::Error) {
            qDebug() << "Failed to play sound effect";
            qApp->quit();
        }
    });

    qApp->exec();
}

bool executeTestOutOfProcess(const QString &testName)
{
    const QStringList args{ "--run-test"_L1, testName };
    const QString processName = QCoreApplication::applicationFilePath();
    const int status = QProcess::execute(processName, args);
    return status == 0;
}

} // namespace

// This main function executes tests like normal qTest, and adds support
// for executing specific test functions when called out of process. In this
// case we don't create a QApplication, because the intent is to test how features
// behave when no QApplication exists.
int main(int argc, char *argv[])
{
    QCommandLineParser cmd;
    const QCommandLineOption runTest{ QStringList{ "run-test" }, "Executes a named test",
                                      "runTest" };
    cmd.addOption(runTest);
    cmd.parse({ argv, argv + argc });

    if (cmd.isSet(runTest)) {
        // We are requested to run a test case in a separate process without a Qt application
        const QString testName = cmd.value(runTest);

        bool returnValue = false;
        tst_multiapp tc;

        // Call the requested function on the test class
        const bool invokeResult =
                QMetaObject::invokeMethod(&tc, testName.toLatin1(), Qt::DirectConnection,
                                          qReturnArg(returnValue), argc, argv);

        return (invokeResult && returnValue) ? 0 : 1;
    }

    // If no special arguments are set, enter the regular QTest main routine
    // The below lines are the same that QTEST_GUILESS_MAIN would stamp out,
    // except the `int main(...)`
    TESTLIB_SELFCOVERAGE_START("tst_multiapp")
    QT_PREPEND_NAMESPACE(QTest::Internal::callInitMain)<tst_multiapp>();
    QCoreApplication app(argc, argv);
    app.setAttribute(Qt::AA_Use96Dpi, true);
    tst_multiapp tc;
    QTEST_SET_MAIN_SOURCE_PATH
    return QTest::qExec(&tc, argc, argv);
}

#include "tst_multiapp.moc"