1 #include <QtCore/QDebug>
2 #include <QtCore/QScopedPointer>
3 #include <QtCore/QTimer>
4 #include <QtDBus/QtDBus>
5 #include <QtTest/QtTest>
6 
7 #include <QDateTime>
8 #include <QString>
9 #include <QVariantMap>
10 
11 #include <TelepathyQt/Connection>
12 #include <TelepathyQt/ConnectionFactory>
13 #include <TelepathyQt/ContactFactory>
14 #include <TelepathyQt/ChannelFactory>
15 #include <TelepathyQt/Debug>
16 #include <TelepathyQt/PendingReady>
17 #include <TelepathyQt/Types>
18 
19 #include <TelepathyQt/test-backdoors.h>
20 
21 #include <telepathy-glib/debug.h>
22 
23 #include <glib-object.h>
24 #include <dbus/dbus-glib.h>
25 
26 #include <tests/lib/glib/contacts-conn.h>
27 #include <tests/lib/test.h>
28 
29 using namespace Tp;
30 
31 class TestDBusProxyFactory : public Test
32 {
33     Q_OBJECT
34 
35 public:
TestDBusProxyFactory(QObject * parent=0)36     TestDBusProxyFactory(QObject *parent = 0)
37         : Test(parent),
38           mConnService1(0), mConnService2(0)
39     { }
40 
41 protected Q_SLOTS:
42     void expectFinished();
43 
44 private Q_SLOTS:
45     void initTestCase();
46     void init();
47 
48     void testCaching();
49     void testDropRefs();
50     void testInvalidate();
51     void testBogusService();
52 
53     void cleanup();
54     void cleanupTestCase();
55 
56 private:
57     TpTestsContactsConnection *mConnService1, *mConnService2;
58     QString mConnPath1, mConnPath2;
59     QString mConnName1, mConnName2;
60     ConnectionFactoryPtr mFactory;
61     uint mNumFinished;
62 };
63 
expectFinished()64 void TestDBusProxyFactory::expectFinished()
65 {
66     mNumFinished++;
67 }
68 
initTestCase()69 void TestDBusProxyFactory::initTestCase()
70 {
71     initTestCaseImpl();
72 
73     g_type_init();
74     g_set_prgname("dbus-proxy-factory");
75     tp_debug_set_flags("all");
76     dbus_g_bus_get(DBUS_BUS_STARTER, 0);
77 
78     gchar *name;
79     gchar *connPath;
80     GError *error = 0;
81 
82     mConnService1 = TP_TESTS_CONTACTS_CONNECTION(g_object_new(
83             TP_TESTS_TYPE_CONTACTS_CONNECTION,
84             "account", "me1@example.com",
85             "protocol", "simple",
86             NULL));
87     QVERIFY(mConnService1 != 0);
88     QVERIFY(tp_base_connection_register(TP_BASE_CONNECTION(mConnService1),
89                 "contacts", &name, &connPath, &error));
90     QVERIFY(error == 0);
91 
92     QVERIFY(name != 0);
93     QVERIFY(connPath != 0);
94 
95     mConnName1 = QLatin1String(name);
96     mConnPath1 = QLatin1String(connPath);
97 
98     g_free(name);
99     g_free(connPath);
100 
101     mConnService2 = TP_TESTS_CONTACTS_CONNECTION(g_object_new(
102             TP_TESTS_TYPE_CONTACTS_CONNECTION,
103             "account", "me2@example.com",
104             "protocol", "simple",
105             NULL));
106     QVERIFY(mConnService2 != 0);
107     QVERIFY(tp_base_connection_register(TP_BASE_CONNECTION(mConnService2),
108                 "contacts", &name, &connPath, &error));
109     QVERIFY(error == 0);
110 
111     QVERIFY(name != 0);
112     QVERIFY(connPath != 0);
113 
114     mConnName2 = QLatin1String(name);
115     mConnPath2 = QLatin1String(connPath);
116 
117     g_free(name);
118     g_free(connPath);
119 }
120 
init()121 void TestDBusProxyFactory::init()
122 {
123     initImpl();
124 
125     mFactory = ConnectionFactory::create(QDBusConnection::sessionBus(),
126             Connection::FeatureCore);
127     mNumFinished = 0;
128 }
129 
testCaching()130 void TestDBusProxyFactory::testCaching()
131 {
132     PendingReady *first = mFactory->proxy(mConnName1, mConnPath1,
133             ChannelFactory::create(QDBusConnection::sessionBus()),
134             ContactFactory::create());
135 
136     QVERIFY(first != NULL);
137     QVERIFY(!first->proxy().isNull());
138 
139     PendingReady *same = mFactory->proxy(mConnName1, mConnPath1,
140             ChannelFactory::create(QDBusConnection::sessionBus()),
141             ContactFactory::create());
142 
143     QVERIFY(same != NULL);
144     QVERIFY(!same->proxy().isNull());
145 
146     QCOMPARE(same->proxy().data(), first->proxy().data());
147 
148     PendingReady *different = mFactory->proxy(mConnName2, mConnPath2,
149             ChannelFactory::create(QDBusConnection::sessionBus()),
150             ContactFactory::create());
151 
152     QVERIFY(different != NULL);
153     QVERIFY(!different->proxy().isNull());
154 
155     QVERIFY(different->proxy() != first->proxy());
156 
157     ConnectionPtr firstProxy = ConnectionPtr::qObjectCast(first->proxy());
158 
159     QVERIFY(!first->isFinished() && !same->isFinished() && !different->isFinished());
160     QVERIFY(connect(first, SIGNAL(finished(Tp::PendingOperation*)),
161                 SLOT(expectFinished())));
162     QVERIFY(connect(same, SIGNAL(finished(Tp::PendingOperation*)),
163                 SLOT(expectFinished())));
164     QVERIFY(connect(different, SIGNAL(finished(Tp::PendingOperation*)),
165                 SLOT(expectFinished())));
166     QVERIFY(!first->isFinished() && !same->isFinished() && !different->isFinished());
167 
168     while (mNumFinished < 3) {
169         mLoop->processEvents();
170     }
171 
172     QCOMPARE(mNumFinished, 3U);
173 
174     PendingReady *another = mFactory->proxy(mConnName1, mConnPath1,
175             ChannelFactory::create(QDBusConnection::sessionBus()),
176             ContactFactory::create());
177 
178     QVERIFY(another != NULL);
179     QVERIFY(!another->proxy().isNull());
180 
181     // Should still be the same even if all the initial requests already finished
182     QCOMPARE(another->proxy().data(), firstProxy.data());
183 
184     QVERIFY(connect(another, SIGNAL(finished(Tp::PendingOperation*)),
185                 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
186     QCOMPARE(mLoop->exec(), 0);
187 }
188 
testDropRefs()189 void TestDBusProxyFactory::testDropRefs()
190 {
191     PendingReady *first = mFactory->proxy(mConnName1, mConnPath1,
192             ChannelFactory::create(QDBusConnection::sessionBus()),
193             ContactFactory::create());
194 
195     QVERIFY(first != NULL);
196     QVERIFY(!first->proxy().isNull());
197 
198     ConnectionPtr firstProxy = ConnectionPtr::qObjectCast(first->proxy());
199     QVERIFY(firstProxy->isValid());
200 
201     QVERIFY(connect(first, SIGNAL(finished(Tp::PendingOperation*)),
202                 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
203     QCOMPARE(mLoop->exec(), 0);
204 
205     PendingReady *same = mFactory->proxy(mConnName1, mConnPath1,
206             ChannelFactory::create(QDBusConnection::sessionBus()),
207             ContactFactory::create());
208 
209     QVERIFY(same != NULL);
210     QVERIFY(!same->proxy().isNull());
211 
212     // The first one is in scope so we should've got it again
213     QCOMPARE(same->proxy().data(), firstProxy.data());
214 
215     QVERIFY(connect(same, SIGNAL(finished(Tp::PendingOperation*)),
216                 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
217     QCOMPARE(mLoop->exec(), 0);
218 
219     // Flush the delete event for the PendingReady, which drops the PendingReady ref to the proxy
220     mLoop->processEvents();
221 
222     // Make the Conn go out of scope
223     Connection *firstPtr = firstProxy.data();
224     firstProxy.reset();
225 
226     // Hopefully this prevents getting the new proxy instantiated on the very same address
227     QScopedPointer<char, QScopedPointerArrayDeleter<char> > hole(new char[sizeof(Connection)]);
228 
229     PendingReady *different = mFactory->proxy(mConnName1, mConnPath1,
230             ChannelFactory::create(QDBusConnection::sessionBus()),
231             ContactFactory::create());
232 
233     QVERIFY(different != NULL);
234     QVERIFY(!different->proxy().isNull());
235 
236     // The first one has gone out of scope and deleted so we should've got a different one
237     QVERIFY(different->proxy().data() != firstPtr);
238 }
239 
testInvalidate()240 void TestDBusProxyFactory::testInvalidate()
241 {
242     PendingReady *first = mFactory->proxy(mConnName1, mConnPath1,
243             ChannelFactory::create(QDBusConnection::sessionBus()),
244             ContactFactory::create());
245 
246     QVERIFY(first != NULL);
247     QVERIFY(!first->proxy().isNull());
248 
249     ConnectionPtr firstProxy = ConnectionPtr::qObjectCast(first->proxy());
250     QVERIFY(firstProxy->isValid());
251 
252     QVERIFY(connect(first, SIGNAL(finished(Tp::PendingOperation*)),
253                 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
254     QCOMPARE(mLoop->exec(), 0);
255 
256     PendingReady *same = mFactory->proxy(mConnName1, mConnPath1,
257             ChannelFactory::create(QDBusConnection::sessionBus()),
258             ContactFactory::create());
259 
260     QVERIFY(same != NULL);
261     QVERIFY(!same->proxy().isNull());
262 
263     // The first one is in scope and valid so we should've got it again
264     QCOMPARE(same->proxy().data(), firstProxy.data());
265 
266     QVERIFY(connect(same, SIGNAL(finished(Tp::PendingOperation*)),
267                 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
268     QCOMPARE(mLoop->exec(), 0);
269 
270     // Flush the delete event for the PendingReady, which drops the PendingReady ref to the proxy
271     mLoop->processEvents();
272 
273     // Synthesize an invalidation for the proxy
274     QVERIFY(connect(firstProxy.data(), SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
275                 mLoop, SLOT(quit())));
276     TestBackdoors::invalidateProxy(firstProxy.data(),
277             QLatin1String("im.bonghits.Errors.Synthetic"), QLatin1String(""));
278     QCOMPARE(mLoop->exec(), 0);
279 
280     QVERIFY(!firstProxy->isValid());
281 
282     PendingReady *different = mFactory->proxy(mConnName1, mConnPath1,
283             ChannelFactory::create(QDBusConnection::sessionBus()),
284             ContactFactory::create());
285 
286     QVERIFY(different != NULL);
287     ConnectionPtr differentProxy = ConnectionPtr::qObjectCast(different->proxy());
288     QVERIFY(!differentProxy.isNull());
289 
290     // The first one is invalid so we should've got a different one
291     QVERIFY(differentProxy.data() != firstProxy.data());
292     QVERIFY(differentProxy->isValid());
293 
294     QVERIFY(!differentProxy->isReady());
295 
296     QVERIFY(connect(different,
297                 SIGNAL(finished(Tp::PendingOperation*)),
298                 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
299     QCOMPARE(mLoop->exec(), 0);
300 
301     QVERIFY(differentProxy->isValid());
302     QVERIFY(differentProxy->isReady());
303 }
304 
testBogusService()305 void TestDBusProxyFactory::testBogusService()
306 {
307     PendingReady *bogus = mFactory->proxy(QLatin1String("org.bogus.Totally"),
308             QLatin1String("/org/bogus/Totally"),
309             ChannelFactory::create(QDBusConnection::sessionBus()),
310             ContactFactory::create());
311 
312     QVERIFY(bogus != NULL);
313     QVERIFY(!bogus->proxy().isNull());
314 
315     QVERIFY(!ConnectionPtr::qObjectCast(bogus->proxy())->isValid());
316 
317     PendingReady *another = mFactory->proxy(QLatin1String("org.bogus.Totally"),
318             QLatin1String("/org/bogus/Totally"),
319             ChannelFactory::create(QDBusConnection::sessionBus()),
320             ContactFactory::create());
321 
322     QVERIFY(another != NULL);
323     QVERIFY(!another->proxy().isNull());
324 
325     QVERIFY(!ConnectionPtr::qObjectCast(another->proxy())->isValid());
326 
327     // We shouldn't get the same proxy twice ie. a proxy should not be cached if not present on bus
328     // and invalidated because of that, otherwise we'll return an invalid instance from the cache
329     // even if after the service appears on the bus
330     QVERIFY(another->proxy() != bogus->proxy());
331 
332     // The PendingReady itself should finish with failure
333     QVERIFY(connect(another, SIGNAL(finished(Tp::PendingOperation*)),
334                 SLOT(expectFailure(Tp::PendingOperation*))));
335     QCOMPARE(mLoop->exec(), 0);
336 }
337 
cleanup()338 void TestDBusProxyFactory::cleanup()
339 {
340     mFactory.reset();
341 
342     cleanupImpl();
343 }
344 
cleanupTestCase()345 void TestDBusProxyFactory::cleanupTestCase()
346 {
347     cleanupTestCaseImpl();
348 }
349 
350 QTEST_MAIN(TestDBusProxyFactory)
351 #include "_gen/dbus-proxy-factory.cpp.moc.hpp"
352