1 /*=============================================================================
2 
3   Library: CTK
4 
5   Copyright (c) German Cancer Research Center,
6     Division of Medical and Biological Informatics
7 
8   Licensed under the Apache License, Version 2.0 (the "License");
9   you may not use this file except in compliance with the License.
10   You may obtain a copy of the License at
11 
12     http://www.apache.org/licenses/LICENSE-2.0
13 
14   Unless required by applicable law or agreed to in writing, software
15   distributed under the License is distributed on an "AS IS" BASIS,
16   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   See the License for the specific language governing permissions and
18   limitations under the License.
19 
20 =============================================================================*/
21 
22 
23 #include "ctkServiceListenerTestSuite_p.h"
24 
25 #include <ctkPlugin.h>
26 #include <ctkPluginContext.h>
27 #include <ctkPluginException.h>
28 #include <ctkPluginConstants.h>
29 #include <ctkServiceEvent.h>
30 #include <ctkServiceException.h>
31 
32 #include <ctkPluginFrameworkTestUtil.h>
33 
34 #include <QTest>
35 
36 //----------------------------------------------------------------------------
ctkServiceListenerTestSuite(ctkPluginContext * pc)37 ctkServiceListenerTestSuite::ctkServiceListenerTestSuite(ctkPluginContext* pc)
38   : pc(pc), p(pc->getPlugin())
39 {
40 }
41 
42 //----------------------------------------------------------------------------
initTestCase()43 void ctkServiceListenerTestSuite::initTestCase()
44 {
45   pA = ctkPluginFrameworkTestUtil::installPlugin(pc, "pluginA_test");
46   QVERIFY(pA);
47   pA2 = ctkPluginFrameworkTestUtil::installPlugin(pc, "pluginA2_test");
48   QVERIFY(pA2);
49   pSL1 = ctkPluginFrameworkTestUtil::installPlugin(pc, "pluginSL1_test");
50   QVERIFY(pSL1);
51   pSL3 = ctkPluginFrameworkTestUtil::installPlugin(pc, "pluginSL3_test");
52   QVERIFY(pSL3);
53   pSL4 = ctkPluginFrameworkTestUtil::installPlugin(pc, "pluginSL4_test");
54   QVERIFY(pSL4);
55 }
56 
57 //----------------------------------------------------------------------------
cleanupTestCase()58 void ctkServiceListenerTestSuite::cleanupTestCase()
59 {
60   pA->uninstall();
61   pA2->uninstall();
62   pSL1->uninstall();
63   pSL3->uninstall();
64   pSL4->uninstall();
65 }
66 
67 //----------------------------------------------------------------------------
frameSL05a()68 void ctkServiceListenerTestSuite::frameSL05a()
69 {
70   bool teststatus = true;
71   int cnt = 1;
72 
73   QList<ctkServiceEvent::Type> events;
74   events << ctkServiceEvent::REGISTERED;
75   events << ctkServiceEvent::UNREGISTERING;
76   teststatus = runStartStopTest("FRAMEsl05A", cnt, pA, events);
77 
78   QVERIFY(teststatus);
79 }
80 
81 //----------------------------------------------------------------------------
frameSL10a()82 void ctkServiceListenerTestSuite::frameSL10a()
83 {
84   int cnt = 1;
85 
86   QList<ctkServiceEvent::Type> events;
87   events << ctkServiceEvent::REGISTERED;
88   events << ctkServiceEvent::UNREGISTERING;
89   QVERIFY(runStartStopTest("FRAMEsl10A", cnt, pA2,events));
90 }
91 
92 //----------------------------------------------------------------------------
frameSL25a()93 void ctkServiceListenerTestSuite::frameSL25a()
94 {
95   ctkServiceListener sListen(pc, false);
96   try
97   {
98     pc->connectServiceListener(&sListen, "serviceChanged");
99   }
100   catch (const ctkIllegalStateException& ise)
101   {
102     qDebug() << "service listener registration failed " << ise.what();
103     QFAIL("service listener registration failed");
104   }
105 
106   QList<ctkServiceEvent::Type> expectedServiceEventTypes;
107 
108   // Startup
109   expectedServiceEventTypes << ctkServiceEvent::REGISTERED; // ctkActivator at start of pSL1
110   expectedServiceEventTypes << ctkServiceEvent::REGISTERED; // ctkFooService at start of pSL4
111   expectedServiceEventTypes << ctkServiceEvent::REGISTERED; // ctkActivator at start of pSL3
112 
113   // Stop pSL4
114   expectedServiceEventTypes << ctkServiceEvent::UNREGISTERING; // ctkFooService at first stop of pSL4
115 
116   // Shutdown
117   expectedServiceEventTypes << ctkServiceEvent::UNREGISTERING; // ctkActivator at stop of pSL1
118   expectedServiceEventTypes << ctkServiceEvent::UNREGISTERING; // Activator at stop of pSL3
119 
120 
121   // Start pSL1 to ensure that the Service interface is available.
122   try
123   {
124     qDebug() << "Starting pSL1: " << pSL1;
125     pSL1->start();
126   }
127   catch (const ctkPluginException& pex)
128   {
129     qDebug() << "Failed to start plugin, got exception:" << pex;
130     QFAIL("Failed to start bundle, got exception: ctkPluginException");
131   }
132   catch (const std::exception& e)
133   {
134     qDebug() << "Failed to start plugin, got exception" << e.what();
135     QFAIL("Failed to start plugin, got exception: std::exception");
136   }
137 
138   // Start pSL4 that will require the serivce interface and publish
139   // ctkFooService
140   try
141   {
142     qDebug() << "Starting pSL4:" << pSL4;
143     pSL4->start();
144   }
145   catch (const ctkPluginException& pex)
146   {
147     qDebug() << "Failed to start plugin, got exception:" << pex;
148     QFAIL("Failed to start bundle, got exception: ctkPluginException");
149   }
150   catch (const std::exception& e)
151   {
152     qDebug() << "Failed to start plugin, got exception" << e.what();
153     QFAIL("Failed to start plubin, got exception.");
154   }
155 
156   // Start buSL3 that will require the serivce interface and get the service
157   try
158   {
159     qDebug() << "Starting pSL3:" << pSL3;
160     pSL3->start();
161   }
162   catch (const ctkPluginException& pex)
163   {
164     qDebug() << "Failed to start plugin, got exception:" << pex;
165     QFAIL("Failed to start plugin, got exception: ctkPluginException");
166   }
167   catch (const std::exception& e)
168   {
169     qDebug() << "Failed to start plugin, got exception" << e.what();
170     QFAIL("Failed to start plugin, got exception.");
171   }
172 
173   // sleep to stabelize state.
174   QTest::qWait(300);
175 
176   // Check that pSL3 has been notified about the ctkFooService.
177   qDebug() << "Check that ctkFooService is added to service tracker in pSL3";
178   try
179   {
180     ctkServiceReference pSL3SR
181         = pc->getServiceReference("ctkActivatorSL3");
182     QObject* pSL3Activator = pc->getService(pSL3SR);
183     QVERIFY2(pSL3Activator, "No activator service.");
184 
185     QVariant serviceAddedField3 = pSL3Activator->property("serviceAdded");
186     QVERIFY2(serviceAddedField3.isValid() && serviceAddedField3.toBool(),
187              "pluginSL3 not notified about presence of ctkFooService");
188     qDebug() << "pSL3Activator::serviceAdded is true";
189     pc->ungetService(pSL3SR);
190   }
191   catch (const ctkServiceException& se)
192   {
193     qDebug() << "Failed to get service reference:" << se;
194     QFAIL("No activator service reference.");
195   }
196 
197   // Check that pSL1 has been notified about the ctkFooService.
198   qDebug() << "Check that ctkFooService is added to service tracker in pSL1";
199   try
200   {
201     ctkServiceReference pSL1SR
202         = pc->getServiceReference("ctkActivatorSL1");
203     QObject* pSL1Activator = pc->getService(pSL1SR);
204     QVERIFY2(pSL1Activator, "No activator service.");
205 
206     QVariant serviceAddedField = pSL1Activator->property("serviceAdded");
207     QVERIFY2(serviceAddedField.isValid() && serviceAddedField.toBool(),
208              "bundleSL1 not notified about presence of ctkFooService");
209     qDebug() << "pSL1Activator::serviceAdded is true";
210     pc->ungetService(pSL1SR);
211   }
212   catch (const ctkServiceException& se)
213   {
214     qDebug() << "Failed to get service reference:" << se;
215     QFAIL("No activator service reference.");
216   }
217 
218   // Stop the service provider: pSL4
219   try
220   {
221     qDebug() << "Stop pSL4:" << pSL4;
222     pSL4->stop();
223   }
224   catch (const ctkPluginException& pex)
225   {
226     qDebug() << "Failed to stop plugin, got exception:" << pex;
227     QFAIL("Failed to stop bundle, got exception: ctkPluginException");
228   }
229   catch (const std::exception& e)
230   {
231     qDebug() << "Failed to stop plugin, got exception:" << e.what();
232     QFAIL("Failed to stop plugin, got exception.");
233   }
234 
235   // sleep to stabelize state.
236   QTest::qWait(300);
237 
238 
239   // Check that pSL3 has been notified about the removal of ctkFooService.
240   qDebug() << "Check that ctkFooService is removed from service tracker in pSL3";
241   try
242   {
243     ctkServiceReference pSL3SR
244         = pc->getServiceReference("ctkActivatorSL3");
245     QObject* pSL3Activator = pc->getService(pSL3SR);
246     QVERIFY2(pSL3Activator, "No activator service.");
247     QVariant serviceRemovedField3 = pSL3Activator->property("serviceRemoved");
248     QVERIFY2(serviceRemovedField3.isValid() && serviceRemovedField3.toBool(),
249              "pluginSL3 not notified about removal of ctkFooService");
250     qDebug() << "pSL3Activator::serviceRemoved is true";
251     pc->ungetService(pSL3SR);
252   }
253   catch (const ctkServiceException& se)
254   {
255     qDebug() << "Failed to get service reference:" << se;
256     QFAIL("No activator service reference.");
257   }
258 
259   // Stop pSL1
260   try
261   {
262     qDebug() << "Stop pSL1:" << pSL1;
263     pSL1->stop();
264   }
265   catch (const ctkPluginException& pex)
266   {
267     qDebug() << "Failed to stop plugin, got exception:" << pex;
268     QFAIL("Failed to stop bundle, got exception: ctkPluginException");
269   }
270   catch (const std::exception& e)
271   {
272     qDebug() << "Failed to stop plugin, got exception" << e.what();
273     QFAIL("Failed to stop plugin, got exception.");
274   }
275 
276   // Stop pSL3
277   try
278   {
279     qDebug() << "Stop pSL3:" << pSL3;
280     pSL3->stop();
281   }
282   catch (const ctkPluginException& pex)
283   {
284     qDebug() << "Failed to stop plugin, got exception:" << pex;
285     QFAIL("Failed to stop plugin, got exception: ctkPluginException");
286   }
287   catch (const std::exception& e)
288   {
289     qDebug() << "Failed to stop plugin, got exception" << e.what();
290     QFAIL("Failed to stop plugin, got exception.");
291   }
292 
293 
294   // sleep to stabelize state.
295   QTest::qWait(300);
296 
297   // Check service events seen by this class
298   qDebug() << "Checking ServiceEvents(ServiceListener):";
299   if (!sListen.checkEvents(expectedServiceEventTypes))
300   {
301     qDebug() << "Service listener event notification error";
302     QFAIL("Service listener event notification error");
303   }
304 
305 
306   QVERIFY2(sListen.teststatus, "Service listener checks");
307   try
308   {
309     //pc->disconnectServiceListener(&sListen, "serviceChanged");
310     sListen.clearEvents();
311   }
312   catch (const ctkIllegalStateException& ise)
313   {
314     qDebug() << ise.what();
315     QFAIL("service listener removal failed ");
316   }
317 }
318 
319 //----------------------------------------------------------------------------
runStartStopTest(const QString & tcName,int cnt,QSharedPointer<ctkPlugin> targetPlugin,const QList<ctkServiceEvent::Type> & events)320 bool ctkServiceListenerTestSuite::runStartStopTest(
321   const QString& tcName, int cnt, QSharedPointer<ctkPlugin> targetPlugin,
322   const QList<ctkServiceEvent::Type>& events)
323 {
324   bool teststatus = true;
325 
326   for (int i = 0; i < cnt && teststatus; ++i)
327   {
328     ctkServiceListener sListen(pc);
329     try
330     {
331       pc->connectServiceListener(&sListen, "serviceChanged");
332     }
333     catch (const ctkIllegalStateException& ise)
334     {
335       teststatus  = false;
336       qDebug() << "service listener registration failed " << ise.what()
337                << " :" << tcName << ":FAIL";
338     }
339 
340     // Start the test target to get a service published.
341     try
342     {
343       qDebug() << "Starting targetPlugin: " << targetPlugin;
344       targetPlugin->start();
345     }
346     catch (const ctkPluginException& pex)
347     {
348       teststatus  = false;
349       qDebug() << "Failed to start plugin, got exception: "
350                << pex.what() << " in " << tcName << ":FAIL";
351     }
352     catch (const std::exception& e)
353     {
354       teststatus  = false;
355       qDebug() << "Failed to start plugin, got exception: "
356                << e.what() << " + in " << tcName << ":FAIL";
357     }
358 
359     // sleep to stabelize state.
360     QTest::qWait(300);
361 
362     // Stop the test target to get a service unpublished.
363     try
364     {
365       targetPlugin->stop();
366     }
367     catch (const ctkPluginException& pex)
368     {
369       teststatus  = false;
370       qDebug() << "Failed to stop plugin, got exception: "
371                << pex.what() << " in " << tcName << ":FAIL";
372     }
373     catch (const std::exception& e)
374     {
375       teststatus  = false;
376       qDebug() << "Failed to stop plugin, got exception: "
377                << e.what() << " + in " << tcName << ":FAIL";
378     }
379 
380     if (teststatus && !sListen.checkEvents(events))
381     {
382       teststatus  = false;
383       qDebug() << "Service listener event notification error :"
384                << tcName << ":FAIL";
385     }
386 
387     try
388     {
389       pc->disconnectServiceListener(&sListen, "serviceChanged");
390       teststatus &= sListen.teststatus;
391       sListen.clearEvents();
392     }
393     catch (const ctkIllegalStateException& ise)
394     {
395       teststatus  = false;
396       qDebug() << "service listener removal failed " << ise.what()
397                << " :" << tcName << ":FAIL";
398     }
399   }
400   return teststatus;
401 }
402 
403 //----------------------------------------------------------------------------
ctkServiceListener(ctkPluginContext * pc,bool checkUsingPlugins)404 ctkServiceListener::ctkServiceListener(ctkPluginContext* pc, bool checkUsingPlugins)
405   : checkUsingPlugins(checkUsingPlugins), teststatus(true), pc(pc)
406 {
407 
408 }
409 
410 //----------------------------------------------------------------------------
clearEvents()411 void ctkServiceListener::clearEvents()
412 {
413   events.clear();
414 }
415 
416 //----------------------------------------------------------------------------
checkEvents(const QList<ctkServiceEvent::Type> & eventTypes)417 bool ctkServiceListener::checkEvents(const QList<ctkServiceEvent::Type>& eventTypes)
418 {
419   if (events.size() != eventTypes.size())
420   {
421     dumpEvents(eventTypes);
422     return false;
423   }
424 
425   for (int i=0; i < eventTypes.size(); ++i)
426   {
427     if (eventTypes[i] != events[i].getType())
428     {
429       dumpEvents(eventTypes);
430       return false;
431     }
432   }
433   return true;
434 }
435 
436 //----------------------------------------------------------------------------
serviceChanged(const ctkServiceEvent & evt)437 void ctkServiceListener::serviceChanged(const ctkServiceEvent& evt)
438 {
439   events.push_back(evt);
440   qDebug() << "ServiceEvent:" << evt;
441   if (ctkServiceEvent::UNREGISTERING == evt.getType())
442   {
443     ctkServiceReference sr = evt.getServiceReference();
444 
445     // Validate that no bundle is marked as using the service
446     QList<QSharedPointer<ctkPlugin> > usingPlugins = sr.getUsingPlugins();
447     if (checkUsingPlugins && !usingPlugins.isEmpty())
448     {
449       teststatus = false;
450       printUsingPlugins(sr, "*** Using plugins (unreg) should be empty but is: ");
451     }
452 
453     // Check if the service can be fetched
454     QObject* service = pc->getService(sr);
455     usingPlugins = sr.getUsingPlugins();
456 //    if (UNREGISTERSERVICE_VALID_DURING_UNREGISTERING) {
457       // In this mode the service shall be obtainable during
458       // unregistration.
459       if (service == 0)
460       {
461         teststatus = false;
462         qDebug() << "*** Service should be available to ServiceListener "
463                  << "while handling unregistering event.";
464       }
465       qDebug() << "Service (unreg): " << service->metaObject()->className();
466       if (checkUsingPlugins && usingPlugins.size() != 1)
467       {
468         teststatus = false;
469         printUsingPlugins(sr, "*** One using plugin expected "
470                           "(unreg, after getService), found: ");
471       }
472       else
473       {
474         printUsingPlugins(sr, "Using plugins (unreg, after getService): ");
475       }
476 //    } else {
477 //      // In this mode the service shall NOT be obtainable during
478 //      // unregistration.
479 //      if (null!=service) {
480 //        teststatus = false;
481 //        out.print("*** Service should not be available to ServiceListener "
482 //                  +"while handling unregistering event.");
483 //      }
484 //      if (checkUsingBundles && null!=usingBundles) {
485 //        teststatus = false;
486 //        printUsingBundles(sr,
487 //                          "*** Using bundles (unreg, after getService), "
488 //                          +"should be null but is: ");
489 //      } else {
490 //        printUsingBundles(sr,
491 //                          "Using bundles (unreg, after getService): null");
492 //      }
493 //    }
494     pc->ungetService(sr);
495 
496     // Check that the UNREGISTERING service can not be looked up
497     // using the service registry.
498     try
499     {
500       qulonglong sid = sr.getProperty(ctkPluginConstants::SERVICE_ID).toLongLong();
501       QString sidFilter = QString("(") + ctkPluginConstants::SERVICE_ID + "=" + sid + ")";
502       QList<ctkServiceReference> srs = pc->getServiceReferences("", sidFilter);
503       if (srs.isEmpty())
504       {
505         qDebug() << "ctkServiceReference for UNREGISTERING service is not"
506                     " found in the service registry; ok.";
507       }
508       else
509       {
510         teststatus = false;
511         qDebug() << "*** ctkServiceReference for UNREGISTERING service,"
512                  << sr << ", not found in the service registry; fail.";
513         qDebug() << "Found the following Service references:";
514         foreach(ctkServiceReference sr, srs)
515         {
516           qDebug() << sr;
517         }
518       }
519     }
520     catch (const std::exception& e)
521     {
522       teststatus = false;
523       qDebug() << "*** Unexpected excpetion when trying to lookup a"
524                   " service while it is in state UNREGISTERING;"
525                << e.what();
526     }
527   }
528 }
529 
530 //----------------------------------------------------------------------------
printUsingPlugins(const ctkServiceReference & sr,const QString & caption)531 void ctkServiceListener::printUsingPlugins(const ctkServiceReference& sr,
532                                            const QString& caption)
533 {
534   QList<QSharedPointer<ctkPlugin> > usingPlugins = sr.getUsingPlugins();
535 
536   qDebug() << (caption.isEmpty() ? "Using plugins: " : caption);
537   foreach(QSharedPointer<ctkPlugin> plugin, usingPlugins)
538   {
539     qDebug() << "  -" << plugin.data();
540   }
541 }
542 
543 //----------------------------------------------------------------------------
dumpEvents(const QList<ctkServiceEvent::Type> & eventTypes)544 void ctkServiceListener::dumpEvents(const QList<ctkServiceEvent::Type>& eventTypes)
545 {
546   int max = events.size() > eventTypes.size() ? events.size() : eventTypes.size();
547   qDebug() << "Expected event type --  Actual event";
548   for (int i=0; i < max; ++i)
549   {
550     ctkServiceEvent evt = i < events.size() ? events[i] : ctkServiceEvent();
551     if (i < eventTypes.size())
552     {
553       qDebug() << " " << eventTypes[i] << "--" << evt;
554     }
555     else
556     {
557       qDebug() << " " << "- NONE - " << "--" << evt;
558     }
559   }
560 }
561