d50fd6acfa
The loops don't change the m_connections container. The call chain is: - registerObjectPeer() unittest constructs a MyServer, which connects QDBusServer::newConnection to MyServer::handleConnection(), the latter stores each new connection's name in m_connections - An QTestEventLoop is entered, which triggers handleConnection(), handleConnection() calls exitLoop() at the bottom (this is repeated multiple times) - server.unregisterObject() is called, iterating over m_connections - server.registerObject() is called iterating over m_connections - between the unregisterObject() call and the registerObject() calls m_connections is not modified AFAICS Thus no need for taking a copy of m_connections (not that it matters much, it's a QStringList with size() == 3). Change-Id: Idaea2ca4d3b27fc88d39f8434e3817a2a4098c72 Reviewed-by: Marc Mutz <marc.mutz@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
291 lines
6.7 KiB
C++
291 lines
6.7 KiB
C++
// Copyright (C) 2016 The Qt Company Ltd.
|
|
// Copyright (C) 2016 Intel Corporation.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
|
|
#ifndef TST_QDBUSCONNECTION_H
|
|
#define TST_QDBUSCONNECTION_H
|
|
|
|
#include <QObject>
|
|
#include <QTest>
|
|
#include <QTestEventLoop>
|
|
#include <QDBusMessage>
|
|
#include <QDBusConnection>
|
|
#include <QDBusServer>
|
|
#include <QDBusVirtualObject>
|
|
|
|
class BaseObject: public QObject
|
|
{
|
|
Q_OBJECT
|
|
Q_CLASSINFO("D-Bus Interface", "local.BaseObject")
|
|
public:
|
|
BaseObject(QObject *parent = nullptr) : QObject(parent) { }
|
|
public slots:
|
|
void anotherMethod() { }
|
|
signals:
|
|
void baseObjectSignal();
|
|
};
|
|
|
|
class MyObject: public BaseObject
|
|
{
|
|
Q_OBJECT
|
|
Q_CLASSINFO("D-Bus Interface", "local.MyObject")
|
|
public slots:
|
|
void method(const QDBusMessage &msg);
|
|
|
|
public:
|
|
static QString path;
|
|
int callCount;
|
|
MyObject(QObject *parent = nullptr) : BaseObject(parent), callCount(0) {}
|
|
|
|
signals:
|
|
void myObjectSignal();
|
|
};
|
|
|
|
class MyObjectWithoutInterface: public QObject
|
|
{
|
|
Q_OBJECT
|
|
public slots:
|
|
void method(const QDBusMessage &msg);
|
|
|
|
public:
|
|
static QString path;
|
|
static QString interface;
|
|
int callCount;
|
|
MyObjectWithoutInterface(QObject *parent = nullptr) : QObject(parent), callCount(0) {}
|
|
};
|
|
|
|
class SignalReceiver : public QObject
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
QString argumentReceived;
|
|
int signalsReceived;
|
|
SignalReceiver() : signalsReceived(0) {}
|
|
|
|
public slots:
|
|
void oneSlot(const QString &arg) { ++signalsReceived; argumentReceived = arg;}
|
|
void oneSlot() { ++signalsReceived; }
|
|
void exitLoop() { ++signalsReceived; QTestEventLoop::instance().exitLoop(); }
|
|
void secondCallWithCallback();
|
|
};
|
|
|
|
class tst_QDBusConnection: public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
static int hookCallCount;
|
|
tst_QDBusConnection();
|
|
|
|
public slots:
|
|
void init();
|
|
void cleanup();
|
|
|
|
private slots:
|
|
void noConnection();
|
|
void connectToBus();
|
|
void connectToPeer();
|
|
void connect();
|
|
void send();
|
|
void sendWithGui();
|
|
void sendAsync();
|
|
void sendSignal();
|
|
void sendSignalToName();
|
|
void sendSignalToOtherName();
|
|
|
|
void registerObject_data();
|
|
void registerObject();
|
|
void registerObjectWithInterface_data();
|
|
void registerObjectWithInterface();
|
|
void registerObjectPeer_data();
|
|
void registerObjectPeer();
|
|
void registerObject2();
|
|
void registerObjectPeer2();
|
|
|
|
void registerQObjectChildren();
|
|
void registerQObjectChildrenPeer();
|
|
|
|
void callSelf();
|
|
void callSelfByAnotherName_data();
|
|
void callSelfByAnotherName();
|
|
void multipleInterfacesInQObject();
|
|
|
|
void connectSignal();
|
|
void slotsWithLessParameters();
|
|
void nestedCallWithCallback();
|
|
|
|
void serviceRegistrationRaceCondition();
|
|
|
|
void registerVirtualObject();
|
|
void callVirtualObject();
|
|
void callVirtualObjectLocal();
|
|
void pendingCallWhenDisconnected();
|
|
|
|
void emptyServerAddress();
|
|
|
|
void parentClassSignal();
|
|
|
|
public:
|
|
QString serviceName() const { return "org.qtproject.Qt.Autotests.QDBusConnection"; }
|
|
bool callMethod(const QDBusConnection &conn, const QString &path);
|
|
bool callMethod(const QDBusConnection &conn, const QString &path, const QString &interface);
|
|
bool callMethodPeer(const QDBusConnection &conn, const QString &path);
|
|
};
|
|
|
|
class QDBusSpy: public QObject
|
|
{
|
|
Q_OBJECT
|
|
public slots:
|
|
void handlePing(const QString &str) { args.clear(); args << str; }
|
|
void asyncReply(const QDBusMessage &msg) { args = msg.arguments(); }
|
|
|
|
public:
|
|
QList<QVariant> args;
|
|
};
|
|
|
|
class MyServer : public QDBusServer
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
MyServer(QString path) : m_path(path), m_connections()
|
|
{
|
|
connect(this, SIGNAL(newConnection(QDBusConnection)), SLOT(handleConnection(QDBusConnection)));
|
|
}
|
|
|
|
bool registerObject(const QDBusConnection& c)
|
|
{
|
|
QDBusConnection conn(c);
|
|
if (!conn.registerObject(m_path, &m_obj, QDBusConnection::ExportAllSlots))
|
|
return false;
|
|
if (!(conn.objectRegisteredAt(m_path) == &m_obj))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool registerObject()
|
|
{
|
|
for (const QString &name : std::as_const(m_connections)) {
|
|
if (!registerObject(QDBusConnection(name)))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void unregisterObject()
|
|
{
|
|
for (const QString &name : std::as_const(m_connections)) {
|
|
QDBusConnection c(name);
|
|
c.unregisterObject(m_path);
|
|
}
|
|
}
|
|
|
|
public slots:
|
|
void handleConnection(const QDBusConnection& c)
|
|
{
|
|
m_connections << c.name();
|
|
QVERIFY(isConnected());
|
|
QVERIFY(c.isConnected());
|
|
QVERIFY(registerObject(c));
|
|
QTestEventLoop::instance().exitLoop();
|
|
}
|
|
|
|
private:
|
|
MyObject m_obj;
|
|
QString m_path;
|
|
QStringList m_connections;
|
|
};
|
|
|
|
class MyServer2 : public QDBusServer
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
MyServer2() : m_conn("none")
|
|
{
|
|
connect(this, SIGNAL(newConnection(QDBusConnection)), SLOT(handleConnection(QDBusConnection)));
|
|
}
|
|
|
|
QDBusConnection connection()
|
|
{
|
|
return m_conn;
|
|
}
|
|
|
|
public slots:
|
|
void handleConnection(const QDBusConnection& c)
|
|
{
|
|
m_conn = c;
|
|
QVERIFY(isConnected());
|
|
QVERIFY(m_conn.isConnected());
|
|
QTestEventLoop::instance().exitLoop();
|
|
}
|
|
|
|
private:
|
|
MyObject m_obj;
|
|
QDBusConnection m_conn;
|
|
};
|
|
|
|
class TestObject : public QObject
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
TestObject(QObject *parent = nullptr) : QObject(parent) {}
|
|
~TestObject() {}
|
|
|
|
QString func;
|
|
|
|
public slots:
|
|
void test0() { func = "test0"; }
|
|
void test1(int i) { func = "test1 " + QString::number(i); }
|
|
int test2() { func = "test2"; return 43; }
|
|
int test3(int i) { func = "test2"; return i + 1; }
|
|
};
|
|
|
|
class RaceConditionSignalWaiter : public QObject
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
int count;
|
|
RaceConditionSignalWaiter() : count (0) {}
|
|
virtual ~RaceConditionSignalWaiter() {}
|
|
|
|
public slots:
|
|
void countUp() { ++count; emit done(); }
|
|
signals:
|
|
void done();
|
|
};
|
|
|
|
class VirtualObject: public QDBusVirtualObject
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
VirtualObject() :success(true) {}
|
|
|
|
QString introspect(const QString & /* path */) const override
|
|
{
|
|
return QString();
|
|
}
|
|
|
|
bool handleMessage(const QDBusMessage &message, const QDBusConnection &connection) override {
|
|
++callCount;
|
|
lastMessage = message;
|
|
|
|
if (success) {
|
|
QDBusMessage reply = message.createReply(replyArguments);
|
|
connection.send(reply);
|
|
}
|
|
emit messageReceived(message);
|
|
return success;
|
|
}
|
|
signals:
|
|
void messageReceived(const QDBusMessage &message) const;
|
|
|
|
public:
|
|
mutable QDBusMessage lastMessage;
|
|
QVariantList replyArguments;
|
|
mutable int callCount;
|
|
bool success;
|
|
};
|
|
|
|
|
|
#endif // TST_QDBUSCONNECTION_H
|
|
|