roby 0.8.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
+