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,399 @@
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 'mockups/tasks'
6
+ require 'flexmock'
7
+ require 'utilrb/hash/slice'
8
+
9
+ class TC_Control < Test::Unit::TestCase
10
+ include Roby::Test
11
+
12
+ def test_add_framework_errors
13
+ # Shut up the logger in this test
14
+ Roby.logger.level = Logger::FATAL
15
+ exception = begin; raise RuntimeError
16
+ rescue; $!
17
+ end
18
+
19
+ Roby.control.abort_on_application_exception = false
20
+ assert_nothing_raised { Propagation.add_framework_error(exception, :exceptions) }
21
+
22
+ Roby.control.abort_on_application_exception = true
23
+ assert_raises(RuntimeError) { Propagation.add_framework_error(exception, :exceptions) }
24
+ end
25
+
26
+ def test_event_loop
27
+ plan.insert(start_node = EmptyTask.new)
28
+ next_event = [ start_node, :start ]
29
+ plan.insert(if_node = ChoiceTask.new)
30
+ start_node.on(:stop) { next_event = [if_node, :start] }
31
+ if_node.on(:stop) { }
32
+
33
+ Control.event_processing << lambda do
34
+ next unless next_event
35
+ task, event = *next_event
36
+ next_event = nil
37
+ task.event(event).call(nil)
38
+ end
39
+ process_events
40
+ assert(start_node.finished?)
41
+
42
+ process_events
43
+ assert(if_node.finished?)
44
+ end
45
+
46
+ def test_every
47
+ # Check that every(cycle_length) works fine
48
+ Roby.control.run :cycle => 0.1, :detach => true
49
+
50
+ samples = []
51
+ id = Control.every(0.1) do
52
+ samples << Roby.control.cycle_start
53
+ end
54
+ sleep(1)
55
+ Control.remove_periodic_handler(id)
56
+ size = samples.size
57
+ assert(size > 2, samples.map { |t| t.to_hms })
58
+
59
+ samples.each_cons(2) do |a, b|
60
+ assert_in_delta(0.1, b - a, 0.001)
61
+ end
62
+
63
+ # Check that no samples have been added after the 'remove_periodic_handler'
64
+ assert_equal(size, samples.size)
65
+ end
66
+
67
+ def test_once
68
+ FlexMock.use do |mock|
69
+ Control.once { mock.called }
70
+ mock.should_receive(:called).once
71
+ process_events
72
+ end
73
+ FlexMock.use do |mock|
74
+ Control.once { mock.called }
75
+ mock.should_receive(:called).once
76
+ process_events
77
+ process_events
78
+ end
79
+ end
80
+
81
+ def test_failing_once
82
+ Roby.logger.level = Logger::FATAL
83
+ Roby.control.abort_on_exception = true
84
+ Roby.control.run :detach => true
85
+
86
+ FlexMock.use do |mock|
87
+ Control.once { mock.called; raise }
88
+ mock.should_receive(:called).once
89
+
90
+ assert_raises(ControlQuitError) do
91
+ Roby.wait_one_cycle
92
+ Roby.control.join
93
+ end
94
+ end
95
+ end
96
+
97
+ class SpecificException < RuntimeError; end
98
+ def test_unhandled_event_exceptions
99
+ Roby.control.abort_on_exception = true
100
+
101
+ # Test that the event is not pending if the command raises
102
+ model = Class.new(SimpleTask) do
103
+ event :start do |context|
104
+ raise SpecificException, "bla"
105
+ end
106
+ end
107
+ plan.insert(t = model.new)
108
+
109
+ assert_original_error(SpecificException, CommandFailed) { t.start! }
110
+ assert(!t.event(:start).pending?)
111
+
112
+ # Check that the propagation is pruned if the command raises
113
+ t = nil
114
+ FlexMock.use do |mock|
115
+ t = Class.new(SimpleTask) do
116
+ event :start do |context|
117
+ mock.command_called
118
+ raise SpecificException, "bla"
119
+ emit :start
120
+ end
121
+ on(:start) { |ev| mock.handler_called }
122
+ end.new
123
+ plan.insert(t)
124
+
125
+ mock.should_receive(:command_called).once
126
+ mock.should_receive(:handler_called).never
127
+
128
+ Control.once { t.start!(nil) }
129
+ assert_original_error(SpecificException, CommandFailed) { process_events }
130
+ assert(!t.event(:start).pending)
131
+ end
132
+
133
+ # Check that the task has been garbage collected in the process
134
+ assert(! plan.include?(t))
135
+ end
136
+
137
+ def apply_structure_checking(&block)
138
+ Control.structure_checks.clear
139
+ Control.structure_checks << lambda(&block)
140
+ process_events
141
+ ensure
142
+ Control.structure_checks.clear
143
+ end
144
+
145
+ def test_structure_checking
146
+ Roby.logger.level = Logger::FATAL
147
+ Roby.control.abort_on_exception = false
148
+
149
+ # Check on a single task
150
+ plan.insert(t = SimpleTask.new)
151
+ apply_structure_checking { LocalizedError.new(t) }
152
+ assert(! plan.include?(t))
153
+
154
+ # Make sure that a task which has been repaired will not be killed
155
+ plan.insert(t = SimpleTask.new)
156
+ did_once = false
157
+ apply_structure_checking do
158
+ unless did_once
159
+ did_once = true
160
+ LocalizedError.new(t)
161
+ end
162
+ end
163
+ assert(plan.include?(t))
164
+
165
+ # Check that whole task trees are killed
166
+ t0, t1, t2, t3 = prepare_plan :discover => 4
167
+ t0.realized_by t2
168
+ t1.realized_by t2
169
+ t2.realized_by t3
170
+
171
+ plan.insert(t0)
172
+ plan.insert(t1)
173
+ FlexMock.use do |mock|
174
+ mock.should_receive(:checking).twice
175
+ apply_structure_checking do
176
+ mock.checking
177
+ LocalizedError.new(t2)
178
+ end
179
+ end
180
+ assert(!plan.include?(t0))
181
+ assert(!plan.include?(t1))
182
+ assert(!plan.include?(t2))
183
+ process_events
184
+ assert(!plan.include?(t3))
185
+
186
+ # Check that we can kill selectively by returning a hash
187
+ t0, t1, t2 = prepare_plan :discover => 3
188
+ t0.realized_by t2
189
+ t1.realized_by t2
190
+ plan.insert(t0)
191
+ plan.insert(t1)
192
+ apply_structure_checking { { LocalizedError.new(t2) => t0 } }
193
+ assert(!plan.include?(t0))
194
+ assert(plan.include?(t1))
195
+ assert(plan.include?(t2))
196
+ end
197
+
198
+ def test_at_cycle_end
199
+ # Shut up the logger in this test
200
+ Roby.logger.level = Logger::FATAL
201
+ Roby.control.abort_on_application_exception = false
202
+
203
+ FlexMock.use do |mock|
204
+ mock.should_receive(:before_error).at_least.once
205
+ mock.should_receive(:after_error).never
206
+ mock.should_receive(:called).at_least.once
207
+
208
+ Control.at_cycle_end do
209
+ mock.before_error
210
+ raise
211
+ mock.after_error
212
+ end
213
+
214
+ Control.at_cycle_end do
215
+ mock.called
216
+ unless Roby.control.quitting?
217
+ Roby.control.quit
218
+ end
219
+ end
220
+ Roby.control.run
221
+ end
222
+ end
223
+
224
+ def test_inside_outside_control
225
+ # First, no control thread
226
+ assert(Roby.inside_control?)
227
+ assert(Roby.outside_control?)
228
+
229
+ # Add a fake control thread
230
+ begin
231
+ Roby.control.thread = Thread.main
232
+ assert(Roby.inside_control?)
233
+ assert(!Roby.outside_control?)
234
+
235
+ t = Thread.new do
236
+ assert(!Roby.inside_control?)
237
+ assert(Roby.outside_control?)
238
+ end
239
+ t.value
240
+ ensure
241
+ Roby.control.thread = nil
242
+ end
243
+
244
+ # .. and test with the real one
245
+ Roby.control.run :detach => true
246
+ Roby.execute do
247
+ assert(Roby.inside_control?)
248
+ assert(!Roby.outside_control?)
249
+ end
250
+ assert(!Roby.inside_control?)
251
+ assert(Roby.outside_control?)
252
+ end
253
+
254
+ def test_execute
255
+ # Set a fake control thread
256
+ Roby.control.thread = Thread.main
257
+
258
+ FlexMock.use do |mock|
259
+ mock.should_receive(:thread_before).once.ordered
260
+ mock.should_receive(:main_before).once.ordered
261
+ mock.should_receive(:execute).once.ordered.with(Thread.current).and_return(42)
262
+ mock.should_receive(:main_after).once.ordered(:finish)
263
+ mock.should_receive(:thread_after).once.ordered(:finish)
264
+
265
+ returned_value = nil
266
+ t = Thread.new do
267
+ mock.thread_before
268
+ returned_value = Roby.execute do
269
+ mock.execute(Thread.current)
270
+ end
271
+ mock.thread_after
272
+ end
273
+
274
+ # Wait for the thread to block
275
+ while !t.stop?; sleep(0.1) end
276
+ mock.main_before
277
+ assert(t.alive?)
278
+ process_events
279
+ mock.main_after
280
+ t.join
281
+
282
+ assert_equal(42, returned_value)
283
+ end
284
+
285
+ ensure
286
+ Roby.control.thread = nil
287
+ end
288
+
289
+ def test_execute_error
290
+ assert(!Roby.control.thread)
291
+ # Set a fake control thread
292
+ Roby.control.thread = Thread.main
293
+ assert(!Roby.control.quitting?)
294
+
295
+ returned_value = nil
296
+ t = Thread.new do
297
+ returned_value = begin
298
+ Roby.execute do
299
+ raise ArgumentError
300
+ end
301
+ rescue ArgumentError => e
302
+ e
303
+ end
304
+ end
305
+
306
+ # Wait for the thread to block
307
+ while !t.stop?; sleep(0.1) end
308
+ process_events
309
+ t.join
310
+
311
+ assert_kind_of(ArgumentError, returned_value)
312
+ assert(!Roby.control.quitting?)
313
+
314
+ ensure
315
+ Roby.control.thread = nil
316
+ end
317
+
318
+ def test_wait_until
319
+ # Set a fake control thread
320
+ Roby.control.thread = Thread.main
321
+
322
+ plan.permanent(task = SimpleTask.new)
323
+ t = Thread.new do
324
+ Roby.wait_until(task.event(:start)) do
325
+ task.start!
326
+ end
327
+ end
328
+
329
+ while !t.stop?; sleep(0.1) end
330
+ process_events
331
+ assert_nothing_raised { t.value }
332
+
333
+ ensure
334
+ Roby.control.thread = nil
335
+ end
336
+
337
+ def test_wait_until_unreachable
338
+ # Set a fake control thread
339
+ Roby.control.thread = Thread.main
340
+
341
+ plan.permanent(task = SimpleTask.new)
342
+ t = Thread.new do
343
+ begin
344
+ Roby.wait_until(task.event(:success)) do
345
+ task.start!
346
+ task.stop!
347
+ end
348
+ rescue Exception => e
349
+ e
350
+ end
351
+ end
352
+
353
+ while !t.stop?; sleep(0.1) end
354
+ process_events
355
+
356
+ result = t.value
357
+ assert_kind_of(UnreachableEvent, result)
358
+ assert_equal(task.event(:success), result.generator)
359
+
360
+ ensure
361
+ Roby.control.thread = nil
362
+ end
363
+
364
+ class CaptureLastStats
365
+ attr_reader :last_stats
366
+ def splat?; true end
367
+ def cycle_end(time, stats)
368
+ @last_stats = stats
369
+ end
370
+ end
371
+
372
+ def test_stats
373
+ Roby.control.run :detach => true, :cycle => 0.1
374
+
375
+ capture = CaptureLastStats.new
376
+ Roby::Log.add_logger capture
377
+
378
+ time_events = [:real_start, :events, :structure_check, :exception_propagation, :exception_fatal, :garbage_collect, :application_errors, :ruby_gc, :sleep, :end]
379
+ 10.times do
380
+ Roby.control.wait_one_cycle
381
+ next unless capture.last_stats
382
+
383
+ Roby::Control.synchronize do
384
+ timepoints = capture.last_stats.slice(*time_events)
385
+ assert(timepoints.all? { |name, d| d > 0 })
386
+
387
+ sorted_by_time = timepoints.sort_by { |name, d| d }
388
+ sorted_by_name = timepoints.sort_by { |name, d| time_events.index(name) }
389
+ sorted_by_time.each_with_index do |(name, d), i|
390
+ assert(sorted_by_name[i][1] == d)
391
+ end
392
+ end
393
+ end
394
+
395
+ ensure
396
+ Roby::Log.remove_logger capture if capture
397
+ end
398
+ end
399
+
@@ -0,0 +1,894 @@
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
+ class TC_Event < Test::Unit::TestCase
8
+ include Roby::Test
9
+
10
+ def test_properties
11
+ event = EventGenerator.new
12
+ assert(! event.controlable?)
13
+
14
+ event = EventGenerator.new(true)
15
+ assert(event.controlable?)
16
+
17
+ # Check command & emission behavior for controlable events
18
+ FlexMock.use do |mock|
19
+ plan.discover(event = EventGenerator.new { |context| mock.call_handler(context); event.emit(*context) })
20
+ event.on { |event| mock.event_handler(event.context) }
21
+
22
+ assert(event.controlable?)
23
+ mock.should_receive(:call_handler).once.with([42])
24
+ mock.should_receive(:event_handler).once.with([42])
25
+ event.call(42)
26
+ end
27
+
28
+ # Check emission behavior for non-controlable events
29
+ FlexMock.use do |mock|
30
+ event = EventGenerator.new
31
+ plan.discover(event)
32
+ event.on { |event| mock.event(event.context) }
33
+ mock.should_receive(:event).once.with([42])
34
+ event.emit(42)
35
+ end
36
+ end
37
+
38
+ def test_executable
39
+ plan.discover(event = EventGenerator.new(true))
40
+ event.executable = false
41
+ assert_raises(EventNotExecutable) { event.call(nil) }
42
+ assert_raises(EventNotExecutable) { event.emit(nil) }
43
+
44
+ event.executable = true
45
+ assert_nothing_raised { event.call(nil) }
46
+ assert_nothing_raised { event.emit(nil) }
47
+
48
+ plan.discover(other = EventGenerator.new(true))
49
+ other.executable = false
50
+ event.on other
51
+ assert_raises(EventNotExecutable) { event.call(nil) }
52
+
53
+ event.remove_signal(other)
54
+ assert_nothing_raised { event.emit(nil) }
55
+ other.emit_on event
56
+ assert_raises(EventNotExecutable) { event.call(nil) }
57
+
58
+ event.remove_forwarding(other)
59
+ assert_nothing_raised { event.emit(nil) }
60
+ event.on { other.emit(nil) }
61
+ assert_original_error(EventNotExecutable, EventHandlerError) { event.call(nil) }
62
+ end
63
+
64
+ def test_emit_failed
65
+ event = EventGenerator.new
66
+ plan.discover(event)
67
+ assert_original_error(NilClass, EmissionFailed) { event.emit_failed }
68
+ assert_original_error(NilClass, EmissionFailed) { event.emit_failed("test") }
69
+
70
+ klass = Class.new(EmissionFailed)
71
+ assert_raises(klass) { event.emit_failed(klass) }
72
+ assert_raises(klass) { event.emit_failed(klass, "test") }
73
+ begin; event.emit_failed(klass, "test")
74
+ rescue klass => e
75
+ assert( e.message =~ /: test$/ )
76
+ end
77
+
78
+ exception = klass.new(nil, event)
79
+ assert_raises(klass) { event.emit_failed(exception, "test") }
80
+ begin; event.emit_failed(exception, "test")
81
+ rescue klass => e
82
+ assert_equal(event, e.failed_generator)
83
+ assert( e.message =~ /: test$/ )
84
+ end
85
+
86
+ event = EventGenerator.new { }
87
+ plan.discover(event)
88
+ event.call
89
+ assert(event.pending?)
90
+ assert_raises(EmissionFailed) { event.emit_failed }
91
+ assert(!event.pending?)
92
+ end
93
+
94
+ def test_propagation_id
95
+ e1, e2, e3 = (1..3).map { EventGenerator.new(true) }.
96
+ each { |e| plan.discover(e) }
97
+ e1.on e2
98
+ e1.emit(nil)
99
+ assert_equal(e1.last.propagation_id, e2.last.propagation_id)
100
+
101
+ e2.emit(nil)
102
+ assert(e1.last.propagation_id < e2.last.propagation_id)
103
+
104
+ e3.emit(nil)
105
+ assert(e1.last.propagation_id < e3.last.propagation_id)
106
+ assert(e2.last.propagation_id < e3.last.propagation_id)
107
+ end
108
+
109
+
110
+ def test_signal_relation
111
+ e1, e2 = EventGenerator.new(true), Roby::EventGenerator.new(true)
112
+ plan.discover([e1, e2])
113
+
114
+ e1.on e2
115
+ assert( e1.child_object?( e2, EventStructure::Signal ))
116
+ assert( e2.parent_object?( e1, EventStructure::Signal ))
117
+
118
+ e1.call(nil)
119
+ assert(e2.happened?)
120
+ end
121
+
122
+ def test_handlers
123
+ e1, e2 = EventGenerator.new(true), Roby::EventGenerator.new(true)
124
+ plan.discover([e1, e2])
125
+ e1.on { e2.call(nil) }
126
+
127
+ FlexMock.use do |mock|
128
+ e1.on { mock.e1 }
129
+ e2.on { mock.e2 }
130
+ e1.on { mock.happened?(e1.happened?) }
131
+ mock.should_receive(:happened?).once.with(true)
132
+ mock.should_receive(:e1).once.ordered
133
+ mock.should_receive(:e2).once.ordered
134
+ e1.call(nil)
135
+ end
136
+ end
137
+
138
+ def test_event_sources
139
+ events = (1..6).map { EventGenerator.new(true) }
140
+ forwarded, signalled,
141
+ emitted_in_handler, called_in_handler,
142
+ emitted_in_command, called_in_command = *events
143
+
144
+ src = EventGenerator.new(true)
145
+ e = EventGenerator.new do
146
+ called_in_command.call
147
+ emitted_in_command.emit
148
+ e.emit
149
+ end
150
+ events << e
151
+
152
+ plan.discover(events)
153
+
154
+ e.forward forwarded
155
+ e.on signalled
156
+ e.on do
157
+ called_in_handler.call
158
+ emitted_in_handler.emit
159
+ end
160
+
161
+ src.on e
162
+ src.call
163
+ assert_equal([e.last], forwarded.last.sources.to_a)
164
+ assert_equal([e.last], emitted_in_handler.last.sources.to_a)
165
+ assert_equal([], emitted_in_command.last.sources.to_a)
166
+ assert_equal([], signalled.last.sources.to_a)
167
+ assert_equal([], called_in_handler.last.sources.to_a)
168
+ assert_equal([], called_in_command.last.sources.to_a)
169
+ end
170
+
171
+ def test_simple_signal_handler_ordering
172
+ e1, e2, e3 = (1..3).map { EventGenerator.new(true) }.
173
+ each { |e| plan.discover(e) }
174
+ e1.on(e2)
175
+ e1.on { e2.remove_signal(e3) }
176
+ e2.on(e3)
177
+
178
+ e1.call(nil)
179
+ assert( e2.happened? )
180
+ assert( !e3.happened? )
181
+ end
182
+
183
+ def test_event_hooks
184
+ FlexMock.use do |mock|
185
+ hooks = [:calling, :called, :fired]
186
+ mod = Module.new do
187
+ hooks.each do |name|
188
+ define_method(name) do |context|
189
+ mock.send(name, self)
190
+ end
191
+ end
192
+ end
193
+
194
+ generator = Class.new(EventGenerator) do
195
+ include mod
196
+ end.new(true)
197
+ plan.discover(generator)
198
+
199
+ hooks.each do |name|
200
+ mock.should_receive(name).once.with(generator).ordered
201
+ end
202
+ generator.call(nil)
203
+ end
204
+ end
205
+
206
+ def test_postpone
207
+ wait_for = EventGenerator.new(true)
208
+ event = EventGenerator.new(true)
209
+ plan.discover([wait_for, event])
210
+ event.singleton_class.class_eval do
211
+ define_method(:calling) do |context|
212
+ super if defined? super
213
+ unless wait_for.happened?
214
+ postpone(wait_for, "bla") {}
215
+ end
216
+ end
217
+ end
218
+
219
+ event.call(nil)
220
+ assert(! event.happened?)
221
+ assert(! event.pending?)
222
+ assert(wait_for.child_object?(event, EventStructure::Signal))
223
+ wait_for.call(nil)
224
+ assert(event.happened?)
225
+
226
+ # Test propagation when the block given to postpone signals the event
227
+ # we are waiting for
228
+ FlexMock.use do |mock|
229
+ wait_for = EventGenerator.new(true)
230
+ event = EventGenerator.new(true)
231
+ plan.discover([wait_for, event])
232
+ event.singleton_class.class_eval do
233
+ define_method(:calling) do |context|
234
+ super if defined? super
235
+ if !wait_for.happened?
236
+ postpone(wait_for, "bla") do
237
+ wait_for.call(nil)
238
+ end
239
+ end
240
+ end
241
+ end
242
+
243
+ wait_for.on { mock.wait_for }
244
+ event.on { mock.event }
245
+
246
+ mock.should_receive(:wait_for).once.ordered
247
+ mock.should_receive(:event).once.ordered
248
+ event.call(nil)
249
+ end
250
+ end
251
+
252
+ def test_can_signal
253
+ a, b = EventGenerator.new(true), EventGenerator.new
254
+ plan.discover([a, b])
255
+ assert_raises(EventNotControlable) { a.on b }
256
+ assert_nothing_raised { a.forward b }
257
+
258
+ a, b = EventGenerator.new(true), EventGenerator.new(true)
259
+ plan.discover([a, b])
260
+ a.on b
261
+ def b.controlable?; false end
262
+
263
+ assert_raise(EmissionFailed) { a.call(nil) }
264
+ end
265
+
266
+ def test_emit_on
267
+ e1, e2 = (1..2).map { EventGenerator.new(true) }.
268
+ each { |e| plan.discover(e) }
269
+ e1.emit_on e2
270
+ FlexMock.use do |mock|
271
+ e1.on { mock.e1 }
272
+ e2.on { mock.e2 }
273
+ mock.should_receive(:e2).once.ordered
274
+ mock.should_receive(:e1).once.ordered
275
+ e2.call(nil)
276
+ end
277
+ end
278
+
279
+ def test_and_generator
280
+ and_event = AndGenerator.new
281
+ FlexMock.use do |mock|
282
+ and_event.on { mock.called }
283
+ mock.should_receive(:called).once
284
+
285
+ events = 5.enum_for(:times).map { EventGenerator.new(true) }
286
+ plan.discover(events)
287
+ events.each { |ev| and_event << ev }
288
+
289
+ events.each do |ev|
290
+ ev.call(nil)
291
+ if ev != events[-1]
292
+ assert(!and_event.happened?)
293
+ end
294
+ end
295
+
296
+ assert(and_event.happened?)
297
+
298
+ # Call the events again. The and generator should not emit.
299
+ # This is checked by the flexmock object
300
+ events.each do |ev|
301
+ ev.call(nil)
302
+ end
303
+ end
304
+
305
+ # Check the behavior of the & operator
306
+ e1, e2, e3, e4 = (1..4).map { EventGenerator.new(true) }.
307
+ each { |e| plan.discover(e) }
308
+ and_event = e1 & e2
309
+ and_and = and_event & e3
310
+ assert_equal([e1, e2].to_set, and_event.waiting.to_set)
311
+ and_and = e4 & and_event
312
+ assert_equal([e1, e2].to_set, and_event.waiting.to_set)
313
+
314
+ # Check dynamic behaviour
315
+ a, b, c, d = (1..4).map { EventGenerator.new(true) }.
316
+ each { |e| plan.discover(e) }
317
+ and1 = a & b
318
+ and2 = and1 & c
319
+ and2.on d
320
+ assert_equal([and1], a.enum_for(:each_signal).to_a)
321
+ assert_equal([and1], b.enum_for(:each_signal).to_a)
322
+ assert_equal([and2], and1.enum_for(:each_signal).to_a)
323
+ assert_equal([and2], c.enum_for(:each_signal).to_a)
324
+ assert_equal([d], and2.enum_for(:each_signal).to_a)
325
+
326
+ a.call(nil)
327
+ assert_equal([b], and1.waiting)
328
+ assert(! and1.happened?)
329
+
330
+ c.call(nil)
331
+ assert_equal([and1], and2.waiting)
332
+ assert(! and2.happened?)
333
+
334
+ b.call(nil)
335
+ assert(and1.happened?)
336
+ assert(and2.happened?)
337
+ assert_equal([], and1.waiting)
338
+ assert_equal([], and2.waiting)
339
+
340
+ assert(d.happened?)
341
+ end
342
+
343
+ def test_and_empty
344
+ plan.discover(and_event = AndGenerator.new)
345
+
346
+ assert(and_event.empty?)
347
+ and_event << EventGenerator.new(true)
348
+ assert(!and_event.empty?)
349
+
350
+ end
351
+
352
+ def test_and_unreachability
353
+ a, b = (1..2).map { EventGenerator.new(true) }.
354
+ each { |e| plan.discover(e) }
355
+
356
+ # Test unreachability
357
+ ## it is unreachable once emitted, but if_unreachable(true) blocks
358
+ ## must no be called
359
+ and_event = (a & b)
360
+ FlexMock.use do |mock|
361
+ and_event.if_unreachable(true) do
362
+ mock.unreachable
363
+ end
364
+ mock.should_receive(:unreachable).never
365
+ a.call
366
+ assert( !and_event.unreachable? )
367
+ b.call
368
+ assert( !and_event.unreachable? )
369
+ end
370
+
371
+ ## must be unreachable once one of the nonemitted source events are
372
+ and_event = (a & b)
373
+ a.call
374
+ a.unreachable!
375
+ assert(!and_event.unreachable?)
376
+ b.unreachable!
377
+ assert(and_event.unreachable?)
378
+ end
379
+
380
+ def test_and_reset
381
+ a, b = (1..2).map { EventGenerator.new(true) }.
382
+ each { |e| plan.discover(e) }
383
+ and_event = (a & b)
384
+ a.emit(nil)
385
+
386
+ and_event.reset
387
+ b.emit(nil)
388
+ assert(!and_event.happened?)
389
+ a.emit(nil)
390
+ assert(and_event.happened?)
391
+
392
+ and_event.reset
393
+ a.emit(nil)
394
+ b.emit(nil)
395
+ assert_equal(2, and_event.history.size)
396
+
397
+ and_event.on { and_event.reset }
398
+ and_event.reset
399
+ a.emit(nil)
400
+ b.emit(nil)
401
+ assert_equal(3, and_event.history.size)
402
+ a.emit(nil)
403
+ b.emit(nil)
404
+ assert_equal(4, and_event.history.size)
405
+ end
406
+
407
+ def setup_aggregation(mock)
408
+ e1, e2, m1, m2, m3 = 5.enum_for(:times).map { EventGenerator.new(true) }
409
+ plan.discover([e1, e2, m1, m2, m3])
410
+ e1.on e2
411
+ m1.on m2
412
+ m2.on m3
413
+
414
+ (e1 & e2 & m2).on { mock.and }
415
+ (e2 | m1).on { mock.or }
416
+ ((e2 & m1) | m2).on { mock.and_or }
417
+
418
+ ((e2 | m1) & m2).on { mock.or_and }
419
+ [e1, e2, m1, m2, m3]
420
+ end
421
+
422
+ def test_aggregator
423
+ FlexMock.use do |mock|
424
+ e1, e2, m1, *_ = setup_aggregation(mock)
425
+ e2.on m1
426
+ mock.should_receive(:or).once
427
+ mock.should_receive(:and).once
428
+ mock.should_receive(:and_or).once
429
+ mock.should_receive(:or_and).once
430
+ e1.call(nil)
431
+ end
432
+
433
+ FlexMock.use do |mock|
434
+ e1, *_ = setup_aggregation(mock)
435
+ mock.should_receive(:or).once
436
+ mock.should_receive(:and).never
437
+ mock.should_receive(:and_or).never
438
+ mock.should_receive(:or_and).never
439
+ e1.call(nil)
440
+ end
441
+
442
+ FlexMock.use do |mock|
443
+ _, _, m1 = setup_aggregation(mock)
444
+ mock.should_receive(:or).once
445
+ mock.should_receive(:and).never
446
+ mock.should_receive(:and_or).once
447
+ mock.should_receive(:or_and).once
448
+ m1.call(nil)
449
+ end
450
+ end
451
+
452
+ def test_or_generator
453
+ a, b, c = (1..3).map { EventGenerator.new(true) }.
454
+ each { |e| plan.discover(e) }
455
+
456
+ or_event = OrGenerator.new
457
+ assert(or_event.empty?)
458
+ or_event << a << b
459
+ assert(!or_event.empty?)
460
+
461
+ or_event = (a | b)
462
+ or_event.on c
463
+ assert( a.enum_for(:each_causal_link).find { |ev| ev == or_event } )
464
+ assert( or_event.enum_for(:each_causal_link).find { |ev| ev == c } )
465
+ a.call(nil)
466
+ assert(c.happened?)
467
+ assert( or_event.happened? )
468
+ end
469
+
470
+ def test_or_emission
471
+ plan.discover(or_event = OrGenerator.new)
472
+ events = (1..4).map { EventGenerator.new(true) }.
473
+ each { |e| or_event << e }
474
+
475
+ FlexMock.use do |mock|
476
+ or_event.on { mock.called }
477
+ mock.should_receive(:called).once
478
+ events.each_with_index do |ev, i|
479
+ ev.call(nil)
480
+ assert(ev.happened?)
481
+ end
482
+ end
483
+ end
484
+
485
+ def test_or_reset
486
+ plan.discover(or_event = OrGenerator.new)
487
+ events = (1..4).map { EventGenerator.new(true) }.
488
+ each { |e| or_event << e }
489
+
490
+ FlexMock.use do |mock|
491
+ events.each_with_index do |ev, i|
492
+ ev.call
493
+ assert_equal(i + 1, or_event.history.size)
494
+ or_event.reset
495
+ end
496
+ end
497
+ end
498
+
499
+ def test_or_unreachability
500
+ # Test unreachability properties
501
+ a, b = (1..3).map { EventGenerator.new(true) }.
502
+ each { |e| plan.discover(e) }
503
+ or_event = (a | b)
504
+
505
+ ## must be unreachable once emitted, but if_unreachable(true) blocks
506
+ ## must not be called
507
+ FlexMock.use do |mock|
508
+ or_event.if_unreachable(true) do
509
+ mock.unreachable
510
+ end
511
+ mock.should_receive(:unreachable).never
512
+
513
+ assert( !or_event.unreachable? )
514
+ a.call
515
+ assert( !or_event.unreachable? )
516
+ end
517
+
518
+ ## must be unreachable if all its source events are
519
+ or_event = (a | b)
520
+ a.unreachable!
521
+ assert(!or_event.unreachable?)
522
+ b.unreachable!
523
+ assert(or_event.unreachable?)
524
+ end
525
+
526
+
527
+ def test_until
528
+ source, sink, filter, limit = 4.enum_for(:times).map { EventGenerator.new(true) }
529
+ plan.discover [source, sink, filter, limit]
530
+
531
+ source.on(filter)
532
+ filter.until(limit).on(sink)
533
+
534
+ FlexMock.use do |mock|
535
+ sink.on { mock.passed }
536
+ mock.should_receive(:passed).once
537
+
538
+ source.call(nil)
539
+ limit.call(nil)
540
+ source.call(nil)
541
+ end
542
+ end
543
+
544
+ def test_event_creation
545
+ # Test for validation of the return value of #event
546
+ generator = Class.new(EventGenerator) do
547
+ def new(context); [Propagation.propagation_id, context] end
548
+ end.new(true)
549
+ plan.discover(generator)
550
+
551
+ assert_raises(EmissionFailed) { generator.emit(nil) }
552
+
553
+ generator = Class.new(EventGenerator) do
554
+ def new(context);
555
+ event_klass = Struct.new :propagation_id, :context, :generator, :sources
556
+ event_klass.new(Propagation.propagation_id, context, self)
557
+ end
558
+ end.new(true)
559
+ plan.discover(generator)
560
+ assert_nothing_raised { generator.call(nil) }
561
+ end
562
+
563
+ def test_context_propagation
564
+ FlexMock.use do |mock|
565
+ e1 = EventGenerator.new { |context| mock.e1_cmd(context); e1.emit(*context) }
566
+ e2 = EventGenerator.new { |context| mock.e2_cmd(context); e2.emit(*context) }
567
+ e1.on e2
568
+ e1.on { |event| mock.e1(event.context) }
569
+ e2.on { |event| mock.e2(event.context) }
570
+ plan.discover([e1, e2])
571
+
572
+ mock.should_receive(:e1_cmd).with([mock]).once
573
+ mock.should_receive(:e2_cmd).with([mock]).once
574
+ mock.should_receive(:e1).with([mock]).once
575
+ mock.should_receive(:e2).with([mock]).once
576
+ e1.call(mock)
577
+ end
578
+
579
+ FlexMock.use do |mock|
580
+ pass_through = EventGenerator.new(true)
581
+ e2 = EventGenerator.new { |context| mock.e2_cmd(context); e2.emit(*context) }
582
+ pass_through.on e2
583
+ pass_through.on { |event| mock.e1(event.context) }
584
+ e2.on { |event| mock.e2(event.context) }
585
+ plan.discover([pass_through, e2])
586
+
587
+ mock.should_receive(:e2_cmd).with([mock]).once
588
+ mock.should_receive(:e1).with([mock]).once
589
+ mock.should_receive(:e2).with([mock]).once
590
+ pass_through.call(mock)
591
+ end
592
+
593
+ FlexMock.use do |mock|
594
+ e1 = EventGenerator.new { |context| mock.e1_cmd(context); e1.emit(*context) }
595
+ e2 = EventGenerator.new { |context| mock.e2_cmd(context); e2.emit(*context) }
596
+ e1.on e2
597
+ e1.on { |event| mock.e1(event.context) }
598
+ e2.on { |event| mock.e2(event.context) }
599
+ plan.discover([e1, e2])
600
+
601
+ mock.should_receive(:e1_cmd).with(nil).once
602
+ mock.should_receive(:e2_cmd).with(nil).once
603
+ mock.should_receive(:e1).with(nil).once
604
+ mock.should_receive(:e2).with(nil).once
605
+ e1.call
606
+ end
607
+ end
608
+
609
+ def test_preconditions
610
+ plan.discover(e1 = EventGenerator.new(true))
611
+ e1.precondition("context must be non-nil") do |generator, context|
612
+ context
613
+ end
614
+
615
+ assert_raises(EventPreconditionFailed) { e1.call(nil) }
616
+ assert_nothing_raised { e1.call(true) }
617
+ end
618
+
619
+ def test_cancel
620
+ e1 = Class.new(EventGenerator) do
621
+ def calling(context)
622
+ cancel("testing cancel method")
623
+ end
624
+ end.new(true)
625
+ plan.discover(e1)
626
+ assert_raises(EventCanceled) { e1.call(nil) }
627
+ end
628
+
629
+ def test_related_events
630
+ e1, e2 = (1..2).map { EventGenerator.new(true) }.
631
+ each { |ev| plan.discover(ev) }
632
+
633
+ assert_equal([].to_value_set, e1.related_events)
634
+ e1.on e2
635
+ assert_equal([e2].to_value_set, e1.related_events)
636
+ assert_equal([e1].to_value_set, e2.related_events)
637
+ end
638
+
639
+ def test_related_tasks
640
+ e1, e2 = (1..2).map { EventGenerator.new(true) }.
641
+ each { |ev| plan.discover(ev) }
642
+ t1 = SimpleTask.new
643
+
644
+ assert_equal([].to_value_set, e1.related_tasks)
645
+ e1.on t1.event(:start)
646
+ assert_equal([t1].to_value_set, e1.related_tasks)
647
+ end
648
+
649
+ def test_command
650
+ FlexMock.use do |mock|
651
+ ev = EventGenerator.new do |context|
652
+ ev.emit(*context)
653
+ mock.called(*context)
654
+ end
655
+ plan.discover(ev)
656
+
657
+ mock.should_receive(:called).with(42).once
658
+ ev.call(42)
659
+
660
+ assert(ev.happened?)
661
+ assert_equal(1, ev.history.size, ev.history)
662
+ assert(!ev.pending?)
663
+ end
664
+ end
665
+
666
+ def test_set_command
667
+ FlexMock.use do |mock|
668
+ ev = EventGenerator.new
669
+ plan.discover(ev)
670
+ assert(!ev.controlable?)
671
+
672
+ ev.command = lambda { mock.first }
673
+ mock.should_receive(:first).once.ordered
674
+ assert(ev.controlable?)
675
+ ev.call(nil)
676
+
677
+ ev.command = lambda { mock.second }
678
+ mock.should_receive(:second).once.ordered
679
+ assert(ev.controlable?)
680
+ ev.call(nil)
681
+
682
+ ev.command = nil
683
+ assert(!ev.controlable?)
684
+ end
685
+ end
686
+
687
+ def test_once
688
+ ev1, ev2 = EventGenerator.new(true), EventGenerator.new(true)
689
+ plan.discover([ev1, ev2])
690
+
691
+
692
+ FlexMock.use do |mock|
693
+ ev1.once(ev2) do
694
+ mock.called_once
695
+ end
696
+
697
+ ev2.on { mock.called }
698
+
699
+ mock.should_receive(:called).once
700
+ mock.should_receive(:called_once).once
701
+
702
+ ev1.call
703
+ ev1.call
704
+ end
705
+ end
706
+
707
+ def test_forward_once
708
+ ev1, ev2 = EventGenerator.new(true), EventGenerator.new(true)
709
+ plan.discover([ev1, ev2])
710
+
711
+ FlexMock.use do |mock|
712
+ ev1.forward_once(ev2)
713
+ ev2.on { mock.called }
714
+
715
+ mock.should_receive(:called).once
716
+
717
+ ev1.call
718
+ ev1.call
719
+ end
720
+ end
721
+
722
+ def test_filter
723
+ ev1, ev_block, ev_value, ev_nil = (1..4).map { EventGenerator.new(true) }.
724
+ each { |e| plan.discover(e) }
725
+
726
+ FlexMock.use do |mock|
727
+ ev1.filter { |v| mock.filtering(v); v*2 }.on ev_block
728
+ ev_block.on { |ev| mock.block_filter(ev.context) }
729
+
730
+ ev1.filter(42).on ev_value
731
+ ev_value.on { |ev| mock.value_filter(ev.context) }
732
+
733
+ ev1.filter.on ev_nil
734
+ ev_nil.on { |ev| mock.nil_filter(ev.context) }
735
+
736
+ mock.should_receive(:filtering).with(21).once
737
+ mock.should_receive(:block_filter).with([ 42 ]).once
738
+ mock.should_receive(:value_filter).with([42]).once
739
+ mock.should_receive(:nil_filter).with(nil).once
740
+ ev1.call(21)
741
+ end
742
+ end
743
+
744
+ def test_gather_events
745
+ e1, e2 = (1..2).map { EventGenerator.new(true) }.
746
+ each { |e| plan.discover(e) }
747
+
748
+ collection = []
749
+
750
+ EventGenerator.gather_events(collection, [e2])
751
+ e1.call
752
+ assert_equal([], collection.map { |ev| ev.generator })
753
+ e2.emit(nil)
754
+ assert_equal([e2], collection.map { |ev| ev.generator })
755
+
756
+ collection.clear
757
+ EventGenerator.gather_events(collection, [e1])
758
+ e1.call
759
+ assert_equal([e1], collection.map { |ev| ev.generator })
760
+ e2.emit(nil)
761
+ assert_equal([e1, e2], collection.map { |ev| ev.generator })
762
+
763
+ # Check that the triggering events are cleared when the events are
764
+ # removed from the plan
765
+ collection.clear
766
+ plan.remove_object(e1)
767
+
768
+ EventGenerator.remove_event_gathering(collection)
769
+ end
770
+
771
+ def test_achieve_with
772
+ slave = EventGenerator.new
773
+ master = EventGenerator.new do
774
+ master.achieve_with slave
775
+ end
776
+ plan.discover([master, slave])
777
+
778
+ master.call
779
+ assert(!master.happened?)
780
+ slave.emit
781
+ assert(master.happened?)
782
+
783
+ # Test what happens if the slave fails
784
+ slave = EventGenerator.new
785
+ master = EventGenerator.new do
786
+ master.achieve_with slave
787
+ end
788
+ plan.discover([master, slave])
789
+
790
+ master.call
791
+ assert(!master.happened?)
792
+ assert_raises(UnreachableEvent) { plan.remove_object(slave) }
793
+
794
+ # Now test the filtering case (when a block is given)
795
+ slave = EventGenerator.new
796
+ master = EventGenerator.new do
797
+ master.achieve_with(slave) { [21, 42] }
798
+ end
799
+ plan.discover([master, slave])
800
+
801
+ master.call
802
+ slave.emit
803
+ assert(master.happened?)
804
+ assert_equal(nil, slave.history[0].context)
805
+ assert_equal([[21, 42]], master.history[0].context)
806
+ end
807
+
808
+ def test_if_unreachable
809
+ FlexMock.use do |mock|
810
+ plan.discover(ev = EventGenerator.new(true))
811
+ ev.if_unreachable(false) { mock.called }
812
+ ev.if_unreachable(true) { mock.canceled_called }
813
+ ev.call
814
+
815
+ mock.should_receive(:called).once
816
+ mock.should_receive(:canceled_called).never
817
+ plan.garbage_collect
818
+ end
819
+ end
820
+
821
+ def test_when_unreachable
822
+ plan.discover(ev = EventGenerator.new(true))
823
+ ev.when_unreachable.on { |ev| mock.unreachable_fired }
824
+ ev.call
825
+ plan.garbage_collect
826
+ assert(ev.happened?)
827
+ end
828
+
829
+ def test_or_if_unreachable
830
+ plan.discover(e1 = EventGenerator.new(true))
831
+ plan.discover(e2 = EventGenerator.new(true))
832
+ a = e1 | e2
833
+ FlexMock.use do |mock|
834
+ a.if_unreachable(false) { mock.called }
835
+ mock.should_receive(:called).never
836
+ plan.remove_object(e1)
837
+ end
838
+
839
+ FlexMock.use do |mock|
840
+ a.if_unreachable(false) { mock.called }
841
+ mock.should_receive(:called).once
842
+ plan.remove_object(e2)
843
+ end
844
+ end
845
+
846
+ def test_and_if_unreachable
847
+ FlexMock.use do |mock|
848
+ plan.discover(e1 = EventGenerator.new(true))
849
+ plan.discover(e2 = EventGenerator.new(true))
850
+ a = e1 & e2
851
+
852
+ a.if_unreachable(false) { mock.called }
853
+ e1.call
854
+
855
+ mock.should_receive(:called).once
856
+ plan.remove_object(e2)
857
+ end
858
+
859
+ FlexMock.use do |mock|
860
+ plan.discover(e1 = EventGenerator.new(true))
861
+ plan.discover(e2 = EventGenerator.new(true))
862
+ a = e1 & e2
863
+
864
+ a.if_unreachable(false) { mock.called }
865
+ e1.call
866
+
867
+ mock.should_receive(:called).never
868
+ plan.remove_object(e1)
869
+ end
870
+ end
871
+
872
+ def test_dup
873
+ plan.discover(e = EventGenerator.new(true))
874
+
875
+ e.call
876
+ new = e.dup
877
+ e.call
878
+ assert_equal(2, e.history.size)
879
+ assert_equal(1, new.history.size)
880
+ end
881
+
882
+ def test_event_after
883
+ plan.discover(e = EventGenerator.new(true))
884
+ e.call
885
+ sleep(0.5)
886
+ plan.discover(delayed = e.last.after(1))
887
+ delayed.poll
888
+ assert(!delayed.happened?)
889
+ sleep(0.5)
890
+ delayed.poll
891
+ assert(delayed.happened?)
892
+ end
893
+ end
894
+