text: /****************************************************************************
  **
  ** Copyright (C) 2015 The Qt Company Ltd.
  ** Contact: http://www.qt.io/licensing/
  **
  ** This file is part of the test suite of the Qt Toolkit.
  **
  ** $QT_BEGIN_LICENSE:LGPL21$
  ** Commercial License Usage
  ** Licensees holding valid commercial Qt licenses may use this file in
  ** accordance with the commercial license agreement provided with the
  ** Software or, alternatively, in accordance with the terms contained in
  ** a written agreement between you and The Qt Company. For licensing terms
  ** and conditions see http://www.qt.io/terms-conditions. For further
  ** information use the contact form at http://www.qt.io/contact-us.
  **
  ** 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 or version 3 as published by the Free
  ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
  ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
  ** following information to ensure the GNU Lesser General Public License
  ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
  ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
  **
  ** As a special exception, The Qt Company gives you certain additional
  ** rights. These rights are described in The Qt Company LGPL Exception
  ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
  **
  ** $QT_END_LICENSE$
  **
  ****************************************************************************/
  #include <qcoreapplication.h>
  #include <qdebug.h>
  
  #include <QtTest/QtTest>
  
  #include <QtDBus>
  
  #include "../qdbusmarshall/common.h"
  #include "myobject.h"
  
  static const char serviceName[] = "org.qtproject.autotests.qmyserver";
  static const char objectPath[] = "/org/qtproject/qmyserver";
  static const char *interfaceName = serviceName;
  
  const char *slotSpy;
  QString valueSpy;
  
  QT_BEGIN_NAMESPACE
  namespace QTest {
      char *toString(QDBusMessage::MessageType t)
      {
          switch (t)
          {
          case QDBusMessage::InvalidMessage:
              return qstrdup("InvalidMessage");
          case QDBusMessage::MethodCallMessage:
              return qstrdup("MethodCallMessage");
          case QDBusMessage::ReplyMessage:
              return qstrdup("ReplyMessage");
          case QDBusMessage::ErrorMessage:
              return qstrdup("ErrorMessage");
          case QDBusMessage::SignalMessage:
              return qstrdup("SignalMessage");
          default:
              return 0;
          }
      }
  }
  QT_END_NAMESPACE
  
  class TypesInterface: public QDBusAbstractAdaptor
  {
      Q_OBJECT
      Q_CLASSINFO("D-Bus Interface", "local.TypesInterface")
  public:
      TypesInterface(QObject *parent)
          : QDBusAbstractAdaptor(parent)
      { }
  
      union
      {
          bool b;
          uchar uc;
          short s;
          ushort us;
          int i;
          uint ui;
          qlonglong ll;
          qulonglong ull;
          double d;
      } dataSpy;
      QVariant variantSpy;
      QString stringSpy;
      QVariantList listSpy;
      QStringList stringlistSpy;
      QByteArray bytearraySpy;
      QVariantMap mapSpy;
      StringStringMap ssmapSpy;
      LLDateTimeMap lldtmapSpy;
      MyStruct structSpy;
  
  public slots:
      void methodBool(bool b)
      {
          slotSpy = "void TypesInterface::methodBool(bool)";
          dataSpy.b = b;
      }
  
      void methodUChar(uchar uc)
      {
          slotSpy = "void TypesInterface::methodUChar(uchar)";
          dataSpy.uc = uc;
      }
  
      void methodShort(short s)
      {
          slotSpy = "void TypesInterface::methodShort(short)";
          dataSpy.s = s;
      }
  
      void methodUShort(ushort us)
      {
          slotSpy = "void TypesInterface::methodUShort(ushort)";
          dataSpy.us = us;
      }
  
      void methodInt(int i)
      {
          slotSpy = "void TypesInterface::methodInt(int)";
          dataSpy.i = i;
      }
  
      void methodUInt(uint ui)
      {
          slotSpy = "void TypesInterface::methodUInt(uint)";
          dataSpy.ui = ui;
      }
  
      void methodLongLong(qlonglong ll)
      {
          slotSpy = "void TypesInterface::methodLongLong(qlonglong)";
          dataSpy.ll = ll;
      }
  
      void methodULongLong(qulonglong ull)
      {
          slotSpy = "void TypesInterface::methodULongLong(qulonglong)";
          dataSpy.ull = ull;
      }
  
      void methodDouble(double d)
      {
          slotSpy = "void TypesInterface::methodDouble(double)";
          dataSpy.d = d;
      }
  
      void methodString(const QString &s)
      {
          slotSpy = "void TypesInterface::methodString(const QString &)";
          stringSpy = s;
      }
  
      void methodObjectPath(const QDBusObjectPath &op)
      {
          slotSpy = "void TypesInterface::methodObjectPath(const QDBusObjectPath &)";
          stringSpy = op.path();
      }
  
      void methodSignature(const QDBusSignature &s)
      {
          slotSpy = "void TypesInterface::methodSignature(const QDBusSignature &)";
          stringSpy = s.signature();
      }
  
      void methodVariant(const QDBusVariant &v)
      {
          slotSpy = "void TypesInterface::methodVariant(const QDBusVariant &)";
          variantSpy = v.variant();
      }
  
      void methodList(const QVariantList &l)
      {
          slotSpy = "void TypesInterface::methodList(const QVariantList &)";
          listSpy = l;
      }
  
      void methodStringList(const QStringList &sl)
      {
          slotSpy = "void TypesInterface::methodStringList(const QStringList &)";
          stringlistSpy = sl;
      }
  
      void methodByteArray(const QByteArray &ba)
      {
          slotSpy = "void TypesInterface::methodByteArray(const QByteArray &)";
          bytearraySpy = ba;
      }
  
      void methodMap(const QVariantMap &m)
      {
          slotSpy = "void TypesInterface::methodMap(const QVariantMap &)";
          mapSpy = m;
      }
  
      void methodSSMap(const StringStringMap &ssmap)
      {
          slotSpy = "void TypesInterface::methodSSMap(const StringStringMap &)";
          ssmapSpy = ssmap;
      }
  
      void methodLLDateTimeMap(const LLDateTimeMap &lldtmap)
      {
          slotSpy = "void TypesInterface::methodLLDateTimeMap(const LLDateTimeMap &)";
          lldtmapSpy = lldtmap;
      }
  
      void methodStruct(const MyStruct &s)
      {
          slotSpy = "void TypesInterface::methodStruct(const MyStruct &)";
          structSpy = s;
      }
  
      bool retrieveBool()
      {
          return dataSpy.b;
      }
  
      uchar retrieveUChar()
      {
          return dataSpy.uc;
      }
  
      short retrieveShort()
      {
          return dataSpy.s;
      }
  
      ushort retrieveUShort()
      {
          return dataSpy.us;
      }
  
      int retrieveInt()
      {
          return dataSpy.i;
      }
  
      uint retrieveUInt()
      {
          return dataSpy.ui;
      }
  
      qlonglong retrieveLongLong()
      {
          return dataSpy.ll;
      }
  
      qulonglong retrieveULongLong()
      {
          return dataSpy.ull;
      }
  
      double retrieveDouble()
      {
          return dataSpy.d;
      }
  
      QString retrieveString()
      {
          return stringSpy;
      }
  
      QDBusObjectPath retrieveObjectPath()
      {
          return QDBusObjectPath(stringSpy);
      }
  
      QDBusSignature retrieveSignature()
      {
          return QDBusSignature(stringSpy);
      }
  
      QDBusVariant retrieveVariant()
      {
          return QDBusVariant(variantSpy);
      }
  
      QVariantList retrieveList()
      {
          return listSpy;
      }
  
      QStringList retrieveStringList()
      {
          return stringlistSpy;
      }
  
      QByteArray retrieveByteArray()
      {
          return bytearraySpy;
      }
  
      QVariantMap retrieveMap()
      {
          return mapSpy;
      }
  
      StringStringMap retrieveSSMap()
      {
          return ssmapSpy;
      }
  
      LLDateTimeMap retrieveLLDateTimeMap()
      {
          return lldtmapSpy;
      }
  
      MyStruct retrieveStruct()
      {
          return structSpy;
      }
  };
  
  void newMyObjectPeer(int nInterfaces = 4)
  {
      QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "newMyObject");
      req << nInterfaces;
      QDBusMessage reply = QDBusConnection::sessionBus().call(req);
  }
  
  void registerMyObjectPeer(const QString & path, QDBusConnection::RegisterOptions options = QDBusConnection::ExportAdaptors)
  {
      QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "registerMyObject");
      req << path;
      req << (int)options;
      QDBusMessage reply = QDBusConnection::sessionBus().call(req);
  }
  
  void syncPeer()
  {
      static int counter = 0;
      QString reqId = QString::number(++counter);
  
      // wait for the sync signal with the right ID
      QEventLoop loop;
      QDBusConnection con("peer");
      con.connect(QString(), objectPath, interfaceName, "syncReceived",
                  QStringList() << reqId, QString(), &loop, SLOT(quit()));
  
      QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "requestSync");
      req << reqId;
      QDBusConnection::sessionBus().send(req);
  
      loop.exec();
  }
  
  void emitSignalPeer(const QString &interface, const QString &name, const QVariant &parameter)
  {
      if (parameter.isValid())
      {
          QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "emitSignal");
          req << interface;
          req << name;
          req << QVariant::fromValue(QDBusVariant(parameter));
          QDBusConnection::sessionBus().send(req);
      }
      else
      {
          QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "emitSignal2");
          req << interface;
          req << name;
          QDBusConnection::sessionBus().send(req);
      }
  }
  
  QString slotSpyPeer()
  {
      QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "slotSpyServer");
      QDBusMessage reply = QDBusConnection::sessionBus().call(req);
      return reply.arguments().at(0).toString();
  }
  
  QString valueSpyPeer()
  {
      QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "valueSpyServer");
      QDBusMessage reply = QDBusConnection::sessionBus().call(req);
      return reply.arguments().at(0).toString();
  }
  
  void clearValueSpyPeer()
  {
      QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "clearValueSpy");
      QDBusMessage reply = QDBusConnection::sessionBus().call(req);
  }
  
  class tst_QDBusAbstractAdaptor: public QObject
  {
      Q_OBJECT
  
  private slots:
      void initTestCase();
      void cleanupTestCase();
  
      void methodCalls_data();
      void methodCalls();
      void methodCallScriptable();
      void signalEmissions_data();
      void signalEmissions();
      void sameSignalDifferentPaths();
      void sameObjectDifferentPaths();
      void scriptableSignalOrNot();
      void overloadedSignalEmission_data();
      void overloadedSignalEmission();
      void readProperties();
      void readPropertiesInvalidInterface();
      void readPropertiesEmptyInterface_data();
      void readPropertiesEmptyInterface();
      void readAllProperties();
      void readAllPropertiesInvalidInterface();
      void readAllPropertiesEmptyInterface_data();
      void readAllPropertiesEmptyInterface();
      void writeProperties();
  
      void methodCallsPeer_data();
      void methodCallsPeer();
      void methodCallScriptablePeer();
      void signalEmissionsPeer_data();
      void signalEmissionsPeer();
      void sameSignalDifferentPathsPeer();
      void sameObjectDifferentPathsPeer();
      void scriptableSignalOrNotPeer();
      void overloadedSignalEmissionPeer_data();
      void overloadedSignalEmissionPeer();
      void readPropertiesPeer();
      void readPropertiesInvalidInterfacePeer();
      void readPropertiesEmptyInterfacePeer_data();
      void readPropertiesEmptyInterfacePeer();
      void readAllPropertiesPeer();
      void readAllPropertiesInvalidInterfacePeer();
      void readAllPropertiesEmptyInterfacePeer_data();
      void readAllPropertiesEmptyInterfacePeer();
      void writePropertiesPeer();
  
      void typeMatching_data();
      void typeMatching();
  
      void methodWithMoreThanOneReturnValue();
      void methodWithMoreThanOneReturnValuePeer();
  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_QDBusAbstractAdaptor::initTestCase()
  {
      commonInit();
  
      // start peer server
  #ifdef Q_OS_WIN
  #  define EXE ".exe"
  #else
  #  define EXE ""
  #endif
      proc.setProcessChannelMode(QProcess::ForwardedErrorChannel);
      proc.start(QFINDTESTDATA("qmyserver/qmyserver" EXE));
      QVERIFY2(proc.waitForStarted(), qPrintable(proc.errorString()));
      QVERIFY(proc.waitForReadyRead());
  
      WaitForQMyServer w;
      QVERIFY(w.ok());
  
      // get peer server address
      QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "address");
      QDBusMessage rpl = QDBusConnection::sessionBus().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, "waitForConnected");
      QDBusMessage rpl2 = QDBusConnection::sessionBus().call(req2);
      QVERIFY2(rpl2.type() == QDBusMessage::ReplyMessage, rpl2.errorMessage().toLatin1());
  }
  
  void tst_QDBusAbstractAdaptor::cleanupTestCase()
  {
      QDBusMessage msg = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "quit");
      QDBusConnection::sessionBus().call(msg);
      proc.waitForFinished(200);
      proc.close();
  }
  
  void tst_QDBusAbstractAdaptor::methodCalls_data()
  {
      QTest::addColumn<int>("nInterfaces");
      QTest::newRow("0") << 0;
      QTest::newRow("1") << 1;
      QTest::newRow("2") << 2;
      QTest::newRow("3") << 3;
      QTest::newRow("4") << 4;
  }
  
  void tst_QDBusAbstractAdaptor::methodCalls()
  {
      QDBusConnection con = QDBusConnection::sessionBus();
      QVERIFY(con.isConnected());
  
      //QDBusInterface emptycon.baseService(), "/", QString());
  
      {
          // must fail: no object
          QDBusInterface if1(con.baseService(), "/", "local.Interface1", con);
          QCOMPARE(if1.call(QDBus::BlockWithGui, "method").type(), QDBusMessage::ErrorMessage);
      }
  
      QFETCH(int, nInterfaces);
      MyObject obj(nInterfaces);
      con.registerObject("/", &obj);
  
      QDBusInterface if1(con.baseService(), "/", "local.Interface1", con);
      QDBusInterface if2(con.baseService(), "/", "local.Interface2", con);
      QDBusInterface if3(con.baseService(), "/", "local.Interface3", con);
      QDBusInterface if4(con.baseService(), "/", "local.Interface4", con);
  
      // must fail: no such method
      QCOMPARE(if1.call(QDBus::BlockWithGui, "method").type(), QDBusMessage::ErrorMessage);
      if (!nInterfaces--)
          return;
      if (!nInterfaces--)
          return;
  
      // simple call: one such method exists
      QCOMPARE(if2.call(QDBus::BlockWithGui, "method").type(), QDBusMessage::ReplyMessage);
      QCOMPARE(slotSpy, "void Interface2::method()");
      if (!nInterfaces--)
          return;
  
      // multiple methods in multiple interfaces, no name overlap
      QCOMPARE(if1.call(QDBus::BlockWithGui, "methodVoid").type(), QDBusMessage::ErrorMessage);
      QCOMPARE(if1.call(QDBus::BlockWithGui, "methodInt").type(), QDBusMessage::ErrorMessage);
      QCOMPARE(if1.call(QDBus::BlockWithGui, "methodString").type(), QDBusMessage::ErrorMessage);
      QCOMPARE(if2.call(QDBus::BlockWithGui, "methodVoid").type(), QDBusMessage::ErrorMessage);
      QCOMPARE(if2.call(QDBus::BlockWithGui, "methodInt").type(), QDBusMessage::ErrorMessage);
      QCOMPARE(if2.call(QDBus::BlockWithGui, "methodString").type(), QDBusMessage::ErrorMessage);
  
      QCOMPARE(if3.call(QDBus::BlockWithGui, "methodVoid").type(), QDBusMessage::ReplyMessage);
      QCOMPARE(slotSpy, "void Interface3::methodVoid()");
      QCOMPARE(if3.call(QDBus::BlockWithGui, "methodInt", 42).type(), QDBusMessage::ReplyMessage);
      QCOMPARE(slotSpy, "void Interface3::methodInt(int)");
      QCOMPARE(if3.call(QDBus::BlockWithGui, "methodString", QString("")).type(), QDBusMessage::ReplyMessage);
      QCOMPARE(slotSpy, "void Interface3::methodString(QString)");
  
      if (!nInterfaces--)
          return;
  
      // method overloading: different interfaces
      QCOMPARE(if4.call(QDBus::BlockWithGui, "method").type(), QDBusMessage::ReplyMessage);
      QCOMPARE(slotSpy, "void Interface4::method()");
  
      // method overloading: different parameters
      QCOMPARE(if4.call(QDBus::BlockWithGui, "method.i", 42).type(), QDBusMessage::ReplyMessage);
      QCOMPARE(slotSpy, "void Interface4::method(int)");
      QCOMPARE(if4.call(QDBus::BlockWithGui, "method.s", QString()).type(), QDBusMessage::ReplyMessage);
      QCOMPARE(slotSpy, "void Interface4::method(QString)");
  
  }
  
  void tst_QDBusAbstractAdaptor::methodCallScriptable()
  {
      QDBusConnection con = QDBusConnection::sessionBus();
      QVERIFY(con.isConnected());
  
      MyObject obj(2);
      con.registerObject("/", &obj);
  
      QDBusInterface if2(con.baseService(), "/", "local.Interface2", con);
  
      QCOMPARE(if2.call(QDBus::BlockWithGui,"scriptableMethod").type(), QDBusMessage::ReplyMessage);
      QCOMPARE(slotSpy, "void Interface2::scriptableMethod()");
  }
  
  static void emitSignal(MyObject *obj, const QString &iface, const QString &name,
                         const QVariant &parameter)
  {
      if (iface.endsWith('2'))
          obj->if2->emitSignal(name, parameter);
      else if (iface.endsWith('3'))
          obj->if3->emitSignal(name, parameter);
      else if (iface.endsWith('4'))
          obj->if4->emitSignal(name, parameter);
      else
          obj->emitSignal(name, parameter);
  }
  
  void tst_QDBusAbstractAdaptor::signalEmissions_data()
  {
      QTest::addColumn<QString>("interface");
      QTest::addColumn<QString>("name");
      QTest::addColumn<QString>("signature");
      QTest::addColumn<QVariant>("parameter");
  
      QTest::newRow("Interface2.signal") << "local.Interface2" << "signal" << QString() << QVariant();
      QTest::newRow("Interface3.signalVoid") << "local.Interface3" << "signalVoid" << QString() << QVariant();
      QTest::newRow("Interface3.signalInt") << "local.Interface3" << "signalInt" << "i" << QVariant(1);
      QTest::newRow("Interface3.signalString") << "local.Interface3" << "signalString" << "s" << QVariant("foo");
      QTest::newRow("MyObject.scriptableSignalVoid") << "local.MyObject" << "scriptableSignalVoid" << QString() << QVariant();
      QTest::newRow("MyObject.scriptableSignalInt") << "local.MyObject" << "scriptableSignalInt" << "i" << QVariant(1);
      QTest::newRow("MyObject.nySignalString") << "local.MyObject" << "scriptableSignalString" << "s" << QVariant("foo");
  }
  
  void tst_QDBusAbstractAdaptor::signalEmissions()
  {
      QFETCH(QString, interface);
      QFETCH(QString, name);
      QFETCH(QVariant, parameter);
  
      QDBusConnection con = QDBusConnection::sessionBus();
      QVERIFY(con.isConnected());
      con.registerService("org.qtproject.tst_QDBusAbstractAdaptor");
  
      MyObject obj(3);
      con.registerObject("/", &obj, QDBusConnection::ExportAdaptors
                                    | QDBusConnection::ExportScriptableSignals);
  
      // connect all signals and emit only one
      {
          QDBusSignalSpy spy;
          con.connect(con.baseService(), "/", "local.Interface2", "signal",
                      &spy, SLOT(slot(QDBusMessage)));
          con.connect(con.baseService(), "/", "local.Interface3", "signalVoid",
                      &spy, SLOT(slot(QDBusMessage)));
          con.connect(con.baseService(), "/", "local.Interface3", "signalInt",
                      &spy, SLOT(slot(QDBusMessage)));
          con.connect(con.baseService(), "/", "local.Interface3", "signalString",
                      &spy, SLOT(slot(QDBusMessage)));
          con.connect(con.baseService(), "/", "local.MyObject", "scriptableSignalVoid",
                      &spy, SLOT(slot(QDBusMessage)));
          con.connect(con.baseService(), "/", "local.MyObject", "scriptableSignalInt",
                      &spy, SLOT(slot(QDBusMessage)));
          con.connect(con.baseService(), "/", "local.MyObject", "scriptableSignalString",
                      &spy, SLOT(slot(QDBusMessage)));
  
          emitSignal(&obj, interface, name, parameter);
  
          QTRY_COMPARE(spy.count, 1);
          QCOMPARE(spy.interface, interface);
          QCOMPARE(spy.name, name);
          QTEST(spy.signature, "signature");
          QCOMPARE(spy.value, parameter);
      }
  
      // connect one signal and emit them all
      {
          QDBusSignalSpy spy;
          con.connect(con.baseService(), "/", interface, name, &spy, SLOT(slot(QDBusMessage)));
          emitSignal(&obj, "local.Interface2", "signal", QVariant());
          emitSignal(&obj, "local.Interface3", "signalVoid", QVariant());
          emitSignal(&obj, "local.Interface3", "signalInt", QVariant(1));
          emitSignal(&obj, "local.Interface3", "signalString", QVariant("foo"));
          emitSignal(&obj, "local.MyObject", "scriptableSignalVoid", QVariant());
          emitSignal(&obj, "local.MyObject", "scriptableSignalInt", QVariant(1));
          emitSignal(&obj, "local.MyObject", "scriptableSignalString", QVariant("foo"));
  
          QTRY_COMPARE(spy.count, 1);
          QCOMPARE(spy.interface, interface);
          QCOMPARE(spy.name, name);
          QTEST(spy.signature, "signature");
          QCOMPARE(spy.value, parameter);
      }
  }
  
  void tst_QDBusAbstractAdaptor::sameSignalDifferentPaths()
  {
      QDBusConnection con = QDBusConnection::sessionBus();
      QVERIFY(con.isConnected());
  
      MyObject obj(2);
  
      con.registerObject("/p1",&obj);
      con.registerObject("/p2",&obj);
  
      QDBusSignalSpy spy;
      con.connect(con.baseService(), "/p1", "local.Interface2", "signal", &spy, SLOT(slot(QDBusMessage)));
      obj.if2->emitSignal(QString(), QVariant());
  
      QTRY_COMPARE(spy.count, 1);
      QCOMPARE(spy.interface, QString("local.Interface2"));
      QCOMPARE(spy.name, QString("signal"));
      QVERIFY(spy.signature.isEmpty());
  
      // now connect the other one
      spy.count = 0;
      con.connect(con.baseService(), "/p2", "local.Interface2", "signal", &spy, SLOT(slot(QDBusMessage)));
      obj.if2->emitSignal(QString(), QVariant());
  
      QTRY_COMPARE(spy.count, 2);
  }
  
  void tst_QDBusAbstractAdaptor::sameObjectDifferentPaths()
  {
      QDBusConnection con = QDBusConnection::sessionBus();
      QVERIFY(con.isConnected());
  
      MyObject obj(2);
  
      con.registerObject("/p1",&obj);
      con.registerObject("/p2",&obj, 0); // don't export anything
  
      QDBusSignalSpy spy;
      con.connect(con.baseService(), "/p1", "local.Interface2", "signal", &spy, SLOT(slot(QDBusMessage)));
      con.connect(con.baseService(), "/p2", "local.Interface2", "signal", &spy, SLOT(slot(QDBusMessage)));
      obj.if2->emitSignal(QString(), QVariant());
  
      QTRY_COMPARE(spy.count, 1);
      QCOMPARE(spy.interface, QString("local.Interface2"));
      QCOMPARE(spy.name, QString("signal"));
      QVERIFY(spy.signature.isEmpty());
  }
  
  void tst_QDBusAbstractAdaptor::scriptableSignalOrNot()
  {
      QDBusConnection con = QDBusConnection::sessionBus();
      QVERIFY(con.isConnected());
  
      {
          MyObject obj(0);
  
          con.registerObject("/p1",&obj, QDBusConnection::ExportScriptableSignals);
          con.registerObject("/p2",&obj, 0); // don't export anything
  
          QDBusSignalSpy spy;
          con.connect(con.baseService(), "/p1", "local.MyObject", "scriptableSignalVoid", &spy, SLOT(slot(QDBusMessage)));
          con.connect(con.baseService(), "/p2", "local.MyObject", "scriptableSignalVoid", &spy, SLOT(slot(QDBusMessage)));
          con.connect(con.baseService(), "/p1", "local.MyObject", "nonScriptableSignalVoid", &spy, SLOT(slot(QDBusMessage)));
          con.connect(con.baseService(), "/p2", "local.MyObject", "nonScriptableSignalVoid", &spy, SLOT(slot(QDBusMessage)));
          obj.emitSignal("scriptableSignalVoid", QVariant());
          obj.emitSignal("nonScriptableSignalVoid", QVariant());
          QTest::qWait(200);
  
          QCOMPARE(spy.count, 1);     // only /p1 must have emitted
          QCOMPARE(spy.interface, QString("local.MyObject"));
          QCOMPARE(spy.name, QString("scriptableSignalVoid"));
          QCOMPARE(spy.path, QString("/p1"));
          QVERIFY(spy.signature.isEmpty());
      }
  
      {
          MyObject obj(0);
  
          con.registerObject("/p1",&obj, QDBusConnection::ExportScriptableSignals);
          con.registerObject("/p2",&obj, QDBusConnection::ExportScriptableSignals
                                         | QDBusConnection::ExportNonScriptableSignals);
  
          QDBusSignalSpy spy;
          con.connect(con.baseService(), "/p1", "local.MyObject", "nonScriptableSignalVoid", &spy, SLOT(slot(QDBusMessage)));
          con.connect(con.baseService(), "/p2", "local.MyObject", "nonScriptableSignalVoid", &spy, SLOT(slot(QDBusMessage)));
          obj.emitSignal("nonScriptableSignalVoid", QVariant());
          QTest::qWait(200);
  
          QCOMPARE(spy.count, 1);     // only /p2 must have emitted now
          QCOMPARE(spy.interface, QString("local.MyObject"));
          QCOMPARE(spy.name, QString("nonScriptableSignalVoid"));
          QCOMPARE(spy.path, QString("/p2"));
          QVERIFY(spy.signature.isEmpty());
      }
  
      {
          QDBusSignalSpy spy;
          con.connect(con.baseService(), "/p1", "local.MyObject", "destroyed", &spy, SLOT(slot(QDBusMessage)));
          con.connect(con.baseService(), "/p2", "local.MyObject", "destroyed", &spy, SLOT(slot(QDBusMessage)));
  
          {
              MyObject obj(0);
  
              con.registerObject("/p1",&obj, QDBusConnection::ExportScriptableSignals);
              con.registerObject("/p2",&obj, QDBusConnection::ExportScriptableSignals
                                             | QDBusConnection::ExportNonScriptableSignals);
          } // <--- QObject emits the destroyed(QObject*) signal at this point
  
          QTest::qWait(200);
  
          QCOMPARE(spy.count, 0);
      }
  }
  
  void tst_QDBusAbstractAdaptor::overloadedSignalEmission_data()
  {
      QTest::addColumn<QString>("signature");
      QTest::addColumn<QVariant>("parameter");
      QTest::newRow("void") << QString("") << QVariant();
      QTest::newRow("int") << "i" << QVariant(1);
      QTest::newRow("string") << "s" << QVariant("foo");
  }
  
  void tst_QDBusAbstractAdaptor::overloadedSignalEmission()
  {
      QDBusConnection con = QDBusConnection::sessionBus();
      QVERIFY(con.isConnected());
  
      MyObject obj;
      con.registerObject("/", &obj);
  
      QString interface = "local.Interface4";
      QString name = "signal";
      QFETCH(QVariant, parameter);
      //QDBusInterface *if4 = new QDBusInterface(con.baseService(), "/", interface, con);
  
      // connect all signals and emit only one
      {
          QDBusSignalSpy spy;
          con.connect(con.baseService(), "/", "local.Interface4", "signal", "",
                      &spy, SLOT(slot(QDBusMessage)));
          con.connect(con.baseService(), "/", "local.Interface4", "signal", "i",
                      &spy, SLOT(slot(QDBusMessage)));
          con.connect(con.baseService(), "/", "local.Interface4", "signal", "s",
                      &spy, SLOT(slot(QDBusMessage)));
  
          emitSignal(&obj, interface, name, parameter);
  
          QTRY_COMPARE(spy.count, 1);
          QCOMPARE(spy.interface, interface);
          QCOMPARE(spy.name, name);
          QTEST(spy.signature, "signature");
          QCOMPARE(spy.value, parameter);
      }
  
      QFETCH(QString, signature);
      // connect one signal and emit them all
      {
          QDBusSignalSpy spy;
          con.connect(con.baseService(), "/", interface, name, signature, &spy, SLOT(slot(QDBusMessage)));
          emitSignal(&obj, "local.Interface4", "signal", QVariant());
          emitSignal(&obj, "local.Interface4", "signal", QVariant(1));
          emitSignal(&obj, "local.Interface4", "signal", QVariant("foo"));
  
          QTRY_COMPARE(spy.count, 1);
          QCOMPARE(spy.interface, interface);
          QCOMPARE(spy.name, name);
          QTEST(spy.signature, "signature");
          QCOMPARE(spy.value, parameter);
      }
  }
  
  void tst_QDBusAbstractAdaptor::readProperties()
  {
      QDBusConnection con = QDBusConnection::sessionBus();
      QVERIFY(con.isConnected());
  
      MyObject obj;
      con.registerObject("/", &obj);
  
      QDBusInterface properties(con.baseService(), "/", "org.freedesktop.DBus.Properties", con);
      for (int i = 2; i <= 4; ++i) {
          QString name = QString("Interface%1").arg(i);
  
          for (int j = 1; j <= 2; ++j) {
              QString propname = QString("prop%1").arg(j);
              QDBusReply<QVariant> reply =
                  properties.call(QDBus::BlockWithGui, "Get", "local." + name, propname);
              QVariant value = reply;
  
              QCOMPARE(value.userType(), int(QVariant::String));
              QCOMPARE(value.toString(), QString("QString %1::%2() const").arg(name, propname));
          }
      }
  }
  
  void tst_QDBusAbstractAdaptor::readPropertiesInvalidInterface()
  {
      QDBusConnection con = QDBusConnection::sessionBus();
      QVERIFY(con.isConnected());
  
      MyObject obj;
      con.registerObject("/", &obj);
  
      QDBusInterface properties(con.baseService(), "/", "org.freedesktop.DBus.Properties", con);
  
      // test an invalid interface:
      QDBusReply<QVariant> reply = properties.call(QDBus::BlockWithGui, "Get", "local.DoesntExist", "prop1");
      QVERIFY(!reply.isValid());
  }
  
  void tst_QDBusAbstractAdaptor::readPropertiesEmptyInterface_data()
  {
      QTest::addColumn<QVariantMap>("expectedProperties");
      QTest::addColumn<bool>("existing");
  
      QVariantMap expectedProperties;
      expectedProperties["prop1"] = QVariant();
      expectedProperties["prop2"] = QVariant();
      expectedProperties["interface3prop"] = "QString Interface3::interface3prop() const";
      expectedProperties["interface4prop"] = "QString Interface4::interface4prop() const";
      QTest::newRow("existing") << expectedProperties << true;
  
      expectedProperties.clear();
      expectedProperties["prop5"] = QVariant();
      expectedProperties["foobar"] = QVariant();
      QTest::newRow("non-existing") << expectedProperties << false;
  }
  
  void tst_QDBusAbstractAdaptor::readPropertiesEmptyInterface()
  {
      QDBusConnection con = QDBusConnection::sessionBus();
      QVERIFY(con.isConnected());
  
      MyObject obj;
      con.registerObject("/", &obj);
  
      QDBusInterface properties(con.baseService(), "/", "org.freedesktop.DBus.Properties", con);
  
      QFETCH(QVariantMap, expectedProperties);
      QFETCH(bool, existing);
  
      QVariantMap::ConstIterator it = expectedProperties.constBegin();
      for ( ; it != expectedProperties.constEnd(); ++it) {
          QDBusReply<QVariant> reply = properties.call(QDBus::BlockWithGui, "Get", "", it.key());
  
          if (existing) {
              QVERIFY2(reply.isValid(), qPrintable(it.key()));
          } else {
              QVERIFY2(!reply.isValid(), qPrintable(it.key()));
              continue;
          }
  
          QCOMPARE(int(reply.value().type()), int(QVariant::String));
          if (it.value().isValid())
              QCOMPARE(reply.value().toString(), it.value().toString());
      }
  }
  
  void tst_QDBusAbstractAdaptor::readAllProperties()
  {
      QDBusConnection con = QDBusConnection::sessionBus();
      QVERIFY(con.isConnected());
  
      MyObject obj;
      con.registerObject("/", &obj);
  
      QDBusInterface properties(con.baseService(), "/", "org.freedesktop.DBus.Properties", con);
      for (int i = 2; i <= 4; ++i) {
          QString name = QString("Interface%1").arg(i);
          QDBusReply<QVariantMap> reply =
              properties.call(QDBus::BlockWithGui, "GetAll", "local." + name);
  
          for (int j = 1; j <= 2; ++j) {
              QString propname = QString("prop%1").arg(j);
              QVERIFY2(reply.value().contains(propname),
                       qPrintable(propname + " on " + name));
              QVariant value = reply.value().value(propname);
  
              QCOMPARE(value.userType(), int(QVariant::String));
              QCOMPARE(value.toString(), QString("QString %1::%2() const").arg(name, propname));
          }
      }
  }
  
  void tst_QDBusAbstractAdaptor::readAllPropertiesInvalidInterface()
  {
      QDBusConnection con = QDBusConnection::sessionBus();
      QVERIFY(con.isConnected());
  
      MyObject obj;
      con.registerObject("/", &obj);
  
      QDBusInterface properties(con.baseService(), "/", "org.freedesktop.DBus.Properties", con);
  
      // test an invalid interface:
      QDBusReply<QVariantMap> reply = properties.call(QDBus::BlockWithGui, "GetAll", "local.DoesntExist");
      QVERIFY(!reply.isValid());
  }
  
  void tst_QDBusAbstractAdaptor::readAllPropertiesEmptyInterface_data()
  {
      readPropertiesEmptyInterface_data();
  }
  
  void tst_QDBusAbstractAdaptor::readAllPropertiesEmptyInterface()
  {
      QDBusConnection con = QDBusConnection::sessionBus();
      QVERIFY(con.isConnected());
  
      MyObject obj;
      con.registerObject("/", &obj);
  
      QDBusInterface properties(con.baseService(), "/", "org.freedesktop.DBus.Properties", con);
  
      QDBusReply<QVariantMap> reply = properties.call(QDBus::BlockWithGui, "GetAll", "");
      QVERIFY(reply.isValid());
  
      QVariantMap allprops = reply;
  
      QFETCH(QVariantMap, expectedProperties);
      QFETCH(bool, existing);
  
      QVariantMap::ConstIterator it = expectedProperties.constBegin();
      if (existing) {
          for ( ; it != expectedProperties.constEnd(); ++it) {
              QVERIFY2(allprops.contains(it.key()), qPrintable(it.key()));
  
              QVariant propvalue = allprops.value(it.key());
              QVERIFY2(!propvalue.isNull(), qPrintable(it.key()));
              QVERIFY2(propvalue.isValid(), qPrintable(it.key()));
  
              QString stringvalue = propvalue.toString();
              QVERIFY2(!stringvalue.isEmpty(), qPrintable(it.key()));
  
              if (it.value().isValid())
                  QCOMPARE(stringvalue, it.value().toString());
  
              // remove this property from the map
              allprops.remove(it.key());
          }
  
          QVERIFY2(allprops.isEmpty(),
                   qPrintable(QStringList(allprops.keys()).join(' ')));
      } else {
          for ( ; it != expectedProperties.constEnd(); ++it)
              QVERIFY2(!allprops.contains(it.key()), qPrintable(it.key()));
      }
  }
  
  void tst_QDBusAbstractAdaptor::writeProperties()
  {
      QDBusConnection con = QDBusConnection::sessionBus();
      QVERIFY(con.isConnected());
  
      MyObject obj;
      con.registerObject("/", &obj);
  
      QDBusInterface properties(con.baseService(), "/", "org.freedesktop.DBus.Properties", con);
      for (int i = 2; i <= 4; ++i) {
          QString name = QString("Interface%1").arg(i);
  
          valueSpy.clear();
          properties.call(QDBus::BlockWithGui, "Set", "local." + name, QString("prop1"),
                          QVariant::fromValue(QDBusVariant(name)));
          QVERIFY(valueSpy.isEmpty()); // call mustn't have succeeded
  
          properties.call(QDBus::BlockWithGui, "Set", "local." + name, QString("prop2"),
                          QVariant::fromValue(QDBusVariant(name)));
          QCOMPARE(valueSpy, name);
          QCOMPARE(QString(slotSpy), QString("void %1::setProp2(const QString &)").arg(name));
      }
  }
  
  void tst_QDBusAbstractAdaptor::methodCallsPeer_data()
  {
      methodCalls_data();
  }
  
  void tst_QDBusAbstractAdaptor::methodCallsPeer()
  {
      QDBusConnection con("peer");
      QVERIFY(con.isConnected());
  
      {
          // must fail: no object
          QDBusInterface if1(QString(), "/", "local.Interface1", con);
          QCOMPARE(if1.call(QDBus::BlockWithGui, "method").type(), QDBusMessage::ErrorMessage);
      }
  
      QFETCH(int, nInterfaces);
      newMyObjectPeer(nInterfaces);
      registerMyObjectPeer("/");
  
      QDBusInterface if1(QString(), "/", "local.Interface1", con);
      QDBusInterface if2(QString(), "/", "local.Interface2", con);
      QDBusInterface if3(QString(), "/", "local.Interface3", con);
      QDBusInterface if4(QString(), "/", "local.Interface4", con);
  
      // must fail: no such method
      QCOMPARE(if1.call(QDBus::BlockWithGui, "method").type(), QDBusMessage::ErrorMessage);
      if (!nInterfaces--)
          return;
      if (!nInterfaces--)
          return;
  
      // simple call: one such method exists
      QCOMPARE(if2.call(QDBus::BlockWithGui, "method").type(), QDBusMessage::ReplyMessage);
      QCOMPARE(slotSpyPeer(), QStringLiteral("void Interface2::method()"));
      if (!nInterfaces--)
          return;
  
      // multiple methods in multiple interfaces, no name overlap
      QCOMPARE(if1.call(QDBus::BlockWithGui, "methodVoid").type(), QDBusMessage::ErrorMessage);
      QCOMPARE(if1.call(QDBus::BlockWithGui, "methodInt").type(), QDBusMessage::ErrorMessage);
      QCOMPARE(if1.call(QDBus::BlockWithGui, "methodString").type(), QDBusMessage::ErrorMessage);
      QCOMPARE(if2.call(QDBus::BlockWithGui, "methodVoid").type(), QDBusMessage::ErrorMessage);
      QCOMPARE(if2.call(QDBus::BlockWithGui, "methodInt").type(), QDBusMessage::ErrorMessage);
      QCOMPARE(if2.call(QDBus::BlockWithGui, "methodString").type(), QDBusMessage::ErrorMessage);
  
      QCOMPARE(if3.call(QDBus::BlockWithGui, "methodVoid").type(), QDBusMessage::ReplyMessage);
      QCOMPARE(slotSpyPeer(), QStringLiteral("void Interface3::methodVoid()"));
      QCOMPARE(if3.call(QDBus::BlockWithGui, "methodInt", 42).type(), QDBusMessage::ReplyMessage);
      QCOMPARE(slotSpyPeer(), QStringLiteral("void Interface3::methodInt(int)"));
      QCOMPARE(if3.call(QDBus::BlockWithGui, "methodString", QString("")).type(), QDBusMessage::ReplyMessage);
      QCOMPARE(slotSpyPeer(), QStringLiteral("void Interface3::methodString(QString)"));
  
      if (!nInterfaces--)
          return;
  
      // method overloading: different interfaces
      QCOMPARE(if4.call(QDBus::BlockWithGui, "method").type(), QDBusMessage::ReplyMessage);
      QCOMPARE(slotSpyPeer(), QStringLiteral("void Interface4::method()"));
  
      // method overloading: different parameters
      QCOMPARE(if4.call(QDBus::BlockWithGui, "method.i", 42).type(), QDBusMessage::ReplyMessage);
      QCOMPARE(slotSpyPeer(), QStringLiteral("void Interface4::method(int)"));
      QCOMPARE(if4.call(QDBus::BlockWithGui, "method.s", QString()).type(), QDBusMessage::ReplyMessage);
      QCOMPARE(slotSpyPeer(), QStringLiteral("void Interface4::method(QString)"));
  }
  
  void tst_QDBusAbstractAdaptor::methodCallScriptablePeer()
  {
      QDBusConnection con("peer");
      QVERIFY(con.isConnected());
  
      newMyObjectPeer(2);
      registerMyObjectPeer("/");
  
      QDBusInterface if2(QString(), "/", "local.Interface2", con);
  
      QCOMPARE(if2.call(QDBus::BlockWithGui,"scriptableMethod").type(), QDBusMessage::ReplyMessage);
      QCOMPARE(slotSpyPeer(), QStringLiteral("void Interface2::scriptableMethod()"));
  }
  
  void tst_QDBusAbstractAdaptor::signalEmissionsPeer_data()
  {
      signalEmissions_data();
  }
  
  void tst_QDBusAbstractAdaptor::signalEmissionsPeer()
  {
      QFETCH(QString, interface);
      QFETCH(QString, name);
      QFETCH(QVariant, parameter);
  
      QDBusConnection con("peer");
      QVERIFY(con.isConnected());
  
      newMyObjectPeer(3);
      registerMyObjectPeer("/", QDBusConnection::ExportAdaptors
                              | QDBusConnection::ExportScriptableSignals);
  
      // connect all signals and emit only one
      {
          syncPeer();
  
          QDBusSignalSpy spy;
          con.connect(QString(), "/", "local.Interface2", "signal",
                      &spy, SLOT(slot(QDBusMessage)));
          con.connect(QString(), "/", "local.Interface3", "signalVoid",
                      &spy, SLOT(slot(QDBusMessage)));
          con.connect(QString(), "/", "local.Interface3", "signalInt",
                      &spy, SLOT(slot(QDBusMessage)));
          con.connect(QString(), "/", "local.Interface3", "signalString",
                      &spy, SLOT(slot(QDBusMessage)));
          con.connect(QString(), "/", "local.MyObject", "scriptableSignalVoid",
                      &spy, SLOT(slot(QDBusMessage)));
          con.connect(QString(), "/", "local.MyObject", "scriptableSignalInt",
                      &spy, SLOT(slot(QDBusMessage)));
          con.connect(QString(), "/", "local.MyObject", "scriptableSignalString",
                      &spy, SLOT(slot(QDBusMessage)));
  
          emitSignalPeer(interface, name, parameter);
  
          QTRY_COMPARE(spy.count, 1);
          QCOMPARE(spy.interface, interface);
          QCOMPARE(spy.name, name);
          QTEST(spy.signature, "signature");
          QCOMPARE(spy.value, parameter);
      }
  
      // connect one signal and emit them all
      {
          syncPeer();
  
          QDBusSignalSpy spy;
          con.connect(QString(), "/", interface, name, &spy, SLOT(slot(QDBusMessage)));
          emitSignalPeer("local.Interface2", "signal", QVariant());
          emitSignalPeer("local.Interface3", "signalVoid", QVariant());
          emitSignalPeer("local.Interface3", "signalInt", QVariant(1));
          emitSignalPeer("local.Interface3", "signalString", QVariant("foo"));
          emitSignalPeer("local.MyObject", "scriptableSignalVoid", QVariant());
          emitSignalPeer("local.MyObject", "scriptableSignalInt", QVariant(1));
          emitSignalPeer("local.MyObject", "scriptableSignalString", QVariant("foo"));
  
          QTRY_COMPARE(spy.count, 1);
          QCOMPARE(spy.interface, interface);
          QCOMPARE(spy.name, name);
          QTEST(spy.signature, "signature");
          QCOMPARE(spy.value, parameter);
      }
  }
  
  void tst_QDBusAbstractAdaptor::sameSignalDifferentPathsPeer()
  {
      QDBusConnection con("peer");
      QVERIFY(con.isConnected());
  
      newMyObjectPeer(2);
  
      registerMyObjectPeer("/p1");
      registerMyObjectPeer("/p2");
  
      syncPeer();
      QDBusSignalSpy spy;
      con.connect(QString(), "/p1", "local.Interface2", "signal", &spy, SLOT(slot(QDBusMessage)));
      emitSignalPeer("local.Interface2", QString(), QVariant());
  
      QTRY_COMPARE(spy.count, 1);
      QCOMPARE(spy.interface, QString("local.Interface2"));
      QCOMPARE(spy.name, QString("signal"));
      QVERIFY(spy.signature.isEmpty());
  
      // now connect the other one
      spy.count = 0;
      con.connect(QString(), "/p2", "local.Interface2", "signal", &spy, SLOT(slot(QDBusMessage)));
      emitSignalPeer("local.Interface2", QString(), QVariant());
  
      QTRY_COMPARE(spy.count, 2);
  }
  
  void tst_QDBusAbstractAdaptor::sameObjectDifferentPathsPeer()
  {
      QDBusConnection con("peer");
      QVERIFY(con.isConnected());
  
      newMyObjectPeer(2);
  
      registerMyObjectPeer("/p1");
      registerMyObjectPeer("/p2", 0); // don't export anything
  
      syncPeer();
      QDBusSignalSpy spy;
      con.connect(QString(), "/p1", "local.Interface2", "signal", &spy, SLOT(slot(QDBusMessage)));
      con.connect(QString(), "/p2", "local.Interface2", "signal", &spy, SLOT(slot(QDBusMessage)));
      emitSignalPeer("local.Interface2", QString(), QVariant());
  
      QTRY_COMPARE(spy.count, 1);
      QCOMPARE(spy.interface, QString("local.Interface2"));
      QCOMPARE(spy.name, QString("signal"));
      QVERIFY(spy.signature.isEmpty());
  }
  
  void tst_QDBusAbstractAdaptor::scriptableSignalOrNotPeer()
  {
      QDBusConnection con("peer");;
      QVERIFY(con.isConnected());
  
      {
          newMyObjectPeer(0);
  
          registerMyObjectPeer("/p1", QDBusConnection::ExportScriptableSignals);
          registerMyObjectPeer("/p2", 0); // don't export anything
  
          syncPeer();
          QDBusSignalSpy spy;
          con.connect(QString(), "/p1", "local.MyObject", "scriptableSignalVoid", &spy, SLOT(slot(QDBusMessage)));
          con.connect(QString(), "/p2", "local.MyObject", "scriptableSignalVoid", &spy, SLOT(slot(QDBusMessage)));
          con.connect(QString(), "/p1", "local.MyObject", "nonScriptableSignalVoid", &spy, SLOT(slot(QDBusMessage)));
          con.connect(QString(), "/p2", "local.MyObject", "nonScriptableSignalVoid", &spy, SLOT(slot(QDBusMessage)));
          emitSignalPeer("local.MyObject", "scriptableSignalVoid", QVariant());
          emitSignalPeer("local.MyObject", "nonScriptableSignalVoid", QVariant());
          QTest::qWait(200);
  
          QCOMPARE(spy.count, 1);     // only /p1 must have emitted
          QCOMPARE(spy.interface, QString("local.MyObject"));
          QCOMPARE(spy.name, QString("scriptableSignalVoid"));
          QCOMPARE(spy.path, QString("/p1"));
          QVERIFY(spy.signature.isEmpty());
      }
  
      {
          newMyObjectPeer(0);
  
          registerMyObjectPeer("/p1", QDBusConnection::ExportScriptableSignals);
          registerMyObjectPeer("/p2", QDBusConnection::ExportScriptableSignals
                                  | QDBusConnection::ExportNonScriptableSignals);
  
          syncPeer();
          QDBusSignalSpy spy;
          con.connect(QString(), "/p1", "local.MyObject", "nonScriptableSignalVoid", &spy, SLOT(slot(QDBusMessage)));
          con.connect(QString(), "/p2", "local.MyObject", "nonScriptableSignalVoid", &spy, SLOT(slot(QDBusMessage)));
          emitSignalPeer("local.MyObject", "nonScriptableSignalVoid", QVariant());
          QTest::qWait(200);
  
          QCOMPARE(spy.count, 1);     // only /p2 must have emitted now
          QCOMPARE(spy.interface, QString("local.MyObject"));
          QCOMPARE(spy.name, QString("nonScriptableSignalVoid"));
          QCOMPARE(spy.path, QString("/p2"));
          QVERIFY(spy.signature.isEmpty());
      }
  
      {
          QDBusSignalSpy spy;
          con.connect(QString(), "/p1", "local.MyObject", "destroyed", &spy, SLOT(slot(QDBusMessage)));
          con.connect(QString(), "/p2", "local.MyObject", "destroyed", &spy, SLOT(slot(QDBusMessage)));
  
          {
              newMyObjectPeer(0);
  
              registerMyObjectPeer("/p1", QDBusConnection::ExportScriptableSignals);
              registerMyObjectPeer("/p2", QDBusConnection::ExportScriptableSignals
                                      | QDBusConnection::ExportNonScriptableSignals);
          } // <--- QObject emits the destroyed(QObject*) signal at this point
  
          QTest::qWait(200);
  
          QCOMPARE(spy.count, 0);
      }
  }
  
  void tst_QDBusAbstractAdaptor::overloadedSignalEmissionPeer_data()
  {
      overloadedSignalEmission_data();
  }
  
  void tst_QDBusAbstractAdaptor::overloadedSignalEmissionPeer()
  {
      QDBusConnection con("peer");
      QVERIFY(con.isConnected());
  
      newMyObjectPeer();
      registerMyObjectPeer("/");
  
      QString interface = "local.Interface4";
      QString name = "signal";
      QFETCH(QVariant, parameter);
      //QDBusInterface *if4 = new QDBusInterface(QString(), "/", interface, con);
  
      // connect all signals and emit only one
      {
          syncPeer();
          QDBusSignalSpy spy;
          con.connect(QString(), "/", "local.Interface4", "signal", "",
                      &spy, SLOT(slot(QDBusMessage)));
          con.connect(QString(), "/", "local.Interface4", "signal", "i",
                      &spy, SLOT(slot(QDBusMessage)));
          con.connect(QString(), "/", "local.Interface4", "signal", "s",
                      &spy, SLOT(slot(QDBusMessage)));
  
          emitSignalPeer(interface, name, parameter);
  
          QTRY_COMPARE(spy.count, 1);
          QCOMPARE(spy.interface, interface);
          QCOMPARE(spy.name, name);
          QTEST(spy.signature, "signature");
          QCOMPARE(spy.value, parameter);
      }
  
      QFETCH(QString, signature);
      // connect one signal and emit them all
      {
          syncPeer();
          QDBusSignalSpy spy;
          con.connect(QString(), "/", interface, name, signature, &spy, SLOT(slot(QDBusMessage)));
          emitSignalPeer("local.Interface4", "signal", QVariant());
          emitSignalPeer("local.Interface4", "signal", QVariant(1));
          emitSignalPeer("local.Interface4", "signal", QVariant("foo"));
  
          QTRY_COMPARE(spy.count, 1);
          QCOMPARE(spy.interface, interface);
          QCOMPARE(spy.name, name);
          QTEST(spy.signature, "signature");
          QCOMPARE(spy.value, parameter);
      }
  }
  
  void tst_QDBusAbstractAdaptor::readPropertiesPeer()
  {
      QDBusConnection con("peer");
      QVERIFY(con.isConnected());
  
      newMyObjectPeer();
      registerMyObjectPeer("/");
  
      QDBusInterface properties(QString(), "/", "org.freedesktop.DBus.Properties", con);
      for (int i = 2; i <= 4; ++i) {
          QString name = QString("Interface%1").arg(i);
  
          for (int j = 1; j <= 2; ++j) {
              QString propname = QString("prop%1").arg(j);
              QDBusReply<QVariant> reply =
                  properties.call(QDBus::BlockWithGui, "Get", "local." + name, propname);
              QVariant value = reply;
  
              QCOMPARE(value.userType(), int(QVariant::String));
              QCOMPARE(value.toString(), QString("QString %1::%2() const").arg(name, propname));
          }
      }
  }
  
  void tst_QDBusAbstractAdaptor::readPropertiesInvalidInterfacePeer()
  {
      QDBusConnection con("peer");
      QVERIFY(con.isConnected());
  
      newMyObjectPeer();
      registerMyObjectPeer("/");
  
      QDBusInterface properties(QString(), "/", "org.freedesktop.DBus.Properties", con);
  
      // test an invalid interface:
      QDBusReply<QVariant> reply = properties.call(QDBus::BlockWithGui, "Get", "local.DoesntExist", "prop1");
      QVERIFY(!reply.isValid());
  }
  
  void tst_QDBusAbstractAdaptor::readPropertiesEmptyInterfacePeer_data()
  {
      readPropertiesEmptyInterface_data();
  }
  
  void tst_QDBusAbstractAdaptor::readPropertiesEmptyInterfacePeer()
  {
      QDBusConnection con("peer");
      QVERIFY(con.isConnected());
  
      newMyObjectPeer();
      registerMyObjectPeer("/");
  
      QDBusInterface properties(QString(), "/", "org.freedesktop.DBus.Properties", con);
  
      QFETCH(QVariantMap, expectedProperties);
      QFETCH(bool, existing);
  
      QVariantMap::ConstIterator it = expectedProperties.constBegin();
      for ( ; it != expectedProperties.constEnd(); ++it) {
          QDBusReply<QVariant> reply = properties.call(QDBus::BlockWithGui, "Get", "", it.key());
  
          if (existing) {
              QVERIFY2(reply.isValid(), qPrintable(it.key()));
          } else {
              QVERIFY2(!reply.isValid(), qPrintable(it.key()));
              continue;
          }
  
          QCOMPARE(int(reply.value().type()), int(QVariant::String));
          if (it.value().isValid())
              QCOMPARE(reply.value().toString(), it.value().toString());
      }
  }
  
  void tst_QDBusAbstractAdaptor::readAllPropertiesPeer()
  {
      QDBusConnection con("peer");
      QVERIFY(con.isConnected());
  
      newMyObjectPeer();
      registerMyObjectPeer("/");
  
      QDBusInterface properties(QString(), "/", "org.freedesktop.DBus.Properties", con);
      for (int i = 2; i <= 4; ++i) {
          QString name = QString("Interface%1").arg(i);
          QDBusReply<QVariantMap> reply =
              properties.call(QDBus::BlockWithGui, "GetAll", "local." + name);
  
          for (int j = 1; j <= 2; ++j) {
              QString propname = QString("prop%1").arg(j);
              QVERIFY2(reply.value().contains(propname),
                       qPrintable(propname + " on " + name));
              QVariant value = reply.value().value(propname);
  
              QCOMPARE(value.userType(), int(QVariant::String));
              QCOMPARE(value.toString(), QString("QString %1::%2() const").arg(name, propname));
          }
      }
  }
  
  void tst_QDBusAbstractAdaptor::readAllPropertiesInvalidInterfacePeer()
  {
      QDBusConnection con("peer");
      QVERIFY(con.isConnected());
  
      newMyObjectPeer();
      registerMyObjectPeer("/");
  
      QDBusInterface properties(QString(), "/", "org.freedesktop.DBus.Properties", con);
  
      // test an invalid interface:
      QDBusReply<QVariantMap> reply = properties.call(QDBus::BlockWithGui, "GetAll", "local.DoesntExist");
      QVERIFY(!reply.isValid());
  }
  
  void tst_QDBusAbstractAdaptor::readAllPropertiesEmptyInterfacePeer_data()
  {
      readAllPropertiesEmptyInterface_data();
  }
  
  void tst_QDBusAbstractAdaptor::readAllPropertiesEmptyInterfacePeer()
  {
      QDBusConnection con("peer");
      QVERIFY(con.isConnected());
  
      newMyObjectPeer();
      registerMyObjectPeer("/");
  
      QDBusInterface properties(QString(), "/", "org.freedesktop.DBus.Properties", con);
  
      QDBusReply<QVariantMap> reply = properties.call(QDBus::BlockWithGui, "GetAll", "");
      QVERIFY(reply.isValid());
  
      QVariantMap allprops = reply;
  
      QFETCH(QVariantMap, expectedProperties);
      QFETCH(bool, existing);
  
      QVariantMap::ConstIterator it = expectedProperties.constBegin();
      if (existing) {
          for ( ; it != expectedProperties.constEnd(); ++it) {
              QVERIFY2(allprops.contains(it.key()), qPrintable(it.key()));
  
              QVariant propvalue = allprops.value(it.key());
              QVERIFY2(!propvalue.isNull(), qPrintable(it.key()));
              QVERIFY2(propvalue.isValid(), qPrintable(it.key()));
  
              QString stringvalue = propvalue.toString();
              QVERIFY2(!stringvalue.isEmpty(), qPrintable(it.key()));
  
              if (it.value().isValid())
                  QCOMPARE(stringvalue, it.value().toString());
  
              // remove this property from the map
              allprops.remove(it.key());
          }
  
          QVERIFY2(allprops.isEmpty(),
                   qPrintable(QStringList(allprops.keys()).join(' ')));
      } else {
          for ( ; it != expectedProperties.constEnd(); ++it)
              QVERIFY2(!allprops.contains(it.key()), qPrintable(it.key()));
      }
  }
  
  void tst_QDBusAbstractAdaptor::writePropertiesPeer()
  {
      QDBusConnection con("peer");
      QVERIFY(con.isConnected());
  
      newMyObjectPeer();
      registerMyObjectPeer("/");
  
      QDBusInterface properties(QString(), "/", "org.freedesktop.DBus.Properties", con);
      for (int i = 2; i <= 4; ++i) {
          QString name = QString("Interface%1").arg(i);
  
          clearValueSpyPeer();
          properties.call(QDBus::BlockWithGui, "Set", "local." + name, QString("prop1"),
                          QVariant::fromValue(QDBusVariant(name)));
          QVERIFY(valueSpyPeer().isEmpty()); // call mustn't have succeeded
  
          properties.call(QDBus::BlockWithGui, "Set", "local." + name, QString("prop2"),
                          QVariant::fromValue(QDBusVariant(name)));
          QCOMPARE(valueSpyPeer(), name);
          QCOMPARE(QString(slotSpyPeer()), QString("void %1::setProp2(const QString &)").arg(name));
      }
  }
  
  #if 0
  void tst_QDBusAbstractAdaptor::adaptorIntrospection_data()
  {
      methodCalls_data();
  }
  
  void tst_QDBusAbstractAdaptor::adaptorIntrospection()
  {
      QDBusConnection con = QDBus::sessionBus();
      QVERIFY(con.isConnected());
  
      QObject obj;
      con.registerObject("/", &obj);
  
      QFETCH(int, nInterfaces);
      switch (nInterfaces)
      {
      case 4:
          new Interface4(&obj);
      case 3:
          new Interface3(&obj);
      case 2:
          new Interface2(&obj);
      case 1:
          new Interface1(&obj);
      }
  
      QDBusObject dobj = con.findObject(con.baseService(), "/");
      QVERIFY(dobj.isValid());
  
      QString xml = dobj.introspect();
      QVERIFY(!xml.isEmpty());
  
      QStringList interfaces = dobj.interfaces();
      QCOMPARE(interfaces.count(), nInterfaces + 2);
      switch (nInterfaces)
      {
      case 4: {
          QVERIFY(interfaces.contains("local.Interface4"));
          QDBusInterface iface(dobj, "local.Interface4");
          QCOMPARE(iface.methodData(), Interface4::methodData);
          QCOMPARE(iface.signalData(), Interface4::signalData);
          QCOMPARE(iface.propertyData(), Interface4::propertyData);
      }
      case 3: {
          QVERIFY(interfaces.contains("local.Interface3"));
          QDBusInterface iface(dobj, "local.Interface3");
          QCOMPARE(iface.methodData(), Interface3::methodData);
          QCOMPARE(iface.signalData(), Interface3::signalData);
          QCOMPARE(iface.propertyData(), Interface3::propertyData);
      }
      case 2: {
          QVERIFY(interfaces.contains("local.Interface2"));
          QDBusInterface iface(dobj, "local.Interface2");
          QCOMPARE(iface.methodData(), Interface2::methodData);
          QCOMPARE(iface.signalData(), Interface2::signalData);
          QCOMPARE(iface.propertyData(), Interface2::propertyData);
      }
      case 1: {
          QVERIFY(interfaces.contains("local.Interface1"));
          QDBusInterface iface(dobj, "local.Interface1");
          QCOMPARE(iface.methodData(), Interface1::methodData);
          QCOMPARE(iface.signalData(), Interface1::signalData);
          QCOMPARE(iface.propertyData(), Interface1::propertyData);
      }
      }
  }
  
  void tst_QDBusAbstractAdaptor::objectTreeIntrospection()
  {
      QDBusConnection con = QDBus::sessionBus();
      QVERIFY(con.isConnected());
  
      {
          QDBusObject dobj = con.findObject(con.baseService(), "/");
          QString xml = dobj.introspect();
  
          QDBusIntrospection::Object tree =
              QDBusIntrospection::parseObject(xml);
          QVERIFY(tree.childObjects.isEmpty());
      }
  
      QObject root;
      con.registerObject("/", &root);
      {
          QDBusObject dobj = con.findObject(con.baseService(), "/");
          QString xml = dobj.introspect();
  
          QDBusIntrospection::Object tree =
              QDBusIntrospection::parseObject(xml);
          QVERIFY(tree.childObjects.isEmpty());
      }
  
      QObject p1;
      con.registerObject("/p1", &p1);
      {
          QDBusObject dobj = con.findObject(con.baseService(), "/");
          QString xml = dobj.introspect();
  
          QDBusIntrospection::Object tree =
              QDBusIntrospection::parseObject(xml);
          QVERIFY(tree.childObjects.contains("p1"));
      }
  
      con.unregisterObject("/");
      {
          QDBusObject dobj = con.findObject(con.baseService(), "/");
          QString xml = dobj.introspect();
  
          QDBusIntrospection::Object tree =
              QDBusIntrospection::parseObject(xml);
          QVERIFY(tree.childObjects.contains("p1"));
      }
  
      con.registerObject("/p1/q/r", &root);
      {
          QDBusObject dobj = con.findObject(con.baseService(), "/p1");
          QString xml = dobj.introspect();
  
          QDBusIntrospection::Object tree =
              QDBusIntrospection::parseObject(xml);
          QVERIFY(tree.childObjects.contains("q"));
      }
      {
          QDBusObject dobj = con.findObject(con.baseService(), "/p1/q");
          QString xml = dobj.introspect();
  
          QDBusIntrospection::Object tree =
              QDBusIntrospection::parseObject(xml);
          QVERIFY(tree.childObjects.contains("r"));
      }
  
      con.unregisterObject("/p1", QDBusConnection::UnregisterTree);
      {
          QDBusObject dobj = con.findObject(con.baseService(), "/");
          QString xml = dobj.introspect();
  
          QDBusIntrospection::Object tree =
              QDBusIntrospection::parseObject(xml);
          QVERIFY(tree.childObjects.isEmpty());
      }
  
      QObject p2;
      con.registerObject("/p2", &p2, QDBusConnection::ExportChildObjects);
      {
          QDBusObject dobj = con.findObject(con.baseService(), "/");
          QString xml = dobj.introspect();
  
          QDBusIntrospection::Object tree =
              QDBusIntrospection::parseObject(xml);
          QVERIFY(!tree.childObjects.contains("p1"));
          QVERIFY(tree.childObjects.contains("p2"));
      }
  
      QObject q;
      q.setParent(&p2);
      {
          QDBusObject dobj = con.findObject(con.baseService(), "/p2");
          QString xml = dobj.introspect();
  
          QDBusIntrospection::Object tree =
              QDBusIntrospection::parseObject(xml);
          QVERIFY(!tree.childObjects.contains("q"));
      }
  
      q.setObjectName("q");
      {
          QDBusObject dobj = con.findObject(con.baseService(), "/p2");
          QString xml = dobj.introspect();
  
          QDBusIntrospection::Object tree =
              QDBusIntrospection::parseObject(xml);
          QVERIFY(tree.childObjects.contains("q"));
      }
  
      q.setParent(0);
      {
          QDBusObject dobj = con.findObject(con.baseService(), "/p2");
          QString xml = dobj.introspect();
  
          QDBusIntrospection::Object tree =
              QDBusIntrospection::parseObject(xml);
          QVERIFY(!tree.childObjects.contains("q"));
      }
  }
  #endif
  
  void tst_QDBusAbstractAdaptor::typeMatching_data()
  {
      QTest::addColumn<QString>("basename");
      QTest::addColumn<QString>("signature");
      QTest::addColumn<QVariant>("value");
  
      QTest::newRow("bool") << "Bool" << "b" << QVariant(true);
      QTest::newRow("byte") << "UChar" << "y" << QVariant::fromValue(uchar(42));
      QTest::newRow("short") << "Short" << "n" << QVariant::fromValue(short(-43));
      QTest::newRow("ushort") << "UShort" << "q" << QVariant::fromValue(ushort(44));
      QTest::newRow("int") << "Int" << "i" << QVariant(42);
      QTest::newRow("uint") << "UInt" << "u" << QVariant(42U);
      QTest::newRow("qlonglong") << "LongLong" << "x" << QVariant(Q_INT64_C(42));
      QTest::newRow("qulonglong") << "ULongLong" << "t" << QVariant(Q_UINT64_C(42));
      QTest::newRow("double") << "Double" << "d" << QVariant(2.5);
      QTest::newRow("string") << "String" << "s" << QVariant("Hello, World!");
  
      QTest::newRow("variant") << "Variant" << "v" << QVariant::fromValue(QDBusVariant("Hello again!"));
      QTest::newRow("list") << "List" << "av" << QVariant(QVariantList()
                                                          << 42
                                                          << QString("foo")
                                                          << QByteArray("bar")
                                                          << QVariant::fromValue(QDBusVariant(QString("baz"))));
      QTest::newRow("stringlist") << "StringList" << "as" << QVariant(QStringList() << "Hello" << "world");
      QTest::newRow("bytearray") << "ByteArray" << "ay" << QVariant(QByteArray("foo"));
  
      QVariantMap map;
      map["one"] = 1;       // int
      map["The answer to life, the Universe and everything"] = 42u; // uint
      map["In the beginning..."] = QString("There was nothing"); // string
      map["but Unix came and said"] = QByteArray("\"Hello, World\""); // bytearray
      map["two"] = QVariant::fromValue(short(2)); // short
      QTest::newRow("map") << "Map" << "a{sv}" << QVariant(map);
  
      StringStringMap ssmap;
      ssmap["a"] = "A";
      ssmap["A"] = "a";
      QTest::newRow("ssmap") << "SSMap" << "a{ss}" << QVariant::fromValue(ssmap);
  
      LLDateTimeMap lldtmap;
      lldtmap[-1] = QDateTime();
      QDateTime now = QDateTime::currentDateTime();
      lldtmap[now.toTime_t()] = now; // array of struct of int64 and struct of 3 ints and struct of 4 ints and int
      QTest::newRow("lldtmap") << "LLDateTimeMap" << "a{x((iii)(iiii)i)}" << QVariant::fromValue(lldtmap);
  
      MyStruct s;
      s.i = 42;
      s.s = "A value";
      QTest::newRow("struct") << "Struct" << "(is)" << QVariant::fromValue(s);
  }
  
  void tst_QDBusAbstractAdaptor::typeMatching()
  {
      QObject obj;
      new TypesInterface(&obj);
  
      QDBusConnection con = QDBusConnection::sessionBus();
      con.registerObject("/types", &obj);
  
      QFETCH(QString, basename);
      QFETCH(QString, signature);
      QFETCH(QVariant, value);
  
      QDBusMessage reply;
      QDBusInterface iface(con.baseService(), "/types", "local.TypesInterface", con);
  
      reply = iface.callWithArgumentList(QDBus::BlockWithGui, "method" + basename,
                                          QVariantList() << value);
      QCOMPARE(reply.type(), QDBusMessage::ReplyMessage);
  
      reply = iface.call(QDBus::BlockWithGui, "retrieve" + basename);
      QCOMPARE(reply.type(), QDBusMessage::ReplyMessage);
      QCOMPARE(reply.arguments().count(), 1);
  
      const QVariant &retval = reply.arguments().at(0);
      QVERIFY(compare(retval, value));
  }
  
  void tst_QDBusAbstractAdaptor::methodWithMoreThanOneReturnValue()
  {
      QDBusConnection con = QDBusConnection::sessionBus();
      QVERIFY(con.isConnected());
  
      MyObject obj;
      con.registerObject("/", &obj);
  
      QString testString = "This is a test string.";
  
      QDBusInterface remote(con.baseService(), "/", "local.Interface3", con);
      QDBusMessage reply = remote.call(QDBus::BlockWithGui, "methodStringString", testString);
      QVERIFY(reply.arguments().count() == 2);
  
      QDBusReply<int> intreply = reply;
      QVERIFY(intreply.isValid());
      QCOMPARE(intreply.value(), 42);
  
      QCOMPARE(reply.arguments().at(1).userType(), int(QVariant::String));
      QCOMPARE(qdbus_cast<QString>(reply.arguments().at(1)), testString);
  }
  
  void tst_QDBusAbstractAdaptor::methodWithMoreThanOneReturnValuePeer()
  {
      QDBusConnection con("peer");
      QVERIFY(con.isConnected());
  
      newMyObjectPeer();
      registerMyObjectPeer("/");
  
      QString testString = "This is a test string.";
  
      QDBusInterface remote(QString(), "/", "local.Interface3", con);
      QDBusMessage reply = remote.call(QDBus::BlockWithGui, "methodStringString", testString);
      QVERIFY(reply.arguments().count() == 2);
  
      QDBusReply<int> intreply = reply;
      QVERIFY(intreply.isValid());
      QCOMPARE(intreply.value(), 42);
  
      QCOMPARE(reply.arguments().at(1).userType(), int(QVariant::String));
      QCOMPARE(qdbus_cast<QString>(reply.arguments().at(1)), testString);
  }
  
  QTEST_MAIN(tst_QDBusAbstractAdaptor)
  
  #include "tst_qdbusabstractadaptor.moc"
  