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,16 @@
1
+ $LOAD_PATH.unshift File.expand_path('..', File.dirname(__FILE__))
2
+ require 'roby/test/common'
3
+ require 'flexmock'
4
+ require 'roby/support'
5
+
6
+ class TC_Utils < Test::Unit::TestCase
7
+ include Roby
8
+ include Roby::Test
9
+
10
+ def test_define_under
11
+ mod = Module.new
12
+ new_mod = mod.define_under(:Foo) { Module.new }
13
+ assert_equal(new_mod, mod.define_under(:Foo) { flunk("block called in #define_under") })
14
+ end
15
+ end
16
+
data/test/test_task.rb ADDED
@@ -0,0 +1,938 @@
1
+ $LOAD_PATH.unshift File.expand_path('..', File.dirname(__FILE__))
2
+ require 'roby/test/common'
3
+ require 'roby/test/tasks/simple_task'
4
+ require 'roby/test/tasks/empty_task'
5
+ require 'flexmock'
6
+
7
+ class TC_Task < Test::Unit::TestCase
8
+ include Roby::Test
9
+
10
+ def test_model_tag
11
+ my_tag = TaskModelTag.new do
12
+ argument :model_tag
13
+ end
14
+ assert(my_tag.const_defined?(:ClassExtension))
15
+ assert(my_tag::ClassExtension.method_defined?(:argument))
16
+ task = Class.new(Task) do
17
+ include my_tag
18
+ argument :task_tag
19
+ end
20
+ assert_equal([:task_tag, :model_tag].to_set, task.arguments.to_set)
21
+ end
22
+
23
+ def test_arguments
24
+ model = Class.new(Task) do
25
+ arguments :from, :to
26
+ end
27
+ plan.discover(task = model.new(:from => 'B', :useless => 'bla'))
28
+ assert_equal([], Task.arguments.to_a)
29
+ assert_equal([:from, :to].to_value_set, task.model.arguments.to_value_set)
30
+ assert_equal({:from => 'B', :useless => 'bla'}, task.arguments)
31
+ assert_equal({:from => 'B'}, task.meaningful_arguments)
32
+
33
+ assert(task.partially_instanciated?)
34
+ task.arguments[:to] = 'A'
35
+ assert_equal('A', task.arguments[:to])
36
+ assert(!task.partially_instanciated?)
37
+ assert_raises(ArgumentError) { task.arguments[:to] = 10 }
38
+
39
+ task.arguments[:bar] = 42
40
+ assert_nothing_raised { task.arguments[:bar] = 43 }
41
+ end
42
+
43
+ def test_command_block
44
+ FlexMock.use do |mock|
45
+ model = Class.new(SimpleTask) do
46
+ event :start do |context|
47
+ mock.start(self, context)
48
+ emit :start
49
+ end
50
+ end
51
+ plan.insert(task = model.new)
52
+ mock.should_receive(:start).once.with(task, [42])
53
+ task.start!(42)
54
+ end
55
+ end
56
+
57
+ def test_command_inheritance
58
+ FlexMock.use do |mock|
59
+ parent_m = Class.new(SimpleTask) do
60
+ event :start do |context|
61
+ mock.parent_started(self, context)
62
+ emit :start
63
+ end
64
+ end
65
+
66
+ child_m = Class.new(parent_m) do
67
+ event :start do |context|
68
+ mock.child_started(self, context.first)
69
+ super(context.first / 2)
70
+ end
71
+ end
72
+
73
+ plan.insert(task = child_m.new)
74
+ mock.should_receive(:parent_started).once.with(task, 21)
75
+ mock.should_receive(:child_started).once.with(task, 42)
76
+ task.start!(42)
77
+ end
78
+ end
79
+
80
+ Precedence = Roby::EventStructure::Precedence
81
+ def assert_direct_precedence(task, relations)
82
+ relations.each do |from, to|
83
+ from = task.event(from)
84
+ to = to.map { |sym| task.event(sym) }
85
+
86
+ assert_equal(to.to_value_set, from.child_objects(Precedence).to_value_set, from.symbol)
87
+ end
88
+ end
89
+
90
+ def test_instantiate_model_event_relations
91
+ # Create a task model with two intermediate events being
92
+ # linked by a causal link
93
+ task = Class.new(Roby::Test::SimpleTask) do
94
+ event :first
95
+ event :second
96
+ event :third
97
+ causal_link :first => :third
98
+ causal_link :second => :third
99
+ end.new
100
+ plan.discover(task)
101
+
102
+ assert_direct_precedence task,
103
+ :start => [:first, :second, :updated_data],
104
+ :first => [:third],
105
+ :second => [:third],
106
+ :third => [:success, :aborted],
107
+ :aborted => [:failed],
108
+ :failed => [:stop],
109
+ :success => [:stop]
110
+
111
+ end
112
+
113
+ # Test the behaviour of Task#on, and event propagation inside a task
114
+ def test_instance_on
115
+ plan.discover(t1 = SimpleTask.new)
116
+ assert_raises(ArgumentError) { t1.on(:start) }
117
+
118
+ # Test command handlers
119
+ plan.discover(task = SimpleTask.new)
120
+ FlexMock.use do |mock|
121
+ task.on(:start) { |event| mock.started(event.context) }
122
+ task.on(:start) { |event| task.emit(:success, *event.context) }
123
+ task.on(:success) { |event| mock.success(event.context) }
124
+ task.on(:stop) { |event| mock.stopped(event.context) }
125
+ mock.should_receive(:started).once.with([42]).ordered
126
+ mock.should_receive(:success).once.with([42]).ordered
127
+ mock.should_receive(:stopped).once.with([42]).ordered
128
+ task.start!(42)
129
+ end
130
+ assert(task.finished?)
131
+ event_history = task.history.map { |ev| ev.generator }
132
+ assert_equal([task.event(:start), task.event(:success), task.event(:stop)], event_history)
133
+
134
+ # Same test, but with signals
135
+ FlexMock.use do |mock|
136
+ t1, t2 = prepare_plan :discover => 2, :model => SimpleTask
137
+ t1.on(:start, t2)
138
+ t2.on(:start) { mock.start }
139
+
140
+ mock.should_receive(:start).once
141
+ t1.start!
142
+ end
143
+
144
+ FlexMock.use do |mock|
145
+ t1, t2 = prepare_plan :discover => 2, :model => SimpleTask
146
+ t2.start!
147
+
148
+ t1.on(:start, t2, :stop)
149
+ t2.on(:start) { mock.start }
150
+ t2.on(:stop) { mock.stop }
151
+
152
+ mock.should_receive(:start).never
153
+ mock.should_receive(:stop).once
154
+ t1.start!
155
+ end
156
+
157
+ t = prepare_plan :missions => 1, :model => SimpleTask
158
+ e = EventGenerator.new(true)
159
+ t.on(:start, e)
160
+ t.start!
161
+ assert(e.happened?)
162
+ end
163
+
164
+ def test_model_event_handling
165
+ model = Class.new(SimpleTask) do
166
+ forward :start => :failed
167
+ end
168
+ assert_equal({ :start => [:failed, :stop].to_value_set }, model.forwarding_sets)
169
+ assert_equal({}, SimpleTask.signal_sets)
170
+
171
+ assert_equal([:failed, :stop].to_value_set, model.forwardings(:start))
172
+ assert_equal([:stop].to_value_set, model.forwardings(:failed))
173
+ assert_equal(model.forwardings(:failed), model.enum_for(:each_forwarding, :failed).to_value_set)
174
+
175
+ # Event handlers must accept exactly one argument
176
+ assert_raises(ArgumentError) { model.on(:start) { || } }
177
+ assert_raises(ArgumentError) { model.on(:start) { |a, b| } }
178
+
179
+ FlexMock.use do |mock|
180
+ model.on :start do
181
+ mock.start_called(self)
182
+ end
183
+ plan.discover(task = model.new)
184
+ mock.should_receive(:start_called).with(task).once
185
+ task.start!
186
+ assert(task.failed?)
187
+ end
188
+
189
+ # Make sure the model-level signal is not applied to parent models
190
+ plan.discover(task = SimpleTask.new)
191
+ task.start!
192
+ assert(!task.failed?)
193
+ end
194
+
195
+ def test_forward
196
+ FlexMock.use do |mock|
197
+ t1, t2 = prepare_plan :missions => 2, :model => SimpleTask
198
+ t1.forward(:start, t2)
199
+ t2.on(:start) { mock.start }
200
+
201
+ mock.should_receive(:start).once
202
+ t1.start!
203
+ end
204
+
205
+ FlexMock.use do |mock|
206
+ t1, t2 = prepare_plan :missions => 2, :model => SimpleTask
207
+ t2.start!
208
+
209
+ t1.forward(:start, t2, :stop)
210
+ t2.on(:start) { mock.start }
211
+ t2.on(:stop) { mock.stop }
212
+
213
+ mock.should_receive(:start).never
214
+ mock.should_receive(:stop).once
215
+ t1.start!
216
+ end
217
+
218
+
219
+ FlexMock.use do |mock|
220
+ t1 = prepare_plan :missions => 1, :model => SimpleTask
221
+ ev = EventGenerator.new do
222
+ mock.called
223
+ ev.emit
224
+ end
225
+ ev.on { mock.emitted }
226
+ t1.forward(:start, ev)
227
+
228
+ mock.should_receive(:called).never
229
+ mock.should_receive(:emitted).once
230
+ t1.start!
231
+ end
232
+ end
233
+
234
+ def test_terminal
235
+ klass = Class.new(Task) do
236
+ event(:terminal_model, :terminal => true)
237
+ event(:terminal_model_signal)
238
+ forward :terminal_model_signal => :terminal_model
239
+
240
+ event(:success_model, :terminal => true)
241
+ event(:success_model_signal)
242
+ forward :success_model_signal => :success
243
+ forward :success_model_signal => :success_model
244
+
245
+ event(:failure_model, :terminal => true)
246
+ event(:failure_model_signal)
247
+ forward :failure_model_signal => :failed
248
+ forward :failure_model_signal => :failure_model
249
+
250
+ event(:ev)
251
+ event :controlable_terminal, :terminal => true, :command => true
252
+ end
253
+ assert(klass.event_model(:stop).terminal?)
254
+ assert(klass.event_model(:success).terminal?)
255
+ assert(klass.event_model(:failed).terminal?)
256
+ assert(klass.event_model(:terminal_model).terminal?)
257
+ assert(klass.event_model(:terminal_model_signal).terminal?)
258
+
259
+ plan.discover(task = klass.new)
260
+ assert(task.event(:stop).terminal?)
261
+ assert(!task.event(:stop).success?)
262
+ assert(!task.event(:stop).failure?)
263
+ assert(task.event(:success).terminal?)
264
+ assert(task.event(:success).success?)
265
+ assert(!task.event(:success).failure?)
266
+ assert(task.event(:failed).terminal?)
267
+ assert(!task.event(:failed).success?)
268
+ assert(task.event(:failed).failure?)
269
+
270
+ ev = task.event(:ev)
271
+ assert(!ev.terminal?)
272
+ assert(!ev.success?)
273
+ assert(!ev.failure?)
274
+ ev.forward task.event(:terminal_model_signal)
275
+ assert(ev.terminal?)
276
+ assert(!ev.success?)
277
+ assert(!ev.failure?)
278
+ ev.forward task.event(:success_model_signal)
279
+ assert(ev.terminal?)
280
+ assert(ev.success?)
281
+ assert(!ev.failure?)
282
+ ev.remove_forwarding task.event(:success_model_signal)
283
+ ev.forward task.event(:failure_model_signal)
284
+ assert(ev.terminal?)
285
+ assert(!ev.success?)
286
+ assert(ev.failure?)
287
+
288
+ ev.remove_forwarding task.event(:failure_model_signal)
289
+ ev.remove_forwarding task.event(:terminal_model_signal)
290
+ assert(!ev.terminal?)
291
+ ev.forward task.event(:failure_model_signal), :delay => 0.2
292
+ assert(!ev.terminal?)
293
+ ev.remove_forwarding task.event(:failure_model_signal)
294
+ ev.signal task.event(:controlable_terminal), :delay => 0.2
295
+ assert(!ev.terminal?)
296
+ end
297
+
298
+ # Tests Task::event
299
+ def test_event_declaration
300
+ klass = Class.new(Task) do
301
+ def ev_not_controlable; end
302
+ def ev_method(event = :ev_method); :ev_method if event == :ev_redirected end
303
+
304
+ event :ev_contingent
305
+ event :ev_controlable do |*events|
306
+ :ev_controlable
307
+ end
308
+
309
+ event :ev_not_controlable
310
+ event :ev_redirected, :command => lambda { |task, event, *args| task.ev_method(event) }
311
+ end
312
+
313
+ klass.event :ev_terminal, :terminal => true, :command => true
314
+
315
+ plan.discover(task = klass.new)
316
+ assert_respond_to(task, :start!)
317
+
318
+ # Test modifications to the class hierarchy
319
+ my_event = nil
320
+ assert_nothing_raised { my_event = klass.const_get(:EvContingent) }
321
+ assert_raise(NameError) { klass.superclass.const_get(:EvContingent) }
322
+ assert_equal( TaskEvent, my_event.superclass )
323
+ assert_equal( :ev_contingent, my_event.symbol )
324
+ assert( klass.has_event?(:ev_contingent) )
325
+
326
+ assert_nothing_raised { my_event = klass.const_get(:EvTerminal) }
327
+ assert_equal( :ev_terminal, my_event.symbol )
328
+
329
+ # Check properties on EvContingent
330
+ assert( !klass::EvContingent.respond_to?(:call) )
331
+ assert( !klass::EvContingent.controlable? )
332
+ assert( !klass::EvContingent.terminal? )
333
+
334
+ # Check properties on EvControlable
335
+ assert( klass::EvControlable.controlable? )
336
+ assert( klass::EvControlable.respond_to?(:call) )
337
+ event = klass::EvControlable.new(task, task.event(:ev_controlable), 0, nil)
338
+ assert_equal(:ev_controlable, klass::EvControlable.call(task, :ev_controlable))
339
+
340
+ # Check Event.terminal? if :terminal => true
341
+ assert( klass::EvTerminal.terminal? )
342
+
343
+ # Check :controlable => [proc] behaviour
344
+ assert( klass::EvRedirected.controlable? )
345
+
346
+ # Check that :command => false disables controlable?
347
+ assert( :ev_not_controlable, !klass::EvNotControlable.controlable? )
348
+
349
+ # Check validation of options[:command]
350
+ assert_raise(ArgumentError) { klass.event :try_event, :command => "bla" }
351
+
352
+ plan.discover(task = EmptyTask.new)
353
+ start_event = task.event(:start)
354
+
355
+ assert_equal(start_event, task.event(:start))
356
+ assert_equal([], start_event.handlers)
357
+ # Note that the start => stop forwarding is added because 'start' is
358
+ # detected as terminal in the EmptyTask model
359
+ assert_equal([task.event(:stop), task.event(:success)].to_set, start_event.enum_for(:each_forwarding).to_set)
360
+ start_model = task.event_model(:start)
361
+ assert_equal(start_model, start_event.event_model)
362
+ assert_equal([:stop, :success].to_set, task.enum_for(:each_forwarding, :start).to_set)
363
+ end
364
+ def test_status
365
+ task = Class.new(Roby::Task) do
366
+ event :start do
367
+ end
368
+ event :failed, :terminal => true do
369
+ end
370
+ event :stop do
371
+ failed!
372
+ end
373
+ end.new
374
+ plan.discover(task)
375
+
376
+ assert(task.pending?)
377
+ assert(!task.starting?)
378
+ assert(!task.running?)
379
+ assert(!task.success?)
380
+ assert(!task.failed?)
381
+ assert(!task.finishing?)
382
+ assert(!task.finished?)
383
+
384
+ task.start!
385
+ assert(!task.pending?)
386
+ assert(task.starting?)
387
+ assert(!task.running?)
388
+ assert(!task.success?)
389
+ assert(!task.failed?)
390
+ assert(!task.finishing?)
391
+ assert(!task.finished?)
392
+
393
+ task.emit(:start)
394
+ assert(!task.pending?)
395
+ assert(!task.starting?)
396
+ assert(task.running?)
397
+ assert(!task.success?)
398
+ assert(!task.failed?)
399
+ assert(!task.finishing?)
400
+ assert(!task.finished?)
401
+
402
+ task.stop!
403
+ assert(!task.pending?)
404
+ assert(!task.starting?)
405
+ assert(task.running?)
406
+ assert(!task.success?)
407
+ assert(!task.failed?)
408
+ assert(task.finishing?)
409
+ assert(!task.finished?)
410
+
411
+ task.emit(:failed)
412
+ assert(!task.pending?)
413
+ assert(!task.starting?)
414
+ assert(!task.running?)
415
+ assert(!task.success?)
416
+ assert(task.failed?)
417
+ assert(!task.finishing?)
418
+ assert(task.finished?)
419
+ end
420
+
421
+ def test_context_propagation
422
+ FlexMock.use do |mock|
423
+ model = Class.new(SimpleTask) do
424
+ event :start do |context|
425
+ mock.starting(context)
426
+ event(:start).emit(*context)
427
+ end
428
+ on(:start) do |event|
429
+ mock.started(event.context)
430
+ end
431
+
432
+
433
+ event :pass_through, :command => true
434
+ on(:pass_through) do |event|
435
+ mock.pass_through(event.context)
436
+ end
437
+
438
+ on(:stop) { |event| mock.stopped(event.context) }
439
+ end
440
+ plan.insert(task = model.new)
441
+
442
+ mock.should_receive(:starting).with([42]).once
443
+ mock.should_receive(:started).with([42]).once
444
+ mock.should_receive(:pass_through).with([10]).once
445
+ mock.should_receive(:stopped).with([21]).once
446
+ task.start!(42)
447
+ task.pass_through!(10)
448
+ task.emit(:stop, 21)
449
+ assert(task.finished?)
450
+ end
451
+ end
452
+
453
+ def test_inheritance_overloading
454
+ base = Class.new(Roby::Task) do
455
+ extend Test::Unit::Assertions
456
+ event :ctrl, :command => true
457
+ event :stop
458
+ assert(!find_event_model(:stop).controlable?)
459
+ end
460
+
461
+ Class.new(base) do
462
+ extend Test::Unit::Assertions
463
+
464
+ assert_nothing_raised { event :start, :command => true }
465
+ assert_raises(ArgumentError) { event :ctrl, :command => false }
466
+ assert_raises(ArgumentError) { event :failed, :terminal => false }
467
+ assert_raises(ArgumentError) { event :failed }
468
+
469
+ assert_nothing_raised { event(:stop) { |context| } }
470
+ assert(find_event_model(:stop).controlable?)
471
+ end
472
+
473
+ Class.new(base) do
474
+ assert_nothing_raised { event(:start) { |context| } }
475
+ end
476
+ end
477
+
478
+ def test_singleton
479
+ model = Class.new(Task) do
480
+ def initialize
481
+ singleton_class.event(:start, :command => true)
482
+ singleton_class.event(:stop)
483
+ super
484
+ end
485
+ event :inter
486
+ end
487
+
488
+ ev_models = Hash[*model.enum_for(:each_event).to_a.flatten]
489
+ assert_equal([:start, :success, :aborted, :updated_data, :stop, :failed, :inter].to_set, ev_models.keys.to_set)
490
+
491
+ plan.discover(task = model.new)
492
+ ev_models = Hash[*task.model.enum_for(:each_event).to_a.flatten]
493
+ assert_equal([:start, :success, :aborted, :updated_data, :stop, :failed, :inter].to_set, ev_models.keys.to_set)
494
+ assert( ev_models[:start].symbol )
495
+ assert( ev_models[:start].name || ev_models[:start].name.length > 0 )
496
+ end
497
+
498
+ def test_check_running
499
+ model = Class.new(SimpleTask) do
500
+ event(:inter, :command => true)
501
+ end
502
+ plan.discover(task = model.new)
503
+
504
+ assert_raises(CommandFailed) { task.inter! }
505
+ assert_raises(EmissionFailed) { task.emit(:inter) }
506
+ assert(!task.event(:inter).pending)
507
+ task.start!
508
+ assert_raise(CommandFailed) { task.start! }
509
+ assert_nothing_raised { task.inter! }
510
+ task.stop!
511
+
512
+ assert_raises(EmissionFailed) { task.emit(:inter) }
513
+ assert_raises(CommandFailed) { task.inter! }
514
+ assert(!task.event(:inter).pending)
515
+
516
+ model = Class.new(SimpleTask) do
517
+ event :start do |context|
518
+ emit :inter
519
+ emit :start
520
+ end
521
+
522
+ event :inter do |context|
523
+ emit :inter
524
+ end
525
+ end
526
+ plan.discover(task = model.new)
527
+ assert_nothing_raised { task.start! }
528
+ end
529
+
530
+ def test_finished
531
+ model = Class.new(Roby::Task) do
532
+ event :start, :command => true
533
+ event :failed, :command => true, :terminal => true
534
+ event :success, :command => true, :terminal => true
535
+ event :stop, :command => true
536
+ end
537
+
538
+ plan.discover(task = model.new)
539
+ task.start!
540
+ task.emit(:stop)
541
+ assert(!task.success?)
542
+ assert(!task.failed?)
543
+ assert(task.finished?)
544
+ assert_equal(task.event(:stop).last, task.terminal_event)
545
+
546
+ plan.discover(task = model.new)
547
+ task.start!
548
+ task.emit(:success)
549
+ assert(task.success?)
550
+ assert(!task.failed?)
551
+ assert(task.finished?)
552
+ assert_equal(task.event(:success).last, task.terminal_event)
553
+
554
+ plan.discover(task = model.new)
555
+ task.start!
556
+ task.emit(:failed)
557
+ assert(!task.success?)
558
+ assert(task.failed?)
559
+ assert(task.finished?)
560
+ assert_equal(task.event(:failed).last, task.terminal_event)
561
+ end
562
+
563
+ def test_executable
564
+ model = Class.new(SimpleTask) do
565
+ event(:inter, :command => true)
566
+ end
567
+ task = model.new
568
+
569
+ assert(!task.executable?)
570
+ assert_raises(EventNotExecutable) { task.start! }
571
+ assert_raises(EventNotExecutable) { task.event(:start).call }
572
+
573
+ plan.discover(task)
574
+ assert(task.executable?)
575
+ assert_nothing_raised { task.event(:start).call(nil) }
576
+
577
+ # The task is running, cannot change the executable flag
578
+ assert_raises(ModelViolation) { task.executable = false }
579
+
580
+ task = SimpleTask.new
581
+ plan.insert(task)
582
+ assert(task.executable?)
583
+ task.executable = false
584
+ assert(!task.executable?)
585
+ task.executable = nil
586
+ assert(task.executable?)
587
+
588
+ task = SimpleTask.new
589
+ plan.permanent(task)
590
+ assert(task.executable?)
591
+ task.executable = false
592
+ assert(!task.executable?)
593
+ task.executable = nil
594
+ assert(task.executable?)
595
+ end
596
+
597
+ def test_task_success_failure
598
+ FlexMock.use do |mock|
599
+ plan.insert(t = EmptyTask.new)
600
+ [:start, :success, :stop].each do |name|
601
+ t.on(name) { mock.send(name) }
602
+ mock.should_receive(name).once.ordered
603
+ end
604
+ t.start!
605
+ end
606
+ end
607
+
608
+ def aggregator_test(a, *tasks)
609
+ plan.insert(a)
610
+ FlexMock.use do |mock|
611
+ [:start, :success, :stop].each do |name|
612
+ a.on(name) { mock.send(name) }
613
+ mock.should_receive(name).once.ordered
614
+ end
615
+ a.start!
616
+ assert( tasks.all? { |t| t.finished? })
617
+ end
618
+ end
619
+
620
+ def test_task_parallel_aggregator
621
+ t1, t2 = EmptyTask.new, EmptyTask.new
622
+ plan.discover([t1, t2])
623
+ aggregator_test((t1 | t2), t1, t2)
624
+ t1, t2 = EmptyTask.new, EmptyTask.new
625
+ plan.discover([t1, t2])
626
+ aggregator_test( (t1 | t2).to_task, t1, t2 )
627
+ end
628
+
629
+ def task_tuple(count)
630
+ tasks = (1..count).map do
631
+ t = EmptyTask.new
632
+ t.executable = true
633
+ t
634
+ end
635
+ yield(tasks)
636
+ end
637
+
638
+ def test_sequence
639
+ task_tuple(2) { |t1, t2| aggregator_test( (t1 + t2), t1, t2 ) }
640
+ task_tuple(2) do |t1, t2|
641
+ s = t1 + t2
642
+ aggregator_test( s.to_task, t1, t2 )
643
+ assert(! t1.event(:stop).related_object?(s.event(:stop)))
644
+ end
645
+
646
+ task_tuple(3) do |t1, t2, t3|
647
+ s = t2 + t3
648
+ s.unshift t1
649
+ aggregator_test(s, t1, t2, t3)
650
+ end
651
+
652
+ task_tuple(3) do |t1, t2, t3|
653
+ s = t2 + t3
654
+ s.unshift t1
655
+ aggregator_test(s.to_task, t1, t2, t3)
656
+ end
657
+ end
658
+ def test_sequence_to_task
659
+ model = Class.new(SimpleTask)
660
+ t1, t2 = prepare_plan :tasks => 2, :model => SimpleTask
661
+
662
+ seq = (t1 + t2)
663
+ assert(seq.child_object?(t1, TaskStructure::Hierarchy))
664
+ assert(seq.child_object?(t2, TaskStructure::Hierarchy))
665
+
666
+ task = seq.to_task(model)
667
+
668
+ plan.insert(task)
669
+
670
+ assert(!seq.child_object?(t1, TaskStructure::Hierarchy))
671
+ assert(!seq.child_object?(t2, TaskStructure::Hierarchy))
672
+
673
+ task.start!
674
+ assert(t1.running?)
675
+ t1.success!
676
+ assert(t2.running?)
677
+ t2.success!
678
+ assert(task.success?)
679
+ end
680
+
681
+ def test_compatible_state
682
+ t1, t2 = prepare_plan :discover => 2, :model => SimpleTask
683
+
684
+ assert(t1.compatible_state?(t2))
685
+ t1.start!; assert(! t1.compatible_state?(t2) && !t2.compatible_state?(t1))
686
+ t1.stop!; assert(t1.compatible_state?(t2) && t2.compatible_state?(t1))
687
+
688
+ plan.discover(t1 = SimpleTask.new)
689
+ t1.start!
690
+ t2.start!; assert(t1.compatible_state?(t2) && t2.compatible_state?(t1))
691
+ t1.stop!; assert(t1.compatible_state?(t2) && !t2.compatible_state?(t1))
692
+ end
693
+
694
+ def test_fullfills
695
+ abstract_task_model = TaskModelTag.new do
696
+ argument :abstract
697
+ end
698
+ task_model = Class.new(Task) do
699
+ include abstract_task_model
700
+ argument :index, :universe
701
+ end
702
+
703
+ t1, t2 = task_model.new, task_model.new
704
+ plan.discover([t1, t2])
705
+ assert(t1.fullfills?(t1.model))
706
+ assert(t1.fullfills?(t2))
707
+ assert(t1.fullfills?(abstract_task_model))
708
+
709
+ plan.discover(t2 = task_model.new(:index => 2))
710
+ assert(!t1.fullfills?(t2))
711
+
712
+ plan.discover(t3 = task_model.new(:universe => 42))
713
+ assert(t3.fullfills?(t1))
714
+ assert(!t1.fullfills?(t3))
715
+
716
+ plan.discover(t3 = Class.new(Task).new)
717
+ assert(!t1.fullfills?(t3))
718
+
719
+ plan.discover(t3 = Class.new(task_model).new)
720
+ assert(!t1.fullfills?(t3))
721
+ assert(t3.fullfills?(t1))
722
+ end
723
+
724
+ def test_related_tasks
725
+ t1, t2, t3 = (1..3).map { SimpleTask.new }.
726
+ each { |t| plan.discover(t) }
727
+ t1.realized_by t2
728
+ t1.event(:start).on t3.event(:start)
729
+ assert_equal([t3].to_value_set, t1.event(:start).related_tasks)
730
+ assert_equal([t2].to_value_set, t1.related_objects)
731
+ assert_equal([t2, t3].to_value_set, t1.related_tasks)
732
+ end
733
+
734
+ def test_related_events
735
+ t1, t2, t3 = (1..3).map { SimpleTask.new }.
736
+ each { |t| plan.discover(t) }
737
+ t1.realized_by t2
738
+ t1.event(:start).on t3.event(:start)
739
+ assert_equal([t3.event(:start)].to_value_set, t1.related_events)
740
+ end
741
+
742
+ def test_if_unreachable
743
+ model = Class.new(SimpleTask) do
744
+ event :ready
745
+ end
746
+
747
+ # Test that the stop event will make the handler called on a running task
748
+ FlexMock.use do |mock|
749
+ plan.discover(task = model.new)
750
+ ev = task.event(:success)
751
+ ev.if_unreachable(false) { mock.success_called }
752
+ ev.if_unreachable(true) { mock.success_cancel_called }
753
+ mock.should_receive(:success_called).once
754
+ mock.should_receive(:success_cancel_called).never
755
+ ev = task.event(:ready)
756
+ ev.if_unreachable(false) { mock.ready_called }
757
+ ev.if_unreachable(true) { mock.ready_cancel_called }
758
+ mock.should_receive(:ready_called).once
759
+ mock.should_receive(:ready_cancel_called).once
760
+
761
+ task.start!
762
+ task.success!
763
+ end
764
+ plan.garbage_collect
765
+
766
+ # Test that it works on pending tasks too
767
+ FlexMock.use do |mock|
768
+ plan.discover(task = model.new)
769
+ ev = task.event(:success)
770
+ ev.if_unreachable(false) { mock.success_called }
771
+ ev.if_unreachable(true) { mock.success_cancel_called }
772
+ mock.should_receive(:success_called).once
773
+ mock.should_receive(:success_cancel_called).once
774
+
775
+ ev = task.event(:ready)
776
+ ev.if_unreachable(false) { mock.ready_called }
777
+ ev.if_unreachable(true) { mock.ready_cancel_called }
778
+ mock.should_receive(:ready_called).once
779
+ mock.should_receive(:ready_cancel_called).once
780
+
781
+ plan.garbage_collect
782
+ end
783
+
784
+ end
785
+
786
+ def test_achieve_with
787
+ slave = SimpleTask.new
788
+ master = Class.new(Task) do
789
+ terminates
790
+ event :start do
791
+ event(:start).achieve_with slave
792
+ end
793
+ end.new
794
+ plan.discover([master, slave])
795
+
796
+ master.start!
797
+ assert(master.starting?)
798
+ assert(master.realized_by?(slave))
799
+ slave.start!
800
+ slave.success!
801
+ assert(master.started?)
802
+
803
+ slave = SimpleTask.new
804
+ master = Class.new(Task) do
805
+ event :start do
806
+ event(:start).achieve_with slave.event(:start)
807
+ end
808
+ end.new
809
+ plan.discover([master, slave])
810
+
811
+ master.start!
812
+ assert(master.starting?)
813
+ assert_raises(UnreachableEvent) { plan.remove_object(slave) }
814
+ end
815
+
816
+ def test_task_group
817
+ t1, t2 = SimpleTask.new, SimpleTask.new
818
+ plan.discover(g = Group.new(t1, t2))
819
+
820
+ g.start!
821
+ assert(t1.running?)
822
+ assert(t2.running?)
823
+
824
+ t1.success!
825
+ assert(g.running?)
826
+ t2.success!
827
+ assert(g.success?)
828
+ end
829
+
830
+ def test_task_poll
831
+ Roby.control.run :cycle => 0.1, :detach => true
832
+
833
+ FlexMock.use do |mock|
834
+ t = Class.new(SimpleTask) do
835
+ poll do
836
+ mock.polled(self)
837
+ end
838
+ end.new
839
+ mock.should_receive(:polled).at_least.once.with(t)
840
+
841
+ Roby.execute do
842
+ plan.permanent(t)
843
+ t.start!
844
+ end
845
+ sleep(1)
846
+ Roby.execute do
847
+ assert(t.running?, t.terminal_event.to_s)
848
+ t.stop!
849
+ end
850
+ end
851
+
852
+ FlexMock.use do |mock|
853
+ mock.should_receive(:polled).at_least.once
854
+ t = Class.new(SimpleTask) do
855
+ poll do
856
+ mock.polled(self)
857
+ raise ArgumentError
858
+ end
859
+ end.new
860
+
861
+ Roby.execute do
862
+ plan.permanent(t)
863
+ t.start!
864
+ end
865
+ sleep(1)
866
+ assert(t.failed?)
867
+ end
868
+ end
869
+
870
+ def test_event_task_sources
871
+ task = Class.new(SimpleTask) do
872
+ event :specialized_failure, :command => true
873
+ forward :specialized_failure => :failed
874
+ end.new
875
+ plan.discover(task)
876
+
877
+ task.start!
878
+ assert_equal([task.event(:start).last], task.event(:start).last.task_sources.to_a)
879
+
880
+ ev = EventGenerator.new(true)
881
+ ev.forward task.event(:specialized_failure)
882
+ ev.call
883
+ assert_equal([task.event(:specialized_failure).last], task.event(:failed).last.task_sources.to_a)
884
+ end
885
+
886
+ def test_virtual_task
887
+ start, success = EventGenerator.new(true), EventGenerator.new
888
+ assert_raises(ArgumentError) { VirtualTask.create(success, start) }
889
+
890
+ assert_kind_of(VirtualTask, task = VirtualTask.create(start, success))
891
+ plan.discover(task)
892
+ assert_equal(start, task.start_event)
893
+ assert_equal(success, task.success_event)
894
+ FlexMock.use do |mock|
895
+ start.on { mock.start_event }
896
+ task.event(:start).on { mock.start_task }
897
+ mock.should_receive(:start_event).once.ordered
898
+ mock.should_receive(:start_task).once.ordered
899
+ task.start!
900
+
901
+ success.emit
902
+ assert(task.success?)
903
+ end
904
+
905
+ start, success = EventGenerator.new(true), EventGenerator.new
906
+ plan.discover(task = VirtualTask.create(start, success))
907
+ task.start!
908
+ plan.remove_object(success)
909
+ assert(task.failed?)
910
+
911
+ start, success = EventGenerator.new(true), EventGenerator.new
912
+ plan.discover(success)
913
+ plan.discover(task = VirtualTask.create(start, success))
914
+ assert_nothing_raised { success.emit }
915
+ end
916
+
917
+ def test_dup
918
+ plan.discover(task = Roby::Test::SimpleTask.new)
919
+ task.start!
920
+
921
+ new = task.dup
922
+ assert_not_same(new.event(:stop), task.event(:stop))
923
+ assert_same(new, new.event(:stop).task)
924
+ assert(plan.include?(new))
925
+ assert_kind_of(Roby::TaskArguments, new.arguments)
926
+ assert_equal(task.arguments.to_hash, new.arguments.to_hash)
927
+ assert_same(plan, new.plan)
928
+ assert(new.event(:failed).child_object?(new.event(:stop), Roby::EventStructure::Forwarding))
929
+
930
+ task.stop!
931
+ assert(!task.running?)
932
+ assert(new.running?)
933
+
934
+ new.event(:stop).call
935
+ assert(new.finished?, new.history)
936
+ end
937
+ end
938
+