From 6d032e84de8238e555a191b04311ed6a811ddd87 Mon Sep 17 00:00:00 2001
From: "Daniele E. Domenichelli" <daniele.domenichelli@gmail.com>
Date: Mon, 18 Apr 2011 11:21:17 -0500
Subject: QtDBus: Add unit tests for QDBusInterface

Merge-request: 2343
Reviewed-by: Marius Storm-Olsen <marius.storm-olsen@nokia.com>
Reviewed-by: Thiago Macieira <thiago.macieira@nokia.com>
---
 tests/auto/qdbusinterface/myobject.h              | 164 ++++++
 tests/auto/qdbusinterface/qdbusinterface.pro      |   7 +-
 tests/auto/qdbusinterface/qmyserver/qmyserver.cpp | 155 ++++++
 tests/auto/qdbusinterface/qmyserver/qmyserver.pro |   5 +
 tests/auto/qdbusinterface/test/test.pro           |   7 +
 tests/auto/qdbusinterface/tst_qdbusinterface.cpp  | 613 ++++++++++++++++++----
 6 files changed, 832 insertions(+), 119 deletions(-)
 create mode 100644 tests/auto/qdbusinterface/myobject.h
 create mode 100644 tests/auto/qdbusinterface/qmyserver/qmyserver.cpp
 create mode 100644 tests/auto/qdbusinterface/qmyserver/qmyserver.pro
 create mode 100644 tests/auto/qdbusinterface/test/test.pro

diff --git a/tests/auto/qdbusinterface/myobject.h b/tests/auto/qdbusinterface/myobject.h
new file mode 100644
index 0000000..68b8d1a
--- /dev/null
+++ b/tests/auto/qdbusinterface/myobject.h
@@ -0,0 +1,164 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MYOBJECT_H
+#define MYOBJECT_H
+
+#include <QtCore/QObject>
+#include <QtDBus/QtDBus>
+
+Q_DECLARE_METATYPE(QVariantList)
+
+class MyObject: public QObject
+{
+    Q_OBJECT
+    Q_CLASSINFO("D-Bus Interface", "com.trolltech.QtDBus.MyObject")
+    Q_CLASSINFO("D-Bus Introspection", ""
+"  <interface name=\"com.trolltech.QtDBus.MyObject\" >\n"
+"    <property access=\"readwrite\" type=\"i\" name=\"prop1\" />\n"
+"    <property name=\"complexProp\" type=\"ai\" access=\"readwrite\">\n"
+"      <annotation name=\"com.trolltech.QtDBus.QtTypeName\" value=\"QList&lt;int&gt;\"/>\n"
+"    </property>\n"
+"    <signal name=\"somethingHappened\" >\n"
+"      <arg direction=\"out\" type=\"s\" />\n"
+"    </signal>\n"
+"    <method name=\"ping\" >\n"
+"      <arg direction=\"in\" type=\"v\" name=\"ping\" />\n"
+"      <arg direction=\"out\" type=\"v\" name=\"ping\" />\n"
+"    </method>\n"
+"    <method name=\"ping_invokable\" >\n"
+"      <arg direction=\"in\" type=\"v\" name=\"ping_invokable\" />\n"
+"      <arg direction=\"out\" type=\"v\" name=\"ping_invokable\" />\n"
+"    </method>\n"
+"    <method name=\"ping\" >\n"
+"      <arg direction=\"in\" type=\"v\" name=\"ping1\" />\n"
+"      <arg direction=\"in\" type=\"v\" name=\"ping2\" />\n"
+"      <arg direction=\"out\" type=\"v\" name=\"pong1\" />\n"
+"      <arg direction=\"out\" type=\"v\" name=\"pong2\" />\n"
+"    </method>\n"
+"    <method name=\"ping_invokable\" >\n"
+"      <arg direction=\"in\" type=\"v\" name=\"ping1_invokable\" />\n"
+"      <arg direction=\"in\" type=\"v\" name=\"ping2_invokable\" />\n"
+"      <arg direction=\"out\" type=\"v\" name=\"pong1_invokable\" />\n"
+"      <arg direction=\"out\" type=\"v\" name=\"pong2_invokable\" />\n"
+"    </method>\n"
+"    <method name=\"ping\" >\n"
+"      <arg direction=\"in\" type=\"ai\" name=\"ping\" />\n"
+"      <arg direction=\"out\" type=\"ai\" name=\"ping\" />\n"
+"      <annotation name=\"com.trolltech.QtDBus.QtTypeName.In0\" value=\"QList&lt;int&gt;\"/>\n"
+"      <annotation name=\"com.trolltech.QtDBus.QtTypeName.Out0\" value=\"QList&lt;int&gt;\"/>\n"
+"    </method>\n"
+"    <method name=\"ping_invokable\" >\n"
+"      <arg direction=\"in\" type=\"ai\" name=\"ping_invokable\" />\n"
+"      <arg direction=\"out\" type=\"ai\" name=\"ping_invokable\" />\n"
+"      <annotation name=\"com.trolltech.QtDBus.QtTypeName.In0\" value=\"QList&lt;int&gt;\"/>\n"
+"      <annotation name=\"com.trolltech.QtDBus.QtTypeName.Out0\" value=\"QList&lt;int&gt;\"/>\n"
+"    </method>\n"
+"  </interface>\n"
+        "")
+    Q_PROPERTY(int prop1 READ prop1 WRITE setProp1)
+    Q_PROPERTY(QList<int> complexProp READ complexProp WRITE setComplexProp)
+
+public:
+    static int callCount;
+    static QVariantList callArgs;
+    MyObject()
+    {
+        QObject *subObject = new QObject(this);
+        subObject->setObjectName("subObject");
+    }
+
+    int m_prop1;
+    int prop1() const
+    {
+        ++callCount;
+        return m_prop1;
+    }
+    void setProp1(int value)
+    {
+        ++callCount;
+        m_prop1 = value;
+    }
+
+    QList<int> m_complexProp;
+    QList<int> complexProp() const
+    {
+        ++callCount;
+        return m_complexProp;
+    }
+    void setComplexProp(const QList<int> &value)
+    {
+        ++callCount;
+        m_complexProp = value;
+    }
+
+    Q_INVOKABLE void ping_invokable(QDBusMessage msg)
+    {
+        QDBusConnection sender = QDBusConnection::sender();
+        if (!sender.isConnected())
+            exit(1);
+
+        ++callCount;
+        callArgs = msg.arguments();
+
+        msg.setDelayedReply(true);
+        if (!sender.send(msg.createReply(callArgs)))
+            exit(1);
+    }
+
+public slots:
+
+    void ping(QDBusMessage msg)
+    {
+        QDBusConnection sender = QDBusConnection::sender();
+        if (!sender.isConnected())
+            exit(1);
+
+        ++callCount;
+        callArgs = msg.arguments();
+
+        msg.setDelayedReply(true);
+        if (!sender.send(msg.createReply(callArgs)))
+            exit(1);
+    }
+};
+
+#endif // INTERFACE_H
diff --git a/tests/auto/qdbusinterface/qdbusinterface.pro b/tests/auto/qdbusinterface/qdbusinterface.pro
index ac14ab7..0aca06c 100644
--- a/tests/auto/qdbusinterface/qdbusinterface.pro
+++ b/tests/auto/qdbusinterface/qdbusinterface.pro
@@ -1,10 +1,11 @@
 load(qttest_p4)
 QT = core
 contains(QT_CONFIG,dbus): {
-	SOURCES += tst_qdbusinterface.cpp
-	QT += dbus
+    TEMPLATE = subdirs
+    CONFIG += ordered
+    SUBDIRS = qmyserver test
 } else {
-	SOURCES += ../qdbusmarshall/dummy.cpp
+    SOURCES += ../qdbusmarshall/dummy.cpp
 }
 
 
diff --git a/tests/auto/qdbusinterface/qmyserver/qmyserver.cpp b/tests/auto/qdbusinterface/qmyserver/qmyserver.cpp
new file mode 100644
index 0000000..c68f7bf
--- /dev/null
+++ b/tests/auto/qdbusinterface/qmyserver/qmyserver.cpp
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QtCore/QtCore>
+#include <QtDBus/QtDBus>
+
+#include "../myobject.h"
+
+static const char serviceName[] = "com.trolltech.autotests.qmyserver";
+static const char objectPath[] = "/com/trolltech/qmyserver";
+//static const char *interfaceName = serviceName;
+
+int MyObject::callCount = 0;
+QVariantList MyObject::callArgs;
+
+class MyServer : public QDBusServer
+{
+    Q_OBJECT
+    Q_CLASSINFO("D-Bus Interface", "com.trolltech.autotests.qmyserver")
+
+public:
+    MyServer(QString addr = "unix:tmpdir=/tmp", QObject* parent = 0)
+        : QDBusServer(addr, parent),
+          m_conn("none")
+    {
+        connect(this, SIGNAL(newConnection(const QDBusConnection&)), SLOT(handleConnection(const QDBusConnection&)));
+    }
+
+public slots:
+    QString address() const
+    {
+        return QDBusServer::address();
+    }
+
+    bool isConnected() const
+    {
+        return m_conn.isConnected();
+    }
+
+    void emitSignal(const QString &interface, const QString &name, const QString &arg)
+    {
+        QDBusMessage msg = QDBusMessage::createSignal("/", interface, name);
+        msg << arg;
+        m_conn.send(msg);
+    }
+
+    void reset()
+    {
+        MyObject::callCount = 0;
+        obj.m_complexProp.clear();
+    }
+
+    int callCount()
+    {
+        return MyObject::callCount;
+    }
+
+    QVariantList callArgs()
+    {
+        qDebug() << "callArgs" << MyObject::callArgs.count();
+        return MyObject::callArgs;
+    }
+
+    void setProp1(int val)
+    {
+        obj.m_prop1 = val;
+    }
+
+    int prop1()
+    {
+        return obj.m_prop1;
+    }
+
+    void setComplexProp(QList<int> val)
+    {
+        obj.m_complexProp = val;
+    }
+
+    QList<int> complexProp()
+    {
+        return obj.m_complexProp;
+    }
+
+
+private slots:
+    void handleConnection(const QDBusConnection& con)
+    {
+        m_conn = con;
+        m_conn.registerObject("/", &obj, QDBusConnection::ExportAllProperties
+                       | QDBusConnection::ExportAllSlots
+                       | QDBusConnection::ExportAllInvokables);
+    }
+
+private:
+    QDBusConnection m_conn;
+    MyObject obj;
+};
+
+int main(int argc, char *argv[])
+{
+    QCoreApplication app(argc, argv);
+
+    QDBusConnection con = QDBusConnection::sessionBus();
+    if (!con.isConnected())
+        exit(1);
+
+    if (!con.registerService(serviceName))
+        exit(2);
+
+    MyServer server;
+    con.registerObject(objectPath, &server, QDBusConnection::ExportAllSlots);
+
+    printf("ready.\n");
+
+    return app.exec();
+}
+
+#include "qmyserver.moc"
\ No newline at end of file
diff --git a/tests/auto/qdbusinterface/qmyserver/qmyserver.pro b/tests/auto/qdbusinterface/qmyserver/qmyserver.pro
new file mode 100644
index 0000000..f4fe02c
--- /dev/null
+++ b/tests/auto/qdbusinterface/qmyserver/qmyserver.pro
@@ -0,0 +1,5 @@
+SOURCES = qmyserver.cpp
+HEADERS = ../myobject.h
+TARGET = qmyserver
+QT += dbus
+QT -= gui
diff --git a/tests/auto/qdbusinterface/test/test.pro b/tests/auto/qdbusinterface/test/test.pro
new file mode 100644
index 0000000..3252188
--- /dev/null
+++ b/tests/auto/qdbusinterface/test/test.pro
@@ -0,0 +1,7 @@
+load(qttest_p4)
+SOURCES += ../tst_qdbusinterface.cpp
+HEADERS += ../myobject.h
+TARGET = ../tst_qdbusinterface
+
+QT = core
+QT += dbus
diff --git a/tests/auto/qdbusinterface/tst_qdbusinterface.cpp b/tests/auto/qdbusinterface/tst_qdbusinterface.cpp
index ce11459..04b3d72 100644
--- a/tests/auto/qdbusinterface/tst_qdbusinterface.cpp
+++ b/tests/auto/qdbusinterface/tst_qdbusinterface.cpp
@@ -40,6 +40,7 @@
 ****************************************************************************/
 /* -*- C++ -*-
  */
+
 #include <qcoreapplication.h>
 #include <qmetatype.h>
 #include <QtTest/QtTest>
@@ -47,125 +48,15 @@
 #include <QtDBus/QtDBus>
 
 #include "../qdbusmarshall/common.h"
-
-Q_DECLARE_METATYPE(QVariantList)
+#include "myobject.h"
 
 #define TEST_INTERFACE_NAME "com.trolltech.QtDBus.MyObject"
 #define TEST_SIGNAL_NAME "somethingHappened"
 
-class MyObject: public QObject
-{
-    Q_OBJECT
-    Q_CLASSINFO("D-Bus Interface", "com.trolltech.QtDBus.MyObject")
-    Q_CLASSINFO("D-Bus Introspection", ""
-"  <interface name=\"com.trolltech.QtDBus.MyObject\" >\n"
-"    <property access=\"readwrite\" type=\"i\" name=\"prop1\" />\n"
-"    <property name=\"complexProp\" type=\"ai\" access=\"readwrite\">\n"
-"      <annotation name=\"com.trolltech.QtDBus.QtTypeName\" value=\"QList&lt;int&gt;\"/>\n"
-"    </property>\n"
-"    <signal name=\"somethingHappened\" >\n"
-"      <arg direction=\"out\" type=\"s\" />\n"
-"    </signal>\n"
-"    <method name=\"ping\" >\n"
-"      <arg direction=\"in\" type=\"v\" name=\"ping\" />\n"
-"      <arg direction=\"out\" type=\"v\" name=\"ping\" />\n"
-"    </method>\n"
-"    <method name=\"ping_invokable\" >\n"
-"      <arg direction=\"in\" type=\"v\" name=\"ping_invokable\" />\n"
-"      <arg direction=\"out\" type=\"v\" name=\"ping_invokable\" />\n"
-"    </method>\n"
-"    <method name=\"ping\" >\n"
-"      <arg direction=\"in\" type=\"v\" name=\"ping1\" />\n"
-"      <arg direction=\"in\" type=\"v\" name=\"ping2\" />\n"
-"      <arg direction=\"out\" type=\"v\" name=\"pong1\" />\n"
-"      <arg direction=\"out\" type=\"v\" name=\"pong2\" />\n"
-"    </method>\n"
-"    <method name=\"ping_invokable\" >\n"
-"      <arg direction=\"in\" type=\"v\" name=\"ping1_invokable\" />\n"
-"      <arg direction=\"in\" type=\"v\" name=\"ping2_invokable\" />\n"
-"      <arg direction=\"out\" type=\"v\" name=\"pong1_invokable\" />\n"
-"      <arg direction=\"out\" type=\"v\" name=\"pong2_invokable\" />\n"
-"    </method>\n"
-"    <method name=\"ping\" >\n"
-"      <arg direction=\"in\" type=\"ai\" name=\"ping\" />\n"
-"      <arg direction=\"out\" type=\"ai\" name=\"ping\" />\n"
-"      <annotation name=\"com.trolltech.QtDBus.QtTypeName.In0\" value=\"QList&lt;int&gt;\"/>\n"
-"      <annotation name=\"com.trolltech.QtDBus.QtTypeName.Out0\" value=\"QList&lt;int&gt;\"/>\n"
-"    </method>\n"
-"    <method name=\"ping_invokable\" >\n"
-"      <arg direction=\"in\" type=\"ai\" name=\"ping_invokable\" />\n"
-"      <arg direction=\"out\" type=\"ai\" name=\"ping_invokable\" />\n"
-"      <annotation name=\"com.trolltech.QtDBus.QtTypeName.In0\" value=\"QList&lt;int&gt;\"/>\n"
-"      <annotation name=\"com.trolltech.QtDBus.QtTypeName.Out0\" value=\"QList&lt;int&gt;\"/>\n"
-"    </method>\n"
-"  </interface>\n"
-        "")
-    Q_PROPERTY(int prop1 READ prop1 WRITE setProp1)
-    Q_PROPERTY(QList<int> complexProp READ complexProp WRITE setComplexProp)
-
-public:
-    static int callCount;
-    static QVariantList callArgs;
-    MyObject()
-    {
-        QObject *subObject = new QObject(this);
-        subObject->setObjectName("subObject");
-    }
-
-    int m_prop1;
-    int prop1() const
-    {
-        ++callCount;
-        return m_prop1;
-    }
-    void setProp1(int value)
-    {
-        ++callCount;
-        m_prop1 = value;
-    }
-
-    QList<int> m_complexProp;
-    QList<int> complexProp() const
-    {
-        ++callCount;
-        return m_complexProp;
-    }
-    void setComplexProp(const QList<int> &value)
-    {
-        ++callCount;
-        m_complexProp = value;
-    }
-
-    Q_INVOKABLE void ping_invokable(QDBusMessage msg)
-    {
-        QDBusConnection sender = QDBusConnection::sender();
-        if (!sender.isConnected())
-            exit(1);
-
-        ++callCount;
-        callArgs = msg.arguments();
-
-        msg.setDelayedReply(true);
-        if (!sender.send(msg.createReply(callArgs)))
-            exit(1);
-    }
-
-public slots:
-
-    void ping(QDBusMessage msg)
-    {
-        QDBusConnection sender = QDBusConnection::sender();
-        if (!sender.isConnected())
-            exit(1);
-
-        ++callCount;
-        callArgs = msg.arguments();
+static const char serviceName[] = "com.trolltech.autotests.qmyserver";
+static const char objectPath[] = "/com/trolltech/qmyserver";
+static const char *interfaceName = serviceName;
 
-        msg.setDelayedReply(true);
-        if (!sender.send(msg.createReply(callArgs)))
-            exit(1);
-    }
-};
 int MyObject::callCount = 0;
 QVariantList MyObject::callArgs;
 
@@ -228,10 +119,70 @@ void emitSignal(const QString &interface, const QString &name, const QString &ar
     QTest::qWait(1000);
 }
 
+void emitSignalPeer(const QString &interface, const QString &name, const QString &arg)
+{
+    QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "emitSignal");
+    req << interface;
+    req << name;
+    req << arg;
+    QDBusConnection::sessionBus().send(req);
+
+    QTest::qWait(1000);
+}
+
+int callCountPeer()
+{
+    QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "callCount");
+    QDBusMessage reply = QDBusConnection::sessionBus().call(req);
+    return reply.arguments().at(0).toInt();
+}
+
+QVariantList callArgsPeer()
+{
+    QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "callArgs");
+    QDBusMessage reply = QDBusConnection::sessionBus().call(req);
+    return qdbus_cast<QVariantList>(reply.arguments().at(0));
+}
+
+void setProp1Peer(int val)
+{
+    QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "setProp1");
+    req << val;
+    QDBusMessage reply = QDBusConnection::sessionBus().call(req);
+}
+
+int prop1Peer()
+{
+    QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "prop1");
+    QDBusMessage reply = QDBusConnection::sessionBus().call(req);
+    return reply.arguments().at(0).toInt();
+}
+
+void setComplexPropPeer(QList<int> val)
+{
+    QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "setComplexProp");
+    req << qVariantFromValue(val);
+    QDBusMessage reply = QDBusConnection::sessionBus().call(req);
+}
+
+QList<int> complexPropPeer()
+{
+    QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "complexProp");
+    QDBusMessage reply = QDBusConnection::sessionBus().call(req);
+    return qdbus_cast<QList<int> >(reply.arguments().at(0));
+}
+
+void resetPeer()
+{
+    QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "reset");
+    QDBusConnection::sessionBus().call(req);
+}
+
 class tst_QDBusInterface: public QObject
 {
     Q_OBJECT
     MyObject obj;
+
 public slots:
     void testServiceOwnerChanged(const QString &service)
     {
@@ -241,6 +192,7 @@ public slots:
 
 private slots:
     void initTestCase();
+    void cleanupTestCase();
 
     void notConnected();
     void notValid();
@@ -254,14 +206,63 @@ private slots:
     void invokeMethodWithMultiReturn();
     void invokeMethodWithComplexReturn();
 
+    void introspectPeer();
+    void callMethodPeer();
+    void invokeMethodPeer();
+    void invokeMethodWithReturnPeer();
+    void invokeMethodWithMultiReturnPeer();
+    void invokeMethodWithComplexReturnPeer();
+
     void signal();
+    void signalPeer();
 
     void propertyRead();
     void propertyWrite();
     void complexPropertyRead();
     void complexPropertyWrite();
+
+    void propertyReadPeer();
+    void propertyWritePeer();
+    void complexPropertyReadPeer();
+    void complexPropertyWritePeer();
+private:
+    QProcess proc;
 };
 
+class WaitForQMyServer: public QObject
+{
+    Q_OBJECT
+public:
+    WaitForQMyServer();
+    bool ok();
+public Q_SLOTS:
+    void ownerChange(const QString &name)
+    {
+        if (name == serviceName)
+            loop.quit();
+    }
+
+private:
+    QEventLoop loop;
+};
+
+WaitForQMyServer::WaitForQMyServer()
+{
+    QDBusConnection con = QDBusConnection::sessionBus();
+    if (!ok()) {
+        connect(con.interface(), SIGNAL(serviceOwnerChanged(QString,QString,QString)),
+                SLOT(ownerChange(QString)));
+        QTimer::singleShot(2000, &loop, SLOT(quit()));
+        loop.exec();
+    }
+}
+
+bool WaitForQMyServer::ok()
+{
+    return QDBusConnection::sessionBus().isConnected() &&
+        QDBusConnection::sessionBus().interface()->isServiceRegistered(serviceName);
+}
+
 void tst_QDBusInterface::initTestCase()
 {
     QDBusConnection con = QDBusConnection::sessionBus();
@@ -271,6 +272,39 @@ void tst_QDBusInterface::initTestCase()
     con.registerObject("/", &obj, QDBusConnection::ExportAllProperties
                        | QDBusConnection::ExportAllSlots
                        | QDBusConnection::ExportAllInvokables);
+
+    // start peer server
+    #ifdef Q_OS_WIN
+    proc.start("qmyserver");
+    #else
+    proc.start("./qmyserver/qmyserver");
+    #endif
+    QVERIFY(proc.waitForStarted());
+
+    WaitForQMyServer w;
+    QVERIFY(w.ok());
+    //QTest::qWait(2000);
+
+    // get peer server address
+    QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "address");
+    QDBusMessage rpl = con.call(req);
+    QVERIFY(rpl.type() == QDBusMessage::ReplyMessage);
+    QString address = rpl.arguments().at(0).toString();
+
+    // connect to peer server
+    QDBusConnection peercon = QDBusConnection::connectToPeer(address, "peer");
+    QVERIFY(peercon.isConnected());
+
+    QDBusMessage req2 = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "isConnected");
+    QDBusMessage rpl2 = con.call(req2);
+    QVERIFY(rpl2.type() == QDBusMessage::ReplyMessage);
+    QVERIFY(rpl2.arguments().at(0).toBool());
+}
+
+void tst_QDBusInterface::cleanupTestCase()
+{
+    proc.close();
+    proc.kill();
 }
 
 void tst_QDBusInterface::notConnected()
@@ -564,7 +598,7 @@ void tst_QDBusInterface::invokeMethodWithComplexReturn()
 
     // verify that we got the reply as expected
     QCOMPARE(retArg, arg);
-    
+
     // make the INVOKABLE call without a return type
     QList<int> arg2 = QList<int>() << 24 << -74;
     QVERIFY(QMetaObject::invokeMethod(&iface, "ping", Q_RETURN_ARG(QList<int>, retArg), Q_ARG(QList<int>, arg2)));
@@ -580,6 +614,250 @@ void tst_QDBusInterface::invokeMethodWithComplexReturn()
     QCOMPARE(retArg, arg2);
 }
 
+void tst_QDBusInterface::introspectPeer()
+{
+    QDBusConnection con("peer");
+    QDBusInterface iface(QString(), QLatin1String("/"),
+                         TEST_INTERFACE_NAME, con);
+
+    const QMetaObject *mo = iface.metaObject();
+
+    QCOMPARE(mo->methodCount() - mo->methodOffset(), 7);
+    QVERIFY(mo->indexOfSignal(TEST_SIGNAL_NAME "(QString)") != -1);
+
+    QCOMPARE(mo->propertyCount() - mo->propertyOffset(), 2);
+    QVERIFY(mo->indexOfProperty("prop1") != -1);
+    QVERIFY(mo->indexOfProperty("complexProp") != -1);
+}
+
+void tst_QDBusInterface::callMethodPeer()
+{
+    QDBusConnection con("peer");
+    QDBusInterface iface(QString(), QLatin1String("/"),
+                         TEST_INTERFACE_NAME, con);
+
+    resetPeer();
+
+    // call a SLOT method
+    QDBusMessage reply = iface.call("ping", qVariantFromValue(QDBusVariant("foo")));
+    QCOMPARE(callCountPeer(), 1);
+    QCOMPARE(reply.type(), QDBusMessage::ReplyMessage);
+
+    // verify what the callee received
+    QVariantList callArgs = callArgsPeer();
+    QCOMPARE(callArgs.count(), 1);
+    QVariant v = callArgs.at(0);
+    QDBusVariant dv = qdbus_cast<QDBusVariant>(v);
+    QCOMPARE(dv.variant().type(), QVariant::String);
+    QCOMPARE(dv.variant().toString(), QString("foo"));
+
+    // verify reply
+    QCOMPARE(reply.arguments().count(), 1);
+    v = reply.arguments().at(0);
+    dv = qdbus_cast<QDBusVariant>(v);
+    QCOMPARE(dv.variant().type(), QVariant::String);
+    QCOMPARE(dv.variant().toString(), QString("foo"));
+
+    // call an INVOKABLE method
+    reply = iface.call("ping_invokable", qVariantFromValue(QDBusVariant("bar")));
+    QCOMPARE(callCountPeer(), 2);
+    QCOMPARE(reply.type(), QDBusMessage::ReplyMessage);
+
+    // verify what the callee received
+    callArgs = callArgsPeer();
+    QCOMPARE(callArgs.count(), 1);
+    v = callArgs.at(0);
+    dv = qdbus_cast<QDBusVariant>(v);
+    QCOMPARE(dv.variant().type(), QVariant::String);
+    QCOMPARE(dv.variant().toString(), QString("bar"));
+
+    // verify reply
+    QCOMPARE(reply.arguments().count(), 1);
+    v = reply.arguments().at(0);
+    dv = qdbus_cast<QDBusVariant>(v);
+    QCOMPARE(dv.variant().type(), QVariant::String);
+    QCOMPARE(dv.variant().toString(), QString("bar"));
+}
+
+void tst_QDBusInterface::invokeMethodPeer()
+{
+    QDBusConnection con("peer");
+    QDBusInterface iface(QString(), QLatin1String("/"),
+                         TEST_INTERFACE_NAME, con);
+
+    resetPeer();
+
+    // make the SLOT call without a return type
+    QDBusVariant arg("foo");
+    QVERIFY(QMetaObject::invokeMethod(&iface, "ping", Q_ARG(QDBusVariant, arg)));
+    QCOMPARE(callCountPeer(), 1);
+
+    // verify what the callee received
+    QVariantList callArgs = callArgsPeer();
+    QCOMPARE(callArgs.count(), 1);
+    QVariant v = callArgs.at(0);
+    QDBusVariant dv = qdbus_cast<QDBusVariant>(v);
+    QCOMPARE(dv.variant().type(), QVariant::String);
+    QCOMPARE(dv.variant().toString(), QString("foo"));
+
+    // make the INVOKABLE call without a return type
+    QDBusVariant arg2("bar");
+    QVERIFY(QMetaObject::invokeMethod(&iface, "ping_invokable", Q_ARG(QDBusVariant, arg2)));
+    QCOMPARE(callCountPeer(), 2);
+
+    // verify what the callee received
+    callArgs = callArgsPeer();
+    QCOMPARE(callArgs.count(), 1);
+    v = callArgs.at(0);
+    dv = qdbus_cast<QDBusVariant>(v);
+    QCOMPARE(dv.variant().type(), QVariant::String);
+    QCOMPARE(dv.variant().toString(), QString("bar"));
+}
+
+void tst_QDBusInterface::invokeMethodWithReturnPeer()
+{
+    QDBusConnection con("peer");
+    QDBusInterface iface(QString(), QLatin1String("/"),
+                         TEST_INTERFACE_NAME, con);
+
+    resetPeer();
+    QDBusVariant retArg;
+
+    // make the SLOT call without a return type
+    QDBusVariant arg("foo");
+    QVERIFY(QMetaObject::invokeMethod(&iface, "ping", Q_RETURN_ARG(QDBusVariant, retArg), Q_ARG(QDBusVariant, arg)));
+    QCOMPARE(callCountPeer(), 1);
+
+    // verify what the callee received
+    QVariantList callArgs = callArgsPeer();
+    QCOMPARE(callArgs.count(), 1);
+    QVariant v = callArgs.at(0);
+    QDBusVariant dv = qdbus_cast<QDBusVariant>(v);
+    QCOMPARE(dv.variant().type(), QVariant::String);
+    QCOMPARE(dv.variant().toString(), arg.variant().toString());
+
+    // verify that we got the reply as expected
+    QCOMPARE(retArg.variant(), arg.variant());
+
+    // make the INVOKABLE call without a return type
+    QDBusVariant arg2("bar");
+    QVERIFY(QMetaObject::invokeMethod(&iface, "ping_invokable", Q_RETURN_ARG(QDBusVariant, retArg), Q_ARG(QDBusVariant, arg2)));
+    QCOMPARE(callCountPeer(), 2);
+
+    // verify what the callee received
+    callArgs = callArgsPeer();
+    QCOMPARE(callArgs.count(), 1);
+    v = callArgs.at(0);
+    dv = qdbus_cast<QDBusVariant>(v);
+    QCOMPARE(dv.variant().type(), QVariant::String);
+    QCOMPARE(dv.variant().toString(), arg2.variant().toString());
+
+    // verify that we got the reply as expected
+    QCOMPARE(retArg.variant(), arg2.variant());
+}
+
+void tst_QDBusInterface::invokeMethodWithMultiReturnPeer()
+{
+    QDBusConnection con("peer");
+    QDBusInterface iface(QString(), QLatin1String("/"),
+                         TEST_INTERFACE_NAME, con);
+
+    resetPeer();
+    QDBusVariant retArg, retArg2;
+
+    // make the SLOT call without a return type
+    QDBusVariant arg("foo"), arg2("bar");
+    QVERIFY(QMetaObject::invokeMethod(&iface, "ping",
+                                      Q_RETURN_ARG(QDBusVariant, retArg),
+                                      Q_ARG(QDBusVariant, arg),
+                                      Q_ARG(QDBusVariant, arg2),
+                                      Q_ARG(QDBusVariant&, retArg2)));
+    QCOMPARE(callCountPeer(), 1);
+
+    // verify what the callee received
+    QVariantList callArgs = callArgsPeer();
+    QCOMPARE(callArgs.count(), 2);
+    QVariant v = callArgs.at(0);
+    QDBusVariant dv = qdbus_cast<QDBusVariant>(v);
+    QCOMPARE(dv.variant().type(), QVariant::String);
+    QCOMPARE(dv.variant().toString(), arg.variant().toString());
+
+    v = callArgs.at(1);
+    dv = qdbus_cast<QDBusVariant>(v);
+    QCOMPARE(dv.variant().type(), QVariant::String);
+    QCOMPARE(dv.variant().toString(), arg2.variant().toString());
+
+    // verify that we got the replies as expected
+    QCOMPARE(retArg.variant(), arg.variant());
+    QCOMPARE(retArg2.variant(), arg2.variant());
+
+    // make the INVOKABLE call without a return type
+    QDBusVariant arg3("hello"), arg4("world");
+    QVERIFY(QMetaObject::invokeMethod(&iface, "ping_invokable",
+                                      Q_RETURN_ARG(QDBusVariant, retArg),
+                                      Q_ARG(QDBusVariant, arg3),
+                                      Q_ARG(QDBusVariant, arg4),
+                                      Q_ARG(QDBusVariant&, retArg2)));
+    QCOMPARE(callCountPeer(), 2);
+
+    // verify what the callee received
+    callArgs = callArgsPeer();
+    QCOMPARE(callArgs.count(), 2);
+    v = callArgs.at(0);
+    dv = qdbus_cast<QDBusVariant>(v);
+    QCOMPARE(dv.variant().type(), QVariant::String);
+    QCOMPARE(dv.variant().toString(), arg3.variant().toString());
+
+    v = callArgs.at(1);
+    dv = qdbus_cast<QDBusVariant>(v);
+    QCOMPARE(dv.variant().type(), QVariant::String);
+    QCOMPARE(dv.variant().toString(), arg4.variant().toString());
+
+    // verify that we got the replies as expected
+    QCOMPARE(retArg.variant(), arg3.variant());
+    QCOMPARE(retArg2.variant(), arg4.variant());
+}
+
+void tst_QDBusInterface::invokeMethodWithComplexReturnPeer()
+{
+    QDBusConnection con("peer");
+    QDBusInterface iface(QString(), QLatin1String("/"),
+                         TEST_INTERFACE_NAME, con);
+
+    resetPeer();
+    QList<int> retArg;
+
+    // make the SLOT call without a return type
+    QList<int> arg = QList<int>() << 42 << -47;
+    QVERIFY(QMetaObject::invokeMethod(&iface, "ping", Q_RETURN_ARG(QList<int>, retArg), Q_ARG(QList<int>, arg)));
+    QCOMPARE(callCountPeer(), 1);
+
+    // verify what the callee received
+    QVariantList callArgs = callArgsPeer();
+    QCOMPARE(callArgs.count(), 1);
+    QVariant v = callArgs.at(0);
+    QCOMPARE(v.userType(), qMetaTypeId<QDBusArgument>());
+    QCOMPARE(qdbus_cast<QList<int> >(v), arg);
+
+    // verify that we got the reply as expected
+    QCOMPARE(retArg, arg);
+
+    // make the INVOKABLE call without a return type
+    QList<int> arg2 = QList<int>() << 24 << -74;
+    QVERIFY(QMetaObject::invokeMethod(&iface, "ping", Q_RETURN_ARG(QList<int>, retArg), Q_ARG(QList<int>, arg2)));
+    QCOMPARE(callCountPeer(), 2);
+
+    // verify what the callee received
+    callArgs = callArgsPeer();
+    QCOMPARE(callArgs.count(), 1);
+    v = callArgs.at(0);
+    QCOMPARE(v.userType(), qMetaTypeId<QDBusArgument>());
+    QCOMPARE(qdbus_cast<QList<int> >(v), arg2);
+
+    // verify that we got the reply as expected
+    QCOMPARE(retArg, arg2);
+}
+
 void tst_QDBusInterface::signal()
 {
     QDBusConnection con = QDBusConnection::sessionBus();
@@ -621,6 +899,47 @@ void tst_QDBusInterface::signal()
     }
 }
 
+void tst_QDBusInterface::signalPeer()
+{
+    QDBusConnection con("peer");
+    QDBusInterface iface(QString(), QLatin1String("/"),
+                         TEST_INTERFACE_NAME, con);
+
+    QString arg = "So long and thanks for all the fish";
+    {
+        Spy spy;
+        spy.connect(&iface, SIGNAL(somethingHappened(QString)), SLOT(spySlot(QString)));
+
+        emitSignalPeer(TEST_INTERFACE_NAME, TEST_SIGNAL_NAME, arg);
+        QCOMPARE(spy.count, 1);
+        QCOMPARE(spy.received, arg);
+    }
+
+    QDBusInterface iface2(QString(), QLatin1String("/"),
+                          TEST_INTERFACE_NAME, con);
+    {
+        Spy spy;
+        spy.connect(&iface, SIGNAL(somethingHappened(QString)), SLOT(spySlot(QString)));
+        spy.connect(&iface2, SIGNAL(somethingHappened(QString)), SLOT(spySlot(QString)));
+
+        emitSignalPeer(TEST_INTERFACE_NAME, TEST_SIGNAL_NAME, arg);
+        QCOMPARE(spy.count, 2);
+        QCOMPARE(spy.received, arg);
+    }
+
+    {
+        Spy spy, spy2;
+        spy.connect(&iface, SIGNAL(somethingHappened(QString)), SLOT(spySlot(QString)));
+        spy2.connect(&iface2, SIGNAL(somethingHappened(QString)), SLOT(spySlot(QString)));
+
+        emitSignalPeer(TEST_INTERFACE_NAME, TEST_SIGNAL_NAME, arg);
+        QCOMPARE(spy.count, 1);
+        QCOMPARE(spy.received, arg);
+        QCOMPARE(spy2.count, 1);
+        QCOMPARE(spy2.received, arg);
+    }
+}
+
 void tst_QDBusInterface::propertyRead()
 {
     QDBusConnection con = QDBusConnection::sessionBus();
@@ -683,7 +1002,69 @@ void tst_QDBusInterface::complexPropertyWrite()
     QCOMPARE(obj.m_complexProp, arg);
 }
 
+void tst_QDBusInterface::propertyReadPeer()
+{
+    QDBusConnection con("peer");
+    QDBusInterface iface(QString(), QLatin1String("/"),
+                         TEST_INTERFACE_NAME, con);
+
+    resetPeer();
+    int arg = 42;
+    setProp1Peer(42);
+
+    QVariant v = iface.property("prop1");
+    QVERIFY(v.isValid());
+    QCOMPARE(v.userType(), int(QVariant::Int));
+    QCOMPARE(v.toInt(), arg);
+    QCOMPARE(callCountPeer(), 1);
+}
+
+void tst_QDBusInterface::propertyWritePeer()
+{
+    QDBusConnection con("peer");
+    QDBusInterface iface(QString(), QLatin1String("/"),
+                         TEST_INTERFACE_NAME, con);
+
+    resetPeer();
+    int arg = 42;
+    setProp1Peer(0);
+
+    QVERIFY(iface.setProperty("prop1", arg));
+    QCOMPARE(callCountPeer(), 1);
+    QCOMPARE(prop1Peer(), arg);
+}
+
+void tst_QDBusInterface::complexPropertyReadPeer()
+{
+    QDBusConnection con("peer");
+    QDBusInterface iface(QString(), QLatin1String("/"),
+                         TEST_INTERFACE_NAME, con);
+
+    resetPeer();
+    QList<int> arg = QList<int>() << 42 << -47;
+    setComplexPropPeer(arg);
+
+    QVariant v = iface.property("complexProp");
+    QVERIFY(v.isValid());
+    QCOMPARE(v.userType(), qMetaTypeId<QList<int> >());
+    QCOMPARE(v.value<QList<int> >(), arg);
+    QCOMPARE(callCountPeer(), 1);
+}
+
+void tst_QDBusInterface::complexPropertyWritePeer()
+{
+    QDBusConnection con("peer");
+    QDBusInterface iface(QString(), QLatin1String("/"),
+                         TEST_INTERFACE_NAME, con);
+
+    resetPeer();
+    QList<int> arg = QList<int>() << -47 << 42;
+
+    QVERIFY(iface.setProperty("complexProp", qVariantFromValue(arg)));
+    QCOMPARE(callCountPeer(), 1);
+    QCOMPARE(complexPropPeer(), arg);
+}
+
 QTEST_MAIN(tst_QDBusInterface)
 
 #include "tst_qdbusinterface.moc"
-
-- 
cgit v0.12