1 | |
2 | /**************************************************************************** |
3 | ** |
4 | ** Copyright (C) 2015 The Qt Company Ltd and/or its subsidiary(-ies). |
5 | ** Contact: http://www.qt-project.org/legal |
6 | ** |
7 | ** This file is part of the QtSystems module of the Qt Toolkit. |
8 | ** |
9 | ** $QT_BEGIN_LICENSE:LGPL21$ |
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 http://www.qt.io/terms-conditions. For further |
16 | ** information use the contact form at http://www.qt.io/contact-us. |
17 | ** |
18 | ** GNU Lesser General Public License Usage |
19 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
20 | ** General Public License version 2.1 or version 3 as published by the Free |
21 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and |
22 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the |
23 | ** following information to ensure the GNU Lesser General Public License |
24 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and |
25 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
26 | ** |
27 | ** As a special exception, The Qt Company gives you certain additional |
28 | ** rights. These rights are described in The Qt Company LGPL Exception |
29 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
30 | ** |
31 | ** $QT_END_LICENSE$ |
32 | ** |
33 | ****************************************************************************/ |
34 | |
35 | //TESTED_COMPONENT=src/serviceframework |
36 | |
37 | #include <QCoreApplication> |
38 | #include "qservicemanager.h" |
39 | #include "qservicefilter.h" |
40 | #include <QTimer> |
41 | #include <QMetaObject> |
42 | #include <QMetaMethod> |
43 | #include <QThread> |
44 | #include <QtTest/QtTest> |
45 | #include <qservice.h> |
46 | #include <qremoteserviceregister.h> |
47 | #include <qservicereply.h> |
48 | |
49 | #include <signal.h> |
50 | |
51 | #ifdef SFW_USE_DBUS_BACKEND |
52 | #include <QtDBus/QtDBus> |
53 | #if QT_VERSION < 0x040800 |
54 | inline QDBusArgument &operator<<(QDBusArgument &arg, const QVariantHash &map) |
55 | { |
56 | arg.beginMap(QVariant::String, qMetaTypeId<QDBusVariant>()); |
57 | QVariantHash::ConstIterator it = map.constBegin(); |
58 | QVariantHash::ConstIterator end = map.constEnd(); |
59 | for ( ; it != end; ++it) { |
60 | arg.beginMapEntry(); |
61 | arg << it.key() << QDBusVariant(it.value()); |
62 | arg.endMapEntry(); |
63 | } |
64 | arg.endMap(); |
65 | return arg; |
66 | } |
67 | #endif |
68 | #endif |
69 | |
70 | |
71 | #ifdef Q_OS_MAC |
72 | #include <stdlib.h> |
73 | #include <sys/resource.h> |
74 | #endif |
75 | |
76 | QT_USE_NAMESPACE |
77 | Q_DECLARE_METATYPE(QServiceFilter); |
78 | Q_DECLARE_METATYPE(QVariant); |
79 | Q_DECLARE_METATYPE(QList<QString>); |
80 | Q_DECLARE_METATYPE(QVariantHash); |
81 | |
82 | char serviceReuqestXml[] = |
83 | "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" |
84 | "<SFW version=\"1.1\">" |
85 | "<service>" |
86 | "<name>com.nokia.mt.processmanager</name>" |
87 | "<ipcaddress>com.nokia.mt.processmanager.ServiceRequestSocket</ipcaddress>" |
88 | "<interface>" |
89 | "<name>com.nokia.mt.processmanager.ProcessManagerController</name>" |
90 | "<version>1.0</version>" |
91 | "</interface>" |
92 | "</service>" |
93 | "</SFW>" ; |
94 | |
95 | char serviceReuqestXmlFake[] = |
96 | "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" |
97 | "<SFW version=\"1.1\">" |
98 | "<service>" |
99 | "<name>com.nokia.qt.does.not.exist</name>" |
100 | "<ipcaddress>com.nokia.qt.does.not.exist</ipcaddress>" |
101 | "<interface>" |
102 | "<name>com.nokia.qt.interface.does.not.exist</name>" |
103 | "<version>1.0</version>" |
104 | "</interface>" |
105 | "</service>" |
106 | "</SFW>" ; |
107 | |
108 | char serviceReuqestXmlStarted[] = |
109 | "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" |
110 | "<SFW version=\"1.1\">" |
111 | "<service>" |
112 | "<name>com.nokia.qt.tests.autostart</name>" |
113 | "<ipcaddress>com.nokia.qt.tests.autostart</ipcaddress>" |
114 | "<interface>" |
115 | "<name>com.nokkia.qt.tests.autostarted</name>" |
116 | "<version>1.0</version>" |
117 | "</interface>" |
118 | "</service>" |
119 | "</SFW>" ; |
120 | |
121 | |
122 | class ServiceRequest : public QObject |
123 | { |
124 | Q_OBJECT |
125 | public: |
126 | ServiceRequest(QObject *o = 0) : QObject(o) |
127 | { |
128 | } |
129 | |
130 | Q_INVOKABLE void startService(const QString location) |
131 | { |
132 | qDebug() << "Got startService" ; |
133 | if (location == "com.nokia.qt.tests.autostart" ) { |
134 | QMetaObject::invokeMethod(obj: this, member: "sendOk" , type: Qt::DirectConnection, Q_ARG(QString, location)); |
135 | } else { |
136 | QMetaObject::invokeMethod(obj: this, member: "sendError" , type: Qt::DirectConnection, Q_ARG(QString, location)); |
137 | } |
138 | } |
139 | |
140 | protected slots: |
141 | void sendError(const QString &location) |
142 | { |
143 | qDebug() << "Send fail" ; |
144 | emit failed(QStringLiteral("Test code failing creation" ), process: location); |
145 | } |
146 | |
147 | void sendOk(const QString &location) |
148 | { |
149 | emit launched(process: location); |
150 | QMetaObject::invokeMethod(obj: this, member: "startNewService" , type: Qt::DirectConnection); |
151 | } |
152 | |
153 | void startNewService() |
154 | { |
155 | QRemoteServiceRegister *r = new QRemoteServiceRegister(this); |
156 | |
157 | QRemoteServiceRegister::Entry pmEntry = |
158 | r->createEntry<ServiceRequest>( |
159 | QStringLiteral("com.nokia.qt.tests.autostart" ), |
160 | QStringLiteral("com.nokkia.qt.tests.autostarted" ), |
161 | QStringLiteral("1.0" )); |
162 | |
163 | r->publishEntries(QStringLiteral("com.nokia.qt.tests.autostart" )); |
164 | } |
165 | |
166 | signals: |
167 | |
168 | void launched(QString process); |
169 | void failed(QString error, QString process); |
170 | |
171 | }; |
172 | |
173 | class tst_QServiceManager_IPC: public QObject |
174 | { |
175 | Q_OBJECT |
176 | public: |
177 | tst_QServiceManager_IPC(); |
178 | |
179 | protected slots: |
180 | void ipcError(QService::UnrecoverableIPCError error); |
181 | void ipcErrorNonTest(QService::UnrecoverableIPCError error); |
182 | void unblockRemote(); |
183 | |
184 | private slots: |
185 | void initTestCase(); |
186 | void cleanupTestCase(); |
187 | void init(); |
188 | void cleanup(); |
189 | |
190 | void verifyIsServiceRunning(); |
191 | |
192 | void verifySharedServiceObject(); //rough count |
193 | void verifySharedMethods(); |
194 | void verifySharedMethods_data(); |
195 | void verifySharedProperties(); |
196 | void verifySharedProperties_data(); |
197 | |
198 | void verifyUniqueServiceObject(); //rough count |
199 | void verifyUniqueMethods(); |
200 | void verifyUniqueMethods_data(); |
201 | |
202 | void verifyUniqueProperties(); |
203 | void verifyUniqueProperties_data(); |
204 | |
205 | void verifyUniqueClassInfo(); |
206 | void verifyUniqueClassInfo_data(); |
207 | |
208 | void verifyUniqueEnumerator(); |
209 | void verifyUniqueEnumerator_data(); |
210 | |
211 | void verifyLargeDataTransfer(); |
212 | void verifyLargeDataTransfer_data(); |
213 | |
214 | void verifyRemoteBlockingFunctions(); |
215 | |
216 | void verifyThreadSafety(); |
217 | void verifyThreadSafety_data(); |
218 | |
219 | void sharedTestService(); |
220 | void uniqueTestService(); |
221 | |
222 | void testInvokableFunctions(); |
223 | void testSlotInvokation(); |
224 | void testSignalling(); |
225 | |
226 | void testSignalSlotOrdering(); |
227 | |
228 | void verifyServiceClass(); |
229 | void verifyFailures(); |
230 | |
231 | void verifyAsyncLoading(); |
232 | void verifyAsyncLoading_data(); |
233 | |
234 | void testServiceSecurity(); |
235 | |
236 | void testProcessLaunch(); |
237 | |
238 | void testIpcFailure(); |
239 | |
240 | signals: |
241 | void threadReady(); |
242 | void threadRun(); |
243 | void threadFinished(); |
244 | void threadError(); |
245 | void threadRemaining(); |
246 | |
247 | private: |
248 | QObject* serviceUnique; |
249 | QObject* serviceUniqueOther; |
250 | QObject* serviceShared; |
251 | QObject* serviceSharedOther; |
252 | QObject* miscTest; |
253 | QServiceManager* manager; |
254 | bool verbose; |
255 | bool ipcfailure; |
256 | }; |
257 | |
258 | tst_QServiceManager_IPC::tst_QServiceManager_IPC() |
259 | : serviceUnique(0) |
260 | , serviceUniqueOther(0) |
261 | , serviceShared(0) |
262 | , serviceSharedOther(0) |
263 | , miscTest(0) |
264 | , manager(0) |
265 | , verbose(false) |
266 | , ipcfailure(false) |
267 | { |
268 | } |
269 | |
270 | #ifdef Q_OS_WIN |
271 | static const char serviceBinaryC[] = "qt_sfw_example_ipc_unittest.exe" ; |
272 | #else |
273 | static const char serviceBinaryC[] = "qt_sfw_example_ipc_unittest" ; |
274 | #endif |
275 | |
276 | void tst_QServiceManager_IPC::initTestCase() |
277 | { |
278 | const QString serviceBinary = QFINDTESTDATA(serviceBinaryC); |
279 | QVERIFY(!serviceBinary.isEmpty()); |
280 | const QFileInfo serviceBinaryInfo(serviceBinary); |
281 | QVERIFY(serviceBinaryInfo.isExecutable()); |
282 | const QString serviceBinaryAbsPath = serviceBinaryInfo.absoluteFilePath(); |
283 | |
284 | const QString path = QFINDTESTDATA("xmldata/ipcexampleservice.xml" ); |
285 | QVERIFY(!path.isEmpty()); |
286 | |
287 | //verbose = true; |
288 | ipcfailure = false; |
289 | verbose = false; |
290 | serviceUnique = 0; |
291 | serviceUniqueOther = 0; |
292 | serviceSharedOther = 0; |
293 | serviceShared = 0; |
294 | serviceSharedOther = 0; |
295 | miscTest = 0; |
296 | qRegisterMetaType<QServiceFilter>(typeName: "QServiceFilter" ); |
297 | qRegisterMetaTypeStreamOperators<QServiceFilter>(typeName: "QServiceFilter" ); |
298 | qRegisterMetaType<QList<QString> >(typeName: "QList<QString>" ); |
299 | qRegisterMetaTypeStreamOperators<QList<QString> >(typeName: "QList<QString>" ); |
300 | qRegisterMetaTypeStreamOperators<QVariantHash>(typeName: "QVariantHash" ); |
301 | qRegisterMetaType<QVariantHash>(typeName: "QVariantHash" ); |
302 | #ifdef SFW_USE_DBUS_BACKEND |
303 | qDBusRegisterMetaType<QVariantHash>(); |
304 | #endif |
305 | |
306 | #ifdef Q_OS_MAC |
307 | struct rlimit rlp; |
308 | int ret = getrlimit(RLIMIT_NOFILE, &rlp); |
309 | if (ret == -1) { |
310 | qWarning() << "Failed to get fd limit on mac, expect the threading test to fail" ; |
311 | } else { |
312 | rlp.rlim_cur = 512; |
313 | ret = setrlimit(RLIMIT_NOFILE, &rlp); |
314 | if (ret == -1) { |
315 | qWarning() << "Failed to raise the number of open files to 512, expect the threading test to fail" ; |
316 | } |
317 | } |
318 | #endif |
319 | |
320 | QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath()); |
321 | |
322 | QServiceManager* manager = new QServiceManager(this); |
323 | |
324 | const bool r = manager->addService(xmlFilePath: path); |
325 | if (!r) |
326 | qWarning() << "Cannot register IPCExampleService" << path; |
327 | |
328 | #ifdef SFW_USE_DBUS_BACKEND |
329 | // D-Bus auto registration |
330 | const QString &file = QDir::homePath() + "/.local/share/dbus-1/services/" + |
331 | "com.nokia.qt.ipcunittest.service" ; |
332 | |
333 | QDir dir(QDir::homePath()); |
334 | dir.mkpath(dirPath: ".local/share/dbus-1/services/" ); |
335 | QFile data(file); |
336 | if (data.open(flags: QFile::WriteOnly)) { |
337 | QTextStream out(&data); |
338 | out << "[D-BUS Service]\n" |
339 | << "Name=com.nokia.qtmobility.sfw.IPCExampleService" << '\n' |
340 | << "Exec=" << serviceBinaryAbsPath << '\n'; |
341 | data.close(); |
342 | } |
343 | QVERIFY(data.exists()); |
344 | #endif // SFW_USE_DBUS_BACKEND |
345 | |
346 | #ifdef Q_OS_UNIX |
347 | char *p = getenv(name: "PATH" ); |
348 | char newpath[strlen(s: p)+3]; |
349 | strcpy(dest: newpath, src: p); |
350 | strcat(dest: newpath, src: ":." ); |
351 | setenv(name: "PATH" , value: newpath, replace: 1); |
352 | #endif |
353 | |
354 | //test that the service is installed |
355 | QList<QServiceInterfaceDescriptor> list = manager->findInterfaces(serviceName: "IPCExampleService" ); |
356 | QVERIFY2(list.count() >= 7,"unit test specific IPCExampleService not registered/found" ); |
357 | QServiceInterfaceDescriptor d; |
358 | foreach (d, list) { |
359 | if (d.majorVersion() == 3 && d.minorVersion() == 5) { |
360 | serviceUnique = manager->loadInterface(descriptor: d); |
361 | QVERIFY(serviceUnique); |
362 | serviceUniqueOther = manager->loadInterface(descriptor: d); |
363 | QVERIFY(serviceUniqueOther); |
364 | connect(sender: serviceUnique, SIGNAL(errorUnrecoverableIPCFault(QService::UnrecoverableIPCError)), |
365 | receiver: this, SLOT(ipcErrorNonTest(QService::UnrecoverableIPCError))); |
366 | connect(sender: serviceUniqueOther, SIGNAL(errorUnrecoverableIPCFault(QService::UnrecoverableIPCError)), |
367 | receiver: this, SLOT(ipcErrorNonTest(QService::UnrecoverableIPCError))); |
368 | |
369 | } |
370 | if (d.majorVersion() == 3 && d.minorVersion() == 4) { |
371 | serviceShared = manager->loadInterface(descriptor: d); |
372 | QVERIFY(serviceShared); |
373 | serviceSharedOther = manager->loadInterface(descriptor: d); |
374 | connect(sender: serviceShared, SIGNAL(errorUnrecoverableIPCFault(QService::UnrecoverableIPCError)), |
375 | receiver: this, SLOT(ipcErrorNonTest(QService::UnrecoverableIPCError))); |
376 | QVERIFY(serviceSharedOther); |
377 | connect(sender: serviceSharedOther, SIGNAL(errorUnrecoverableIPCFault(QService::UnrecoverableIPCError)), |
378 | receiver: this, SLOT(ipcErrorNonTest(QService::UnrecoverableIPCError))); |
379 | |
380 | } |
381 | if (d.majorVersion() == 3 && d.minorVersion() == 8) { |
382 | miscTest = manager->loadInterface(descriptor: d); |
383 | QVERIFY(miscTest); |
384 | connect(sender: miscTest, SIGNAL(errorUnrecoverableIPCFault(QService::UnrecoverableIPCError)), |
385 | receiver: this, SLOT(ipcErrorNonTest(QService::UnrecoverableIPCError))); |
386 | } |
387 | |
388 | } |
389 | |
390 | QString errorCode = "Cannot find service. Error: %1" ; |
391 | errorCode = errorCode.arg(a: manager->error()); |
392 | QVERIFY2(serviceUnique,errorCode.toLatin1()); |
393 | QVERIFY2(serviceUniqueOther,errorCode.toLatin1()); |
394 | QVERIFY2(serviceShared,errorCode.toLatin1()); |
395 | QVERIFY2(serviceSharedOther,errorCode.toLatin1()); |
396 | |
397 | // all objects come from the same service, just need to connect to 1 signal |
398 | connect(sender: serviceSharedOther, SIGNAL(errorUnrecoverableIPCFault(QService::UnrecoverableIPCError)), receiver: this, SLOT(ipcError(QService::UnrecoverableIPCError))); |
399 | |
400 | QRemoteServiceRegister *reg = new QRemoteServiceRegister(); |
401 | |
402 | QRemoteServiceRegister::Entry pmEntry = |
403 | reg->createEntry<ServiceRequest>( |
404 | QStringLiteral("com.nokia.mt.processmanager" ), |
405 | QStringLiteral("com.nokia.mt.processmanager.ServiceRequest" ), |
406 | QStringLiteral("1.0" )); |
407 | |
408 | reg->publishEntries(ident: "com.nokia.mt.processmanager.ServiceRequestSocket" ); |
409 | |
410 | QByteArray ba(serviceReuqestXml); |
411 | QBuffer b(&ba); |
412 | manager->removeService(QStringLiteral("com.nokia.mt.processmanager" )); |
413 | manager->addService(xmlDevice: &b); |
414 | |
415 | QByteArray baFake(serviceReuqestXmlFake); |
416 | QBuffer bFake(&baFake); |
417 | manager->removeService(QStringLiteral("com.nokia.qt.does.not.exists" )); |
418 | manager->addService(xmlDevice: &bFake); |
419 | |
420 | QByteArray baAutoStart(serviceReuqestXmlStarted); |
421 | QBuffer bAutoStart(&baAutoStart); |
422 | manager->removeService(QStringLiteral("com.nokia.qt.tests.autostart" )); |
423 | manager->addService(xmlDevice: &bAutoStart); |
424 | |
425 | } |
426 | |
427 | void tst_QServiceManager_IPC::ipcError(QService::UnrecoverableIPCError err) |
428 | { |
429 | Q_UNUSED(err); |
430 | ipcfailure = true; |
431 | } |
432 | |
433 | // unexpected ipc failures |
434 | void tst_QServiceManager_IPC::ipcErrorNonTest(QService::UnrecoverableIPCError err) |
435 | { |
436 | Q_UNUSED(err); |
437 | } |
438 | |
439 | void tst_QServiceManager_IPC::cleanupTestCase() |
440 | { |
441 | #ifdef SFW_USE_DBUS_BACKEND |
442 | const QString &file = QDir::homePath() + "/.local/share/dbus-1/services/" + |
443 | "com.nokia.qt.ipcunittest.service" ; |
444 | QFile::remove(fileName: file); |
445 | #endif |
446 | |
447 | if (serviceUnique) { |
448 | delete serviceUnique; |
449 | } |
450 | |
451 | if (serviceUniqueOther) { |
452 | delete serviceUniqueOther; |
453 | } |
454 | |
455 | if (serviceShared) { |
456 | delete serviceShared; |
457 | } |
458 | |
459 | if (serviceSharedOther) { |
460 | delete serviceSharedOther; |
461 | } |
462 | |
463 | // clean up the unit, don't leave it registered |
464 | QServiceManager m; |
465 | m.removeService(serviceName: "IPCExampleService" ); |
466 | m.removeService(QStringLiteral("com.nokia.mt.processmanager" )); |
467 | m.removeService(QStringLiteral("com.nokia.qt.does.not.exists" )); |
468 | m.removeService(QStringLiteral("com.nokia.qt.tests.autostart" )); |
469 | } |
470 | |
471 | void tst_QServiceManager_IPC::init() |
472 | { |
473 | } |
474 | |
475 | void tst_QServiceManager_IPC::cleanup() |
476 | { |
477 | } |
478 | |
479 | void tst_QServiceManager_IPC::verifyIsServiceRunning() |
480 | { |
481 | #if defined(Q_OS_UNIX) && !defined(SFW_USE_DBUS_BACKEND) |
482 | |
483 | QServiceManager m; |
484 | |
485 | QCOMPARE(m.isInterfaceRunning("com.nokia.qt.ipcunittest" ), true); |
486 | QCOMPARE(m.isInterfaceRunning("com.nokis.qt.ipcunittest.that.does.not.exsist.and.should.not.be.created" ), false); |
487 | |
488 | QList<QServiceInterfaceDescriptor> list = m.findInterfaces("IPCExampleService" ); |
489 | QServiceInterfaceDescriptor d; |
490 | foreach (d, list) { |
491 | QCOMPARE(m.isInterfaceRunning(d), true); |
492 | } |
493 | #else |
494 | QSKIP("isInterfaceRunning needs more testing on supported platforms" ); |
495 | #endif |
496 | } |
497 | |
498 | void tst_QServiceManager_IPC::sharedTestService() |
499 | { |
500 | const QMetaObject *meta = serviceShared->metaObject(); |
501 | const QMetaObject *metaOther = serviceSharedOther->metaObject(); |
502 | |
503 | // test changes on the property 'value' |
504 | QMetaProperty prop = meta->property(index: 1); |
505 | QMetaProperty propOther = metaOther->property(index: 1); |
506 | |
507 | // check that the property values are the same before and after an edit |
508 | QCOMPARE(prop.read(serviceShared), propOther.read(serviceSharedOther)); |
509 | prop.write(obj: serviceShared, value: "Shared" ); |
510 | // write does not block so the QCOMPARE below may result in serviceSharedOther being read before |
511 | // the new value has been sent to the service. To avoid this, read the property back now |
512 | (void)prop.read(obj: serviceShared); // flush |
513 | QCOMPARE(prop.read(serviceShared), propOther.read(serviceSharedOther)); |
514 | prop.reset(obj: serviceShared); |
515 | |
516 | // test changes through a method call |
517 | uint hash, hashOther; |
518 | QMetaObject::invokeMethod(obj: serviceShared, member: "setConfirmationHash" , |
519 | Q_ARG(uint, 0)); |
520 | QMetaObject::invokeMethod(obj: serviceShared, member: "slotConfirmation" , |
521 | Q_RETURN_ARG(uint, hash)); |
522 | QMetaObject::invokeMethod(obj: serviceSharedOther, member: "setConfirmationHash" , |
523 | Q_ARG(uint, 0)); |
524 | QMetaObject::invokeMethod(obj: serviceSharedOther, member: "slotConfirmation" , |
525 | Q_RETURN_ARG(uint, hashOther)); |
526 | QCOMPARE(hash, (uint)0); |
527 | QCOMPARE(hashOther, (uint)0); |
528 | |
529 | // check that the method values are the same after an edit |
530 | QMetaObject::invokeMethod(obj: serviceShared, member: "setConfirmationHash" , |
531 | Q_ARG(uint, 1)); |
532 | QMetaObject::invokeMethod(obj: serviceShared, member: "slotConfirmation" , |
533 | Q_RETURN_ARG(uint, hash)); |
534 | QMetaObject::invokeMethod(obj: serviceSharedOther, member: "slotConfirmation" , |
535 | Q_RETURN_ARG(uint, hashOther)); |
536 | QCOMPARE(hash, (uint)1); |
537 | QCOMPARE(hashOther, (uint)1); |
538 | } |
539 | |
540 | void tst_QServiceManager_IPC::uniqueTestService() |
541 | { |
542 | const QMetaObject *meta = serviceUnique->metaObject(); |
543 | const QMetaObject *metaOther = serviceUniqueOther->metaObject(); |
544 | |
545 | // test changes on the property 'value' |
546 | QMetaProperty prop = meta->property(index: 1); |
547 | QMetaProperty propOther = metaOther->property(index: 1); |
548 | |
549 | // check that the property values are not the same after an edit |
550 | QCOMPARE(prop.read(serviceUnique), propOther.read(serviceUniqueOther)); |
551 | prop.write(obj: serviceUnique, value: "Unique" ); |
552 | QVERIFY(prop.read(serviceUnique) != propOther.read(serviceUniqueOther)); |
553 | prop.reset(obj: serviceUnique); |
554 | |
555 | // test changes through a method call |
556 | uint hash, hashOther; |
557 | QMetaObject::invokeMethod(obj: serviceUnique, member: "setConfirmationHash" , |
558 | Q_ARG(uint, 0)); |
559 | QMetaObject::invokeMethod(obj: serviceUnique, member: "slotConfirmation" , |
560 | Q_RETURN_ARG(uint, hash)); |
561 | QMetaObject::invokeMethod(obj: serviceUniqueOther, member: "setConfirmationHash" , |
562 | Q_ARG(uint, 0)); |
563 | QMetaObject::invokeMethod(obj: serviceUniqueOther, member: "slotConfirmation" , |
564 | Q_RETURN_ARG(uint, hashOther)); |
565 | QCOMPARE(hash, (uint)0); |
566 | QCOMPARE(hashOther, (uint)0); |
567 | |
568 | // check that the method values are not the same after an edit |
569 | QMetaObject::invokeMethod(obj: serviceUnique, member: "setConfirmationHash" , |
570 | Q_ARG(uint, 1)); |
571 | QMetaObject::invokeMethod(obj: serviceUnique, member: "slotConfirmation" , |
572 | Q_RETURN_ARG(uint, hash)); |
573 | QMetaObject::invokeMethod(obj: serviceUniqueOther, member: "slotConfirmation" , |
574 | Q_RETURN_ARG(uint, hashOther)); |
575 | QCOMPARE(hash, (uint)1); |
576 | QCOMPARE(hashOther, (uint)0); |
577 | } |
578 | |
579 | void tst_QServiceManager_IPC::verifySharedServiceObject() |
580 | { |
581 | QVERIFY(serviceShared != 0); |
582 | const QMetaObject* mo = serviceShared->metaObject(); |
583 | QCOMPARE(mo->className(), "SharedTestService" ); |
584 | QVERIFY(mo->superClass()); |
585 | QCOMPARE(mo->superClass()->className(), "QServiceProxyBase" ); |
586 | QCOMPARE(mo->methodCount()-mo-> methodOffset(), 19); |
587 | QCOMPARE(mo->methodCount(), 25); //20 meta functions available |
588 | //actual function presence will be tested later |
589 | |
590 | // for (int i = 0; i < mo->methodCount(); i++) { |
591 | // qDebug() << "Methods" << i << mo->method(i).methodSignature(); |
592 | // } |
593 | |
594 | //test properties |
595 | QCOMPARE(mo->propertyCount()-mo->propertyOffset(), 1); |
596 | QCOMPARE(mo->propertyCount(), 2); |
597 | |
598 | QCOMPARE(mo->enumeratorCount()-mo->enumeratorOffset(), 0); |
599 | QCOMPARE(mo->enumeratorCount(), 0); |
600 | |
601 | QCOMPARE(mo->classInfoCount()-mo->classInfoOffset(), 0); |
602 | QCOMPARE(mo->classInfoCount(), 0); |
603 | |
604 | if (verbose) { |
605 | qDebug() << "ServiceObject class: " << mo->className() << mo->superClass() << mo->superClass()->className(); |
606 | qDebug() << "------------------- Meta Methods -----------" ; |
607 | qDebug() << "Methods:" << mo->methodCount()- mo->methodOffset() << "(" << mo->methodCount() << ")" ; |
608 | for (int i=0; i< mo->methodCount(); i++) { |
609 | QMetaMethod method = mo->method(index: i); |
610 | QString type; |
611 | switch (method.methodType()) { |
612 | case QMetaMethod::Signal: |
613 | type = "signal" ; break; |
614 | case QMetaMethod::Slot: |
615 | type = "slot" ; break; |
616 | case QMetaMethod::Constructor: |
617 | type = "constrcutor" ; break; |
618 | case QMetaMethod::Method: |
619 | type = "method" ; break; |
620 | } |
621 | qDebug() << " " << i << "." << method.methodSignature() << type; |
622 | } |
623 | |
624 | qDebug() << "------------------- Meta Properties --------" ; |
625 | qDebug() << "Properties:" << mo->propertyCount()- mo->propertyOffset() << "(" << mo->propertyCount() << ")" ; |
626 | for (int i=0; i< mo->propertyCount(); i++) { |
627 | QMetaProperty property = mo->property(index: i); |
628 | QString info = "Readable: %1 Resettable: %2 Writeable: %3 Designable: %4 Scriptable: %5 User: %6 Stored: %7 Constant: %8 Final: %9 HasNotify: %10 EnumType: %11 FlagType: %12" ; |
629 | info = info.arg(a: property.isReadable()).arg(a: property.isResettable()).arg(a: property.isWritable()); |
630 | info = info.arg(a: property.isDesignable()).arg(a: property.isScriptable()).arg(a: property.isUser()); |
631 | info = info.arg(a: property.isStored()).arg(a: property.isConstant()).arg(a: property.isFinal()); |
632 | info = info.arg(a: property.hasNotifySignal()).arg(a: property.isEnumType()).arg(a: property.isFlagType()); |
633 | |
634 | qDebug() << " " << i << "." << property.name() << "Type:" << property.typeName() << info; |
635 | } |
636 | |
637 | qDebug() << "------------------- Meta Enumerators --------" ; |
638 | qDebug() << "Enums:" << mo->enumeratorCount()- mo->enumeratorOffset() << "(" << mo->enumeratorCount() << ")" ; |
639 | |
640 | qDebug() << "------------------- Meta class info ---------" ; |
641 | qDebug() << "ClassInfos:" << mo->classInfoCount()- mo->classInfoOffset() << "(" << mo->classInfoCount() << ")" ; |
642 | } |
643 | } |
644 | |
645 | void tst_QServiceManager_IPC::verifySharedMethods_data() |
646 | { |
647 | QTest::addColumn<QByteArray>(name: "signature" ); |
648 | QTest::addColumn<int>(name: "metaMethodType" ); |
649 | QTest::addColumn<QByteArray>(name: "returnType" ); |
650 | |
651 | //list of all slots, signals and invokable functions |
652 | QTest::newRow(dataTag: "signalWithIntParam(int)" ) |
653 | << QByteArray("signalWithIntParam(int)" ) << (int)( QMetaMethod::Signal) << QByteArray("void" ); |
654 | QTest::newRow(dataTag: "signalWithVariousParam(QVariant,QString,QServiceFilter)" ) |
655 | << QByteArray("signalWithVariousParam(QVariant,QString,QServiceFilter)" ) << (int)( QMetaMethod::Signal) << QByteArray("void" ); |
656 | QTest::newRow(dataTag: "valueChanged()" ) |
657 | << QByteArray("valueChanged()" ) << (int)( QMetaMethod::Signal) << QByteArray("void" ); |
658 | QTest::newRow(dataTag: "triggerSignalWithIntParam()" ) |
659 | << QByteArray("triggerSignalWithIntParam()" ) << (int)( QMetaMethod::Slot) << QByteArray("void" ); |
660 | QTest::newRow(dataTag: "triggerSignalWithVariousParam()" ) |
661 | << QByteArray("triggerSignalWithVariousParam()" ) << (int)( QMetaMethod::Slot) << QByteArray("void" ); |
662 | QTest::newRow(dataTag: "triggerSignalWithIntParamExecute()" ) |
663 | << QByteArray("triggerSignalWithIntParamExecute()" ) << (int)( QMetaMethod::Slot) << QByteArray("void" ); |
664 | QTest::newRow(dataTag: "triggerSignalWithVariousParamExecute()" ) |
665 | << QByteArray("triggerSignalWithVariousParamExecute()" ) << (int)( QMetaMethod::Slot) << QByteArray("void" ); |
666 | QTest::newRow(dataTag: "testSlot()" ) |
667 | << QByteArray("testSlot()" ) << (int)( QMetaMethod::Slot) << QByteArray("void" ); |
668 | QTest::newRow(dataTag: "testSlotWithArgs(QByteArray,int,QVariant)" ) |
669 | << QByteArray("testSlotWithArgs(QByteArray,int,QVariant)" ) << (int)( QMetaMethod::Slot) << QByteArray("void" ); |
670 | QTest::newRow(dataTag: "testSlotWithCustomArg(QServiceFilter)" ) |
671 | << QByteArray("testSlotWithCustomArg(QServiceFilter)" ) << (int)( QMetaMethod::Slot) << QByteArray("void" ); |
672 | QTest::newRow(dataTag: "testSlotWidthComplexArg(QVariantHash)" ) |
673 | << QByteArray("testSlotWithComplexArg(QVariantHash)" ) << (int)( QMetaMethod::Slot) << QByteArray("void" ); |
674 | |
675 | //QServiceInterfaceDescriptor has not been declared as meta type |
676 | QTest::newRow(dataTag: "testSlotWithUnknownArg(QServiceInterfaceDescriptor)" ) |
677 | << QByteArray("testSlotWithUnknownArg(QServiceInterfaceDescriptor)" ) << (int)( QMetaMethod::Slot) << QByteArray("void" ); |
678 | QTest::newRow(dataTag: "testFunctionWithReturnValue(int)" ) |
679 | << QByteArray("testFunctionWithReturnValue(int)" ) << (int)( QMetaMethod::Method) << QByteArray("QString" ); |
680 | QTest::newRow(dataTag: "testFunctionWithVariantReturnValue(QVariant)" ) |
681 | << QByteArray("testFunctionWithVariantReturnValue(QVariant)" ) << (int)( QMetaMethod::Method) << QByteArray("QVariant" ); |
682 | QTest::newRow(dataTag: "testFunctionWithCustomReturnValue()" ) |
683 | << QByteArray("testFunctionWithCustomReturnValue()" ) << (int)( QMetaMethod::Method) << QByteArray("QServiceFilter" ); |
684 | QTest::newRow(dataTag: "slotConfirmation()" ) |
685 | << QByteArray("slotConfirmation()" ) << (int)( QMetaMethod::Method) << QByteArray("uint" ); |
686 | QTest::newRow(dataTag: "setConfirmationHash(uint)" ) |
687 | << QByteArray("setConfirmationHash(uint)" ) << (int)( QMetaMethod::Method) << QByteArray("void" ); |
688 | } |
689 | |
690 | void tst_QServiceManager_IPC::verifySharedMethods() |
691 | { |
692 | QVERIFY(serviceShared); |
693 | QFETCH(QByteArray, signature); |
694 | QFETCH(int, metaMethodType); |
695 | QFETCH(QByteArray, returnType); |
696 | |
697 | const QMetaObject* meta = serviceShared->metaObject(); |
698 | const int index = meta->indexOfMethod(method: signature.constData()); |
699 | QVERIFY(index>=0); |
700 | QMetaMethod method = meta->method(index); |
701 | QCOMPARE(metaMethodType, (int)method.methodType()); |
702 | QCOMPARE(returnType, QByteArray(method.typeName())); |
703 | } |
704 | |
705 | void tst_QServiceManager_IPC::verifySharedProperties_data() |
706 | { |
707 | QTest::addColumn<QByteArray>(name: "signature" ); |
708 | QTest::addColumn<QString>(name: "typeName" ); |
709 | QTest::addColumn<qint16>(name: "flags" ); |
710 | QTest::addColumn<QVariant>(name: "defaultValue" ); |
711 | QTest::addColumn<QVariant>(name: "writeValue" ); |
712 | QTest::addColumn<QVariant>(name: "expectedReturnValue" ); |
713 | |
714 | // |
715 | // bit order: |
716 | // 0 isWritable - 1 isUser - 2 isStored - 3 isScriptable |
717 | // 4 isResettable - 5 isReadable - 6 isFlagType - 7 isFinal |
718 | // 8 isEnumType - 9 isDesignable - 10 isConstant - 11 hasNotifySgnal |
719 | // |
720 | // for more details see verifyProperties() |
721 | // |
722 | |
723 | QTest::newRow(dataTag: "value property" ) << QByteArray("value" ) |
724 | << QString("QString" ) << qint16(0x0A3D) << QVariant(QString("FFF" )) << QVariant(QString("GGG" )) << QVariant(QString("GGG" )); |
725 | } |
726 | |
727 | void tst_QServiceManager_IPC::verifySharedProperties() |
728 | { |
729 | QVERIFY(serviceShared); |
730 | QFETCH(QByteArray, signature); |
731 | QFETCH(QString, typeName); |
732 | QFETCH(qint16, flags); |
733 | QFETCH(QVariant, defaultValue); |
734 | QFETCH(QVariant, writeValue); |
735 | QFETCH(QVariant, expectedReturnValue); |
736 | |
737 | const QMetaObject* meta = serviceShared->metaObject(); |
738 | const int index = meta->indexOfProperty(name: signature.constData()); |
739 | QVERIFY(index>=0); |
740 | QMetaProperty property = meta->property(index); |
741 | QVERIFY(property.isValid()); |
742 | QCOMPARE(QString(property.typeName()), typeName); |
743 | |
744 | QVERIFY( property.isWritable() == (bool) (flags & 0x0001) ); |
745 | QVERIFY( property.isUser() == (bool) (flags & 0x0002) ); |
746 | QVERIFY( property.isStored() == (bool) (flags & 0x0004) ); |
747 | QVERIFY( property.isScriptable() == (bool) (flags & 0x0008) ); |
748 | QVERIFY( property.isResettable() == (bool) (flags & 0x0010) ); |
749 | QVERIFY( property.isReadable() == (bool) (flags & 0x0020) ); |
750 | QVERIFY( property.isFlagType() == (bool) (flags & 0x0040) ); |
751 | QVERIFY( property.isFinal() == (bool) (flags & 0x0080) ); |
752 | QVERIFY( property.isEnumType() == (bool) (flags & 0x0100) ); |
753 | QVERIFY( property.isDesignable() == (bool) (flags & 0x0200) ); |
754 | QVERIFY( property.isConstant() == (bool) (flags & 0x0400) ); |
755 | QVERIFY( property.hasNotifySignal() == (bool) (flags & 0x0800) ); |
756 | |
757 | if (property.isReadable()) { |
758 | QCOMPARE(defaultValue, serviceShared->property(signature)); |
759 | if (property.isWritable()) { |
760 | serviceShared->setProperty(name: signature, value: writeValue); |
761 | QCOMPARE(expectedReturnValue, serviceShared->property(signature)); |
762 | if (property.isResettable()) { |
763 | property.reset(obj: serviceShared); |
764 | QCOMPARE(defaultValue, serviceShared->property(signature)); |
765 | } |
766 | serviceShared->setProperty(name: signature, value: defaultValue); |
767 | QCOMPARE(defaultValue, serviceShared->property(signature)); |
768 | } |
769 | } |
770 | } |
771 | |
772 | void tst_QServiceManager_IPC::verifyUniqueServiceObject() |
773 | { |
774 | QVERIFY(serviceUnique != 0); |
775 | const QMetaObject* mo = serviceUnique->metaObject(); |
776 | QCOMPARE(mo->className(), "UniqueTestService" ); |
777 | QVERIFY(mo->superClass()); |
778 | QCOMPARE(mo->superClass()->className(), "QServiceProxyBase" ); |
779 | // TODO adding the ipc failure signal seems to break these |
780 | QCOMPARE(mo->methodCount()-mo-> methodOffset(), 27); // 28+1 added signal for error signal added by library |
781 | QCOMPARE(mo->methodCount(), 33); //33 meta functions available + 1 signal |
782 | //actual function presence will be tested later |
783 | |
784 | // for (int i = 0; i < mo->methodCount(); i++) { |
785 | // qDebug() << "Methods" << i << mo->method(i).methodSignature(); |
786 | // } |
787 | |
788 | //test properties |
789 | QCOMPARE(mo->propertyCount()-mo->propertyOffset(), 5); |
790 | QCOMPARE(mo->propertyCount(), 6); |
791 | |
792 | QCOMPARE(mo->enumeratorCount()-mo->enumeratorOffset(), 3); |
793 | QCOMPARE(mo->enumeratorCount(), 3); |
794 | |
795 | QCOMPARE(mo->classInfoCount()-mo->classInfoOffset(), 2); |
796 | QCOMPARE(mo->classInfoCount(), 2); |
797 | |
798 | if (verbose) { |
799 | qDebug() << "ServiceObject class: " << mo->className() << mo->superClass() << mo->superClass()->className(); |
800 | qDebug() << "------------------- Meta Methods -----------" ; |
801 | qDebug() << "Methods:" << mo->methodCount()- mo->methodOffset() << "(" << mo->methodCount() << ")" ; |
802 | for (int i=0; i< mo->methodCount(); i++) { |
803 | QMetaMethod method = mo->method(index: i); |
804 | QString type; |
805 | switch (method.methodType()) { |
806 | case QMetaMethod::Signal: |
807 | type = "signal" ; break; |
808 | case QMetaMethod::Slot: |
809 | type = "slot" ; break; |
810 | case QMetaMethod::Constructor: |
811 | type = "constrcutor" ; break; |
812 | case QMetaMethod::Method: |
813 | type = "method" ; break; |
814 | } |
815 | qDebug() << " " << i << "." << method.methodSignature() << type; |
816 | } |
817 | |
818 | qDebug() << "------------------- Meta Properties --------" ; |
819 | qDebug() << "Properties:" << mo->propertyCount()- mo->propertyOffset() << "(" << mo->propertyCount() << ")" ; |
820 | for (int i=0; i< mo->propertyCount(); i++) { |
821 | QMetaProperty property = mo->property(index: i); |
822 | QString info = "Readable: %1 Resettable: %2 Writeable: %3 Designable: %4 Scriptable: %5 User: %6 Stored: %7 Constant: %8 Final: %9 HasNotify: %10 EnumType: %11 FlagType: %12" ; |
823 | info = info.arg(a: property.isReadable()).arg(a: property.isResettable()).arg(a: property.isWritable()); |
824 | info = info.arg(a: property.isDesignable()).arg(a: property.isScriptable()).arg(a: property.isUser()); |
825 | info = info.arg(a: property.isStored()).arg(a: property.isConstant()).arg(a: property.isFinal()); |
826 | info = info.arg(a: property.hasNotifySignal()).arg(a: property.isEnumType()).arg(a: property.isFlagType()); |
827 | |
828 | qDebug() << " " << i << "." << property.name() << "Type:" << property.typeName() << info; |
829 | } |
830 | |
831 | qDebug() << "------------------- Meta Enumerators --------" ; |
832 | qDebug() << "Enums:" << mo->enumeratorCount()- mo->enumeratorOffset() << "(" << mo->enumeratorCount() << ")" ; |
833 | for (int i=0; i< mo->enumeratorCount(); i++) { |
834 | QMetaEnum e = mo->enumerator(index: i); |
835 | qDebug() << " " << i << "." << e.name() << "Scope:" << e.scope() << "KeyCount: " << e.keyCount(); |
836 | for (int j = 0; j<e.keyCount(); j++) |
837 | qDebug() << " " << e.key(index: j) << " - " << e.value(index: j); |
838 | } |
839 | |
840 | qDebug() << "------------------- Meta class info ---------" ; |
841 | qDebug() << "ClassInfos:" << mo->classInfoCount()- mo->classInfoOffset() << "(" << mo->classInfoCount() << ")" ; |
842 | for (int i=0; i< mo->classInfoCount(); i++) { |
843 | QMetaClassInfo info = mo->classInfo(index: i); |
844 | qDebug() << " " << i << "." << info.name() << "Value:" << info.value(); |
845 | } |
846 | } |
847 | } |
848 | |
849 | void tst_QServiceManager_IPC::verifyUniqueMethods_data() |
850 | { |
851 | QTest::addColumn<QByteArray>(name: "signature" ); |
852 | QTest::addColumn<int>(name: "metaMethodType" ); |
853 | QTest::addColumn<QByteArray>(name: "returnType" ); |
854 | |
855 | //list of all slots, signals and invokable functions |
856 | QTest::newRow(dataTag: "signalWithIntParam(int)" ) |
857 | << QByteArray("signalWithIntParam(int)" ) << (int)( QMetaMethod::Signal) << QByteArray("void" ); |
858 | QTest::newRow(dataTag: "signalWithVariousParam(QVariant,QString,QServiceFilter,QVariant)" ) |
859 | << QByteArray("signalWithVariousParam(QVariant,QString,QServiceFilter,QVariant)" ) << (int)( QMetaMethod::Signal) << QByteArray("void" ); |
860 | QTest::newRow(dataTag: "valueChanged()" ) |
861 | << QByteArray("valueChanged()" ) << (int)( QMetaMethod::Signal) << QByteArray("void" ); |
862 | QTest::newRow(dataTag: "priorityChanged()" ) |
863 | << QByteArray("priorityChanged()" ) << (int)( QMetaMethod::Signal) << QByteArray("void" ); |
864 | QTest::newRow(dataTag: "triggerSignalWithIntParam()" ) |
865 | << QByteArray("triggerSignalWithIntParam()" ) << (int)( QMetaMethod::Slot) << QByteArray("void" ); |
866 | QTest::newRow(dataTag: "triggerSignalWithVariousParam()" ) |
867 | << QByteArray("triggerSignalWithVariousParam()" ) << (int)( QMetaMethod::Slot) << QByteArray("void" ); |
868 | QTest::newRow(dataTag: "triggerSignalWithIntParamExecute()" ) |
869 | << QByteArray("triggerSignalWithIntParamExecute()" ) << (int)( QMetaMethod::Slot) << QByteArray("void" ); |
870 | QTest::newRow(dataTag: "triggerSignalWithVariousParamExecute()" ) |
871 | << QByteArray("triggerSignalWithVariousParamExecute()" ) << (int)( QMetaMethod::Slot) << QByteArray("void" ); |
872 | QTest::newRow(dataTag: "testSlot()" ) |
873 | << QByteArray("testSlot()" ) << (int)( QMetaMethod::Slot) << QByteArray("void" ); |
874 | QTest::newRow(dataTag: "testSlotWithArgs(QByteArray,int,QVariant)" ) |
875 | << QByteArray("testSlotWithArgs(QByteArray,int,QVariant)" ) << (int)( QMetaMethod::Slot) << QByteArray("void" ); |
876 | QTest::newRow(dataTag: "testSlotWithCustomArg(QServiceFilter)" ) |
877 | << QByteArray("testSlotWithCustomArg(QServiceFilter)" ) << (int)( QMetaMethod::Slot) << QByteArray("void" ); |
878 | |
879 | //QServiceInterfaceDescriptor has not been declared as meta type |
880 | QTest::newRow(dataTag: "testSlotWithUnknownArg(QServiceInterfaceDescriptor)" ) |
881 | << QByteArray("testSlotWithUnknownArg(QServiceInterfaceDescriptor)" ) << (int)( QMetaMethod::Slot) << QByteArray("void" ); |
882 | QTest::newRow(dataTag: "testFunctionWithReturnValue(int)" ) |
883 | << QByteArray("testFunctionWithReturnValue(int)" ) << (int)( QMetaMethod::Method) << QByteArray("QString" ); |
884 | QTest::newRow(dataTag: "testFunctionWithVariantReturnValue(QVariant)" ) |
885 | << QByteArray("testFunctionWithVariantReturnValue(QVariant)" ) << (int)( QMetaMethod::Method) << QByteArray("QVariant" ); |
886 | QTest::newRow(dataTag: "testFunctionWithCustomReturnValue()" ) |
887 | << QByteArray("testFunctionWithCustomReturnValue()" ) << (int)( QMetaMethod::Method) << QByteArray("QServiceFilter" ); |
888 | QTest::newRow(dataTag: "slotConfirmation()" ) |
889 | << QByteArray("slotConfirmation()" ) << (int)( QMetaMethod::Method) << QByteArray("uint" ); |
890 | QTest::newRow(dataTag: "setConfirmationHash(uint)" ) |
891 | << QByteArray("setConfirmationHash(uint)" ) << (int)( QMetaMethod::Method) << QByteArray("void" ); |
892 | } |
893 | |
894 | void tst_QServiceManager_IPC::verifyUniqueMethods() |
895 | { |
896 | QVERIFY(serviceUnique); |
897 | QFETCH(QByteArray, signature); |
898 | QFETCH(int, metaMethodType); |
899 | QFETCH(QByteArray, returnType); |
900 | |
901 | const QMetaObject* meta = serviceUnique->metaObject(); |
902 | const int index = meta->indexOfMethod(method: signature.constData()); |
903 | QVERIFY(index>=0); |
904 | QMetaMethod method = meta->method(index); |
905 | QCOMPARE((int)method.methodType(), metaMethodType); |
906 | QCOMPARE(QByteArray(method.typeName()), returnType); |
907 | } |
908 | |
909 | void tst_QServiceManager_IPC::verifyUniqueProperties_data() |
910 | { |
911 | QTest::addColumn<QByteArray>(name: "signature" ); |
912 | QTest::addColumn<QString>(name: "typeName" ); |
913 | QTest::addColumn<qint16>(name: "flags" ); |
914 | QTest::addColumn<QVariant>(name: "defaultValue" ); |
915 | QTest::addColumn<QVariant>(name: "writeValue" ); |
916 | QTest::addColumn<QVariant>(name: "expectedReturnValue" ); |
917 | |
918 | // |
919 | // bit order: |
920 | // 0 isWritable - 1 isUser - 2 isStored - 3 isScriptable |
921 | // 4 isResettable - 5 isReadable - 6 isFlagType - 7 isFinal |
922 | // 8 isEnumType - 9 isDesignable - 10 isConstant - 11 hasNotifySgnal |
923 | // |
924 | // for more details see verifyProperties() |
925 | // |
926 | |
927 | QTest::newRow(dataTag: "value property" ) << QByteArray("value" ) |
928 | << QString("QString" ) << qint16(0x0A3D) << QVariant(QString("FFF" )) << QVariant(QString("GGG" )) << QVariant(QString("GGG" )); |
929 | #ifndef SFW_USE_DBUS_BACKEND |
930 | // ENUMS DONT WORK OVER DBUS |
931 | QTest::newRow("priority property" ) << QByteArray("priority" ) |
932 | << QString("Priority" ) << qint16(0x0B2D) << QVariant((int)0) << QVariant("Low" ) << QVariant((int)1) ; |
933 | QTest::newRow("serviceFlags property" ) << QByteArray("serviceFlags" ) |
934 | << QString("ServiceFlag" ) << qint16(0x036D) << QVariant((int)4) << QVariant("FirstBit|ThirdBit" ) << QVariant((int)5); |
935 | #endif |
936 | } |
937 | |
938 | void tst_QServiceManager_IPC::verifyUniqueProperties() |
939 | { |
940 | QVERIFY(serviceUnique); |
941 | QFETCH(QByteArray, signature); |
942 | QFETCH(QString, typeName); |
943 | QFETCH(qint16, flags); |
944 | QFETCH(QVariant, defaultValue); |
945 | QFETCH(QVariant, writeValue); |
946 | QFETCH(QVariant, expectedReturnValue); |
947 | |
948 | const QMetaObject* meta = serviceUnique->metaObject(); |
949 | const int index = meta->indexOfProperty(name: signature.constData()); |
950 | QVERIFY(index>=0); |
951 | QMetaProperty property = meta->property(index); |
952 | QVERIFY(property.isValid()); |
953 | QCOMPARE(QString(property.typeName()), typeName); |
954 | |
955 | QVERIFY( property.isWritable() == (bool) (flags & 0x0001) ); |
956 | QVERIFY( property.isUser() == (bool) (flags & 0x0002) ); |
957 | QVERIFY( property.isStored() == (bool) (flags & 0x0004) ); |
958 | QVERIFY( property.isScriptable() == (bool) (flags & 0x0008) ); |
959 | QVERIFY( property.isResettable() == (bool) (flags & 0x0010) ); |
960 | QVERIFY( property.isReadable() == (bool) (flags & 0x0020) ); |
961 | QVERIFY( property.isFlagType() == (bool) (flags & 0x0040) ); |
962 | QVERIFY( property.isFinal() == (bool) (flags & 0x0080) ); |
963 | QVERIFY( property.isEnumType() == (bool) (flags & 0x0100) ); |
964 | QVERIFY( property.isDesignable() == (bool) (flags & 0x0200) ); |
965 | QVERIFY( property.isConstant() == (bool) (flags & 0x0400) ); |
966 | QVERIFY( property.hasNotifySignal() == (bool) (flags & 0x0800) ); |
967 | |
968 | if (property.isReadable()) { |
969 | QCOMPARE(defaultValue, serviceUnique->property(signature)); |
970 | if (property.isWritable()) { |
971 | serviceUnique->setProperty(name: signature, value: writeValue); |
972 | QCOMPARE(expectedReturnValue, serviceUnique->property(signature)); |
973 | if (property.isResettable()) { |
974 | property.reset(obj: serviceUnique); |
975 | QCOMPARE(defaultValue, serviceUnique->property(signature)); |
976 | } |
977 | serviceUnique->setProperty(name: signature, value: defaultValue); |
978 | QCOMPARE(defaultValue, serviceUnique->property(signature)); |
979 | } |
980 | } |
981 | } |
982 | |
983 | void tst_QServiceManager_IPC::verifyUniqueClassInfo_data() |
984 | { |
985 | QTest::addColumn<QString>(name: "classInfoKey" ); |
986 | QTest::addColumn<QString>(name: "classInfoValue" ); |
987 | |
988 | QTest::newRow(dataTag: "UniqueTestService" ) << QString("UniqueTestService" ) << QString("First test" ); |
989 | QTest::newRow(dataTag: "Key" ) << QString("Key" ) << QString("Value" ); |
990 | |
991 | } |
992 | void tst_QServiceManager_IPC::verifyUniqueClassInfo() |
993 | { |
994 | QFETCH(QString, classInfoKey); |
995 | QFETCH(QString, classInfoValue); |
996 | |
997 | const QMetaObject* meta = serviceUnique->metaObject(); |
998 | const int index = meta->indexOfClassInfo(name: classInfoKey.toLatin1().constData()); |
999 | |
1000 | QMetaClassInfo info = meta->classInfo(index); |
1001 | QCOMPARE(classInfoKey, QString(info.name())); |
1002 | QCOMPARE(classInfoValue, QString(info.value())); |
1003 | |
1004 | } |
1005 | |
1006 | Q_DECLARE_METATYPE(QList<int> ) |
1007 | void tst_QServiceManager_IPC::verifyUniqueEnumerator_data() |
1008 | { |
1009 | QTest::addColumn<QString>(name: "enumName" ); |
1010 | QTest::addColumn<QString>(name: "enumScope" ); |
1011 | QTest::addColumn<int>(name: "enumKeyCount" ); |
1012 | QTest::addColumn<bool>(name: "enumIsFlag" ); |
1013 | QTest::addColumn<QStringList>(name: "enumKeyNames" ); |
1014 | QTest::addColumn<QList<int> >(name: "enumKeyValues" ); |
1015 | |
1016 | |
1017 | QStringList keynames; |
1018 | keynames << "FirstBit" << "SecondBit" << "ThirdBit" ; |
1019 | QList<int> values; |
1020 | values << 1 << 2 << 4; |
1021 | QTest::newRow(dataTag: "ServiceFlag" ) << QString("ServiceFlag" ) << QString("UniqueTestService" ) |
1022 | << 3 << true << keynames << values; |
1023 | QTest::newRow(dataTag: "ServiceFlags" ) << QString("ServiceFlags" ) << QString("UniqueTestService" ) |
1024 | << 3 << true << keynames << values; |
1025 | |
1026 | keynames.clear(); |
1027 | values.clear(); |
1028 | |
1029 | keynames << "High" << "Low" << "VeryLow" << "ExtremelyLow" ; |
1030 | values << 0 << 1 << 2 << 3 ; |
1031 | |
1032 | QTest::newRow(dataTag: "Priority" ) << QString("Priority" ) << QString("UniqueTestService" ) |
1033 | << 4 << false << keynames << values; |
1034 | } |
1035 | |
1036 | void tst_QServiceManager_IPC::verifyUniqueEnumerator() |
1037 | { |
1038 | QFETCH(QString, enumName); |
1039 | QFETCH(QString, enumScope); |
1040 | QFETCH(int, enumKeyCount); |
1041 | QFETCH(bool, enumIsFlag); |
1042 | QFETCH(QStringList, enumKeyNames); |
1043 | QFETCH(QList<int>, enumKeyValues); |
1044 | |
1045 | const QMetaObject* meta = serviceUnique->metaObject(); |
1046 | const int index = meta->indexOfEnumerator(name: enumName.toLatin1().constData()); |
1047 | QMetaEnum metaEnum = meta->enumerator(index); |
1048 | |
1049 | QVERIFY(metaEnum.isValid()); |
1050 | QCOMPARE(enumScope, QString(metaEnum.scope())); |
1051 | QCOMPARE(enumKeyCount, metaEnum.keyCount()); |
1052 | QCOMPARE(enumIsFlag, metaEnum.isFlag()); |
1053 | QCOMPARE(enumKeyNames.count(), enumKeyValues.count()); |
1054 | |
1055 | for (int i=0; i<enumKeyNames.count(); i++) { |
1056 | QCOMPARE(enumKeyNames.at(i), QString(metaEnum.valueToKey(enumKeyValues.at(i)))); |
1057 | QCOMPARE(enumKeyValues.at(i), metaEnum.keyToValue(enumKeyNames.at(i).toLatin1().constData())); |
1058 | } |
1059 | } |
1060 | |
1061 | void tst_QServiceManager_IPC::testInvokableFunctions() |
1062 | { |
1063 | // Test invokable method input with calculated return value |
1064 | QString temp; |
1065 | QString result; |
1066 | QString patternShared("%1 + 3 = %2" ); |
1067 | QString patternUnique("%1 x 3 = %2" ); |
1068 | for (int i = -10; i<10; i++) { |
1069 | result.clear(); temp.clear(); |
1070 | QVERIFY(result.isEmpty()); |
1071 | QVERIFY(temp.isEmpty()); |
1072 | |
1073 | // Shared service |
1074 | QMetaObject::invokeMethod(obj: serviceShared, member: "testFunctionWithReturnValue" , |
1075 | Q_RETURN_ARG(QString, result), |
1076 | Q_ARG(int, i)); |
1077 | temp = patternShared.arg(a: i).arg(a: i+3); |
1078 | QCOMPARE(temp, result); |
1079 | |
1080 | result.clear(); temp.clear(); |
1081 | QVERIFY(result.isEmpty()); |
1082 | QVERIFY(temp.isEmpty()); |
1083 | |
1084 | // Unique service |
1085 | QMetaObject::invokeMethod(obj: serviceUnique, member: "testFunctionWithReturnValue" , |
1086 | Q_RETURN_ARG(QString, result), |
1087 | Q_ARG(int, i)); |
1088 | temp = patternUnique.arg(a: i).arg(a: i*3); |
1089 | QCOMPARE(temp, result); |
1090 | } |
1091 | |
1092 | // Test invokable method with QVariant return type |
1093 | QList<QVariant> variants; |
1094 | variants << QVariant("CANT BE NULL" ) << QVariant(6) << QVariant(QString("testString" )); |
1095 | for (int i = 0; i<variants.count(); i++) { |
1096 | QVariant varResult; |
1097 | |
1098 | // Shared service |
1099 | QMetaObject::invokeMethod(obj: serviceShared, member: "testFunctionWithVariantReturnValue" , |
1100 | Q_RETURN_ARG(QVariant, varResult), |
1101 | Q_ARG(QVariant, variants.at(i))); |
1102 | QCOMPARE(variants.at(i), varResult); |
1103 | |
1104 | // Unique service |
1105 | QMetaObject::invokeMethod(obj: serviceUnique, member: "testFunctionWithVariantReturnValue" , |
1106 | Q_RETURN_ARG(QVariant, varResult), |
1107 | Q_ARG(QVariant, variants.at(i))); |
1108 | QCOMPARE(variants.at(i), varResult); |
1109 | } |
1110 | |
1111 | // Test invokable method with custom return type |
1112 | QServiceFilter f; |
1113 | // Shared service |
1114 | QMetaObject::invokeMethod(obj: serviceShared, member: "testFunctionWithCustomReturnValue" , |
1115 | Q_RETURN_ARG(QServiceFilter, f)); |
1116 | QCOMPARE(f.serviceName(), QString("MySharedService" )); |
1117 | QCOMPARE(f.interfaceName(), QString("com.nokia.qt.ipcunittest" )); |
1118 | QCOMPARE(f.majorVersion(), 6); |
1119 | QCOMPARE(f.minorVersion(), 2); |
1120 | |
1121 | // Unique service |
1122 | QMetaObject::invokeMethod(obj: serviceUnique, member: "testFunctionWithCustomReturnValue" , |
1123 | Q_RETURN_ARG(QServiceFilter, f)); |
1124 | QCOMPARE(f.serviceName(), QString("MyUniqueService" )); |
1125 | QCOMPARE(f.interfaceName(), QString("com.nokia.qt.ipcunittest" )); |
1126 | QCOMPARE(f.majorVersion(), 6); |
1127 | QCOMPARE(f.minorVersion(), 7); |
1128 | |
1129 | // Test invokable method with list return type |
1130 | QList<QString> list, retList; |
1131 | list << "1" << "2" << "3" ; |
1132 | QMetaObject::invokeMethod(obj: serviceUnique, member: "testFunctionWithListReturn" , |
1133 | Q_RETURN_ARG(QList<QString>, retList)); |
1134 | QCOMPARE(list, retList); |
1135 | } |
1136 | |
1137 | void tst_QServiceManager_IPC::testSignalling() |
1138 | { |
1139 | // Test signalling for simple methods |
1140 | QSignalSpy spy(serviceUnique, SIGNAL(signalWithIntParam(int))); |
1141 | QMetaObject::invokeMethod(obj: serviceUnique, member: "triggerSignalWithIntParam" ); |
1142 | QTRY_VERIFY(spy.count() > 0); |
1143 | QCOMPARE(spy.at(0).at(0).toInt(), 5); |
1144 | |
1145 | // Test signalling for property changes |
1146 | QCOMPARE(QString("FFF" ), serviceUnique->property("value" ).toString()); |
1147 | QSignalSpy propSpy(serviceUnique, SIGNAL(valueChanged())); |
1148 | |
1149 | serviceUnique->setProperty(name: "value" , value: QString("GGG" )); |
1150 | QTRY_VERIFY(propSpy.count() == 1); |
1151 | propSpy.clear(); |
1152 | serviceUnique->setProperty(name: "value" , value: QString("FFF" )); |
1153 | QTRY_VERIFY(propSpy.count() == 1); |
1154 | QCOMPARE(QString("FFF" ), serviceUnique->property("value" ).toString()); |
1155 | |
1156 | // Test signalling with custom types |
1157 | QSignalSpy variousSpy(serviceUnique, SIGNAL(signalWithVariousParam(QVariant,QString,QServiceFilter,QVariant))); |
1158 | QMetaObject::invokeMethod(obj: serviceUnique, member: "triggerSignalWithVariousParam" ); |
1159 | |
1160 | QTRY_VERIFY(variousSpy.count() == 1); |
1161 | QCOMPARE(variousSpy.at(0).count(), 4); |
1162 | QCOMPARE(variousSpy.at(0).at(0).value<QVariant>(), QVariant("CAN'T BE NULL" )); |
1163 | QCOMPARE(variousSpy.at(0).at(1).toString(), QString("string-value" )); |
1164 | |
1165 | QVariant vFilter = variousSpy.at(i: 0).at(i: 2); |
1166 | QServiceFilter filter; |
1167 | QVERIFY(vFilter.canConvert<QServiceFilter>()); |
1168 | filter = vFilter.value<QServiceFilter>(); |
1169 | QCOMPARE(filter.serviceName(), QString("MyService" )); |
1170 | QCOMPARE(filter.interfaceName(), QString("com.nokia.qt.ipcunittest" )); |
1171 | QCOMPARE(filter.majorVersion(), 6); |
1172 | QCOMPARE(filter.minorVersion(), 7); |
1173 | |
1174 | QCOMPARE(variousSpy.at(0).at(3).value<QVariant>(), QVariant(5)); |
1175 | } |
1176 | |
1177 | void tst_QServiceManager_IPC::testSlotInvokation() |
1178 | { |
1179 | QObject *service = serviceUnique; |
1180 | |
1181 | // check the success of our invokable slot by using a hash of the method and its parameters |
1182 | uint hash = 1; |
1183 | uint expectedHash = 0; |
1184 | QMetaObject::invokeMethod(obj: service, member: "setConfirmationHash" , |
1185 | Q_ARG(uint, 0)); |
1186 | QMetaObject::invokeMethod(obj: service, member: "slotConfirmation" , |
1187 | Q_RETURN_ARG(uint, hash)); |
1188 | QCOMPARE(hash, (uint)0); |
1189 | |
1190 | // Test invokable slot with no arguments |
1191 | QMetaObject::invokeMethod(obj: service, member: "testSlot" ); |
1192 | QMetaObject::invokeMethod(obj: service, member: "slotConfirmation" , |
1193 | Q_RETURN_ARG(uint, hash)); |
1194 | expectedHash = qHash(key: QString("testSlot()" )); |
1195 | QCOMPARE(hash, expectedHash); |
1196 | |
1197 | // Test invokable slot with various arguments |
1198 | QVariant test("CANT BE NULL" ); |
1199 | QByteArray d = "array" ; |
1200 | int num = 5; |
1201 | QString output = QString("%1, %2, %3, %4" ); |
1202 | output = output.arg(a: d.constData()).arg(a: num).arg(a: test.toString()).arg(a: test.isValid()); |
1203 | expectedHash = qHash(key: output); |
1204 | QMetaObject::invokeMethod(obj: service, member: "testSlotWithArgs" , |
1205 | Q_ARG(QByteArray, d), Q_ARG(int, num), Q_ARG(QVariant, test)); |
1206 | QMetaObject::invokeMethod(obj: service, member: "slotConfirmation" , |
1207 | Q_RETURN_ARG(uint, hash)); |
1208 | QCOMPARE(hash, expectedHash); |
1209 | |
1210 | // Test failed invokable slot since QServiceInterfaceDescriptor is not a registered meta type |
1211 | // by checking that the service didn't set the hash to -1 due to being called |
1212 | QServiceInterfaceDescriptor desc; |
1213 | QMetaObject::invokeMethod(obj: service, member: "testSlotWithUnknownArg" , |
1214 | Q_ARG(QServiceInterfaceDescriptor, desc)); |
1215 | QMetaObject::invokeMethod(obj: service, member: "slotConfirmation" , |
1216 | Q_RETURN_ARG(uint, hash)); |
1217 | QVERIFY(hash != 1); |
1218 | |
1219 | // Test slot function with custom argument |
1220 | QServiceFilter f("com.myInterface" , "4.5" ); |
1221 | f.setServiceName("MyService" ); |
1222 | output = QString("%1: %2 - %3.%4" ); |
1223 | output = output.arg(a: f.serviceName()).arg(a: f.interfaceName()) |
1224 | .arg(a: f.majorVersion()).arg(a: f.minorVersion()); |
1225 | expectedHash = qHash(key: output); |
1226 | QMetaObject::invokeMethod(obj: service, member: "testSlotWithCustomArg" , |
1227 | Q_ARG(QServiceFilter, f)); |
1228 | QMetaObject::invokeMethod(obj: service, member: "slotConfirmation" , |
1229 | Q_RETURN_ARG(uint, hash)); |
1230 | QCOMPARE(hash, expectedHash); |
1231 | |
1232 | // Test invokable method with a QList argument |
1233 | QList<QString> list; |
1234 | list << "one" << "two" << "three" ; |
1235 | output = "" ; |
1236 | for (int i=0; i<list.size(); i++) { |
1237 | output += list[i]; |
1238 | if (i<list.size()-1) |
1239 | output += ", " ; |
1240 | } |
1241 | expectedHash = qHash(key: output); |
1242 | QMetaObject::invokeMethod(obj: serviceUnique, member: "testSlotWithListArg" , |
1243 | Q_ARG(QList<QString>, list)); |
1244 | QMetaObject::invokeMethod(obj: service, member: "slotConfirmation" , |
1245 | Q_RETURN_ARG(uint, hash)); |
1246 | QCOMPARE(hash, expectedHash); |
1247 | |
1248 | // Test slot with QHash<QString, QVariant> |
1249 | QHash<QString, QVariant> hashv; |
1250 | hashv["test" ] = QVariant::fromValue(value: 1); |
1251 | hashv["test2" ] = QVariant::fromValue(value: QString("test2 string" )); |
1252 | hashv["test3" ] = QVariant::fromValue(value: true); |
1253 | QHashIterator<QString, QVariant> i(hashv); |
1254 | output.clear(); |
1255 | QStringList lines; |
1256 | while (i.hasNext()) { |
1257 | i.next(); |
1258 | QString line = i.key(); |
1259 | line += "=" ; |
1260 | line += i.value().toString(); |
1261 | lines << line; |
1262 | } |
1263 | lines.sort(); |
1264 | output = lines.join(QStringLiteral("," )); |
1265 | expectedHash = qHash(key: output); |
1266 | |
1267 | QMetaObject::invokeMethod(obj: serviceUnique, member: "testSlotWithComplexArg" , |
1268 | Q_ARG(QVariantHash, hashv)); |
1269 | QMetaObject::invokeMethod(obj: service, member: "slotConfirmation" , |
1270 | Q_RETURN_ARG(uint, hash)); |
1271 | QCOMPARE(hash, expectedHash); |
1272 | |
1273 | |
1274 | } |
1275 | |
1276 | void tst_QServiceManager_IPC::verifyServiceClass() |
1277 | { |
1278 | QRemoteServiceRegister *registerObject = new QRemoteServiceRegister(); |
1279 | |
1280 | QVERIFY2(registerObject->quitOnLastInstanceClosed() == true, "should default to true, default is to shutdown" ); |
1281 | registerObject->setQuitOnLastInstanceClosed(false); |
1282 | QVERIFY2(registerObject->quitOnLastInstanceClosed() == false, "must transition to false" ); |
1283 | registerObject->setQuitOnLastInstanceClosed(true); |
1284 | QVERIFY2(registerObject->quitOnLastInstanceClosed() == true, "must transition back to true" ); |
1285 | |
1286 | delete registerObject; |
1287 | } |
1288 | |
1289 | void tst_QServiceManager_IPC::verifyLargeDataTransfer() |
1290 | { |
1291 | QFETCH(QByteArray, data); |
1292 | |
1293 | QMetaObject::invokeMethod(obj: serviceUnique, member: "testSlotWithData" , |
1294 | Q_ARG(QByteArray, data)); |
1295 | QByteArray ret_data; |
1296 | QMetaObject::invokeMethod(obj: serviceUnique, member: "testInvoableWithReturnData" , Q_RETURN_ARG(QByteArray, ret_data)); |
1297 | |
1298 | QVERIFY2(data.length() == ret_data.length(), "Returned data from the service is not the same length as data sent" ); |
1299 | QVERIFY2(data == ret_data, "Returned data from service does not match" ); |
1300 | } |
1301 | |
1302 | void tst_QServiceManager_IPC::verifyLargeDataTransfer_data() |
1303 | { |
1304 | QTest::addColumn<QByteArray>(name: "data" ); |
1305 | |
1306 | QTest::newRow(dataTag: "Trivial" ) << QByteArray("c" ); |
1307 | QTest::newRow(dataTag: "1k" ) << QByteArray(1024, 'A'); |
1308 | QTest::newRow(dataTag: "2k" ) << QByteArray(2048, 'A'); |
1309 | QTest::newRow(dataTag: "4k" ) << QByteArray(4096, 'A'); |
1310 | QTest::newRow(dataTag: "8k" ) << QByteArray(8192, 'A'); |
1311 | QTest::newRow(dataTag: "16k" ) << QByteArray(16384, 'A'); |
1312 | QTest::newRow(dataTag: "32k" ) << QByteArray(32768, 'A'); |
1313 | QTest::newRow(dataTag: "64k" ) << QByteArray(65536, 'A'); |
1314 | QTest::newRow(dataTag: "128k" ) << QByteArray(131072, 'A'); |
1315 | QTest::newRow(dataTag: "1 megabyte" ) << QByteArray(1048576, 'A'); |
1316 | QTest::newRow(dataTag: "Free mem" ) << QByteArray("" ); |
1317 | } |
1318 | |
1319 | class FetchLotsOfProperties : public QThread |
1320 | { |
1321 | Q_OBJECT |
1322 | public: |
1323 | FetchLotsOfProperties(int runs, bool shared, QObject *parent = 0); |
1324 | void run(); |
1325 | |
1326 | public slots: |
1327 | void startFetches(); |
1328 | void printProgress(); |
1329 | |
1330 | private slots: |
1331 | void setup(); |
1332 | void fetchProperty(); |
1333 | void ipcError(); |
1334 | |
1335 | signals: |
1336 | void ready(); |
1337 | void error(); |
1338 | |
1339 | private: |
1340 | int runs; |
1341 | int start_runs; |
1342 | bool shared; |
1343 | QString lastValue; |
1344 | QObject *service; |
1345 | QServiceManager *mgr; |
1346 | }; |
1347 | |
1348 | FetchLotsOfProperties::FetchLotsOfProperties(int runs, bool shared, QObject *parent) |
1349 | : QThread(parent), |
1350 | service(0), |
1351 | mgr(0) |
1352 | { |
1353 | this->runs = runs; |
1354 | this->shared = shared; |
1355 | this->start_runs = runs; |
1356 | } |
1357 | |
1358 | void FetchLotsOfProperties::run() |
1359 | { |
1360 | QMetaObject::invokeMethod(obj: this, member: "setup" , type: Qt::QueuedConnection); |
1361 | exec(); |
1362 | } |
1363 | |
1364 | void FetchLotsOfProperties::setup() |
1365 | { |
1366 | mgr = new QServiceManager(this); |
1367 | |
1368 | if (!mgr) { |
1369 | qWarning() << "Failed to create a manager" ; |
1370 | emit error(); |
1371 | exit(retcode: -1); |
1372 | } |
1373 | |
1374 | QList<QServiceInterfaceDescriptor> list = mgr->findInterfaces(serviceName: "IPCExampleService" ); |
1375 | if (list.isEmpty()) { |
1376 | qWarning() << "Thread failed to query the database, or the database contained no services" ; |
1377 | emit error(); |
1378 | exit(retcode: -1); |
1379 | return; |
1380 | } |
1381 | foreach (const QServiceInterfaceDescriptor &d, list) { |
1382 | if (d.majorVersion() == 3 && d.minorVersion() == 5 && !shared) { |
1383 | service = mgr->loadInterface(descriptor: d); |
1384 | } |
1385 | if (d.majorVersion() == 3 && d.minorVersion() == 4 && shared) { |
1386 | service = mgr->loadInterface(descriptor: d); |
1387 | } |
1388 | if (service) |
1389 | connect(sender: service, SIGNAL(errorUnrecoverableIPCFault(QService::UnrecoverableIPCError)), receiver: this, SLOT(ipcError())); |
1390 | } |
1391 | |
1392 | if (!service) { |
1393 | qWarning() << "Thread failed to load service" ; |
1394 | emit error(); |
1395 | exit(retcode: -1); |
1396 | return; |
1397 | } |
1398 | |
1399 | connect(sender: this, SIGNAL(destroyed()), receiver: service, SLOT(deleteLater())); |
1400 | |
1401 | lastValue = service->property(name: "value" ).toString(); |
1402 | |
1403 | emit ready(); |
1404 | } |
1405 | |
1406 | void FetchLotsOfProperties::startFetches() |
1407 | { |
1408 | if (!mgr || !service) { |
1409 | exit(retcode: -1); |
1410 | } |
1411 | |
1412 | if (runs > 0) { |
1413 | QMetaObject::invokeMethod(obj: this, member: "fetchProperty" , type: Qt::QueuedConnection); |
1414 | } |
1415 | else { |
1416 | quit(); |
1417 | } |
1418 | } |
1419 | |
1420 | void FetchLotsOfProperties::printProgress() |
1421 | { |
1422 | qWarning() << "Thread has" << runs << "left, started with" << start_runs; |
1423 | } |
1424 | |
1425 | void FetchLotsOfProperties::fetchProperty() |
1426 | { |
1427 | if (!service) { |
1428 | emit error(); |
1429 | exit(retcode: -1); |
1430 | } |
1431 | |
1432 | QString value = service->property(name: "value" ).toString(); |
1433 | if (value != lastValue) { |
1434 | qWarning() << "Value changed on read! Got" << value << "wanted" << lastValue; |
1435 | emit error(); |
1436 | exit(retcode: -1); |
1437 | } |
1438 | |
1439 | if (runs-- > 0) { |
1440 | QMetaObject::invokeMethod(obj: this, member: "fetchProperty" , type: Qt::QueuedConnection); |
1441 | } |
1442 | else { |
1443 | quit(); |
1444 | } |
1445 | } |
1446 | |
1447 | void FetchLotsOfProperties::ipcError() |
1448 | { |
1449 | |
1450 | } |
1451 | |
1452 | void tst_QServiceManager_IPC::verifyThreadSafety() |
1453 | { |
1454 | QFETCH(int, shared); |
1455 | QFETCH(int, runs); |
1456 | QFETCH(int, threads); |
1457 | |
1458 | QSignalSpy ready(this, SIGNAL(threadReady())); |
1459 | QSignalSpy finished(this, SIGNAL(threadFinished())); |
1460 | QSignalSpy errors(this, SIGNAL(threadError())); |
1461 | bool mixed = true; |
1462 | for (int i = 0; i < threads; i++) { |
1463 | switch (shared) { |
1464 | case 0: |
1465 | mixed = false; |
1466 | break; |
1467 | case 1: |
1468 | mixed = true; |
1469 | break; |
1470 | case 2: |
1471 | mixed = !mixed; |
1472 | } |
1473 | FetchLotsOfProperties *fetch = new FetchLotsOfProperties(runs, mixed, this); |
1474 | connect(sender: this, SIGNAL(threadRun()), receiver: fetch, SLOT(startFetches())); |
1475 | connect(sender: fetch, SIGNAL(ready()), receiver: this, SIGNAL(threadReady())); |
1476 | connect(sender: fetch, SIGNAL(error()), receiver: this, SIGNAL(threadError())); |
1477 | connect(sender: fetch, SIGNAL(finished()), receiver: this, SIGNAL(threadFinished())); |
1478 | connect(sender: fetch, SIGNAL(finished()), receiver: fetch, SLOT(deleteLater())); |
1479 | connect(sender: this, SIGNAL(threadRemaining()), receiver: fetch, SLOT(printProgress())); |
1480 | fetch->start(); |
1481 | } |
1482 | |
1483 | |
1484 | #define THREAD_TIMEOUT 30*1000 |
1485 | |
1486 | QTimer timer; |
1487 | timer.setInterval(THREAD_TIMEOUT); |
1488 | timer.setSingleShot(true); |
1489 | timer.start(); |
1490 | |
1491 | QTime start = QTime::currentTime(); |
1492 | |
1493 | while (((ready.count()) < threads) && (timer.isActive())) { |
1494 | QCOMPARE(errors.count(), 0); |
1495 | QCoreApplication::processEvents(); |
1496 | QCoreApplication::sendPostedEvents(receiver: 0, event_type: QEvent::DeferredDelete); |
1497 | } |
1498 | QTRY_COMPARE(ready.count(), threads); |
1499 | QVERIFY2(!errors.count(), "Threads reported errors on setup" ); |
1500 | emit threadRun(); |
1501 | |
1502 | qDebug() << "Waiting on" << threads << "threads to finish" ; |
1503 | |
1504 | while (((finished.count() + errors.count()) < threads) && (timer.isActive())) { |
1505 | QCOMPARE(errors.count(), 0); |
1506 | QCoreApplication::processEvents(); |
1507 | QCoreApplication::sendPostedEvents(receiver: 0, event_type: QEvent::DeferredDelete); |
1508 | } |
1509 | |
1510 | qDebug() << "Waited" << start.secsTo(QTime::currentTime()) << "seconds" ; |
1511 | |
1512 | if (finished.count() != threads){ |
1513 | emit threadRemaining(); |
1514 | } |
1515 | |
1516 | QCOMPARE(finished.count(), threads); |
1517 | QVERIFY2(!errors.count(), "Threads reported errors" ); |
1518 | } |
1519 | |
1520 | void tst_QServiceManager_IPC::verifyThreadSafety_data() |
1521 | { |
1522 | QTest::addColumn<int>(name: "shared" ); |
1523 | QTest::addColumn<int>(name: "runs" ); |
1524 | QTest::addColumn<int>(name: "threads" ); |
1525 | |
1526 | QTest::newRow(dataTag: "unique, 2 threads" ) << 0 << 50 << 2; |
1527 | QTest::newRow(dataTag: "unique, 4 threads" ) << 0 << 25 << 4; |
1528 | QTest::newRow(dataTag: "unique, 16 threads" ) << 0 << 5 << 16; |
1529 | QTest::newRow(dataTag: "shared, 2 threads" ) << 1 << 50 << 2; |
1530 | QTest::newRow(dataTag: "shared, 4 threads" ) << 1 << 25 << 4; |
1531 | QTest::newRow(dataTag: "shared, 16 threads" ) << 1 << 5 << 16; |
1532 | QTest::newRow(dataTag: "mixed, 16 threads" ) << 2 << 5 << 16; |
1533 | #ifndef Q_OS_WIN // Limit on event notifiers. |
1534 | QTest::newRow(dataTag: "unique, 128 threads" ) << 0 << 5 << 128; |
1535 | QTest::newRow(dataTag: "shared, 128 threads" ) << 1 << 5 << 128; |
1536 | QTest::newRow(dataTag: "mixed, 128 threads" ) << 2 << 5 << 128; |
1537 | #endif // Q_OS_WIN |
1538 | } |
1539 | |
1540 | void tst_QServiceManager_IPC::unblockRemote() |
1541 | { |
1542 | QString ret = serviceUnique->property(name: "releaseBlockingRead" ).toString(); |
1543 | QCOMPARE(ret, QStringLiteral("releaseBlockingReadReturned" )); |
1544 | } |
1545 | |
1546 | void tst_QServiceManager_IPC::verifyRemoteBlockingFunctions() |
1547 | { |
1548 | #ifdef SFW_USE_DBUS_BACKEND |
1549 | QEXPECT_FAIL("" , "Test case known to segfault on dbus, skipping, QTBUG-23168" , Abort); |
1550 | QVERIFY(false); |
1551 | #else |
1552 | connect(serviceUnique, SIGNAL(blockingValueRead()), this, SLOT(unblockRemote())); |
1553 | |
1554 | QString ret = serviceUnique->property("blockingValue" ).toString(); |
1555 | QCOMPARE(ret, QStringLiteral("blockingValueReturned" )); |
1556 | #endif |
1557 | } |
1558 | |
1559 | void tst_QServiceManager_IPC::testSignalSlotOrdering() |
1560 | { |
1561 | QSignalSpy spy(serviceUnique, SIGNAL(count(int))); |
1562 | |
1563 | int value = -1; |
1564 | bool ret = QMetaObject::invokeMethod(obj: serviceUnique, member: "testSignalSlotOrdering" , Q_RETURN_ARG(int, value)); |
1565 | QVERIFY2(ret == true, "Verify slot call worked" ); |
1566 | QVERIFY2(value == 0, "Verify slot return value" ); |
1567 | QVERIFY2(spy.count() == 0, "Verify no signals fired before the invokeMethod returns" ); |
1568 | QTRY_COMPARE(spy.count(), 20); |
1569 | } |
1570 | |
1571 | void tst_QServiceManager_IPC::testServiceSecurity() |
1572 | { |
1573 | |
1574 | QServiceManager* manager = new QServiceManager(this); |
1575 | QList<QServiceInterfaceDescriptor> list = manager->findInterfaces(serviceName: "IPCExampleService" ); |
1576 | QServiceInterfaceDescriptor d, dPriv, dGlobal; |
1577 | foreach (QServiceInterfaceDescriptor d, list){ |
1578 | if (d.majorVersion() == 3 && d.minorVersion() == 9) { |
1579 | dPriv = d; |
1580 | } |
1581 | if (d.majorVersion() == 3 && d.minorVersion() == 10) { |
1582 | dGlobal = d; |
1583 | } |
1584 | } |
1585 | |
1586 | QVERIFY(dPriv.isValid()); |
1587 | QVERIFY(dGlobal.isValid()); |
1588 | |
1589 | // Rejects all connections |
1590 | QObject *o = manager->loadInterface(descriptor: dPriv); |
1591 | QVERIFY2(o == 0, "Everything should fail security/creation with this interface" ); |
1592 | |
1593 | o = manager->loadInterface(descriptor: dGlobal); |
1594 | QVERIFY2(o, "Global class should allow the first connection" ); |
1595 | |
1596 | bool status; |
1597 | // make sync call to ensure remote is in the same state |
1598 | QMetaObject::invokeMethod(obj: o, member: "disableConnections" , Q_RETURN_ARG(bool, status), Q_ARG(bool, true)); |
1599 | QCOMPARE(status, true); |
1600 | |
1601 | // expect a null object back |
1602 | QObject *second = manager->loadInterface(descriptor: dGlobal); |
1603 | QVERIFY2(second == 0, "Security checks should no longer allow objects to be created" ); |
1604 | |
1605 | // make sync call to ensure remote is in the same state |
1606 | QMetaObject::invokeMethod(obj: o, member: "disableConnections" , Q_RETURN_ARG(bool, status), Q_ARG(bool, false)); |
1607 | |
1608 | second = manager->loadInterface(descriptor: dGlobal); |
1609 | QVERIFY2(second != 0, "Object creation should be allowed" ); |
1610 | |
1611 | delete second; |
1612 | |
1613 | } |
1614 | |
1615 | void tst_QServiceManager_IPC::testProcessLaunch() |
1616 | { |
1617 | } |
1618 | |
1619 | void tst_QServiceManager_IPC::verifyAsyncLoading() |
1620 | { |
1621 | |
1622 | QFETCH(QString, serviceInterface); |
1623 | QFETCH(int, descriptor); |
1624 | QFETCH(bool, errors); |
1625 | QFETCH(int, simultaneous); |
1626 | |
1627 | QServiceManager mgr; |
1628 | |
1629 | QServiceReply *reply = 0; |
1630 | QServiceReply *replies[16]; // must be constant for windows, set to max size below |
1631 | |
1632 | if (simultaneous > 0) { |
1633 | for (int i = 0; i < simultaneous; i++) { |
1634 | replies[i] = mgr.loadInterfaceRequest(interfaceName: serviceInterface); |
1635 | } |
1636 | reply = replies[0]; |
1637 | } else if (descriptor > 0) { |
1638 | QList<QServiceInterfaceDescriptor> list = mgr.findInterfaces(serviceName: "IPCExampleService" ); |
1639 | QServiceInterfaceDescriptor d; |
1640 | foreach (d, list) { |
1641 | if (d.majorVersion() == 3 && d.minorVersion() == descriptor) { |
1642 | break; |
1643 | } |
1644 | } |
1645 | reply = mgr.loadInterfaceRequest(descriptor: d); |
1646 | } else if (!serviceInterface.isEmpty()) { |
1647 | reply = mgr.loadInterfaceRequest(interfaceName: serviceInterface); |
1648 | } else { |
1649 | QFAIL("No descriptor nor interface speficied" ); |
1650 | } |
1651 | |
1652 | |
1653 | QSignalSpy startedSpy(reply, SIGNAL(started())); |
1654 | QSignalSpy errorSpy(reply, SIGNAL(errorChanged())); |
1655 | QSignalSpy finishedSpy(reply, SIGNAL(finished())); |
1656 | |
1657 | QCOMPARE(startedSpy.count(), 0); |
1658 | |
1659 | QTRY_COMPARE(startedSpy.count(), 1); |
1660 | if (!errors) |
1661 | QCOMPARE(errorSpy.count(), 0); |
1662 | |
1663 | QTRY_COMPARE(finishedSpy.count(), 1); |
1664 | QCOMPARE(reply->isFinished(), true); |
1665 | if (!errors) { |
1666 | QCOMPARE(errorSpy.count(), 0); |
1667 | QCOMPARE(reply->error(), QServiceManager::NoError); |
1668 | } else { |
1669 | QCOMPARE(errorSpy.count(), 1); |
1670 | QVERIFY(reply->error() != QServiceManager::NoError); |
1671 | } |
1672 | |
1673 | |
1674 | |
1675 | if (simultaneous) { |
1676 | for (int i = 0; i < simultaneous; i++) { |
1677 | QTRY_COMPARE(replies[i]->isFinished(), true); |
1678 | QCOMPARE(replies[i]->error(), QServiceManager::NoError); |
1679 | } |
1680 | } |
1681 | |
1682 | delete reply; |
1683 | } |
1684 | |
1685 | void tst_QServiceManager_IPC::verifyAsyncLoading_data() |
1686 | { |
1687 | QTest::addColumn<QString>(name: "serviceInterface" ); |
1688 | QTest::addColumn<int>(name: "descriptor" ); |
1689 | QTest::addColumn<bool>(name: "errors" ); |
1690 | QTest::addColumn<int>(name: "simultaneous" ); |
1691 | |
1692 | |
1693 | QTest::newRow(dataTag: "Load minor version 5" ) << QString() << 5 << false << 0; |
1694 | QTest::newRow(dataTag: "Load default interface" ) << QString("com.nokia.qt.ipcunittest" ) << 0 << false << 0; |
1695 | QTest::newRow(dataTag: "Load invalid interface" ) << QString("com.nokia.qt.ipcunittest.does.not.exist" ) << 0 << true << 0; |
1696 | QTest::newRow(dataTag: "Load 4 interfaces" ) << QString("com.nokia.qt.ipcunittest" ) << 1 << false << 4; |
1697 | QTest::newRow(dataTag: "Load 16 interfaces" ) << QString("com.nokia.qt.ipcunittest" ) << 1 << false << 16; |
1698 | |
1699 | } |
1700 | |
1701 | void tst_QServiceManager_IPC::testIpcFailure() |
1702 | { |
1703 | // test deleting an object doesn't trigger an IPC fault |
1704 | ipcfailure = false; |
1705 | delete serviceShared; |
1706 | QVERIFY2(!ipcfailure, "Deleting an object should not cause an IPC failure message" ); |
1707 | serviceShared = 0; |
1708 | |
1709 | ipcfailure = false; |
1710 | QMetaObject::invokeMethod(obj: serviceUnique, member: "testIpcFailure" ); |
1711 | int i = 0; |
1712 | while (!ipcfailure && i++ < 50) |
1713 | QTest::qWait(ms: 50); |
1714 | |
1715 | QVERIFY(ipcfailure); |
1716 | |
1717 | // TODO restart the connection |
1718 | //initTestCase(); |
1719 | } |
1720 | |
1721 | void tst_QServiceManager_IPC::verifyFailures() |
1722 | { |
1723 | bool result; |
1724 | |
1725 | QServiceManager* manager = new QServiceManager(this); |
1726 | QList<QServiceInterfaceDescriptor> list = manager->findInterfaces(serviceName: "IPCExampleService" ); |
1727 | QServiceInterfaceDescriptor d; |
1728 | foreach (d, list){ |
1729 | if (d.majorVersion() == 3 && d.minorVersion() == 6) { |
1730 | QObject *o = manager->loadInterface(descriptor: d); |
1731 | QVERIFY2(o == 0, "Failure to allocate remote object returns null" ); |
1732 | } |
1733 | if (d.majorVersion() == 3 && d.minorVersion() == 7) { |
1734 | QObject *o = manager->loadInterface(descriptor: d); |
1735 | QVERIFY2(o == 0, "Failure to allocate remote object returns null" ); |
1736 | } |
1737 | } |
1738 | |
1739 | QMetaObject::invokeMethod(obj: miscTest, member: "addTwice" , |
1740 | Q_RETURN_ARG(bool, result)); |
1741 | QVERIFY2(result, "Added the same service twice, returned different entries" ); |
1742 | |
1743 | QMetaObject::invokeMethod(obj: miscTest, member: "getInvalidEntry" , |
1744 | Q_RETURN_ARG(bool, result)); |
1745 | QVERIFY2(result, "Invalid entry returns invalid meta data" ); |
1746 | |
1747 | delete manager; |
1748 | |
1749 | } |
1750 | |
1751 | |
1752 | |
1753 | QTEST_MAIN(tst_QServiceManager_IPC); |
1754 | #include "tst_qservicemanager_ipc.moc" |
1755 | |