a-la-karte/tests/dbus_smoketest.cpp

703 lines
26 KiB
C++

// 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 <QSet>
#include <QSignalSpy>
#include <QStandardPaths>
#include <QStringList>
#include <QUuid>
#include <QtTest>
#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<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 gameCenterUniqueness();
void gameCenterPolicyAndCapabilities();
void gameCenterLaunchFailureContract();
void pingRunner();
void runnerVersionAndListRunners();
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<QString> 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<QString> 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<QString> 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<uint, uint> reply = iface.Version();
reply.waitForFinished();
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
QCOMPARE(reply.argumentAt<0>(), 1u);
QCOMPARE(reply.argumentAt<1>(), 0u);
}
{
QDBusPendingReply<QVariantMap> 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<bool>());
QVERIFY(caps.contains(QStringLiteral("supportsSystemBus")));
QCOMPARE(caps.value(QStringLiteral("supportsSystemBus")).toBool(), false);
QVERIFY(caps.contains(QStringLiteral("supportsPowerProfiles")));
QVERIFY(caps.value(QStringLiteral("supportsPowerProfiles")).canConvert<bool>());
}
QVariantMap originalPolicy;
{
QDBusPendingReply<QVariantMap> 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<QVariantMap> 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<QVariantList> 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<QString> 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<QVariant> 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<QString> 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<uint, uint> reply = iface.Version();
reply.waitForFinished();
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
QCOMPARE(reply.argumentAt<0>(), 1u);
QCOMPARE(reply.argumentAt<1>(), 0u);
}
QDBusPendingReply<QVariantList> reply = iface.ListRunners();
reply.waitForFinished();
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
const QVariantList list = reply.value();
QSet<QString> 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<QString> 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<uint, uint> reply = iface.Version();
reply.waitForFinished();
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
QCOMPARE(reply.argumentAt<0>(), 1u);
QCOMPARE(reply.argumentAt<1>(), 0u);
}
{
QDBusPendingReply<QVariantMap> 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<bool>());
QCOMPARE(caps.value(QStringLiteral("supportsBattery")).toBool(), true);
QVERIFY(caps.contains(QStringLiteral("supportsHotplug")));
QVERIFY(caps.value(QStringLiteral("supportsHotplug")).canConvert<bool>());
QCOMPARE(caps.value(QStringLiteral("supportsHotplug")).toBool(), true);
QVERIFY(caps.contains(QStringLiteral("supportsProfiles")));
QVERIFY(caps.value(QStringLiteral("supportsProfiles")).canConvert<bool>());
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<QString> createReply = iface.CreateProfile(QStringLiteral("Test Profile"));
createReply.waitForFinished();
QVERIFY2(!createReply.isError(), qPrintable(createReply.error().message()));
const QString profileId = createReply.value();
QVERIFY(!profileId.isEmpty());
{
QDBusPendingReply<QVariantList> 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<QVariantMap> 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<bool> delReply = iface.DeleteProfile(profileId);
delReply.waitForFinished();
QVERIFY2(!delReply.isError(), qPrintable(delReply.error().message()));
QVERIFY(delReply.value());
}
{
QDBusPendingReply<QVariantList> 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<QVariantMap> 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::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<QVariantMap> 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<QVariantMap> 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<QVariantMap> 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<QVariantMap> 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<QVariantList> 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<QVariantMap> 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"