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,282 @@
1
+ $LOAD_PATH.unshift File.expand_path('../..', File.dirname(__FILE__))
2
+ require 'roby/test/distributed'
3
+ require 'roby/test/tasks/simple_task'
4
+ require 'flexmock'
5
+
6
+ class TC_DistributedConnection < Test::Unit::TestCase
7
+ include Rinda
8
+ include Roby
9
+ include Distributed
10
+ include Roby::Distributed::Test
11
+
12
+ def assert_has_neighbour(&check)
13
+ Distributed.state.start_neighbour_discovery
14
+ Distributed.state.wait_discovery
15
+
16
+ assert(!Distributed.state.discovering?)
17
+ assert(1, Distributed.neighbours.size)
18
+ assert(Distributed.neighbours.find(&check), Distributed.neighbours.map { |n| [n.name, n.remote_id] }.to_s)
19
+ end
20
+
21
+ # Test neighbour discovery using a remote central tuplespace as neighbour list
22
+ def test_centralized_drb_discovery
23
+ DRb.stop_service
24
+
25
+ central_tuplespace = TupleSpace.new
26
+ DRb.start_service 'druby://localhost:1245', central_tuplespace
27
+
28
+ remote_pid = remote_process do
29
+ DRb.stop_service
30
+ DRb.start_service
31
+ central_tuplespace = DRbObject.new_with_uri('druby://localhost:1245')
32
+
33
+ Distributed.state = ConnectionSpace.new :ring_discovery => false,
34
+ :discovery_tuplespace => central_tuplespace
35
+ end
36
+ Distributed.state = ConnectionSpace.new :ring_discovery => false,
37
+ :discovery_tuplespace => central_tuplespace
38
+ assert_has_neighbour { |n| n.name == "#{Socket.gethostname}-#{remote_pid}" }
39
+ end
40
+
41
+ BROADCAST = (1..10).map { |i| "127.0.0.#{i}" }
42
+ # Test neighbour discovery using UDP for discovery
43
+ def test_ringserver_discovery
44
+ DRb.stop_service
45
+
46
+ remote_pid = remote_process do
47
+ DRb.start_service
48
+ Distributed.state = ConnectionSpace.new :period => 0.5, :ring_discovery => true, :ring_broadcast => BROADCAST
49
+ Distributed.publish :bind => '127.0.0.2'
50
+ end
51
+
52
+ DRb.start_service
53
+ Distributed.state = ConnectionSpace.new :period => 0.5, :ring_discovery => true, :ring_broadcast => BROADCAST
54
+ Distributed.publish :bind => '127.0.0.1'
55
+
56
+ assert_has_neighbour { |n| n.name == "#{Socket.gethostname}-#{remote_pid}" }
57
+ end
58
+
59
+ # Test establishing peer-to-peer connection between two ConnectionSpace objects
60
+ # Note that #peer2peer is the exact same process
61
+ def test_connect(standalone = true)
62
+ if standalone
63
+ start_peers(true)
64
+
65
+ notified = []
66
+ Distributed.on_neighbour do |n|
67
+ notified << n
68
+ end
69
+ end
70
+
71
+ assert(local.discovery_thread)
72
+
73
+ # Initiate the connection from +local+
74
+ remote_neighbour = Distributed.neighbours.find { true }
75
+ Roby.execute do
76
+ did_yield = nil
77
+ Peer.initiate_connection(local, remote_neighbour) do |did_yield|
78
+ end
79
+
80
+ # Wait for the remote peer to take into account the fact that we
81
+ # try connecting
82
+ Distributed.state.synchronize do
83
+ remote_id = remote_neighbour.remote_id
84
+ assert(Distributed.state.pending_connections[remote_id] ||
85
+ Distributed.state.peers[remote_id])
86
+ end
87
+
88
+ sleep(1)
89
+ Distributed.state.synchronize do
90
+ remote_id = remote_neighbour.remote_id
91
+ assert(@remote_peer = Distributed.state.peers[remote_id], Distributed.state.peers)
92
+ assert_equal(remote_peer, did_yield)
93
+ end
94
+ assert(remote_peer.connected?)
95
+ assert(remote.send_local_peer(:connected?))
96
+ assert(remote_peer.link_alive?)
97
+ assert(remote.send_local_peer(:link_alive?))
98
+
99
+ did_yield = nil
100
+ Peer.initiate_connection(local, remote_neighbour) do |did_yield|
101
+ end
102
+ assert_equal(remote_peer, did_yield)
103
+ assert_equal(remote_peer, Peer.connect(remote_neighbour))
104
+ end
105
+
106
+ Roby.control.wait_one_cycle
107
+ assert(remote_peer.task.running?)
108
+ #assert_raises(ArgumentError) { Peer.initiate_connection(local, remote_neighbour) }
109
+ assert(remote_peer.link_alive?)
110
+
111
+ remote_peer.synchro_point
112
+ assert(remote_peer.connected?)
113
+ assert(remote_peer.task.ready?)
114
+
115
+ assert_equal('remote', remote_peer.remote_name)
116
+ assert_equal('remote', remote_peer.local_server.remote_name)
117
+ assert_equal('local', remote_peer.local_server.local_name)
118
+
119
+ if standalone
120
+ assert_equal(1, notified.size)
121
+ assert_equal([remote_neighbour], notified)
122
+ end
123
+ end
124
+
125
+ def test_synchronous_connect
126
+ start_peers(true) do |remote|
127
+ def remote.connected?
128
+ local_peer.connected?
129
+ end
130
+ end
131
+
132
+ sleep(0.5)
133
+ assert(remote_neighbour = Distributed.neighbours.find { true })
134
+ assert(remote_peer = Peer.connect(remote_neighbour))
135
+
136
+ assert_kind_of(Distributed::Peer, remote_peer)
137
+ assert(remote.connected?)
138
+ assert(remote_peer.connected?)
139
+ end
140
+
141
+ def test_concurrent_connection
142
+ GC.disable
143
+ start_peers(true) do |remote|
144
+ class << remote
145
+ def find_neighbour
146
+ @neighbour = Roby::Distributed.neighbours.find { true }
147
+ end
148
+ def connect
149
+ Roby::Distributed::Peer.initiate_connection(Roby::Distributed.state, @neighbour) do
150
+ @callback_called ||= 0
151
+ @callback_called += 1
152
+ end
153
+ nil
154
+ end
155
+
156
+ attr_reader :callback_called
157
+ def peer_objects
158
+ peer_objects = ObjectSpace.enum_for(:each_object, Roby::Distributed::Peer).to_a
159
+ [Roby::Distributed.peers.keys.size, peer_objects.size]
160
+ end
161
+ end
162
+ end
163
+
164
+ sleep(0.5)
165
+ assert(remote.find_neighbour)
166
+ assert(remote_neighbour = Distributed.neighbours.find { true })
167
+
168
+ # We want to check that a concurrent connection creates only one Peer
169
+ # object. Still, we have to take into account that the remote peer is a
170
+ # fork of ourselves, and as such inherits the Peer objects this process
171
+ # has in its ObjectSpace (like the leftovers of other tests)
172
+ registered_peer_count, initial_remote_peer_count = remote.peer_objects
173
+ assert_equal(0, registered_peer_count)
174
+ registered_peer_count, initial_local_peer_count = Distributed.peers.keys.size,
175
+ ObjectSpace.enum_for(:each_object, Distributed::Peer).to_a.size
176
+ assert_equal(0, registered_peer_count)
177
+
178
+ remote.connect
179
+ remote.connect
180
+
181
+ callback_called = 0
182
+ 2.times do
183
+ Peer.initiate_connection(local, remote_neighbour) do
184
+ callback_called += 1
185
+ end
186
+ end
187
+ sleep(1)
188
+
189
+ assert_equal(2, callback_called)
190
+ assert_equal(2, remote.callback_called)
191
+ assert_equal([1, initial_remote_peer_count + 1], remote.peer_objects)
192
+ assert_equal(1, Distributed.peers.keys.size)
193
+ assert_equal(initial_local_peer_count + 1, ObjectSpace.enum_for(:each_object, Distributed::Peer).to_a.size)
194
+ ensure
195
+ GC.enable
196
+ end
197
+
198
+ # Test the normal disconnection process
199
+ def test_disconnect
200
+ peer2peer(true) do |remote|
201
+ def remote.peers_empty?; Distributed.peers.empty? end
202
+ end
203
+
204
+ Roby.control.wait_one_cycle
205
+ assert(remote_peer.task.ready?)
206
+
207
+ remote_peer.disconnect
208
+ assert(remote_peer.disconnecting?)
209
+ process_events
210
+ remote.process_events
211
+
212
+ assert(remote_peer.disconnected?)
213
+ remote.send_local_peer(:disconnected?)
214
+
215
+ assert(Distributed.peers.empty?)
216
+ assert(remote.peers_empty?)
217
+ remote.reset_local_peer
218
+
219
+ # Make sure that we can reconnect
220
+ test_connect(false)
221
+ end
222
+
223
+ # Tests that the remote peer disconnects if #demux raises DisconnectedError
224
+ def test_disconnect_on_error
225
+ Roby.logger.level = Logger::FATAL
226
+ peer2peer(true) do |remote|
227
+ class << remote
228
+ include Test::Unit::Assertions
229
+ def assert_demux_raises
230
+ peer = peers.find { true }[1]
231
+ peer.transmit(:whatever)
232
+ peer.synchro_point rescue nil
233
+ end
234
+ end
235
+ end
236
+
237
+ remote_peer.disconnect
238
+ remote.assert_demux_raises
239
+ assert(remote.send_local_peer(:disconnected?))
240
+
241
+ assert(remote_peer.disconnected?)
242
+ remote.reset_local_peer
243
+
244
+ # Make sure that we can reconnect
245
+ test_connect(false)
246
+ end
247
+
248
+ def test_socket_reconnect
249
+ peer2peer(true)
250
+ Distributed.state.synchronize do
251
+ remote_peer.socket.close
252
+ assert(!remote_peer.link_alive?)
253
+ end
254
+
255
+ sleep(1)
256
+ assert(!remote_peer.socket.closed?)
257
+ assert(remote_peer.connected?)
258
+ assert(remote_peer.link_alive?)
259
+ assert(remote.send_local_peer(:connected?))
260
+ assert(remote.send_local_peer(:link_alive?))
261
+ end
262
+
263
+ def test_remote_dies
264
+ peer2peer(true)
265
+ Process.kill('KILL', remote_processes[1][0])
266
+
267
+ sleep(1)
268
+ assert(remote_peer.disconnected?)
269
+ end
270
+
271
+
272
+ def test_abort_connection
273
+ peer2peer(true)
274
+ remote_peer.disconnected!
275
+
276
+ sleep(1)
277
+ assert(remote_peer.socket.closed?)
278
+ assert(!remote_peer.connected?)
279
+ assert(!remote.send_local_peer(:connected?))
280
+ end
281
+ end
282
+
@@ -0,0 +1,373 @@
1
+ $LOAD_PATH.unshift File.expand_path('../..', File.dirname(__FILE__))
2
+ require 'roby/test/distributed'
3
+ require 'roby/test/tasks/simple_task'
4
+ require 'flexmock'
5
+
6
+ class TC_DistributedExecution < Test::Unit::TestCase
7
+ include Roby::Distributed::Test
8
+ SimpleTask = Roby::Test::SimpleTask
9
+
10
+ def test_event_status
11
+ peer2peer(true) do |remote|
12
+ class << remote
13
+ attr_reader :controlable
14
+ attr_reader :contingent
15
+ def create
16
+ # Put the task to avoir having GC clearing the events
17
+ plan.insert(t = SimpleTask.new(:id => 'task'))
18
+ plan.discover(@controlable = Roby::EventGenerator.new(true))
19
+ plan.discover(@contingent = Roby::EventGenerator.new(false))
20
+ t.on(:start, controlable)
21
+ t.forward(:start, contingent)
22
+ nil
23
+ end
24
+ def fire
25
+ Roby.execute do
26
+ controlable.call(nil)
27
+ contingent.emit(nil)
28
+ end
29
+ nil
30
+ end
31
+ end
32
+ end
33
+
34
+ remote.create
35
+ task = subscribe_task(:id => 'task')
36
+ controlable = *task.event(:start).child_objects(EventStructure::Signal).to_a
37
+ contingent = *task.event(:start).child_objects(EventStructure::Forwarding).to_a
38
+
39
+ FlexMock.use do |mock|
40
+ controlable.on do
41
+ mock.fired_controlable(Roby::Propagation.gathering?)
42
+ end
43
+ contingent.on do
44
+ mock.fired_contingent(Roby::Propagation.gathering?)
45
+ end
46
+
47
+ mock.should_receive(:fired_controlable).with(true).once
48
+ mock.should_receive(:fired_contingent).with(true).once
49
+ remote.fire
50
+ remote_peer.synchro_point
51
+ end
52
+
53
+ assert(controlable.happened?)
54
+ assert(contingent.happened?)
55
+ end
56
+
57
+ def test_signal_establishment
58
+ peer2peer(true) do |remote|
59
+ Roby::Distributed.on_transaction do |trsc|
60
+ trsc.edit do
61
+ local_task = trsc.find_tasks.which_fullfills(Roby::Test::SimpleTask).to_a.first
62
+ t = trsc[SimpleTask.new(:id => 'remote_task')]
63
+ local_task.realized_by t
64
+ local_task.on :start, t, :start
65
+ nil
66
+ end
67
+ end
68
+ end
69
+
70
+ trsc = Roby::Distributed::Transaction.new(plan)
71
+ trsc.add_owner remote_peer
72
+ trsc.propose(remote_peer)
73
+
74
+ plan.insert(local_task = Roby::Test::SimpleTask.new)
75
+ trsc[local_task]
76
+ trsc.release
77
+ trsc.edit
78
+ trsc.commit_transaction
79
+
80
+ Roby.execute { local_task.start! }
81
+ Roby.wait_one_cycle
82
+ remote_peer.synchro_point
83
+ remote_task = subscribe_task(:id => 'remote_task')
84
+ assert(remote_task.running?)
85
+
86
+ Roby.execute do
87
+ plan.discard(local_task)
88
+ local_task.stop!
89
+ end
90
+ end
91
+
92
+ # This test that the event/plan modification order is kept on a remote host
93
+ def test_keeps_causality
94
+ peer2peer(true) do |remote|
95
+ class << remote
96
+ attr_reader :event
97
+ attr_reader :task
98
+ def create
99
+ # Put the task to avoir having GC clearing the events
100
+ plan.insert(@task = SimpleTask.new(:id => 'task'))
101
+ plan.discover(@event = Roby::EventGenerator.new(true))
102
+ event.on task.event(:start)
103
+ nil
104
+ end
105
+ def fire
106
+ Roby.execute do
107
+ event.on do
108
+ plan.discard(task)
109
+ task.event(:start).on task.event(:success)
110
+ end
111
+
112
+ event.call(nil)
113
+ end
114
+ nil
115
+ end
116
+ end
117
+ end
118
+
119
+ remote.create
120
+ task = subscribe_task(:id => 'task')
121
+ event = *task.event(:start).parent_objects(EventStructure::Signal).to_a
122
+
123
+ FlexMock.use do |mock|
124
+ event.on do
125
+ mock.fired
126
+ assert(plan.free_events.include?(event))
127
+ end
128
+
129
+ mock.should_receive(:fired).once
130
+ remote.fire
131
+ remote_peer.synchro_point
132
+ end
133
+
134
+ assert(event.happened?)
135
+ end
136
+
137
+ def test_task_status
138
+ Roby.control.abort_on_exception = false
139
+ peer2peer(true) do |remote|
140
+ class << remote
141
+ include Test::Unit::Assertions
142
+ attr_reader :task
143
+ def create_task
144
+ plan.clear
145
+ plan.insert(@task = SimpleTask.new(:id => 1))
146
+ end
147
+ def start_task; Roby::Control.once { task.start! }; nil end
148
+ def stop_task
149
+ assert(task.executable?)
150
+ Roby::Control.once do
151
+ plan.discard(task)
152
+ task.stop!
153
+ end
154
+ nil
155
+ end
156
+ end
157
+ end
158
+
159
+ remote.create_task
160
+ p_task = remote_task(:id => 1)
161
+ assert(!p_task.event(:start).happened?)
162
+ process_events
163
+ assert(!p_task.plan)
164
+
165
+ # Start the task *before* subscribing to test that #subscribe maps the
166
+ # task status
167
+ remote.start_task
168
+ process_events
169
+ p_task = subscribe_task(:id => 1)
170
+ assert(p_task.running?)
171
+ assert(p_task.event(:start).happened?)
172
+
173
+ # Stop the task to see if the fired event is propagated
174
+ remote.stop_task
175
+ process_events
176
+ assert(p_task.finished?)
177
+ assert(p_task.failed?)
178
+ assert(p_task.event(:stop).happened?)
179
+ assert(p_task.finished?)
180
+ end
181
+
182
+ def test_signalling
183
+ peer2peer(true) do |remote|
184
+ remote.plan.insert(task = SimpleTask.new(:id => 1))
185
+ remote.class.class_eval do
186
+ include Test::Unit::Assertions
187
+ define_method(:start_task) do
188
+ events = plan.free_events.to_a
189
+ assert_equal(2, events.size)
190
+ assert(sev = events.find { |ev| ev.controlable? })
191
+ assert(fev = events.find { |ev| !ev.controlable? })
192
+ assert(task.event(:start).child_object?(sev, Roby::EventStructure::Signal))
193
+ assert(task.event(:start).child_object?(fev, Roby::EventStructure::Forwarding))
194
+ Control.once { task.start! }
195
+ nil
196
+ end
197
+ end
198
+ end
199
+ p_task = subscribe_task(:id => 1)
200
+
201
+ FlexMock.use do |mock|
202
+ signalled_ev = EventGenerator.new do |context|
203
+ mock.signal_command
204
+ signalled_ev.emit(nil)
205
+ end
206
+ signalled_ev.on { |ev| mock.signal_emitted }
207
+ assert(signalled_ev.controlable?)
208
+
209
+ forwarded_ev = EventGenerator.new
210
+ forwarded_ev.on { |ev| mock.forward_emitted }
211
+ assert(!forwarded_ev.controlable?)
212
+
213
+ p_task.event(:start).on signalled_ev
214
+ p_task.event(:start).forward forwarded_ev
215
+
216
+ mock.should_receive(:signal_command).once.ordered('signal')
217
+ mock.should_receive(:signal_emitted).once.ordered('signal')
218
+ mock.should_receive(:forward_emitted).once
219
+ process_events
220
+
221
+ remote.start_task
222
+ process_events
223
+ assert(signalled_ev.happened?)
224
+ assert(forwarded_ev.happened?)
225
+ end
226
+ end
227
+
228
+ def test_event_handlers
229
+ peer2peer(true) do |remote|
230
+ remote.plan.insert(task = SimpleTask.new(:id => 1))
231
+ def remote.start(task)
232
+ task = local_peer.local_object(task)
233
+ Roby::Control.once { task.start! }
234
+ nil
235
+ end
236
+ end
237
+ FlexMock.use do |mock|
238
+ mock.should_receive(:started).once
239
+
240
+ task = subscribe_task(:id => 1)
241
+ task.on(:start) { mock.started }
242
+ remote.start(Distributed.format(task))
243
+ process_events
244
+
245
+ assert(task.running?)
246
+ end
247
+ end
248
+
249
+ # Test that we can 'forget' running tasks that was known to us because they
250
+ # were related to subscribed tasks
251
+ def test_forgetting
252
+ peer2peer(true) do |remote|
253
+ parent, child =
254
+ SimpleTask.new(:id => 'parent'),
255
+ SimpleTask.new(:id => 'child')
256
+ parent.realized_by child
257
+
258
+ remote.plan.insert(parent)
259
+ child.start!
260
+ remote.singleton_class.class_eval do
261
+ define_method(:remove_link) do
262
+ parent.remove_child(child)
263
+ end
264
+ end
265
+ end
266
+
267
+ parent = subscribe_task(:id => 'parent')
268
+ child = nil
269
+ assert(child = local.plan.known_tasks.find { |t| t.arguments[:id] == 'child' })
270
+ assert(!child.subscribed?)
271
+ assert(child.running?)
272
+ remote.remove_link
273
+ process_events
274
+ assert(!local.plan.known_tasks.find { |t| t.arguments[:id] == 'child' })
275
+ end
276
+
277
+ # Tests that running remote tasks are aborted and pending tasks GCed if the
278
+ # connection is killed
279
+ def test_disconnect_kills_tasks
280
+ peer2peer(true) do |remote|
281
+ remote.plan.insert(task = SimpleTask.new(:id => 'remote-1'))
282
+ def remote.start(task)
283
+ task = local_peer.local_object(task)
284
+ Roby.execute do
285
+ task.start!
286
+ end
287
+ nil
288
+ end
289
+ end
290
+
291
+ task = subscribe_task(:id => 'remote-1')
292
+ remote.start(Distributed.format(task))
293
+ process_events
294
+ assert(task.running?)
295
+ assert(task.child_object?(remote_peer.task, TaskStructure::ExecutionAgent))
296
+ assert(task.subscribed?)
297
+
298
+ Roby.execute do
299
+ remote_peer.disconnected!
300
+ end
301
+ assert(!task.subscribed?)
302
+ assert(remote_peer.task.finished?)
303
+ assert(remote_peer.task.event(:aborted).happened?)
304
+ assert(remote_peer.task.event(:stop).happened?)
305
+ assert(task.finished?)
306
+ end
307
+
308
+ # Checks that the code blocks are called only in owning controllers
309
+ class CodeBlocksOwnersMockup < Roby::Test::SimpleTask
310
+ attr_reader :command_called
311
+ event :start do
312
+ @command_called = true
313
+ emit :start
314
+ end
315
+
316
+ attr_reader :handler_called
317
+ on(:start) { @handler_called = true }
318
+
319
+ attr_reader :poller_called
320
+ poll { @poller_called = true }
321
+ end
322
+
323
+ def test_code_blocks_owners
324
+ peer2peer(true) do |remote|
325
+ remote.plan.insert(CodeBlocksOwnersMockup.new(:id => 'mockup'))
326
+
327
+ def remote.call
328
+ task = plan.find_tasks(CodeBlocksOwnersMockup).to_a.first
329
+ Roby.execute { task.start! }
330
+ end
331
+
332
+ def remote.blocks_called
333
+ task = plan.find_tasks(CodeBlocksOwnersMockup).to_a.first
334
+ task.command_called && task.poller_called && task.handler_called
335
+ end
336
+ end
337
+
338
+ mockup = subscribe_task(:id => 'mockup')
339
+ remote.call
340
+ remote_peer.synchro_point
341
+
342
+ assert(remote.blocks_called)
343
+ assert(!mockup.command_called)
344
+ assert(!mockup.poller_called)
345
+ assert(!mockup.handler_called)
346
+ end
347
+
348
+ # Checks that we get the update fine if +fired+ and +signalled+ are
349
+ # received in the same cycle
350
+ def test_joint_fired_signalled
351
+ peer2peer(true) do |remote|
352
+ remote.plan.insert(task = SimpleTask.new(:id => 'remote-1'))
353
+ Roby::Control.once { task.start! }
354
+ end
355
+
356
+ event_time = Time.now
357
+ remote = subscribe_task(:id => 'remote-1')
358
+ plan.insert(local = SimpleTask.new(:id => 'local'))
359
+ remote_peer.synchro_point
360
+
361
+ Roby.execute do
362
+ remote_peer.local_server.event_fired(remote.event(:success), 0, Time.now, [42])
363
+ remote_peer.local_server.event_add_propagation(true, remote.event(:success), local.event(:start), 0, event_time, [42])
364
+ end
365
+ process_events
366
+
367
+ assert(remote.finished?)
368
+ assert(remote.success?)
369
+ assert(local.started?)
370
+ assert_equal(1, remote.history.size, remote.history)
371
+ end
372
+ end
373
+