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,592 @@
1
+ $LOAD_PATH.unshift File.expand_path('..', File.dirname(__FILE__))
2
+ require 'roby/test/common'
3
+ require 'flexmock'
4
+ require 'roby/test/tasks/simple_task'
5
+
6
+ require 'roby'
7
+
8
+ class TC_Exceptions < Test::Unit::TestCase
9
+ include Roby::Test
10
+ class SpecializedError < LocalizedError; end
11
+
12
+ def test_execution_exception_initialize
13
+ plan.discover(task = Task.new)
14
+ error = ExecutionException.new(LocalizedError.new(task))
15
+ assert_equal(task, error.task)
16
+ assert_equal([task], error.trace)
17
+ assert_equal(nil, error.generator)
18
+
19
+ ev = task.event(:start)
20
+ error = ExecutionException.new(LocalizedError.new(ev))
21
+ assert_equal(task, error.task)
22
+ assert_equal(ev, error.generator)
23
+ assert_equal([task], error.trace)
24
+ end
25
+
26
+ def test_execution_exception_fork
27
+ task, t1, t2, t3 = prepare_plan :discover => 5
28
+ e = ExecutionException.new(LocalizedError.new(task))
29
+ s = e.fork
30
+
31
+ assert_equal([e, s], e.siblings)
32
+ assert_equal([e, s], s.siblings)
33
+ e.trace << t1
34
+ s.trace << t2
35
+ assert_equal([task, t1], e.trace)
36
+ assert_equal([task, t2], s.trace)
37
+
38
+ e.merge(s)
39
+ assert_equal([task, [t1, t2]], e.trace)
40
+
41
+ s = e.fork
42
+ e.merge(s)
43
+ assert_equal([t1, t2], e.task)
44
+ assert_equal(task, e.origin)
45
+
46
+ s = e.fork
47
+ s.trace << t3
48
+ e.merge(s)
49
+ assert_equal([t1, t2, t3], e.task)
50
+ assert_equal(task, e.origin)
51
+
52
+ e = ExecutionException.new(LocalizedError.new(task))
53
+ s = e.fork
54
+ t1, t2 = prepare_plan :discover => 2
55
+ s.trace << t1 << t2
56
+ e.merge(s)
57
+ assert_equal([task, t2], e.task)
58
+ assert_equal(task, e.origin)
59
+
60
+ e = ExecutionException.new(LocalizedError.new(task))
61
+ s = e.fork
62
+ e.merge(s)
63
+ assert_equal(task, e.task)
64
+ assert_equal(task, e.origin)
65
+ end
66
+
67
+ class SignallingHandler < Roby::LocalizedError; end
68
+ def test_task_handle_exception
69
+ FlexMock.use do |mock|
70
+ received_handler2 = false
71
+ klass = Class.new(Task) do
72
+ on_exception(SpecializedError) do |exception|
73
+ mock.handler1(exception, exception.task, self)
74
+ end
75
+ on_exception(SpecializedError) do |exception|
76
+ if received_handler2
77
+ pass_exception
78
+ end
79
+ received_handler2 = true
80
+ mock.handler2(exception, exception.task, self)
81
+ end
82
+ on_exception(RuntimeError) do |exception|
83
+ pass_exception
84
+ end
85
+ on_exception(SignalException) do |exception|
86
+ raise
87
+ end
88
+ end
89
+
90
+ plan.discover(task = klass.new)
91
+ error = ExecutionException.new(SpecializedError.new(task))
92
+ mock.should_receive(:handler2).with(error, task, task).once.ordered
93
+ mock.should_receive(:handler1).with(error, task, task).once.ordered
94
+ assert(task.handle_exception(error))
95
+ assert(task.handle_exception(error))
96
+
97
+ error = ExecutionException.new(CodeError.new(nil, task))
98
+ assert(! task.handle_exception(error))
99
+ error = ExecutionException.new(SignallingHandler.new(task))
100
+ assert(! task.handle_exception(error))
101
+ end
102
+ end
103
+
104
+ def test_exception_in_handler
105
+ Roby.logger.level = Logger::FATAL
106
+
107
+ Roby.control.abort_on_exception = true
108
+ Roby.control.abort_on_application_exception = false
109
+ FlexMock.use do |mock|
110
+ klass = Class.new(SimpleTask) do
111
+ define_method(:mock) { mock }
112
+ event :start do |context|
113
+ mock.event_called
114
+ raise SpecializedError.new(self)
115
+ end
116
+
117
+ on_exception(RuntimeError) do |exception|
118
+ mock.task_handler_called
119
+ raise
120
+ end
121
+ end
122
+
123
+ Roby.on_exception(RuntimeError) do |task, exception|
124
+ mock.global_handler_called
125
+ raise
126
+ end
127
+
128
+ t1, t2 = klass.new, klass.new
129
+ t1.realized_by t2
130
+ plan.insert(t1)
131
+
132
+ mock.should_receive(:event_called).once.ordered
133
+ mock.should_receive(:task_handler_called).once.ordered
134
+ mock.should_receive(:global_handler_called).once.ordered
135
+ Control.once { t2.start! }
136
+ assert_raises(SpecializedError) { process_events }
137
+ end
138
+ end
139
+
140
+ def test_linear_propagation
141
+ FlexMock.use do |mock|
142
+ t1, t2 = Task.new, Task.new
143
+ t0 = Class.new(Task) do
144
+ on_exception(SpecializedError) do |exception|
145
+ mock.handler(exception, exception.task, self)
146
+ end
147
+ end.new
148
+ plan.discover(t0)
149
+ t0.realized_by t1
150
+ t1.realized_by t2
151
+
152
+ error = ExecutionException.new(SpecializedError.new(t2))
153
+ mock.should_receive(:handler).with(error, t1, t0).once
154
+ assert_equal([], Propagation.propagate_exceptions([error]))
155
+ assert_equal([error], error.siblings)
156
+ assert_equal([t2, t1], error.trace)
157
+
158
+ error = ExecutionException.new(CodeError.new(nil, t2))
159
+ assert_equal([error], Propagation.propagate_exceptions([error]))
160
+ assert_equal(t0, error.task)
161
+ assert_equal([t2, t1, t0], error.trace)
162
+
163
+ # Redo that but this time define a global exception handler
164
+ error = ExecutionException.new(CodeError.new(nil, t2))
165
+ Roby.on_exception(CodeError) do |mod, exception|
166
+ mock.global_handler(exception, exception.task, mod)
167
+ end
168
+ mock.should_receive(:global_handler).with(error, t0, Roby).once
169
+ assert_equal([], Propagation.propagate_exceptions([error]))
170
+ end
171
+ end
172
+
173
+ def test_forked_propagation
174
+ # We build a 0 -> 1 -> 2 3 -> 2 task tree with
175
+ # 0 being able to handle the exception and 1, 3 not
176
+
177
+ FlexMock.use do |mock|
178
+ t1, t2, t3 = prepare_plan :discover => 3
179
+ t0 = Class.new(Task) do
180
+ attr_accessor :handled_exception
181
+ on_exception(CodeError) do |exception|
182
+ self.handled_exception = exception
183
+ mock.handler(exception, exception.task, self)
184
+ end
185
+ end.new
186
+ plan.discover(t0)
187
+ t0.realized_by t1
188
+ t1.realized_by t2
189
+ t3.realized_by t2
190
+
191
+ error = ExecutionException.new(CodeError.new(nil, t2))
192
+ mock.should_receive(:handler).with(ExecutionException, t1, t0).once
193
+ # There are two possibilities here:
194
+ # 1/ the error propagation begins with t1 -> t0, in which case +error+
195
+ # is t0.handled_exception and there may be no sibling (the error is
196
+ # never tested on t3
197
+ # 2/ propagation begins with t3, in which case +error+ is a sibling of
198
+ # t0.handled_exception
199
+ assert_equal([], Propagation.propagate_exceptions([error]))
200
+ assert_equal([t2, t1], t0.handled_exception.trace)
201
+ if t0.handled_exception != error
202
+ assert_equal([t2, t3], error.trace)
203
+ assert_equal([t0.handled_exception, error].to_set, error.siblings.to_set)
204
+ end
205
+
206
+ error = ExecutionException.new(LocalizedError.new(t2))
207
+ assert(fatal = Propagation.propagate_exceptions([error]))
208
+ assert_equal(1, fatal.size)
209
+ e = *fatal
210
+ assert_equal(t2, e.origin)
211
+ assert_equal([t3, t0], e.task)
212
+ end
213
+ end
214
+
215
+ def test_diamond_propagation
216
+ # We build a 0 -> 1 -> 2 3 -> 2 task tree with
217
+ # 0 being able to handle the exception and 1, 3 not
218
+
219
+ FlexMock.use do |mock|
220
+ t1, t2, t3 = prepare_plan :discover => 3
221
+
222
+ found_exception = nil
223
+ t0 = Class.new(Task) do
224
+ on_exception(LocalizedError) do |exception|
225
+ found_exception = exception
226
+ mock.handler(exception, exception.task.to_set, self)
227
+ end
228
+ end.new
229
+ plan.discover(t0)
230
+ t0.realized_by t1 ; t1.realized_by t2
231
+ t0.realized_by t3 ; t3.realized_by t2
232
+
233
+
234
+ error = ExecutionException.new(LocalizedError.new(t2))
235
+ mock.should_receive(:handler).with(ExecutionException, [t1, t3].to_set, t0).once
236
+ assert_equal([], Propagation.propagate_exceptions([error]))
237
+ assert_equal(2, found_exception.trace.size, found_exception.trace)
238
+ assert_equal(t2, found_exception.origin)
239
+ assert_equal([t3, t1].to_set, found_exception.task.to_set)
240
+ end
241
+ end
242
+
243
+ def test_event_propagation_with_exception
244
+ ev = EventGenerator.new do |context|
245
+ raise RuntimeError
246
+ ev.emit(context)
247
+ end
248
+ plan.discover(ev)
249
+ assert_original_error(RuntimeError, CommandFailed) { ev.call(nil) }
250
+ assert(!ev.happened?)
251
+
252
+ # Check that the event is emitted anyway
253
+ ev = EventGenerator.new do |context|
254
+ ev.emit(context)
255
+ raise RuntimeError
256
+ end
257
+ plan.discover(ev)
258
+ assert_original_error(RuntimeError, CommandFailed) { ev.call(nil) }
259
+ assert(ev.happened?)
260
+
261
+ # Check signalling
262
+ ev = EventGenerator.new do |context|
263
+ ev.emit(context)
264
+ raise RuntimeError
265
+ end
266
+ plan.discover(ev)
267
+ ev2 = EventGenerator.new(true)
268
+ ev.on ev2
269
+
270
+ assert_original_error(RuntimeError, CommandFailed) { ev.call(nil) }
271
+ assert(ev.happened?)
272
+ assert(ev2.happened?)
273
+
274
+ # Check event handlers
275
+ FlexMock.use do |mock|
276
+ ev = EventGenerator.new(true)
277
+ plan.discover(ev)
278
+ ev.on { mock.handler ; raise RuntimeError }
279
+ ev.on { mock.handler }
280
+ mock.should_receive(:handler).twice
281
+ assert_original_error(RuntimeError, EventHandlerError) { ev.call }
282
+ end
283
+ end
284
+
285
+ # Tests exception handling mechanism during event propagation
286
+ def test_task_propagation_with_exception
287
+ Roby.control.abort_on_exception = true
288
+ Roby.logger.level = Logger::FATAL
289
+
290
+ task = Class.new(SimpleTask) do
291
+ event :start do |context|
292
+ emit(:start)
293
+ raise RuntimeError, "failed"
294
+ end
295
+ end.new
296
+
297
+ FlexMock.use do |mock|
298
+ parent = Class.new(Task) do
299
+ on_exception RuntimeError do
300
+ mock.exception
301
+ task.pass_exception
302
+ end
303
+ end.new
304
+ mock.should_receive(:exception).once
305
+
306
+ parent.realized_by task
307
+ plan.insert(parent)
308
+
309
+ Roby::Control.once { task.start! }
310
+
311
+ mock.should_receive(:other_once_handler).once
312
+ mock.should_receive(:other_event_processing).once
313
+ Roby::Control.once { mock.other_once_handler }
314
+ Roby::Control.event_processing << lambda { mock.other_event_processing }
315
+
316
+ begin
317
+ process_events
318
+ flunk("should have raised")
319
+ rescue Roby::CommandFailed => e
320
+ assert_kind_of(RuntimeError, e.error)
321
+ end
322
+ end
323
+ assert(task.event(:start).happened?)
324
+ end
325
+
326
+ def test_exception_argument_count_validation
327
+ assert_raises(ArgumentError) do
328
+ Class.new(Task).on_exception(RuntimeError) do ||
329
+ end
330
+ end
331
+ assert_raises(ArgumentError) do
332
+ Class.new(Task).on_exception(RuntimeError) do |a, b|
333
+ end
334
+ end
335
+ assert_nothing_raised do
336
+ Class.new(Task).on_exception(RuntimeError) do |_|
337
+ end
338
+ end
339
+
340
+ assert_raises(ArgumentError) do
341
+ Roby.on_exception(RuntimeError) do ||
342
+ end
343
+ end
344
+ assert_raises(ArgumentError) do |a, b|
345
+ Roby.on_exception(RuntimeError) do |_|
346
+ end
347
+ end
348
+ assert_nothing_raised do
349
+ Roby.on_exception(RuntimeError) do |_, _|
350
+ end
351
+ end
352
+ end
353
+
354
+ def test_exception_propagation_merging
355
+ FlexMock.use do |mock|
356
+ t11 = Task.new(:id => '11')
357
+ t12 = Task.new(:id => '12')
358
+ t13 = Task.new(:id => '13')
359
+
360
+ root = Class.new(Task) do
361
+ include Test::Unit::Assertions
362
+ on_exception(RuntimeError) do |exception|
363
+ assert_equal([t11, t12, t13].to_set, exception.task.to_set)
364
+ mock.caught(exception.task)
365
+ end
366
+ end.new(:id => 'root')
367
+ plan.discover(root)
368
+ root.realized_by(t11)
369
+ root.realized_by(t12)
370
+ root.realized_by(t13)
371
+
372
+ t11.realized_by(t21 = Task.new(:id => '21'))
373
+ t12.realized_by(t21)
374
+
375
+ t13.realized_by(t22 = Task.new(:id => '22'))
376
+ t22.realized_by(t31 = Task.new(:id => '31'))
377
+ t31.realized_by(t21)
378
+
379
+ mock.should_receive(:caught).once
380
+ Propagation.propagate_exceptions([ExecutionException.new(LocalizedError.new(t21))])
381
+ end
382
+ end
383
+
384
+ def test_plan_repairs
385
+ model = Class.new(SimpleTask) do
386
+ event :blocked
387
+ forward :blocked => :failed
388
+ end
389
+
390
+ # First, check methods located in Plan
391
+ plan.discover(task = model.new)
392
+ r1, r2 = SimpleTask.new, SimpleTask.new
393
+
394
+ task.start!
395
+ task.emit :blocked
396
+
397
+ blocked_event = task.history[-3]
398
+ failed_event = task.history[-2]
399
+ stop_event = task.history[-1]
400
+ plan.add_repair failed_event, r1
401
+ plan.add_repair blocked_event, r2
402
+ assert(plan.task_index.repaired_tasks.include?(task))
403
+
404
+ assert_equal({}, plan.repairs_for(stop_event))
405
+ assert_equal({failed_event => r1}, plan.repairs_for(failed_event))
406
+ assert_equal({blocked_event => r2, failed_event => r1}, plan.repairs_for(blocked_event))
407
+ plan.remove_repair r1
408
+ assert_equal({}, plan.repairs_for(failed_event))
409
+ assert_equal({blocked_event => r2}, plan.repairs_for(blocked_event))
410
+ plan.remove_repair r2
411
+ assert_equal({}, plan.repairs_for(stop_event))
412
+ assert_equal({}, plan.repairs_for(failed_event))
413
+ assert_equal({}, plan.repairs_for(blocked_event))
414
+ assert(!plan.task_index.repaired_tasks.include?(task))
415
+ end
416
+
417
+ def test_exception_inhibition
418
+ parent, child = prepare_plan :tasks => 2, :model => SimpleTask
419
+ plan.insert(parent)
420
+ parent.realized_by child
421
+ parent.on :start, child, :start
422
+ parent.start!
423
+ child.failed!
424
+
425
+ exceptions = Roby.control.structure_checking
426
+
427
+ plan.discover(repairing_task = SimpleTask.new)
428
+ repairing_task.start!
429
+ assert_equal(exceptions.to_a, Propagation.remove_inhibited_exceptions(exceptions))
430
+ assert_equal(exceptions.keys, Propagation.propagate_exceptions(exceptions))
431
+ plan.add_repair(child.terminal_event, repairing_task)
432
+ assert_equal([], Propagation.remove_inhibited_exceptions(exceptions))
433
+ assert_equal([], Propagation.propagate_exceptions(exceptions))
434
+
435
+ ensure
436
+ # Remove the child so that the test's plan cleanup does not complain
437
+ parent.remove_child child if child
438
+ end
439
+
440
+ def test_error_handling_relation(error_event = :failed)
441
+ task_model = Class.new(SimpleTask) do
442
+ event :blocked
443
+ forward :blocked => :failed
444
+ end
445
+
446
+ parent, child = prepare_plan :tasks => 2, :model => task_model
447
+ plan.insert(parent)
448
+ parent.realized_by child
449
+ repairing_task = SimpleTask.new
450
+ child.event(:failed).handle_with repairing_task
451
+
452
+ parent.start!
453
+ child.start!
454
+ child.emit error_event
455
+
456
+ exceptions = Roby.control.structure_checking
457
+
458
+ assert_equal([], Propagation.propagate_exceptions(exceptions))
459
+ assert_equal({ child.terminal_event => repairing_task },
460
+ plan.repairs_for(child.terminal_event), [plan.repairs, child.terminal_event])
461
+
462
+ Roby.control.abort_on_exception = false
463
+ process_events
464
+ assert(repairing_task.running?)
465
+
466
+ # Make the "repair task" finish, but do not repair the plan.
467
+ # propagate_exceptions must not add a new repair
468
+ repairing_task.success!
469
+ assert_equal(exceptions.keys, Propagation.propagate_exceptions(exceptions))
470
+
471
+ ensure
472
+ parent.remove_child child if child
473
+ end
474
+
475
+ def test_error_handling_relation_generalization
476
+ test_error_handling_relation(:blocked)
477
+ end
478
+
479
+ def test_handling_missions_exceptions
480
+ mission = prepare_plan :missions => 1, :model => SimpleTask
481
+ repairing_task = SimpleTask.new
482
+ mission.event(:failed).handle_with repairing_task
483
+
484
+ mission.start!
485
+ mission.emit :failed
486
+
487
+ exceptions = Roby.control.structure_checking
488
+ assert_equal(1, exceptions.size)
489
+ assert_kind_of(Roby::MissionFailedError, exceptions.to_a[0][0].exception, exceptions)
490
+
491
+ assert_equal([], Propagation.propagate_exceptions(exceptions))
492
+ assert_equal({ mission.terminal_event => repairing_task },
493
+ plan.repairs_for(mission.terminal_event), [plan.repairs, mission.terminal_event])
494
+
495
+ Roby.control.abort_on_exception = false
496
+ process_events
497
+ assert(plan.mission?(mission))
498
+ assert(repairing_task.running?)
499
+
500
+ # Make the "repair task" finish, but do not repair the plan.
501
+ # propagate_exceptions must not add a new repair
502
+ repairing_task.success!
503
+ assert_equal(exceptions.keys, Propagation.propagate_exceptions(exceptions))
504
+
505
+ # Discard the mission so that the test teardown does not complain
506
+ plan.discard(mission)
507
+ end
508
+
509
+ def test_filter_command_errors
510
+ model = Class.new(SimpleTask) do
511
+ event :start do
512
+ raise ArgumentError
513
+ end
514
+ end
515
+
516
+ task = prepare_plan :permanent => 1, :model => model
517
+ error = begin task.start!
518
+ rescue Exception => e; e
519
+ end
520
+ assert_kind_of CodeError, e
521
+ assert_nothing_raised do
522
+ Roby.format_exception e
523
+ end
524
+
525
+ trace = e.error.backtrace
526
+ filtered = Roby.filter_backtrace(trace)
527
+ assert(filtered[0] =~ /command for 'start'/, filtered[0])
528
+ assert(filtered[1] =~ /test_filter_command_errors/, filtered[1])
529
+ end
530
+
531
+ def test_filter_handler_errors
532
+ task = prepare_plan :permanent => 1, :model => SimpleTask
533
+ task.on(:start) { raise ArgumentError }
534
+ error = begin task.start!
535
+ rescue Exception => e; e
536
+ end
537
+ assert_kind_of CodeError, e
538
+ assert_nothing_raised do
539
+ Roby.format_exception e
540
+ end
541
+
542
+ trace = e.error.backtrace
543
+ filtered = Roby.filter_backtrace(trace)
544
+ assert(filtered[0] =~ /event handler/, filtered.join("\n"))
545
+ assert(filtered[1] =~ /test_filter_handler_errors/, filtered.join("\n"))
546
+
547
+ model = Class.new(SimpleTask) do
548
+ on :start do
549
+ raise ArgumentError
550
+ end
551
+ end
552
+ task = prepare_plan :permanent => 1, :model => model
553
+ error = begin task.start!
554
+ rescue Exception => e; e
555
+ end
556
+ assert_kind_of CodeError, e
557
+ assert_nothing_raised do
558
+ Roby.format_exception e
559
+ end
560
+
561
+ trace = e.error.backtrace
562
+ filtered = Roby.filter_backtrace(trace)
563
+ assert(filtered[0] =~ /event handler for 'start'$/, filtered.join("\n"))
564
+ assert(filtered[1] =~ /test_filter_handler_errors/, filtered.join("\n"))
565
+ end
566
+
567
+ def test_filter_polling_errors
568
+ #Roby.control.fatal_exceptions = false
569
+
570
+ model = Class.new(SimpleTask) do
571
+ poll do
572
+ raise ArgumentError, "bla"
573
+ end
574
+ end
575
+
576
+ parent = prepare_plan :permanent => 1, :model => SimpleTask
577
+ child = prepare_plan :permanent => 1, :model => model
578
+ parent.realized_by child
579
+ parent.start!
580
+ child.start!
581
+ child.failed!
582
+
583
+ error = TaskStructure::Hierarchy.check_structure(plan).first.exception
584
+ assert_kind_of(ChildFailedError, error)
585
+ assert_nothing_raised do
586
+ Roby.format_exception(error)
587
+ end
588
+ # To silently finish the test ...
589
+ parent.stop!
590
+ end
591
+ end
592
+