ctkCmdLineModuleFutureTest.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  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 <ctkCmdLineModuleFrontendFactory.h>
  17. #include <ctkCmdLineModuleFrontend.h>
  18. #include <ctkCmdLineModuleReference.h>
  19. #include <ctkCmdLineModuleDescription.h>
  20. #include <ctkCmdLineModuleParameter.h>
  21. #include <ctkCmdLineModuleRunException.h>
  22. #include <ctkCmdLineModuleFuture.h>
  23. #include <ctkCmdLineModuleFutureWatcher.h>
  24. #include "ctkCmdLineModuleSignalTester.h"
  25. #include "ctkCmdLineModuleBackendLocalProcess.h"
  26. #include "ctkTest.h"
  27. #include <QVariant>
  28. #include <QCoreApplication>
  29. #include <QDebug>
  30. #include <QFutureWatcher>
  31. //-----------------------------------------------------------------------------
  32. class ctkCmdLineModuleFrontendMockupFactory : public ctkCmdLineModuleFrontendFactory
  33. {
  34. public:
  35. virtual ctkCmdLineModuleFrontend* create(const ctkCmdLineModuleReference& moduleRef)
  36. {
  37. struct ModuleFrontendMockup : public ctkCmdLineModuleFrontend
  38. {
  39. ModuleFrontendMockup(const ctkCmdLineModuleReference& moduleRef)
  40. : ctkCmdLineModuleFrontend(moduleRef) {}
  41. virtual QObject* guiHandle() const { return NULL; }
  42. virtual QVariant value(const QString& parameter, int role) const
  43. {
  44. Q_UNUSED(role)
  45. QVariant value = currentValues[parameter];
  46. if (!value.isValid())
  47. return this->moduleReference().description().parameter(parameter).defaultValue();
  48. return value;
  49. }
  50. virtual void setValue(const QString& parameter, const QVariant& value, int role = DisplayRole)
  51. {
  52. Q_UNUSED(role)
  53. currentValues[parameter] = value;
  54. }
  55. private:
  56. QHash<QString, QVariant> currentValues;
  57. };
  58. return new ModuleFrontendMockup(moduleRef);
  59. }
  60. virtual QString name() const { return "Mock-up"; }
  61. virtual QString description() const { return "A mock-up factory for testing."; }
  62. };
  63. //-----------------------------------------------------------------------------
  64. class ctkCmdLineModuleFutureTester : public QObject
  65. {
  66. Q_OBJECT
  67. public Q_SLOTS:
  68. void ouputDataReady();
  69. void errorDataReady();
  70. private Q_SLOTS:
  71. void initTestCase();
  72. void init();
  73. void cleanup();
  74. void testStartFinish();
  75. void testProgress();
  76. void testPauseAndCancel();
  77. void testOutput();
  78. void testError();
  79. private:
  80. QByteArray outputData;
  81. QByteArray errorData;
  82. ctkCmdLineModuleFutureWatcher* currentWatcher;
  83. ctkCmdLineModuleFrontendMockupFactory factory;
  84. ctkCmdLineModuleBackendLocalProcess backend;
  85. ctkCmdLineModuleManager manager;
  86. ctkCmdLineModuleReference moduleRef;
  87. ctkCmdLineModuleFrontend* frontend;
  88. };
  89. //-----------------------------------------------------------------------------
  90. void ctkCmdLineModuleFutureTester::ouputDataReady()
  91. {
  92. if (this->currentWatcher)
  93. {
  94. outputData.append(currentWatcher->readPendingOutputData());
  95. }
  96. }
  97. //-----------------------------------------------------------------------------
  98. void ctkCmdLineModuleFutureTester::errorDataReady()
  99. {
  100. if (this->currentWatcher)
  101. {
  102. errorData.append(currentWatcher->readPendingErrorData());
  103. }
  104. }
  105. //-----------------------------------------------------------------------------
  106. void ctkCmdLineModuleFutureTester::initTestCase()
  107. {
  108. manager.registerBackend(&backend);
  109. QUrl moduleUrl = QUrl::fromLocalFile(QCoreApplication::applicationDirPath() + "/ctkCmdLineModuleTestBed");
  110. moduleRef = manager.registerModule(moduleUrl);
  111. }
  112. //-----------------------------------------------------------------------------
  113. void ctkCmdLineModuleFutureTester::init()
  114. {
  115. currentWatcher = 0;
  116. frontend = factory.create(moduleRef);
  117. }
  118. //-----------------------------------------------------------------------------
  119. void ctkCmdLineModuleFutureTester::cleanup()
  120. {
  121. delete frontend;
  122. outputData.clear();
  123. errorData.clear();
  124. }
  125. //-----------------------------------------------------------------------------
  126. void ctkCmdLineModuleFutureTester::testStartFinish()
  127. {
  128. QList<QString> expectedSignals;
  129. expectedSignals << "module.started"
  130. // the following signals are send when connecting a QFutureWatcher to
  131. // an already started QFuture
  132. << "module.progressRangeChanged(0,0)"
  133. << "module.progressValueChanged(0)"
  134. << "module.progressRangeChanged(0,1000)"
  135. // the test module always reports error data when starting
  136. << "module.errorReady"
  137. // the following two signals are send when the module reports "filter start"
  138. << "module.progressValueChanged(1)"
  139. << "module.progressTextChanged(Test Filter)"
  140. // imageOutput result
  141. << "module.resultReadyAt(0,1)"
  142. << "module.resultReadyAt(0)"
  143. // exitStatusOutput result
  144. << "module.resultReadyAt(1,2)"
  145. << "module.resultReadyAt(1)"
  146. // the following signal is sent at the end to report completion
  147. << "module.progressValueChanged(1000)"
  148. << "module.finished";
  149. ctkCmdLineModuleSignalTester signalTester;
  150. ctkCmdLineModuleFuture future = manager.run(frontend);
  151. signalTester.setFuture(future);
  152. future.waitForFinished();
  153. QCoreApplication::processEvents();
  154. QVERIFY(signalTester.checkSignals(expectedSignals));
  155. QList<ctkCmdLineModuleResult> results;
  156. results << ctkCmdLineModuleResult("imageOutput", "/tmp/out.nrrd");
  157. results << ctkCmdLineModuleResult("exitStatusOutput", "Normal exit");
  158. QCOMPARE(signalTester.results(), results);
  159. }
  160. //-----------------------------------------------------------------------------
  161. void ctkCmdLineModuleFutureTester::testProgress()
  162. {
  163. QList<QString> expectedSignals;
  164. expectedSignals << "module.started"
  165. // the following signals are send when connecting a QFutureWatcher to
  166. // an already started QFuture
  167. << "module.progressRangeChanged(0,0)"
  168. << "module.progressValueChanged(0)"
  169. << "module.progressRangeChanged(0,1000)"
  170. // the test module always reports error data when starting
  171. << "module.errorReady"
  172. // the following two signals are send when the module reports "filter start"
  173. << "module.progressValueChanged(1)"
  174. << "module.progressTextChanged(Test Filter)"
  175. // the output data on the standard output channel
  176. << "module.outputReady"
  177. // this signal is send when the module reports progress for "output1"
  178. << "module.progressValueChanged(999)"
  179. // first resultNumberOutput result
  180. << "module.resultReadyAt(0,1)"
  181. << "module.resultReadyAt(0)"
  182. // imageOutput result
  183. << "module.resultReadyAt(1,2)"
  184. << "module.resultReadyAt(1)"
  185. // exitStatusOutput result
  186. << "module.resultReadyAt(2,3)"
  187. << "module.resultReadyAt(2)"
  188. // the following signal is sent at the end to report completion
  189. << "module.progressValueChanged(1000)"
  190. << "module.finished";
  191. ctkCmdLineModuleSignalTester signalTester;
  192. frontend->setValue("numOutputsVar", 1);
  193. ctkCmdLineModuleFuture future = manager.run(frontend);
  194. signalTester.setFuture(future);
  195. future.waitForFinished();
  196. // process pending events
  197. QCoreApplication::processEvents();
  198. QVERIFY(signalTester.checkSignals(expectedSignals));
  199. QList<ctkCmdLineModuleResult> results;
  200. results << ctkCmdLineModuleResult("resultNumberOutput", 1);
  201. results << ctkCmdLineModuleResult("imageOutput", "/tmp/out.nrrd");
  202. results << ctkCmdLineModuleResult("exitStatusOutput", "Normal exit");
  203. QCOMPARE(signalTester.results(), results);
  204. }
  205. //-----------------------------------------------------------------------------
  206. void ctkCmdLineModuleFutureTester::testPauseAndCancel()
  207. {
  208. ctkCmdLineModuleSignalTester signalTester;
  209. frontend->setValue("runtimeVar", 60);
  210. ctkCmdLineModuleFuture future = manager.run(frontend);
  211. signalTester.setFuture(future);
  212. QList<QString> expectedSignals;
  213. expectedSignals << "module.started"
  214. << "module.progressRangeChanged(0,0)"
  215. << "module.progressValueChanged(0)"
  216. << "module.progressRangeChanged(0,1000)"
  217. << "module.errorReady";
  218. if (future.canPause())
  219. {
  220. // Due to Qt bug 12152, these two signals are reversed
  221. expectedSignals.push_back("module.resumed");
  222. expectedSignals.push_back("module.paused");
  223. }
  224. if (future.canCancel())
  225. {
  226. expectedSignals.push_back("module.canceled");
  227. }
  228. expectedSignals.push_back("module.finished");
  229. QTest::qWait(100);
  230. if (future.canPause())
  231. {
  232. future.pause();
  233. QTest::qWait(100);
  234. QVERIFY(future.isPaused());
  235. }
  236. QVERIFY(future.isRunning());
  237. if (future.canPause())
  238. {
  239. future.togglePaused();
  240. QTest::qWait(100);
  241. }
  242. QVERIFY(!future.isPaused());
  243. QVERIFY(future.isRunning());
  244. if (future.canCancel())
  245. {
  246. // give event processing a chance before killing the process
  247. QTest::qWait(200);
  248. future.cancel();
  249. }
  250. future.waitForFinished();
  251. // process pending events
  252. QCoreApplication::processEvents();
  253. QVERIFY(future.isCanceled());
  254. QVERIFY(future.isFinished());
  255. QVERIFY(signalTester.checkSignals(expectedSignals));
  256. }
  257. //-----------------------------------------------------------------------------
  258. void ctkCmdLineModuleFutureTester::testOutput()
  259. {
  260. ctkCmdLineModuleSignalTester signalTester;
  261. connect(signalTester.watcher(), SIGNAL(outputDataReady()), SLOT(ouputDataReady()));
  262. connect(signalTester.watcher(), SIGNAL(errorDataReady()), SLOT(errorDataReady()));
  263. this->currentWatcher = signalTester.watcher();
  264. frontend->setValue("numOutputsVar", 2);
  265. frontend->setValue("errorTextVar", "Final error msg.");
  266. ctkCmdLineModuleFuture future = manager.run(frontend);
  267. signalTester.setFuture(future);
  268. future.waitForFinished();
  269. // process pending events
  270. QCoreApplication::processEvents();
  271. QVERIFY(future.isFinished());
  272. QList<QString> expectedSignals;
  273. expectedSignals << "module.started"
  274. // the following signals are send when connecting a QFutureWatcher to
  275. // an already started QFuture
  276. << "module.progressRangeChanged(0,0)"
  277. << "module.progressValueChanged(0)"
  278. << "module.progressRangeChanged(0,1000)"
  279. // the test module always reports error data when starting
  280. << "module.errorReady"
  281. // the following two signals are send when the module reports "filter start"
  282. << "module.progressValueChanged(1)"
  283. << "module.progressTextChanged(Test Filter)"
  284. // the output data on the standard output channel "Output 1"
  285. << "module.outputReady"
  286. // this signal is send when the module reports progress for "output1"
  287. << "module.progressValueChanged(500)"
  288. // first resultNumberOutput result
  289. << "module.resultReadyAt(0,1)"
  290. << "module.resultReadyAt(0)"
  291. // the output data on the standard output channel "Output 2"
  292. << "module.outputReady"
  293. // this signal is send when the module reports progress for "output2"
  294. << "module.progressValueChanged(999)"
  295. // second resultNumberOutput result
  296. << "module.resultReadyAt(1,2)"
  297. << "module.resultReadyAt(1)"
  298. // imageOutput result
  299. << "module.resultReadyAt(2,3)"
  300. << "module.resultReadyAt(2)"
  301. // exitStatusOutput result
  302. << "module.resultReadyAt(3,4)"
  303. << "module.resultReadyAt(3)"
  304. // final error message
  305. << "module.errorReady"
  306. // the following signal is sent at the end to report completion
  307. << "module.progressValueChanged(1000)"
  308. << "module.finished";
  309. QVERIFY(signalTester.checkSignals(expectedSignals));
  310. const char* expectedOutput = "Output 1\nOutput 2\n";
  311. const char* expectedError = "A superficial error message.\nFinal error msg.";
  312. QCOMPARE(this->outputData.data(), expectedOutput);
  313. QCOMPARE(this->errorData.data(), expectedError);
  314. QCOMPARE(future.readAllOutputData().data(), expectedOutput);
  315. QCOMPARE(future.readAllErrorData().data(), expectedError);
  316. QList<ctkCmdLineModuleResult> results;
  317. results << ctkCmdLineModuleResult("resultNumberOutput", 1);
  318. results << ctkCmdLineModuleResult("resultNumberOutput", 2);
  319. results << ctkCmdLineModuleResult("errorMsgOutput", "Final error msg.");
  320. results << ctkCmdLineModuleResult("exitStatusOutput", "Normal exit");
  321. QCOMPARE(signalTester.results(), results);
  322. }
  323. //-----------------------------------------------------------------------------
  324. void ctkCmdLineModuleFutureTester::testError()
  325. {
  326. frontend->setValue("fileVar", "output1");
  327. frontend->setValue("exitCodeVar", 24);
  328. frontend->setValue("errorTextVar", "Some error occured\n");
  329. ctkCmdLineModuleFuture future = manager.run(frontend);
  330. try
  331. {
  332. future.waitForFinished();
  333. QFAIL("Expected exception not thrown.");
  334. }
  335. catch (const ctkCmdLineModuleRunException& e)
  336. {
  337. QVERIFY2(e.errorCode() == 24, "Test matching error code");
  338. QCOMPARE(future.readAllErrorData().data(), "A superficial error message.\nSome error occured\n");
  339. }
  340. }
  341. // ----------------------------------------------------------------------------
  342. CTK_TEST_MAIN(ctkCmdLineModuleFutureTest)
  343. #include "moc_ctkCmdLineModuleFutureTest.cpp"