roby 0.8.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (644) hide show
  1. checksums.yaml +7 -0
  2. data/.deep-cover.rb +3 -0
  3. data/.gitattributes +1 -0
  4. data/.gitignore +24 -0
  5. data/.simplecov +10 -0
  6. data/.travis.yml +17 -0
  7. data/.yardopts +4 -0
  8. data/Gemfile +15 -0
  9. data/README.md +11 -0
  10. data/Rakefile +47 -177
  11. data/benchmark/{alloc_misc.rb → attic/alloc_misc.rb} +2 -2
  12. data/benchmark/{discovery_latency.rb → attic/discovery_latency.rb} +19 -19
  13. data/benchmark/{garbage_collection.rb → attic/garbage_collection.rb} +9 -9
  14. data/benchmark/{genom.rb → attic/genom.rb} +0 -0
  15. data/benchmark/attic/transactions.rb +62 -0
  16. data/benchmark/plan_basic_operations.rb +28 -0
  17. data/benchmark/relations/graph.rb +63 -0
  18. data/benchmark/ruby/identity.rb +18 -0
  19. data/benchmark/ruby/set_intersect_vs_hash_merge.rb +39 -0
  20. data/benchmark/ruby/yield_vs_block.rb +35 -0
  21. data/benchmark/run +5 -0
  22. data/benchmark/synthetic_plan_modifications_with_transactions.rb +79 -0
  23. data/benchmark/transactions.rb +99 -51
  24. data/bin/roby +38 -197
  25. data/bin/roby-display +14 -0
  26. data/bin/roby-log +3 -176
  27. data/doc/guide/{src → attic}/abstraction/achieve_with.page +1 -1
  28. data/doc/guide/{src → attic}/abstraction/forwarding.page +1 -1
  29. data/doc/guide/{src → attic}/abstraction/hierarchy.page +1 -1
  30. data/doc/guide/{src → attic}/abstraction/index.page +1 -1
  31. data/doc/guide/{src → attic}/abstraction/task_models.page +1 -1
  32. data/doc/guide/{overview.rdoc → attic/cycle/api_overview.rdoc} +6 -1
  33. data/doc/guide/{src → attic}/cycle/cycle-overview.png +0 -0
  34. data/doc/guide/{src → attic}/cycle/cycle-overview.svg +0 -0
  35. data/doc/guide/attic/cycle/error_handling.page +98 -0
  36. data/doc/guide/{src → attic}/cycle/error_instantaneous_repair.png +0 -0
  37. data/doc/guide/{src → attic}/cycle/error_instantaneous_repair.svg +0 -0
  38. data/doc/guide/{src/cycle/error_handling.page → attic/cycle/error_sources.page} +46 -89
  39. data/doc/guide/{src → attic}/cycle/garbage_collection.page +1 -1
  40. data/doc/guide/{src → attic}/cycle/index.page +1 -1
  41. data/doc/guide/{src → attic}/cycle/propagation.page +11 -1
  42. data/doc/guide/{src → attic}/cycle/propagation_diamond.png +0 -0
  43. data/doc/guide/{src → attic}/cycle/propagation_diamond.svg +0 -0
  44. data/doc/guide/attic/plans/building_plans.page +89 -0
  45. data/doc/guide/attic/plans/code.page +192 -0
  46. data/doc/guide/{src/basics → attic/plans}/events.page +3 -4
  47. data/doc/guide/attic/plans/index.page +7 -0
  48. data/doc/guide/{plan_modifications.rdoc → attic/plans/plan_modifications.rdoc} +5 -3
  49. data/doc/guide/{src/basics → attic/plans}/plan_objects.page +2 -1
  50. data/doc/guide/attic/plans/querying_plans.page +5 -0
  51. data/doc/guide/{src/basics → attic/plans}/tasks.page +20 -20
  52. data/doc/guide/config.yaml +7 -4
  53. data/doc/guide/ext/extended_menu.rb +29 -0
  54. data/doc/guide/ext/init.rb +6 -0
  55. data/doc/guide/ext/rdoc_links.rb +7 -6
  56. data/doc/guide/src/advanced_concepts/history.page +5 -0
  57. data/doc/guide/src/advanced_concepts/index.page +11 -0
  58. data/doc/guide/src/advanced_concepts/recognizing_patterns.page +83 -0
  59. data/doc/guide/src/advanced_concepts/scheduling.page +87 -0
  60. data/doc/guide/src/advanced_concepts/transactions.page +5 -0
  61. data/doc/guide/src/advanced_concepts/unreachability.page +42 -0
  62. data/doc/guide/src/base.template +96 -0
  63. data/doc/guide/src/basics_shell_header.txt +5 -7
  64. data/doc/guide/src/building/action_coordination.page +96 -0
  65. data/doc/guide/src/building/actions.page +124 -0
  66. data/doc/guide/src/building/file_layout.page +71 -0
  67. data/doc/guide/src/building/index.page +50 -0
  68. data/doc/guide/src/building/patterns.page +86 -0
  69. data/doc/guide/src/building/patterns_forwarding.png +0 -0
  70. data/doc/guide/src/building/patterns_forwarding.svg +277 -0
  71. data/doc/guide/src/building/runtime.page +95 -0
  72. data/doc/guide/src/building/task_models.page +94 -0
  73. data/doc/guide/src/building/tasks.page +284 -0
  74. data/doc/guide/src/concepts/error_handling.page +100 -0
  75. data/doc/guide/src/concepts/exception_propagation.png +0 -0
  76. data/doc/guide/src/concepts/exception_propagation.svg +445 -0
  77. data/doc/guide/src/concepts/execution.page +85 -0
  78. data/doc/guide/src/concepts/execution.png +0 -0
  79. data/doc/guide/src/concepts/execution.svg +573 -0
  80. data/doc/guide/src/concepts/execution_cycle.png +0 -0
  81. data/doc/guide/src/concepts/garbage_collection.page +57 -0
  82. data/doc/guide/src/concepts/index.page +27 -0
  83. data/doc/guide/src/concepts/plans.page +101 -0
  84. data/doc/guide/src/concepts/policy.page +31 -0
  85. data/doc/guide/src/concepts/reactor.page +61 -0
  86. data/doc/guide/src/concepts/simple_plan_example.png +0 -0
  87. data/doc/guide/src/concepts/simple_plan_example.svg +376 -0
  88. data/doc/guide/src/default.template +9 -74
  89. data/doc/guide/src/event_relations/forward.page +71 -0
  90. data/doc/guide/src/event_relations/index.page +12 -0
  91. data/doc/guide/src/event_relations/scheduling_constraints.page +43 -0
  92. data/doc/guide/src/event_relations/signal.page +55 -0
  93. data/doc/guide/src/event_relations/temporal_constraints.page +77 -0
  94. data/doc/guide/src/htmldoc.metainfo +21 -8
  95. data/doc/guide/src/index.page +8 -3
  96. data/doc/guide/src/{introduction/install.page → installation/index.page} +37 -25
  97. data/doc/guide/src/installation/publications.page +14 -0
  98. data/doc/guide/src/{introduction → installation}/videos.page +14 -7
  99. data/doc/guide/src/interacting/index.page +16 -0
  100. data/doc/guide/src/interacting/run.page +33 -0
  101. data/doc/guide/src/interacting/shell.page +95 -0
  102. data/doc/guide/src/plugins/creating_plugins.page +72 -0
  103. data/doc/guide/src/plugins/index.page +27 -5
  104. data/doc/guide/src/plugins/{fault_tolerance.page → standard_plugins/fault_tolerance.page} +2 -2
  105. data/doc/guide/src/plugins/standard_plugins/index.page +11 -0
  106. data/doc/guide/src/plugins/{subsystems.page → standard_plugins/subsystems.page} +2 -2
  107. data/doc/guide/src/style_screen.css +687 -0
  108. data/doc/guide/src/task_relations/dependency.page +107 -0
  109. data/doc/guide/src/task_relations/executed_by.page +77 -0
  110. data/doc/guide/src/task_relations/index.page +12 -0
  111. data/doc/guide/src/task_relations/new_relations.page +119 -0
  112. data/doc/guide/src/task_relations/planned_by.page +46 -0
  113. data/doc/guide/src/tutorial/app.page +117 -0
  114. data/doc/guide/src/{basics → tutorial}/code_examples.page +6 -5
  115. data/doc/guide/src/{basics → tutorial}/dry.page +15 -15
  116. data/doc/guide/src/{basics → tutorial}/errors.page +43 -68
  117. data/doc/guide/src/tutorial/events.page +195 -0
  118. data/doc/guide/src/{basics → tutorial}/hierarchy.page +53 -52
  119. data/doc/guide/src/tutorial/index.page +13 -0
  120. data/doc/guide/src/tutorial/log_replay/goForward_1.png +0 -0
  121. data/doc/guide/src/tutorial/log_replay/goForward_2.png +0 -0
  122. data/doc/guide/src/tutorial/log_replay/goForward_3.png +0 -0
  123. data/doc/guide/src/{basics → tutorial}/log_replay/goForward_4.png +0 -0
  124. data/doc/guide/src/tutorial/log_replay/goForward_5.png +0 -0
  125. data/doc/guide/src/{basics → tutorial}/log_replay/hierarchy_error_1.png +0 -0
  126. data/doc/guide/src/{basics → tutorial}/log_replay/hierarchy_error_2.png +0 -0
  127. data/doc/guide/src/{basics → tutorial}/log_replay/hierarchy_error_3.png +0 -0
  128. data/doc/guide/src/tutorial/log_replay/moveto_code_error.png +0 -0
  129. data/doc/guide/src/{basics → tutorial}/log_replay/plan_repair_1.png +0 -0
  130. data/doc/guide/src/{basics → tutorial}/log_replay/plan_repair_2.png +0 -0
  131. data/doc/guide/src/{basics → tutorial}/log_replay/plan_repair_3.png +0 -0
  132. data/doc/guide/src/tutorial/log_replay/plan_repair_4.png +0 -0
  133. data/doc/guide/src/tutorial/log_replay/roby_log_main_window.png +0 -0
  134. data/doc/guide/src/{basics → tutorial}/log_replay/roby_log_relation_window.png +0 -0
  135. data/doc/guide/src/{basics → tutorial}/log_replay/roby_replay_event_representation.png +0 -0
  136. data/doc/guide/src/tutorial/relations_display.page +153 -0
  137. data/doc/guide/src/{basics → tutorial}/roby_cycle_overview.png +0 -0
  138. data/doc/guide/src/tutorial/shell.page +121 -0
  139. data/doc/guide/src/{basics → tutorial}/summary.page +1 -1
  140. data/doc/guide/src/tutorial/tasks.page +374 -0
  141. data/lib/roby.rb +102 -47
  142. data/lib/roby/actions.rb +17 -0
  143. data/lib/roby/actions/action.rb +80 -0
  144. data/lib/roby/actions/interface.rb +45 -0
  145. data/lib/roby/actions/library.rb +23 -0
  146. data/lib/roby/actions/models/action.rb +224 -0
  147. data/lib/roby/actions/models/coordination_action.rb +58 -0
  148. data/lib/roby/actions/models/interface.rb +22 -0
  149. data/lib/roby/actions/models/interface_base.rb +294 -0
  150. data/lib/roby/actions/models/library.rb +12 -0
  151. data/lib/roby/actions/models/method_action.rb +90 -0
  152. data/lib/roby/actions/task.rb +114 -0
  153. data/lib/roby/and_generator.rb +125 -0
  154. data/lib/roby/app.rb +2795 -829
  155. data/lib/roby/app/autotest_console_reporter.rb +138 -0
  156. data/lib/roby/app/base.rb +21 -0
  157. data/lib/roby/app/cucumber.rb +2 -0
  158. data/lib/roby/app/cucumber/controller.rb +439 -0
  159. data/lib/roby/app/cucumber/helpers.rb +280 -0
  160. data/lib/roby/app/cucumber/world.rb +32 -0
  161. data/lib/roby/app/debug.rb +136 -0
  162. data/lib/roby/app/gen.rb +2 -0
  163. data/lib/roby/app/rake.rb +178 -38
  164. data/lib/roby/app/robot_config.rb +9 -0
  165. data/lib/roby/app/robot_names.rb +115 -0
  166. data/lib/roby/app/run.rb +3 -2
  167. data/lib/roby/app/scripts.rb +72 -0
  168. data/lib/roby/app/scripts/autotest.rb +173 -0
  169. data/lib/roby/app/scripts/display.rb +2 -0
  170. data/lib/roby/app/scripts/restart.rb +52 -0
  171. data/lib/roby/app/scripts/results.rb +17 -8
  172. data/lib/roby/app/scripts/run.rb +155 -24
  173. data/lib/roby/app/scripts/shell.rb +147 -62
  174. data/lib/roby/app/scripts/test.rb +107 -22
  175. data/lib/roby/app/test_reporter.rb +74 -0
  176. data/lib/roby/app/test_server.rb +159 -0
  177. data/lib/roby/app/vagrant.rb +47 -0
  178. data/lib/roby/backports.rb +16 -0
  179. data/lib/roby/cli/display.rb +190 -0
  180. data/lib/roby/cli/exceptions.rb +17 -0
  181. data/lib/roby/cli/gen/actions/class.rb +5 -0
  182. data/lib/roby/cli/gen/actions/test.rb +6 -0
  183. data/lib/roby/cli/gen/app/.yardopts +6 -0
  184. data/lib/roby/cli/gen/app/README.md +28 -0
  185. data/lib/roby/cli/gen/app/Rakefile +15 -0
  186. data/{app → lib/roby/cli/gen/app}/config/app.yml +29 -39
  187. data/lib/roby/cli/gen/app/models/.gitattributes +1 -0
  188. data/{app → lib/roby/cli/gen/app/scripts}/controllers/.gitattributes +0 -0
  189. data/{app/data/.gitattributes → lib/roby/cli/gen/app/test/.gitignore} +0 -0
  190. data/lib/roby/cli/gen/class/class.rb +6 -0
  191. data/lib/roby/cli/gen/class/test.rb +7 -0
  192. data/lib/roby/cli/gen/helpers.rb +203 -0
  193. data/lib/roby/cli/gen/module/module.rb +5 -0
  194. data/lib/roby/cli/gen/module/test.rb +6 -0
  195. data/lib/roby/cli/gen/roby_app/config/init.rb +17 -0
  196. data/lib/roby/cli/gen/roby_app/config/robots/robot.rb +40 -0
  197. data/lib/roby/cli/gen/task/class.rb +44 -0
  198. data/lib/roby/cli/gen/task/test.rb +6 -0
  199. data/lib/roby/cli/gen_main.rb +120 -0
  200. data/lib/roby/cli/log.rb +276 -0
  201. data/lib/roby/cli/log/flamegraph.html +499 -0
  202. data/lib/roby/cli/log/flamegraph_renderer.rb +88 -0
  203. data/lib/roby/cli/main.rb +153 -0
  204. data/lib/roby/coordination.rb +60 -0
  205. data/lib/roby/coordination/action_script.rb +25 -0
  206. data/lib/roby/coordination/action_state_machine.rb +125 -0
  207. data/lib/roby/coordination/actions.rb +106 -0
  208. data/lib/roby/coordination/base.rb +145 -0
  209. data/lib/roby/coordination/calculus.rb +40 -0
  210. data/lib/roby/coordination/child.rb +28 -0
  211. data/lib/roby/coordination/event.rb +29 -0
  212. data/lib/roby/coordination/fault_handler.rb +25 -0
  213. data/lib/roby/coordination/fault_handling_task.rb +13 -0
  214. data/lib/roby/coordination/fault_response_table.rb +110 -0
  215. data/lib/roby/coordination/models/action_script.rb +64 -0
  216. data/lib/roby/coordination/models/action_state_machine.rb +224 -0
  217. data/lib/roby/coordination/models/actions.rb +191 -0
  218. data/lib/roby/coordination/models/arguments.rb +55 -0
  219. data/lib/roby/coordination/models/base.rb +176 -0
  220. data/lib/roby/coordination/models/capture.rb +86 -0
  221. data/lib/roby/coordination/models/child.rb +35 -0
  222. data/lib/roby/coordination/models/event.rb +41 -0
  223. data/lib/roby/coordination/models/exceptions.rb +42 -0
  224. data/lib/roby/coordination/models/fault_handler.rb +219 -0
  225. data/lib/roby/coordination/models/fault_response_table.rb +77 -0
  226. data/lib/roby/coordination/models/root.rb +22 -0
  227. data/lib/roby/coordination/models/script.rb +283 -0
  228. data/lib/roby/coordination/models/task.rb +184 -0
  229. data/lib/roby/coordination/models/task_from_action.rb +50 -0
  230. data/lib/roby/coordination/models/task_from_as_plan.rb +33 -0
  231. data/lib/roby/coordination/models/task_from_instanciation_object.rb +31 -0
  232. data/lib/roby/coordination/models/task_from_variable.rb +27 -0
  233. data/lib/roby/coordination/models/task_with_dependencies.rb +48 -0
  234. data/lib/roby/coordination/models/variable.rb +32 -0
  235. data/lib/roby/coordination/script.rb +200 -0
  236. data/lib/roby/coordination/script_instruction.rb +12 -0
  237. data/lib/roby/coordination/task.rb +45 -0
  238. data/lib/roby/coordination/task_base.rb +69 -0
  239. data/lib/roby/coordination/task_script.rb +293 -0
  240. data/lib/roby/coordination/task_state_machine.rb +308 -0
  241. data/lib/roby/decision_control.rb +33 -21
  242. data/lib/roby/distributed_object.rb +76 -0
  243. data/lib/roby/droby.rb +17 -0
  244. data/lib/roby/droby/droby_id.rb +6 -0
  245. data/lib/roby/droby/enable.rb +153 -0
  246. data/lib/roby/droby/event_logger.rb +189 -0
  247. data/lib/roby/droby/event_logging.rb +57 -0
  248. data/lib/roby/droby/exceptions.rb +14 -0
  249. data/lib/roby/droby/identifiable.rb +22 -0
  250. data/lib/roby/droby/logfile.rb +141 -0
  251. data/lib/roby/droby/logfile/client.rb +176 -0
  252. data/lib/roby/droby/logfile/file_format.md +97 -0
  253. data/lib/roby/droby/logfile/index.rb +117 -0
  254. data/lib/roby/droby/logfile/reader.rb +139 -0
  255. data/lib/roby/droby/logfile/server.rb +199 -0
  256. data/lib/roby/droby/logfile/writer.rb +114 -0
  257. data/lib/roby/droby/marshal.rb +264 -0
  258. data/lib/roby/droby/marshallable.rb +12 -0
  259. data/lib/roby/droby/null_event_logger.rb +25 -0
  260. data/lib/roby/droby/object_manager.rb +205 -0
  261. data/lib/roby/droby/peer_id.rb +6 -0
  262. data/lib/roby/droby/plan_rebuilder.rb +373 -0
  263. data/lib/roby/droby/rebuilt_plan.rb +160 -0
  264. data/lib/roby/droby/remote_droby_id.rb +6 -0
  265. data/lib/roby/droby/timepoints.rb +205 -0
  266. data/lib/roby/droby/timepoints_ctf.metadata.erb +101 -0
  267. data/lib/roby/droby/timepoints_ctf.rb +125 -0
  268. data/lib/roby/droby/v5.rb +14 -0
  269. data/lib/roby/droby/v5/builtin.rb +120 -0
  270. data/lib/roby/droby/v5/droby_class.rb +45 -0
  271. data/lib/roby/droby/v5/droby_constant.rb +81 -0
  272. data/lib/roby/droby/v5/droby_dump.rb +1026 -0
  273. data/lib/roby/droby/v5/droby_id.rb +44 -0
  274. data/lib/roby/droby/v5/droby_model.rb +82 -0
  275. data/lib/roby/droby/v5/peer_id.rb +10 -0
  276. data/lib/roby/droby/v5/remote_droby_id.rb +42 -0
  277. data/lib/roby/event.rb +79 -957
  278. data/lib/roby/event_constraints.rb +835 -0
  279. data/lib/roby/event_generator.rb +1047 -0
  280. data/lib/roby/event_structure/causal_link.rb +6 -0
  281. data/lib/roby/event_structure/forwarding.rb +6 -0
  282. data/lib/roby/event_structure/precedence.rb +7 -0
  283. data/lib/roby/event_structure/signal.rb +8 -0
  284. data/lib/roby/event_structure/temporal_constraints.rb +640 -0
  285. data/lib/roby/exceptions.rb +446 -152
  286. data/lib/roby/executable_plan.rb +549 -0
  287. data/lib/roby/execution_engine.rb +1997 -950
  288. data/lib/roby/filter_generator.rb +26 -0
  289. data/lib/roby/gui/chronicle_view.rb +225 -0
  290. data/lib/roby/gui/chronicle_widget.rb +925 -0
  291. data/lib/roby/gui/dot_id.rb +11 -0
  292. data/lib/roby/gui/exception_view.rb +44 -0
  293. data/lib/roby/gui/log_display.rb +273 -0
  294. data/lib/roby/gui/model_views.rb +2 -0
  295. data/lib/roby/gui/model_views/action_interface.rb +53 -0
  296. data/lib/roby/gui/model_views/task.rb +47 -0
  297. data/lib/roby/gui/model_views/task.rhtml +41 -0
  298. data/lib/roby/gui/object_info_view.rb +89 -0
  299. data/lib/roby/gui/plan_dot_layout.rb +427 -0
  300. data/lib/roby/gui/plan_rebuilder_widget.rb +357 -0
  301. data/lib/roby/gui/qt4_toMSecsSinceEpoch.rb +8 -0
  302. data/lib/roby/gui/relations_view.rb +278 -0
  303. data/lib/roby/gui/relations_view/relations.ui +139 -0
  304. data/lib/roby/gui/relations_view/relations_canvas.rb +1088 -0
  305. data/lib/roby/gui/relations_view/relations_config.rb +292 -0
  306. data/lib/roby/gui/relations_view/relations_view.ui +53 -0
  307. data/lib/roby/gui/scheduler_view.css +24 -0
  308. data/lib/roby/gui/scheduler_view.rb +46 -0
  309. data/lib/roby/gui/scheduler_view.rhtml +53 -0
  310. data/lib/roby/gui/stepping.rb +93 -0
  311. data/lib/roby/gui/stepping.ui +181 -0
  312. data/lib/roby/gui/styles.rb +81 -0
  313. data/lib/roby/gui/task_display_configuration.rb +42 -0
  314. data/lib/roby/gui/task_state_at.rb +38 -0
  315. data/lib/roby/hooks.rb +26 -0
  316. data/lib/roby/interface.rb +136 -469
  317. data/lib/roby/interface/async.rb +20 -0
  318. data/lib/roby/interface/async/action_monitor.rb +188 -0
  319. data/lib/roby/interface/async/interface.rb +498 -0
  320. data/lib/roby/interface/async/job_monitor.rb +213 -0
  321. data/lib/roby/interface/async/log.rb +238 -0
  322. data/lib/roby/interface/async/new_job_listener.rb +79 -0
  323. data/lib/roby/interface/async/ui_connector.rb +183 -0
  324. data/lib/roby/interface/client.rb +553 -0
  325. data/lib/roby/interface/command.rb +24 -0
  326. data/lib/roby/interface/command_argument.rb +16 -0
  327. data/lib/roby/interface/command_library.rb +92 -0
  328. data/lib/roby/interface/droby_channel.rb +174 -0
  329. data/lib/roby/interface/exceptions.rb +22 -0
  330. data/lib/roby/interface/interface.rb +655 -0
  331. data/lib/roby/interface/job.rb +47 -0
  332. data/lib/roby/interface/rest.rb +10 -0
  333. data/lib/roby/interface/rest/api.rb +29 -0
  334. data/lib/roby/interface/rest/helpers.rb +24 -0
  335. data/lib/roby/interface/rest/server.rb +212 -0
  336. data/lib/roby/interface/server.rb +154 -0
  337. data/lib/roby/interface/shell_client.rb +468 -0
  338. data/lib/roby/interface/shell_subcommand.rb +24 -0
  339. data/lib/roby/interface/subcommand_client.rb +35 -0
  340. data/lib/roby/interface/tcp.rb +168 -0
  341. data/lib/roby/models/arguments.rb +112 -0
  342. data/lib/roby/models/plan_object.rb +83 -0
  343. data/lib/roby/models/task.rb +835 -0
  344. data/lib/roby/models/task_event.rb +62 -0
  345. data/lib/roby/models/task_service.rb +78 -0
  346. data/lib/roby/or_generator.rb +88 -0
  347. data/lib/roby/plan.rb +1751 -864
  348. data/lib/roby/plan_object.rb +611 -0
  349. data/lib/roby/plan_service.rb +200 -0
  350. data/lib/roby/promise.rb +332 -0
  351. data/lib/roby/queries.rb +23 -0
  352. data/lib/roby/queries/and_matcher.rb +32 -0
  353. data/lib/roby/queries/any.rb +27 -0
  354. data/lib/roby/queries/code_error_matcher.rb +58 -0
  355. data/lib/roby/queries/event_generator_matcher.rb +9 -0
  356. data/lib/roby/queries/execution_exception_matcher.rb +165 -0
  357. data/lib/roby/queries/index.rb +165 -0
  358. data/lib/roby/queries/localized_error_matcher.rb +149 -0
  359. data/lib/roby/queries/matcher_base.rb +107 -0
  360. data/lib/roby/queries/none.rb +27 -0
  361. data/lib/roby/queries/not_matcher.rb +30 -0
  362. data/lib/roby/queries/op_matcher.rb +8 -0
  363. data/lib/roby/queries/or_matcher.rb +30 -0
  364. data/lib/roby/queries/plan_object_matcher.rb +363 -0
  365. data/lib/roby/queries/query.rb +188 -0
  366. data/lib/roby/queries/task_event_generator_matcher.rb +86 -0
  367. data/lib/roby/queries/task_matcher.rb +344 -0
  368. data/lib/roby/relations.rb +42 -678
  369. data/lib/roby/relations/bidirectional_directed_adjacency_graph.rb +492 -0
  370. data/lib/roby/relations/directed_relation_support.rb +268 -0
  371. data/lib/roby/relations/event_relation_graph.rb +19 -0
  372. data/lib/roby/relations/fork_merge_visitor.rb +154 -0
  373. data/lib/roby/relations/graph.rb +533 -0
  374. data/lib/roby/relations/models/directed_relation_support.rb +11 -0
  375. data/lib/roby/relations/models/graph.rb +75 -0
  376. data/lib/roby/relations/models/task_relation_graph.rb +18 -0
  377. data/lib/roby/relations/space.rb +380 -0
  378. data/lib/roby/relations/task_relation_graph.rb +20 -0
  379. data/lib/roby/robot.rb +85 -38
  380. data/lib/roby/schedulers/basic.rb +155 -25
  381. data/lib/roby/schedulers/null.rb +20 -0
  382. data/lib/roby/schedulers/reporting.rb +31 -0
  383. data/lib/roby/schedulers/state.rb +129 -0
  384. data/lib/roby/schedulers/temporal.rb +91 -0
  385. data/lib/roby/singletons.rb +87 -0
  386. data/lib/roby/standalone.rb +4 -2
  387. data/lib/roby/standard_errors.rb +405 -82
  388. data/lib/roby/state.rb +6 -3
  389. data/lib/roby/state/conf_model.rb +5 -0
  390. data/lib/roby/state/events.rb +181 -95
  391. data/lib/roby/state/goal_model.rb +77 -0
  392. data/lib/roby/state/open_struct.rb +591 -0
  393. data/lib/roby/state/open_struct_model.rb +68 -0
  394. data/lib/roby/state/pos.rb +45 -45
  395. data/lib/roby/state/shapes.rb +11 -11
  396. data/lib/roby/state/state_model.rb +303 -0
  397. data/lib/roby/state/task.rb +43 -0
  398. data/lib/roby/support.rb +88 -148
  399. data/lib/roby/task.rb +1361 -1750
  400. data/lib/roby/task_arguments.rb +428 -0
  401. data/lib/roby/task_event.rb +127 -0
  402. data/lib/roby/task_event_generator.rb +337 -0
  403. data/lib/roby/task_service.rb +6 -0
  404. data/lib/roby/task_structure/conflicts.rb +104 -0
  405. data/lib/roby/task_structure/dependency.rb +932 -0
  406. data/lib/roby/task_structure/error_handling.rb +118 -0
  407. data/lib/roby/task_structure/executed_by.rb +234 -0
  408. data/lib/roby/task_structure/planned_by.rb +90 -0
  409. data/lib/roby/tasks/aggregator.rb +37 -0
  410. data/lib/roby/tasks/external_process.rb +275 -0
  411. data/lib/roby/tasks/group.rb +27 -0
  412. data/lib/roby/tasks/null.rb +19 -0
  413. data/lib/roby/tasks/parallel.rb +43 -0
  414. data/lib/roby/tasks/sequence.rb +88 -0
  415. data/lib/roby/tasks/simple.rb +21 -0
  416. data/lib/roby/{thread_task.rb → tasks/thread.rb} +50 -24
  417. data/lib/roby/tasks/timeout.rb +17 -0
  418. data/lib/roby/tasks/virtual.rb +55 -0
  419. data/lib/roby/template_plan.rb +7 -0
  420. data/lib/roby/test/aruba_minitest.rb +74 -0
  421. data/lib/roby/test/assertion.rb +16 -0
  422. data/lib/roby/test/assertions.rb +490 -0
  423. data/lib/roby/test/common.rb +368 -591
  424. data/lib/roby/test/dsl.rb +149 -0
  425. data/lib/roby/test/error.rb +18 -0
  426. data/lib/roby/test/event_reporter.rb +83 -0
  427. data/lib/roby/test/execution_expectations.rb +1134 -0
  428. data/lib/roby/test/expect_execution.rb +151 -0
  429. data/lib/roby/test/minitest_helpers.rb +166 -0
  430. data/lib/roby/test/roby_app_helpers.rb +200 -0
  431. data/lib/roby/test/run_planners.rb +155 -0
  432. data/lib/roby/test/self.rb +112 -0
  433. data/lib/roby/test/spec.rb +198 -0
  434. data/lib/roby/test/tasks/empty_task.rb +4 -4
  435. data/lib/roby/test/tasks/goto.rb +28 -27
  436. data/lib/roby/test/teardown_plans.rb +100 -0
  437. data/lib/roby/test/testcase.rb +239 -307
  438. data/lib/roby/test/tools.rb +159 -155
  439. data/lib/roby/test/validate_state_machine.rb +75 -0
  440. data/lib/roby/transaction.rb +1125 -0
  441. data/lib/roby/transaction/event_generator_proxy.rb +63 -0
  442. data/lib/roby/transaction/plan_object_proxy.rb +99 -0
  443. data/lib/roby/transaction/plan_service_proxy.rb +43 -0
  444. data/lib/roby/transaction/proxying.rb +120 -0
  445. data/lib/roby/transaction/task_event_generator_proxy.rb +19 -0
  446. data/lib/roby/transaction/task_proxy.rb +135 -0
  447. data/lib/roby/until_generator.rb +30 -0
  448. data/lib/roby/version.rb +5 -0
  449. data/lib/roby/yard.rb +169 -0
  450. data/lib/yard-roby.rb +1 -0
  451. data/manifest.xml +32 -6
  452. data/roby.gemspec +59 -0
  453. metadata +788 -587
  454. data/Manifest.txt +0 -321
  455. data/NOTES +0 -4
  456. data/README.txt +0 -166
  457. data/TODO.txt +0 -146
  458. data/app/README.txt +0 -24
  459. data/app/Rakefile +0 -8
  460. data/app/config/ROBOT.rb +0 -5
  461. data/app/config/init.rb +0 -33
  462. data/app/config/roby.yml +0 -3
  463. data/app/controllers/ROBOT.rb +0 -2
  464. data/app/planners/ROBOT/main.rb +0 -6
  465. data/app/planners/main.rb +0 -5
  466. data/app/scripts/distributed +0 -3
  467. data/app/scripts/generate/bookmarks +0 -3
  468. data/app/scripts/replay +0 -3
  469. data/app/scripts/results +0 -3
  470. data/app/scripts/run +0 -3
  471. data/app/scripts/server +0 -3
  472. data/app/scripts/shell +0 -3
  473. data/app/scripts/test +0 -3
  474. data/app/tasks/.gitattributes +0 -0
  475. data/app/tasks/ROBOT/.gitattributes +0 -0
  476. data/bin/roby-shell +0 -25
  477. data/doc/guide/src/basics/app.page +0 -139
  478. data/doc/guide/src/basics/index.page +0 -11
  479. data/doc/guide/src/basics/log_replay/goForward_1.png +0 -0
  480. data/doc/guide/src/basics/log_replay/goForward_2.png +0 -0
  481. data/doc/guide/src/basics/log_replay/goForward_3.png +0 -0
  482. data/doc/guide/src/basics/log_replay/goForward_5.png +0 -0
  483. data/doc/guide/src/basics/log_replay/plan_repair_4.png +0 -0
  484. data/doc/guide/src/basics/log_replay/roby_log_main_window.png +0 -0
  485. data/doc/guide/src/basics/relations_display.page +0 -203
  486. data/doc/guide/src/basics/shell.page +0 -102
  487. data/doc/guide/src/default.css +0 -319
  488. data/doc/guide/src/introduction/index.page +0 -29
  489. data/doc/guide/src/introduction/publications.page +0 -14
  490. data/doc/guide/src/relations/dependency.page +0 -89
  491. data/doc/guide/src/relations/index.page +0 -12
  492. data/ext/droby/dump.cc +0 -175
  493. data/ext/droby/extconf.rb +0 -3
  494. data/ext/graph/algorithm.cc +0 -746
  495. data/ext/graph/extconf.rb +0 -7
  496. data/ext/graph/graph.cc +0 -575
  497. data/ext/graph/graph.hh +0 -183
  498. data/ext/graph/iterator_sequence.hh +0 -102
  499. data/ext/graph/undirected_dfs.hh +0 -226
  500. data/ext/graph/undirected_graph.hh +0 -421
  501. data/lib/roby/app/scripts/generate/bookmarks.rb +0 -162
  502. data/lib/roby/app/scripts/replay.rb +0 -31
  503. data/lib/roby/app/scripts/server.rb +0 -18
  504. data/lib/roby/basic_object.rb +0 -151
  505. data/lib/roby/config.rb +0 -14
  506. data/lib/roby/distributed.rb +0 -36
  507. data/lib/roby/distributed/base.rb +0 -448
  508. data/lib/roby/distributed/communication.rb +0 -875
  509. data/lib/roby/distributed/connection_space.rb +0 -616
  510. data/lib/roby/distributed/distributed_object.rb +0 -206
  511. data/lib/roby/distributed/drb.rb +0 -62
  512. data/lib/roby/distributed/notifications.rb +0 -531
  513. data/lib/roby/distributed/peer.rb +0 -555
  514. data/lib/roby/distributed/protocol.rb +0 -529
  515. data/lib/roby/distributed/proxy.rb +0 -343
  516. data/lib/roby/distributed/subscription.rb +0 -311
  517. data/lib/roby/distributed/transaction.rb +0 -498
  518. data/lib/roby/external_process_task.rb +0 -225
  519. data/lib/roby/graph.rb +0 -160
  520. data/lib/roby/log.rb +0 -3
  521. data/lib/roby/log/chronicle.rb +0 -303
  522. data/lib/roby/log/console.rb +0 -74
  523. data/lib/roby/log/data_stream.rb +0 -275
  524. data/lib/roby/log/dot.rb +0 -279
  525. data/lib/roby/log/event_stream.rb +0 -161
  526. data/lib/roby/log/file.rb +0 -396
  527. data/lib/roby/log/gui/basic_display.ui +0 -83
  528. data/lib/roby/log/gui/basic_display_ui.rb +0 -89
  529. data/lib/roby/log/gui/chronicle.rb +0 -26
  530. data/lib/roby/log/gui/chronicle_view.rb +0 -40
  531. data/lib/roby/log/gui/chronicle_view.ui +0 -70
  532. data/lib/roby/log/gui/chronicle_view_ui.rb +0 -90
  533. data/lib/roby/log/gui/data_displays.rb +0 -171
  534. data/lib/roby/log/gui/data_displays.ui +0 -155
  535. data/lib/roby/log/gui/data_displays_ui.rb +0 -146
  536. data/lib/roby/log/gui/notifications.rb +0 -26
  537. data/lib/roby/log/gui/relations.rb +0 -269
  538. data/lib/roby/log/gui/relations.ui +0 -123
  539. data/lib/roby/log/gui/relations_ui.rb +0 -120
  540. data/lib/roby/log/gui/relations_view.rb +0 -185
  541. data/lib/roby/log/gui/relations_view.ui +0 -149
  542. data/lib/roby/log/gui/relations_view_ui.rb +0 -144
  543. data/lib/roby/log/gui/replay.rb +0 -366
  544. data/lib/roby/log/gui/replay_controls.rb +0 -206
  545. data/lib/roby/log/gui/replay_controls.ui +0 -282
  546. data/lib/roby/log/gui/replay_controls_ui.rb +0 -249
  547. data/lib/roby/log/gui/runtime.rb +0 -130
  548. data/lib/roby/log/hooks.rb +0 -186
  549. data/lib/roby/log/logger.rb +0 -203
  550. data/lib/roby/log/notifications.rb +0 -244
  551. data/lib/roby/log/plan_rebuilder.rb +0 -468
  552. data/lib/roby/log/relations.rb +0 -1084
  553. data/lib/roby/log/server.rb +0 -547
  554. data/lib/roby/log/sqlite.rb +0 -47
  555. data/lib/roby/log/timings.rb +0 -233
  556. data/lib/roby/plan-object.rb +0 -371
  557. data/lib/roby/planning.rb +0 -13
  558. data/lib/roby/planning/loops.rb +0 -309
  559. data/lib/roby/planning/model.rb +0 -1012
  560. data/lib/roby/planning/task.rb +0 -180
  561. data/lib/roby/query.rb +0 -655
  562. data/lib/roby/relations/conflicts.rb +0 -67
  563. data/lib/roby/relations/dependency.rb +0 -358
  564. data/lib/roby/relations/ensured.rb +0 -19
  565. data/lib/roby/relations/error_handling.rb +0 -22
  566. data/lib/roby/relations/events.rb +0 -7
  567. data/lib/roby/relations/executed_by.rb +0 -208
  568. data/lib/roby/relations/influence.rb +0 -10
  569. data/lib/roby/relations/planned_by.rb +0 -63
  570. data/lib/roby/state/information.rb +0 -55
  571. data/lib/roby/state/state.rb +0 -367
  572. data/lib/roby/task-operations.rb +0 -186
  573. data/lib/roby/task_index.rb +0 -80
  574. data/lib/roby/test/distributed.rb +0 -230
  575. data/lib/roby/test/tasks/simple_task.rb +0 -23
  576. data/lib/roby/transactions.rb +0 -507
  577. data/lib/roby/transactions/proxy.rb +0 -325
  578. data/plugins/fault_injection/History.txt +0 -4
  579. data/plugins/fault_injection/README.txt +0 -34
  580. data/plugins/fault_injection/Rakefile +0 -12
  581. data/plugins/fault_injection/TODO.txt +0 -0
  582. data/plugins/fault_injection/app.rb +0 -52
  583. data/plugins/fault_injection/fault_injection.rb +0 -89
  584. data/plugins/fault_injection/test/test_fault_injection.rb +0 -78
  585. data/plugins/subsystems/README.txt +0 -37
  586. data/plugins/subsystems/Rakefile +0 -13
  587. data/plugins/subsystems/app.rb +0 -182
  588. data/plugins/subsystems/test/app/README +0 -24
  589. data/plugins/subsystems/test/app/Rakefile +0 -8
  590. data/plugins/subsystems/test/app/config/app.yml +0 -71
  591. data/plugins/subsystems/test/app/config/init.rb +0 -12
  592. data/plugins/subsystems/test/app/config/roby.yml +0 -3
  593. data/plugins/subsystems/test/app/planners/main.rb +0 -20
  594. data/plugins/subsystems/test/app/scripts/distributed +0 -3
  595. data/plugins/subsystems/test/app/scripts/replay +0 -3
  596. data/plugins/subsystems/test/app/scripts/results +0 -3
  597. data/plugins/subsystems/test/app/scripts/run +0 -3
  598. data/plugins/subsystems/test/app/scripts/server +0 -3
  599. data/plugins/subsystems/test/app/scripts/shell +0 -3
  600. data/plugins/subsystems/test/app/scripts/test +0 -3
  601. data/plugins/subsystems/test/app/tasks/services.rb +0 -15
  602. data/plugins/subsystems/test/test_subsystems.rb +0 -78
  603. data/test/distributed/test_communication.rb +0 -195
  604. data/test/distributed/test_connection.rb +0 -284
  605. data/test/distributed/test_execution.rb +0 -378
  606. data/test/distributed/test_mixed_plan.rb +0 -341
  607. data/test/distributed/test_plan_notifications.rb +0 -238
  608. data/test/distributed/test_protocol.rb +0 -525
  609. data/test/distributed/test_query.rb +0 -106
  610. data/test/distributed/test_remote_plan.rb +0 -491
  611. data/test/distributed/test_transaction.rb +0 -466
  612. data/test/mockups/external_process +0 -28
  613. data/test/mockups/tasks.rb +0 -27
  614. data/test/planning/test_loops.rb +0 -432
  615. data/test/planning/test_model.rb +0 -427
  616. data/test/planning/test_task.rb +0 -126
  617. data/test/relations/test_conflicts.rb +0 -42
  618. data/test/relations/test_dependency.rb +0 -324
  619. data/test/relations/test_ensured.rb +0 -38
  620. data/test/relations/test_executed_by.rb +0 -224
  621. data/test/relations/test_planned_by.rb +0 -56
  622. data/test/suite_core.rb +0 -29
  623. data/test/suite_distributed.rb +0 -10
  624. data/test/suite_planning.rb +0 -4
  625. data/test/suite_relations.rb +0 -8
  626. data/test/tasks/test_external_process.rb +0 -126
  627. data/test/tasks/test_thread_task.rb +0 -70
  628. data/test/test_bgl.rb +0 -528
  629. data/test/test_event.rb +0 -969
  630. data/test/test_exceptions.rb +0 -591
  631. data/test/test_execution_engine.rb +0 -987
  632. data/test/test_gui.rb +0 -20
  633. data/test/test_interface.rb +0 -43
  634. data/test/test_log.rb +0 -125
  635. data/test/test_log_server.rb +0 -133
  636. data/test/test_plan.rb +0 -418
  637. data/test/test_query.rb +0 -424
  638. data/test/test_relations.rb +0 -260
  639. data/test/test_state.rb +0 -432
  640. data/test/test_support.rb +0 -16
  641. data/test/test_task.rb +0 -1181
  642. data/test/test_testcase.rb +0 -138
  643. data/test/test_transactions.rb +0 -610
  644. data/test/test_transactions_proxy.rb +0 -216
@@ -0,0 +1,149 @@
1
+ module Roby
2
+ module Test
3
+ module DSL
4
+ include Minitest::Spec::DSL
5
+
6
+ extend MetaRuby::Attributes
7
+ inherited_attribute(:run_mode, :run_modes) { Array.new }
8
+ inherited_attribute(:enabled_robot, :enabled_robots) { Set.new }
9
+
10
+ # Enable this test only on the configurations in which the given
11
+ # block returns true
12
+ #
13
+ # If more than one call to the run_ methods is given, the test will
14
+ # run as soon as at least one of the conditions is met
15
+ #
16
+ # @yieldparam [Roby::Application] app
17
+ # @yieldreturn [Boolean] true if the spec should run, false
18
+ # otherwise
19
+ #
20
+ # By default, the tests are enabled in all modes. As soon as one of
21
+ # the run_ methods gets called, it is restricted to this particular
22
+ # mode
23
+ def run_if(&block)
24
+ run_modes << lambda(&block)
25
+ end
26
+
27
+ # Enable this test only on the given robot
28
+ def run_on_robot(*robot_names, &block)
29
+ if block
30
+ describe "in interactive mode" do
31
+ run_on_robot(*robot_names)
32
+ class_eval(&block)
33
+ end
34
+ else
35
+ enabled_robots.merge(robot_names)
36
+ end
37
+ end
38
+
39
+ # Enable this test in single mode
40
+ #
41
+ # By default, the tests are enabled in all modes. As soon as one of
42
+ # the run_ methods gets called, it is restricted to this particular
43
+ # mode
44
+ def run_single(&block)
45
+ if block
46
+ describe "in single mode" do
47
+ run_single
48
+ class_eval(&block)
49
+ end
50
+ else
51
+ run_if { |app| app.single? }
52
+ end
53
+ end
54
+
55
+ # Enable this test in simulated mode
56
+ #
57
+ # By default, the tests are enabled in all modes. As soon as one of
58
+ # the run_ methods gets called, it is restricted to this particular
59
+ # mode
60
+ def run_simulated(&block)
61
+ if block
62
+ describe "in simulation mode" do
63
+ run_simulated
64
+ class_eval(&block)
65
+ end
66
+ else
67
+ run_if { |app| app.simulation? }
68
+ end
69
+ end
70
+
71
+ # Enable this test in live (non-simulated mode)
72
+ #
73
+ # By default, the tests are enabled in all modes. As soon as one of
74
+ # the run_ methods gets called, it is restricted to this particular
75
+ # mode
76
+ def run_live(&block)
77
+ if block
78
+ describe "in live mode" do
79
+ run_live
80
+ class_eval(&block)
81
+ end
82
+ else
83
+ run_if { |app| !app.simulation? }
84
+ end
85
+ end
86
+
87
+ # Enable this test in interactive mode
88
+ #
89
+ # By default, the tests are enabled in all modes. As soon as one of
90
+ # the run_ methods gets called, it is restricted to this particular
91
+ # mode
92
+ def run_interactive(&block)
93
+ if block
94
+ describe "in interactive mode" do
95
+ run_interactive
96
+ class_eval(&block)
97
+ end
98
+ else
99
+ run_if { |app| !app.automatic_testing? }
100
+ end
101
+ end
102
+
103
+ # Tests whether self should run on the given app configuration
104
+ #
105
+ # @param [Roby::Application] app
106
+ # @return [Boolean]
107
+ def roby_should_run(test, app)
108
+ run_modes = all_run_mode
109
+ enabled_robots = all_enabled_robot
110
+ if !run_modes.empty? && run_modes.all? { |blk| !blk.call(app) }
111
+ test.skip("#{test.name} cannot run in this roby test configuration")
112
+ elsif !enabled_robots.empty? && !enabled_robots.include?(app.robot_name)
113
+ test.skip("#{test.name} can only be run on robots #{enabled_robots.sort.join(", ")}")
114
+ end
115
+ end
116
+
117
+ # Register sub-hooks
118
+ def describe(*desc, &block)
119
+ if kind_of?(Class)
120
+ super
121
+ else
122
+ behaviour = Module.new do
123
+ extend Roby::Test::DSL
124
+ class_eval(&block)
125
+ end
126
+
127
+ @__describe_blocks ||= Array.new
128
+ @__describe_blocks << [desc, behaviour]
129
+ end
130
+ end
131
+
132
+ def included(target)
133
+ super
134
+
135
+ @__describe_blocks ||= Array.new
136
+ if Class === target
137
+ @__describe_blocks.each do |desc, behaviour|
138
+ target.describe(desc) { include behaviour }
139
+ end
140
+ else
141
+ target_blocks = (target.instance_variable_get(:@__describe_blocks) || Array.new).
142
+ concat(@__describe_blocks)
143
+ target.instance_variable_set(:@__describe_blocks, target_blocks)
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
149
+
@@ -0,0 +1,18 @@
1
+ module Roby
2
+ module Test
3
+ # Class used to wrap exceptions so that #message returns the
4
+ # pretty-printed version of the message
5
+ class Error < RuntimeError
6
+ attr_reader :original_error
7
+
8
+ def initialize(original_error)
9
+ @original_error = original_error
10
+ end
11
+
12
+ def message
13
+ [super].concat(Roby.format_exception(original_error)).join("\n")
14
+ end
15
+ end
16
+ end
17
+ end
18
+
@@ -0,0 +1,83 @@
1
+ module Roby
2
+ module Test
3
+ # A event-logging compatible object that is used to
4
+ class EventReporter
5
+ # Whether the reporter should report anything
6
+ attr_predicate :enabled?, true
7
+
8
+ attr_reader :received_events
9
+
10
+ def initialize(io, enabled: false)
11
+ @io = io
12
+ @enabled = enabled
13
+ @filters = Array.new
14
+ @filters_out = Array.new
15
+ @received_events = Array.new
16
+ end
17
+
18
+ def dump_time
19
+ Time.now
20
+ end
21
+
22
+ def log_queue_size
23
+ 0
24
+ end
25
+
26
+ # Show only events matching this pattern
27
+ #
28
+ # Patterns are OR-ed (i.e. an event is displayed if it matches at
29
+ # least one pattern)
30
+ def filter(pattern)
31
+ @filters << pattern
32
+ end
33
+
34
+ # Hide events that match this pattern
35
+ #
36
+ # Patterns are OR-ed (i.e. an event is displayed if it matches at
37
+ # least one pattern)
38
+ def filter_out(pattern)
39
+ @filters_out << pattern
40
+ end
41
+
42
+ # Remove all filters
43
+ def clear_filters
44
+ @filters.clear
45
+ end
46
+
47
+ # Test if an event matches the filters setup
48
+ #
49
+ # It returns a match if no filters have been added
50
+ def matches_filter?(event)
51
+ if @filters.empty? || @filters.any? { |pattern| pattern === event.to_s }
52
+ @filters_out.empty? || @filters_out.none? { |pattern| pattern === event.to_s }
53
+ end
54
+ end
55
+
56
+ def dump_timepoint(event, time, *args)
57
+ dump(event, time, *args)
58
+ end
59
+
60
+ # This is the API used by Roby to actually log events
61
+ def dump(m, time, *args)
62
+ received_events << [m, time, *args]
63
+ if enabled? && matches_filter?(m)
64
+ @io.puts "#{time.to_hms} #{m}(#{args.map(&:to_s).join(", ")})"
65
+ end
66
+ end
67
+
68
+ def has_received_event?(expected_m, *expected_args)
69
+ received_events.any? do |m, time, args|
70
+ if args.size == expected_args.size
71
+ [m, *args].zip([expected_m, *expected_args]).all? do |v, expected|
72
+ expected === v
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ def flush_cycle(m, *args)
79
+ end
80
+ end
81
+ end
82
+ end
83
+
@@ -0,0 +1,1134 @@
1
+ module Roby
2
+ module Test
3
+ # Underlying implementation for Roby's when do end.expect ... feature
4
+ #
5
+ # The expectation's documented return value are NOT the values returned
6
+ # by the method itself, but the value that the user can expect out of
7
+ # the expectation run.
8
+ #
9
+ # @example execute until a block returns true. The call returns the block's return value
10
+ # expect_execution.to do
11
+ # achieve { plan.num_tasks }
12
+ # end # => the number of tasks from the plan
13
+ #
14
+ # @example execute until an event was emitted and an error raised. The call will in this case return the error object and the emitted event
15
+ # expect_execution.to do
16
+ # event = emit task.start_event
17
+ # error = have_error_matching CodeError
18
+ # [error, event]
19
+ # end # => the pair (raised error, emitted event)
20
+ #
21
+ class ExecutionExpectations
22
+ # @!group Expectations
23
+
24
+ # Expect that an event is not emitted after the expect_execution block
25
+ #
26
+ # Note that only one event propagation pass is guaranteed to happen
27
+ # before the "no emission" expectation is validated. I.e. this
28
+ # cannot test for the non-existence of a delayed emission
29
+ #
30
+ # @return [nil]
31
+ def not_emit(*generators, backtrace: caller(1))
32
+ generators.each do |generator|
33
+ if generator.kind_of?(EventGenerator)
34
+ add_expectation(NotEmitGenerator.new(generator, backtrace))
35
+ else
36
+ add_expectation(NotEmitGeneratorModel.new(generator, backtrace))
37
+ end
38
+ end
39
+ nil
40
+ end
41
+
42
+ # Expect that an event is emitted after the expect_execution block
43
+ #
44
+ # @param [EventGenerator,Queries::EventGeneratorMatcher] generator
45
+ # @return [Event,[Event]]
46
+ #
47
+ # @overload emit(generator)
48
+ # @param [EventGenerator] generator the generator we're waiting
49
+ # the emission of
50
+ # @return [Event] the emitted event
51
+ #
52
+ # @overload emit(generator_query)
53
+ # @param [Queries::EventGeneratorMatcher] query a query that
54
+ # matches the event whose emission we're watching.
55
+ # @return [[Event]] all the events whose generator match the
56
+ # query
57
+ #
58
+ # @example wait for the emission of the start event of any task of model MyTask. The call will return the emitted events that match this.
59
+ # expect_execution.to do
60
+ # emit find_tasks(MyTask).start_event
61
+ # end
62
+ #
63
+ def emit(*generators, backtrace: caller(1))
64
+ return_values = generators.map do |generator|
65
+ if generator.kind_of?(EventGenerator)
66
+ add_expectation(EmitGenerator.new(generator, backtrace))
67
+ else
68
+ add_expectation(EmitGeneratorModel.new(generator, backtrace))
69
+ end
70
+ end
71
+ if return_values.size == 1
72
+ return_values.first
73
+ else
74
+ return_values
75
+ end
76
+ end
77
+
78
+ # Expect that the generator(s) become unreachable
79
+ #
80
+ # @param [Array<EventGenerator>] generators the generators that are
81
+ # expected to become unreachable
82
+ # @return [Object,Array<Object>] if only one generator is provided,
83
+ # its unreachability reason. Otherwise, the unreachability reasons
84
+ # of all the generators, in the same order than the argument
85
+ def become_unreachable(*generators, backtrace: caller(1))
86
+ return_values = generators.map do |generator|
87
+ add_expectation(BecomeUnreachable.new(generator, backtrace))
88
+ end
89
+ if return_values.size == 1
90
+ return_values.first
91
+ else
92
+ return_values
93
+ end
94
+ end
95
+
96
+ # Expect that the generator(s) do not become unreachable
97
+ #
98
+ # @param [Array<EventGenerator>] generators the generators that are
99
+ # expected to not become unreachable
100
+ def not_become_unreachable(*generators, backtrace: caller(1))
101
+ generators.map do |generator|
102
+ add_expectation(NotBecomeUnreachable.new(generator, backtrace))
103
+ end
104
+ end
105
+
106
+ # Expect that the given block is true during a certain amount of
107
+ # time
108
+ #
109
+ # @param [Float] at_least_during the minimum duration in seconds. If
110
+ # zero, the expectations will run at least one execution cycle. The
111
+ # exact duration depends on the other expectations.
112
+ # @yieldparam [ExecutionEngine::PropagationInfo]
113
+ # all_propagation_info all that happened during the propagations
114
+ # since the beginning of expect_execution block. It contains event
115
+ # emissions and raised/caught errors.
116
+ # @yieldreturn [Boolean] expected to be true over duration seconds
117
+ def maintain(at_least_during: 0, description: nil, backtrace: caller(1), &block)
118
+ add_expectation(Maintain.new(at_least_during, block, description, backtrace))
119
+ end
120
+
121
+ # Expect that the given block returns true
122
+ #
123
+ # @yieldparam [ExecutionEngine::PropagationInfo]
124
+ # all_propagation_info all that happened during the propagations
125
+ # since the beginning of expect_execution block. It contains event
126
+ # emissions and raised/caught errors.
127
+ # @yieldreturn the value that should be returned by the expectation
128
+ def achieve(description: nil, backtrace: caller(1), &block)
129
+ add_expectation(Achieve.new(block, description, backtrace))
130
+ end
131
+
132
+ # Expect that the given task fails to start
133
+ #
134
+ # @param [Task] task
135
+ # @return [nil]
136
+ def fail_to_start(task, reason: nil, backtrace: caller(1))
137
+ add_expectation(FailsToStart.new(task, reason, backtrace))
138
+ end
139
+
140
+ # Expect that the given task starts
141
+ #
142
+ # @param [Task] task
143
+ # @return [Event] the task's start event
144
+ def start(task, backtrace: caller(1))
145
+ emit task.start_event, backtrace: backtrace
146
+ end
147
+
148
+ # Expect that the given task either starts or is running, and does not stop
149
+ #
150
+ # The caveats of {#not_emit} apply to the "does not stop" part of
151
+ # the expectation. This should usually be used in conjunction with a
152
+ # synchronization point.
153
+ #
154
+ # @example task keeps running until action_task stops
155
+ # expect_execution.to do
156
+ # keep_running task
157
+ # finish action_task
158
+ # end
159
+ #
160
+ # @param [Task] task
161
+ # @return [nil]
162
+ def have_running(task, backtrace: caller(1))
163
+ if !task.running?
164
+ emit task.start_event, backtrace: backtrace
165
+ end
166
+ not_emit task.stop_event
167
+ nil
168
+ end
169
+
170
+ # Expect that the given task finishes
171
+ #
172
+ # @param [Task] task
173
+ # @return [Event] the task's stop event
174
+ def finish(task, backtrace: caller(1))
175
+ emit task.start_event, backtrace: backtrace if !task.running?
176
+ emit task.stop_event, backtrace: backtrace
177
+ nil
178
+ end
179
+
180
+ # Expect that plan objects (task or event) are finalized
181
+ #
182
+ # @param [Array<PlanObject>] plan_objects
183
+ # @return [nil]
184
+ def finalize(*plan_objects, backtrace: caller(1))
185
+ plan_objects.each do |plan_object|
186
+ add_expectation(Finalize.new(plan_object, backtrace))
187
+ end
188
+ nil
189
+ end
190
+
191
+ # Expect that plan objects (task or event) are not finalized
192
+ #
193
+ # @param [Array<PlanObject>] plan_objects
194
+ # @return [nil]
195
+ def not_finalize(*plan_objects, backtrace: caller(1))
196
+ plan_objects.each do |plan_object|
197
+ add_expectation(NotFinalize.new(plan_object, backtrace))
198
+ end
199
+ nil
200
+ end
201
+
202
+ # Expect that the given task emits its internal_error event
203
+ #
204
+ # @param [Task] task
205
+ # @return [Event] the emitted internal error event
206
+ def have_internal_error(task, original_exception)
207
+ have_handled_error_matching original_exception.match.with_origin(task)
208
+ emit task.internal_error_event
209
+ end
210
+
211
+ # Expect that the given task is put in quarantine
212
+ #
213
+ # @param [Task] task
214
+ # @return [nil]
215
+ def quarantine(task, backtrace: caller(1))
216
+ add_expectation(Quarantine.new(task, backtrace))
217
+ nil
218
+ end
219
+
220
+ # Expect that the given promise finishes
221
+ #
222
+ # @param [Promise] promise
223
+ # @return [nil]
224
+ def finish_promise(promise, backtrace: caller(1))
225
+ add_expectation(PromiseFinishes.new(promise, backtrace))
226
+ nil
227
+ end
228
+
229
+ # Expect that an error is raised and not caught
230
+ #
231
+ # @param [#===] matcher an error matching object. These are usually
232
+ # obtained by calling {Exception.match} on an exception class and then refining
233
+ # the match by using the {Queries::LocalizedErrorMatcher} AP (see
234
+ # example above)I
235
+ # @return [ExecutionException] the matched exception
236
+ #
237
+ # @example expect that a {ChildFailedError} is raised from 'task'
238
+ # expect_execution.to do
239
+ # have_error_matching Roby::ChildFailedError.match.
240
+ # with_origin(task)
241
+ # end
242
+ def have_error_matching(matcher, backtrace: caller(1))
243
+ add_expectation(HaveErrorMatching.new(matcher, backtrace))
244
+ end
245
+
246
+ # Expect that an error is raised and caught
247
+ #
248
+ # @param [#===] matcher an error matching object. These are usually
249
+ # obtained by calling {Exception.match} on an exception class and then refining
250
+ # the match by using the {Queries::LocalizedErrorMatcher} API (see
251
+ # example above)
252
+ # @return [ExecutionException] the matched exception
253
+ #
254
+ # @example expect that a {ChildFailedError} is raised from 'task' and caught somewhere
255
+ # expect_execution.to do
256
+ # have_handled_error_matching Roby::ChildFailedError.match.
257
+ # with_origin(task)
258
+ # end
259
+ def have_handled_error_matching(matcher, backtrace: caller(1))
260
+ add_expectation(HaveHandledErrorMatching.new(matcher, backtrace))
261
+ end
262
+
263
+ # Expect that a framework error is added
264
+ #
265
+ # Framework errors are errors that are raised outside of user code.
266
+ # They are fatal inconsistencies, and cause the whole Roby instance
267
+ # to quit forcefully
268
+ #
269
+ # Unlike with {#have_error_matching} and
270
+ # {#have_handled_error_matching}, the error is rarely a
271
+ # LocalizedError. For simple exceptions, one can simply use the
272
+ # exception class to match.
273
+ def have_framework_error_matching(error, backtrace: caller(1))
274
+ add_expectation(HaveFrameworkError.new(error, backtrace))
275
+ end
276
+
277
+ # @!endgroup Expectations
278
+
279
+ # Parse a expect { } block into an Expectation object
280
+ #
281
+ # @return [Expectation]
282
+ def self.parse(test, plan, &block)
283
+ new(test, plan).parse(&block)
284
+ end
285
+
286
+ def parse(ret: true, &block)
287
+ block_ret = instance_eval(&block)
288
+ @return_objects = block_ret if ret
289
+ self
290
+ end
291
+
292
+ def initialize(test, plan)
293
+ @test = test
294
+ @plan = plan
295
+
296
+ @expectations = Array.new
297
+ @execute_blocks = Array.new
298
+
299
+ @scheduler = false
300
+ @timeout = 5
301
+ @join_all_waiting_work = true
302
+ @wait_until_timeout = true
303
+ @garbage_collect = false
304
+ @validate_unexpected_errors = true
305
+ @display_exceptions = false
306
+ end
307
+
308
+ def find_tasks(*args)
309
+ @test.plan.find_tasks(*args)
310
+ end
311
+
312
+ def respond_to_missing?(m, include_private)
313
+ @test.respond_to?(m) || super
314
+ end
315
+
316
+ def method_missing(m, *args, &block)
317
+ if @test.respond_to?(m)
318
+ @test.public_send(m, *args, &block)
319
+ else super
320
+ end
321
+ end
322
+
323
+ def self.format_propagation_info(propagation_info, indent: 0)
324
+ PP.pp(propagation_info).split("\n").join("\n" + " " * indent)
325
+ end
326
+
327
+ class Unmet < Minitest::Assertion
328
+ def initialize(expectations_with_explanations, propagation_info)
329
+ @expectations = expectations_with_explanations
330
+ @propagation_info = propagation_info
331
+ end
332
+
333
+ def each_original_exception
334
+ return enum_for(__method__) if !block_given?
335
+
336
+ @expectations.each do |_, e|
337
+ if e.kind_of?(Exception)
338
+ yield(e)
339
+ end
340
+ end
341
+ end
342
+
343
+ def pretty_print(pp)
344
+ pp.text "#{@expectations.size} unmet expectations"
345
+ @expectations.each do |exp, explanation|
346
+ pp.breakable
347
+ exp.pretty_print(pp)
348
+ if explanation
349
+ pp.text ", but did not because of "
350
+ explanation.pretty_print(pp)
351
+ end
352
+ end
353
+ if !@propagation_info.empty?
354
+ pp.breakable
355
+ @propagation_info.pretty_print(pp)
356
+ end
357
+ end
358
+
359
+ def to_s
360
+ PP.pp(self, "", 1).strip
361
+ end
362
+ end
363
+
364
+ class UnexpectedErrors < Minitest::Assertion
365
+ def initialize(errors)
366
+ @errors = errors
367
+ end
368
+
369
+ def each_original_exception
370
+ return enum_for(__method__) if !block_given?
371
+
372
+ @errors.each do |_, e|
373
+ if e.kind_of?(Exception)
374
+ yield(e)
375
+ end
376
+ end
377
+ end
378
+
379
+ def droby_dump(peer)
380
+ UnexpectedErrors.new(
381
+ @errors.map { |e| peer.dump(e) })
382
+ end
383
+
384
+ def proxy(peer)
385
+ UnexpectedErrors.new(
386
+ @errors.map { |e| peer.local_object(e) })
387
+ end
388
+
389
+ def to_s
390
+ "#{@errors.size} unexpected errors\n" +
391
+ @errors.each_with_index.map do |e, i|
392
+ formatted_execution_exception =
393
+ "[#{i + 1}/#{@errors.size}] " + Roby.format_exception(e).join("\n")
394
+
395
+ if e.kind_of?(ExecutionException)
396
+ e = e.exception
397
+ end
398
+ if e.backtrace && !e.backtrace.empty?
399
+ formatted_execution_exception += "\n " + e.backtrace.join("\n ")
400
+ end
401
+
402
+ sub_exceptions = Roby.flatten_exception(e)
403
+ sub_exceptions.delete(e)
404
+ formatted_sub_exceptions = sub_exceptions.each_with_index.map do |sub_e, sub_i|
405
+ formatted = "[#{sub_i}] " + Roby.format_exception(sub_e).join("\n ")
406
+ backtrace = Roby.format_backtrace(sub_e)
407
+ if !backtrace.empty?
408
+ formatted += " " + backtrace.join("\n ")
409
+ end
410
+ formatted
411
+ end.join("\n ")
412
+
413
+ if !formatted_sub_exceptions.empty?
414
+ formatted_execution_exception += "\n " + formatted_sub_exceptions
415
+ end
416
+ formatted_execution_exception
417
+ end.join("\n")
418
+ end
419
+ end
420
+
421
+ # @!group Setup
422
+
423
+ # @!method timeout(timeout)
424
+ #
425
+ # How long will the test wait either for asynchronous jobs (if
426
+ # #wait_until_timeout is false and #join_all_waiting_work is true)
427
+ # or until it succeeds (if #wait_until_timeout is true)
428
+ #
429
+ # @param [Float] timeout
430
+ #
431
+ # The default is 5s
432
+ dsl_attribute :timeout
433
+
434
+ # @!method wait_until_timeout(wait)
435
+ #
436
+ # Whether the execution will run until the timeout if the
437
+ # expectations have not been met yet.
438
+ #
439
+ # The default is 5s
440
+ #
441
+ # @param [Boolean] wait
442
+ dsl_attribute :wait_until_timeout
443
+
444
+ # @!method join_all_waiting_work(join)
445
+ #
446
+ # Whether the expectation test should wait for asynchronous work to
447
+ # finish between event propagations
448
+ #
449
+ # The default is true
450
+ #
451
+ # @param [Boolean] join
452
+ dsl_attribute :join_all_waiting_work
453
+
454
+ # @!method scheduler(enabled_or_scheduler)
455
+ #
456
+ # Controls the scheduler
457
+ #
458
+ # The default is false
459
+ #
460
+ # @overload scheduler(enabled)
461
+ # @param [Boolean] enabled controls whether the scheduler is
462
+ # enabled or not
463
+ #
464
+ # @overload scheduler(scheduler)
465
+ # @param [Schedulers::Basic] the scheduler object that should be used
466
+ dsl_attribute :scheduler
467
+
468
+ # @!method garbage_collect(enable)
469
+ #
470
+ # Whether a garbage collection pass should be run
471
+ #
472
+ # The default is false
473
+ #
474
+ # @param [Boolean] enable
475
+ dsl_attribute :garbage_collect
476
+
477
+ # @!method validate_unexpected_errors(enable)
478
+ #
479
+ # Whether the expectations will pass if exceptions are propagated
480
+ # that are not explicitely expected
481
+ #
482
+ # The default is true
483
+ #
484
+ # @param [Boolean] enable
485
+ dsl_attribute :validate_unexpected_errors
486
+
487
+ # @!method display_exceptions(enable)
488
+ #
489
+ # Whether exceptions should be displayed by the execution engine
490
+ #
491
+ # The default is false
492
+ #
493
+ # @param [Boolean] enable
494
+ dsl_attribute :display_exceptions
495
+
496
+ # @!endgroup Setup
497
+
498
+ # Add a new expectation to be run during {#verify}
499
+ def add_expectation(expectation)
500
+ @expectations << expectation
501
+ expectation
502
+ end
503
+
504
+ # Queue a block for execution
505
+ #
506
+ # This is meant to be used by expectation objects which require to
507
+ # perform some actions in execution context.
508
+ def execute(&block)
509
+ @execute_blocks << block
510
+ nil
511
+ end
512
+
513
+ # Whether some blocks have been queued for execution with
514
+ # {#execute}
515
+ def has_pending_execute_blocks?
516
+ !@execute_blocks.empty?
517
+ end
518
+
519
+ def with_execution_engine_setup
520
+ engine = @plan.execution_engine
521
+ current_scheduler = engine.scheduler
522
+ current_scheduler_state = engine.scheduler.enabled?
523
+ current_display_exceptions = engine.display_exceptions?
524
+ if !@display_exceptions.nil?
525
+ engine.display_exceptions = @display_exceptions
526
+ end
527
+ if !@scheduler.nil?
528
+ if @scheduler != true && @scheduler != false
529
+ engine.scheduler = @scheduler
530
+ else
531
+ engine.scheduler.enabled = @scheduler
532
+ end
533
+ end
534
+
535
+ yield
536
+ ensure
537
+ engine.scheduler = current_scheduler
538
+ engine.scheduler.enabled = current_scheduler_state
539
+ engine.display_exceptions = current_display_exceptions
540
+ end
541
+
542
+ # Verify that executing the given block in event propagation context
543
+ # will cause the expectations to be met
544
+ #
545
+ # @return [Object] a value or array of value as returned by the
546
+ # parsed block. If the block returns expectations, they are
547
+ # converted to a user-visible object by calling their
548
+ # #return_object method. Each expectation documents this as their
549
+ # return value (for instance, {#achieve} returns the block's
550
+ # "trueish" value)
551
+ def verify(&block)
552
+ all_propagation_info = ExecutionEngine::PropagationInfo.new
553
+ timeout_deadline = Time.now + @timeout
554
+
555
+ if block
556
+ @execute_blocks << block
557
+ end
558
+
559
+ begin
560
+ engine = @plan.execution_engine
561
+ engine.start_new_cycle
562
+ with_execution_engine_setup do
563
+ propagation_info = engine.process_events(raise_framework_errors: false, garbage_collect_pass: @garbage_collect) do
564
+ @execute_blocks.delete_if do |block|
565
+ block.call
566
+ true
567
+ end
568
+ end
569
+ all_propagation_info.merge(propagation_info)
570
+
571
+ exceptions = engine.cycle_end(Hash.new, raise_framework_errors: false)
572
+ all_propagation_info.framework_errors.concat(exceptions)
573
+ end
574
+
575
+ unmet = find_all_unmet_expectations(all_propagation_info)
576
+ unachievable = unmet.find_all { |expectation| expectation.unachievable?(all_propagation_info) }
577
+ if !unachievable.empty?
578
+ unachievable = unachievable.map do |expectation|
579
+ [expectation, expectation.explain_unachievable(all_propagation_info)]
580
+ end
581
+ raise Unmet.new(unachievable, all_propagation_info)
582
+ end
583
+
584
+ if @validate_unexpected_errors
585
+ validate_has_no_unexpected_error(all_propagation_info)
586
+ end
587
+
588
+ remaining_timeout = timeout_deadline - Time.now
589
+ break if remaining_timeout < 0
590
+
591
+ if engine.has_waiting_work? && @join_all_waiting_work
592
+ _, propagation_info = with_execution_engine_setup do
593
+ engine.join_all_waiting_work(timeout: remaining_timeout)
594
+ end
595
+ all_propagation_info.merge(propagation_info)
596
+ elsif !has_pending_execute_blocks? && unmet.empty?
597
+ break
598
+ end
599
+ end while has_pending_execute_blocks? || @wait_until_timeout || (engine.has_waiting_work? && @join_all_waiting_work)
600
+
601
+ unmet = find_all_unmet_expectations(all_propagation_info)
602
+ if !unmet.empty?
603
+ raise Unmet.new(unmet, all_propagation_info)
604
+ end
605
+
606
+ if @validate_unexpected_errors
607
+ validate_has_no_unexpected_error(all_propagation_info)
608
+ end
609
+
610
+ if @return_objects.respond_to?(:to_ary)
611
+ @return_objects.map do |obj|
612
+ if obj.respond_to?(:return_object)
613
+ obj.return_object
614
+ else
615
+ obj
616
+ end
617
+ end
618
+ else
619
+ obj = @return_objects
620
+ if obj.respond_to?(:return_object)
621
+ obj.return_object
622
+ else
623
+ obj
624
+ end
625
+ end
626
+ end
627
+
628
+ def validate_has_no_unexpected_error(propagation_info)
629
+ unexpected_errors = propagation_info.exceptions.find_all do |e|
630
+ unexpected_error?(e)
631
+ end
632
+ unexpected_errors.concat propagation_info.each_framework_error.
633
+ map(&:first).find_all { |e| unexpected_error?(e) }
634
+
635
+ # Look for internal_error_event, which is how the tasks report
636
+ # on their internal errors
637
+ internal_errors = propagation_info.emitted_events.find_all do |ev|
638
+ if ev.generator.respond_to?(:symbol) && ev.generator.symbol == :internal_error
639
+ exceptions_context = ev.context.find_all { |obj| obj.kind_of?(Exception) }
640
+ !exceptions_context.any? { |exception| @expectations.any? { |expectation| expectation.relates_to_error?(ExecutionException.new(exception)) } }
641
+ end
642
+ end
643
+
644
+ unexpected_errors += internal_errors.flat_map { |ev| ev.context }
645
+ if !unexpected_errors.empty?
646
+ raise UnexpectedErrors.new(unexpected_errors)
647
+ end
648
+ end
649
+
650
+ def unexpected_error?(error)
651
+ @expectations.each do |expectation|
652
+ if expectation.relates_to_error?(error)
653
+ return false
654
+ elsif error.respond_to?(:original_exceptions)
655
+ error.original_exceptions.each do |orig_e|
656
+ if expectation.relates_to_error?(orig_e)
657
+ return false
658
+ end
659
+ end
660
+ end
661
+ end
662
+ true
663
+ end
664
+
665
+ def find_all_unmet_expectations(all_propagation_info)
666
+ @expectations.find_all do |exp|
667
+ !exp.update_match(all_propagation_info)
668
+ end
669
+ end
670
+
671
+ # Null implementation of an expectation
672
+ class Expectation
673
+ attr_reader :backtrace
674
+
675
+ def initialize(backtrace)
676
+ @backtrace = backtrace
677
+ end
678
+
679
+ # Verifies whether the expectation is met at this point
680
+ #
681
+ # This method is meant to update
682
+ def update_match(propagation_info)
683
+ true
684
+ end
685
+ def unachievable?(propagation_info)
686
+ false
687
+ end
688
+ def explain_unachievable(propagation_info)
689
+ nil
690
+ end
691
+ def relates_to_error?(error)
692
+ false
693
+ end
694
+ end
695
+
696
+ class NotEmitGenerator < Expectation
697
+ def initialize(generator, backtrace)
698
+ super(backtrace)
699
+ @generator = generator
700
+ @related_error_matcher = Queries::LocalizedErrorMatcher.new.
701
+ with_origin(@generator).
702
+ to_execution_exception_matcher
703
+ end
704
+
705
+ def to_s
706
+ "#{@generator} should not be emitted"
707
+ end
708
+
709
+ def update_match(propagation_info)
710
+ @emitted_events = propagation_info.emitted_events.
711
+ find_all { |ev| ev.generator == @generator }
712
+ @emitted_events.empty?
713
+ end
714
+
715
+ def unachievable?(propagation_info)
716
+ !@emitted_events.empty?
717
+ end
718
+
719
+ def explain_unachievable(propagation_info)
720
+ @emitted_events.first
721
+ end
722
+
723
+ def relates_to_error?(error)
724
+ @related_error_matcher === error
725
+ end
726
+ end
727
+
728
+ class NotEmitGeneratorModel < Expectation
729
+ attr_reader :generator_model
730
+
731
+ def initialize(event_query, backtrace)
732
+ super(backtrace)
733
+ @event_query = event_query
734
+ @generators = Array.new
735
+ @related_error_matchers = Array.new
736
+ @emitted_events = Array.new
737
+ end
738
+
739
+ def to_s
740
+ "no events matching #{@event_query} should be emitted"
741
+ end
742
+
743
+ def update_match(propagation_info)
744
+ @emitted_events = propagation_info.emitted_events.
745
+ find_all do |ev|
746
+ if @event_query === ev.generator
747
+ @generators << ev.generator
748
+ @related_error_matchers << Queries::LocalizedErrorMatcher.new.
749
+ with_origin(ev.generator).
750
+ to_execution_exception_matcher
751
+ end
752
+ end
753
+ @emitted_events.empty?
754
+ end
755
+
756
+ def unachievable?(propagation_info)
757
+ !@emitted_events.empty?
758
+ end
759
+
760
+ def explain_unachievable(propagation_info)
761
+ @emitted_events.first
762
+ end
763
+
764
+ def relates_to_error?(error)
765
+ @related_error_matchers.any? { |match| match === error }
766
+ end
767
+ end
768
+
769
+ class EmitGeneratorModel < Expectation
770
+ attr_reader :generator_model
771
+
772
+ def initialize(event_query, backtrace)
773
+ super(backtrace)
774
+ @event_query = event_query
775
+ @generators = Array.new
776
+ @related_error_matchers = Array.new
777
+ @emitted_events = Array.new
778
+ end
779
+
780
+ def to_s
781
+ "at least one event matching #{@event_query} should be emitted"
782
+ end
783
+
784
+ def update_match(propagation_info)
785
+ @emitted_events = propagation_info.emitted_events.
786
+ find_all do |ev|
787
+ if @event_query === ev.generator
788
+ @generators << ev.generator
789
+ @related_error_matchers << Queries::LocalizedErrorMatcher.new.
790
+ with_origin(ev.generator).
791
+ to_execution_exception_matcher
792
+ end
793
+ end
794
+ !@emitted_events.empty?
795
+ end
796
+
797
+ def return_object
798
+ @emitted_events
799
+ end
800
+
801
+ def relates_to_error?(error)
802
+ @related_error_matchers.any? { |match| match === error }
803
+ end
804
+ end
805
+
806
+ class EmitGenerator < Expectation
807
+ attr_reader :generator
808
+
809
+ def initialize(generator, backtrace)
810
+ super(backtrace)
811
+ @generator = generator
812
+ @related_error_matcher = Queries::LocalizedErrorMatcher.new.
813
+ with_origin(@generator).
814
+ to_execution_exception_matcher
815
+ end
816
+
817
+ def to_s
818
+ "#{@generator} should be emitted"
819
+ end
820
+
821
+ def update_match(propagation_info)
822
+ @emitted_events = propagation_info.emitted_events.
823
+ find_all { |ev| ev.generator == @generator }
824
+ !@emitted_events.empty?
825
+ end
826
+
827
+ def return_object
828
+ @emitted_events.first
829
+ end
830
+
831
+ def unachievable?(propagation_info)
832
+ @generator.unreachable?
833
+ end
834
+
835
+ def explain_unachievable(propagation_info)
836
+ @generator.unreachability_reason
837
+ end
838
+
839
+ def relates_to_error?(error)
840
+ @related_error_matcher === error
841
+ end
842
+ end
843
+
844
+ class ErrorExpectation < Expectation
845
+ def initialize(matcher, backtrace)
846
+ super(backtrace)
847
+ @matcher = matcher.to_execution_exception_matcher
848
+ @matched_execution_exceptions = Array.new
849
+ @matched_exceptions = Array.new
850
+ end
851
+
852
+ def update_match(exceptions, emitted_events)
853
+ @matched_execution_exceptions = exceptions.
854
+ find_all { |error| @matcher === error }
855
+ matched_exceptions = @matched_execution_exceptions.
856
+ map(&:exception).to_set
857
+
858
+ emitted_events.each do |ev|
859
+ next if !ev.generator.respond_to?(:symbol) || ev.generator.symbol != :internal_error
860
+
861
+ ev.context.each do |obj|
862
+ if obj.kind_of?(Exception) && (@matcher === ExecutionException.new(obj))
863
+ matched_exceptions << obj
864
+ end
865
+ end
866
+ end
867
+
868
+ @matched_exceptions = matched_exceptions.flat_map do |e|
869
+ Roby.flatten_exception(e).to_a
870
+ end.to_set
871
+ !@matched_exceptions.empty?
872
+ end
873
+
874
+ def relates_to_error?(execution_exception)
875
+ @matched_execution_exceptions.include?(execution_exception) ||
876
+ @matched_exceptions.include?(execution_exception.exception) ||
877
+ Roby.flatten_exception(execution_exception.exception).
878
+ any? { |e| @matched_exceptions.include?(e) }
879
+ end
880
+
881
+ def return_object
882
+ @matched_execution_exceptions.first
883
+ end
884
+ end
885
+
886
+ class HaveErrorMatching < ErrorExpectation
887
+ def update_match(propagation_info)
888
+ super(propagation_info.exceptions, propagation_info.emitted_events)
889
+ end
890
+
891
+ def to_s
892
+ "should have an error matching #{@matcher}"
893
+ end
894
+ end
895
+
896
+ class HaveHandledErrorMatching < ErrorExpectation
897
+ def update_match(propagation_info)
898
+ super(propagation_info.handled_errors.map(&:first), propagation_info.emitted_events)
899
+ end
900
+
901
+ def to_s
902
+ "should have handled an error matching #{@matcher}"
903
+ end
904
+ end
905
+
906
+ class Quarantine < Expectation
907
+ def initialize(task, backtrace)
908
+ super(backtrace)
909
+ @task = task
910
+ end
911
+
912
+ def update_match(propagation_info)
913
+ @task.quarantined?
914
+ end
915
+
916
+ def to_s
917
+ "#{@task} should be quarantined"
918
+ end
919
+ end
920
+
921
+ class BecomeUnreachable < Expectation
922
+ def initialize(generator, backtrace)
923
+ super(backtrace)
924
+ @generator = generator
925
+ end
926
+
927
+ def update_match(propagation_info)
928
+ @generator.unreachable?
929
+ end
930
+
931
+ def return_object
932
+ @generator.unreachability_reason
933
+ end
934
+
935
+ def to_s
936
+ "#{@generator} should be unreachable"
937
+ end
938
+ end
939
+
940
+ class NotBecomeUnreachable < Expectation
941
+ def initialize(generator, backtrace)
942
+ super(backtrace)
943
+ @generator = generator
944
+ end
945
+
946
+ def update_match(propagation_info)
947
+ !@generator.unreachable?
948
+ end
949
+
950
+ def unachievable?(propagation_info)
951
+ @generator.unreachable?
952
+ end
953
+
954
+ def to_s
955
+ "#{@generator} should not be unreachable"
956
+ end
957
+ end
958
+
959
+ class FailsToStart < Expectation
960
+ def initialize(task, reason, backtrace)
961
+ super(backtrace)
962
+ @task = task
963
+ @reason = reason
964
+ if @reason && @reason.respond_to?(:to_execution_exception_matcher)
965
+ @reason = @reason.to_execution_exception_matcher
966
+ @related_error_matcher = LocalizedError.match.with_original_exception(@reason).
967
+ to_execution_exception_matcher
968
+ end
969
+ end
970
+
971
+ def update_match(propagation_info)
972
+ if !@task.failed_to_start?
973
+ false
974
+ elsif !@reason
975
+ true
976
+ else
977
+ @reason === @task.failure_reason
978
+ end
979
+ end
980
+
981
+ def unachievable?(propagation_info)
982
+ if @reason && @task.failed_to_start?
983
+ !(@reason === @task.failure_reason)
984
+ end
985
+ end
986
+
987
+ def relates_to_error?(exception)
988
+ if @reason
989
+ (@reason === exception) || (@related_error_matcher === exception)
990
+ end
991
+ end
992
+
993
+ def explain_unachievable(propagation_info)
994
+ "#{@task.failure_reason} does not match #{@reason}"
995
+ end
996
+
997
+ def return_object
998
+ @task.failure_reason
999
+ end
1000
+
1001
+ def to_s
1002
+ "#{@generator} should fail to start"
1003
+ end
1004
+ end
1005
+
1006
+ class PromiseFinishes < Expectation
1007
+ def initialize(promise, backtrace)
1008
+ super(backtrace)
1009
+ @promise = promise
1010
+ end
1011
+
1012
+ def update_match(propagation_info)
1013
+ @promise.complete?
1014
+ end
1015
+
1016
+ def to_s
1017
+ "#{@promise} should have finished"
1018
+ end
1019
+ end
1020
+
1021
+ class HaveFrameworkError < Expectation
1022
+ def initialize(error_matcher, backtrace)
1023
+ super(backtrace)
1024
+ @error_matcher = error_matcher
1025
+ end
1026
+
1027
+ def update_match(propagation_info)
1028
+ @matched_exceptions = propagation_info.framework_errors.
1029
+ map(&:first).find_all { |e| @error_matcher === e }
1030
+ !@matched_exceptions.empty?
1031
+ end
1032
+
1033
+ def relates_to_error?(error)
1034
+ @matched_exceptions.include?(error)
1035
+ end
1036
+
1037
+ def to_s
1038
+ "should have a framework error matching #{@error_matcher}"
1039
+ end
1040
+ end
1041
+
1042
+ class Maintain < Expectation
1043
+ def initialize(at_least_during, block, description, backtrace)
1044
+ super(backtrace)
1045
+ @at_least_during = at_least_during
1046
+ @description = description
1047
+ @block = block
1048
+ @deadline = Time.now + at_least_during
1049
+ @failed = false
1050
+ end
1051
+
1052
+ def update_match(propagation_info)
1053
+ if !@block.call(propagation_info)
1054
+ @failed = true
1055
+ return false
1056
+ elsif Time.now > @deadline
1057
+ return true
1058
+ end
1059
+ end
1060
+
1061
+ def unachievable?(propagation_info)
1062
+ @failed
1063
+ end
1064
+
1065
+ def explain_unachievable(propagation_info)
1066
+ "#{self} returned false"
1067
+ end
1068
+
1069
+ def to_s
1070
+ if @description
1071
+ @description
1072
+ else
1073
+ @backtrace[0].to_s
1074
+ end
1075
+ end
1076
+ end
1077
+
1078
+ class Achieve < Expectation
1079
+ def initialize(block, description, backtrace)
1080
+ super(backtrace)
1081
+ @description = description
1082
+ @block = block
1083
+ end
1084
+
1085
+ def update_match(propagation_info)
1086
+ @achieved ||= @block.call(propagation_info)
1087
+ end
1088
+
1089
+ def return_object
1090
+ @achieved
1091
+ end
1092
+
1093
+ def to_s
1094
+ if @description
1095
+ @description
1096
+ else
1097
+ @backtrace[0].to_s
1098
+ end
1099
+ end
1100
+ end
1101
+
1102
+ class NotFinalize < Expectation
1103
+ def initialize(plan_object, backtrace)
1104
+ super(backtrace)
1105
+ @plan_object = plan_object
1106
+ end
1107
+
1108
+ def update_match(propagation_info)
1109
+ @plan_object.plan
1110
+ end
1111
+
1112
+ def to_s
1113
+ "#{@plan_object} should not be finalized"
1114
+ end
1115
+ end
1116
+
1117
+ class Finalize < Expectation
1118
+ def initialize(plan_object, backtrace)
1119
+ super(backtrace)
1120
+ @plan_object = plan_object
1121
+ end
1122
+
1123
+ def update_match(propagation_info)
1124
+ !@plan_object.plan
1125
+ end
1126
+
1127
+ def to_s
1128
+ "#{@plan_object} should be finalized"
1129
+ end
1130
+ end
1131
+ end
1132
+ end
1133
+ end
1134
+