From 8891e85dbcf4f9c5ed625aab6d87501b21f39c46 Mon Sep 17 00:00:00 2001 From: Marco Allegretti Date: Sat, 14 Feb 2026 13:34:10 +0100 Subject: [PATCH] tests: recover sessions after gamecenter restart --- tests/stop_launching_regression_test.cpp | 96 ++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/tests/stop_launching_regression_test.cpp b/tests/stop_launching_regression_test.cpp index fc09bb9..273d4c2 100644 --- a/tests/stop_launching_regression_test.cpp +++ b/tests/stop_launching_regression_test.cpp @@ -2,6 +2,7 @@ // SPDX-FileCopyrightText: 2026 A-La-Karte Contributors #include +#include #include #include #include @@ -53,6 +54,27 @@ static bool waitForService(QDBusConnection &bus, const QString &service, int tim return false; } +static QVariant unwrapDbusVariant(QVariant v) +{ + if (v.canConvert()) { + v = v.value().variant(); + } + return v; +} + +static QVariantMap unwrapVariantMap(QVariant v) +{ + v = unwrapDbusVariant(v); + if (v.canConvert()) { + const QDBusArgument arg = v.value(); + return qdbus_cast(arg); + } + if (v.canConvert()) { + return v.toMap(); + } + return {}; +} + static QByteArray readProcFile(const QString &path, qint64 maxSize = 65536) { QFile f(path); @@ -226,6 +248,8 @@ private Q_SLOTS: void stopDirectLaunch(); void stopByGameIdDirectLaunch(); + void recoverSessionsAfterDaemonRestart(); + private: QString m_gamecenterPath; QProcess m_gamecenter; @@ -351,6 +375,78 @@ void StopLaunchingRegressionTest::cleanupTestCase() } } +void StopLaunchingRegressionTest::recoverSessionsAfterDaemonRestart() +{ + QDBusConnection bus = QDBusConnection::sessionBus(); + if (!bus.isConnected()) { + QSKIP("session bus not available"); + } + + QDBusInterface iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/ALaKarte/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 envExe = QStandardPaths::findExecutable(QStringLiteral("env")); + if (envExe.isEmpty()) { + QSKIP("env executable not found"); + } + + const QString gameId = QStringLiteral("test-reattach-%1").arg(QUuid::createUuid().toString(QUuid::WithoutBraces)); + + 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); + + const QDBusReply 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"); + + m_gamecenter.kill(); + QVERIFY(m_gamecenter.waitForFinished(5000)); + QVERIFY2(waitForNoEnvironEntry(markerNeedle, 1000) == false, "marker process unexpectedly disappeared after daemon kill"); + + m_gamecenter.start(); + QVERIFY(m_gamecenter.waitForStarted(5000)); + QVERIFY2(waitForService(bus, QStringLiteral("org.kde.GameCenter1"), 5000), "GameCenter1 service did not re-appear after restart"); + + QDBusInterface iface2(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/ALaKarte/GameCenter1"), QStringLiteral("org.kde.GameCenter1"), bus); + QVERIFY2(iface2.isValid(), "GameCenter1 DBus interface not valid after restart"); + + const QDBusReply listReply = iface2.call(QStringLiteral("ListSessions")); + QVERIFY2(listReply.isValid(), qPrintable(listReply.error().message())); + + bool found = false; + for (const QVariant &v : listReply.value()) { + const QVariantMap m = unwrapVariantMap(v); + if (m.value(QStringLiteral("sessionId")).toString() == sessionId) { + found = true; + QCOMPARE(m.value(QStringLiteral("gameId")).toString(), gameId); + QCOMPARE(m.value(QStringLiteral("state")).toString(), QStringLiteral("Running")); + break; + } + } + QVERIFY2(found, "expected recovered session not found in ListSessions() after restart"); + + const QDBusReply stopReply = iface2.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() on recovered session"); + QVERIFY2(waitForUnitNotActive(unitName, 15000), "systemd unit still active after Stop() on recovered session"); +} + void StopLaunchingRegressionTest::stopWhileLaunchingSteam() { QDBusConnection bus = QDBusConnection::sessionBus();