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