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.
- checksums.yaml +7 -0
- data/.deep-cover.rb +3 -0
- data/.gitattributes +1 -0
- data/.gitignore +24 -0
- data/.simplecov +10 -0
- data/.travis.yml +17 -0
- data/.yardopts +4 -0
- data/Gemfile +15 -0
- data/README.md +11 -0
- data/Rakefile +47 -177
- data/benchmark/{alloc_misc.rb → attic/alloc_misc.rb} +2 -2
- data/benchmark/{discovery_latency.rb → attic/discovery_latency.rb} +19 -19
- data/benchmark/{garbage_collection.rb → attic/garbage_collection.rb} +9 -9
- data/benchmark/{genom.rb → attic/genom.rb} +0 -0
- data/benchmark/attic/transactions.rb +62 -0
- data/benchmark/plan_basic_operations.rb +28 -0
- data/benchmark/relations/graph.rb +63 -0
- data/benchmark/ruby/identity.rb +18 -0
- data/benchmark/ruby/set_intersect_vs_hash_merge.rb +39 -0
- data/benchmark/ruby/yield_vs_block.rb +35 -0
- data/benchmark/run +5 -0
- data/benchmark/synthetic_plan_modifications_with_transactions.rb +79 -0
- data/benchmark/transactions.rb +99 -51
- data/bin/roby +38 -197
- data/bin/roby-display +14 -0
- data/bin/roby-log +3 -176
- data/doc/guide/{src → attic}/abstraction/achieve_with.page +1 -1
- data/doc/guide/{src → attic}/abstraction/forwarding.page +1 -1
- data/doc/guide/{src → attic}/abstraction/hierarchy.page +1 -1
- data/doc/guide/{src → attic}/abstraction/index.page +1 -1
- data/doc/guide/{src → attic}/abstraction/task_models.page +1 -1
- data/doc/guide/{overview.rdoc → attic/cycle/api_overview.rdoc} +6 -1
- data/doc/guide/{src → attic}/cycle/cycle-overview.png +0 -0
- data/doc/guide/{src → attic}/cycle/cycle-overview.svg +0 -0
- data/doc/guide/attic/cycle/error_handling.page +98 -0
- data/doc/guide/{src → attic}/cycle/error_instantaneous_repair.png +0 -0
- data/doc/guide/{src → attic}/cycle/error_instantaneous_repair.svg +0 -0
- data/doc/guide/{src/cycle/error_handling.page → attic/cycle/error_sources.page} +46 -89
- data/doc/guide/{src → attic}/cycle/garbage_collection.page +1 -1
- data/doc/guide/{src → attic}/cycle/index.page +1 -1
- data/doc/guide/{src → attic}/cycle/propagation.page +11 -1
- data/doc/guide/{src → attic}/cycle/propagation_diamond.png +0 -0
- data/doc/guide/{src → attic}/cycle/propagation_diamond.svg +0 -0
- data/doc/guide/attic/plans/building_plans.page +89 -0
- data/doc/guide/attic/plans/code.page +192 -0
- data/doc/guide/{src/basics → attic/plans}/events.page +3 -4
- data/doc/guide/attic/plans/index.page +7 -0
- data/doc/guide/{plan_modifications.rdoc → attic/plans/plan_modifications.rdoc} +5 -3
- data/doc/guide/{src/basics → attic/plans}/plan_objects.page +2 -1
- data/doc/guide/attic/plans/querying_plans.page +5 -0
- data/doc/guide/{src/basics → attic/plans}/tasks.page +20 -20
- data/doc/guide/config.yaml +7 -4
- data/doc/guide/ext/extended_menu.rb +29 -0
- data/doc/guide/ext/init.rb +6 -0
- data/doc/guide/ext/rdoc_links.rb +7 -6
- data/doc/guide/src/advanced_concepts/history.page +5 -0
- data/doc/guide/src/advanced_concepts/index.page +11 -0
- data/doc/guide/src/advanced_concepts/recognizing_patterns.page +83 -0
- data/doc/guide/src/advanced_concepts/scheduling.page +87 -0
- data/doc/guide/src/advanced_concepts/transactions.page +5 -0
- data/doc/guide/src/advanced_concepts/unreachability.page +42 -0
- data/doc/guide/src/base.template +96 -0
- data/doc/guide/src/basics_shell_header.txt +5 -7
- data/doc/guide/src/building/action_coordination.page +96 -0
- data/doc/guide/src/building/actions.page +124 -0
- data/doc/guide/src/building/file_layout.page +71 -0
- data/doc/guide/src/building/index.page +50 -0
- data/doc/guide/src/building/patterns.page +86 -0
- data/doc/guide/src/building/patterns_forwarding.png +0 -0
- data/doc/guide/src/building/patterns_forwarding.svg +277 -0
- data/doc/guide/src/building/runtime.page +95 -0
- data/doc/guide/src/building/task_models.page +94 -0
- data/doc/guide/src/building/tasks.page +284 -0
- data/doc/guide/src/concepts/error_handling.page +100 -0
- data/doc/guide/src/concepts/exception_propagation.png +0 -0
- data/doc/guide/src/concepts/exception_propagation.svg +445 -0
- data/doc/guide/src/concepts/execution.page +85 -0
- data/doc/guide/src/concepts/execution.png +0 -0
- data/doc/guide/src/concepts/execution.svg +573 -0
- data/doc/guide/src/concepts/execution_cycle.png +0 -0
- data/doc/guide/src/concepts/garbage_collection.page +57 -0
- data/doc/guide/src/concepts/index.page +27 -0
- data/doc/guide/src/concepts/plans.page +101 -0
- data/doc/guide/src/concepts/policy.page +31 -0
- data/doc/guide/src/concepts/reactor.page +61 -0
- data/doc/guide/src/concepts/simple_plan_example.png +0 -0
- data/doc/guide/src/concepts/simple_plan_example.svg +376 -0
- data/doc/guide/src/default.template +9 -74
- data/doc/guide/src/event_relations/forward.page +71 -0
- data/doc/guide/src/event_relations/index.page +12 -0
- data/doc/guide/src/event_relations/scheduling_constraints.page +43 -0
- data/doc/guide/src/event_relations/signal.page +55 -0
- data/doc/guide/src/event_relations/temporal_constraints.page +77 -0
- data/doc/guide/src/htmldoc.metainfo +21 -8
- data/doc/guide/src/index.page +8 -3
- data/doc/guide/src/{introduction/install.page → installation/index.page} +37 -25
- data/doc/guide/src/installation/publications.page +14 -0
- data/doc/guide/src/{introduction → installation}/videos.page +14 -7
- data/doc/guide/src/interacting/index.page +16 -0
- data/doc/guide/src/interacting/run.page +33 -0
- data/doc/guide/src/interacting/shell.page +95 -0
- data/doc/guide/src/plugins/creating_plugins.page +72 -0
- data/doc/guide/src/plugins/index.page +27 -5
- data/doc/guide/src/plugins/{fault_tolerance.page → standard_plugins/fault_tolerance.page} +2 -2
- data/doc/guide/src/plugins/standard_plugins/index.page +11 -0
- data/doc/guide/src/plugins/{subsystems.page → standard_plugins/subsystems.page} +2 -2
- data/doc/guide/src/style_screen.css +687 -0
- data/doc/guide/src/task_relations/dependency.page +107 -0
- data/doc/guide/src/task_relations/executed_by.page +77 -0
- data/doc/guide/src/task_relations/index.page +12 -0
- data/doc/guide/src/task_relations/new_relations.page +119 -0
- data/doc/guide/src/task_relations/planned_by.page +46 -0
- data/doc/guide/src/tutorial/app.page +117 -0
- data/doc/guide/src/{basics → tutorial}/code_examples.page +6 -5
- data/doc/guide/src/{basics → tutorial}/dry.page +15 -15
- data/doc/guide/src/{basics → tutorial}/errors.page +43 -68
- data/doc/guide/src/tutorial/events.page +195 -0
- data/doc/guide/src/{basics → tutorial}/hierarchy.page +53 -52
- data/doc/guide/src/tutorial/index.page +13 -0
- data/doc/guide/src/tutorial/log_replay/goForward_1.png +0 -0
- data/doc/guide/src/tutorial/log_replay/goForward_2.png +0 -0
- data/doc/guide/src/tutorial/log_replay/goForward_3.png +0 -0
- data/doc/guide/src/{basics → tutorial}/log_replay/goForward_4.png +0 -0
- data/doc/guide/src/tutorial/log_replay/goForward_5.png +0 -0
- data/doc/guide/src/{basics → tutorial}/log_replay/hierarchy_error_1.png +0 -0
- data/doc/guide/src/{basics → tutorial}/log_replay/hierarchy_error_2.png +0 -0
- data/doc/guide/src/{basics → tutorial}/log_replay/hierarchy_error_3.png +0 -0
- data/doc/guide/src/tutorial/log_replay/moveto_code_error.png +0 -0
- data/doc/guide/src/{basics → tutorial}/log_replay/plan_repair_1.png +0 -0
- data/doc/guide/src/{basics → tutorial}/log_replay/plan_repair_2.png +0 -0
- data/doc/guide/src/{basics → tutorial}/log_replay/plan_repair_3.png +0 -0
- data/doc/guide/src/tutorial/log_replay/plan_repair_4.png +0 -0
- data/doc/guide/src/tutorial/log_replay/roby_log_main_window.png +0 -0
- data/doc/guide/src/{basics → tutorial}/log_replay/roby_log_relation_window.png +0 -0
- data/doc/guide/src/{basics → tutorial}/log_replay/roby_replay_event_representation.png +0 -0
- data/doc/guide/src/tutorial/relations_display.page +153 -0
- data/doc/guide/src/{basics → tutorial}/roby_cycle_overview.png +0 -0
- data/doc/guide/src/tutorial/shell.page +121 -0
- data/doc/guide/src/{basics → tutorial}/summary.page +1 -1
- data/doc/guide/src/tutorial/tasks.page +374 -0
- data/lib/roby.rb +102 -47
- data/lib/roby/actions.rb +17 -0
- data/lib/roby/actions/action.rb +80 -0
- data/lib/roby/actions/interface.rb +45 -0
- data/lib/roby/actions/library.rb +23 -0
- data/lib/roby/actions/models/action.rb +224 -0
- data/lib/roby/actions/models/coordination_action.rb +58 -0
- data/lib/roby/actions/models/interface.rb +22 -0
- data/lib/roby/actions/models/interface_base.rb +294 -0
- data/lib/roby/actions/models/library.rb +12 -0
- data/lib/roby/actions/models/method_action.rb +90 -0
- data/lib/roby/actions/task.rb +114 -0
- data/lib/roby/and_generator.rb +125 -0
- data/lib/roby/app.rb +2795 -829
- data/lib/roby/app/autotest_console_reporter.rb +138 -0
- data/lib/roby/app/base.rb +21 -0
- data/lib/roby/app/cucumber.rb +2 -0
- data/lib/roby/app/cucumber/controller.rb +439 -0
- data/lib/roby/app/cucumber/helpers.rb +280 -0
- data/lib/roby/app/cucumber/world.rb +32 -0
- data/lib/roby/app/debug.rb +136 -0
- data/lib/roby/app/gen.rb +2 -0
- data/lib/roby/app/rake.rb +178 -38
- data/lib/roby/app/robot_config.rb +9 -0
- data/lib/roby/app/robot_names.rb +115 -0
- data/lib/roby/app/run.rb +3 -2
- data/lib/roby/app/scripts.rb +72 -0
- data/lib/roby/app/scripts/autotest.rb +173 -0
- data/lib/roby/app/scripts/display.rb +2 -0
- data/lib/roby/app/scripts/restart.rb +52 -0
- data/lib/roby/app/scripts/results.rb +17 -8
- data/lib/roby/app/scripts/run.rb +155 -24
- data/lib/roby/app/scripts/shell.rb +147 -62
- data/lib/roby/app/scripts/test.rb +107 -22
- data/lib/roby/app/test_reporter.rb +74 -0
- data/lib/roby/app/test_server.rb +159 -0
- data/lib/roby/app/vagrant.rb +47 -0
- data/lib/roby/backports.rb +16 -0
- data/lib/roby/cli/display.rb +190 -0
- data/lib/roby/cli/exceptions.rb +17 -0
- data/lib/roby/cli/gen/actions/class.rb +5 -0
- data/lib/roby/cli/gen/actions/test.rb +6 -0
- data/lib/roby/cli/gen/app/.yardopts +6 -0
- data/lib/roby/cli/gen/app/README.md +28 -0
- data/lib/roby/cli/gen/app/Rakefile +15 -0
- data/{app → lib/roby/cli/gen/app}/config/app.yml +29 -39
- data/lib/roby/cli/gen/app/models/.gitattributes +1 -0
- data/{app → lib/roby/cli/gen/app/scripts}/controllers/.gitattributes +0 -0
- data/{app/data/.gitattributes → lib/roby/cli/gen/app/test/.gitignore} +0 -0
- data/lib/roby/cli/gen/class/class.rb +6 -0
- data/lib/roby/cli/gen/class/test.rb +7 -0
- data/lib/roby/cli/gen/helpers.rb +203 -0
- data/lib/roby/cli/gen/module/module.rb +5 -0
- data/lib/roby/cli/gen/module/test.rb +6 -0
- data/lib/roby/cli/gen/roby_app/config/init.rb +17 -0
- data/lib/roby/cli/gen/roby_app/config/robots/robot.rb +40 -0
- data/lib/roby/cli/gen/task/class.rb +44 -0
- data/lib/roby/cli/gen/task/test.rb +6 -0
- data/lib/roby/cli/gen_main.rb +120 -0
- data/lib/roby/cli/log.rb +276 -0
- data/lib/roby/cli/log/flamegraph.html +499 -0
- data/lib/roby/cli/log/flamegraph_renderer.rb +88 -0
- data/lib/roby/cli/main.rb +153 -0
- data/lib/roby/coordination.rb +60 -0
- data/lib/roby/coordination/action_script.rb +25 -0
- data/lib/roby/coordination/action_state_machine.rb +125 -0
- data/lib/roby/coordination/actions.rb +106 -0
- data/lib/roby/coordination/base.rb +145 -0
- data/lib/roby/coordination/calculus.rb +40 -0
- data/lib/roby/coordination/child.rb +28 -0
- data/lib/roby/coordination/event.rb +29 -0
- data/lib/roby/coordination/fault_handler.rb +25 -0
- data/lib/roby/coordination/fault_handling_task.rb +13 -0
- data/lib/roby/coordination/fault_response_table.rb +110 -0
- data/lib/roby/coordination/models/action_script.rb +64 -0
- data/lib/roby/coordination/models/action_state_machine.rb +224 -0
- data/lib/roby/coordination/models/actions.rb +191 -0
- data/lib/roby/coordination/models/arguments.rb +55 -0
- data/lib/roby/coordination/models/base.rb +176 -0
- data/lib/roby/coordination/models/capture.rb +86 -0
- data/lib/roby/coordination/models/child.rb +35 -0
- data/lib/roby/coordination/models/event.rb +41 -0
- data/lib/roby/coordination/models/exceptions.rb +42 -0
- data/lib/roby/coordination/models/fault_handler.rb +219 -0
- data/lib/roby/coordination/models/fault_response_table.rb +77 -0
- data/lib/roby/coordination/models/root.rb +22 -0
- data/lib/roby/coordination/models/script.rb +283 -0
- data/lib/roby/coordination/models/task.rb +184 -0
- data/lib/roby/coordination/models/task_from_action.rb +50 -0
- data/lib/roby/coordination/models/task_from_as_plan.rb +33 -0
- data/lib/roby/coordination/models/task_from_instanciation_object.rb +31 -0
- data/lib/roby/coordination/models/task_from_variable.rb +27 -0
- data/lib/roby/coordination/models/task_with_dependencies.rb +48 -0
- data/lib/roby/coordination/models/variable.rb +32 -0
- data/lib/roby/coordination/script.rb +200 -0
- data/lib/roby/coordination/script_instruction.rb +12 -0
- data/lib/roby/coordination/task.rb +45 -0
- data/lib/roby/coordination/task_base.rb +69 -0
- data/lib/roby/coordination/task_script.rb +293 -0
- data/lib/roby/coordination/task_state_machine.rb +308 -0
- data/lib/roby/decision_control.rb +33 -21
- data/lib/roby/distributed_object.rb +76 -0
- data/lib/roby/droby.rb +17 -0
- data/lib/roby/droby/droby_id.rb +6 -0
- data/lib/roby/droby/enable.rb +153 -0
- data/lib/roby/droby/event_logger.rb +189 -0
- data/lib/roby/droby/event_logging.rb +57 -0
- data/lib/roby/droby/exceptions.rb +14 -0
- data/lib/roby/droby/identifiable.rb +22 -0
- data/lib/roby/droby/logfile.rb +141 -0
- data/lib/roby/droby/logfile/client.rb +176 -0
- data/lib/roby/droby/logfile/file_format.md +97 -0
- data/lib/roby/droby/logfile/index.rb +117 -0
- data/lib/roby/droby/logfile/reader.rb +139 -0
- data/lib/roby/droby/logfile/server.rb +199 -0
- data/lib/roby/droby/logfile/writer.rb +114 -0
- data/lib/roby/droby/marshal.rb +264 -0
- data/lib/roby/droby/marshallable.rb +12 -0
- data/lib/roby/droby/null_event_logger.rb +25 -0
- data/lib/roby/droby/object_manager.rb +205 -0
- data/lib/roby/droby/peer_id.rb +6 -0
- data/lib/roby/droby/plan_rebuilder.rb +373 -0
- data/lib/roby/droby/rebuilt_plan.rb +160 -0
- data/lib/roby/droby/remote_droby_id.rb +6 -0
- data/lib/roby/droby/timepoints.rb +205 -0
- data/lib/roby/droby/timepoints_ctf.metadata.erb +101 -0
- data/lib/roby/droby/timepoints_ctf.rb +125 -0
- data/lib/roby/droby/v5.rb +14 -0
- data/lib/roby/droby/v5/builtin.rb +120 -0
- data/lib/roby/droby/v5/droby_class.rb +45 -0
- data/lib/roby/droby/v5/droby_constant.rb +81 -0
- data/lib/roby/droby/v5/droby_dump.rb +1026 -0
- data/lib/roby/droby/v5/droby_id.rb +44 -0
- data/lib/roby/droby/v5/droby_model.rb +82 -0
- data/lib/roby/droby/v5/peer_id.rb +10 -0
- data/lib/roby/droby/v5/remote_droby_id.rb +42 -0
- data/lib/roby/event.rb +79 -957
- data/lib/roby/event_constraints.rb +835 -0
- data/lib/roby/event_generator.rb +1047 -0
- data/lib/roby/event_structure/causal_link.rb +6 -0
- data/lib/roby/event_structure/forwarding.rb +6 -0
- data/lib/roby/event_structure/precedence.rb +7 -0
- data/lib/roby/event_structure/signal.rb +8 -0
- data/lib/roby/event_structure/temporal_constraints.rb +640 -0
- data/lib/roby/exceptions.rb +446 -152
- data/lib/roby/executable_plan.rb +549 -0
- data/lib/roby/execution_engine.rb +1997 -950
- data/lib/roby/filter_generator.rb +26 -0
- data/lib/roby/gui/chronicle_view.rb +225 -0
- data/lib/roby/gui/chronicle_widget.rb +925 -0
- data/lib/roby/gui/dot_id.rb +11 -0
- data/lib/roby/gui/exception_view.rb +44 -0
- data/lib/roby/gui/log_display.rb +273 -0
- data/lib/roby/gui/model_views.rb +2 -0
- data/lib/roby/gui/model_views/action_interface.rb +53 -0
- data/lib/roby/gui/model_views/task.rb +47 -0
- data/lib/roby/gui/model_views/task.rhtml +41 -0
- data/lib/roby/gui/object_info_view.rb +89 -0
- data/lib/roby/gui/plan_dot_layout.rb +427 -0
- data/lib/roby/gui/plan_rebuilder_widget.rb +357 -0
- data/lib/roby/gui/qt4_toMSecsSinceEpoch.rb +8 -0
- data/lib/roby/gui/relations_view.rb +278 -0
- data/lib/roby/gui/relations_view/relations.ui +139 -0
- data/lib/roby/gui/relations_view/relations_canvas.rb +1088 -0
- data/lib/roby/gui/relations_view/relations_config.rb +292 -0
- data/lib/roby/gui/relations_view/relations_view.ui +53 -0
- data/lib/roby/gui/scheduler_view.css +24 -0
- data/lib/roby/gui/scheduler_view.rb +46 -0
- data/lib/roby/gui/scheduler_view.rhtml +53 -0
- data/lib/roby/gui/stepping.rb +93 -0
- data/lib/roby/gui/stepping.ui +181 -0
- data/lib/roby/gui/styles.rb +81 -0
- data/lib/roby/gui/task_display_configuration.rb +42 -0
- data/lib/roby/gui/task_state_at.rb +38 -0
- data/lib/roby/hooks.rb +26 -0
- data/lib/roby/interface.rb +136 -469
- data/lib/roby/interface/async.rb +20 -0
- data/lib/roby/interface/async/action_monitor.rb +188 -0
- data/lib/roby/interface/async/interface.rb +498 -0
- data/lib/roby/interface/async/job_monitor.rb +213 -0
- data/lib/roby/interface/async/log.rb +238 -0
- data/lib/roby/interface/async/new_job_listener.rb +79 -0
- data/lib/roby/interface/async/ui_connector.rb +183 -0
- data/lib/roby/interface/client.rb +553 -0
- data/lib/roby/interface/command.rb +24 -0
- data/lib/roby/interface/command_argument.rb +16 -0
- data/lib/roby/interface/command_library.rb +92 -0
- data/lib/roby/interface/droby_channel.rb +174 -0
- data/lib/roby/interface/exceptions.rb +22 -0
- data/lib/roby/interface/interface.rb +655 -0
- data/lib/roby/interface/job.rb +47 -0
- data/lib/roby/interface/rest.rb +10 -0
- data/lib/roby/interface/rest/api.rb +29 -0
- data/lib/roby/interface/rest/helpers.rb +24 -0
- data/lib/roby/interface/rest/server.rb +212 -0
- data/lib/roby/interface/server.rb +154 -0
- data/lib/roby/interface/shell_client.rb +468 -0
- data/lib/roby/interface/shell_subcommand.rb +24 -0
- data/lib/roby/interface/subcommand_client.rb +35 -0
- data/lib/roby/interface/tcp.rb +168 -0
- data/lib/roby/models/arguments.rb +112 -0
- data/lib/roby/models/plan_object.rb +83 -0
- data/lib/roby/models/task.rb +835 -0
- data/lib/roby/models/task_event.rb +62 -0
- data/lib/roby/models/task_service.rb +78 -0
- data/lib/roby/or_generator.rb +88 -0
- data/lib/roby/plan.rb +1751 -864
- data/lib/roby/plan_object.rb +611 -0
- data/lib/roby/plan_service.rb +200 -0
- data/lib/roby/promise.rb +332 -0
- data/lib/roby/queries.rb +23 -0
- data/lib/roby/queries/and_matcher.rb +32 -0
- data/lib/roby/queries/any.rb +27 -0
- data/lib/roby/queries/code_error_matcher.rb +58 -0
- data/lib/roby/queries/event_generator_matcher.rb +9 -0
- data/lib/roby/queries/execution_exception_matcher.rb +165 -0
- data/lib/roby/queries/index.rb +165 -0
- data/lib/roby/queries/localized_error_matcher.rb +149 -0
- data/lib/roby/queries/matcher_base.rb +107 -0
- data/lib/roby/queries/none.rb +27 -0
- data/lib/roby/queries/not_matcher.rb +30 -0
- data/lib/roby/queries/op_matcher.rb +8 -0
- data/lib/roby/queries/or_matcher.rb +30 -0
- data/lib/roby/queries/plan_object_matcher.rb +363 -0
- data/lib/roby/queries/query.rb +188 -0
- data/lib/roby/queries/task_event_generator_matcher.rb +86 -0
- data/lib/roby/queries/task_matcher.rb +344 -0
- data/lib/roby/relations.rb +42 -678
- data/lib/roby/relations/bidirectional_directed_adjacency_graph.rb +492 -0
- data/lib/roby/relations/directed_relation_support.rb +268 -0
- data/lib/roby/relations/event_relation_graph.rb +19 -0
- data/lib/roby/relations/fork_merge_visitor.rb +154 -0
- data/lib/roby/relations/graph.rb +533 -0
- data/lib/roby/relations/models/directed_relation_support.rb +11 -0
- data/lib/roby/relations/models/graph.rb +75 -0
- data/lib/roby/relations/models/task_relation_graph.rb +18 -0
- data/lib/roby/relations/space.rb +380 -0
- data/lib/roby/relations/task_relation_graph.rb +20 -0
- data/lib/roby/robot.rb +85 -38
- data/lib/roby/schedulers/basic.rb +155 -25
- data/lib/roby/schedulers/null.rb +20 -0
- data/lib/roby/schedulers/reporting.rb +31 -0
- data/lib/roby/schedulers/state.rb +129 -0
- data/lib/roby/schedulers/temporal.rb +91 -0
- data/lib/roby/singletons.rb +87 -0
- data/lib/roby/standalone.rb +4 -2
- data/lib/roby/standard_errors.rb +405 -82
- data/lib/roby/state.rb +6 -3
- data/lib/roby/state/conf_model.rb +5 -0
- data/lib/roby/state/events.rb +181 -95
- data/lib/roby/state/goal_model.rb +77 -0
- data/lib/roby/state/open_struct.rb +591 -0
- data/lib/roby/state/open_struct_model.rb +68 -0
- data/lib/roby/state/pos.rb +45 -45
- data/lib/roby/state/shapes.rb +11 -11
- data/lib/roby/state/state_model.rb +303 -0
- data/lib/roby/state/task.rb +43 -0
- data/lib/roby/support.rb +88 -148
- data/lib/roby/task.rb +1361 -1750
- data/lib/roby/task_arguments.rb +428 -0
- data/lib/roby/task_event.rb +127 -0
- data/lib/roby/task_event_generator.rb +337 -0
- data/lib/roby/task_service.rb +6 -0
- data/lib/roby/task_structure/conflicts.rb +104 -0
- data/lib/roby/task_structure/dependency.rb +932 -0
- data/lib/roby/task_structure/error_handling.rb +118 -0
- data/lib/roby/task_structure/executed_by.rb +234 -0
- data/lib/roby/task_structure/planned_by.rb +90 -0
- data/lib/roby/tasks/aggregator.rb +37 -0
- data/lib/roby/tasks/external_process.rb +275 -0
- data/lib/roby/tasks/group.rb +27 -0
- data/lib/roby/tasks/null.rb +19 -0
- data/lib/roby/tasks/parallel.rb +43 -0
- data/lib/roby/tasks/sequence.rb +88 -0
- data/lib/roby/tasks/simple.rb +21 -0
- data/lib/roby/{thread_task.rb → tasks/thread.rb} +50 -24
- data/lib/roby/tasks/timeout.rb +17 -0
- data/lib/roby/tasks/virtual.rb +55 -0
- data/lib/roby/template_plan.rb +7 -0
- data/lib/roby/test/aruba_minitest.rb +74 -0
- data/lib/roby/test/assertion.rb +16 -0
- data/lib/roby/test/assertions.rb +490 -0
- data/lib/roby/test/common.rb +368 -591
- data/lib/roby/test/dsl.rb +149 -0
- data/lib/roby/test/error.rb +18 -0
- data/lib/roby/test/event_reporter.rb +83 -0
- data/lib/roby/test/execution_expectations.rb +1134 -0
- data/lib/roby/test/expect_execution.rb +151 -0
- data/lib/roby/test/minitest_helpers.rb +166 -0
- data/lib/roby/test/roby_app_helpers.rb +200 -0
- data/lib/roby/test/run_planners.rb +155 -0
- data/lib/roby/test/self.rb +112 -0
- data/lib/roby/test/spec.rb +198 -0
- data/lib/roby/test/tasks/empty_task.rb +4 -4
- data/lib/roby/test/tasks/goto.rb +28 -27
- data/lib/roby/test/teardown_plans.rb +100 -0
- data/lib/roby/test/testcase.rb +239 -307
- data/lib/roby/test/tools.rb +159 -155
- data/lib/roby/test/validate_state_machine.rb +75 -0
- data/lib/roby/transaction.rb +1125 -0
- data/lib/roby/transaction/event_generator_proxy.rb +63 -0
- data/lib/roby/transaction/plan_object_proxy.rb +99 -0
- data/lib/roby/transaction/plan_service_proxy.rb +43 -0
- data/lib/roby/transaction/proxying.rb +120 -0
- data/lib/roby/transaction/task_event_generator_proxy.rb +19 -0
- data/lib/roby/transaction/task_proxy.rb +135 -0
- data/lib/roby/until_generator.rb +30 -0
- data/lib/roby/version.rb +5 -0
- data/lib/roby/yard.rb +169 -0
- data/lib/yard-roby.rb +1 -0
- data/manifest.xml +32 -6
- data/roby.gemspec +59 -0
- metadata +788 -587
- data/Manifest.txt +0 -321
- data/NOTES +0 -4
- data/README.txt +0 -166
- data/TODO.txt +0 -146
- data/app/README.txt +0 -24
- data/app/Rakefile +0 -8
- data/app/config/ROBOT.rb +0 -5
- data/app/config/init.rb +0 -33
- data/app/config/roby.yml +0 -3
- data/app/controllers/ROBOT.rb +0 -2
- data/app/planners/ROBOT/main.rb +0 -6
- data/app/planners/main.rb +0 -5
- data/app/scripts/distributed +0 -3
- data/app/scripts/generate/bookmarks +0 -3
- data/app/scripts/replay +0 -3
- data/app/scripts/results +0 -3
- data/app/scripts/run +0 -3
- data/app/scripts/server +0 -3
- data/app/scripts/shell +0 -3
- data/app/scripts/test +0 -3
- data/app/tasks/.gitattributes +0 -0
- data/app/tasks/ROBOT/.gitattributes +0 -0
- data/bin/roby-shell +0 -25
- data/doc/guide/src/basics/app.page +0 -139
- data/doc/guide/src/basics/index.page +0 -11
- data/doc/guide/src/basics/log_replay/goForward_1.png +0 -0
- data/doc/guide/src/basics/log_replay/goForward_2.png +0 -0
- data/doc/guide/src/basics/log_replay/goForward_3.png +0 -0
- data/doc/guide/src/basics/log_replay/goForward_5.png +0 -0
- data/doc/guide/src/basics/log_replay/plan_repair_4.png +0 -0
- data/doc/guide/src/basics/log_replay/roby_log_main_window.png +0 -0
- data/doc/guide/src/basics/relations_display.page +0 -203
- data/doc/guide/src/basics/shell.page +0 -102
- data/doc/guide/src/default.css +0 -319
- data/doc/guide/src/introduction/index.page +0 -29
- data/doc/guide/src/introduction/publications.page +0 -14
- data/doc/guide/src/relations/dependency.page +0 -89
- data/doc/guide/src/relations/index.page +0 -12
- data/ext/droby/dump.cc +0 -175
- data/ext/droby/extconf.rb +0 -3
- data/ext/graph/algorithm.cc +0 -746
- data/ext/graph/extconf.rb +0 -7
- data/ext/graph/graph.cc +0 -575
- data/ext/graph/graph.hh +0 -183
- data/ext/graph/iterator_sequence.hh +0 -102
- data/ext/graph/undirected_dfs.hh +0 -226
- data/ext/graph/undirected_graph.hh +0 -421
- data/lib/roby/app/scripts/generate/bookmarks.rb +0 -162
- data/lib/roby/app/scripts/replay.rb +0 -31
- data/lib/roby/app/scripts/server.rb +0 -18
- data/lib/roby/basic_object.rb +0 -151
- data/lib/roby/config.rb +0 -14
- data/lib/roby/distributed.rb +0 -36
- data/lib/roby/distributed/base.rb +0 -448
- data/lib/roby/distributed/communication.rb +0 -875
- data/lib/roby/distributed/connection_space.rb +0 -616
- data/lib/roby/distributed/distributed_object.rb +0 -206
- data/lib/roby/distributed/drb.rb +0 -62
- data/lib/roby/distributed/notifications.rb +0 -531
- data/lib/roby/distributed/peer.rb +0 -555
- data/lib/roby/distributed/protocol.rb +0 -529
- data/lib/roby/distributed/proxy.rb +0 -343
- data/lib/roby/distributed/subscription.rb +0 -311
- data/lib/roby/distributed/transaction.rb +0 -498
- data/lib/roby/external_process_task.rb +0 -225
- data/lib/roby/graph.rb +0 -160
- data/lib/roby/log.rb +0 -3
- data/lib/roby/log/chronicle.rb +0 -303
- data/lib/roby/log/console.rb +0 -74
- data/lib/roby/log/data_stream.rb +0 -275
- data/lib/roby/log/dot.rb +0 -279
- data/lib/roby/log/event_stream.rb +0 -161
- data/lib/roby/log/file.rb +0 -396
- data/lib/roby/log/gui/basic_display.ui +0 -83
- data/lib/roby/log/gui/basic_display_ui.rb +0 -89
- data/lib/roby/log/gui/chronicle.rb +0 -26
- data/lib/roby/log/gui/chronicle_view.rb +0 -40
- data/lib/roby/log/gui/chronicle_view.ui +0 -70
- data/lib/roby/log/gui/chronicle_view_ui.rb +0 -90
- data/lib/roby/log/gui/data_displays.rb +0 -171
- data/lib/roby/log/gui/data_displays.ui +0 -155
- data/lib/roby/log/gui/data_displays_ui.rb +0 -146
- data/lib/roby/log/gui/notifications.rb +0 -26
- data/lib/roby/log/gui/relations.rb +0 -269
- data/lib/roby/log/gui/relations.ui +0 -123
- data/lib/roby/log/gui/relations_ui.rb +0 -120
- data/lib/roby/log/gui/relations_view.rb +0 -185
- data/lib/roby/log/gui/relations_view.ui +0 -149
- data/lib/roby/log/gui/relations_view_ui.rb +0 -144
- data/lib/roby/log/gui/replay.rb +0 -366
- data/lib/roby/log/gui/replay_controls.rb +0 -206
- data/lib/roby/log/gui/replay_controls.ui +0 -282
- data/lib/roby/log/gui/replay_controls_ui.rb +0 -249
- data/lib/roby/log/gui/runtime.rb +0 -130
- data/lib/roby/log/hooks.rb +0 -186
- data/lib/roby/log/logger.rb +0 -203
- data/lib/roby/log/notifications.rb +0 -244
- data/lib/roby/log/plan_rebuilder.rb +0 -468
- data/lib/roby/log/relations.rb +0 -1084
- data/lib/roby/log/server.rb +0 -547
- data/lib/roby/log/sqlite.rb +0 -47
- data/lib/roby/log/timings.rb +0 -233
- data/lib/roby/plan-object.rb +0 -371
- data/lib/roby/planning.rb +0 -13
- data/lib/roby/planning/loops.rb +0 -309
- data/lib/roby/planning/model.rb +0 -1012
- data/lib/roby/planning/task.rb +0 -180
- data/lib/roby/query.rb +0 -655
- data/lib/roby/relations/conflicts.rb +0 -67
- data/lib/roby/relations/dependency.rb +0 -358
- data/lib/roby/relations/ensured.rb +0 -19
- data/lib/roby/relations/error_handling.rb +0 -22
- data/lib/roby/relations/events.rb +0 -7
- data/lib/roby/relations/executed_by.rb +0 -208
- data/lib/roby/relations/influence.rb +0 -10
- data/lib/roby/relations/planned_by.rb +0 -63
- data/lib/roby/state/information.rb +0 -55
- data/lib/roby/state/state.rb +0 -367
- data/lib/roby/task-operations.rb +0 -186
- data/lib/roby/task_index.rb +0 -80
- data/lib/roby/test/distributed.rb +0 -230
- data/lib/roby/test/tasks/simple_task.rb +0 -23
- data/lib/roby/transactions.rb +0 -507
- data/lib/roby/transactions/proxy.rb +0 -325
- data/plugins/fault_injection/History.txt +0 -4
- data/plugins/fault_injection/README.txt +0 -34
- data/plugins/fault_injection/Rakefile +0 -12
- data/plugins/fault_injection/TODO.txt +0 -0
- data/plugins/fault_injection/app.rb +0 -52
- data/plugins/fault_injection/fault_injection.rb +0 -89
- data/plugins/fault_injection/test/test_fault_injection.rb +0 -78
- data/plugins/subsystems/README.txt +0 -37
- data/plugins/subsystems/Rakefile +0 -13
- data/plugins/subsystems/app.rb +0 -182
- data/plugins/subsystems/test/app/README +0 -24
- data/plugins/subsystems/test/app/Rakefile +0 -8
- data/plugins/subsystems/test/app/config/app.yml +0 -71
- data/plugins/subsystems/test/app/config/init.rb +0 -12
- data/plugins/subsystems/test/app/config/roby.yml +0 -3
- data/plugins/subsystems/test/app/planners/main.rb +0 -20
- data/plugins/subsystems/test/app/scripts/distributed +0 -3
- data/plugins/subsystems/test/app/scripts/replay +0 -3
- data/plugins/subsystems/test/app/scripts/results +0 -3
- data/plugins/subsystems/test/app/scripts/run +0 -3
- data/plugins/subsystems/test/app/scripts/server +0 -3
- data/plugins/subsystems/test/app/scripts/shell +0 -3
- data/plugins/subsystems/test/app/scripts/test +0 -3
- data/plugins/subsystems/test/app/tasks/services.rb +0 -15
- data/plugins/subsystems/test/test_subsystems.rb +0 -78
- data/test/distributed/test_communication.rb +0 -195
- data/test/distributed/test_connection.rb +0 -284
- data/test/distributed/test_execution.rb +0 -378
- data/test/distributed/test_mixed_plan.rb +0 -341
- data/test/distributed/test_plan_notifications.rb +0 -238
- data/test/distributed/test_protocol.rb +0 -525
- data/test/distributed/test_query.rb +0 -106
- data/test/distributed/test_remote_plan.rb +0 -491
- data/test/distributed/test_transaction.rb +0 -466
- data/test/mockups/external_process +0 -28
- data/test/mockups/tasks.rb +0 -27
- data/test/planning/test_loops.rb +0 -432
- data/test/planning/test_model.rb +0 -427
- data/test/planning/test_task.rb +0 -126
- data/test/relations/test_conflicts.rb +0 -42
- data/test/relations/test_dependency.rb +0 -324
- data/test/relations/test_ensured.rb +0 -38
- data/test/relations/test_executed_by.rb +0 -224
- data/test/relations/test_planned_by.rb +0 -56
- data/test/suite_core.rb +0 -29
- data/test/suite_distributed.rb +0 -10
- data/test/suite_planning.rb +0 -4
- data/test/suite_relations.rb +0 -8
- data/test/tasks/test_external_process.rb +0 -126
- data/test/tasks/test_thread_task.rb +0 -70
- data/test/test_bgl.rb +0 -528
- data/test/test_event.rb +0 -969
- data/test/test_exceptions.rb +0 -591
- data/test/test_execution_engine.rb +0 -987
- data/test/test_gui.rb +0 -20
- data/test/test_interface.rb +0 -43
- data/test/test_log.rb +0 -125
- data/test/test_log_server.rb +0 -133
- data/test/test_plan.rb +0 -418
- data/test/test_query.rb +0 -424
- data/test/test_relations.rb +0 -260
- data/test/test_state.rb +0 -432
- data/test/test_support.rb +0 -16
- data/test/test_task.rb +0 -1181
- data/test/test_testcase.rb +0 -138
- data/test/test_transactions.rb +0 -610
- 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
|
+
|