ctkWorkflowTest1.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. /*=========================================================================
  2. Library: CTK
  3. Copyright (c) Kitware Inc.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0.txt
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. =========================================================================*/
  14. // QT includes
  15. #include <QCoreApplication>
  16. #include <QTimer>
  17. // CTK includes
  18. #include "ctkExampleDerivedWorkflowStep.h"
  19. #include "ctkWorkflow.h"
  20. // STD includes
  21. #include <cstdlib>
  22. #include <iostream>
  23. //-----------------------------------------------------------------------------
  24. int numberOfTimesEntryExitTest(ctkExampleDerivedWorkflowStep* step1=0, int step1Entry=0, int step1Exit=0, ctkExampleDerivedWorkflowStep* step2=0, int step2Entry=0, int step2Exit=0, ctkExampleDerivedWorkflowStep* step3=0, int step3Entry=0, int step3Exit=0, ctkExampleDerivedWorkflowStep* step4=0, int step4Entry=0, int step4Exit=0)
  25. {
  26. if (step1)
  27. {
  28. if (step1->numberOfTimesRanOnEntry() != step1Entry || step1->numberOfTimesRanOnExit() != step1Exit)
  29. {
  30. return 0;
  31. }
  32. }
  33. if (step2)
  34. {
  35. if (step2->numberOfTimesRanOnEntry() != step2Entry || step2->numberOfTimesRanOnExit() != step2Exit)
  36. {
  37. return 0;
  38. }
  39. }
  40. if (step3)
  41. {
  42. if (step3->numberOfTimesRanOnEntry() != step3Entry || step3->numberOfTimesRanOnExit() != step3Exit)
  43. {
  44. return 0;
  45. }
  46. }
  47. if (step4)
  48. {
  49. if (step4->numberOfTimesRanOnEntry() != step4Entry || step4->numberOfTimesRanOnExit() != step4Exit)
  50. {
  51. return 0;
  52. }
  53. }
  54. return 1;
  55. }
  56. //-----------------------------------------------------------------------------
  57. int currentStepAndNumberOfTimesEntryExitTest(ctkWorkflow* workflow, ctkExampleDerivedWorkflowStep* expectedStep, ctkExampleDerivedWorkflowStep* step1, int step1Entry, int step1Exit, ctkExampleDerivedWorkflowStep* step2, int step2Entry, int step2Exit, ctkExampleDerivedWorkflowStep* step3=0, int step3Entry=0, int step3Exit=0, ctkExampleDerivedWorkflowStep* step4=0, int step4Entry=0, int step4Exit=0)
  58. {
  59. if (expectedStep)
  60. {
  61. if (workflow->currentStep() != expectedStep)
  62. {
  63. return 0;
  64. }
  65. }
  66. return numberOfTimesEntryExitTest(step1, step1Entry, step1Exit, step2, step2Entry, step2Exit, step3, step3Entry, step3Exit, step4, step4Entry, step4Exit);
  67. }
  68. //-----------------------------------------------------------------------------
  69. int transitionTest(ctkWorkflow* workflow, int defaultTime, ctkExampleDerivedWorkflowStep* expectedStep, ctkExampleDerivedWorkflowStep* step1, int step1Entry, int step1Exit, ctkExampleDerivedWorkflowStep* step2, int step2Entry, int step2Exit, ctkExampleDerivedWorkflowStep* step3=0, int step3Entry=0, int step3Exit=0, ctkExampleDerivedWorkflowStep* step4=0, int step4Entry=0, int step4Exit=0)
  70. {
  71. QTimer::singleShot(defaultTime, qApp, SLOT(quit()));
  72. qApp->exec();
  73. return currentStepAndNumberOfTimesEntryExitTest(workflow, expectedStep, step1, step1Entry, step1Exit, step2, step2Entry, step2Exit, step3, step3Entry, step3Exit, step4, step4Entry, step4Exit);
  74. }
  75. //-----------------------------------------------------------------------------
  76. int testStartWorkflow(ctkWorkflow* workflow, int defaultTime, bool shouldRun, ctkExampleDerivedWorkflowStep* expectedStep=0, ctkExampleDerivedWorkflowStep* step1=0, int step1Entry=0, int step1Exit=0, ctkExampleDerivedWorkflowStep* step2=0, int step2Entry=0, int step2Exit=0, ctkExampleDerivedWorkflowStep* step3=0, int step3Entry=0, int step3Exit=0, ctkExampleDerivedWorkflowStep* step4=0, int step4Entry=0, int step4Exit=0)
  77. {
  78. workflow->start();
  79. QTimer::singleShot(defaultTime, qApp, SLOT(quit()));
  80. qApp->exec();
  81. if (workflow->isRunning() != shouldRun)
  82. {
  83. return 0;
  84. }
  85. return currentStepAndNumberOfTimesEntryExitTest(workflow, expectedStep, step1, step1Entry, step1Exit, step2, step2Entry, step2Exit, step3, step3Entry, step3Exit, step4, step4Entry, step4Exit);
  86. }
  87. //-----------------------------------------------------------------------------
  88. int testStopWorkflow(ctkWorkflow* workflow, int defaultTime, ctkExampleDerivedWorkflowStep* step1, int step1Entry, int step1Exit, ctkExampleDerivedWorkflowStep* step2, int step2Entry, int step2Exit, ctkExampleDerivedWorkflowStep* step3=0, int step3Entry=0, int step3Exit=0, ctkExampleDerivedWorkflowStep* step4=0, int step4Entry=0, int step4Exit=0)
  89. {
  90. workflow->stop();
  91. QTimer::singleShot(defaultTime, qApp, SLOT(quit()));
  92. qApp->exec();
  93. if (workflow->isRunning())
  94. {
  95. return 0;
  96. }
  97. return numberOfTimesEntryExitTest(step1, step1Entry, step1Exit, step2, step2Entry, step2Exit, step3, step3Entry, step3Exit, step4, step4Entry, step4Exit);
  98. }
  99. //-----------------------------------------------------------------------------
  100. int ctkWorkflowTest1(int argc, char * argv [] )
  101. {
  102. QCoreApplication app(argc, argv);
  103. int defaultTime = 100;
  104. // create two steps and the workflow
  105. ctkWorkflow *workflow = new ctkWorkflow();
  106. ctkExampleDerivedWorkflowStep *step1 = new ctkExampleDerivedWorkflowStep("Step 1");
  107. step1->setName("Step 1");
  108. step1->setDescription("Description for step 1");
  109. ctkExampleDerivedWorkflowStep *step2 = new ctkExampleDerivedWorkflowStep("Step 2");
  110. step2->setName("Step 2");
  111. step2->setDescription("Description for step 2");
  112. // --------------------------------------------------------------------------
  113. // try to add a transition for a step with the same id
  114. ctkExampleDerivedWorkflowStep *step1Duplicated = new ctkExampleDerivedWorkflowStep("Step 1");
  115. if (workflow->addTransition(step1, step1Duplicated))
  116. {
  117. std::cerr << "workflow connected two steps with the same id";
  118. return EXIT_FAILURE;
  119. }
  120. // try to add a transition from a step to itself
  121. if (workflow->addTransition(step1, step1))
  122. {
  123. std::cerr << "workflow connected two steps with the same id";
  124. return EXIT_FAILURE;
  125. }
  126. // --------------------------------------------------------------------------
  127. // workflow with no steps
  128. // try erroneously starting with no steps
  129. if (!testStartWorkflow(workflow, defaultTime, false))
  130. {
  131. std::cerr << "empty workflow is running after start()";
  132. return EXIT_FAILURE;
  133. }
  134. // add the first step
  135. if (!workflow->addTransition(step1, 0))
  136. {
  137. std::cerr << "could not add first step";
  138. return EXIT_FAILURE;
  139. }
  140. // try erroneously starting with no initial step
  141. if (!testStartWorkflow(workflow, defaultTime, false))
  142. {
  143. std::cerr << "workflow is running after start() with no initial step";
  144. return EXIT_FAILURE;
  145. }
  146. // --------------------------------------------------------------------------
  147. // workflow with one step
  148. // set the initial step (which sets the initial state)
  149. workflow->setInitialStep(step1);
  150. // try starting with one step
  151. if (!testStartWorkflow(workflow, defaultTime, true, step1, step1, 1, 0, step2, 0, 0))
  152. {
  153. std::cerr << "workflow is not running after start() with a single step";
  154. return EXIT_FAILURE;
  155. }
  156. // triggering ValidationTransition and TransitionToPreviousStep
  157. // should keep us in the same step, when there is only one step
  158. workflow->goForward();
  159. if (!transitionTest(workflow, defaultTime, step1, step1, 1, 0, step2, 0, 0))
  160. {
  161. std::cerr << "error in validation transition in a workflow with a single step";
  162. return EXIT_FAILURE;
  163. }
  164. // transition to the previous step
  165. workflow->goBackward();
  166. if (!transitionTest(workflow, defaultTime, step1, step1, 1, 0, step2, 0, 0))
  167. {
  168. std::cerr << "error after transition to previous step in a workflow with a single step";
  169. return EXIT_FAILURE;
  170. }
  171. // stop the workflow
  172. if (!testStopWorkflow(workflow, defaultTime, step1, 1, 1, step2, 0, 0))
  173. {
  174. std::cerr << "workflow with one step still running after stop";
  175. return EXIT_FAILURE;
  176. }
  177. // --------------------------------------------------------------------------
  178. // workflow with two steps
  179. // add the second step
  180. if (!workflow->addTransition(step1, step2))
  181. {
  182. std::cerr << "could not add second step";
  183. return EXIT_FAILURE;
  184. }
  185. // start the workflow
  186. if (!testStartWorkflow(workflow, defaultTime, true, step1, step1, 2, 1, step2, 0, 0))
  187. {
  188. std::cerr << "workflow is not running after start() with two steps";
  189. return EXIT_FAILURE;
  190. }
  191. // make sure the workflow has the steps
  192. if (!workflow->hasStep(step1->id()))
  193. {
  194. std::cerr << "Step1 not added to workflow";
  195. return EXIT_FAILURE;
  196. }
  197. if (!workflow->hasStep(step2->id()))
  198. {
  199. std::cerr << "Step2 not added to workflow";
  200. return EXIT_FAILURE;
  201. }
  202. // if (workflow->numberOfSteps() != 2)
  203. // {
  204. // std::cerr << "workflow has " << workflow->numberOfSteps() << " steps, not 2";
  205. // return EXIT_FAILURE;
  206. // }
  207. // Test that the workflow transitions from processing to validation state
  208. workflow->goForward();
  209. if (!transitionTest(workflow, defaultTime, step2, step1, 2, 2, step2, 1, 0))
  210. {
  211. std::cerr << "error transitioning to next step in workflow with two steps";
  212. return EXIT_FAILURE;
  213. }
  214. // Test that the workflow transitions back to the previous step
  215. workflow->goBackward();
  216. if (!transitionTest(workflow, defaultTime, step1, step1, 3, 2, step2, 1, 1))
  217. {
  218. std::cerr << "error transitioning to previous step in workflow with step steps";
  219. return EXIT_FAILURE;
  220. }
  221. // make sure the workflow stops properly
  222. if (!testStopWorkflow(workflow, defaultTime, step1, 3, 3, step2, 1, 1))
  223. {
  224. std::cerr << "workflow with two steps is running after stop()";
  225. return EXIT_FAILURE;
  226. }
  227. // --------------------------------------------------------------------------
  228. // Step3
  229. ctkExampleDerivedWorkflowStep *step3 = new ctkExampleDerivedWorkflowStep("Step 3");
  230. step3->setName("Step 3");
  231. step3->setDescription("Description for step 3");
  232. // --------------------------------------------------------------------------
  233. // Attempt to retrieve the forward or backward steps of step not yet added to the workflow
  234. if (workflow->forwardSteps(step3).count() != 0)
  235. {
  236. std::cerr << "No forward steps should be associated with step3";
  237. return EXIT_FAILURE;
  238. }
  239. if (workflow->backwardSteps(step3).count() != 0)
  240. {
  241. std::cerr << "No backward steps should be associated with step3";
  242. return EXIT_FAILURE;
  243. }
  244. // --------------------------------------------------------------------------
  245. // workflow with three steps
  246. // add a third step manually
  247. if (!workflow->addTransition(step2, step3, "", ctkWorkflow::Forward))
  248. {
  249. std::cerr << "could not add step 3 with forward transition";
  250. return EXIT_FAILURE;
  251. }
  252. if (!workflow->addTransition(step2, step3, "", ctkWorkflow::Backward))
  253. {
  254. std::cerr << "could not add next transition between step2 and step 3";
  255. return EXIT_FAILURE;
  256. }
  257. if (workflow->forwardSteps(step1).length() != 1
  258. || workflow->forwardSteps(step1).first() != step2
  259. || workflow->forwardSteps(step2).length() != 1
  260. || workflow->forwardSteps(step2).first() != step3
  261. || workflow->forwardSteps(step3).length() != 0)
  262. {
  263. std::cerr << "error in list of forward steps" << std::endl;
  264. return EXIT_FAILURE;
  265. }
  266. if (workflow->backwardSteps(step1).length() != 0
  267. || workflow->backwardSteps(step2).length() != 1
  268. || workflow->backwardSteps(step2).first() != step1
  269. || workflow->backwardSteps(step3).length() != 1
  270. || workflow->backwardSteps(step3).first() != step2)
  271. {
  272. std::cerr << "error in list of backward steps" << std::endl;
  273. return EXIT_FAILURE;
  274. }
  275. if (!workflow->hasStep(step3->id()))
  276. {
  277. std::cerr << "Step3 not added to workflow";
  278. return EXIT_FAILURE;
  279. }
  280. // if (workflow->numberOfSteps() != 3)
  281. // {
  282. // std::cerr << "workflow has " << workflow->numberOfSteps() << " steps, not 2";
  283. // return EXIT_FAILURE;
  284. // }
  285. // now that we've stopped and restarted the state machine, we should
  286. // be back in the initial step (step 1)
  287. if (!testStartWorkflow(workflow, defaultTime, true, step1, step1, 4, 3, step2, 1, 1, step3, 0, 0))
  288. {
  289. std::cerr << "workflow is not running after start() with three steps";
  290. return EXIT_FAILURE;
  291. }
  292. // test to make sure our lists of forward and backwards steps is correct
  293. if (!workflow->canGoForward(step1)
  294. || workflow->canGoBackward(step1)
  295. || !workflow->canGoForward(step2)
  296. || !workflow->canGoBackward(step2)
  297. || workflow->canGoForward(step3)
  298. || !workflow->canGoBackward(step3))
  299. {
  300. std::cerr << "error in can go forward/backward" << std::endl;
  301. return EXIT_FAILURE;
  302. }
  303. // Test that the workflow transitions from step1 to step 2 to step 3
  304. workflow->goForward();
  305. QTimer::singleShot(defaultTime, &app, SLOT(quit()));
  306. app.exec();
  307. workflow->goForward();
  308. if (!transitionTest(workflow, defaultTime, step3, step1, 4, 4, step2, 2, 2, step3, 1, 0))
  309. {
  310. std::cerr << "error transitioning to step3 in workflow with three steps";
  311. return EXIT_FAILURE;
  312. }
  313. // Test that the workflow transitions back to the previous step
  314. workflow->goBackward();
  315. if (!transitionTest(workflow, defaultTime, step2, step1, 4, 4, step2, 3, 2, step3, 1, 1))
  316. {
  317. std::cerr << "error transitioning to previous step in workflow with three steps";
  318. return EXIT_FAILURE;
  319. }
  320. // make sure the workflow stops properly
  321. if (!testStopWorkflow(workflow, defaultTime, step1, 4, 4, step2, 3, 3, step3, 1, 1))
  322. {
  323. std::cerr << "error stopping workflow with three steps";
  324. return EXIT_FAILURE;
  325. }
  326. // --------------------------------------------------------------------------
  327. // workflow with a finish step (step 3)
  328. // restart the workflow
  329. if (!testStartWorkflow(workflow, defaultTime, true, step1, step1, 5, 4, step2, 3, 3, step3, 1, 1))
  330. {
  331. std::cerr << "workflow with finish step is not running after start()";
  332. return EXIT_FAILURE;
  333. }
  334. // try to go automatically to step 3
  335. workflow->goToStep("Step 3");
  336. if (!transitionTest(workflow, defaultTime, step1, step1, 6, 5, step2, 4, 4, step3, 2, 2))
  337. {
  338. std::cerr << "error after going to finish step";
  339. return EXIT_FAILURE;
  340. }
  341. // try to go automatically to step 3 again
  342. workflow->goToStep("Step 3");
  343. if (!transitionTest(workflow, defaultTime, step1, step1, 7, 6, step2, 5, 5, step3, 3, 3))
  344. {
  345. std::cerr << "error after going to finish step the second time";
  346. return EXIT_FAILURE;
  347. }
  348. // stop workflow
  349. if (!testStopWorkflow(workflow, defaultTime, step1, 7, 7, step2, 5, 5, step3, 3, 3))
  350. {
  351. std::cerr << "error stopping workflow with finish step";
  352. return EXIT_FAILURE;
  353. }
  354. // --------------------------------------------------------------------------
  355. // workflow with two finishing steps (step3 and step4)
  356. ctkExampleDerivedWorkflowStep *step4 = new ctkExampleDerivedWorkflowStep("Step 4");
  357. step4->setName("Step 4");
  358. step4->setDescription("Description for step 4");
  359. workflow->addTransition(step3, step4);
  360. // restart the workflow
  361. if (!testStartWorkflow(workflow, defaultTime, true, step1, step1, 8, 7, step2, 5, 5, step3, 3, 3, step4, 0, 0))
  362. {
  363. std::cerr << "workflow with two finish steps is not running after start()";
  364. return EXIT_FAILURE;
  365. }
  366. // try to go automatically to step 3
  367. workflow->goToStep("Step 3");
  368. if (!transitionTest(workflow, defaultTime, step1, step1, 9, 8, step2, 6, 6, step3, 4, 4, step4, 0, 0))
  369. {
  370. std::cerr << "error going to the first finish step of two";
  371. return EXIT_FAILURE;
  372. }
  373. // try to go automatically to step 4
  374. workflow->goToStep("Step 4");
  375. if (!transitionTest(workflow, defaultTime, step1, step1, 10, 9, step2, 7, 7, step3, 5, 5, step4, 1, 1))
  376. {
  377. std::cerr << "error going to the second finish step of two";
  378. return EXIT_FAILURE;
  379. }
  380. // go to step 3 (a finish step)
  381. workflow->goForward();
  382. QTimer::singleShot(defaultTime, &app, SLOT(quit()));
  383. app.exec();
  384. workflow->goForward();
  385. if (!transitionTest(workflow, defaultTime, step3, step1, 10, 10, step2, 8, 8, step3, 6, 5, step4, 1, 1))
  386. {
  387. std::cerr << "error going from step1 to step3";
  388. return EXIT_FAILURE;
  389. }
  390. // try to go automatically to step 4 (another goTo step)
  391. workflow->goToStep("Step 4");
  392. if (!transitionTest(workflow, defaultTime, step3, step1, 10, 10, step2, 8, 8, step3, 7, 6, step4, 2, 2))
  393. {
  394. std::cerr << "error going from the first finish step to the second finish step";
  395. return EXIT_FAILURE;
  396. }
  397. // go to step 4, and then go forward (should not let you go past last step)
  398. workflow->goForward();
  399. QTimer::singleShot(defaultTime, &app, SLOT(quit()));
  400. app.exec();
  401. workflow->goForward();
  402. if (!transitionTest(workflow, defaultTime, step4, step1, 10, 10, step2, 8, 8, step3, 7, 7, step4, 3, 2))
  403. {
  404. std::cerr << "error going forward past last step - shouldn't let you";
  405. return EXIT_FAILURE;
  406. }
  407. // now try to go from step 4 to step 4 (should loop)
  408. workflow->goToStep("Step 4");
  409. if (!transitionTest(workflow, defaultTime, step4, step1, 10, 10, step2, 8, 8, step3, 7, 7, step4, 4, 3))
  410. {
  411. std::cerr << "error looping from step 4 to step 4";
  412. return EXIT_FAILURE;
  413. }
  414. // go back to step 3, and then go from step 3 to step 3 (should loop without hitting step4)
  415. workflow->goBackward();
  416. QTimer::singleShot(defaultTime, &app, SLOT(quit()));
  417. app.exec();
  418. workflow->goToStep("Step 3");
  419. if (!transitionTest(workflow, defaultTime, step3, step1, 10, 10, step2, 8, 8, step3, 9, 8, step4, 4, 4))
  420. {
  421. std::cerr << "error looping from step 3 to step 3";
  422. return EXIT_FAILURE;
  423. }
  424. // try to go automatically to step 4 and stay there by setting the property goBackToOriginStepUponSuccess to false
  425. workflow->setGoBackToOriginStepUponSuccess(false);
  426. workflow->goToStep("Step 4");
  427. if (!transitionTest(workflow, defaultTime, step4, step1, 10, 10, step2, 8, 8, step3, 9, 9, step4, 5, 4))
  428. {
  429. std::cerr << "error staying at step 4 if property goBackToOriginStepUponSuccess is false";
  430. return EXIT_FAILURE;
  431. }
  432. // after, going backwards to step 3,
  433. // try to go automatically to step 4 with the property goBackToOriginStepUponSuccess set to true
  434. workflow->setGoBackToOriginStepUponSuccess(true);
  435. workflow->goBackward(); // now at step3
  436. QTimer::singleShot(defaultTime, &app, SLOT(quit()));
  437. app.exec();
  438. workflow->goToStep("Step 4");
  439. if (!transitionTest(workflow, defaultTime, step3, step1, 10, 10, step2, 8, 8, step3, 11, 10, step4, 6, 6))
  440. {
  441. std::cerr << "error while coming back to step 3 if property goBackToOriginStepUponSuccess is true";
  442. return EXIT_FAILURE;
  443. }
  444. // handles deletions of the workflow, steps, states and transitions
  445. delete workflow;
  446. return EXIT_SUCCESS;
  447. }