roby 0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (240) hide show
  1. data/.gitignore +29 -0
  2. data/History.txt +4 -0
  3. data/License-fr.txt +519 -0
  4. data/License.txt +515 -0
  5. data/Manifest.txt +245 -0
  6. data/NOTES +4 -0
  7. data/README.txt +163 -0
  8. data/Rakefile +161 -0
  9. data/TODO.txt +146 -0
  10. data/app/README.txt +24 -0
  11. data/app/Rakefile +8 -0
  12. data/app/config/ROBOT.rb +5 -0
  13. data/app/config/app.yml +91 -0
  14. data/app/config/init.rb +7 -0
  15. data/app/config/roby.yml +3 -0
  16. data/app/controllers/.gitattributes +0 -0
  17. data/app/controllers/ROBOT.rb +2 -0
  18. data/app/data/.gitattributes +0 -0
  19. data/app/planners/ROBOT/main.rb +6 -0
  20. data/app/planners/main.rb +5 -0
  21. data/app/scripts/distributed +3 -0
  22. data/app/scripts/generate/bookmarks +3 -0
  23. data/app/scripts/replay +3 -0
  24. data/app/scripts/results +3 -0
  25. data/app/scripts/run +3 -0
  26. data/app/scripts/server +3 -0
  27. data/app/scripts/shell +3 -0
  28. data/app/scripts/test +3 -0
  29. data/app/tasks/.gitattributes +0 -0
  30. data/app/tasks/ROBOT/.gitattributes +0 -0
  31. data/bin/roby +210 -0
  32. data/bin/roby-log +168 -0
  33. data/bin/roby-shell +25 -0
  34. data/doc/images/event_generalization.png +0 -0
  35. data/doc/images/exception_propagation_1.png +0 -0
  36. data/doc/images/exception_propagation_2.png +0 -0
  37. data/doc/images/exception_propagation_3.png +0 -0
  38. data/doc/images/exception_propagation_4.png +0 -0
  39. data/doc/images/exception_propagation_5.png +0 -0
  40. data/doc/images/replay_handler_error.png +0 -0
  41. data/doc/images/replay_handler_error_0.png +0 -0
  42. data/doc/images/replay_handler_error_1.png +0 -0
  43. data/doc/images/roby_cycle_overview.png +0 -0
  44. data/doc/images/roby_replay_02.png +0 -0
  45. data/doc/images/roby_replay_03.png +0 -0
  46. data/doc/images/roby_replay_04.png +0 -0
  47. data/doc/images/roby_replay_event_representation.png +0 -0
  48. data/doc/images/roby_replay_first_state.png +0 -0
  49. data/doc/images/roby_replay_relations.png +0 -0
  50. data/doc/images/roby_replay_startup.png +0 -0
  51. data/doc/images/task_event_generalization.png +0 -0
  52. data/doc/papers.rdoc +11 -0
  53. data/doc/styles/allison.css +314 -0
  54. data/doc/styles/allison.js +316 -0
  55. data/doc/styles/allison.rb +276 -0
  56. data/doc/styles/jamis.rb +593 -0
  57. data/doc/tutorials/01-GettingStarted.rdoc +86 -0
  58. data/doc/tutorials/02-GoForward.rdoc +220 -0
  59. data/doc/tutorials/03-PlannedPath.rdoc +268 -0
  60. data/doc/tutorials/04-EventPropagation.rdoc +236 -0
  61. data/doc/tutorials/05-ErrorHandling.rdoc +319 -0
  62. data/doc/tutorials/06-Overview.rdoc +40 -0
  63. data/doc/videos.rdoc +69 -0
  64. data/ext/droby/dump.cc +175 -0
  65. data/ext/droby/extconf.rb +3 -0
  66. data/ext/graph/algorithm.cc +746 -0
  67. data/ext/graph/extconf.rb +7 -0
  68. data/ext/graph/graph.cc +529 -0
  69. data/ext/graph/graph.hh +183 -0
  70. data/ext/graph/iterator_sequence.hh +102 -0
  71. data/ext/graph/undirected_dfs.hh +226 -0
  72. data/ext/graph/undirected_graph.hh +421 -0
  73. data/lib/roby.rb +41 -0
  74. data/lib/roby/app.rb +870 -0
  75. data/lib/roby/app/rake.rb +56 -0
  76. data/lib/roby/app/run.rb +14 -0
  77. data/lib/roby/app/scripts/distributed.rb +13 -0
  78. data/lib/roby/app/scripts/generate/bookmarks.rb +162 -0
  79. data/lib/roby/app/scripts/replay.rb +31 -0
  80. data/lib/roby/app/scripts/results.rb +15 -0
  81. data/lib/roby/app/scripts/run.rb +26 -0
  82. data/lib/roby/app/scripts/server.rb +18 -0
  83. data/lib/roby/app/scripts/shell.rb +88 -0
  84. data/lib/roby/app/scripts/test.rb +40 -0
  85. data/lib/roby/basic_object.rb +151 -0
  86. data/lib/roby/config.rb +5 -0
  87. data/lib/roby/control.rb +747 -0
  88. data/lib/roby/decision_control.rb +17 -0
  89. data/lib/roby/distributed.rb +32 -0
  90. data/lib/roby/distributed/base.rb +440 -0
  91. data/lib/roby/distributed/communication.rb +871 -0
  92. data/lib/roby/distributed/connection_space.rb +592 -0
  93. data/lib/roby/distributed/distributed_object.rb +206 -0
  94. data/lib/roby/distributed/drb.rb +62 -0
  95. data/lib/roby/distributed/notifications.rb +539 -0
  96. data/lib/roby/distributed/peer.rb +550 -0
  97. data/lib/roby/distributed/protocol.rb +529 -0
  98. data/lib/roby/distributed/proxy.rb +343 -0
  99. data/lib/roby/distributed/subscription.rb +311 -0
  100. data/lib/roby/distributed/transaction.rb +498 -0
  101. data/lib/roby/event.rb +897 -0
  102. data/lib/roby/exceptions.rb +234 -0
  103. data/lib/roby/executives/simple.rb +30 -0
  104. data/lib/roby/graph.rb +166 -0
  105. data/lib/roby/interface.rb +390 -0
  106. data/lib/roby/log.rb +3 -0
  107. data/lib/roby/log/chronicle.rb +303 -0
  108. data/lib/roby/log/console.rb +72 -0
  109. data/lib/roby/log/data_stream.rb +197 -0
  110. data/lib/roby/log/dot.rb +279 -0
  111. data/lib/roby/log/event_stream.rb +151 -0
  112. data/lib/roby/log/file.rb +340 -0
  113. data/lib/roby/log/gui/basic_display.ui +83 -0
  114. data/lib/roby/log/gui/chronicle.rb +26 -0
  115. data/lib/roby/log/gui/chronicle_view.rb +40 -0
  116. data/lib/roby/log/gui/chronicle_view.ui +70 -0
  117. data/lib/roby/log/gui/data_displays.rb +172 -0
  118. data/lib/roby/log/gui/data_displays.ui +155 -0
  119. data/lib/roby/log/gui/notifications.rb +26 -0
  120. data/lib/roby/log/gui/relations.rb +248 -0
  121. data/lib/roby/log/gui/relations.ui +123 -0
  122. data/lib/roby/log/gui/relations_view.rb +185 -0
  123. data/lib/roby/log/gui/relations_view.ui +149 -0
  124. data/lib/roby/log/gui/replay.rb +327 -0
  125. data/lib/roby/log/gui/replay_controls.rb +200 -0
  126. data/lib/roby/log/gui/replay_controls.ui +259 -0
  127. data/lib/roby/log/gui/runtime.rb +130 -0
  128. data/lib/roby/log/hooks.rb +185 -0
  129. data/lib/roby/log/logger.rb +202 -0
  130. data/lib/roby/log/notifications.rb +244 -0
  131. data/lib/roby/log/plan_rebuilder.rb +470 -0
  132. data/lib/roby/log/relations.rb +1056 -0
  133. data/lib/roby/log/server.rb +550 -0
  134. data/lib/roby/log/sqlite.rb +47 -0
  135. data/lib/roby/log/timings.rb +164 -0
  136. data/lib/roby/plan-object.rb +247 -0
  137. data/lib/roby/plan.rb +762 -0
  138. data/lib/roby/planning.rb +13 -0
  139. data/lib/roby/planning/loops.rb +302 -0
  140. data/lib/roby/planning/model.rb +906 -0
  141. data/lib/roby/planning/task.rb +151 -0
  142. data/lib/roby/propagation.rb +562 -0
  143. data/lib/roby/query.rb +619 -0
  144. data/lib/roby/relations.rb +583 -0
  145. data/lib/roby/relations/conflicts.rb +70 -0
  146. data/lib/roby/relations/ensured.rb +20 -0
  147. data/lib/roby/relations/error_handling.rb +23 -0
  148. data/lib/roby/relations/events.rb +9 -0
  149. data/lib/roby/relations/executed_by.rb +193 -0
  150. data/lib/roby/relations/hierarchy.rb +239 -0
  151. data/lib/roby/relations/influence.rb +10 -0
  152. data/lib/roby/relations/planned_by.rb +63 -0
  153. data/lib/roby/robot.rb +7 -0
  154. data/lib/roby/standard_errors.rb +218 -0
  155. data/lib/roby/state.rb +5 -0
  156. data/lib/roby/state/events.rb +221 -0
  157. data/lib/roby/state/information.rb +55 -0
  158. data/lib/roby/state/pos.rb +110 -0
  159. data/lib/roby/state/shapes.rb +32 -0
  160. data/lib/roby/state/state.rb +353 -0
  161. data/lib/roby/support.rb +92 -0
  162. data/lib/roby/task-operations.rb +182 -0
  163. data/lib/roby/task.rb +1618 -0
  164. data/lib/roby/test/common.rb +399 -0
  165. data/lib/roby/test/distributed.rb +214 -0
  166. data/lib/roby/test/tasks/empty_task.rb +9 -0
  167. data/lib/roby/test/tasks/goto.rb +36 -0
  168. data/lib/roby/test/tasks/simple_task.rb +23 -0
  169. data/lib/roby/test/testcase.rb +519 -0
  170. data/lib/roby/test/tools.rb +160 -0
  171. data/lib/roby/thread_task.rb +87 -0
  172. data/lib/roby/transactions.rb +462 -0
  173. data/lib/roby/transactions/proxy.rb +292 -0
  174. data/lib/roby/transactions/updates.rb +139 -0
  175. data/plugins/fault_injection/History.txt +4 -0
  176. data/plugins/fault_injection/README.txt +37 -0
  177. data/plugins/fault_injection/Rakefile +18 -0
  178. data/plugins/fault_injection/TODO.txt +0 -0
  179. data/plugins/fault_injection/app.rb +52 -0
  180. data/plugins/fault_injection/fault_injection.rb +89 -0
  181. data/plugins/fault_injection/test/test_fault_injection.rb +84 -0
  182. data/plugins/subsystems/README.txt +40 -0
  183. data/plugins/subsystems/Rakefile +18 -0
  184. data/plugins/subsystems/app.rb +171 -0
  185. data/plugins/subsystems/test/app/README +24 -0
  186. data/plugins/subsystems/test/app/Rakefile +8 -0
  187. data/plugins/subsystems/test/app/config/app.yml +71 -0
  188. data/plugins/subsystems/test/app/config/init.rb +9 -0
  189. data/plugins/subsystems/test/app/config/roby.yml +3 -0
  190. data/plugins/subsystems/test/app/planners/main.rb +20 -0
  191. data/plugins/subsystems/test/app/scripts/distributed +3 -0
  192. data/plugins/subsystems/test/app/scripts/replay +3 -0
  193. data/plugins/subsystems/test/app/scripts/results +3 -0
  194. data/plugins/subsystems/test/app/scripts/run +3 -0
  195. data/plugins/subsystems/test/app/scripts/server +3 -0
  196. data/plugins/subsystems/test/app/scripts/shell +3 -0
  197. data/plugins/subsystems/test/app/scripts/test +3 -0
  198. data/plugins/subsystems/test/app/tasks/services.rb +15 -0
  199. data/plugins/subsystems/test/test_subsystems.rb +71 -0
  200. data/test/distributed/test_communication.rb +178 -0
  201. data/test/distributed/test_connection.rb +282 -0
  202. data/test/distributed/test_execution.rb +373 -0
  203. data/test/distributed/test_mixed_plan.rb +341 -0
  204. data/test/distributed/test_plan_notifications.rb +238 -0
  205. data/test/distributed/test_protocol.rb +516 -0
  206. data/test/distributed/test_query.rb +102 -0
  207. data/test/distributed/test_remote_plan.rb +491 -0
  208. data/test/distributed/test_transaction.rb +463 -0
  209. data/test/mockups/tasks.rb +27 -0
  210. data/test/planning/test_loops.rb +380 -0
  211. data/test/planning/test_model.rb +427 -0
  212. data/test/planning/test_task.rb +106 -0
  213. data/test/relations/test_conflicts.rb +42 -0
  214. data/test/relations/test_ensured.rb +38 -0
  215. data/test/relations/test_executed_by.rb +149 -0
  216. data/test/relations/test_hierarchy.rb +158 -0
  217. data/test/relations/test_planned_by.rb +54 -0
  218. data/test/suite_core.rb +24 -0
  219. data/test/suite_distributed.rb +9 -0
  220. data/test/suite_planning.rb +3 -0
  221. data/test/suite_relations.rb +8 -0
  222. data/test/test_bgl.rb +508 -0
  223. data/test/test_control.rb +399 -0
  224. data/test/test_event.rb +894 -0
  225. data/test/test_exceptions.rb +592 -0
  226. data/test/test_interface.rb +37 -0
  227. data/test/test_log.rb +114 -0
  228. data/test/test_log_server.rb +132 -0
  229. data/test/test_plan.rb +584 -0
  230. data/test/test_propagation.rb +210 -0
  231. data/test/test_query.rb +266 -0
  232. data/test/test_relations.rb +180 -0
  233. data/test/test_state.rb +414 -0
  234. data/test/test_support.rb +16 -0
  235. data/test/test_task.rb +938 -0
  236. data/test/test_testcase.rb +122 -0
  237. data/test/test_thread_task.rb +73 -0
  238. data/test/test_transactions.rb +569 -0
  239. data/test/test_transactions_proxy.rb +198 -0
  240. 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
+