1 #include <QtCore/QDebug>
2 #include <QtCore/QTimer>
3 #include <QtDBus/QtDBus>
4 #include <QtTest/QtTest>
5 
6 #include <cstring>
7 
8 #include <QDateTime>
9 #include <QString>
10 #include <QVariantMap>
11 
12 #define TP_QT_ENABLE_LOWLEVEL_API
13 
14 #include <TelepathyQt/Account>
15 #include <TelepathyQt/ChannelClassSpec>
16 #include <TelepathyQt/Client>
17 #include <TelepathyQt/ConnectionLowlevel>
18 #include <TelepathyQt/Debug>
19 #include <TelepathyQt/PendingReady>
20 #include <TelepathyQt/SimpleCallObserver>
21 #include <TelepathyQt/SimpleObserver>
22 #include <TelepathyQt/SimpleTextObserver>
23 #include <TelepathyQt/StreamedMediaChannel>
24 #include <TelepathyQt/TextChannel>
25 #include <TelepathyQt/Types>
26 
27 #include <telepathy-glib/cm-message.h>
28 #include <telepathy-glib/debug.h>
29 
30 #include <glib-object.h>
31 #include <dbus/dbus-glib.h>
32 
33 #include <tests/lib/glib/contacts-conn.h>
34 #include <tests/lib/glib/echo2/chan.h>
35 #include <tests/lib/glib/callable/media-channel.h>
36 #include <tests/lib/test.h>
37 
38 using namespace Tp;
39 using namespace Tp::Client;
40 
41 class TestSimpleObserver;
42 
43 namespace
44 {
45 
channels(const QList<TextChannelPtr> & channels)46 QList<ChannelPtr> channels(const QList<TextChannelPtr> &channels)
47 {
48     QList<ChannelPtr> ret;
49     Q_FOREACH (const TextChannelPtr &channel, channels) {
50         ret << channel;
51     }
52     return ret;
53 }
54 
channels(const QList<StreamedMediaChannelPtr> & channels)55 QList<ChannelPtr> channels(const QList<StreamedMediaChannelPtr> &channels)
56 {
57     QList<ChannelPtr> ret;
58     Q_FOREACH (const StreamedMediaChannelPtr &channel, channels) {
59         ret << channel;
60     }
61     return ret;
62 }
63 
64 }
65 
66 class AccountAdaptor : public QDBusAbstractAdaptor
67 {
68     Q_OBJECT
69     Q_CLASSINFO("D-Bus Interface", "org.freedesktop.Telepathy.Account")
70     Q_CLASSINFO("D-Bus Introspection", ""
71 "  <interface name=\"org.freedesktop.Telepathy.Account\" >\n"
72 "    <property name=\"Interfaces\" type=\"as\" access=\"read\" />\n"
73 "    <property name=\"Connection\" type=\"o\" access=\"read\" />\n"
74 "    <signal name=\"AccountPropertyChanged\" >\n"
75 "      <arg name=\"Properties\" type=\"a{sv}\" />\n"
76 "    </signal>\n"
77 "  </interface>\n"
78         "")
79 
80     Q_PROPERTY(QDBusObjectPath Connection READ Connection)
81     Q_PROPERTY(QStringList Interfaces READ Interfaces)
82 
83 public:
AccountAdaptor(QObject * parent)84     AccountAdaptor(QObject *parent)
85         : QDBusAbstractAdaptor(parent), mConnection(QLatin1String("/"))
86     {
87     }
88 
~AccountAdaptor()89     virtual ~AccountAdaptor()
90     {
91     }
92 
setConnection(QString conn)93     void setConnection(QString conn)
94     {
95         if (conn.isEmpty()) {
96             conn = QLatin1String("/");
97         }
98 
99         mConnection = QDBusObjectPath(conn);
100         QVariantMap props;
101         props.insert(QLatin1String("Connection"), QVariant::fromValue(mConnection));
102         Q_EMIT AccountPropertyChanged(props);
103     }
104 
105 public: // Properties
Connection() const106     inline QDBusObjectPath Connection() const
107     {
108         return mConnection;
109     }
110 
Interfaces() const111     inline QStringList Interfaces() const
112     {
113         return QStringList();
114     }
115 
116 Q_SIGNALS: // Signals
117     void AccountPropertyChanged(const QVariantMap &properties);
118 
119 private:
120     QDBusObjectPath mConnection;
121 };
122 
123 class Dispatcher : public QObject, public QDBusContext
124 {
125     Q_OBJECT;
126 
127 public:
Dispatcher(QObject * parent)128     Dispatcher(QObject *parent)
129         : QObject(parent)
130     {
131     }
132 
~Dispatcher()133     ~Dispatcher()
134     {
135     }
136 };
137 
138 class TestSimpleObserver : public Test
139 {
140     Q_OBJECT
141 
142 public:
TestSimpleObserver(QObject * parent=0)143     TestSimpleObserver(QObject *parent = 0)
144         : Test(parent),
145           mChannelsCount(0), mSMChannelsCount(0)
146     {
147         std::memset(mMessagesChanServices, 0, sizeof(mMessagesChanServices));
148         std::memset(mCallableChanServices, 0, sizeof(mCallableChanServices));
149     }
150 
151 protected Q_SLOTS:
152     void onObserverNewChannels(const QList<Tp::ChannelPtr> &channels);
153     void onObserverChannelInvalidated(const Tp::ChannelPtr &channel,
154             const QString &errorName, const QString &errorMessage);
155     void onObserverStreamedMediaCallStarted(
156             const Tp::StreamedMediaChannelPtr &channel);
157     void onObserverStreamedMediaCallEnded(
158             const Tp::StreamedMediaChannelPtr &channel,
159             const QString &errorMessage, const QString &errorName);
160 
161 private Q_SLOTS:
162     void initTestCase();
163     void init();
164 
165     void testObserverRegistration();
166     void testCrossTalk();
167 
168     void cleanup();
169     void cleanupTestCase();
170 
171 private:
172     QMap<QString, QString> ourObservers();
173 
174     AccountPtr mAccounts[2];
175 
176     struct ConnInfo {
ConnInfoTestSimpleObserver::ConnInfo177         ConnInfo()
178             : connService(0), baseConnService(0), contactRepo(0)
179         {
180         }
181 
182         TpTestsContactsConnection *connService;
183         TpBaseConnection *baseConnService;
184         ConnectionPtr conn;
185         TpHandleRepoIface *contactRepo;
186     };
187     ConnInfo mConns[2];
188 
189     QStringList mContacts;
190 
191     ExampleEcho2Channel *mMessagesChanServices[2];
192     TextChannelPtr mTextChans[2];
193 
194     ExampleCallableMediaChannel *mCallableChanServices[2];
195     StreamedMediaChannelPtr mSMChans[2];
196 
197     int mChannelsCount;
198     int mSMChannelsCount;
199 };
200 
onObserverNewChannels(const QList<Tp::ChannelPtr> & channels)201 void TestSimpleObserver::onObserverNewChannels(const QList<Tp::ChannelPtr> &channels)
202 {
203     QVERIFY(channels.size() == 1);
204     mChannelsCount += channels.size();
205 }
206 
onObserverChannelInvalidated(const Tp::ChannelPtr & channel,const QString & errorName,const QString & errorMessage)207 void TestSimpleObserver::onObserverChannelInvalidated(const Tp::ChannelPtr &channel,
208         const QString &errorName, const QString &errorMessage)
209 {
210     QVERIFY(!channel.isNull());
211     mChannelsCount -= 1;
212 }
213 
onObserverStreamedMediaCallStarted(const Tp::StreamedMediaChannelPtr & channel)214 void TestSimpleObserver::onObserverStreamedMediaCallStarted(
215         const Tp::StreamedMediaChannelPtr &channel)
216 {
217     QVERIFY(!channel.isNull());
218     mSMChannelsCount++;
219 }
220 
onObserverStreamedMediaCallEnded(const Tp::StreamedMediaChannelPtr & channel,const QString & errorMessage,const QString & errorName)221 void TestSimpleObserver::onObserverStreamedMediaCallEnded(const Tp::StreamedMediaChannelPtr &channel,
222         const QString &errorMessage, const QString &errorName)
223 {
224     QVERIFY(!channel.isNull());
225     mSMChannelsCount--;
226 }
227 
initTestCase()228 void TestSimpleObserver::initTestCase()
229 {
230     initTestCaseImpl();
231 
232     g_type_init();
233     g_set_prgname("simple-observer");
234     tp_debug_set_flags("all");
235     dbus_g_bus_get(DBUS_BUS_STARTER, 0);
236 
237     QDBusConnection bus = QDBusConnection::sessionBus();
238     QString channelDispatcherBusName = TP_QT_IFACE_CHANNEL_DISPATCHER;
239     QString channelDispatcherPath = QLatin1String("/org/freedesktop/Telepathy/ChannelDispatcher");
240     Dispatcher *dispatcher = new Dispatcher(this);
241     QVERIFY(bus.registerService(channelDispatcherBusName));
242     QVERIFY(bus.registerObject(channelDispatcherPath, dispatcher));
243 
244     mContacts << QLatin1String("alice") << QLatin1String("bob");
245 
246     // Create 2 accounts to be used by the tests:
247     // - each account contains a connection, a text channel and a SM channel setup:
248     // - the channels for the first account have alice as target and;
249     // - the channels for the second account have bob as target
250     for (int i = 0; i < 2; ++i) {
251         // setup account
252         QString accountBusName = TP_QT_IFACE_ACCOUNT_MANAGER;
253         QString accountPath = QLatin1String("/org/freedesktop/Telepathy/Account/simple/account/") +
254             QString::number(i);
255 
256         QObject *adaptorObject = new QObject(this);
257         AccountAdaptor *accountAdaptor = new AccountAdaptor(adaptorObject);
258         QVERIFY(bus.registerService(accountBusName));
259         QVERIFY(bus.registerObject(accountPath, adaptorObject));
260 
261         AccountPtr acc = Account::create(accountBusName, accountPath);
262         QVERIFY(connect(acc->becomeReady(),
263                         SIGNAL(finished(Tp::PendingOperation *)),
264                         SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
265         QCOMPARE(mLoop->exec(), 0);
266         QCOMPARE(acc->isReady(), true);
267         QCOMPARE(acc->supportsRequestHints(), false);
268         QCOMPARE(acc->requestsSucceedWithChannel(), false);
269         mAccounts[i] = acc;
270 
271         // setup conn
272         TpTestsContactsConnection *connService = TP_TESTS_CONTACTS_CONNECTION(
273                 g_object_new(TP_TESTS_TYPE_CONTACTS_CONNECTION,
274                     "account", "me@example.com",
275                     "protocol", "example",
276                     NULL));
277         QVERIFY(connService != 0);
278         TpBaseConnection *baseConnService = TP_BASE_CONNECTION(connService);
279         QVERIFY(baseConnService != 0);
280 
281         gchar *connName, *connPath;
282         GError *error = NULL;
283 
284         QString name(QLatin1String("example") + QString::number(i));
285         QVERIFY(tp_base_connection_register(baseConnService,
286                     name.toLatin1().constData(), &connName, &connPath, &error));
287         QVERIFY(error == 0);
288 
289         QVERIFY(connName != 0);
290         QVERIFY(connPath != 0);
291 
292         ConnectionPtr conn = Connection::create(QLatin1String(connName), QLatin1String(connPath),
293                 ChannelFactory::create(QDBusConnection::sessionBus()), ContactFactory::create());
294         QCOMPARE(conn->isReady(), false);
295 
296         accountAdaptor->setConnection(QLatin1String(connPath));
297 
298         QVERIFY(connect(conn->lowlevel()->requestConnect(),
299                         SIGNAL(finished(Tp::PendingOperation*)),
300                         SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
301         QCOMPARE(mLoop->exec(), 0);
302         QCOMPARE(conn->isReady(), true);
303         QCOMPARE(conn->status(), ConnectionStatusConnected);
304 
305         TpHandleRepoIface *contactRepo = tp_base_connection_get_handles(baseConnService,
306                 TP_HANDLE_TYPE_CONTACT);
307 
308         mConns[i].connService = connService;
309         mConns[i].baseConnService = baseConnService;
310         mConns[i].conn = conn;
311         mConns[i].contactRepo = contactRepo;
312 
313         // setup channels
314         guint handle = tp_handle_ensure(contactRepo, mContacts[i].toLatin1().constData(), 0, 0);
315 
316         QString messagesChanPath = QLatin1String(connPath) +
317             QLatin1String("/MessagesChannel/") + QString::number(i);
318         mMessagesChanServices[i] = EXAMPLE_ECHO_2_CHANNEL(g_object_new(
319                     EXAMPLE_TYPE_ECHO_2_CHANNEL,
320                     "connection", connService,
321                     "object-path", messagesChanPath.toLatin1().constData(),
322                     "handle", handle,
323                     NULL));
324 
325         QVariantMap immutableProperties;
326         immutableProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetID"), mContacts[i]);
327         mTextChans[i] = TextChannel::create(conn, messagesChanPath, immutableProperties);
328         QVERIFY(connect(mTextChans[i]->becomeReady(),
329                     SIGNAL(finished(Tp::PendingOperation *)),
330                     SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
331         QCOMPARE(mLoop->exec(), 0);
332 
333         QString callableChanPath = QLatin1String(connPath) +
334             QLatin1String("/CallableChannel/") + QString::number(i);
335         mCallableChanServices[i] = EXAMPLE_CALLABLE_MEDIA_CHANNEL(g_object_new(
336                     EXAMPLE_TYPE_CALLABLE_MEDIA_CHANNEL,
337                     "connection", connService,
338                     "object-path", callableChanPath.toLatin1().constData(),
339                     "handle", handle,
340                     NULL));
341 
342         immutableProperties.clear();
343         immutableProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetID"), mContacts[i]);
344         mSMChans[i] = StreamedMediaChannel::create(conn, callableChanPath, immutableProperties);
345         QVERIFY(connect(mSMChans[i]->becomeReady(),
346                     SIGNAL(finished(Tp::PendingOperation *)),
347                     SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
348         QCOMPARE(mLoop->exec(), 0);
349 
350         g_free(connName);
351         g_free(connPath);
352     }
353 }
354 
init()355 void TestSimpleObserver::init()
356 {
357     initImpl();
358 }
359 
testObserverRegistration()360 void TestSimpleObserver::testObserverRegistration()
361 {
362     QVERIFY(ourObservers().isEmpty());
363 
364     QList<SimpleObserverPtr> observers;
365     QList<SimpleTextObserverPtr> textObservers;
366     QList<SimpleCallObserverPtr> callObservers;
367     int numRegisteredObservers = 0;
368     // Observers should be shared by bus and channel class, meaning that
369     // the following code should register only 4 observers:
370     // - one for text chat rooms
371     // - one for text chats
372     // - one for incoming/outgoing calls
373     // - one for incoming calls (incoming)
374     //
375     // The Simple*Observer instances are added to the lists observers/textObservers/callObservers,
376     // so they don't get deleted (refcount == 0) when out of scope
377     for (int i = 0; i < 2; ++i) {
378         AccountPtr acc = mAccounts[i];
379 
380         for (int j = 0; j < 2; ++j) {
381             QString contact = mContacts[j];
382 
383             if (i == 0 && j == 0) {
384                 numRegisteredObservers = 1;
385             }
386 
387             // on first run the following code should register an observer for text chat rooms,
388             // on consecutive runs it should use the already registered observer for text chat rooms
389             SimpleObserverPtr observer = SimpleObserver::create(acc,
390                     ChannelClassSpec::textChatroom(), contact);
391             QCOMPARE(observer->account(), acc);
392             QVERIFY(observer->channelFilter().size() == 1);
393             QVERIFY(observer->channelFilter().contains(ChannelClassSpec::textChatroom()));
394             QCOMPARE(observer->contactIdentifier(), contact);
395             QVERIFY(observer->extraChannelFeatures().isEmpty());
396             QVERIFY(observer->channels().isEmpty());
397             observers.append(observer);
398             QCOMPARE(ourObservers().size(), numRegisteredObservers);
399 
400             // the following code should always reuse the observer for text chat rooms already
401             // created.
402             QList<ChannelClassFeatures> extraChannelFeatures;
403             extraChannelFeatures.append(QPair<ChannelClassSpec, Features>(
404                         ChannelClassSpec::textChatroom(), Channel::FeatureCore));
405             observer = SimpleObserver::create(acc,
406                     ChannelClassSpec::textChatroom(), contact, extraChannelFeatures);
407             QCOMPARE(observer->account(), acc);
408             QVERIFY(observer->channelFilter().size() == 1);
409             QVERIFY(observer->channelFilter().contains(ChannelClassSpec::textChatroom()));
410             QCOMPARE(observer->contactIdentifier(), contact);
411             QCOMPARE(observer->extraChannelFeatures(), extraChannelFeatures);
412             QVERIFY(observer->channels().isEmpty());
413             observers.append(observer);
414             QCOMPARE(ourObservers().size(), numRegisteredObservers);
415 
416             if (i == 0 && j == 0) {
417                 numRegisteredObservers = 2;
418             }
419 
420             // on first run the following code should register an observer for text chats,
421             // on consecutive runs it should use the already registered observer for text chats
422             SimpleTextObserverPtr textObserver = SimpleTextObserver::create(acc, contact);
423             QCOMPARE(textObserver->account(), acc);
424             QCOMPARE(textObserver->contactIdentifier(), contact);
425             QVERIFY(textObserver->textChats().isEmpty());
426             textObservers.append(textObserver);
427             QCOMPARE(ourObservers().size(), numRegisteredObservers);
428 
429             // the following code should always reuse the observer for text chats already
430             // created.
431             textObserver = SimpleTextObserver::create(acc, contact);
432             QCOMPARE(textObserver->account(), acc);
433             QCOMPARE(textObserver->contactIdentifier(), contact);
434             QVERIFY(textObserver->textChats().isEmpty());
435             textObservers.append(textObserver);
436             QCOMPARE(ourObservers().size(), numRegisteredObservers);
437 
438             if (i == 0 && j == 0) {
439                 numRegisteredObservers = 3;
440             }
441 
442             // on first run the following code should register an observer for incoming/outgoing
443             // calls, on consecutive runs it should use the already registered observer for
444             // incoming/outgoing calls
445             SimpleCallObserverPtr callObserver = SimpleCallObserver::create(acc, contact);
446             QCOMPARE(callObserver->account(), acc);
447             QCOMPARE(callObserver->contactIdentifier(), contact);
448             QCOMPARE(callObserver->direction(), SimpleCallObserver::CallDirectionBoth);
449             QVERIFY(callObserver->streamedMediaCalls().isEmpty());
450             callObservers.append(callObserver);
451             QCOMPARE(ourObservers().size(), numRegisteredObservers);
452 
453             if (i == 0 && j == 0) {
454                 numRegisteredObservers = 4;
455             }
456 
457             // on first run the following code should register an observer for incoming calls,
458             // on consecutive runs it should use the already registered observer for incoming calls
459             callObserver = SimpleCallObserver::create(acc, contact,
460                     SimpleCallObserver::CallDirectionIncoming);
461             QCOMPARE(callObserver->account(), acc);
462             QCOMPARE(callObserver->contactIdentifier(), contact);
463             QCOMPARE(callObserver->direction(), SimpleCallObserver::CallDirectionIncoming);
464             QVERIFY(callObserver->streamedMediaCalls().isEmpty());
465             callObservers.append(callObserver);
466             QCOMPARE(ourObservers().size(), numRegisteredObservers);
467         }
468     }
469 
470     // deleting all SimpleObserver instances (text chat room) should unregister 1 observer
471     observers.clear();
472     QCOMPARE(ourObservers().size(), 3);
473     // deleting all SimpleTextObserver instances should unregister 1 observer
474     textObservers.clear();
475     QCOMPARE(ourObservers().size(), 2);
476     // deleting all SimpleCallObserver instances should unregister 2 observers
477     callObservers.clear();
478     QVERIFY(ourObservers().isEmpty());
479 }
480 
testCrossTalk()481 void TestSimpleObserver::testCrossTalk()
482 {
483     SimpleObserverPtr observers[2];
484     SimpleTextObserverPtr textObservers[2];
485     SimpleTextObserverPtr textObserversNoContact[2];
486     SimpleCallObserverPtr callObservers[2];
487     SimpleCallObserverPtr callObserversNoContact[2];
488 
489     for (int i = 0; i < 2; ++i) {
490         AccountPtr acc = mAccounts[i];
491         observers[i] = SimpleObserver::create(acc,
492                 ChannelClassSpec::textChat(), mContacts[i]);
493         QVERIFY(connect(observers[i].data(), SIGNAL(newChannels(QList<Tp::ChannelPtr>)),
494                         SLOT(onObserverNewChannels(QList<Tp::ChannelPtr>))));
495         QVERIFY(connect(observers[i].data(),
496                         SIGNAL(channelInvalidated(Tp::ChannelPtr,QString,QString)),
497                         SLOT(onObserverChannelInvalidated(Tp::ChannelPtr,QString,QString))));
498 
499         // SimpleTextObserver::messageSent/Received is already tested in contact-messenger test
500         textObservers[i] = SimpleTextObserver::create(acc, mContacts[i]);
501         textObserversNoContact[i] = SimpleTextObserver::create(acc);
502 
503         callObservers[i] = SimpleCallObserver::create(acc, mContacts[i]);
504         QVERIFY(connect(callObservers[i].data(), SIGNAL(streamedMediaCallStarted(Tp::StreamedMediaChannelPtr)),
505                         SLOT(onObserverStreamedMediaCallStarted(Tp::StreamedMediaChannelPtr))));
506         QVERIFY(connect(callObservers[i].data(), SIGNAL(streamedMediaCallEnded(Tp::StreamedMediaChannelPtr,QString,QString)),
507                         SLOT(onObserverStreamedMediaCallEnded(Tp::StreamedMediaChannelPtr,QString,QString))));
508         callObserversNoContact[i] = SimpleCallObserver::create(acc);
509     }
510 
511     QMap<QString, QString> ourObserversMap = ourObservers();
512     QMap<QString, QString>::const_iterator it = ourObserversMap.constBegin();
513     QMap<QString, QString>::const_iterator end = ourObserversMap.constEnd();
514     for (; it != end; ++it) {
515         ClientObserverInterface *observerIface = new ClientObserverInterface(
516                 it.key(), it.value(), this);
517 
518         ChannelClassList observerFilter;
519         if (!waitForProperty(observerIface->requestPropertyObserverChannelFilter(),
520                     &observerFilter)) {
521             continue;
522         }
523 
524         for (int i = 0; i < 2; ++i) {
525             Q_FOREACH (const ChannelClassSpec &spec, observerFilter) {
526                 // only call ObserveChannels for text chat channels on observers that support text
527                 // chat
528                 if (spec.isSubsetOf(ChannelClassSpec::textChat())) {
529                     ChannelDetails textChan = {
530                         QDBusObjectPath(mTextChans[i]->objectPath()),
531                         mTextChans[i]->immutableProperties()
532                     };
533                     observerIface->ObserveChannels(
534                             QDBusObjectPath(mAccounts[i]->objectPath()),
535                             QDBusObjectPath(mTextChans[i]->connection()->objectPath()),
536                             ChannelDetailsList() << textChan,
537                             QDBusObjectPath(QLatin1String("/")),
538                             Tp::ObjectPathList(),
539                             QVariantMap());
540                     break;
541                 }
542             }
543 
544             Q_FOREACH (const ChannelClassSpec &spec, observerFilter) {
545                 // only call ObserveChannels for SM channels on observers that support SM channels
546                 if (spec.isSubsetOf(ChannelClassSpec::streamedMediaCall())) {
547                     ChannelDetails smChan = {
548                         QDBusObjectPath(mSMChans[i]->objectPath()),
549                         mSMChans[i]->immutableProperties()
550                     };
551                     observerIface->ObserveChannels(
552                             QDBusObjectPath(mAccounts[i]->objectPath()),
553                             QDBusObjectPath(mSMChans[i]->connection()->objectPath()),
554                             ChannelDetailsList() << smChan,
555                             QDBusObjectPath(QLatin1String("/")),
556                             Tp::ObjectPathList(),
557                             QVariantMap());
558                     break;
559                 }
560             }
561         }
562     }
563 
564     // due to QtDBus limitation, we cannot wait for ObserveChannels to finish properly before
565     // proceeding, so we have to wait till all observers receive the channels
566     while (observers[0]->channels().isEmpty() ||
567            observers[1]->channels().isEmpty() ||
568            textObservers[0]->textChats().isEmpty() ||
569            textObservers[1]->textChats().isEmpty() ||
570            textObserversNoContact[0]->textChats().isEmpty() ||
571            textObserversNoContact[1]->textChats().isEmpty() ||
572            callObservers[0]->streamedMediaCalls().isEmpty() ||
573            callObservers[1]->streamedMediaCalls().isEmpty() ||
574            callObserversNoContact[0]->streamedMediaCalls().isEmpty() ||
575            callObserversNoContact[1]->streamedMediaCalls().isEmpty()) {
576         mLoop->processEvents();
577     }
578 
579     QCOMPARE(mChannelsCount, 2);
580     QCOMPARE(mSMChannelsCount, 2);
581 
582     QCOMPARE(observers[0]->channels().size(), 1);
583     QCOMPARE(textObservers[0]->textChats().size(), 1);
584     QCOMPARE(textObserversNoContact[0]->textChats().size(), 1);
585     QCOMPARE(callObservers[0]->streamedMediaCalls().size(), 1);
586     QCOMPARE(callObserversNoContact[0]->streamedMediaCalls().size(), 1);
587     QCOMPARE(observers[1]->channels().size(), 1);
588     QCOMPARE(textObservers[1]->textChats().size(), 1);
589     QCOMPARE(textObserversNoContact[1]->textChats().size(), 1);
590     QCOMPARE(callObservers[1]->streamedMediaCalls().size(), 1);
591     QCOMPARE(callObserversNoContact[1]->streamedMediaCalls().size(), 1);
592 
593     QVERIFY(textObservers[0]->textChats() != textObservers[1]->textChats());
594     QVERIFY(textObserversNoContact[0]->textChats() != textObserversNoContact[1]->textChats());
595     QCOMPARE(textObservers[0]->textChats(), textObserversNoContact[0]->textChats());
596     QCOMPARE(textObservers[1]->textChats(), textObserversNoContact[1]->textChats());
597 
598     QVERIFY(callObservers[0]->streamedMediaCalls() != callObservers[1]->streamedMediaCalls());
599     QVERIFY(callObservers[0]->streamedMediaCalls() != callObserversNoContact[1]->streamedMediaCalls());
600     QCOMPARE(callObservers[0]->streamedMediaCalls(), callObserversNoContact[0]->streamedMediaCalls());
601     QCOMPARE(callObservers[1]->streamedMediaCalls(), callObserversNoContact[1]->streamedMediaCalls());
602 
603     QCOMPARE(observers[0]->channels(), channels(textObservers[0]->textChats()));
604     QVERIFY(observers[0]->channels() != channels(textObservers[1]->textChats()));
605     QVERIFY(observers[0]->channels() != channels(callObservers[0]->streamedMediaCalls()));
606     QVERIFY(observers[0]->channels() != channels(callObservers[1]->streamedMediaCalls()));
607     QVERIFY(observers[1]->channels() != channels(textObservers[0]->textChats()));
608     QCOMPARE(observers[1]->channels(), channels(textObservers[1]->textChats()));
609     QVERIFY(observers[1]->channels() != channels(callObservers[0]->streamedMediaCalls()));
610     QVERIFY(observers[1]->channels() != channels(callObservers[1]->streamedMediaCalls()));
611 
612     QVERIFY(channels(callObservers[0]->streamedMediaCalls()) != channels(textObservers[0]->textChats()));
613     QVERIFY(channels(callObservers[0]->streamedMediaCalls()) != channels(textObservers[1]->textChats()));
614     QVERIFY(channels(callObservers[1]->streamedMediaCalls()) != channels(textObservers[0]->textChats()));
615     QVERIFY(channels(callObservers[1]->streamedMediaCalls()) != channels(textObservers[1]->textChats()));
616 
617     QCOMPARE(observers[0]->channels().first()->objectPath(), mTextChans[0]->objectPath());
618     QCOMPARE(observers[1]->channels().first()->objectPath(), mTextChans[1]->objectPath());
619     QCOMPARE(textObservers[0]->textChats().first()->objectPath(), mTextChans[0]->objectPath());
620     QCOMPARE(textObservers[1]->textChats().first()->objectPath(), mTextChans[1]->objectPath());
621     QCOMPARE(textObserversNoContact[0]->textChats().first()->objectPath(), mTextChans[0]->objectPath());
622     QCOMPARE(textObserversNoContact[1]->textChats().first()->objectPath(), mTextChans[1]->objectPath());
623     QCOMPARE(callObservers[0]->streamedMediaCalls().first()->objectPath(), mSMChans[0]->objectPath());
624     QCOMPARE(callObservers[1]->streamedMediaCalls().first()->objectPath(), mSMChans[1]->objectPath());
625 
626     // invalidate channels
627     for (int i = 0; i < 2; ++i) {
628         if (mMessagesChanServices[i] != 0) {
629             g_object_unref(mMessagesChanServices[i]);
630             mMessagesChanServices[i] = 0;
631         }
632 
633         if (mCallableChanServices[i] != 0) {
634             g_object_unref(mCallableChanServices[i]);
635             mCallableChanServices[i] = 0;
636         }
637     }
638 
639     while (!observers[0]->channels().isEmpty() ||
640            !observers[1]->channels().isEmpty() ||
641            !textObservers[0]->textChats().isEmpty() ||
642            !textObservers[1]->textChats().isEmpty() ||
643            !textObserversNoContact[0]->textChats().isEmpty() ||
644            !textObserversNoContact[1]->textChats().isEmpty() ||
645            !callObservers[0]->streamedMediaCalls().isEmpty() ||
646            !callObservers[1]->streamedMediaCalls().isEmpty() ||
647            !callObserversNoContact[0]->streamedMediaCalls().isEmpty() ||
648            !callObserversNoContact[1]->streamedMediaCalls().isEmpty()) {
649         mLoop->processEvents();
650     }
651 
652     QCOMPARE(mChannelsCount, 0);
653     QCOMPARE(mSMChannelsCount, 0);
654 }
655 
cleanup()656 void TestSimpleObserver::cleanup()
657 {
658     cleanupImpl();
659 }
660 
cleanupTestCase()661 void TestSimpleObserver::cleanupTestCase()
662 {
663     for (int i = 0; i < 2; ++i) {
664         if (!mConns[i].conn) {
665             continue;
666         }
667 
668         if (mConns[i].conn->requestedFeatures().contains(Connection::FeatureCore)) {
669             QVERIFY(mConns[i].connService != NULL);
670 
671             if (TP_BASE_CONNECTION(mConns[i].connService)->status != TP_CONNECTION_STATUS_DISCONNECTED) {
672                 tp_base_connection_change_status(TP_BASE_CONNECTION(mConns[i].connService),
673                         TP_CONNECTION_STATUS_DISCONNECTED,
674                         TP_CONNECTION_STATUS_REASON_REQUESTED);
675             }
676 
677             while (mConns[i].conn->isValid()) {
678                 mLoop->processEvents();
679             }
680 
681         }
682         mConns[i].conn.reset();
683 
684         mTextChans[i].reset();
685         mSMChans[i].reset();
686 
687         if (mMessagesChanServices[i] != 0) {
688             g_object_unref(mMessagesChanServices[i]);
689             mMessagesChanServices[i] = 0;
690         }
691 
692         if (mCallableChanServices[i] != 0) {
693             g_object_unref(mCallableChanServices[i]);
694             mCallableChanServices[i] = 0;
695         }
696 
697         if (mConns[i].connService != 0) {
698             mConns[i].baseConnService = 0;
699             g_object_unref(mConns[i].connService);
700             mConns[i].connService = 0;
701         }
702     }
703 
704     cleanupTestCaseImpl();
705 }
706 
ourObservers()707 QMap<QString, QString> TestSimpleObserver::ourObservers()
708 {
709     QStringList registeredNames =
710         QDBusConnection::sessionBus().interface()->registeredServiceNames();
711     QMap<QString, QString> observers;
712 
713     Q_FOREACH (QString name, registeredNames) {
714         if (!name.startsWith(QLatin1String("org.freedesktop.Telepathy.Client.TpQtSO"))) {
715             continue;
716         }
717 
718         if (QDBusConnection::sessionBus().interface()->serviceOwner(name).value() !=
719                 QDBusConnection::sessionBus().baseService()) {
720             continue;
721         }
722 
723         QString path = QLatin1Char('/') + name;
724         path.replace(QLatin1Char('.'), QLatin1Char('/'));
725 
726         ClientInterface client(name, path);
727         QStringList ifaces;
728         if (!waitForProperty(client.requestPropertyInterfaces(), &ifaces)) {
729             continue;
730         }
731 
732         if (!ifaces.contains(TP_QT_IFACE_CLIENT_OBSERVER)) {
733             continue;
734         }
735 
736         observers.insert(name, path);
737     }
738 
739     return observers;
740 }
741 
742 QTEST_MAIN(TestSimpleObserver)
743 #include "_gen/simple-observer.cpp.moc.hpp"
744