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.
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
+