Skip to content

Commit

Permalink
Fix race condition in tst_QDBusConnection::pendingCallWhenDisconnected
Browse files Browse the repository at this point in the history
The test sent a message and hoped that the "delete server" would cause
the server object in the QDBusConnectionManager thread be deleted before
it could receive and process the message. That's racy, because on some
CI machines, it did and then sent back an error reply indicating the
object sought was not found.

Instead, let's use a child process that we can kill to make it exit at
the right time.

I've chosen to use dbus-daemon itself, because that lets us test the
actual conditions that triggered the original bug: the daemon getting
killed during a desktop session shutdown on Linux.

Change-Id: I87e17314d8b24ae983b1fffd1454483aea87c921
Reviewed-by: Tony Sarajärvi <[email protected]>
  • Loading branch information
thiagomacieira authored and Tony Sarajärvi committed Jun 6, 2016
1 parent d5ac97e commit f8b5142
Showing 1 changed file with 30 additions and 9 deletions.
39 changes: 30 additions & 9 deletions tests/auto/dbus/qdbusconnection/tst_qdbusconnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@
#include <QtTest/QtTest>
#include <QtDBus/QtDBus>

#ifdef Q_OS_UNIX
# include <sys/types.h>
# include <signal.h>
#endif

void MyObject::method(const QDBusMessage &msg)
{
path = msg.path();
Expand Down Expand Up @@ -1357,23 +1362,39 @@ void tst_QDBusConnection::callVirtualObjectLocal()

void tst_QDBusConnection::pendingCallWhenDisconnected()
{
#ifdef QT_NO_PROCESS
QSKIP("Test requires QProcess");
#else
if (!QCoreApplication::instance())
QSKIP("Test requires a QCoreApplication");

QDBusServer *server = new QDBusServer;
QDBusConnection con = QDBusConnection::connectToPeer(server->address(), "disconnect");
QTestEventLoop::instance().enterLoop(2);
QVERIFY(con.isConnected());
QDBusMessage message = QDBusMessage::createMethodCall("", "/", QString(), "method");
QDBusPendingCall reply = con.asyncCall(message);
QProcess daemon;
daemon.start("dbus-daemon", QStringList() << "--session" << "--nofork" << "--print-address");
QVERIFY2(daemon.waitForReadyRead(2000),
"Daemon didn't print its address in time; error: \"" + daemon.errorString().toLocal8Bit() +
"\"; stderr:\n" + daemon.readAllStandardError());

delete server;
QString address = QString::fromLocal8Bit(daemon.readAll().trimmed());
QDBusConnection con = QDBusConnection::connectToBus(address, "disconnect");
QVERIFY2(con.isConnected(), (con.lastError().name() + ": " + con.lastError().message()).toLocal8Bit());

QTestEventLoop::instance().enterLoop(2);
// confirm we're connected and we're alone in this bus
QCOMPARE(con.baseService(), QString(":1.0"));

// kill the bus
daemon.terminate();
daemon.waitForFinished();

// send something, which we should get an error with
QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.DBus", "/", QString(), "ListNames");
QDBusPendingCall reply = con.asyncCall(message);

reply.waitForFinished();
QVERIFY(!con.isConnected());
QVERIFY(reply.isFinished());
QVERIFY(reply.isError());
QVERIFY(reply.error().type() == QDBusError::Disconnected);
QCOMPARE(reply.error().type(), QDBusError::Disconnected);
#endif
}

QString MyObject::path;
Expand Down

0 comments on commit f8b5142

Please sign in to comment.