1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Copyright (C) 2016 Intel Corporation.
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the test suite of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:GPL-EXCEPT$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU
20** General Public License version 3 as published by the Free Software
21** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29#include <QtCore/QObject>
30#include <QtCore/QVariant>
31#include <QtCore/QList>
32#include <QtCore/QThread>
33#include <QtCore/QVector>
34#include <QtTest/QtTest>
35#include <QtDBus>
36
37#define TEST_INTERFACE_NAME "org.qtproject.QtDBus.MyObject"
38
39class MyObject : public QDBusAbstractAdaptor
40{
41 Q_OBJECT
42 Q_CLASSINFO("D-Bus Interface", "org.qtproject.QtDBus.MyObject")
43
44public:
45 MyObject(QObject* parent =0)
46 : QDBusAbstractAdaptor(parent)
47 {}
48
49public slots:
50 QStringList returnFoo() const
51 { return QStringList() << QString::fromLatin1(str: "foo"); }
52
53 void returnError(const QDBusMessage &msg) const
54 {
55 msg.setDelayedReply(true);
56 QDBusConnection::sessionBus().send(message: msg.createErrorReply(name: "dbuspendingcall_error", msg: ""));
57 }
58};
59
60class tst_QDBusPendingCall: public QObject
61{
62 Q_OBJECT
63
64public:
65 tst_QDBusPendingCall();
66
67public Q_SLOTS:
68 void callback(const QStringList &list);
69 void errorCallback(const QDBusError &error);
70 void finished(QDBusPendingCallWatcher *call);
71 void makeCall();
72
73private Q_SLOTS:
74 void initTestCase();
75 void waitForFinished();
76 void waitForFinished_error();
77 void watcher();
78 void watcher_error();
79 void watcher_waitForFinished();
80 void watcher_waitForFinished_threaded();
81 void watcher_waitForFinished_alreadyFinished();
82 void watcher_waitForFinished_alreadyFinished_eventLoop();
83 void watcher_waitForFinished_error();
84 void callInsideWaitForFinished();
85
86 void callWithCallback_localLoop();
87 void callWithCallback_localLoop_errorReply();
88
89private:
90 QDBusPendingCall sendMessage();
91 QDBusPendingCall sendError();
92
93 QDBusConnection conn;
94
95 enum { CallbackCalled, ErrorCallbackCalled, FinishCalled, MakeCallCalled };
96 int slotCalled;
97 int callCount;
98 QStringList callbackArgument;
99 QDBusError errorArgument;
100 QDBusPendingCallWatcher *watchArgument;
101 MyObject *obj;
102};
103
104tst_QDBusPendingCall::tst_QDBusPendingCall()
105 : conn(QDBusConnection::sessionBus())
106 , obj(new MyObject(this))
107{
108}
109
110void tst_QDBusPendingCall::finished(QDBusPendingCallWatcher *call)
111{
112 slotCalled = FinishCalled;
113 ++callCount;
114 watchArgument = call;
115 if (QThread::currentThread() == thread())
116 QTestEventLoop::instance().exitLoop();
117}
118
119void tst_QDBusPendingCall::callback(const QStringList &list)
120{
121 slotCalled = CallbackCalled;
122 ++callCount;
123 callbackArgument = list;
124 QTestEventLoop::instance().exitLoop();
125}
126
127void tst_QDBusPendingCall::errorCallback(const QDBusError &error)
128{
129 slotCalled = ErrorCallbackCalled;
130 ++callCount;
131 errorArgument = error;
132 QTestEventLoop::instance().exitLoop();
133}
134
135void tst_QDBusPendingCall::makeCall()
136{
137 // make an external call to D-Bus to make sure we haven't left any locks
138 slotCalled = MakeCallCalled;
139 ++callCount;
140
141 sendMessage().waitForFinished();
142}
143
144void tst_QDBusPendingCall::initTestCase()
145{
146 QVERIFY(conn.isConnected());
147 QVERIFY(conn.registerObject("/", this));
148}
149
150QDBusPendingCall tst_QDBusPendingCall::sendMessage()
151{
152 QDBusMessage msg = QDBusMessage::createMethodCall(destination: "org.freedesktop.DBus",
153 path: "/",
154 interface: "org.freedesktop.DBus",
155 method: "ListNames");
156 return conn.asyncCall(message: msg);
157}
158
159QDBusPendingCall tst_QDBusPendingCall::sendError()
160{
161 QDBusMessage msg = QDBusMessage::createMethodCall(destination: "org.freedesktop.DBus",
162 path: "/",
163 interface: "org.freedesktop.DBus",
164 method: "ThisNameWontExist");
165 return conn.asyncCall(message: msg);
166}
167
168void tst_QDBusPendingCall::waitForFinished()
169{
170 QDBusPendingCall ac = sendMessage();
171 ac.waitForFinished();
172 QVERIFY(ac.isFinished());
173 QVERIFY(!ac.isError());
174
175 const QDBusMessage reply = ac.reply();
176 QCOMPARE(reply.type(), QDBusMessage::ReplyMessage);
177 QCOMPARE(reply.signature(), QString("as"));
178
179 const QVariantList args = ac.reply().arguments();
180 QCOMPARE(args.count(), 1);
181
182 const QVariant &arg = args.at(i: 0);
183 QCOMPARE(arg.type(), QVariant::StringList);
184 QVERIFY(arg.toStringList().contains(conn.baseService()));
185}
186
187void tst_QDBusPendingCall::waitForFinished_error()
188{
189 QDBusPendingCall ac = sendError();
190 ac.waitForFinished();
191 QVERIFY(ac.isFinished());
192 QVERIFY(ac.isError());
193
194 QDBusError error = ac.error();
195 QVERIFY(error.isValid());
196 QCOMPARE(error.name(), QString("org.freedesktop.DBus.Error.UnknownMethod"));
197 QCOMPARE(error.type(), QDBusError::UnknownMethod);
198}
199
200void tst_QDBusPendingCall::callWithCallback_localLoop()
201{
202 // Verify that a callback actually gets called when the call is dispatched locally.
203 QDBusInterface iface(QDBusConnection::sessionBus().baseService(), QLatin1String("/"),
204 TEST_INTERFACE_NAME);
205 QVERIFY(iface.isValid());
206
207 QVERIFY(iface.callWithCallback("returnFoo", QVariantList(), this, SLOT(callback(QStringList))));
208
209 // May be called synchronously or asynchronously...
210 if (callbackArgument != (QStringList() << QString::fromLatin1(str: "foo"))) {
211 QTestEventLoop::instance().enterLoop(secs: 2);
212 QVERIFY(!QTestEventLoop::instance().timeout());
213 }
214
215 QCOMPARE(callbackArgument, QStringList() << QString::fromLatin1("foo"));
216}
217
218void tst_QDBusPendingCall::callWithCallback_localLoop_errorReply()
219{
220 // Verify that an error callback actually gets called when the call is
221 // dispatched locally and the called method returns an error
222
223 QDBusInterface iface(QDBusConnection::sessionBus().baseService(), QLatin1String("/"),
224 TEST_INTERFACE_NAME);
225 QVERIFY(iface.isValid());
226
227 callbackArgument.clear();
228
229 QVERIFY(iface.callWithCallback("returnError", QVariantList(), this,
230 SLOT(callback(QStringList)), SLOT(errorCallback(QDBusError))));
231
232 // May be called synchronously or asynchronously...
233 if (errorArgument.name() != "dbuspendingcall_error") {
234 QTestEventLoop::instance().enterLoop(secs: 2);
235 QVERIFY(!QTestEventLoop::instance().timeout());
236 }
237
238 QCOMPARE(errorArgument.name(), QString::fromLatin1("dbuspendingcall_error"));
239 QVERIFY(callbackArgument.isEmpty());
240}
241
242void tst_QDBusPendingCall::watcher()
243{
244 QDBusPendingCall ac = sendMessage();
245 callCount = 0;
246 watchArgument = 0;
247
248 QDBusPendingCallWatcher watch(ac);
249 connect(asender: &watch, SIGNAL(finished(QDBusPendingCallWatcher*)),
250 SLOT(finished(QDBusPendingCallWatcher*)));
251
252 QTestEventLoop::instance().enterLoop(secs: 2);
253 QVERIFY(!QTestEventLoop::instance().timeout());
254
255 QVERIFY(ac.isFinished());
256 QVERIFY(!ac.isError());
257
258 QCOMPARE(callCount, 1);
259 QCOMPARE(slotCalled, (int)FinishCalled);
260 QCOMPARE(watchArgument, &watch);
261 QVERIFY(!watch.isError());
262
263 const QVariantList args2 = ac.reply().arguments();
264 QVERIFY(!args2.isEmpty());
265 QVERIFY(args2.at(0).toStringList().contains(conn.baseService()));
266}
267
268void tst_QDBusPendingCall::watcher_error()
269{
270 QDBusPendingCall ac = sendError();
271 callCount = 0;
272 watchArgument = 0;
273
274 QDBusPendingCallWatcher watch(ac);
275 connect(asender: &watch, SIGNAL(finished(QDBusPendingCallWatcher*)),
276 SLOT(finished(QDBusPendingCallWatcher*)));
277
278 QTestEventLoop::instance().enterLoop(secs: 2);
279 QVERIFY(!QTestEventLoop::instance().timeout());
280
281 QVERIFY(ac.isFinished());
282 QVERIFY(ac.isError());
283
284 QCOMPARE(callCount, 1);
285 QCOMPARE(slotCalled, (int)FinishCalled);
286 QCOMPARE(watchArgument, &watch);
287
288 QVERIFY(watch.isError());
289 QVERIFY(watch.error().isValid());
290}
291
292void tst_QDBusPendingCall::watcher_waitForFinished()
293{
294 QDBusPendingCall ac = sendMessage();
295 callCount = 0;
296 watchArgument = 0;
297
298 QDBusPendingCallWatcher watch(ac);
299 connect(asender: &watch, SIGNAL(finished(QDBusPendingCallWatcher*)),
300 SLOT(finished(QDBusPendingCallWatcher*)));
301
302 watch.waitForFinished();
303
304 QVERIFY(ac.isFinished());
305 QVERIFY(!ac.isError());
306
307 QCOMPARE(callCount, 1);
308 QCOMPARE(slotCalled, (int)FinishCalled);
309 QCOMPARE(watchArgument, &watch);
310 QVERIFY(!watch.isError());
311
312 const QVariantList args2 = ac.reply().arguments();
313 QVERIFY(!args2.isEmpty());
314 QVERIFY(args2.at(0).toStringList().contains(conn.baseService()));
315}
316
317void tst_QDBusPendingCall::watcher_waitForFinished_threaded()
318{
319 callCount = 0;
320 watchArgument = 0;
321 slotCalled = 0;
322
323 class WorkerThread: public QThread {
324 public:
325 tst_QDBusPendingCall *tst;
326 WorkerThread(tst_QDBusPendingCall *tst) : tst(tst) {}
327 void run()
328 {
329 QDBusPendingCall ac = tst->sendMessage();
330// QVERIFY(!ac.isFinished());
331// QVERIFY(!ac.isError());
332// QCOMPARE(ac.reply().type(), QDBusMessage::InvalidMessage);
333
334 QDBusPendingCallWatcher watch(ac);
335 tst->connect(asender: &watch, SIGNAL(finished(QDBusPendingCallWatcher*)),
336 SLOT(finished(QDBusPendingCallWatcher*)), atype: Qt::DirectConnection);
337
338 QTest::qSleep(ms: 100); // don't process events in this thread
339
340// QVERIFY(!ac.isFinished());
341// QVERIFY(!ac.isError());
342// QCOMPARE(ac.reply().type(), QDBusMessage::InvalidMessage);
343 QCOMPARE(tst->callCount, 0);
344 QCOMPARE(tst->slotCalled, 0);
345
346 watch.waitForFinished();
347 QVERIFY(ac.isFinished());
348 QVERIFY(!ac.isError());
349
350 QCOMPARE(tst->callCount, 1);
351 QCOMPARE(tst->slotCalled, (int)FinishCalled);
352 QCOMPARE(tst->watchArgument, &watch);
353 QVERIFY(!watch.isError());
354
355 const QVariantList args2 = ac.reply().arguments();
356 QVERIFY(!args2.isEmpty());
357 QVERIFY(args2.at(0).toStringList().contains(tst->conn.baseService()));
358 }
359 } thread(this);
360 QTestEventLoop::instance().connect(asender: &thread, SIGNAL(finished()), SLOT(exitLoop()));
361 thread.start();
362 QTestEventLoop::instance().enterLoop(secs: 10);
363 QVERIFY(thread.wait(3000));
364 QVERIFY(!QTestEventLoop::instance().timeout());
365}
366
367void tst_QDBusPendingCall::watcher_waitForFinished_alreadyFinished()
368{
369 QDBusPendingCall ac = sendMessage();
370 ac.waitForFinished();
371 QVERIFY(ac.isFinished());
372 QVERIFY(!ac.isError());
373
374 callCount = 0;
375 watchArgument = 0;
376
377 // create a watcher on an already-finished reply
378 QDBusPendingCallWatcher watch(ac);
379 connect(asender: &watch, SIGNAL(finished(QDBusPendingCallWatcher*)),
380 SLOT(finished(QDBusPendingCallWatcher*)));
381
382 watch.waitForFinished();
383
384 QVERIFY(ac.isFinished());
385 QVERIFY(!ac.isError());
386
387 QCOMPARE(callCount, 1);
388 QCOMPARE(slotCalled, (int)FinishCalled);
389 QCOMPARE(watchArgument, &watch);
390 QVERIFY(!watch.isError());
391
392 const QVariantList args2 = ac.reply().arguments();
393 QVERIFY(!args2.isEmpty());
394 QVERIFY(args2.at(0).toStringList().contains(conn.baseService()));
395}
396
397void tst_QDBusPendingCall::watcher_waitForFinished_alreadyFinished_eventLoop()
398{
399 QDBusPendingCall ac = sendMessage();
400 ac.waitForFinished();
401 QVERIFY(ac.isFinished());
402 QVERIFY(!ac.isError());
403
404 callCount = 0;
405 watchArgument = 0;
406
407 // create a watcher on an already-finished reply
408 QDBusPendingCallWatcher watch(ac);
409 connect(asender: &watch, SIGNAL(finished(QDBusPendingCallWatcher*)),
410 SLOT(finished(QDBusPendingCallWatcher*)));
411 connect(sender: &watch, SIGNAL(finished(QDBusPendingCallWatcher*)),
412 receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
413
414 QTestEventLoop::instance().enterLoop(secs: 1);
415 QVERIFY(!QTestEventLoop::instance().timeout());
416
417 QVERIFY(ac.isFinished());
418 QVERIFY(!ac.isError());
419
420 QCOMPARE(callCount, 1);
421 QCOMPARE(slotCalled, (int)FinishCalled);
422 QCOMPARE(watchArgument, &watch);
423 QVERIFY(!watch.isError());
424
425 const QVariantList args2 = ac.reply().arguments();
426 QVERIFY(!args2.isEmpty());
427 QVERIFY(args2.at(0).toStringList().contains(conn.baseService()));
428}
429
430void tst_QDBusPendingCall::watcher_waitForFinished_error()
431{
432 QDBusPendingCall ac = sendError();
433 callCount = 0;
434 watchArgument = 0;
435
436 QDBusPendingCallWatcher watch(ac);
437 connect(asender: &watch, SIGNAL(finished(QDBusPendingCallWatcher*)),
438 SLOT(finished(QDBusPendingCallWatcher*)));
439
440 watch.waitForFinished();
441
442 QVERIFY(ac.isFinished());
443 QVERIFY(ac.isError());
444
445 QCOMPARE(callCount, 1);
446 QCOMPARE(slotCalled, (int)FinishCalled);
447 QCOMPARE(watchArgument, &watch);
448
449 QVERIFY(watch.isError());
450 QVERIFY(watch.error().isValid());
451}
452
453void tst_QDBusPendingCall::callInsideWaitForFinished()
454{
455 QDBusPendingCall ac = sendMessage();
456 QDBusPendingCallWatcher watch(ac);
457
458 callCount = 0;
459
460 connect(asender: &watch, SIGNAL(finished(QDBusPendingCallWatcher*)),
461 SLOT(makeCall()));
462
463 watch.waitForFinished();
464
465 QCOMPARE(callCount, 1);
466 QCOMPARE(slotCalled, (int)MakeCallCalled);
467 QVERIFY(!watch.isError());
468
469 const QVariantList args2 = ac.reply().arguments();
470 QVERIFY(!args2.isEmpty());
471 QVERIFY(args2.at(0).toStringList().contains(conn.baseService()));
472}
473
474QTEST_MAIN(tst_QDBusPendingCall)
475#include "tst_qdbuspendingcall.moc"
476

source code of qtbase/tests/auto/dbus/qdbuspendingcall/tst_qdbuspendingcall.cpp