roby 0.7
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.
- data/.gitignore +29 -0
- data/History.txt +4 -0
- data/License-fr.txt +519 -0
- data/License.txt +515 -0
- data/Manifest.txt +245 -0
- data/NOTES +4 -0
- data/README.txt +163 -0
- data/Rakefile +161 -0
- data/TODO.txt +146 -0
- data/app/README.txt +24 -0
- data/app/Rakefile +8 -0
- data/app/config/ROBOT.rb +5 -0
- data/app/config/app.yml +91 -0
- data/app/config/init.rb +7 -0
- data/app/config/roby.yml +3 -0
- data/app/controllers/.gitattributes +0 -0
- data/app/controllers/ROBOT.rb +2 -0
- data/app/data/.gitattributes +0 -0
- data/app/planners/ROBOT/main.rb +6 -0
- data/app/planners/main.rb +5 -0
- data/app/scripts/distributed +3 -0
- data/app/scripts/generate/bookmarks +3 -0
- data/app/scripts/replay +3 -0
- data/app/scripts/results +3 -0
- data/app/scripts/run +3 -0
- data/app/scripts/server +3 -0
- data/app/scripts/shell +3 -0
- data/app/scripts/test +3 -0
- data/app/tasks/.gitattributes +0 -0
- data/app/tasks/ROBOT/.gitattributes +0 -0
- data/bin/roby +210 -0
- data/bin/roby-log +168 -0
- data/bin/roby-shell +25 -0
- data/doc/images/event_generalization.png +0 -0
- data/doc/images/exception_propagation_1.png +0 -0
- data/doc/images/exception_propagation_2.png +0 -0
- data/doc/images/exception_propagation_3.png +0 -0
- data/doc/images/exception_propagation_4.png +0 -0
- data/doc/images/exception_propagation_5.png +0 -0
- data/doc/images/replay_handler_error.png +0 -0
- data/doc/images/replay_handler_error_0.png +0 -0
- data/doc/images/replay_handler_error_1.png +0 -0
- data/doc/images/roby_cycle_overview.png +0 -0
- data/doc/images/roby_replay_02.png +0 -0
- data/doc/images/roby_replay_03.png +0 -0
- data/doc/images/roby_replay_04.png +0 -0
- data/doc/images/roby_replay_event_representation.png +0 -0
- data/doc/images/roby_replay_first_state.png +0 -0
- data/doc/images/roby_replay_relations.png +0 -0
- data/doc/images/roby_replay_startup.png +0 -0
- data/doc/images/task_event_generalization.png +0 -0
- data/doc/papers.rdoc +11 -0
- data/doc/styles/allison.css +314 -0
- data/doc/styles/allison.js +316 -0
- data/doc/styles/allison.rb +276 -0
- data/doc/styles/jamis.rb +593 -0
- data/doc/tutorials/01-GettingStarted.rdoc +86 -0
- data/doc/tutorials/02-GoForward.rdoc +220 -0
- data/doc/tutorials/03-PlannedPath.rdoc +268 -0
- data/doc/tutorials/04-EventPropagation.rdoc +236 -0
- data/doc/tutorials/05-ErrorHandling.rdoc +319 -0
- data/doc/tutorials/06-Overview.rdoc +40 -0
- data/doc/videos.rdoc +69 -0
- data/ext/droby/dump.cc +175 -0
- data/ext/droby/extconf.rb +3 -0
- data/ext/graph/algorithm.cc +746 -0
- data/ext/graph/extconf.rb +7 -0
- data/ext/graph/graph.cc +529 -0
- data/ext/graph/graph.hh +183 -0
- data/ext/graph/iterator_sequence.hh +102 -0
- data/ext/graph/undirected_dfs.hh +226 -0
- data/ext/graph/undirected_graph.hh +421 -0
- data/lib/roby.rb +41 -0
- data/lib/roby/app.rb +870 -0
- data/lib/roby/app/rake.rb +56 -0
- data/lib/roby/app/run.rb +14 -0
- data/lib/roby/app/scripts/distributed.rb +13 -0
- data/lib/roby/app/scripts/generate/bookmarks.rb +162 -0
- data/lib/roby/app/scripts/replay.rb +31 -0
- data/lib/roby/app/scripts/results.rb +15 -0
- data/lib/roby/app/scripts/run.rb +26 -0
- data/lib/roby/app/scripts/server.rb +18 -0
- data/lib/roby/app/scripts/shell.rb +88 -0
- data/lib/roby/app/scripts/test.rb +40 -0
- data/lib/roby/basic_object.rb +151 -0
- data/lib/roby/config.rb +5 -0
- data/lib/roby/control.rb +747 -0
- data/lib/roby/decision_control.rb +17 -0
- data/lib/roby/distributed.rb +32 -0
- data/lib/roby/distributed/base.rb +440 -0
- data/lib/roby/distributed/communication.rb +871 -0
- data/lib/roby/distributed/connection_space.rb +592 -0
- data/lib/roby/distributed/distributed_object.rb +206 -0
- data/lib/roby/distributed/drb.rb +62 -0
- data/lib/roby/distributed/notifications.rb +539 -0
- data/lib/roby/distributed/peer.rb +550 -0
- data/lib/roby/distributed/protocol.rb +529 -0
- data/lib/roby/distributed/proxy.rb +343 -0
- data/lib/roby/distributed/subscription.rb +311 -0
- data/lib/roby/distributed/transaction.rb +498 -0
- data/lib/roby/event.rb +897 -0
- data/lib/roby/exceptions.rb +234 -0
- data/lib/roby/executives/simple.rb +30 -0
- data/lib/roby/graph.rb +166 -0
- data/lib/roby/interface.rb +390 -0
- data/lib/roby/log.rb +3 -0
- data/lib/roby/log/chronicle.rb +303 -0
- data/lib/roby/log/console.rb +72 -0
- data/lib/roby/log/data_stream.rb +197 -0
- data/lib/roby/log/dot.rb +279 -0
- data/lib/roby/log/event_stream.rb +151 -0
- data/lib/roby/log/file.rb +340 -0
- data/lib/roby/log/gui/basic_display.ui +83 -0
- data/lib/roby/log/gui/chronicle.rb +26 -0
- data/lib/roby/log/gui/chronicle_view.rb +40 -0
- data/lib/roby/log/gui/chronicle_view.ui +70 -0
- data/lib/roby/log/gui/data_displays.rb +172 -0
- data/lib/roby/log/gui/data_displays.ui +155 -0
- data/lib/roby/log/gui/notifications.rb +26 -0
- data/lib/roby/log/gui/relations.rb +248 -0
- data/lib/roby/log/gui/relations.ui +123 -0
- data/lib/roby/log/gui/relations_view.rb +185 -0
- data/lib/roby/log/gui/relations_view.ui +149 -0
- data/lib/roby/log/gui/replay.rb +327 -0
- data/lib/roby/log/gui/replay_controls.rb +200 -0
- data/lib/roby/log/gui/replay_controls.ui +259 -0
- data/lib/roby/log/gui/runtime.rb +130 -0
- data/lib/roby/log/hooks.rb +185 -0
- data/lib/roby/log/logger.rb +202 -0
- data/lib/roby/log/notifications.rb +244 -0
- data/lib/roby/log/plan_rebuilder.rb +470 -0
- data/lib/roby/log/relations.rb +1056 -0
- data/lib/roby/log/server.rb +550 -0
- data/lib/roby/log/sqlite.rb +47 -0
- data/lib/roby/log/timings.rb +164 -0
- data/lib/roby/plan-object.rb +247 -0
- data/lib/roby/plan.rb +762 -0
- data/lib/roby/planning.rb +13 -0
- data/lib/roby/planning/loops.rb +302 -0
- data/lib/roby/planning/model.rb +906 -0
- data/lib/roby/planning/task.rb +151 -0
- data/lib/roby/propagation.rb +562 -0
- data/lib/roby/query.rb +619 -0
- data/lib/roby/relations.rb +583 -0
- data/lib/roby/relations/conflicts.rb +70 -0
- data/lib/roby/relations/ensured.rb +20 -0
- data/lib/roby/relations/error_handling.rb +23 -0
- data/lib/roby/relations/events.rb +9 -0
- data/lib/roby/relations/executed_by.rb +193 -0
- data/lib/roby/relations/hierarchy.rb +239 -0
- data/lib/roby/relations/influence.rb +10 -0
- data/lib/roby/relations/planned_by.rb +63 -0
- data/lib/roby/robot.rb +7 -0
- data/lib/roby/standard_errors.rb +218 -0
- data/lib/roby/state.rb +5 -0
- data/lib/roby/state/events.rb +221 -0
- data/lib/roby/state/information.rb +55 -0
- data/lib/roby/state/pos.rb +110 -0
- data/lib/roby/state/shapes.rb +32 -0
- data/lib/roby/state/state.rb +353 -0
- data/lib/roby/support.rb +92 -0
- data/lib/roby/task-operations.rb +182 -0
- data/lib/roby/task.rb +1618 -0
- data/lib/roby/test/common.rb +399 -0
- data/lib/roby/test/distributed.rb +214 -0
- data/lib/roby/test/tasks/empty_task.rb +9 -0
- data/lib/roby/test/tasks/goto.rb +36 -0
- data/lib/roby/test/tasks/simple_task.rb +23 -0
- data/lib/roby/test/testcase.rb +519 -0
- data/lib/roby/test/tools.rb +160 -0
- data/lib/roby/thread_task.rb +87 -0
- data/lib/roby/transactions.rb +462 -0
- data/lib/roby/transactions/proxy.rb +292 -0
- data/lib/roby/transactions/updates.rb +139 -0
- data/plugins/fault_injection/History.txt +4 -0
- data/plugins/fault_injection/README.txt +37 -0
- data/plugins/fault_injection/Rakefile +18 -0
- data/plugins/fault_injection/TODO.txt +0 -0
- data/plugins/fault_injection/app.rb +52 -0
- data/plugins/fault_injection/fault_injection.rb +89 -0
- data/plugins/fault_injection/test/test_fault_injection.rb +84 -0
- data/plugins/subsystems/README.txt +40 -0
- data/plugins/subsystems/Rakefile +18 -0
- data/plugins/subsystems/app.rb +171 -0
- data/plugins/subsystems/test/app/README +24 -0
- data/plugins/subsystems/test/app/Rakefile +8 -0
- data/plugins/subsystems/test/app/config/app.yml +71 -0
- data/plugins/subsystems/test/app/config/init.rb +9 -0
- data/plugins/subsystems/test/app/config/roby.yml +3 -0
- data/plugins/subsystems/test/app/planners/main.rb +20 -0
- data/plugins/subsystems/test/app/scripts/distributed +3 -0
- data/plugins/subsystems/test/app/scripts/replay +3 -0
- data/plugins/subsystems/test/app/scripts/results +3 -0
- data/plugins/subsystems/test/app/scripts/run +3 -0
- data/plugins/subsystems/test/app/scripts/server +3 -0
- data/plugins/subsystems/test/app/scripts/shell +3 -0
- data/plugins/subsystems/test/app/scripts/test +3 -0
- data/plugins/subsystems/test/app/tasks/services.rb +15 -0
- data/plugins/subsystems/test/test_subsystems.rb +71 -0
- data/test/distributed/test_communication.rb +178 -0
- data/test/distributed/test_connection.rb +282 -0
- data/test/distributed/test_execution.rb +373 -0
- data/test/distributed/test_mixed_plan.rb +341 -0
- data/test/distributed/test_plan_notifications.rb +238 -0
- data/test/distributed/test_protocol.rb +516 -0
- data/test/distributed/test_query.rb +102 -0
- data/test/distributed/test_remote_plan.rb +491 -0
- data/test/distributed/test_transaction.rb +463 -0
- data/test/mockups/tasks.rb +27 -0
- data/test/planning/test_loops.rb +380 -0
- data/test/planning/test_model.rb +427 -0
- data/test/planning/test_task.rb +106 -0
- data/test/relations/test_conflicts.rb +42 -0
- data/test/relations/test_ensured.rb +38 -0
- data/test/relations/test_executed_by.rb +149 -0
- data/test/relations/test_hierarchy.rb +158 -0
- data/test/relations/test_planned_by.rb +54 -0
- data/test/suite_core.rb +24 -0
- data/test/suite_distributed.rb +9 -0
- data/test/suite_planning.rb +3 -0
- data/test/suite_relations.rb +8 -0
- data/test/test_bgl.rb +508 -0
- data/test/test_control.rb +399 -0
- data/test/test_event.rb +894 -0
- data/test/test_exceptions.rb +592 -0
- data/test/test_interface.rb +37 -0
- data/test/test_log.rb +114 -0
- data/test/test_log_server.rb +132 -0
- data/test/test_plan.rb +584 -0
- data/test/test_propagation.rb +210 -0
- data/test/test_query.rb +266 -0
- data/test/test_relations.rb +180 -0
- data/test/test_state.rb +414 -0
- data/test/test_support.rb +16 -0
- data/test/test_task.rb +938 -0
- data/test/test_testcase.rb +122 -0
- data/test/test_thread_task.rb +73 -0
- data/test/test_transactions.rb +569 -0
- data/test/test_transactions_proxy.rb +198 -0
- metadata +570 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'roby/task'
|
|
2
|
+
|
|
3
|
+
# We define here a set of tasks needed by unit testing
|
|
4
|
+
if !defined?(ChoiceTask)
|
|
5
|
+
class ChoiceTask < Roby::Task
|
|
6
|
+
event :start do |context|
|
|
7
|
+
emit :start, context
|
|
8
|
+
if rand > 0.5
|
|
9
|
+
emit :b
|
|
10
|
+
else
|
|
11
|
+
emit :a
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
event :a
|
|
16
|
+
forward :a => :success
|
|
17
|
+
event :b
|
|
18
|
+
forward :b => :success
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class MultiEventTask < Roby::Task
|
|
22
|
+
event :start, :command => true
|
|
23
|
+
event :inter
|
|
24
|
+
forward :start => :inter, :inter => :success
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('..', File.dirname(__FILE__))
|
|
2
|
+
require 'roby/test/common'
|
|
3
|
+
require 'roby/planning'
|
|
4
|
+
|
|
5
|
+
require 'flexmock'
|
|
6
|
+
require 'roby/test/tasks/simple_task'
|
|
7
|
+
|
|
8
|
+
class TC_PlanningLoop < Test::Unit::TestCase
|
|
9
|
+
include Roby::Planning
|
|
10
|
+
include Roby::Test
|
|
11
|
+
|
|
12
|
+
# The planner model
|
|
13
|
+
attr_reader :planner_model
|
|
14
|
+
# The task model
|
|
15
|
+
attr_reader :task_model
|
|
16
|
+
# The options to be used for the planning tasks generated by the loop
|
|
17
|
+
# planner
|
|
18
|
+
attr_reader :planning_task_options
|
|
19
|
+
|
|
20
|
+
def setup
|
|
21
|
+
super
|
|
22
|
+
|
|
23
|
+
task_model = @task_model = Class.new(SimpleTask)
|
|
24
|
+
pattern_id = 0
|
|
25
|
+
@planner_model = Class.new(Planning::Planner) do
|
|
26
|
+
method(:task) do
|
|
27
|
+
pattern_id += 1
|
|
28
|
+
task_model.new(:id => pattern_id)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
@planning_task_options = {
|
|
33
|
+
:planning_owners => nil,
|
|
34
|
+
:planner_model => planner_model,
|
|
35
|
+
:planned_model => SimpleTask,
|
|
36
|
+
:method_name => :task,
|
|
37
|
+
:method_options => {} }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def teardown
|
|
41
|
+
@planner_model, @task_model, @planning_task_options = nil
|
|
42
|
+
super
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Prepare the default plan for all planning loop tests
|
|
46
|
+
def prepare_plan(loop_options = {})
|
|
47
|
+
plan.insert(main_task = Roby::Task.new)
|
|
48
|
+
loop_task_options = planning_task_options.merge(loop_options)
|
|
49
|
+
loop_planner = PlanningLoop.new(loop_task_options)
|
|
50
|
+
main_task.planned_by loop_planner
|
|
51
|
+
|
|
52
|
+
return main_task, loop_planner
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Waits for +planning_task+ to finish and returns the planned result
|
|
56
|
+
def planning_task_result(planning_task)
|
|
57
|
+
assert(planning_task)
|
|
58
|
+
assert(planning_task.running? || planning_task.success?, planning_task)
|
|
59
|
+
if planning_task.running?
|
|
60
|
+
planning_task.thread.join
|
|
61
|
+
process_events
|
|
62
|
+
end
|
|
63
|
+
assert(planning_task.success?, planning_task.terminal_event.context)
|
|
64
|
+
planning_task.planned_task
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def test_append_pattern
|
|
68
|
+
main_task, loop_planner = prepare_plan
|
|
69
|
+
|
|
70
|
+
loop_planner.append_pattern
|
|
71
|
+
assert_equal(1, main_task.children.to_a.size)
|
|
72
|
+
first_task = main_task.children.find { true }
|
|
73
|
+
assert_equal(SimpleTask, first_task.class)
|
|
74
|
+
first_planner = first_task.planning_task
|
|
75
|
+
assert_equal(0, first_planner.arguments[:method_options][:pattern_id])
|
|
76
|
+
assert_equal(planning_task_options.merge(:method_options => { :pattern_id => 0 }),
|
|
77
|
+
first_planner.arguments)
|
|
78
|
+
assert_equal(1, loop_planner.patterns.size)
|
|
79
|
+
|
|
80
|
+
loop_planner.append_pattern
|
|
81
|
+
assert_equal(2, main_task.children.to_a.size)
|
|
82
|
+
second_task = main_task.children.find { |t| t != first_task }
|
|
83
|
+
assert_equal(SimpleTask, second_task.class)
|
|
84
|
+
second_planner = second_task.planning_task
|
|
85
|
+
assert_equal(planning_task_options.merge(:method_options => { :pattern_id => 1 }),
|
|
86
|
+
second_planner.arguments)
|
|
87
|
+
assert_equal(2, loop_planner.patterns.size)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# First use-case: non periodic loops with non-zero lookahead. It means that
|
|
91
|
+
# a generated subplan will only be started if #loop_start! is called on the
|
|
92
|
+
# loop planner, and that the system tries to always have some prepared
|
|
93
|
+
# subplans ready to be executed.
|
|
94
|
+
def test_nonperiodic
|
|
95
|
+
main_task, loop_planner = prepare_plan :period => nil, :lookahead => 2
|
|
96
|
+
loop_planner.start!
|
|
97
|
+
|
|
98
|
+
# We have a lookahead of 2, so we should have two patterns. The first
|
|
99
|
+
# planner should be already running but the second one should wait for
|
|
100
|
+
# the first to finish
|
|
101
|
+
assert_equal(2, loop_planner.patterns.size)
|
|
102
|
+
first_planner = loop_planner.patterns[-1].first
|
|
103
|
+
second_planner = loop_planner.patterns[-2].first
|
|
104
|
+
assert(first_planner.running?)
|
|
105
|
+
assert(!second_planner.running?)
|
|
106
|
+
|
|
107
|
+
# Wait for the first two patterns to be planned and check the result.
|
|
108
|
+
# The planned tasks should not be started until we call loop_start!
|
|
109
|
+
# explicitely
|
|
110
|
+
first_task = planning_task_result(first_planner)
|
|
111
|
+
assert(second_planner.running?)
|
|
112
|
+
assert_equal(1, first_task.arguments[:id])
|
|
113
|
+
assert(!first_task.running?)
|
|
114
|
+
assert_equal(2, loop_planner.patterns.size)
|
|
115
|
+
|
|
116
|
+
second_task = planning_task_result(second_planner)
|
|
117
|
+
assert_equal(2, second_task.arguments[:id])
|
|
118
|
+
assert(!first_task.running?)
|
|
119
|
+
assert(!second_task.running?)
|
|
120
|
+
assert_equal(2, loop_planner.patterns.size)
|
|
121
|
+
|
|
122
|
+
# Start the first pattern, check we have one more planner and that it
|
|
123
|
+
# is running to keep the lookahead
|
|
124
|
+
loop_planner.loop_start!
|
|
125
|
+
assert(first_task.running?)
|
|
126
|
+
assert(!second_task.running?)
|
|
127
|
+
assert_equal(3, main_task.children.to_a.size)
|
|
128
|
+
third_planner = loop_planner.last_planning_task
|
|
129
|
+
assert(! [first_planner, second_planner].include?(third_planner))
|
|
130
|
+
assert(third_planner.running?)
|
|
131
|
+
|
|
132
|
+
# Stop the first task. We have no period here, so the second task
|
|
133
|
+
# should not be running until we call #loop_start! again
|
|
134
|
+
first_task.success!
|
|
135
|
+
assert(!second_task.running?)
|
|
136
|
+
loop_planner.loop_start!
|
|
137
|
+
assert(second_task.running?)
|
|
138
|
+
|
|
139
|
+
# We started the second pattern, so a fourth should be in preparation
|
|
140
|
+
# since we did not call #process_events in the meantime, so the third
|
|
141
|
+
# planner is still running from Roby's point of view
|
|
142
|
+
fourth_planner = loop_planner.last_planning_task
|
|
143
|
+
assert(! [first_planner, second_planner, third_planner].include?(fourth_planner))
|
|
144
|
+
assert(third_planner.running?)
|
|
145
|
+
assert(!fourth_planner.running?)
|
|
146
|
+
|
|
147
|
+
# Now, we make the second task finish and call #loop_start! before
|
|
148
|
+
# actually acknowledging the end of the third planner. The loop should
|
|
149
|
+
# nicely handle that by starting the third task and the fourth planner
|
|
150
|
+
# right after the end of planning.
|
|
151
|
+
second_task.success!
|
|
152
|
+
loop_planner.loop_start!
|
|
153
|
+
assert(third_planner.running?)
|
|
154
|
+
third_task = planning_task_result(third_planner)
|
|
155
|
+
assert(third_task.running?)
|
|
156
|
+
assert(fourth_planner.running?)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Second use-case: periodic loops with non-zero lookahead. It means that a
|
|
160
|
+
# generated subplan will be started either because #loop_start! is called
|
|
161
|
+
# *or* because a specified timespan has been reached since the last pattern
|
|
162
|
+
# end. The system tries to always have some prepared subplans ready to be
|
|
163
|
+
# executed.
|
|
164
|
+
def test_periodic
|
|
165
|
+
main_task, loop_planner = prepare_plan :period => 0.5, :lookahead => 2
|
|
166
|
+
loop_planner.start!
|
|
167
|
+
|
|
168
|
+
assert_equal(2, loop_planner.patterns.size)
|
|
169
|
+
first_planner = loop_planner.patterns[-1].first
|
|
170
|
+
second_planner = loop_planner.patterns[-2].first
|
|
171
|
+
assert(first_planner.running?)
|
|
172
|
+
assert(!second_planner.running?)
|
|
173
|
+
|
|
174
|
+
# Call #loop_start! already, to make the loop start the first running
|
|
175
|
+
# task as soon as it is ready.
|
|
176
|
+
loop_planner.loop_start!
|
|
177
|
+
|
|
178
|
+
# Usual pattern: wait for the result of the first two planners, check
|
|
179
|
+
# that the first task actually runs
|
|
180
|
+
first_task = planning_task_result(first_planner)
|
|
181
|
+
second_task = planning_task_result(second_planner)
|
|
182
|
+
third_planner = loop_planner.patterns[-3].first
|
|
183
|
+
assert(third_planner.running?)
|
|
184
|
+
assert(first_task.running?)
|
|
185
|
+
assert(second_task.pending?)
|
|
186
|
+
|
|
187
|
+
# Make the first task finish and make sure the system does not start it right away
|
|
188
|
+
first_task.success!
|
|
189
|
+
assert(first_task.success?)
|
|
190
|
+
assert(second_task.pending?)
|
|
191
|
+
process_events
|
|
192
|
+
assert(second_task.pending?)
|
|
193
|
+
sleep(0.6)
|
|
194
|
+
process_events
|
|
195
|
+
assert(second_task.running?, loop_planner.arguments)
|
|
196
|
+
|
|
197
|
+
# Use the third task to check that the timeout can be overriden by
|
|
198
|
+
# calling loop_start! on the PlanningLoop task
|
|
199
|
+
third_task = planning_task_result(third_planner)
|
|
200
|
+
|
|
201
|
+
assert(second_task.running? && !third_task.running?)
|
|
202
|
+
second_task.success!
|
|
203
|
+
loop_planner.loop_start!
|
|
204
|
+
assert(!second_task.running? && third_task.running?)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Test periodic loop tasks with zero lookahead
|
|
208
|
+
def test_periodic_zero_lookahead
|
|
209
|
+
main_task, loop_planner = prepare_plan :period => 0.5, :lookahead => 0
|
|
210
|
+
loop_planner.start!
|
|
211
|
+
|
|
212
|
+
# Zero lookahead: no children until we call #loop_start!
|
|
213
|
+
assert(main_task.children.empty?)
|
|
214
|
+
|
|
215
|
+
# Start a first pattern
|
|
216
|
+
loop_planner.loop_start!(:id => 1)
|
|
217
|
+
assert_equal(1, loop_planner.patterns.size)
|
|
218
|
+
first_planner = loop_planner.last_planning_task
|
|
219
|
+
first_task = planning_task_result(first_planner)
|
|
220
|
+
assert_equal(1, first_task.arguments[:id])
|
|
221
|
+
|
|
222
|
+
# Check the normal behaviour: a new pattern is to be added only when
|
|
223
|
+
# the first pattern has finished AND the period has occured.
|
|
224
|
+
assert(first_task.running?)
|
|
225
|
+
assert_equal(1, main_task.children.to_a.size)
|
|
226
|
+
first_task.success!
|
|
227
|
+
assert_equal(1, main_task.children.to_a.size)
|
|
228
|
+
sleep(0.6)
|
|
229
|
+
process_events
|
|
230
|
+
assert_equal(2, main_task.children.to_a.size)
|
|
231
|
+
assert(second_planner = loop_planner.last_planning_task)
|
|
232
|
+
assert(second_planner.running?)
|
|
233
|
+
second_task = planning_task_result(second_planner)
|
|
234
|
+
assert(second_task.running?)
|
|
235
|
+
assert_equal(1, main_task.children.to_a.size)
|
|
236
|
+
|
|
237
|
+
# And queue one other. The second call to #loop_start! should be
|
|
238
|
+
# completely ignored because there is already one pending pattern.
|
|
239
|
+
loop_planner.loop_start!(:id => 3)
|
|
240
|
+
loop_planner.loop_start!(:id => 4)
|
|
241
|
+
assert_equal(2, main_task.children.to_a.size)
|
|
242
|
+
third_planner = loop_planner.last_planning_task
|
|
243
|
+
third_task = planning_task_result(third_planner)
|
|
244
|
+
assert_equal(3, third_task.arguments[:id])
|
|
245
|
+
|
|
246
|
+
# Check the dynamic behaviour
|
|
247
|
+
# - the 3rd task should start as soon as the 2nd has: the call to
|
|
248
|
+
# #loop_start! should have done that for us.
|
|
249
|
+
assert(second_task.running?)
|
|
250
|
+
assert(third_task.pending?)
|
|
251
|
+
second_task.success!
|
|
252
|
+
assert(second_task.success?)
|
|
253
|
+
assert(third_task.running?)
|
|
254
|
+
third_task.success!
|
|
255
|
+
assert(third_task.success?)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def test_reinit_periodic
|
|
259
|
+
main_task, loop_planner = prepare_plan :period => 0.5, :lookahead => 3
|
|
260
|
+
|
|
261
|
+
FlexMock.use do |mock|
|
|
262
|
+
mock.should_receive(:started).twice
|
|
263
|
+
task_model.on(:start) { mock.started }
|
|
264
|
+
|
|
265
|
+
loop_planner.start!
|
|
266
|
+
planners = loop_planner.patterns.reverse.map { |t, _| t }
|
|
267
|
+
tasks = planners.map { |p| planning_task_result(p) }
|
|
268
|
+
|
|
269
|
+
loop_planner.loop_start!
|
|
270
|
+
assert(tasks[0].running?)
|
|
271
|
+
|
|
272
|
+
loop_planner.reinit!
|
|
273
|
+
process_events
|
|
274
|
+
sleep(0.1)
|
|
275
|
+
process_events
|
|
276
|
+
|
|
277
|
+
# reinit should keep the first pattern because it is running, but
|
|
278
|
+
# the other ones should be new (and the second pattern should be
|
|
279
|
+
# being planned)
|
|
280
|
+
assert(loop_planner.event(:reinit).happened?)
|
|
281
|
+
assert_equal(3, loop_planner.patterns.size)
|
|
282
|
+
|
|
283
|
+
new_planners = loop_planner.patterns.reverse.map { |t, _| t }
|
|
284
|
+
new_tasks = new_planners.map { |p| planning_task_result(p) }
|
|
285
|
+
|
|
286
|
+
new_tasks.each do |t|
|
|
287
|
+
assert(!tasks.include?(t))
|
|
288
|
+
end
|
|
289
|
+
# assert_equal([1, 5, 6, 7], new_tasks.map { |t| t.arguments[:id] })
|
|
290
|
+
# ... but the first pattern should be GCed right now, and the next
|
|
291
|
+
# pattern started
|
|
292
|
+
process_events
|
|
293
|
+
assert(new_tasks[0].running?)
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
#def test_planning_loop_reinit_zero_lookahead
|
|
298
|
+
# task_model = Class.new(SimpleTask)
|
|
299
|
+
# planner_model = Class.new(Planning::Planner) do
|
|
300
|
+
# @@id = 0
|
|
301
|
+
# method(:task) do
|
|
302
|
+
# task_model.new(:id => (@@id += 1))
|
|
303
|
+
# end
|
|
304
|
+
# end
|
|
305
|
+
|
|
306
|
+
# plan.insert(main_task = Roby::Task.new)
|
|
307
|
+
# loop_planner = PlanningLoop.new :period => nil, :lookahead => 0,
|
|
308
|
+
# :planner_model => planner_model, :planned_model => Roby::Task,
|
|
309
|
+
# :method_name => :task, :method_options => {}
|
|
310
|
+
# main_task.planned_by loop_planner
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
# FlexMock.use do |mock|
|
|
314
|
+
# mock.should_receive(:started).twice
|
|
315
|
+
# task_model.on(:start) { |ev| STDERR.puts "started pattern #{ev.task}"; mock.started }
|
|
316
|
+
|
|
317
|
+
# loop_planner.start!
|
|
318
|
+
# loop_planner.loop_start!
|
|
319
|
+
# first_task, first_planner = planning_loop_next(main_task)
|
|
320
|
+
# assert(first_task.running?)
|
|
321
|
+
|
|
322
|
+
# loop_planner.reinit
|
|
323
|
+
# loop_planner.loop_start!
|
|
324
|
+
# old_first = first_task
|
|
325
|
+
# first_task, first_planner = planning_loop_next(main_task)
|
|
326
|
+
# assert_equal(2, first_task.arguments[:id])
|
|
327
|
+
|
|
328
|
+
# assert(old_first.running?)
|
|
329
|
+
# assert(first_task.pending?)
|
|
330
|
+
|
|
331
|
+
# process_events
|
|
332
|
+
# assert(old_first.finished?)
|
|
333
|
+
# assert(first_task.running?)
|
|
334
|
+
# end
|
|
335
|
+
#end
|
|
336
|
+
|
|
337
|
+
#def test_make_loop
|
|
338
|
+
# planner_model = Class.new(Planning::Planner) do
|
|
339
|
+
# include Test::Unit::Assertions
|
|
340
|
+
|
|
341
|
+
# @result_task = nil
|
|
342
|
+
# attr_reader :result_task
|
|
343
|
+
# method(:task) { @result_task = SimpleTask.new(:id => arguments[:task_id])}
|
|
344
|
+
# method(:looping_tasks) do
|
|
345
|
+
# t1 = make_loop(:period => 0, :child_argument => 2) do
|
|
346
|
+
# # arguments of 'my_looping_task' shall be forwarded
|
|
347
|
+
# raise unless arguments[:parent_argument] == 1
|
|
348
|
+
# raise unless arguments[:child_argument] == 2
|
|
349
|
+
# task(:task_id => 'first_loop')
|
|
350
|
+
# end
|
|
351
|
+
# t2 = make_loop do
|
|
352
|
+
# task(:task_id => 'second_loop')
|
|
353
|
+
# end
|
|
354
|
+
# # Make sure the two loops are different
|
|
355
|
+
# assert(t1.method_options[:id] != t2.method_options[:id])
|
|
356
|
+
# [t1, t2]
|
|
357
|
+
# end
|
|
358
|
+
# end
|
|
359
|
+
|
|
360
|
+
# planner = planner_model.new(plan)
|
|
361
|
+
# t1, t2 = planner.looping_tasks(:parent_argument => 1)
|
|
362
|
+
# plan.insert(t1)
|
|
363
|
+
# plan.insert(t2)
|
|
364
|
+
|
|
365
|
+
# t1.start!
|
|
366
|
+
# planned_task = planning_task_result(t1.last_planning_task)
|
|
367
|
+
# assert_equal('first_loop', planned_task.arguments[:id])
|
|
368
|
+
|
|
369
|
+
# t2.start!
|
|
370
|
+
# planned_task = planning_task_result(t2.last_planning_task)
|
|
371
|
+
# assert_equal('second_loop', planned_task.arguments[:id])
|
|
372
|
+
|
|
373
|
+
# t3 = planner.make_loop(:period => 0, :parent_argument => 1, :child_argument => 2) do
|
|
374
|
+
# task(:task_id => 'third_loop')
|
|
375
|
+
# end
|
|
376
|
+
# plan.insert(t3)
|
|
377
|
+
# t3.start!
|
|
378
|
+
# assert_equal('third_loop', planning_task_result(t3.last_planning_task).arguments[:id])
|
|
379
|
+
#end
|
|
380
|
+
end
|
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('..', File.dirname(__FILE__))
|
|
2
|
+
require 'roby/test/common'
|
|
3
|
+
require 'roby/planning'
|
|
4
|
+
|
|
5
|
+
require 'flexmock'
|
|
6
|
+
require 'roby/test/tasks/simple_task'
|
|
7
|
+
|
|
8
|
+
class TC_Planner < Test::Unit::TestCase
|
|
9
|
+
include Roby::Planning
|
|
10
|
+
include Roby::Test
|
|
11
|
+
|
|
12
|
+
def test_id_validation
|
|
13
|
+
assert_equal(15, Planner.validate_method_id("15"))
|
|
14
|
+
assert_equal('foo', Planner.validate_method_id(:foo))
|
|
15
|
+
assert_equal('foo', Planner.validate_method_id('foo'))
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def test_method_definition
|
|
19
|
+
base_model, base_1, base_15, base_foobar, base_barfoo, recursive = nil
|
|
20
|
+
model = Class.new(Planner) do
|
|
21
|
+
base_model = method(:base)
|
|
22
|
+
base_1 = method(:base) { NullTask.new }
|
|
23
|
+
base_15 = method(:base, :id => "15") { NullTask.new }
|
|
24
|
+
base_foobar = method(:base, :id => :foobar) { NullTask.new }
|
|
25
|
+
base_barfoo = method(:base, :id => 'barfoo') { NullTask.new }
|
|
26
|
+
recursive = method(:recursive, :recursive => true) { NullTask.new }
|
|
27
|
+
end
|
|
28
|
+
assert_equal(17, model.next_id)
|
|
29
|
+
|
|
30
|
+
assert(model.respond_to?(:base_methods))
|
|
31
|
+
assert(model.respond_to?(:each_base_method), model.methods.find_all { |name| name =~ /base/ }.inspect)
|
|
32
|
+
assert_equal({ 1 => base_1, 15 => base_15, "foobar" => base_foobar, "barfoo" => base_barfoo }.to_set, model.enum_for(:each_base_method).to_set)
|
|
33
|
+
|
|
34
|
+
assert(model.respond_to?(:base_model))
|
|
35
|
+
|
|
36
|
+
assert(model.find_methods(:base))
|
|
37
|
+
assert_equal(4, model.find_methods(:base).size)
|
|
38
|
+
assert_equal(1, model.find_methods(:base, :id => 1).size)
|
|
39
|
+
assert_equal(1, model.find_methods(:base, :id => 15).size) # Check handling of the string -> integer convertion
|
|
40
|
+
assert_equal(1, model.find_methods(:base, :id => 'foobar').size) # Check handling of the symbol -> string convertion
|
|
41
|
+
assert_equal(1, model.find_methods(:base, :id => :barfoo).size)
|
|
42
|
+
|
|
43
|
+
assert_equal(nil, model.find_methods('recursive', :recursive => false))
|
|
44
|
+
assert_equal([recursive], model.find_methods('recursive', :recursive => true))
|
|
45
|
+
|
|
46
|
+
planner = model.new(plan)
|
|
47
|
+
assert(planner.respond_to?(:base))
|
|
48
|
+
assert(planner.base.null?)
|
|
49
|
+
assert(planner.respond_to?(:recursive))
|
|
50
|
+
assert_raises(Planning::NotFound) { planner.recursive(:recursive => false) }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def test_reuse
|
|
54
|
+
task_model = Class.new(Task)
|
|
55
|
+
derived_model = Class.new(task_model)
|
|
56
|
+
planner_model = Class.new(Planner) do
|
|
57
|
+
method(:reusable, :returns => task_model)
|
|
58
|
+
method(:not_reusable, :returns => task_model, :reuse => false)
|
|
59
|
+
end
|
|
60
|
+
assert_raise(ArgumentError) { planner_model.method(:not_reusable, :reuse => true) }
|
|
61
|
+
assert_nothing_raised { planner_model.method(:not_reusable, :reuse => false) }
|
|
62
|
+
assert_nothing_raised { planner_model.method(:reusable, :reuse => true) }
|
|
63
|
+
|
|
64
|
+
planner_model.class_eval do
|
|
65
|
+
method(:reusable, :id => 'base') { task_model.new }
|
|
66
|
+
method(:reusable, :id => 'derived', :returns => derived_model) { derived_model.new }
|
|
67
|
+
method(:not_reusable) { task_model.new }
|
|
68
|
+
|
|
69
|
+
# This one should build two tasks
|
|
70
|
+
method(:check_not_reusable, :id => 1) do
|
|
71
|
+
[reusable(:id => 'base'), not_reusable]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# This one should build two tasks
|
|
75
|
+
method(:check_not_reusable, :id => 2) do
|
|
76
|
+
[not_reusable, not_reusable]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# This one should build one task
|
|
80
|
+
method(:check_reusable, :id => 1) do
|
|
81
|
+
[not_reusable, reusable(:id => 'base')]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# This one should build only one task
|
|
85
|
+
method(:check_reusable, :id => 2) do
|
|
86
|
+
[reusable(:id => 'base'), reusable(:id => 'base')]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# This one whouls build two tasks
|
|
90
|
+
method(:check_reusable, :id => 3) do
|
|
91
|
+
[reusable(:id => 'base'), reusable(:id => 'derived')]
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# This one whouls build one task
|
|
95
|
+
method(:check_reusable, :id => 4) do
|
|
96
|
+
[reusable(:id => 'derived'), reusable(:id => 'base')]
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
assert_result_plan_size(1, planner_model, :check_reusable, :id => 1)
|
|
101
|
+
assert_result_plan_size(1, planner_model, :check_reusable, :id => 2)
|
|
102
|
+
assert_result_plan_size(2, planner_model, :check_reusable, :id => 3)
|
|
103
|
+
assert_result_plan_size(1, planner_model, :check_reusable, :id => 4)
|
|
104
|
+
|
|
105
|
+
assert_result_plan_size(2, planner_model, :check_not_reusable, :id => 1)
|
|
106
|
+
assert_result_plan_size(2, planner_model, :check_not_reusable, :id => 2)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def test_empty_method_set
|
|
110
|
+
task_model = Class.new(Roby::Task)
|
|
111
|
+
model = Class.new(Roby::Planning::Planner) do
|
|
112
|
+
method(:empty_set, :returns => task_model)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
planner = model.new(plan)
|
|
116
|
+
assert_raises(NotFound) { planner.empty_set }
|
|
117
|
+
|
|
118
|
+
plan.insert(task = task_model.new)
|
|
119
|
+
found_task = nil
|
|
120
|
+
assert_nothing_raised { found_task = planner.empty_set }
|
|
121
|
+
assert_equal(found_task, task)
|
|
122
|
+
assert_raises(NotFound) { planner.empty_set :reuse => false }
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def assert_result_plan_size(size, planner_model, method, options)
|
|
126
|
+
planner = planner_model.new(plan)
|
|
127
|
+
result = planner.send(method, options)
|
|
128
|
+
result.each do |task|
|
|
129
|
+
planner.plan.insert(task)
|
|
130
|
+
end
|
|
131
|
+
assert_equal(size, planner.plan.size, planner.plan.known_tasks.to_a.inspect)
|
|
132
|
+
|
|
133
|
+
new_plan
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def test_recursive
|
|
137
|
+
task_model = Class.new(Roby::Task) do
|
|
138
|
+
argument :id
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
model = Class.new(Planner) do
|
|
142
|
+
method(:not_recursive) { root }
|
|
143
|
+
method(:recursive, :recursive => true) do
|
|
144
|
+
if @rec_already_called
|
|
145
|
+
task_model.new(:id => 'recursive')
|
|
146
|
+
else
|
|
147
|
+
@rec_already_called = true
|
|
148
|
+
root
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
method(:root, :recursive => true) do
|
|
152
|
+
if @root_already_called
|
|
153
|
+
task_model.new(:id => 'root')
|
|
154
|
+
else
|
|
155
|
+
@root_already_called = true
|
|
156
|
+
[recursive, not_recursive]
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
planner = model.new(plan)
|
|
162
|
+
assert(planner.has_method?(:recursive))
|
|
163
|
+
assert(planner.respond_to?(:recursive))
|
|
164
|
+
recursive = planner.class.find_methods(:recursive)
|
|
165
|
+
assert_equal(1, recursive.size)
|
|
166
|
+
assert(recursive.first.recursive?)
|
|
167
|
+
|
|
168
|
+
# Calls:
|
|
169
|
+
# not_recursive
|
|
170
|
+
# - root
|
|
171
|
+
# - recursive
|
|
172
|
+
# - not_recursive <= FAILS HERE
|
|
173
|
+
assert_raises(NotFound) { model.new(new_plan).not_recursive }
|
|
174
|
+
|
|
175
|
+
# Calls:
|
|
176
|
+
# recursive
|
|
177
|
+
# - root
|
|
178
|
+
# - recursive => Task(id: recursive)
|
|
179
|
+
# - not_recursive
|
|
180
|
+
# - root => Task(id: root)
|
|
181
|
+
planner = model.new(new_plan)
|
|
182
|
+
assert_nothing_raised { planner.recursive }
|
|
183
|
+
assert_equal(2, plan.size, plan.known_tasks)
|
|
184
|
+
assert_equal(1, plan.find_tasks.which_fullfills(task_model, :id => 'recursive').to_a.size)
|
|
185
|
+
assert_equal(1, plan.find_tasks.which_fullfills(task_model, :id => 'root').to_a.size)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def test_method_model
|
|
189
|
+
# Some task models
|
|
190
|
+
tm_a = Class.new(Roby::Task)
|
|
191
|
+
tm_a_a = Class.new(tm_a)
|
|
192
|
+
tm_b = Class.new(Roby::Task)
|
|
193
|
+
foo_klass = Class.new
|
|
194
|
+
|
|
195
|
+
# The planning model
|
|
196
|
+
model = Class.new(Planner)
|
|
197
|
+
|
|
198
|
+
# Fails because foo_klass is not a task
|
|
199
|
+
assert_raises(ArgumentError) { model.method(:root, :returns => foo_klass) }
|
|
200
|
+
# Check the definition of instance methods on Planner instances
|
|
201
|
+
model.method(:root, :returns => tm_a)
|
|
202
|
+
assert_equal( model.root_model, model.method_model(:root) )
|
|
203
|
+
assert_equal(tm_a, model.method_model(:root).returns)
|
|
204
|
+
# Fails because we can't override a :returns option
|
|
205
|
+
assert_raises(ArgumentError) { model.method(:root, :returns => tm_b) }
|
|
206
|
+
# Does not fail since tm_a is the curren :returns task model
|
|
207
|
+
assert_nothing_raised { model.method(:root, :returns => tm_a) }
|
|
208
|
+
|
|
209
|
+
# Check that :returns is properly validated on methods
|
|
210
|
+
model.method(:root, :id => 1) {}
|
|
211
|
+
assert_raises(ArgumentError) { model.method(:root, :returns => tm_b) {} }
|
|
212
|
+
assert_nothing_raised { model.method(:root, :returns => tm_a) {} }
|
|
213
|
+
assert_nothing_raised { model.method(:root, :returns => tm_a_a) {} }
|
|
214
|
+
|
|
215
|
+
# Cannot redefine the model since there are methods
|
|
216
|
+
assert_raises(ArgumentError) { model.method(:root, :returns => tm_a) }
|
|
217
|
+
|
|
218
|
+
# Check that we can't override an already-defined method
|
|
219
|
+
assert_raises(ArgumentError) { model.method(:root, :id => 1) {} }
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def test_model_of
|
|
223
|
+
tm1 = Class.new(Roby::Task)
|
|
224
|
+
tm2 = Class.new(tm1)
|
|
225
|
+
tm3 = Class.new(tm2)
|
|
226
|
+
base = Class.new(Planner) do
|
|
227
|
+
method(:root, :returns => tm1)
|
|
228
|
+
method(:root, :id => 'nil') { }
|
|
229
|
+
method(:root, :id => 'tm2', :returns => tm2) { }
|
|
230
|
+
end
|
|
231
|
+
derived = Class.new(base) do
|
|
232
|
+
method(:root, :id => 'derived', :returns => tm2) { }
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
assert_equal(tm1, base.model_of(:root).returns)
|
|
236
|
+
assert_equal(tm1, base.model_of(:root, :id => 'nil').returns)
|
|
237
|
+
assert_equal(tm2, base.model_of(:root, :id => 'tm2').returns)
|
|
238
|
+
assert_equal(tm1, derived.model_of(:root).returns)
|
|
239
|
+
assert_equal(tm1, derived.model_of(:root, :id => 'nil').returns)
|
|
240
|
+
assert_equal(tm2, derived.model_of(:root, :id => 'tm2').returns)
|
|
241
|
+
assert_equal(tm2, derived.model_of(:root, :id => 'derived').returns)
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def test_returns_validation
|
|
245
|
+
task_model = Class.new(Roby::Task)
|
|
246
|
+
task_tag = TaskModelTag.new
|
|
247
|
+
|
|
248
|
+
planner_model = Class.new(Planning::Planner)
|
|
249
|
+
assert_nothing_raised { planner_model.method(:returns_task, :returns => task_model) }
|
|
250
|
+
assert_nothing_raised { planner_model.method(:returns_tag, :returns => task_tag) }
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def test_returns_inheritance
|
|
255
|
+
# Some task models
|
|
256
|
+
tm_a = Class.new(Roby::Task)
|
|
257
|
+
tm_a_a = Class.new(tm_a)
|
|
258
|
+
tm_b = Class.new(Roby::Task)
|
|
259
|
+
foo_klass = Class.new
|
|
260
|
+
|
|
261
|
+
# The planning models
|
|
262
|
+
base = Class.new(Planner)
|
|
263
|
+
base.method(:root, :returns => tm_a)
|
|
264
|
+
derived = Class.new(base)
|
|
265
|
+
|
|
266
|
+
# Check that we can override the model on derived
|
|
267
|
+
assert_raises(ArgumentError) { derived.method(:root, :returns => tm_b) }
|
|
268
|
+
assert_nothing_raised { derived.method(:root, :returns => tm_a_a) }
|
|
269
|
+
assert_equal(base.root_model.returns, tm_a)
|
|
270
|
+
assert_equal(derived.root_model.returns, tm_a_a)
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def test_method_inheritance
|
|
274
|
+
# Define a few task models
|
|
275
|
+
tm_a = Class.new(Roby::Task)
|
|
276
|
+
tm_b = Class.new(Roby::Task)
|
|
277
|
+
tm_a_a = Class.new(tm_a)
|
|
278
|
+
tm_a_a_a = Class.new(tm_a_a)
|
|
279
|
+
tm_b_a = Class.new(tm_a)
|
|
280
|
+
|
|
281
|
+
base = Class.new(Planner) do
|
|
282
|
+
method(:root, :returns => tm_a)
|
|
283
|
+
method(:root, :id => 1, :returns => tm_a_a) {}
|
|
284
|
+
end
|
|
285
|
+
base_root = base.enum_for(:each_root_method).to_a
|
|
286
|
+
|
|
287
|
+
d1 = Class.new(base)
|
|
288
|
+
# There are methods defined on :root, cannot override the :returns option
|
|
289
|
+
assert_raises(ArgumentError) { d1.method(:root, :returns => tm_a_a) }
|
|
290
|
+
assert_raises(ArgumentError) { d1.method(:root, :returns => tm_b) }
|
|
291
|
+
assert_raises(ArgumentError) { d1.method(:root, :returns => tm_b) {} }
|
|
292
|
+
|
|
293
|
+
d1_root = []
|
|
294
|
+
# Define a few methods and check :returns is validated properly
|
|
295
|
+
assert_nothing_raised { d1_root << d1.method(:root, :returns => tm_a) {} }
|
|
296
|
+
assert_nothing_raised { d1_root << d1.method(:root, :returns => tm_a_a) {} }
|
|
297
|
+
assert_nothing_raised { d1_root << d1.method(:root, :returns => tm_b_a) {} }
|
|
298
|
+
d2_root = d1_root.dup
|
|
299
|
+
assert_nothing_raised { d1_root << d1.method(:root, :id => 1, :returns => tm_a_a) {} }
|
|
300
|
+
|
|
301
|
+
d2 = Class.new(d1)
|
|
302
|
+
assert_nothing_raised { d2_root << d2.method(:root, :id => 1, :returns => tm_a_a_a) {} }
|
|
303
|
+
|
|
304
|
+
# Check that methods are defined at the proper level in the class hierarchy
|
|
305
|
+
assert_equal(base_root.to_set, base.enum_for(:each_root_method).to_set)
|
|
306
|
+
assert_equal(d1_root.to_set, d1.enum_for(:each_root_method).map { |_, x| x }.to_set)
|
|
307
|
+
assert_equal(d2_root.to_set, d2.enum_for(:each_root_method).map { |_, x| x }.to_set)
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def test_library
|
|
311
|
+
a = Planning::Library.new do
|
|
312
|
+
method(:root, :id => 'a') { }
|
|
313
|
+
end
|
|
314
|
+
b = Planning::Library.new do
|
|
315
|
+
include a
|
|
316
|
+
method(:root, :id => 'b') { }
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
planner = Class.new(Planner) do
|
|
320
|
+
include b
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
assert( planner.find_methods(:root) )
|
|
324
|
+
assert_equal(['a', 'b'], planner.find_methods(:root).map { |m| m.id } )
|
|
325
|
+
|
|
326
|
+
c = Module.new do
|
|
327
|
+
planning_library
|
|
328
|
+
using a
|
|
329
|
+
end
|
|
330
|
+
planner = Class.new(Planner) { include c }
|
|
331
|
+
assert_equal(['a'], planner.find_methods(:root).map { |m| m.id } )
|
|
332
|
+
|
|
333
|
+
d = Module.new do
|
|
334
|
+
include b
|
|
335
|
+
end
|
|
336
|
+
assert_nothing_raised { d.method(:root, :id => 'c') { } }
|
|
337
|
+
|
|
338
|
+
e = Module.new do
|
|
339
|
+
planning_library(:id => "e")
|
|
340
|
+
method(:test) { Roby::Test::SimpleTask.new }
|
|
341
|
+
end
|
|
342
|
+
planner = Class.new(Planner) do
|
|
343
|
+
using e
|
|
344
|
+
end.new(plan)
|
|
345
|
+
assert_nothing_raised { planner.test(:id => 'e') }
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def test_return_type
|
|
349
|
+
task_model = Class.new(Task) do
|
|
350
|
+
argument :arg
|
|
351
|
+
end
|
|
352
|
+
planner = Class.new(Planner) do
|
|
353
|
+
method(:test, :returns => task_model, :reuse => false)
|
|
354
|
+
method(:test, :id => "good") { task_model.new(:arg => 42, :unmatched => 21) }
|
|
355
|
+
method(:test, :id => "bad_argument") { task_model.new(:arg => 21) }
|
|
356
|
+
method(:test, :id => "bad_model") { NullTask.new(:arg => 42) }
|
|
357
|
+
method(:test, :id => "array") { [task_model.new] }
|
|
358
|
+
method(:not_a_task) { nil }
|
|
359
|
+
end.new(plan)
|
|
360
|
+
assert_nothing_raised { planner.test(:id => "good", :arg => 42, :unmatched => 10) }
|
|
361
|
+
assert_raises(Planning::NotFound) { planner.test(:id => "bad_argument", :arg => 42) }
|
|
362
|
+
assert_raises(Planning::NotFound) { planner.test(:id => "bad_model", :arg => 42) }
|
|
363
|
+
assert_raises(Planning::NotFound) { planner.test(:id => "array", :arg => 42) }
|
|
364
|
+
assert_raises(Planning::NotFound) { planner.not_a_task }
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
def test_planning_methods_names
|
|
368
|
+
model = Class.new(Planner) do
|
|
369
|
+
def not_a_planning_method
|
|
370
|
+
end
|
|
371
|
+
method(:test) { }
|
|
372
|
+
method(:localization) { }
|
|
373
|
+
method(:model_only)
|
|
374
|
+
end
|
|
375
|
+
assert_equal(['test', 'localization', 'model_only'].to_set,
|
|
376
|
+
model.planning_methods_names.to_set)
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def test_method_filter
|
|
380
|
+
base = Class.new(Planner) do
|
|
381
|
+
method(:test, :id => 1) { arguments[:mock].m(1) }
|
|
382
|
+
method(:test, :id => 2) { arguments[:mock].m(2) }
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
assert_raises(ArgumentError) { Class.new(base).filter(:test) { || true } }
|
|
386
|
+
assert_raises(ArgumentError) { Class.new(base).filter(:test) { |a| true } }
|
|
387
|
+
assert_raises(ArgumentError) { Class.new(base).filter(:test) { |a, b, c| true } }
|
|
388
|
+
|
|
389
|
+
planner, filter_block = nil, lambda { |a, b| true }
|
|
390
|
+
assert_nothing_raised do
|
|
391
|
+
planner = Class.new(base) do
|
|
392
|
+
filter(:test, &filter_block)
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
assert(planner.respond_to?(:each_test_filter))
|
|
397
|
+
assert_equal([filter_block], planner.enum_for(:each_test_filter).to_a)
|
|
398
|
+
assert_equal(2, planner.find_methods('test', :index => 10).size)
|
|
399
|
+
|
|
400
|
+
planner = Class.new(base) do
|
|
401
|
+
filter(:test) { false }
|
|
402
|
+
end
|
|
403
|
+
assert(!planner.find_methods('test', :index => 10))
|
|
404
|
+
|
|
405
|
+
(1..2).each do |i|
|
|
406
|
+
FlexMock.use do |mock|
|
|
407
|
+
planner = Class.new(base) do
|
|
408
|
+
filter(:test) do |opt, m|
|
|
409
|
+
mock.filtered(m.id)
|
|
410
|
+
m.id == i
|
|
411
|
+
end
|
|
412
|
+
end.new(plan)
|
|
413
|
+
|
|
414
|
+
mock.should_receive(:m).with(i).once.returns(NullTask.new)
|
|
415
|
+
mock.should_receive(:filtered).with(2).once
|
|
416
|
+
mock.should_receive(:filtered).with(1).once
|
|
417
|
+
planner.test(:mock => mock)
|
|
418
|
+
end
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
planner = Class.new(base) do
|
|
422
|
+
filter(:test) { false }
|
|
423
|
+
end.new(plan)
|
|
424
|
+
assert_raises(Planning::NotFound) { planner.test }
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
|