mirror of
https://invent.kde.org/marcoa/a-la-karte.git
synced 2026-03-26 17:03:08 +00:00
tests: add dbus smoke + stop-launching regression
This commit is contained in:
parent
e9071c2b86
commit
b0d09be497
4 changed files with 1118 additions and 0 deletions
|
|
@ -26,6 +26,7 @@ include(ECMPoQmTools)
|
|||
include(ECMDeprecationSettings)
|
||||
include(ECMQmlModule)
|
||||
include(ECMAddAppIcon)
|
||||
include(CTest)
|
||||
include(FeatureSummary)
|
||||
|
||||
ecm_setup_version(${PROJECT_VERSION}
|
||||
|
|
@ -114,6 +115,10 @@ add_subdirectory(src)
|
|||
add_subdirectory(src/krunner)
|
||||
add_subdirectory(icons)
|
||||
|
||||
if (BUILD_TESTING)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
ki18n_install(po)
|
||||
|
||||
install(FILES org.kde.alakarte.desktop DESTINATION ${KDE_INSTALL_APPDIR})
|
||||
|
|
|
|||
45
tests/CMakeLists.txt
Normal file
45
tests/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
find_package(Qt6 ${QT_MIN_VERSION} QUIET COMPONENTS Test)
|
||||
|
||||
if (NOT Qt6Test_FOUND OR NOT TARGET Qt6::Test)
|
||||
return()
|
||||
endif()
|
||||
|
||||
add_executable(alakarte_dbus_smoketest
|
||||
dbus_smoketest.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(alakarte_dbus_smoketest PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::DBus
|
||||
Qt6::Test
|
||||
)
|
||||
|
||||
add_test(NAME alakarte_dbus_smoketest
|
||||
COMMAND alakarte_dbus_smoketest
|
||||
--gamecenter $<TARGET_FILE:alakarte_gamecenter>
|
||||
--runnerd $<TARGET_FILE:alakarte_runnerd>
|
||||
--inputd $<TARGET_FILE:alakarte_inputd>
|
||||
)
|
||||
|
||||
set_tests_properties(alakarte_dbus_smoketest PROPERTIES
|
||||
TIMEOUT 30
|
||||
)
|
||||
|
||||
add_executable(alakarte_stop_launching_regression_test
|
||||
stop_launching_regression_test.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(alakarte_stop_launching_regression_test PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::DBus
|
||||
Qt6::Test
|
||||
)
|
||||
|
||||
add_test(NAME alakarte_stop_launching_regression_test
|
||||
COMMAND alakarte_stop_launching_regression_test
|
||||
--gamecenter $<TARGET_FILE:alakarte_gamecenter>
|
||||
)
|
||||
|
||||
set_tests_properties(alakarte_stop_launching_regression_test PROPERTIES
|
||||
TIMEOUT 60
|
||||
)
|
||||
438
tests/dbus_smoketest.cpp
Normal file
438
tests/dbus_smoketest.cpp
Normal file
|
|
@ -0,0 +1,438 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// SPDX-FileCopyrightText: 2026 A-La-Karte Contributors
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDBusArgument>
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusConnectionInterface>
|
||||
#include <QDBusInterface>
|
||||
#include <QDBusReply>
|
||||
#include <QDBusVariant>
|
||||
#include <QDir>
|
||||
#include <QElapsedTimer>
|
||||
#include <QProcess>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QStandardPaths>
|
||||
#include <QStringList>
|
||||
#include <QUuid>
|
||||
#include <QtTest>
|
||||
|
||||
namespace
|
||||
{
|
||||
static QString takeArgValue(const QStringList &args, const QString &key)
|
||||
{
|
||||
const int idx = args.indexOf(key);
|
||||
if (idx < 0) {
|
||||
return {};
|
||||
}
|
||||
if (idx + 1 >= args.size()) {
|
||||
return {};
|
||||
}
|
||||
return args.at(idx + 1);
|
||||
}
|
||||
|
||||
static bool waitForService(QDBusConnection &bus, const QString &service, int timeoutMs)
|
||||
{
|
||||
if (!bus.isConnected() || !bus.interface()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QElapsedTimer t;
|
||||
t.start();
|
||||
while (t.elapsed() < timeoutMs) {
|
||||
if (bus.interface()->isServiceRegistered(service)) {
|
||||
return true;
|
||||
}
|
||||
QTest::qWait(50);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static QVariant unwrapDbusVariant(QVariant v)
|
||||
{
|
||||
if (v.metaType() == QMetaType::fromType<QDBusVariant>()) {
|
||||
v = v.value<QDBusVariant>().variant();
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
static QVariantMap unwrapVariantMap(QVariant v)
|
||||
{
|
||||
v = unwrapDbusVariant(v);
|
||||
if (v.metaType() == QMetaType::fromType<QDBusArgument>()) {
|
||||
const QDBusArgument arg = v.value<QDBusArgument>();
|
||||
return qdbus_cast<QVariantMap>(arg);
|
||||
}
|
||||
if (v.canConvert<QVariantMap>()) {
|
||||
return v.toMap();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
class DbusSmokeTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
|
||||
void pingGameCenter();
|
||||
void pingRunner();
|
||||
void pingInput();
|
||||
void runnerResolveLaunchNative();
|
||||
void runnerGameProfiles();
|
||||
|
||||
private:
|
||||
QString m_gamecenterPath;
|
||||
QString m_runnerdPath;
|
||||
QString m_inputdPath;
|
||||
|
||||
QString m_dbusAddress;
|
||||
QProcess m_dbusDaemon;
|
||||
QProcess m_gamecenter;
|
||||
QProcess m_runnerd;
|
||||
QProcess m_inputd;
|
||||
|
||||
QString m_testXdgBase;
|
||||
|
||||
QDBusConnection m_bus = QDBusConnection::connectToBus(QString(), QStringLiteral("alakarte_test"));
|
||||
|
||||
bool startPrivateBus();
|
||||
bool haveDbusDaemon() const;
|
||||
void startDaemon(QProcess &p, const QString &program);
|
||||
void stopProcess(QProcess &p);
|
||||
};
|
||||
|
||||
bool DbusSmokeTest::haveDbusDaemon() const
|
||||
{
|
||||
return !QStandardPaths::findExecutable(QStringLiteral("dbus-daemon")).isEmpty();
|
||||
}
|
||||
|
||||
bool DbusSmokeTest::startPrivateBus()
|
||||
{
|
||||
const QString dbusDaemon = QStandardPaths::findExecutable(QStringLiteral("dbus-daemon"));
|
||||
if (dbusDaemon.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_dbusDaemon.setProgram(dbusDaemon);
|
||||
m_dbusDaemon.setArguments({QStringLiteral("--session"), QStringLiteral("--nofork"), QStringLiteral("--print-address=1")});
|
||||
m_dbusDaemon.setProcessChannelMode(QProcess::MergedChannels);
|
||||
m_dbusDaemon.start();
|
||||
if (!m_dbusDaemon.waitForStarted(5000)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_dbusDaemon.waitForReadyRead(5000)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_dbusAddress = QString::fromUtf8(m_dbusDaemon.readLine()).trimmed();
|
||||
if (m_dbusAddress.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QDBusConnection::disconnectFromBus(QStringLiteral("alakarte_test"));
|
||||
m_bus = QDBusConnection::connectToBus(m_dbusAddress, QStringLiteral("alakarte_test"));
|
||||
return m_bus.isConnected();
|
||||
}
|
||||
|
||||
void DbusSmokeTest::startDaemon(QProcess &p, const QString &program)
|
||||
{
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert(QStringLiteral("DBUS_SESSION_BUS_ADDRESS"), m_dbusAddress);
|
||||
if (!m_testXdgBase.isEmpty()) {
|
||||
env.insert(QStringLiteral("XDG_DATA_HOME"), m_testXdgBase + QStringLiteral("/data"));
|
||||
env.insert(QStringLiteral("XDG_CONFIG_HOME"), m_testXdgBase + QStringLiteral("/config"));
|
||||
env.insert(QStringLiteral("XDG_CACHE_HOME"), m_testXdgBase + QStringLiteral("/cache"));
|
||||
}
|
||||
p.setProcessEnvironment(env);
|
||||
p.setProgram(program);
|
||||
p.setArguments({});
|
||||
p.setProcessChannelMode(QProcess::MergedChannels);
|
||||
p.start();
|
||||
}
|
||||
|
||||
void DbusSmokeTest::stopProcess(QProcess &p)
|
||||
{
|
||||
if (p.state() == QProcess::NotRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
p.terminate();
|
||||
if (!p.waitForFinished(3000)) {
|
||||
p.kill();
|
||||
p.waitForFinished(3000);
|
||||
}
|
||||
}
|
||||
|
||||
void DbusSmokeTest::initTestCase()
|
||||
{
|
||||
m_gamecenterPath = qApp ? qApp->property("alakarte_test_gamecenter").toString() : QString();
|
||||
m_runnerdPath = qApp ? qApp->property("alakarte_test_runnerd").toString() : QString();
|
||||
m_inputdPath = qApp ? qApp->property("alakarte_test_inputd").toString() : QString();
|
||||
|
||||
if (m_gamecenterPath.isEmpty() || m_runnerdPath.isEmpty() || m_inputdPath.isEmpty()) {
|
||||
const QStringList args = QCoreApplication::arguments();
|
||||
if (m_gamecenterPath.isEmpty()) {
|
||||
m_gamecenterPath = takeArgValue(args, QStringLiteral("--gamecenter"));
|
||||
}
|
||||
if (m_runnerdPath.isEmpty()) {
|
||||
m_runnerdPath = takeArgValue(args, QStringLiteral("--runnerd"));
|
||||
}
|
||||
if (m_inputdPath.isEmpty()) {
|
||||
m_inputdPath = takeArgValue(args, QStringLiteral("--inputd"));
|
||||
}
|
||||
}
|
||||
|
||||
QVERIFY2(!m_gamecenterPath.isEmpty(), "--gamecenter is required");
|
||||
QVERIFY2(!m_runnerdPath.isEmpty(), "--runnerd is required");
|
||||
QVERIFY2(!m_inputdPath.isEmpty(), "--inputd is required");
|
||||
|
||||
if (!haveDbusDaemon()) {
|
||||
QSKIP("dbus-daemon not found");
|
||||
}
|
||||
|
||||
if (!startPrivateBus()) {
|
||||
QSKIP("failed to start private dbus session");
|
||||
}
|
||||
|
||||
m_testXdgBase = QDir::tempPath() + QStringLiteral("/alakarte-test-xdg-") + QUuid::createUuid().toString(QUuid::WithoutBraces);
|
||||
QVERIFY(QDir().mkpath(m_testXdgBase + QStringLiteral("/data")));
|
||||
QVERIFY(QDir().mkpath(m_testXdgBase + QStringLiteral("/config")));
|
||||
QVERIFY(QDir().mkpath(m_testXdgBase + QStringLiteral("/cache")));
|
||||
|
||||
startDaemon(m_runnerd, m_runnerdPath);
|
||||
QVERIFY(m_runnerd.waitForStarted(5000));
|
||||
|
||||
startDaemon(m_inputd, m_inputdPath);
|
||||
QVERIFY(m_inputd.waitForStarted(5000));
|
||||
|
||||
startDaemon(m_gamecenter, m_gamecenterPath);
|
||||
QVERIFY(m_gamecenter.waitForStarted(5000));
|
||||
|
||||
QVERIFY2(waitForService(m_bus, QStringLiteral("org.kde.ALaKarte.Runner1"), 5000), "Runner1 service did not appear on the bus");
|
||||
QVERIFY2(waitForService(m_bus, QStringLiteral("org.kde.ALaKarte.Input1"), 5000), "Input1 service did not appear on the bus");
|
||||
QVERIFY2(waitForService(m_bus, QStringLiteral("org.kde.GameCenter1"), 5000), "GameCenter1 service did not appear on the bus");
|
||||
}
|
||||
|
||||
void DbusSmokeTest::cleanupTestCase()
|
||||
{
|
||||
stopProcess(m_gamecenter);
|
||||
stopProcess(m_inputd);
|
||||
stopProcess(m_runnerd);
|
||||
stopProcess(m_dbusDaemon);
|
||||
QDBusConnection::disconnectFromBus(QStringLiteral("alakarte_test"));
|
||||
|
||||
if (!m_testXdgBase.isEmpty()) {
|
||||
QDir(m_testXdgBase).removeRecursively();
|
||||
m_testXdgBase.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void DbusSmokeTest::pingGameCenter()
|
||||
{
|
||||
QDBusInterface iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/GameCenter1"), QStringLiteral("org.kde.GameCenter1"), m_bus);
|
||||
QVERIFY(iface.isValid());
|
||||
iface.setTimeout(2000);
|
||||
|
||||
const QDBusReply<QString> reply = iface.call(QStringLiteral("Ping"));
|
||||
QVERIFY(reply.isValid());
|
||||
QCOMPARE(reply.value(), QStringLiteral("ok"));
|
||||
}
|
||||
|
||||
void DbusSmokeTest::pingRunner()
|
||||
{
|
||||
QDBusInterface iface(QStringLiteral("org.kde.ALaKarte.Runner1"),
|
||||
QStringLiteral("/org/kde/ALaKarte/Runner1"),
|
||||
QStringLiteral("org.kde.ALaKarte.Runner1"),
|
||||
m_bus);
|
||||
QVERIFY(iface.isValid());
|
||||
iface.setTimeout(2000);
|
||||
|
||||
const QDBusReply<QString> reply = iface.call(QStringLiteral("Ping"));
|
||||
QVERIFY(reply.isValid());
|
||||
QCOMPARE(reply.value(), QStringLiteral("ok"));
|
||||
}
|
||||
|
||||
void DbusSmokeTest::pingInput()
|
||||
{
|
||||
QDBusInterface iface(QStringLiteral("org.kde.ALaKarte.Input1"),
|
||||
QStringLiteral("/org/kde/ALaKarte/Input1"),
|
||||
QStringLiteral("org.kde.ALaKarte.Input1"),
|
||||
m_bus);
|
||||
QVERIFY(iface.isValid());
|
||||
iface.setTimeout(2000);
|
||||
|
||||
const QDBusReply<QString> reply = iface.call(QStringLiteral("Ping"));
|
||||
QVERIFY(reply.isValid());
|
||||
QCOMPARE(reply.value(), QStringLiteral("ok"));
|
||||
}
|
||||
|
||||
void DbusSmokeTest::runnerResolveLaunchNative()
|
||||
{
|
||||
QDBusInterface iface(QStringLiteral("org.kde.ALaKarte.Runner1"),
|
||||
QStringLiteral("/org/kde/ALaKarte/Runner1"),
|
||||
QStringLiteral("org.kde.ALaKarte.Runner1"),
|
||||
m_bus);
|
||||
QVERIFY(iface.isValid());
|
||||
iface.setTimeout(2000);
|
||||
|
||||
QVariantMap spec;
|
||||
spec.insert(QStringLiteral("program"), QStringLiteral("/bin/true"));
|
||||
spec.insert(QStringLiteral("args"), QStringList{});
|
||||
|
||||
const QDBusReply<QVariantMap> reply = iface.call(QStringLiteral("ResolveLaunch"), spec);
|
||||
QVERIFY(reply.isValid());
|
||||
|
||||
const QVariantMap out = reply.value();
|
||||
QVERIFY(out.value(QStringLiteral("ok")).toBool());
|
||||
QCOMPARE(out.value(QStringLiteral("finalProgram")).toString(), QStringLiteral("/bin/true"));
|
||||
QCOMPARE(out.value(QStringLiteral("finalArgs")).toStringList(), QStringList{});
|
||||
}
|
||||
|
||||
void DbusSmokeTest::runnerGameProfiles()
|
||||
{
|
||||
QDBusInterface iface(QStringLiteral("org.kde.ALaKarte.Runner1"),
|
||||
QStringLiteral("/org/kde/ALaKarte/Runner1"),
|
||||
QStringLiteral("org.kde.ALaKarte.Runner1"),
|
||||
m_bus);
|
||||
QVERIFY(iface.isValid());
|
||||
iface.setTimeout(2000);
|
||||
|
||||
const QString gameId = QStringLiteral("alakarte-test-game");
|
||||
|
||||
{
|
||||
const QDBusReply<QVariantMap> reply = iface.call(QStringLiteral("ClearGameProfile"), gameId);
|
||||
QVERIFY(reply.isValid());
|
||||
QVERIFY(reply.value().value(QStringLiteral("ok")).toBool());
|
||||
}
|
||||
|
||||
{
|
||||
QVariantMap spec;
|
||||
spec.insert(QStringLiteral("gameId"), gameId);
|
||||
spec.insert(QStringLiteral("runner"), QStringLiteral("wine"));
|
||||
spec.insert(QStringLiteral("runnerPath"), QStringLiteral("/bin/true"));
|
||||
spec.insert(QStringLiteral("envOverrides"), QVariantMap{{QStringLiteral("ALAKARTE_TEST_VAR"), QStringLiteral("1")}});
|
||||
spec.insert(QStringLiteral("extraArgs"), QStringList{QStringLiteral("--profile-arg")});
|
||||
spec.insert(QStringLiteral("dllOverrides"),
|
||||
QVariantMap{{QStringLiteral("d3d11"), QStringLiteral("native,builtin")}, {QStringLiteral("dxgi"), QStringLiteral("native,builtin")}});
|
||||
|
||||
const QDBusReply<QVariantMap> reply = iface.call(QStringLiteral("SetGameProfile"), spec);
|
||||
QVERIFY(reply.isValid());
|
||||
const QVariantMap out = reply.value();
|
||||
QVERIFY(out.value(QStringLiteral("ok")).toBool());
|
||||
|
||||
const QVariantMap profile = unwrapVariantMap(out.value(QStringLiteral("profile")));
|
||||
QCOMPARE(profile.value(QStringLiteral("gameId")).toString(), gameId);
|
||||
const QVariantMap env = unwrapVariantMap(profile.value(QStringLiteral("envOverrides")));
|
||||
QCOMPARE(env.value(QStringLiteral("ALAKARTE_TEST_VAR")).toString(), QStringLiteral("1"));
|
||||
|
||||
QCOMPARE(profile.value(QStringLiteral("extraArgs")).toStringList(), QStringList{QStringLiteral("--profile-arg")});
|
||||
|
||||
const QVariantMap dll = unwrapVariantMap(profile.value(QStringLiteral("dllOverrides")));
|
||||
QCOMPARE(dll.value(QStringLiteral("d3d11")).toString(), QStringLiteral("native,builtin"));
|
||||
QCOMPARE(dll.value(QStringLiteral("dxgi")).toString(), QStringLiteral("native,builtin"));
|
||||
}
|
||||
|
||||
{
|
||||
const QDBusReply<QVariantMap> reply = iface.call(QStringLiteral("GetGameProfile"), gameId);
|
||||
QVERIFY(reply.isValid());
|
||||
const QVariantMap out = reply.value();
|
||||
QVERIFY(out.value(QStringLiteral("ok")).toBool());
|
||||
const QVariantMap profile = unwrapVariantMap(out.value(QStringLiteral("profile")));
|
||||
QCOMPARE(profile.value(QStringLiteral("gameId")).toString(), gameId);
|
||||
|
||||
QCOMPARE(profile.value(QStringLiteral("extraArgs")).toStringList(), QStringList{QStringLiteral("--profile-arg")});
|
||||
|
||||
const QVariantMap dll = unwrapVariantMap(profile.value(QStringLiteral("dllOverrides")));
|
||||
QCOMPARE(dll.value(QStringLiteral("d3d11")).toString(), QStringLiteral("native,builtin"));
|
||||
QCOMPARE(dll.value(QStringLiteral("dxgi")).toString(), QStringLiteral("native,builtin"));
|
||||
}
|
||||
|
||||
{
|
||||
QVariantMap spec;
|
||||
spec.insert(QStringLiteral("gameId"), gameId);
|
||||
spec.insert(QStringLiteral("useGameProfile"), true);
|
||||
spec.insert(QStringLiteral("program"), QStringLiteral("/bin/true"));
|
||||
spec.insert(QStringLiteral("args"), QStringList{QStringLiteral("--base-arg")});
|
||||
spec.insert(QStringLiteral("extraArgs"), QStringList{QStringLiteral("--spec-arg")});
|
||||
|
||||
const QDBusReply<QVariantMap> reply = iface.call(QStringLiteral("ResolveLaunch"), spec);
|
||||
QVERIFY(reply.isValid());
|
||||
const QVariantMap out = reply.value();
|
||||
QVERIFY(out.value(QStringLiteral("ok")).toBool());
|
||||
QCOMPARE(out.value(QStringLiteral("finalProgram")).toString(), QStringLiteral("/bin/true"));
|
||||
const QStringList expectedFinalArgs = QStringList({
|
||||
QStringLiteral("/bin/true"),
|
||||
QStringLiteral("--base-arg"),
|
||||
QStringLiteral("--profile-arg"),
|
||||
QStringLiteral("--spec-arg"),
|
||||
});
|
||||
QCOMPARE(out.value(QStringLiteral("finalArgs")).toStringList(), expectedFinalArgs);
|
||||
|
||||
const QVariantMap env = unwrapVariantMap(out.value(QStringLiteral("effectiveEnv")));
|
||||
QCOMPARE(env.value(QStringLiteral("ALAKARTE_TEST_VAR")).toString(), QStringLiteral("1"));
|
||||
QCOMPARE(env.value(QStringLiteral("WINEDLLOVERRIDES")).toString(), QStringLiteral("d3d11=native,builtin;dxgi=native,builtin"));
|
||||
}
|
||||
|
||||
{
|
||||
const QDBusReply<QVariantList> reply = iface.call(QStringLiteral("ListGameProfiles"));
|
||||
QVERIFY(reply.isValid());
|
||||
const QVariantList list = reply.value();
|
||||
bool found = false;
|
||||
for (const QVariant &v : list) {
|
||||
const QVariantMap m = unwrapVariantMap(v);
|
||||
if (m.value(QStringLiteral("gameId")).toString() == gameId) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
QVERIFY(found);
|
||||
}
|
||||
|
||||
{
|
||||
const QDBusReply<QVariantMap> reply = iface.call(QStringLiteral("ClearGameProfile"), gameId);
|
||||
QVERIFY(reply.isValid());
|
||||
QVERIFY(reply.value().value(QStringLiteral("ok")).toBool());
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
const QStringList args = app.arguments();
|
||||
const QString gamecenterPath = takeArgValue(args, QStringLiteral("--gamecenter"));
|
||||
const QString runnerdPath = takeArgValue(args, QStringLiteral("--runnerd"));
|
||||
const QString inputdPath = takeArgValue(args, QStringLiteral("--inputd"));
|
||||
|
||||
app.setProperty("alakarte_test_gamecenter", gamecenterPath);
|
||||
app.setProperty("alakarte_test_runnerd", runnerdPath);
|
||||
app.setProperty("alakarte_test_inputd", inputdPath);
|
||||
|
||||
QStringList filtered;
|
||||
filtered.reserve(args.size());
|
||||
if (!args.isEmpty()) {
|
||||
filtered.push_back(args.first());
|
||||
}
|
||||
for (int i = 1; i < args.size(); ++i) {
|
||||
const QString a = args.at(i);
|
||||
if (a == QLatin1String("--gamecenter") || a == QLatin1String("--runnerd")) {
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
if (a == QLatin1String("--inputd")) {
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
filtered.push_back(a);
|
||||
}
|
||||
|
||||
DbusSmokeTest tc;
|
||||
return QTest::qExec(&tc, filtered);
|
||||
}
|
||||
|
||||
#include "dbus_smoketest.moc"
|
||||
630
tests/stop_launching_regression_test.cpp
Normal file
630
tests/stop_launching_regression_test.cpp
Normal file
|
|
@ -0,0 +1,630 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// SPDX-FileCopyrightText: 2026 A-La-Karte Contributors
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusConnectionInterface>
|
||||
#include <QDBusInterface>
|
||||
#include <QDBusReply>
|
||||
#include <QDBusVariant>
|
||||
#include <QDir>
|
||||
#include <QElapsedTimer>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QProcess>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QStandardPaths>
|
||||
#include <QStringList>
|
||||
#include <QTemporaryDir>
|
||||
#include <QUuid>
|
||||
#include <QtTest>
|
||||
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
static QString takeArgValue(const QStringList &args, const QString &key)
|
||||
{
|
||||
const int idx = args.indexOf(key);
|
||||
if (idx < 0) {
|
||||
return {};
|
||||
}
|
||||
if (idx + 1 >= args.size()) {
|
||||
return {};
|
||||
}
|
||||
return args.at(idx + 1);
|
||||
}
|
||||
|
||||
static bool waitForService(QDBusConnection &bus, const QString &service, int timeoutMs)
|
||||
{
|
||||
if (!bus.isConnected() || !bus.interface()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QElapsedTimer t;
|
||||
t.start();
|
||||
while (t.elapsed() < timeoutMs) {
|
||||
if (bus.interface()->isServiceRegistered(service)) {
|
||||
return true;
|
||||
}
|
||||
QTest::qWait(50);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static QByteArray readProcFile(const QString &path, qint64 maxSize = 65536)
|
||||
{
|
||||
QFile f(path);
|
||||
if (!f.open(QIODevice::ReadOnly)) {
|
||||
return {};
|
||||
}
|
||||
return f.read(maxSize);
|
||||
}
|
||||
|
||||
static bool containsNullSeparatedEntry(const QByteArray &blob, const QByteArray &needle)
|
||||
{
|
||||
if (blob.isEmpty() || needle.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int pos = -1;
|
||||
while (true) {
|
||||
pos = blob.indexOf(needle, pos + 1);
|
||||
if (pos < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool startOk = (pos == 0) || (blob.at(pos - 1) == '\0');
|
||||
const int endPos = pos + needle.size();
|
||||
const bool endOk = (endPos == blob.size()) || (blob.at(endPos) == '\0');
|
||||
if (startOk && endOk) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static QList<uint> pidsWithEnvironEntry(const QByteArray &needle)
|
||||
{
|
||||
QList<uint> out;
|
||||
const QDir proc(QStringLiteral("/proc"));
|
||||
const QStringList entries = proc.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
for (const QString &entry : entries) {
|
||||
bool ok = false;
|
||||
const uint pid = entry.toUInt(&ok);
|
||||
if (!ok || pid <= 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const QByteArray env = readProcFile(QStringLiteral("/proc/%1/environ").arg(pid));
|
||||
if (containsNullSeparatedEntry(env, needle)) {
|
||||
out.push_back(pid);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool waitForAnyEnvironEntry(const QByteArray &needle, int timeoutMs)
|
||||
{
|
||||
QElapsedTimer t;
|
||||
t.start();
|
||||
while (t.elapsed() < timeoutMs) {
|
||||
if (!pidsWithEnvironEntry(needle).isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
QTest::qWait(50);
|
||||
}
|
||||
return !pidsWithEnvironEntry(needle).isEmpty();
|
||||
}
|
||||
|
||||
static bool waitForNoEnvironEntry(const QByteArray &needle, int timeoutMs)
|
||||
{
|
||||
QElapsedTimer t;
|
||||
t.start();
|
||||
while (t.elapsed() < timeoutMs) {
|
||||
if (pidsWithEnvironEntry(needle).isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
QTest::qWait(150);
|
||||
}
|
||||
return pidsWithEnvironEntry(needle).isEmpty();
|
||||
}
|
||||
|
||||
static void killPids(const QList<uint> &pids)
|
||||
{
|
||||
for (uint pid : pids) {
|
||||
if (pid > 1) {
|
||||
::kill(static_cast<pid_t>(pid), SIGKILL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void stopProcess(QProcess &p)
|
||||
{
|
||||
if (p.state() == QProcess::NotRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
p.terminate();
|
||||
if (!p.waitForFinished(3000)) {
|
||||
p.kill();
|
||||
p.waitForFinished(3000);
|
||||
}
|
||||
}
|
||||
|
||||
static bool waitForUnitNotActive(const QString &unitName, int timeoutMs)
|
||||
{
|
||||
QDBusConnection bus = QDBusConnection::sessionBus();
|
||||
if (!bus.isConnected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QDBusInterface manager(QStringLiteral("org.freedesktop.systemd1"),
|
||||
QStringLiteral("/org/freedesktop/systemd1"),
|
||||
QStringLiteral("org.freedesktop.systemd1.Manager"),
|
||||
bus);
|
||||
if (!manager.isValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QElapsedTimer t;
|
||||
t.start();
|
||||
while (t.elapsed() < timeoutMs) {
|
||||
const QDBusReply<QDBusObjectPath> unitReply = manager.call(QStringLiteral("GetUnit"), unitName);
|
||||
if (!unitReply.isValid()) {
|
||||
if (unitReply.error().name() == QLatin1String("org.freedesktop.systemd1.NoSuchUnit")) {
|
||||
return true;
|
||||
}
|
||||
QTest::qWait(100);
|
||||
continue;
|
||||
}
|
||||
|
||||
QDBusInterface props(QStringLiteral("org.freedesktop.systemd1"), unitReply.value().path(), QStringLiteral("org.freedesktop.DBus.Properties"), bus);
|
||||
if (!props.isValid()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const QDBusReply<QVariant> stateReply =
|
||||
props.call(QStringLiteral("Get"), QStringLiteral("org.freedesktop.systemd1.Unit"), QStringLiteral("ActiveState"));
|
||||
if (!stateReply.isValid()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
QVariant v = stateReply.value();
|
||||
if (v.canConvert<QDBusVariant>()) {
|
||||
v = v.value<QDBusVariant>().variant();
|
||||
}
|
||||
const QString state = v.toString();
|
||||
if (state != QLatin1String("active") && state != QLatin1String("activating") && state != QLatin1String("deactivating")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
QTest::qWait(100);
|
||||
}
|
||||
|
||||
const QDBusReply<QDBusObjectPath> unitReply = manager.call(QStringLiteral("GetUnit"), unitName);
|
||||
if (!unitReply.isValid() && unitReply.error().name() == QLatin1String("org.freedesktop.systemd1.NoSuchUnit")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class StopLaunchingRegressionTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
|
||||
void stopWhileLaunchingSteam();
|
||||
void stopByGameIdWhileLaunchingSteam();
|
||||
void stopWhileLaunchingLutris();
|
||||
void stopByGameIdWhileLaunchingLutris();
|
||||
|
||||
void stopDirectLaunch();
|
||||
void stopByGameIdDirectLaunch();
|
||||
|
||||
private:
|
||||
QString m_gamecenterPath;
|
||||
QProcess m_gamecenter;
|
||||
QString m_testXdgBase;
|
||||
QTemporaryDir m_stubBin;
|
||||
QString m_markerValue;
|
||||
};
|
||||
|
||||
void StopLaunchingRegressionTest::initTestCase()
|
||||
{
|
||||
m_gamecenterPath = qApp->property("alakarte_test_gamecenter").toString();
|
||||
if (m_gamecenterPath.isEmpty()) {
|
||||
const QStringList args = QCoreApplication::arguments();
|
||||
m_gamecenterPath = takeArgValue(args, QStringLiteral("--gamecenter"));
|
||||
}
|
||||
QVERIFY2(!m_gamecenterPath.isEmpty(), "--gamecenter is required");
|
||||
|
||||
QDBusConnection bus = QDBusConnection::sessionBus();
|
||||
if (!bus.isConnected() || !bus.interface()) {
|
||||
QSKIP("session bus not available");
|
||||
}
|
||||
|
||||
QDBusInterface systemd(QStringLiteral("org.freedesktop.systemd1"),
|
||||
QStringLiteral("/org/freedesktop/systemd1"),
|
||||
QStringLiteral("org.freedesktop.systemd1.Manager"),
|
||||
bus);
|
||||
if (!systemd.isValid()) {
|
||||
QSKIP("systemd user manager not available on session bus");
|
||||
}
|
||||
|
||||
if (bus.interface()->isServiceRegistered(QStringLiteral("org.kde.GameCenter1"))) {
|
||||
QSKIP("org.kde.GameCenter1 already registered on the session bus");
|
||||
}
|
||||
|
||||
if (!m_stubBin.isValid()) {
|
||||
QSKIP("failed to create temporary directory for stub executables");
|
||||
}
|
||||
|
||||
m_markerValue = QUuid::createUuid().toString(QUuid::WithoutBraces);
|
||||
const QByteArray markerNeedle = (QStringLiteral("ALAKARTE_TEST_MARKER=") + m_markerValue).toUtf8();
|
||||
killPids(pidsWithEnvironEntry(markerNeedle));
|
||||
|
||||
m_testXdgBase = QDir::tempPath() + QStringLiteral("/alakarte-stop-launching-test-xdg-") + QUuid::createUuid().toString(QUuid::WithoutBraces);
|
||||
QVERIFY(QDir().mkpath(m_testXdgBase + QStringLiteral("/data")));
|
||||
QVERIFY(QDir().mkpath(m_testXdgBase + QStringLiteral("/config")));
|
||||
QVERIFY(QDir().mkpath(m_testXdgBase + QStringLiteral("/cache")));
|
||||
|
||||
const QString steamPath = m_stubBin.path() + QStringLiteral("/steam");
|
||||
{
|
||||
QFile steam(steamPath);
|
||||
QVERIFY(steam.open(QIODevice::WriteOnly | QIODevice::Truncate));
|
||||
const QByteArray script =
|
||||
"#!/bin/sh\n"
|
||||
"appid=\"\"\n"
|
||||
"while [ $# -gt 0 ]; do\n"
|
||||
" if [ \"$1\" = \"-applaunch\" ] && [ $# -ge 2 ]; then appid=\"$2\"; break; fi\n"
|
||||
" shift\n"
|
||||
"done\n"
|
||||
"if [ -z \"$appid\" ]; then appid=\"123456\"; fi\n"
|
||||
"marker=\"${ALAKARTE_TEST_MARKER_VALUE}\"\n"
|
||||
"nohup sh -c \"sleep 1; env SteamAppId=$appid SteamGameId=$appid ALAKARTE_TEST_MARKER=$marker /bin/sleep 60\" >/dev/null 2>&1 &\n"
|
||||
"exit 0\n";
|
||||
QVERIFY(steam.write(script) == script.size());
|
||||
}
|
||||
QVERIFY(QFile::setPermissions(steamPath,
|
||||
QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner | QFileDevice::ReadGroup | QFileDevice::ExeGroup
|
||||
| QFileDevice::ReadOther | QFileDevice::ExeOther));
|
||||
|
||||
const QString lutrisPath = m_stubBin.path() + QStringLiteral("/lutris");
|
||||
{
|
||||
QFile lutris(lutrisPath);
|
||||
QVERIFY(lutris.open(QIODevice::WriteOnly | QIODevice::Truncate));
|
||||
const QByteArray script =
|
||||
"#!/bin/sh\n"
|
||||
"id=\"\"\n"
|
||||
"if [ $# -ge 1 ]; then\n"
|
||||
" id=\"$1\"\n"
|
||||
"fi\n"
|
||||
"case \"$id\" in\n"
|
||||
" lutris:rungameid/*) id=\"${id##*/}\" ;;\n"
|
||||
" lutris:rungame/*) id=\"${id##*/}\" ;;\n"
|
||||
"esac\n"
|
||||
"if [ -z \"$id\" ]; then id=\"test-lutris\"; fi\n"
|
||||
"marker=\"${ALAKARTE_TEST_MARKER_VALUE}\"\n"
|
||||
"nohup sh -c \"sleep 1; env LUTRIS_GAME_SLUG=$id ALAKARTE_TEST_MARKER=$marker /bin/sleep 60\" >/dev/null 2>&1 &\n"
|
||||
"exit 0\n";
|
||||
QVERIFY(lutris.write(script) == script.size());
|
||||
}
|
||||
QVERIFY(QFile::setPermissions(lutrisPath,
|
||||
QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner | QFileDevice::ReadGroup | QFileDevice::ExeGroup
|
||||
| QFileDevice::ReadOther | QFileDevice::ExeOther));
|
||||
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert(QStringLiteral("PATH"), m_stubBin.path() + QStringLiteral(":") + env.value(QStringLiteral("PATH")));
|
||||
env.insert(QStringLiteral("ALAKARTE_TEST_MARKER_VALUE"), m_markerValue);
|
||||
env.insert(QStringLiteral("XDG_DATA_HOME"), m_testXdgBase + QStringLiteral("/data"));
|
||||
env.insert(QStringLiteral("XDG_CONFIG_HOME"), m_testXdgBase + QStringLiteral("/config"));
|
||||
env.insert(QStringLiteral("XDG_CACHE_HOME"), m_testXdgBase + QStringLiteral("/cache"));
|
||||
|
||||
m_gamecenter.setProcessEnvironment(env);
|
||||
m_gamecenter.setProgram(m_gamecenterPath);
|
||||
m_gamecenter.setArguments({});
|
||||
m_gamecenter.setProcessChannelMode(QProcess::MergedChannels);
|
||||
m_gamecenter.start();
|
||||
QVERIFY(m_gamecenter.waitForStarted(5000));
|
||||
|
||||
QVERIFY2(waitForService(bus, QStringLiteral("org.kde.GameCenter1"), 5000), "GameCenter1 service did not appear on the session bus");
|
||||
}
|
||||
|
||||
void StopLaunchingRegressionTest::cleanupTestCase()
|
||||
{
|
||||
stopProcess(m_gamecenter);
|
||||
|
||||
if (!m_markerValue.isEmpty()) {
|
||||
const QByteArray markerNeedle = (QStringLiteral("ALAKARTE_TEST_MARKER=") + m_markerValue).toUtf8();
|
||||
killPids(pidsWithEnvironEntry(markerNeedle));
|
||||
m_markerValue.clear();
|
||||
}
|
||||
|
||||
if (!m_testXdgBase.isEmpty()) {
|
||||
QDir(m_testXdgBase).removeRecursively();
|
||||
m_testXdgBase.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void StopLaunchingRegressionTest::stopWhileLaunchingSteam()
|
||||
{
|
||||
QDBusConnection bus = QDBusConnection::sessionBus();
|
||||
if (!bus.isConnected()) {
|
||||
QSKIP("session bus not available");
|
||||
}
|
||||
|
||||
QDBusInterface iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/GameCenter1"), QStringLiteral("org.kde.GameCenter1"), bus);
|
||||
QVERIFY2(iface.isValid(), "GameCenter1 DBus interface not valid");
|
||||
|
||||
const QString appId = QStringLiteral("987654");
|
||||
QVariantMap spec;
|
||||
spec.insert(QStringLiteral("provider"), QStringLiteral("steam"));
|
||||
spec.insert(QStringLiteral("command"), QStringLiteral("steam -applaunch %1").arg(appId));
|
||||
spec.insert(QStringLiteral("gameId"), QStringLiteral("test-steam-%1").arg(appId));
|
||||
|
||||
QDBusReply<QString> launchReply = iface.call(QStringLiteral("Launch"), spec);
|
||||
QVERIFY2(launchReply.isValid(), qPrintable(launchReply.error().message()));
|
||||
const QString sessionId = launchReply.value();
|
||||
QVERIFY2(!sessionId.isEmpty(), "Launch returned empty sessionId");
|
||||
const QString unitName = QStringLiteral("alakarte-game-%1.scope").arg(sessionId);
|
||||
|
||||
QDBusReply<void> stopReply = iface.call(QStringLiteral("Stop"), sessionId);
|
||||
QVERIFY2(stopReply.isValid(), qPrintable(stopReply.error().message()));
|
||||
|
||||
const QByteArray markerNeedle = (QStringLiteral("ALAKARTE_TEST_MARKER=") + m_markerValue).toUtf8();
|
||||
waitForAnyEnvironEntry(markerNeedle, 5000);
|
||||
|
||||
const bool cleaned = waitForNoEnvironEntry(markerNeedle, 15000);
|
||||
if (!cleaned) {
|
||||
const QList<uint> pids = pidsWithEnvironEntry(markerNeedle);
|
||||
killPids(pids);
|
||||
}
|
||||
QVERIFY2(cleaned, "marker process still alive after Stop() during Launching");
|
||||
|
||||
QVERIFY2(waitForUnitNotActive(unitName, 15000), "systemd unit still active after Stop() during Launching");
|
||||
}
|
||||
|
||||
void StopLaunchingRegressionTest::stopByGameIdWhileLaunchingSteam()
|
||||
{
|
||||
QDBusConnection bus = QDBusConnection::sessionBus();
|
||||
if (!bus.isConnected()) {
|
||||
QSKIP("session bus not available");
|
||||
}
|
||||
|
||||
QDBusInterface iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/GameCenter1"), QStringLiteral("org.kde.GameCenter1"), bus);
|
||||
QVERIFY2(iface.isValid(), "GameCenter1 DBus interface not valid");
|
||||
|
||||
const QByteArray markerNeedle = (QStringLiteral("ALAKARTE_TEST_MARKER=") + m_markerValue).toUtf8();
|
||||
killPids(pidsWithEnvironEntry(markerNeedle));
|
||||
|
||||
const QString appId = QStringLiteral("876543");
|
||||
const QString gameId = QStringLiteral("test-steam-stop-by-gameid-%1").arg(QUuid::createUuid().toString(QUuid::WithoutBraces));
|
||||
|
||||
QVariantMap spec;
|
||||
spec.insert(QStringLiteral("provider"), QStringLiteral("steam"));
|
||||
spec.insert(QStringLiteral("command"), QStringLiteral("steam -applaunch %1").arg(appId));
|
||||
spec.insert(QStringLiteral("gameId"), gameId);
|
||||
|
||||
QDBusReply<QString> launchReply = iface.call(QStringLiteral("Launch"), spec);
|
||||
QVERIFY2(launchReply.isValid(), qPrintable(launchReply.error().message()));
|
||||
const QString sessionId = launchReply.value();
|
||||
QVERIFY2(!sessionId.isEmpty(), "Launch returned empty sessionId");
|
||||
const QString unitName = QStringLiteral("alakarte-game-%1.scope").arg(sessionId);
|
||||
|
||||
QDBusReply<void> stopReply = iface.call(QStringLiteral("StopByGameId"), gameId);
|
||||
QVERIFY2(stopReply.isValid(), qPrintable(stopReply.error().message()));
|
||||
|
||||
waitForAnyEnvironEntry(markerNeedle, 5000);
|
||||
|
||||
const bool cleaned = waitForNoEnvironEntry(markerNeedle, 15000);
|
||||
if (!cleaned) {
|
||||
const QList<uint> pids = pidsWithEnvironEntry(markerNeedle);
|
||||
killPids(pids);
|
||||
}
|
||||
QVERIFY2(cleaned, "marker process still alive after StopByGameId() during Launching");
|
||||
|
||||
QVERIFY2(waitForUnitNotActive(unitName, 15000), "systemd unit still active after StopByGameId() during Launching");
|
||||
}
|
||||
|
||||
void StopLaunchingRegressionTest::stopWhileLaunchingLutris()
|
||||
{
|
||||
QDBusConnection bus = QDBusConnection::sessionBus();
|
||||
if (!bus.isConnected()) {
|
||||
QSKIP("session bus not available");
|
||||
}
|
||||
|
||||
QDBusInterface iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/GameCenter1"), QStringLiteral("org.kde.GameCenter1"), bus);
|
||||
QVERIFY2(iface.isValid(), "GameCenter1 DBus interface not valid");
|
||||
|
||||
const QByteArray markerNeedle = (QStringLiteral("ALAKARTE_TEST_MARKER=") + m_markerValue).toUtf8();
|
||||
killPids(pidsWithEnvironEntry(markerNeedle));
|
||||
|
||||
const QString lutrisId = QStringLiteral("rungameid/%1").arg(QStringLiteral("lutris-test-1"));
|
||||
QVariantMap spec;
|
||||
spec.insert(QStringLiteral("provider"), QStringLiteral("lutris"));
|
||||
spec.insert(QStringLiteral("command"), QStringLiteral("lutris lutris:%1").arg(lutrisId));
|
||||
spec.insert(QStringLiteral("gameId"), QStringLiteral("test-lutris-%1").arg(QUuid::createUuid().toString(QUuid::WithoutBraces)));
|
||||
|
||||
QDBusReply<QString> launchReply = iface.call(QStringLiteral("Launch"), spec);
|
||||
QVERIFY2(launchReply.isValid(), qPrintable(launchReply.error().message()));
|
||||
const QString sessionId = launchReply.value();
|
||||
QVERIFY2(!sessionId.isEmpty(), "Launch returned empty sessionId");
|
||||
const QString unitName = QStringLiteral("alakarte-game-%1.scope").arg(sessionId);
|
||||
|
||||
QDBusReply<void> stopReply = iface.call(QStringLiteral("Stop"), sessionId);
|
||||
QVERIFY2(stopReply.isValid(), qPrintable(stopReply.error().message()));
|
||||
|
||||
waitForAnyEnvironEntry(markerNeedle, 5000);
|
||||
const bool cleaned = waitForNoEnvironEntry(markerNeedle, 15000);
|
||||
if (!cleaned) {
|
||||
killPids(pidsWithEnvironEntry(markerNeedle));
|
||||
}
|
||||
QVERIFY2(cleaned, "marker process still alive after Stop() during Launching (lutris)");
|
||||
|
||||
QVERIFY2(waitForUnitNotActive(unitName, 15000), "systemd unit still active after Stop() during Launching (lutris)");
|
||||
}
|
||||
|
||||
void StopLaunchingRegressionTest::stopByGameIdWhileLaunchingLutris()
|
||||
{
|
||||
QDBusConnection bus = QDBusConnection::sessionBus();
|
||||
if (!bus.isConnected()) {
|
||||
QSKIP("session bus not available");
|
||||
}
|
||||
|
||||
QDBusInterface iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/GameCenter1"), QStringLiteral("org.kde.GameCenter1"), bus);
|
||||
QVERIFY2(iface.isValid(), "GameCenter1 DBus interface not valid");
|
||||
|
||||
const QByteArray markerNeedle = (QStringLiteral("ALAKARTE_TEST_MARKER=") + m_markerValue).toUtf8();
|
||||
killPids(pidsWithEnvironEntry(markerNeedle));
|
||||
|
||||
const QString gameId = QStringLiteral("test-lutris-stop-by-gameid-%1").arg(QUuid::createUuid().toString(QUuid::WithoutBraces));
|
||||
QVariantMap spec;
|
||||
spec.insert(QStringLiteral("provider"), QStringLiteral("lutris"));
|
||||
spec.insert(QStringLiteral("command"), QStringLiteral("lutris lutris:rungameid/%1").arg(QStringLiteral("lutris-test-2")));
|
||||
spec.insert(QStringLiteral("gameId"), gameId);
|
||||
|
||||
QDBusReply<QString> launchReply = iface.call(QStringLiteral("Launch"), spec);
|
||||
QVERIFY2(launchReply.isValid(), qPrintable(launchReply.error().message()));
|
||||
const QString sessionId = launchReply.value();
|
||||
QVERIFY2(!sessionId.isEmpty(), "Launch returned empty sessionId");
|
||||
const QString unitName = QStringLiteral("alakarte-game-%1.scope").arg(sessionId);
|
||||
|
||||
QDBusReply<void> stopReply = iface.call(QStringLiteral("StopByGameId"), gameId);
|
||||
QVERIFY2(stopReply.isValid(), qPrintable(stopReply.error().message()));
|
||||
|
||||
waitForAnyEnvironEntry(markerNeedle, 5000);
|
||||
const bool cleaned = waitForNoEnvironEntry(markerNeedle, 15000);
|
||||
if (!cleaned) {
|
||||
killPids(pidsWithEnvironEntry(markerNeedle));
|
||||
}
|
||||
QVERIFY2(cleaned, "marker process still alive after StopByGameId() during Launching (lutris)");
|
||||
|
||||
QVERIFY2(waitForUnitNotActive(unitName, 15000), "systemd unit still active after StopByGameId() during Launching (lutris)");
|
||||
}
|
||||
|
||||
void StopLaunchingRegressionTest::stopDirectLaunch()
|
||||
{
|
||||
QDBusConnection bus = QDBusConnection::sessionBus();
|
||||
if (!bus.isConnected()) {
|
||||
QSKIP("session bus not available");
|
||||
}
|
||||
|
||||
QDBusInterface iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/GameCenter1"), QStringLiteral("org.kde.GameCenter1"), bus);
|
||||
QVERIFY2(iface.isValid(), "GameCenter1 DBus interface not valid");
|
||||
|
||||
const QByteArray markerNeedle = (QStringLiteral("ALAKARTE_TEST_MARKER=") + m_markerValue).toUtf8();
|
||||
killPids(pidsWithEnvironEntry(markerNeedle));
|
||||
|
||||
const QString gameId = QStringLiteral("test-direct-stop-%1").arg(QUuid::createUuid().toString(QUuid::WithoutBraces));
|
||||
|
||||
const QString envExe = QStandardPaths::findExecutable(QStringLiteral("env"));
|
||||
if (envExe.isEmpty()) {
|
||||
QSKIP("env executable not found");
|
||||
}
|
||||
|
||||
QVariantMap spec;
|
||||
spec.insert(QStringLiteral("provider"), QStringLiteral("manual"));
|
||||
spec.insert(QStringLiteral("program"), envExe);
|
||||
spec.insert(QStringLiteral("args"),
|
||||
QStringList{QStringLiteral("ALAKARTE_TEST_MARKER=%1").arg(m_markerValue), QStringLiteral("/bin/sleep"), QStringLiteral("60")});
|
||||
spec.insert(QStringLiteral("gameId"), gameId);
|
||||
|
||||
QDBusReply<QString> launchReply = iface.call(QStringLiteral("Launch"), spec);
|
||||
QVERIFY2(launchReply.isValid(), qPrintable(launchReply.error().message()));
|
||||
const QString sessionId = launchReply.value();
|
||||
QVERIFY2(!sessionId.isEmpty(), "Launch returned empty sessionId");
|
||||
const QString unitName = QStringLiteral("alakarte-game-%1.scope").arg(sessionId);
|
||||
|
||||
QVERIFY2(waitForAnyEnvironEntry(markerNeedle, 5000), "direct launch marker process did not appear");
|
||||
|
||||
QDBusReply<void> stopReply = iface.call(QStringLiteral("Stop"), sessionId);
|
||||
QVERIFY2(stopReply.isValid(), qPrintable(stopReply.error().message()));
|
||||
|
||||
const bool cleaned = waitForNoEnvironEntry(markerNeedle, 15000);
|
||||
if (!cleaned) {
|
||||
killPids(pidsWithEnvironEntry(markerNeedle));
|
||||
}
|
||||
QVERIFY2(cleaned, "marker process still alive after Stop() (direct launch)");
|
||||
|
||||
QVERIFY2(waitForUnitNotActive(unitName, 15000), "systemd unit still active after Stop() (direct launch)");
|
||||
}
|
||||
|
||||
void StopLaunchingRegressionTest::stopByGameIdDirectLaunch()
|
||||
{
|
||||
QDBusConnection bus = QDBusConnection::sessionBus();
|
||||
if (!bus.isConnected()) {
|
||||
QSKIP("session bus not available");
|
||||
}
|
||||
|
||||
QDBusInterface iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/GameCenter1"), QStringLiteral("org.kde.GameCenter1"), bus);
|
||||
QVERIFY2(iface.isValid(), "GameCenter1 DBus interface not valid");
|
||||
|
||||
const QByteArray markerNeedle = (QStringLiteral("ALAKARTE_TEST_MARKER=") + m_markerValue).toUtf8();
|
||||
killPids(pidsWithEnvironEntry(markerNeedle));
|
||||
|
||||
const QString gameId = QStringLiteral("test-direct-stop-by-gameid-%1").arg(QUuid::createUuid().toString(QUuid::WithoutBraces));
|
||||
|
||||
const QString envExe = QStandardPaths::findExecutable(QStringLiteral("env"));
|
||||
if (envExe.isEmpty()) {
|
||||
QSKIP("env executable not found");
|
||||
}
|
||||
|
||||
QVariantMap spec;
|
||||
spec.insert(QStringLiteral("provider"), QStringLiteral("manual"));
|
||||
spec.insert(QStringLiteral("program"), envExe);
|
||||
spec.insert(QStringLiteral("args"),
|
||||
QStringList{QStringLiteral("ALAKARTE_TEST_MARKER=%1").arg(m_markerValue), QStringLiteral("/bin/sleep"), QStringLiteral("60")});
|
||||
spec.insert(QStringLiteral("gameId"), gameId);
|
||||
|
||||
QDBusReply<QString> launchReply = iface.call(QStringLiteral("Launch"), spec);
|
||||
QVERIFY2(launchReply.isValid(), qPrintable(launchReply.error().message()));
|
||||
const QString sessionId = launchReply.value();
|
||||
QVERIFY2(!sessionId.isEmpty(), "Launch returned empty sessionId");
|
||||
const QString unitName = QStringLiteral("alakarte-game-%1.scope").arg(sessionId);
|
||||
|
||||
QVERIFY2(waitForAnyEnvironEntry(markerNeedle, 5000), "direct launch marker process did not appear");
|
||||
|
||||
QDBusReply<void> stopReply = iface.call(QStringLiteral("StopByGameId"), gameId);
|
||||
QVERIFY2(stopReply.isValid(), qPrintable(stopReply.error().message()));
|
||||
|
||||
const bool cleaned = waitForNoEnvironEntry(markerNeedle, 15000);
|
||||
if (!cleaned) {
|
||||
killPids(pidsWithEnvironEntry(markerNeedle));
|
||||
}
|
||||
QVERIFY2(cleaned, "marker process still alive after StopByGameId() (direct launch)");
|
||||
|
||||
QVERIFY2(waitForUnitNotActive(unitName, 15000), "systemd unit still active after StopByGameId() (direct launch)");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
const QStringList args = app.arguments();
|
||||
const QString gamecenterPath = takeArgValue(args, QStringLiteral("--gamecenter"));
|
||||
app.setProperty("alakarte_test_gamecenter", gamecenterPath);
|
||||
|
||||
QStringList filtered;
|
||||
filtered.reserve(args.size());
|
||||
if (!args.isEmpty()) {
|
||||
filtered.push_back(args.first());
|
||||
}
|
||||
for (int i = 1; i < args.size(); ++i) {
|
||||
const QString a = args.at(i);
|
||||
if (a == QLatin1String("--gamecenter")) {
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
filtered.push_back(a);
|
||||
}
|
||||
|
||||
StopLaunchingRegressionTest tc;
|
||||
return QTest::qExec(&tc, filtered);
|
||||
}
|
||||
|
||||
#include "stop_launching_regression_test.moc"
|
||||
Loading…
Reference in a new issue