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