Advance Wayland and KDE package bring-up

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-04-14 10:51:06 +01:00
parent 51f3c21121
commit cf12defd28
15214 changed files with 20594243 additions and 269 deletions
@@ -0,0 +1,23 @@
remove_definitions(-DQT_NO_CAST_FROM_ASCII)
find_package(Qt6Test ${REQUIRED_QT_VERSION} REQUIRED CONFIG)
include(ECMAddTests)
if(UNIX)
add_executable(kdbussimpleservice kdbussimpleservice.cpp)
target_link_libraries(kdbussimpleservice Qt6::Core KF6::DBusAddons)
ecm_add_tests(
deadservicetest.cpp
LINK_LIBRARIES Qt6::Test KF6::DBusAddons
)
add_dependencies(deadservicetest kdbussimpleservice)
endif()
ecm_add_tests(
kdbusservicetest.cpp
LINK_LIBRARIES Qt6::Test KF6::DBusAddons
)
@@ -0,0 +1,107 @@
/*
This file is part of libkdbus
SPDX-FileCopyrightText: 2019 Harald Sitter <sitter@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include <QDBusConnection>
#include <QDBusConnectionInterface>
#include <QDBusServiceWatcher>
#include <QDebug>
#include <QProcess>
#include <QTest>
#include <signal.h>
#include <unistd.h>
static const QString s_serviceName = QStringLiteral("org.kde.kdbussimpleservice");
class TestObject : public QObject
{
Q_OBJECT
QList<int> m_danglingPids;
private Q_SLOTS:
void cleanupTestCase()
{
// Make sure we don't leave dangling processes even when we had an
// error and the process is stopped.
for (int pid : m_danglingPids) {
kill(pid, SIGKILL);
}
}
void testDeadService()
{
QVERIFY(!QDBusConnection::sessionBus().interface()->isServiceRegistered(s_serviceName).value());
QProcess proc1;
proc1.setProgram(QFINDTESTDATA("kdbussimpleservice"));
proc1.setProcessChannelMode(QProcess::ForwardedChannels);
proc1.start();
QVERIFY(proc1.waitForStarted());
m_danglingPids << proc1.processId();
// Spy isn't very suitable here because we'd be racing with proc1 or
// signal blocking since we'd need to unblock before spying but then
// there is an ɛ between unblock and spy.
qint64 pid1 = proc1.processId(); // store local, in case the proc disappears
QVERIFY(pid1 >= 0);
bool proc1Registered = QTest::qWaitFor(
[&]() {
QTest::qSleep(1000);
return QDBusConnection::sessionBus().interface()->servicePid(s_serviceName).value() == pid1;
},
8000);
QVERIFY(proc1Registered);
// suspend proc1, we don't want it responding on dbus anymore, but still
// be running so it holds the name.
QCOMPARE(kill(proc1.processId(), SIGSTOP), 0);
// start second instance
QProcess proc2;
proc2.setProgram(QFINDTESTDATA("kdbussimpleservice"));
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert("KCRASH_AUTO_RESTARTED", "1");
proc2.setProcessEnvironment(env);
proc2.setProcessChannelMode(QProcess::ForwardedChannels);
proc2.start();
QVERIFY(proc2.waitForStarted());
m_danglingPids << proc2.processId();
// sleep a bit. fairly awkward. we need proc2 to be waiting on the name
// but we can't easily determine when it started waiting. in lieu of
// better instrumentation let's just sleep a bit.
qDebug() << "sleeping";
QTest::qSleep(4000);
// Let proc1 go up in flames so that dbus-daemon reclaims the name and
// gives it to proc2.
qDebug() << "murder on the orient express";
QCOMPARE(0, kill(proc1.processId(), SIGUSR1));
QCOMPARE(0, kill(proc1.processId(), SIGCONT));
qint64 pid2 = proc2.processId(); // store local, in case the proc disappears
QVERIFY(pid2 >= 0);
// Wait for service to be owned by proc2.
bool proc2Registered = QTest::qWaitFor(
[&]() {
QTest::qSleep(1000);
return QDBusConnection::sessionBus().interface()->servicePid(s_serviceName).value() == pid2;
},
8000);
QVERIFY(proc2Registered);
proc1.kill();
m_danglingPids.removeAll(pid1);
proc2.kill();
m_danglingPids.removeAll(pid2);
}
};
QTEST_MAIN(TestObject)
#include "deadservicetest.moc"
@@ -0,0 +1,158 @@
/*
This file is part of libkdbus
SPDX-FileCopyrightText: 1999 Waldo Bastian <bastian@kde.org>
SPDX-FileCopyrightText: 2011 David Faure <faure@kde.org>
SPDX-FileCopyrightText: 2011 Kevin Ottens <ervin@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QMetaObject>
#include <QProcess>
#include <QTimer>
#include <kdbusservice.h>
#include <stdio.h>
class TestObject : public QObject
{
Q_OBJECT
public:
TestObject(KDBusService *service)
: m_proc(nullptr)
, m_callCount(0)
, m_service(service)
{
}
~TestObject() override
{
if (m_proc) {
m_proc->waitForFinished();
}
}
int callCount() const
{
return m_callCount;
}
void slotActivateRequested(const QStringList &args, const QString &workingDirectory)
{
Q_UNUSED(workingDirectory);
qDebug() << "Application executed with args" << args;
++m_callCount;
if (m_callCount == 1) {
Q_ASSERT(args.count() == 1);
Q_ASSERT(args.at(0) == QLatin1String("dummy call"));
} else if (m_callCount == 2) {
Q_ASSERT(args.count() == 2);
Q_ASSERT(args.at(1) == QLatin1String("bad call"));
m_service->setExitValue(4);
} else if (m_callCount == 3) {
Q_ASSERT(args.count() == 3);
Q_ASSERT(args.at(1) == QLatin1String("real call"));
Q_ASSERT(args.at(2) == QLatin1String("second arg"));
// OK, all done, quit
QCoreApplication::instance()->quit();
}
}
public Q_SLOTS:
void firstCall()
{
QStringList args;
args << QStringLiteral("bad call");
executeNewChild(args);
}
private Q_SLOTS:
void slotProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
Q_UNUSED(exitStatus)
qDebug() << "Process exited with code" << exitCode;
m_proc = nullptr;
if (m_callCount == 2) {
Q_ASSERT(exitCode == 4);
secondCall();
}
}
void secondCall()
{
QStringList args;
args << QStringLiteral("real call") << QStringLiteral("second arg");
executeNewChild(args);
}
private:
void executeNewChild(const QStringList &args)
{
// Duplicated from kglobalsettingstest.cpp - make a shared helper method?
m_proc = new QProcess(this);
connect(m_proc, &QProcess::finished, this, &TestObject::slotProcessFinished);
QString appName = QStringLiteral("kdbusservicetest");
#ifdef Q_OS_WIN
appName += ".exe";
#else
if (QFile::exists(appName + ".shell")) {
appName = "./" + appName + ".shell";
} else {
Q_ASSERT(QFile::exists(appName));
appName = "./" + appName;
}
#endif
qDebug() << "about to run" << appName << args;
m_proc->start(appName, args);
}
QProcess *m_proc;
int m_callCount;
KDBusService *m_service;
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QCoreApplication::setApplicationName(QStringLiteral("kdbusservicetest"));
QCoreApplication::setOrganizationDomain(QStringLiteral("kde.org"));
QDir::setCurrent(QCoreApplication::applicationDirPath());
KDBusService service(KDBusService::Unique);
TestObject testObject(&service);
QObject::connect(&service, &KDBusService::activateRequested, &testObject, &TestObject::slotActivateRequested);
// Testcase for the problem coming from the old fork-on-startup solution:
// the "Activate" D-Bus call would time out if the app took too much time
// to be ready.
// printf("Sleeping.\n");
// sleep(200);
QStringList args;
args << QStringLiteral("dummy call");
auto activateSignal = [&service, &args]() {
service.activateRequested(args, QDir::currentPath());
};
QMetaObject::invokeMethod(&service, activateSignal, Qt::QueuedConnection);
QTimer::singleShot(400, &testObject, &TestObject::firstCall);
qDebug() << "Running.";
a.exec();
qDebug() << "Terminating.";
Q_ASSERT(testObject.callCount() == 3);
const bool ok = testObject.callCount() == 3;
return ok ? 0 : 1;
}
#include "kdbusservicetest.moc"
@@ -0,0 +1,79 @@
/*
This file is part of libkdbus
SPDX-FileCopyrightText: 2019 Harald Sitter <sitter@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include <QCoreApplication>
#include <QDebug>
#include <QThread>
#include <kdbusservice.h>
// sigaction
#include <signal.h>
// close
#include <unistd.h>
// rlimit
#include <sys/resource.h>
#include <sys/time.h>
// USR1.
// Close all sockets and eventually ABRT. This mimics behavior during KCrash
// handling. Closing all sockets will effectively disconnect us from the bus
// and result in the daemon reclaiming all our service names and allowing
// other processes to register them instead.
void usr1_handler(int signum)
{
qDebug() << "usr1" << signum << SIGSEGV;
// Close all remaining file descriptors so we drop off of the bus.
struct rlimit rlp;
getrlimit(RLIMIT_NOFILE, &rlp);
for (int i = 3; i < (int)rlp.rlim_cur; i++) {
close(i);
}
// Sleep a bit for good measure. We could actually loop ad infinitum here
// as after USR1 we are expected to get killed. In the interest of sane
// behavior we'll simply exit on our own as well though.
sleep(4);
abort();
}
// Simple application under test.
// Closes all sockets on USR1 and aborts to simulate a kcrash shutdown behavior
// which can result in a service registration race.
int main(int argc, char *argv[])
{
qDebug() << "hello there!";
struct sigaction action;
action.sa_handler = usr1_handler;
sigemptyset(&action.sa_mask);
sigaddset(&action.sa_mask, SIGUSR1);
action.sa_flags = SA_RESTART;
if (sigaction(SIGUSR1, &action, nullptr) < 0) {
qDebug() << "failed to register segv handler";
return 1;
}
QCoreApplication app(argc, argv);
QCoreApplication::setApplicationName("kdbussimpleservice");
QCoreApplication::setOrganizationDomain("kde.org");
KDBusService service(KDBusService::Unique);
if (!service.isRegistered()) {
qDebug() << "service not registered => exiting";
return 1;
}
qDebug() << "service registered";
int ret = app.exec();
qDebug() << "exiting deadservice";
return ret;
}