ctkCmdLineModuleManagerTest.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. /*=============================================================================
  2. Library: CTK
  3. Copyright (c) German Cancer Research Center,
  4. Division of Medical and Biological Informatics
  5. Licensed under the Apache License, Version 2.0 (the "License");
  6. you may not use this file except in compliance with the License.
  7. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. =============================================================================*/
  15. #include "ctkCmdLineModuleManager.h"
  16. #include "ctkCmdLineModuleBackend.h"
  17. #include "ctkException.h"
  18. #include "ctkCmdLineModuleFuture.h"
  19. #include "ctkCmdLineModuleReferenceResult.h"
  20. #include <ctkCmdLineModuleConcurrentHelpers.h>
  21. #include <ctkCmdLineModuleTimeoutException.h>
  22. #include "ctkUtils.h"
  23. #include "ctkTest.h"
  24. #include <QCoreApplication>
  25. #include <QBuffer>
  26. #include <QDataStream>
  27. #include <QDebug>
  28. #if (QT_VERSION < QT_VERSION_CHECK(4,7,0))
  29. extern int qHash(const QUrl& url);
  30. #endif
  31. namespace {
  32. class BackendMockUp : public ctkCmdLineModuleBackend
  33. {
  34. public:
  35. void addModule(const QUrl& location, const QByteArray& xml, int msDelay = 0)
  36. {
  37. this->m_UrlToXmlRetrievalCount[location] = 0;
  38. this->m_UrlToXml[location] = xml;
  39. this->m_UrlToXmlOutputDelay[location] = msDelay;
  40. }
  41. void setTimestamp(const QUrl& location, qint64 timestamp)
  42. {
  43. this->m_UrlToTimestamp[location] = timestamp;
  44. }
  45. QList<QUrl> moduleLocations() const
  46. {
  47. return this->m_UrlToXml.keys();
  48. }
  49. int xmlRetrievalCount(const QUrl& location) const
  50. {
  51. QHash<QUrl,int>::ConstIterator iter = this->m_UrlToXmlRetrievalCount.find(location);
  52. return iter == this->m_UrlToXmlRetrievalCount.end() ? 0 : iter.value();
  53. }
  54. virtual QString name() const { return "Mockup"; }
  55. virtual QString description() const { return "Test Mock-up"; }
  56. virtual QList<QString> schemes() const { return QList<QString>() << "test"; }
  57. virtual qint64 timeStamp(const QUrl& location) const
  58. {
  59. QHash<QUrl,qint64>::ConstIterator iter = this->m_UrlToTimestamp.find(location);
  60. return iter == this->m_UrlToTimestamp.end() ? 0 : iter.value();
  61. }
  62. virtual QByteArray rawXmlDescription(const QUrl& location, int timeout)
  63. {
  64. ++m_UrlToXmlRetrievalCount[location];
  65. if (timeout < m_UrlToXmlOutputDelay[location])
  66. {
  67. throw ctkCmdLineModuleTimeoutException(location, "Timeout in BackendMockUp occurred");
  68. }
  69. return m_UrlToXml[location];
  70. }
  71. protected:
  72. virtual ctkCmdLineModuleFuture run(ctkCmdLineModuleFrontend* /*frontend*/)
  73. {
  74. return ctkCmdLineModuleFuture();
  75. }
  76. private:
  77. QHash<QUrl, qint64> m_UrlToTimestamp;
  78. QHash<QUrl, int> m_UrlToXmlRetrievalCount;
  79. QHash<QUrl, int> m_UrlToXmlOutputDelay;
  80. QHash<QUrl, QByteArray> m_UrlToXml;
  81. };
  82. }
  83. //-----------------------------------------------------------------------------
  84. class ctkCmdLineModuleManagerTester : public QObject
  85. {
  86. Q_OBJECT
  87. private Q_SLOTS:
  88. void initTestCase();
  89. void cleanup();
  90. void testStrictValidation();
  91. void testWeakValidation();
  92. void testSkipValidation();
  93. void testTimeoutHandling();
  94. void testCaching();
  95. private:
  96. QByteArray validXml;
  97. QByteArray invalidXml;
  98. QString cachePath;
  99. };
  100. //-----------------------------------------------------------------------------
  101. void ctkCmdLineModuleManagerTester::initTestCase()
  102. {
  103. validXml = "<executable>\n"
  104. " <title>My Filter</title>\n"
  105. " <description>Awesome filter</description>\n"
  106. " <parameters>\n"
  107. " <label>bla</label>\n"
  108. " <description>bla</description>\n"
  109. " <integer>\n"
  110. " <name>param</name>\n"
  111. " <flag>i</flag>\n"
  112. " <description>bla</description>\n"
  113. " <label>bla</label>\n"
  114. " </integer>\n"
  115. " </parameters>\n"
  116. "</executable>\n";
  117. invalidXml = "<executable>\n"
  118. " <description>Awesome filter</description>\n"
  119. " <title>My Filter</title>\n"
  120. " <parameters>\n"
  121. " <label>bla</label>\n"
  122. " <description>bla</description>\n"
  123. " <integer>\n"
  124. " <name>param</name>\n"
  125. " <flag>i</flag>\n"
  126. " <description>bla</description>\n"
  127. " <label>bla</label>\n"
  128. " </integer>\n"
  129. " </parameters>\n"
  130. "</executable>\n";
  131. cachePath = QDir::tempPath() + QDir::separator() + "ctkCmdLineModuleManagerTester_cache";
  132. }
  133. //-----------------------------------------------------------------------------
  134. void ctkCmdLineModuleManagerTester::cleanup()
  135. {
  136. ctk::removeDirRecursively(cachePath);
  137. }
  138. //-----------------------------------------------------------------------------
  139. void ctkCmdLineModuleManagerTester::testStrictValidation()
  140. {
  141. BackendMockUp backend;
  142. backend.addModule(QUrl("test://validXml"), validXml);
  143. backend.addModule(QUrl("test://invalidXml"), invalidXml);
  144. ctkCmdLineModuleManager manager;
  145. manager.registerBackend(&backend);
  146. ctkCmdLineModuleReference moduleRef = manager.registerModule(QUrl("test://validXml"));
  147. QVERIFY(moduleRef);
  148. QVERIFY(moduleRef.xmlValidationErrorString().isEmpty());
  149. try
  150. {
  151. manager.registerModule(QUrl("test://invalidXml"));
  152. QFAIL("Succeeded in registering invalid module");
  153. }
  154. catch (const ctkInvalidArgumentException&)
  155. {
  156. }
  157. }
  158. //-----------------------------------------------------------------------------
  159. void ctkCmdLineModuleManagerTester::testWeakValidation()
  160. {
  161. BackendMockUp backend;
  162. backend.addModule(QUrl("test://validXml"), validXml);
  163. backend.addModule(QUrl("test://invalidXml"), invalidXml);
  164. ctkCmdLineModuleManager manager(ctkCmdLineModuleManager::WEAK_VALIDATION);
  165. manager.registerBackend(&backend);
  166. ctkCmdLineModuleReference moduleRef = manager.registerModule(QUrl("test://validXml"));
  167. QVERIFY(moduleRef);
  168. QVERIFY(moduleRef.xmlValidationErrorString().isEmpty());
  169. ctkCmdLineModuleReference moduleRef2 = manager.registerModule(QUrl("test://invalidXml"));
  170. QVERIFY(moduleRef2);
  171. QVERIFY(!moduleRef2.xmlValidationErrorString().isEmpty());
  172. }
  173. //-----------------------------------------------------------------------------
  174. void ctkCmdLineModuleManagerTester::testSkipValidation()
  175. {
  176. BackendMockUp backend;
  177. backend.addModule(QUrl("test://validXml"), validXml);
  178. backend.addModule(QUrl("test://invalidXml"), invalidXml);
  179. ctkCmdLineModuleManager manager(ctkCmdLineModuleManager::SKIP_VALIDATION);
  180. manager.registerBackend(&backend);
  181. ctkCmdLineModuleReference moduleRef = manager.registerModule(QUrl("test://validXml"));
  182. QVERIFY(moduleRef);
  183. QVERIFY(moduleRef.xmlValidationErrorString().isEmpty());
  184. ctkCmdLineModuleReference moduleRef2 = manager.registerModule(QUrl("test://invalidXml"));
  185. QVERIFY(moduleRef2);
  186. QVERIFY(moduleRef2.xmlValidationErrorString().isEmpty());
  187. }
  188. //-----------------------------------------------------------------------------
  189. void ctkCmdLineModuleManagerTester::testTimeoutHandling()
  190. {
  191. BackendMockUp backend;
  192. backend.addModule(QUrl("test://validXml"), validXml, 1000);
  193. backend.addModule(QUrl("test://validXml2"), validXml);
  194. ctkCmdLineModuleManager manager(ctkCmdLineModuleManager::STRICT_VALIDATION, cachePath);
  195. manager.setTimeOutForXMLRetrieval(2000);
  196. manager.registerBackend(&backend);
  197. // register modules with a sufficient large timeout value
  198. QList<ctkCmdLineModuleReferenceResult> results =
  199. QtConcurrent::blockingMapped(backend.moduleLocations(),
  200. ctkCmdLineModuleConcurrentRegister(&manager, true));
  201. QVERIFY(results.size() == 2);
  202. QVERIFY(results[0].m_Reference && results[0].m_RuntimeError.isEmpty());
  203. QVERIFY(results[1].m_Reference && results[1].m_RuntimeError.isEmpty());
  204. // unregister the modules
  205. QList<bool> unregisterResults =
  206. QtConcurrent::blockingMapped(backend.moduleLocations(),
  207. ctkCmdLineModuleConcurrentUnRegister(&manager));
  208. QVERIFY(unregisterResults.size() == 2);
  209. QVERIFY(unregisterResults[0] && unregisterResults[1]);
  210. // register modules where one runs into a timeout
  211. manager.setTimeOutForXMLRetrieval(500);
  212. results = QtConcurrent::blockingMapped(backend.moduleLocations(),
  213. ctkCmdLineModuleConcurrentRegister(&manager, true));
  214. QVERIFY(results.size() == 2);
  215. QVERIFY(!results[0].m_Reference && !results[0].m_RuntimeError.isEmpty());
  216. QVERIFY(results[1].m_Reference && results[1].m_RuntimeError.isEmpty());
  217. }
  218. //-----------------------------------------------------------------------------
  219. void ctkCmdLineModuleManagerTester::testCaching()
  220. {
  221. QUrl location("test://validXml");
  222. QUrl location2("test://validXml2");
  223. // The code below is inside a local scopes so the manager is destroyed
  224. // but the cache is still available (unregistering the module would also
  225. // remove the cache entry, but we need to test the entry)
  226. {
  227. BackendMockUp backend;
  228. backend.addModule(location, validXml, 1000);
  229. ctkCmdLineModuleManager manager(ctkCmdLineModuleManager::STRICT_VALIDATION, cachePath);
  230. manager.setTimeOutForXMLRetrieval(500);
  231. manager.registerBackend(&backend);
  232. QVERIFY(backend.xmlRetrievalCount(location) == 0);
  233. // module runs into a timeout
  234. try
  235. {
  236. manager.registerModule(location);
  237. QFAIL("ctkCmdLineModuleTimeoutException expected");
  238. }
  239. catch (const ctkCmdLineModuleTimeoutException&)
  240. {}
  241. QVERIFY(backend.xmlRetrievalCount(location) == 1);
  242. // Increase the timeout and register the module again. It should
  243. // not have been cached and the manager is supposed to try to
  244. // retrieve the XML description again
  245. manager.setTimeOutForXMLRetrieval(2000);
  246. QVERIFY(manager.registerModule(location));
  247. QVERIFY(backend.xmlRetrievalCount(location) == 2);
  248. // Registering the same module again should just return the already
  249. // created module reference
  250. QVERIFY(manager.registerModule(location));
  251. QVERIFY(backend.xmlRetrievalCount(location) == 2);
  252. }
  253. {
  254. BackendMockUp backend;
  255. backend.addModule(location, validXml);
  256. backend.setTimestamp(location, 1);
  257. backend.addModule(location2, invalidXml);
  258. backend.setTimestamp(location2, 1);
  259. ctkCmdLineModuleManager manager(ctkCmdLineModuleManager::STRICT_VALIDATION, cachePath);
  260. manager.registerBackend(&backend);
  261. QVERIFY(manager.registerModule(location));
  262. QVERIFY(backend.xmlRetrievalCount(location) == 1);
  263. try
  264. {
  265. manager.registerModule(location2);
  266. QFAIL("ctkInvalidArgumentException (invalid XML) expected");
  267. }
  268. catch (const ctkInvalidArgumentException&)
  269. {}
  270. QVERIFY(backend.xmlRetrievalCount(location2) == 1);
  271. }
  272. // Do the same again but now the cache entries should be returned
  273. {
  274. BackendMockUp backend;
  275. backend.addModule(location, validXml);
  276. backend.setTimestamp(location, 1);
  277. backend.addModule(location2, invalidXml);
  278. backend.setTimestamp(location2, 1);
  279. ctkCmdLineModuleManager manager(ctkCmdLineModuleManager::STRICT_VALIDATION, cachePath);
  280. manager.registerBackend(&backend);
  281. QVERIFY(manager.registerModule(location));
  282. QVERIFY(backend.xmlRetrievalCount(location) == 0);
  283. try
  284. {
  285. manager.registerModule(location2);
  286. QFAIL("ctkInvalidArgumentException (invalid XML) expected");
  287. }
  288. catch (const ctkInvalidArgumentException&)
  289. {}
  290. QVERIFY(backend.xmlRetrievalCount(location2) == 0);
  291. }
  292. // Now test updated timestamps
  293. {
  294. BackendMockUp backend;
  295. backend.addModule(location, validXml);
  296. backend.setTimestamp(location, 2);
  297. // use valid XML now but keep the previous timestamp
  298. backend.addModule(location2, validXml);
  299. backend.setTimestamp(location2, 1);
  300. ctkCmdLineModuleManager manager(ctkCmdLineModuleManager::STRICT_VALIDATION, cachePath);
  301. manager.registerBackend(&backend);
  302. QVERIFY(manager.registerModule(location));
  303. QVERIFY(backend.xmlRetrievalCount(location) == 1);
  304. // should still throw an exception due to the cache entry
  305. try
  306. {
  307. manager.registerModule(location2);
  308. QFAIL("ctkInvalidArgumentException (invalid XML) expected");
  309. }
  310. catch (const ctkInvalidArgumentException&)
  311. {}
  312. QVERIFY(backend.xmlRetrievalCount(location2) == 0);
  313. // now update the timestamp and check that the valid XML is retrieved
  314. backend.setTimestamp(location, 2);
  315. QVERIFY(manager.registerModule(location));
  316. QVERIFY(backend.xmlRetrievalCount(location) == 1);
  317. }
  318. }
  319. // ----------------------------------------------------------------------------
  320. CTK_TEST_MAIN(ctkCmdLineModuleManagerTest)
  321. #include "moc_ctkCmdLineModuleManagerTest.cpp"