// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2026 A-La-Karte Contributors #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gamecenter1interface.h" #include "input1interface.h" #include "runner1interface.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 QVariant unwrapDbusVariant(QVariant v) { if (v.metaType() == QMetaType::fromType()) { v = v.value().variant(); } return v; } static QVariantMap unwrapVariantMap(QVariant v) { v = unwrapDbusVariant(v); if (v.metaType() == QMetaType::fromType()) { const QDBusArgument arg = v.value(); return qdbus_cast(arg); } if (v.canConvert()) { return v.toMap(); } return {}; } } class DbusSmokeTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void pingGameCenter(); void gameCenterUniqueness(); void gameCenterPolicyAndCapabilities(); void gameCenterLaunchFailureContract(); void pingRunner(); void runnerVersionAndListRunners(); void runnerResolveLaunchMissingProgram(); void runnerResolveLaunchUnknownRunnerId(); void pingInput(); void inputVersionAndCapabilities(); void inputProfilesCrud(); 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() { org::kde::GameCenter1 iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/ALaKarte/GameCenter1"), m_bus); QVERIFY(iface.isValid()); iface.setTimeout(2000); QDBusPendingReply reply = iface.Ping(); reply.waitForFinished(); QVERIFY2(!reply.isError(), qPrintable(reply.error().message())); QCOMPARE(reply.value(), QStringLiteral("ok")); } void DbusSmokeTest::gameCenterUniqueness() { pingGameCenter(); QVERIFY(m_bus.interface()); const QDBusReply ownerBeforeReply = m_bus.interface()->serviceOwner(QStringLiteral("org.kde.GameCenter1")); QVERIFY(ownerBeforeReply.isValid()); const QString ownerBefore = ownerBeforeReply.value(); QVERIFY(!ownerBefore.isEmpty()); QProcess second; startDaemon(second, m_gamecenterPath); QVERIFY(second.waitForStarted(5000)); const bool finished = second.waitForFinished(5000); if (!finished) { stopProcess(second); } QVERIFY(finished); QCOMPARE(second.exitStatus(), QProcess::NormalExit); const QDBusReply ownerAfterReply = m_bus.interface()->serviceOwner(QStringLiteral("org.kde.GameCenter1")); QVERIFY(ownerAfterReply.isValid()); QCOMPARE(ownerAfterReply.value(), ownerBefore); pingGameCenter(); } void DbusSmokeTest::gameCenterPolicyAndCapabilities() { org::kde::GameCenter1 iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/ALaKarte/GameCenter1"), m_bus); QVERIFY(iface.isValid()); iface.setTimeout(2000); { QDBusPendingReply reply = iface.Version(); reply.waitForFinished(); QVERIFY2(!reply.isError(), qPrintable(reply.error().message())); QCOMPARE(reply.argumentAt<0>(), 1u); QCOMPARE(reply.argumentAt<1>(), 0u); } { QDBusPendingReply reply = iface.GetCapabilities(); reply.waitForFinished(); QVERIFY2(!reply.isError(), qPrintable(reply.error().message())); const QVariantMap caps = reply.value(); QVERIFY(caps.contains(QStringLiteral("supportsSystemd"))); QVERIFY(caps.value(QStringLiteral("supportsSystemd")).canConvert()); QVERIFY(caps.contains(QStringLiteral("supportsSystemBus"))); QCOMPARE(caps.value(QStringLiteral("supportsSystemBus")).toBool(), false); QVERIFY(caps.contains(QStringLiteral("supportsPowerProfiles"))); QVERIFY(caps.value(QStringLiteral("supportsPowerProfiles")).canConvert()); } QVariantMap originalPolicy; { QDBusPendingReply reply = iface.GetPolicy(); reply.waitForFinished(); QVERIFY2(!reply.isError(), qPrintable(reply.error().message())); originalPolicy = reply.value(); } { QVariantMap policy; policy.insert(QStringLiteral("maxConcurrent"), 0); QDBusPendingReply<> reply = iface.SetPolicy(policy); reply.waitForFinished(); QVERIFY2(!reply.isError(), qPrintable(reply.error().message())); } { QDBusPendingReply reply = iface.GetPolicy(); reply.waitForFinished(); QVERIFY2(!reply.isError(), qPrintable(reply.error().message())); QCOMPARE(reply.value().value(QStringLiteral("maxConcurrent")).toInt(), 0); } { const int originalMaxConcurrent = originalPolicy.value(QStringLiteral("maxConcurrent"), 0).toInt(); QVariantMap policy; policy.insert(QStringLiteral("maxConcurrent"), originalMaxConcurrent); QDBusPendingReply<> reply = iface.SetPolicy(policy); reply.waitForFinished(); QVERIFY2(!reply.isError(), qPrintable(reply.error().message())); } { QDBusPendingReply reply = iface.ListSessions(); reply.waitForFinished(); QVERIFY2(!reply.isError(), qPrintable(reply.error().message())); } { QDBusPendingReply<> reply = iface.StopByGameId(QStringLiteral("alakarte-test-nonexistent")); reply.waitForFinished(); QVERIFY2(!reply.isError(), qPrintable(reply.error().message())); } } void DbusSmokeTest::gameCenterLaunchFailureContract() { org::kde::GameCenter1 iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/ALaKarte/GameCenter1"), m_bus); QVERIFY(iface.isValid()); iface.setTimeout(2000); QSignalSpy launchFailedSpy(&iface, &org::kde::GameCenter1::LaunchFailed); QVERIFY(launchFailedSpy.isValid()); QVariantMap launchSpec; launchSpec.insert(QStringLiteral("gameId"), QStringLiteral("alakarte-test-game")); launchSpec.insert(QStringLiteral("program"), QStringLiteral("/bin/true")); launchSpec.insert(QStringLiteral("args"), QStringList{}); QDBusPendingReply reply = iface.Launch(launchSpec); reply.waitForFinished(); QVERIFY2(!reply.isError(), qPrintable(reply.error().message())); QVERIFY(reply.value().isEmpty()); QTRY_COMPARE_WITH_TIMEOUT(launchFailedSpy.count(), 1, 2000); const QList args = launchFailedSpy.takeFirst(); QCOMPARE(args.size(), 1); const QVariantMap error = unwrapVariantMap(args.first()); QVERIFY(error.contains(QStringLiteral("error"))); QVERIFY(error.value(QStringLiteral("error")).toString().contains(QStringLiteral("systemd"), Qt::CaseInsensitive)); QCOMPARE(error.value(QStringLiteral("gameId")).toString(), QStringLiteral("alakarte-test-game")); } void DbusSmokeTest::pingRunner() { org::kde::ALaKarte::Runner1 iface(QStringLiteral("org.kde.ALaKarte.Runner1"), QStringLiteral("/org/kde/ALaKarte/Runner1"), m_bus); QVERIFY(iface.isValid()); iface.setTimeout(2000); QDBusPendingReply reply = iface.Ping(); reply.waitForFinished(); QVERIFY2(!reply.isError(), qPrintable(reply.error().message())); QCOMPARE(reply.value(), QStringLiteral("ok")); } void DbusSmokeTest::runnerVersionAndListRunners() { org::kde::ALaKarte::Runner1 iface(QStringLiteral("org.kde.ALaKarte.Runner1"), QStringLiteral("/org/kde/ALaKarte/Runner1"), m_bus); QVERIFY(iface.isValid()); iface.setTimeout(5000); { QDBusPendingReply reply = iface.Version(); reply.waitForFinished(); QVERIFY2(!reply.isError(), qPrintable(reply.error().message())); QCOMPARE(reply.argumentAt<0>(), 1u); QCOMPARE(reply.argumentAt<1>(), 0u); } QDBusPendingReply reply = iface.ListRunners(); reply.waitForFinished(); QVERIFY2(!reply.isError(), qPrintable(reply.error().message())); const QVariantList list = reply.value(); QSet ids; for (const QVariant &v : list) { const QVariantMap m = unwrapVariantMap(v); const QString id = m.value(QStringLiteral("id")).toString(); const QString type = m.value(QStringLiteral("type")).toString(); const QString path = m.value(QStringLiteral("path")).toString(); QVERIFY2(!id.isEmpty(), "runner entry missing id"); QVERIFY2(!type.isEmpty(), qPrintable(QStringLiteral("runner '%1' missing type").arg(id))); QVERIFY2(!path.isEmpty(), qPrintable(QStringLiteral("runner '%1' missing path").arg(id))); QVERIFY2(!ids.contains(id), qPrintable(QStringLiteral("duplicate runner id '%1'").arg(id))); ids.insert(id); } } void DbusSmokeTest::pingInput() { org::kde::ALaKarte::Input1 iface(QStringLiteral("org.kde.ALaKarte.Input1"), QStringLiteral("/org/kde/ALaKarte/Input1"), m_bus); QVERIFY(iface.isValid()); iface.setTimeout(2000); QDBusPendingReply reply = iface.Ping(); reply.waitForFinished(); QVERIFY2(!reply.isError(), qPrintable(reply.error().message())); QCOMPARE(reply.value(), QStringLiteral("ok")); } void DbusSmokeTest::inputVersionAndCapabilities() { org::kde::ALaKarte::Input1 iface(QStringLiteral("org.kde.ALaKarte.Input1"), QStringLiteral("/org/kde/ALaKarte/Input1"), m_bus); QVERIFY(iface.isValid()); iface.setTimeout(2000); { QDBusPendingReply reply = iface.Version(); reply.waitForFinished(); QVERIFY2(!reply.isError(), qPrintable(reply.error().message())); QCOMPARE(reply.argumentAt<0>(), 1u); QCOMPARE(reply.argumentAt<1>(), 0u); } { QDBusPendingReply reply = iface.GetCapabilities(); reply.waitForFinished(); QVERIFY2(!reply.isError(), qPrintable(reply.error().message())); const QVariantMap caps = reply.value(); QVERIFY(caps.contains(QStringLiteral("supportsBattery"))); QVERIFY(caps.value(QStringLiteral("supportsBattery")).canConvert()); QCOMPARE(caps.value(QStringLiteral("supportsBattery")).toBool(), true); QVERIFY(caps.contains(QStringLiteral("supportsHotplug"))); QVERIFY(caps.value(QStringLiteral("supportsHotplug")).canConvert()); QCOMPARE(caps.value(QStringLiteral("supportsHotplug")).toBool(), true); QVERIFY(caps.contains(QStringLiteral("supportsProfiles"))); QVERIFY(caps.value(QStringLiteral("supportsProfiles")).canConvert()); QCOMPARE(caps.value(QStringLiteral("supportsProfiles")).toBool(), true); } } void DbusSmokeTest::inputProfilesCrud() { org::kde::ALaKarte::Input1 iface(QStringLiteral("org.kde.ALaKarte.Input1"), QStringLiteral("/org/kde/ALaKarte/Input1"), m_bus); QVERIFY(iface.isValid()); iface.setTimeout(2000); QDBusPendingReply createReply = iface.CreateProfile(QStringLiteral("Test Profile")); createReply.waitForFinished(); QVERIFY2(!createReply.isError(), qPrintable(createReply.error().message())); const QString profileId = createReply.value(); QVERIFY(!profileId.isEmpty()); { QDBusPendingReply listReply = iface.ListProfiles(); listReply.waitForFinished(); QVERIFY2(!listReply.isError(), qPrintable(listReply.error().message())); const QVariantList list = listReply.value(); bool found = false; for (const QVariant &v : list) { const QVariantMap m = unwrapVariantMap(v); if (m.value(QStringLiteral("id")).toString() == profileId) { found = true; QCOMPARE(m.value(QStringLiteral("name")).toString(), QStringLiteral("Test Profile")); break; } } QVERIFY(found); } { QDBusPendingReply getReply = iface.GetProfile(profileId); getReply.waitForFinished(); QVERIFY2(!getReply.isError(), qPrintable(getReply.error().message())); const QVariantMap m = getReply.value(); QCOMPARE(m.value(QStringLiteral("id")).toString(), profileId); QCOMPARE(m.value(QStringLiteral("name")).toString(), QStringLiteral("Test Profile")); } { QDBusPendingReply delReply = iface.DeleteProfile(profileId); delReply.waitForFinished(); QVERIFY2(!delReply.isError(), qPrintable(delReply.error().message())); QVERIFY(delReply.value()); } { QDBusPendingReply listReply = iface.ListProfiles(); listReply.waitForFinished(); QVERIFY2(!listReply.isError(), qPrintable(listReply.error().message())); const QVariantList list = listReply.value(); for (const QVariant &v : list) { const QVariantMap m = unwrapVariantMap(v); QVERIFY(m.value(QStringLiteral("id")).toString() != profileId); } } } void DbusSmokeTest::runnerResolveLaunchNative() { org::kde::ALaKarte::Runner1 iface(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{}); QDBusPendingReply reply = iface.ResolveLaunch(spec); reply.waitForFinished(); QVERIFY2(!reply.isError(), qPrintable(reply.error().message())); 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::runnerResolveLaunchMissingProgram() { org::kde::ALaKarte::Runner1 iface(QStringLiteral("org.kde.ALaKarte.Runner1"), QStringLiteral("/org/kde/ALaKarte/Runner1"), m_bus); QVERIFY(iface.isValid()); iface.setTimeout(2000); QVariantMap spec; spec.insert(QStringLiteral("args"), QStringList{}); QDBusPendingReply reply = iface.ResolveLaunch(spec); reply.waitForFinished(); QVERIFY2(!reply.isError(), qPrintable(reply.error().message())); const QVariantMap out = reply.value(); QCOMPARE(out.value(QStringLiteral("ok")).toBool(), false); QVERIFY(out.contains(QStringLiteral("error"))); QVERIFY(out.value(QStringLiteral("error")).toString().contains(QStringLiteral("missing program"), Qt::CaseInsensitive)); } void DbusSmokeTest::runnerResolveLaunchUnknownRunnerId() { org::kde::ALaKarte::Runner1 iface(QStringLiteral("org.kde.ALaKarte.Runner1"), QStringLiteral("/org/kde/ALaKarte/Runner1"), m_bus); QVERIFY(iface.isValid()); iface.setTimeout(2000); QVariantMap spec; spec.insert(QStringLiteral("runnerId"), QStringLiteral("alakarte-test-nonexistent")); spec.insert(QStringLiteral("program"), QStringLiteral("/bin/true")); spec.insert(QStringLiteral("args"), QStringList{}); QDBusPendingReply reply = iface.ResolveLaunch(spec); reply.waitForFinished(); QVERIFY2(!reply.isError(), qPrintable(reply.error().message())); const QVariantMap out = reply.value(); QCOMPARE(out.value(QStringLiteral("ok")).toBool(), false); QVERIFY(out.contains(QStringLiteral("error"))); QVERIFY(out.value(QStringLiteral("error")).toString().contains(QStringLiteral("unknown runnerId"), Qt::CaseInsensitive)); } void DbusSmokeTest::runnerGameProfiles() { org::kde::ALaKarte::Runner1 iface(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"); { QDBusPendingReply reply = iface.ClearGameProfile(gameId); reply.waitForFinished(); QVERIFY2(!reply.isError(), qPrintable(reply.error().message())); 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")}}); QDBusPendingReply reply = iface.SetGameProfile(spec); reply.waitForFinished(); QVERIFY2(!reply.isError(), qPrintable(reply.error().message())); 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")); } { QDBusPendingReply reply = iface.GetGameProfile(gameId); reply.waitForFinished(); QVERIFY2(!reply.isError(), qPrintable(reply.error().message())); 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")}); QDBusPendingReply reply = iface.ResolveLaunch(spec); reply.waitForFinished(); QVERIFY2(!reply.isError(), qPrintable(reply.error().message())); 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")); } { QDBusPendingReply reply = iface.ListGameProfiles(); reply.waitForFinished(); QVERIFY2(!reply.isError(), qPrintable(reply.error().message())); 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); } { QDBusPendingReply reply = iface.ClearGameProfile(gameId); reply.waitForFinished(); QVERIFY2(!reply.isError(), qPrintable(reply.error().message())); 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"