1 #include <QtCore/QDebug>
2 #include <QtCore/QTimer>
3 
4 #include <QtDBus/QtDBus>
5 
6 #include <QtTest/QtTest>
7 
8 #define TP_QT_ENABLE_LOWLEVEL_API
9 
10 #include <TelepathyQt/ChannelFactory>
11 #include <TelepathyQt/Connection>
12 #include <TelepathyQt/ConnectionLowlevel>
13 #include <TelepathyQt/Contact>
14 #include <TelepathyQt/ContactFactory>
15 #include <TelepathyQt/ContactManager>
16 #include <TelepathyQt/PendingContacts>
17 #include <TelepathyQt/PendingReady>
18 #include <TelepathyQt/Debug>
19 
20 #include <telepathy-glib/debug.h>
21 
22 #include <tests/lib/glib/contactlist2/conn.h>
23 #include <tests/lib/test.h>
24 
25 using namespace Tp;
26 
27 class TestConnRosterGroups : public Test
28 {
29     Q_OBJECT
30 
31 public:
TestConnRosterGroups(QObject * parent=0)32     TestConnRosterGroups(QObject *parent = 0)
33         : Test(parent), mConnService(0),
34           mContactsAddedToGroup(0), mContactsRemovedFromGroup(0)
35     { }
36 
37 private:
38     void causeCongestion(const ConnectionPtr &conn, const ContactPtr &contact);
39 
40 protected Q_SLOTS:
41     void onGroupAdded(const QString &group);
42     void onGroupRemoved(const QString &group);
43     void onContactAddedToGroup(const QString &group);
44     void onContactRemovedFromGroup(const QString &group);
45     void expectConnInvalidated();
46     void expectContact(Tp::PendingOperation*);
47     void exitOnStateSuccess(Tp::ContactListState);
48 
49 private Q_SLOTS:
50     void initTestCase();
51     void init();
52 
53     void testGroupsAfterStateChange();
54     void testIntrospectAfterStateChange();
55     void testRosterGroups();
56     void testNotADeathTrap();
57 
58     void cleanup();
59     void cleanupTestCase();
60 
61 private:
62     QString mConnName, mConnPath;
63     ExampleContactListConnection *mConnService;
64     ConnectionPtr mConn;
65     ContactPtr mContact;
66 
67     QString mGroupAdded;
68     QString mGroupRemoved;
69     int mContactsAddedToGroup;
70     int mContactsRemovedFromGroup;
71     bool mConnInvalidated;
72 };
73 
causeCongestion(const ConnectionPtr & conn,const ContactPtr & contact)74 void TestConnRosterGroups::causeCongestion(const ConnectionPtr &conn, const ContactPtr &contact)
75 {
76     // Cause some congestion in the roster events queue so we can check that it doesn't cause
77     // inconsistent event reordering
78     for (int i = 0; i < 5; i++) {
79         QString name = QString(QLatin1String("Rush%1")).arg(i);
80         conn->contactManager()->addGroup(name);
81         conn->contactManager()->addContactsToGroup(name, QList<ContactPtr>() << contact);
82         contact->requestPresenceSubscription();
83         contact->removePresenceSubscription();
84         conn->contactManager()->removeGroup(name);
85     }
86 }
87 
onGroupAdded(const QString & group)88 void TestConnRosterGroups::onGroupAdded(const QString &group)
89 {
90     if (group.startsWith(QLatin1String("Rush"))) {
91         return;
92     }
93 
94     mGroupAdded = group;
95 }
96 
onGroupRemoved(const QString & group)97 void TestConnRosterGroups::onGroupRemoved(const QString &group)
98 {
99     if (group.startsWith(QLatin1String("Rush"))) {
100         return;
101     }
102 
103     mGroupRemoved = group;
104 }
105 
onContactAddedToGroup(const QString & group)106 void TestConnRosterGroups::onContactAddedToGroup(const QString &group)
107 {
108     if (group.startsWith(QLatin1String("Rush"))) {
109         return;
110     }
111 
112     mContactsAddedToGroup++;
113 }
114 
onContactRemovedFromGroup(const QString & group)115 void TestConnRosterGroups::onContactRemovedFromGroup(const QString &group)
116 {
117     if (group.startsWith(QLatin1String("Rush"))) {
118         return;
119     }
120 
121     mContactsRemovedFromGroup++;
122 }
123 
expectConnInvalidated()124 void TestConnRosterGroups::expectConnInvalidated()
125 {
126     mConnInvalidated = true;
127     mLoop->exit(0);
128 }
129 
expectContact(Tp::PendingOperation * op)130 void TestConnRosterGroups::expectContact(Tp::PendingOperation *op)
131 {
132     PendingContacts *contacts = qobject_cast<PendingContacts *>(op);
133     QVERIFY(contacts != 0);
134 
135     QVERIFY(contacts->isValid());
136     QCOMPARE(contacts->contacts().length(), 1);
137 
138     mContact = contacts->contacts()[0];
139 
140     mLoop->exit(0);
141 }
142 
exitOnStateSuccess(Tp::ContactListState state)143 void TestConnRosterGroups::exitOnStateSuccess(Tp::ContactListState state)
144 {
145     qDebug() << "got contact list state" << state;
146 
147     if (state == ContactListStateSuccess) {
148         mLoop->exit(0);
149     }
150 }
151 
initTestCase()152 void TestConnRosterGroups::initTestCase()
153 {
154     initTestCaseImpl();
155 
156     g_type_init();
157     g_set_prgname("conn-roster-groups");
158     tp_debug_set_flags("all");
159     dbus_g_bus_get(DBUS_BUS_STARTER, 0);
160 }
161 
init()162 void TestConnRosterGroups::init()
163 {
164     gchar *name;
165     gchar *connPath;
166     GError *error = 0;
167 
168     mConnService = EXAMPLE_CONTACT_LIST_CONNECTION(g_object_new(
169             EXAMPLE_TYPE_CONTACT_LIST_CONNECTION,
170             "account", "me@example.com",
171             "simulation-delay", 0,
172             "protocol", "example-contact-list",
173             NULL));
174     QVERIFY(mConnService != 0);
175     QVERIFY(tp_base_connection_register(TP_BASE_CONNECTION(mConnService),
176                 "foo", &name, &connPath, &error));
177     QVERIFY(error == 0);
178 
179     QVERIFY(name != 0);
180     QVERIFY(connPath != 0);
181 
182     mConnName = QLatin1String(name);
183     mConnPath = QLatin1String(connPath);
184 
185     g_free(name);
186     g_free(connPath);
187     initImpl();
188 
189     mConnInvalidated = false;
190 }
191 
testGroupsAfterStateChange()192 void TestConnRosterGroups::testGroupsAfterStateChange()
193 {
194     // Create a conn and make the roster groups related features ready
195     mConn = Connection::create(mConnName, mConnPath,
196             ChannelFactory::create(QDBusConnection::sessionBus()),
197             ContactFactory::create());
198 
199     ContactManagerPtr contactManager = mConn->contactManager();
200 
201     Features features = Features() << Connection::FeatureRoster << Connection::FeatureRosterGroups;
202     QVERIFY(connect(mConn->becomeReady(features),
203             SIGNAL(finished(Tp::PendingOperation*)),
204             SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
205     QCOMPARE(mLoop->exec(), 0);
206     QCOMPARE(mConn->isReady(Connection::FeatureRoster), true);
207     QCOMPARE(mConn->isReady(Connection::FeatureRosterGroups), true);
208 
209     // Now start connecting it, and wait for the ContactManager state to turn to Success
210     QVERIFY(connect(contactManager.data(),
211                     SIGNAL(stateChanged(Tp::ContactListState)),
212                     SLOT(exitOnStateSuccess(Tp::ContactListState))));
213 
214     mConn->lowlevel()->requestConnect();
215 
216     QCOMPARE(mLoop->exec(), 0);
217     QCOMPARE(static_cast<uint>(contactManager->state()),
218              static_cast<uint>(ContactListStateSuccess));
219 
220     // The conn should be valid and have the roster groups features ready when it emits Success
221     QVERIFY(mConn->isValid());
222     QCOMPARE(mConn->isReady(Connection::FeatureRoster), true);
223     QCOMPARE(mConn->isReady(Connection::FeatureRosterGroups), true);
224 
225     // We should have all the group data downloaded now, check for that
226     QStringList expectedGroups;
227     expectedGroups << QLatin1String("Cambridge") << QLatin1String("Francophones")
228         << QLatin1String("Montreal");
229     expectedGroups.sort();
230     QStringList groups = contactManager->allKnownGroups();
231     groups.sort();
232     QCOMPARE(groups, expectedGroups);
233 
234     // Cambridge
235     {
236         QStringList expectedContacts;
237         expectedContacts << QLatin1String("geraldine@example.com")
238             << QLatin1String("helen@example.com")
239             << QLatin1String("guillaume@example.com")
240             << QLatin1String("sjoerd@example.com");
241         expectedContacts.sort();
242         QStringList contacts;
243         Q_FOREACH (const ContactPtr &contact, contactManager->groupContacts(QLatin1String("Cambridge"))) {
244             contacts << contact->id();
245         }
246         contacts.sort();
247         QCOMPARE(contacts, expectedContacts);
248     }
249 
250     // Francophones
251     {
252         QStringList expectedContacts;
253         expectedContacts << QLatin1String("olivier@example.com")
254             << QLatin1String("geraldine@example.com")
255             << QLatin1String("guillaume@example.com");
256         expectedContacts.sort();
257         QStringList contacts;
258         Q_FOREACH (const ContactPtr &contact, contactManager->groupContacts(QLatin1String("Francophones"))) {
259             contacts << contact->id();
260         }
261         contacts.sort();
262         QCOMPARE(contacts, expectedContacts);
263     }
264 
265     // Montreal
266     {
267         QStringList expectedContacts;
268         expectedContacts << QLatin1String("olivier@example.com");
269         expectedContacts.sort();
270         QStringList contacts;
271         Q_FOREACH (const ContactPtr &contact, contactManager->groupContacts(QLatin1String("Montreal"))) {
272             contacts << contact->id();
273         }
274         contacts.sort();
275         QCOMPARE(contacts, expectedContacts);
276     }
277 }
278 
testIntrospectAfterStateChange()279 void TestConnRosterGroups::testIntrospectAfterStateChange()
280 {
281     // Create a conn and make the roster feature ready
282     mConn = Connection::create(mConnName, mConnPath,
283             ChannelFactory::create(QDBusConnection::sessionBus()),
284             ContactFactory::create());
285 
286     ContactManagerPtr contactManager = mConn->contactManager();
287 
288     Features features = Features() << Connection::FeatureRoster;
289     QVERIFY(connect(mConn->becomeReady(features),
290             SIGNAL(finished(Tp::PendingOperation*)),
291             SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
292     QCOMPARE(mLoop->exec(), 0);
293     QCOMPARE(mConn->isReady(Connection::FeatureRoster), true);
294     QCOMPARE(mConn->isReady(Connection::FeatureRosterGroups), false);
295 
296     // Now start connecting it, and wait for the ContactManager state to turn to Success
297     QVERIFY(connect(contactManager.data(),
298                     SIGNAL(stateChanged(Tp::ContactListState)),
299                     SLOT(exitOnStateSuccess(Tp::ContactListState))));
300 
301     mConn->lowlevel()->requestConnect();
302 
303     QCOMPARE(mLoop->exec(), 0);
304     QCOMPARE(static_cast<uint>(contactManager->state()),
305              static_cast<uint>(ContactListStateSuccess));
306 
307     // The conn should be valid and have the roster feature ready when it emits Success, but not
308     // RosterGroups because we didn't request it
309     QVERIFY(mConn->isValid());
310     QCOMPARE(mConn->isReady(Connection::FeatureRoster), true);
311     QCOMPARE(mConn->isReady(Connection::FeatureRosterGroups), false);
312 
313     // We should have roster contacts now, but no groups
314     QVERIFY(!contactManager->allKnownContacts().isEmpty());
315     QVERIFY(contactManager->allKnownGroups().isEmpty());
316 
317     // Make RosterGroups ready too
318     features = Features() << Connection::FeatureRosterGroups;
319     QVERIFY(connect(mConn->becomeReady(features),
320             SIGNAL(finished(Tp::PendingOperation*)),
321             SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
322     QCOMPARE(mLoop->exec(), 0);
323     QCOMPARE(mConn->isReady(Connection::FeatureRoster), true);
324     QCOMPARE(mConn->isReady(Connection::FeatureRosterGroups), true);
325 
326     // We should still have the contacts, and the state should be success
327     QVERIFY(!contactManager->allKnownContacts().isEmpty());
328     QCOMPARE(static_cast<uint>(contactManager->state()),
329              static_cast<uint>(ContactListStateSuccess));
330 
331     // We should have all the group data downloaded now, check for that
332     QStringList expectedGroups;
333     expectedGroups << QLatin1String("Cambridge") << QLatin1String("Francophones")
334         << QLatin1String("Montreal");
335     expectedGroups.sort();
336     QStringList groups = contactManager->allKnownGroups();
337     groups.sort();
338     QCOMPARE(groups, expectedGroups);
339 
340     // Cambridge
341     {
342         QStringList expectedContacts;
343         expectedContacts << QLatin1String("geraldine@example.com")
344             << QLatin1String("helen@example.com")
345             << QLatin1String("guillaume@example.com")
346             << QLatin1String("sjoerd@example.com");
347         expectedContacts.sort();
348         QStringList contacts;
349         Q_FOREACH (const ContactPtr &contact, contactManager->groupContacts(QLatin1String("Cambridge"))) {
350             contacts << contact->id();
351         }
352         contacts.sort();
353         QCOMPARE(contacts, expectedContacts);
354     }
355 }
356 
testRosterGroups()357 void TestConnRosterGroups::testRosterGroups()
358 {
359     mConn = Connection::create(mConnName, mConnPath,
360             ChannelFactory::create(QDBusConnection::sessionBus()),
361             ContactFactory::create());
362 
363     QVERIFY(connect(mConn->lowlevel()->requestConnect(),
364                     SIGNAL(finished(Tp::PendingOperation*)),
365                     SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
366     QCOMPARE(mLoop->exec(), 0);
367     QCOMPARE(mConn->isReady(), true);
368     QCOMPARE(mConn->status(), ConnectionStatusConnected);
369 
370     Features features = Features() << Connection::FeatureRoster << Connection::FeatureRosterGroups
371         << Connection::FeatureSelfContact;
372     QVERIFY(connect(mConn->becomeReady(features),
373             SIGNAL(finished(Tp::PendingOperation*)),
374             SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
375     QCOMPARE(mLoop->exec(), 0);
376     QCOMPARE(mConn->isReady(features), true);
377 
378     QCOMPARE(static_cast<uint>(mConn->contactManager()->state()),
379              static_cast<uint>(ContactListStateSuccess));
380 
381     ContactManagerPtr contactManager = mConn->contactManager();
382 
383     QStringList expectedGroups;
384     expectedGroups << QLatin1String("Cambridge") << QLatin1String("Francophones")
385         << QLatin1String("Montreal");
386     expectedGroups.sort();
387     QStringList groups = contactManager->allKnownGroups();
388     groups.sort();
389     QCOMPARE(groups, expectedGroups);
390 
391     QString group(QLatin1String("foo"));
392     QVERIFY(contactManager->groupContacts(group).isEmpty());
393 
394     causeCongestion(mConn, mConn->selfContact());
395 
396     // add group foo
397     QVERIFY(connect(contactManager.data(),
398                     SIGNAL(groupAdded(const QString&)),
399                     SLOT(onGroupAdded(const QString&))));
400 
401     causeCongestion(mConn, mConn->selfContact());
402 
403     QVERIFY(connect(contactManager->addGroup(group),
404                     SIGNAL(finished(Tp::PendingOperation*)),
405                     SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
406     QCOMPARE(mLoop->exec(), 0);
407     QVERIFY(!mGroupAdded.isEmpty());
408     QCOMPARE(mGroupAdded, group);
409 
410     expectedGroups << group;
411     expectedGroups.sort();
412     groups = contactManager->allKnownGroups();
413     groups.sort();
414     QCOMPARE(groups, expectedGroups);
415 
416     causeCongestion(mConn, mConn->selfContact());
417 
418     // add Montreal contacts to group foo
419     Contacts contacts = contactManager->groupContacts(QLatin1String("Montreal"));
420     Q_FOREACH (const ContactPtr &contact, contacts) {
421         QVERIFY(connect(contact.data(),
422                         SIGNAL(addedToGroup(const QString&)),
423                         SLOT(onContactAddedToGroup(const QString&))));
424     }
425 
426     causeCongestion(mConn, mConn->selfContact());
427 
428     QVERIFY(connect(contactManager->addContactsToGroup(group, contacts.toList()),
429                     SIGNAL(finished(Tp::PendingOperation*)),
430                     SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
431     QCOMPARE(mLoop->exec(), 0);
432     QCOMPARE(mContactsAddedToGroup, contacts.size());
433     Q_FOREACH (const ContactPtr &contact, contacts) {
434         QVERIFY(contact->groups().contains(group));
435     }
436 
437     causeCongestion(mConn, mConn->selfContact());
438 
439     // remove all contacts from group foo
440     contacts = contactManager->groupContacts(group);
441     Q_FOREACH (const ContactPtr &contact, contacts) {
442         QVERIFY(connect(contact.data(),
443                         SIGNAL(removedFromGroup(const QString&)),
444                         SLOT(onContactRemovedFromGroup(const QString&))));
445     }
446 
447     causeCongestion(mConn, mConn->selfContact());
448 
449     QVERIFY(connect(contactManager->removeContactsFromGroup(group, contacts.toList()),
450                     SIGNAL(finished(Tp::PendingOperation*)),
451                     SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
452     QCOMPARE(mLoop->exec(), 0);
453     QCOMPARE(mContactsRemovedFromGroup, contacts.size());
454     Q_FOREACH (const ContactPtr &contact, contacts) {
455         QVERIFY(!contact->groups().contains(group));
456     }
457 
458     causeCongestion(mConn, mConn->selfContact());
459 
460     // remove group foo
461     QVERIFY(connect(contactManager.data(),
462                     SIGNAL(groupRemoved(const QString&)),
463                     SLOT(onGroupRemoved(const QString&))));
464 
465     causeCongestion(mConn, mConn->selfContact());
466 
467     QVERIFY(connect(contactManager->removeGroup(group),
468                     SIGNAL(finished(Tp::PendingOperation*)),
469                     SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
470     QCOMPARE(mLoop->exec(), 0);
471     QVERIFY(!mGroupRemoved.isEmpty());
472     QCOMPARE(mGroupRemoved, group);
473 
474     expectedGroups.removeOne(group);
475     expectedGroups.sort();
476     groups = contactManager->allKnownGroups();
477     groups.sort();
478     QCOMPARE(groups, expectedGroups);
479 }
480 
481 /**
482  * Verify that ContactManager isn't a death-trap.
483  *
484  * Background: Connection::contactManager() used to unpredictably waver between NULL and the real
485  * manager when the connection was in the process of being disconnected / otherwise invalidated,
486  * which led to a great many segfaults, which was especially unfortunate considering the
487  * ContactManager methods didn't do much any checks at all.
488  */
testNotADeathTrap()489 void TestConnRosterGroups::testNotADeathTrap()
490 {
491     mConn = Connection::create(mConnName, mConnPath,
492             ChannelFactory::create(QDBusConnection::sessionBus()),
493             ContactFactory::create());
494     QCOMPARE(mConn->isReady(), false);
495 
496     // Check that the contact manager doesn't crash, but returns an error (because the conn isn't
497     // ready)
498     QVERIFY(!mConn->contactManager().isNull());
499     QVERIFY(connect(mConn->contactManager()->contactsForIdentifiers(QStringList()),
500                 SIGNAL(finished(Tp::PendingOperation*)),
501                 SLOT(expectFailure(Tp::PendingOperation*))));
502     QCOMPARE(mLoop->exec(), 0);
503 
504     QVERIFY(connect(mConn->lowlevel()->requestConnect(),
505                     SIGNAL(finished(Tp::PendingOperation*)),
506                     SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
507     QCOMPARE(mLoop->exec(), 0);
508     QCOMPARE(mConn->isReady(), true);
509     QCOMPARE(mConn->status(), ConnectionStatusConnected);
510 
511     // As the conn is now ready, the contact building functions shouldn't return an error now
512     QVERIFY(!mConn->contactManager().isNull());
513 
514     QVERIFY(connect(mConn->contactManager()->contactsForIdentifiers(QStringList()),
515                 SIGNAL(finished(Tp::PendingOperation*)),
516                 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
517     QCOMPARE(mLoop->exec(), 0);
518 
519     QVERIFY(connect(mConn->contactManager()->contactsForHandles(UIntList()),
520                 SIGNAL(finished(Tp::PendingOperation*)),
521                 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
522     QCOMPARE(mLoop->exec(), 0);
523 
524     QVERIFY(connect(mConn->contactManager()->upgradeContacts(QList<ContactPtr>(),
525                     Features()),
526                 SIGNAL(finished(Tp::PendingOperation*)),
527                 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
528     QCOMPARE(mLoop->exec(), 0);
529 
530     // In fact, let's build a contact for future use
531     QVERIFY(connect(mConn->contactManager()->contactsForIdentifiers(
532                     QStringList() << QLatin1String("friendorfoe@example.com")),
533                 SIGNAL(finished(Tp::PendingOperation*)),
534                 SLOT(expectContact(Tp::PendingOperation*))));
535     QCOMPARE(mLoop->exec(), 0);
536     QVERIFY(mContact->id() == QLatin1String("friendorfoe@example.com"));
537 
538     // Roster operations SHOULD still fail though, as FeatureRoster isn't ready
539     QVERIFY(connect(mConn->contactManager()->requestPresenceSubscription(
540                     QList<ContactPtr>() << mContact,
541                     QLatin1String("I just want to see you fail")),
542                 SIGNAL(finished(Tp::PendingOperation*)),
543                 SLOT(expectFailure(Tp::PendingOperation*))));
544     QCOMPARE(mLoop->exec(), 0);
545 
546     QVERIFY(connect(mConn->contactManager()->removePresenceSubscription(
547                     QList<ContactPtr>() << mContact,
548                     QLatin1String("I just want to see you fail")),
549                 SIGNAL(finished(Tp::PendingOperation*)),
550                 SLOT(expectFailure(Tp::PendingOperation*))));
551     QCOMPARE(mLoop->exec(), 0);
552 
553     QVERIFY(connect(mConn->contactManager()->authorizePresencePublication(
554                     QList<ContactPtr>() << mContact,
555                     QLatin1String("I just want to see you fail")),
556                 SIGNAL(finished(Tp::PendingOperation*)),
557                 SLOT(expectFailure(Tp::PendingOperation*))));
558     QCOMPARE(mLoop->exec(), 0);
559 
560     QVERIFY(connect(mConn->contactManager()->removePresencePublication(
561                     QList<ContactPtr>() << mContact,
562                     QLatin1String("I just want to see you fail")),
563                 SIGNAL(finished(Tp::PendingOperation*)),
564                 SLOT(expectFailure(Tp::PendingOperation*))));
565     QCOMPARE(mLoop->exec(), 0);
566 
567     // Now, make Roster ready
568     Features features = Features() << Connection::FeatureRoster;
569     QVERIFY(connect(mConn->becomeReady(features),
570             SIGNAL(finished(Tp::PendingOperation*)),
571             SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
572     QCOMPARE(mLoop->exec(), 0);
573     QCOMPARE(mConn->isReady(features), true);
574 
575     causeCongestion(mConn, mContact);
576 
577     // The roster functions should work now
578     QVERIFY(connect(mConn->contactManager()->requestPresenceSubscription(
579                     QList<ContactPtr>() << mContact,
580                     QLatin1String("Please don't fail")),
581                 SIGNAL(finished(Tp::PendingOperation*)),
582                 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
583     QCOMPARE(mLoop->exec(), 0);
584 
585     QVERIFY(mContact->subscriptionState() != Contact::PresenceStateNo);
586 
587     causeCongestion(mConn, mContact);
588 
589     QVERIFY(connect(mConn->contactManager()->removePresenceSubscription(
590                     QList<ContactPtr>() << mContact,
591                     QLatin1String("Please don't fail")),
592                 SIGNAL(finished(Tp::PendingOperation*)),
593                 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
594     QCOMPARE(mLoop->exec(), 0);
595 
596     QCOMPARE(mContact->subscriptionState(), Contact::PresenceStateNo);
597 
598     causeCongestion(mConn, mContact);
599 
600     QVERIFY(connect(mConn->contactManager()->authorizePresencePublication(
601                     QList<ContactPtr>() << mContact,
602                     QLatin1String("Please don't fail")),
603                 SIGNAL(finished(Tp::PendingOperation*)),
604                 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
605     QCOMPARE(mLoop->exec(), 0);
606 
607     causeCongestion(mConn, mContact);
608 
609     QVERIFY(connect(mConn->contactManager()->removePresencePublication(
610                     QList<ContactPtr>() << mContact,
611                     QLatin1String("Please don't fail")),
612                 SIGNAL(finished(Tp::PendingOperation*)),
613                 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
614     QCOMPARE(mLoop->exec(), 0);
615 
616     // ... but still not the RosterGroup ones
617     QVERIFY(connect(mConn->contactManager()->addGroup(QLatin1String("Those who failed")),
618                 SIGNAL(finished(Tp::PendingOperation*)),
619                 SLOT(expectFailure(Tp::PendingOperation*))));
620     QCOMPARE(mLoop->exec(), 0);
621 
622     QVERIFY(connect(mConn->contactManager()->removeGroup(QLatin1String("Those who failed")),
623                 SIGNAL(finished(Tp::PendingOperation*)),
624                 SLOT(expectFailure(Tp::PendingOperation*))));
625     QCOMPARE(mLoop->exec(), 0);
626 
627     QVERIFY(connect(mConn->contactManager()->addContactsToGroup(QLatin1String("Those who failed"),
628                     QList<ContactPtr>() << mContact),
629                 SIGNAL(finished(Tp::PendingOperation*)),
630                 SLOT(expectFailure(Tp::PendingOperation*))));
631     QCOMPARE(mLoop->exec(), 0);
632 
633     QVERIFY(connect(mConn->contactManager()->removeContactsFromGroup(QLatin1String("Those who failed"),
634                     QList<ContactPtr>() << mContact),
635                 SIGNAL(finished(Tp::PendingOperation*)),
636                 SLOT(expectFailure(Tp::PendingOperation*))));
637     QCOMPARE(mLoop->exec(), 0);
638 
639     // Make RosterGroups ready too
640     features = Features() << Connection::FeatureRosterGroups;
641     QVERIFY(connect(mConn->becomeReady(features),
642             SIGNAL(finished(Tp::PendingOperation*)),
643             SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
644     QCOMPARE(mLoop->exec(), 0);
645     QCOMPARE(mConn->isReady(features), true);
646 
647     // Now that Core, Roster and RosterGroups are all ready, everything should work
648     QVERIFY(!mConn->contactManager().isNull());
649 
650     QVERIFY(connect(mConn->contactManager()->contactsForIdentifiers(QStringList()),
651                 SIGNAL(finished(Tp::PendingOperation*)),
652                 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
653     QCOMPARE(mLoop->exec(), 0);
654 
655     QVERIFY(connect(mConn->contactManager()->contactsForHandles(UIntList()),
656                 SIGNAL(finished(Tp::PendingOperation*)),
657                 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
658     QCOMPARE(mLoop->exec(), 0);
659 
660     QVERIFY(connect(mConn->contactManager()->upgradeContacts(QList<ContactPtr>(),
661                     Features()),
662                 SIGNAL(finished(Tp::PendingOperation*)),
663                 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
664     QCOMPARE(mLoop->exec(), 0);
665 
666     causeCongestion(mConn, mContact);
667 
668     QVERIFY(connect(mConn->contactManager()->requestPresenceSubscription(
669                     QList<ContactPtr>() << mContact,
670                     QLatin1String("Please don't fail")),
671                 SIGNAL(finished(Tp::PendingOperation*)),
672                 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
673     QCOMPARE(mLoop->exec(), 0);
674 
675     QVERIFY(mContact->subscriptionState() != Contact::PresenceStateNo);
676 
677     causeCongestion(mConn, mContact);
678 
679     QVERIFY(connect(mConn->contactManager()->removePresenceSubscription(
680                     QList<ContactPtr>() << mContact,
681                     QLatin1String("Please don't fail")),
682                 SIGNAL(finished(Tp::PendingOperation*)),
683                 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
684     QCOMPARE(mLoop->exec(), 0);
685 
686     QCOMPARE(mContact->subscriptionState(), Contact::PresenceStateNo);
687 
688     QVERIFY(connect(mConn->contactManager()->authorizePresencePublication(
689                     QList<ContactPtr>() << mContact,
690                     QLatin1String("Please don't fail")),
691                 SIGNAL(finished(Tp::PendingOperation*)),
692                 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
693     QCOMPARE(mLoop->exec(), 0);
694 
695     QVERIFY(connect(mConn->contactManager()->removePresencePublication(
696                     QList<ContactPtr>() << mContact,
697                     QLatin1String("Please don't fail")),
698                 SIGNAL(finished(Tp::PendingOperation*)),
699                 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
700     QCOMPARE(mLoop->exec(), 0);
701 
702     causeCongestion(mConn, mContact);
703 
704     QVERIFY(connect(mConn->contactManager()->addGroup(QLatin1String("My successful entourage")),
705                 SIGNAL(finished(Tp::PendingOperation*)),
706                 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
707     QCOMPARE(mLoop->exec(), 0);
708 
709     QVERIFY(mConn->contactManager()->allKnownGroups().contains(QLatin1String("My successful entourage")));
710 
711     causeCongestion(mConn, mContact);
712 
713     QVERIFY(connect(mConn->contactManager()->addContactsToGroup(QLatin1String("My successful entourage"),
714                     QList<ContactPtr>() << mContact),
715                 SIGNAL(finished(Tp::PendingOperation*)),
716                 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
717     QCOMPARE(mLoop->exec(), 0);
718 
719     QVERIFY(mConn->contactManager()->
720         groupContacts(QLatin1String("My successful entourage")).contains(mContact));
721 
722     causeCongestion(mConn, mContact);
723 
724     QVERIFY(connect(mConn->contactManager()->removeContactsFromGroup(QLatin1String("My successful entourage"),
725                     QList<ContactPtr>() << mContact),
726                 SIGNAL(finished(Tp::PendingOperation*)),
727                 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
728     QCOMPARE(mLoop->exec(), 0);
729 
730     QVERIFY(!mConn->contactManager()->
731         groupContacts(QLatin1String("My successful entourage")).contains(mContact));
732 
733     causeCongestion(mConn, mContact);
734 
735     QVERIFY(connect(mConn->contactManager()->removeGroup(QLatin1String("My successful entourage")),
736                 SIGNAL(finished(Tp::PendingOperation*)),
737                 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
738     QCOMPARE(mLoop->exec(), 0);
739 
740     QVERIFY(!mConn->contactManager()->allKnownGroups().contains(QLatin1String("My successful entourage")));
741 
742     // Now, invalidate the connection by disconnecting it
743     QVERIFY(connect(mConn.data(),
744                 SIGNAL(invalidated(Tp::DBusProxy *,
745                         const QString &, const QString &)),
746                 SLOT(expectConnInvalidated())));
747     mConn->lowlevel()->requestDisconnect();
748 
749     // Check that contactManager doesn't go NULL in the process of the connection going invalidated
750     do {
751         QVERIFY(!mConn->contactManager().isNull());
752         mLoop->processEvents();
753     } while (!mConnInvalidated);
754 
755     QVERIFY(!mConn->isValid());
756     QCOMPARE(mConn->status(), ConnectionStatusDisconnected);
757 
758     // Now that the conn is invalidated NOTHING should work anymore
759     QVERIFY(connect(mConn->contactManager()->contactsForIdentifiers(QStringList()),
760                 SIGNAL(finished(Tp::PendingOperation*)),
761                 SLOT(expectFailure(Tp::PendingOperation*))));
762     QCOMPARE(mLoop->exec(), 0);
763 
764     QVERIFY(connect(mConn->contactManager()->contactsForHandles(UIntList()),
765                 SIGNAL(finished(Tp::PendingOperation*)),
766                 SLOT(expectFailure(Tp::PendingOperation*))));
767     QCOMPARE(mLoop->exec(), 0);
768 
769     QVERIFY(connect(mConn->contactManager()->upgradeContacts(QList<ContactPtr>(),
770                     Features()),
771                 SIGNAL(finished(Tp::PendingOperation*)),
772                 SLOT(expectFailure(Tp::PendingOperation*))));
773     QCOMPARE(mLoop->exec(), 0);
774 
775     QVERIFY(connect(mConn->contactManager()->requestPresenceSubscription(QList<ContactPtr>(),
776                     QLatin1String("You fail at life")),
777                 SIGNAL(finished(Tp::PendingOperation*)),
778                 SLOT(expectFailure(Tp::PendingOperation*))));
779     QCOMPARE(mLoop->exec(), 0);
780 
781     QVERIFY(connect(mConn->contactManager()->removePresenceSubscription(QList<ContactPtr>(),
782                     QLatin1String("You fail at life")),
783                 SIGNAL(finished(Tp::PendingOperation*)),
784                 SLOT(expectFailure(Tp::PendingOperation*))));
785     QCOMPARE(mLoop->exec(), 0);
786 
787     QVERIFY(connect(mConn->contactManager()->authorizePresencePublication(QList<ContactPtr>(),
788                     QLatin1String("You fail at life")),
789                 SIGNAL(finished(Tp::PendingOperation*)),
790                 SLOT(expectFailure(Tp::PendingOperation*))));
791     QCOMPARE(mLoop->exec(), 0);
792 
793     QVERIFY(connect(mConn->contactManager()->removePresencePublication(QList<ContactPtr>(),
794                     QLatin1String("You fail at life")),
795                 SIGNAL(finished(Tp::PendingOperation*)),
796                 SLOT(expectFailure(Tp::PendingOperation*))));
797     QCOMPARE(mLoop->exec(), 0);
798 
799     QVERIFY(connect(mConn->contactManager()->addGroup(QLatin1String("Future failures")),
800                 SIGNAL(finished(Tp::PendingOperation*)),
801                 SLOT(expectFailure(Tp::PendingOperation*))));
802     QCOMPARE(mLoop->exec(), 0);
803 
804     QVERIFY(connect(mConn->contactManager()->removeGroup(QLatin1String("Future failures")),
805                 SIGNAL(finished(Tp::PendingOperation*)),
806                 SLOT(expectFailure(Tp::PendingOperation*))));
807     QCOMPARE(mLoop->exec(), 0);
808 
809     QVERIFY(connect(mConn->contactManager()->addContactsToGroup(QLatin1String("Future failures"),
810                     QList<ContactPtr>() << mContact),
811                 SIGNAL(finished(Tp::PendingOperation*)),
812                 SLOT(expectFailure(Tp::PendingOperation*))));
813     QCOMPARE(mLoop->exec(), 0);
814 
815     QVERIFY(connect(mConn->contactManager()->removeContactsFromGroup(QLatin1String("Future failures"),
816                     QList<ContactPtr>() << mContact),
817                 SIGNAL(finished(Tp::PendingOperation*)),
818                 SLOT(expectFailure(Tp::PendingOperation*))));
819     QCOMPARE(mLoop->exec(), 0);
820 }
821 
cleanup()822 void TestConnRosterGroups::cleanup()
823 {
824     mContact.reset();
825 
826     if (mConn && mConn->requestedFeatures().contains(Connection::FeatureCore)) {
827         QVERIFY(mConnService != NULL);
828 
829         if (TP_BASE_CONNECTION(mConnService)->status != TP_CONNECTION_STATUS_DISCONNECTED) {
830             tp_base_connection_change_status(TP_BASE_CONNECTION(mConnService),
831                     TP_CONNECTION_STATUS_DISCONNECTED,
832                     TP_CONNECTION_STATUS_REASON_REQUESTED);
833         }
834 
835         while (mConn->isValid()) {
836             mLoop->processEvents();
837         }
838 
839     }
840     mConn.reset();
841 
842     if (mConnService != 0) {
843         g_object_unref(mConnService);
844         mConnService = 0;
845     }
846 
847     cleanupImpl();
848 }
849 
cleanupTestCase()850 void TestConnRosterGroups::cleanupTestCase()
851 {
852     cleanupTestCaseImpl();
853 }
854 
855 QTEST_MAIN(TestConnRosterGroups)
856 #include "_gen/conn-roster-groups.cpp.moc.hpp"
857