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
+ require 'test/unit'
2
+ require 'utilrb/time/to_hms'
3
+ require 'roby'
4
+ require 'utilrb/module/attr_predicate'
5
+
6
+ module Roby
7
+ module Test
8
+ include Roby
9
+ Unit = ::Test::Unit
10
+
11
+ BASE_PORT = 1245
12
+ DISCOVERY_SERVER = "druby://localhost:#{BASE_PORT}"
13
+ REMOTE_PORT = BASE_PORT + 1
14
+ LOCAL_PORT = BASE_PORT + 2
15
+ REMOTE_SERVER = "druby://localhost:#{BASE_PORT + 3}"
16
+ LOCAL_SERVER = "druby://localhost:#{BASE_PORT + 4}"
17
+
18
+
19
+ attr_reader :timings
20
+ class << self
21
+ attr_accessor :check_allocation_count
22
+ end
23
+
24
+ # The plan used by the tests
25
+ def plan; Roby.plan end
26
+
27
+ # Clear the plan and return it
28
+ def new_plan
29
+ Roby.plan.clear
30
+ plan
31
+ end
32
+
33
+ # a [collection, collection_backup] array of the collections saved
34
+ # by #original_collections
35
+ attr_reader :original_collections
36
+
37
+ # Saves the current state of +obj+. This state will be restored by
38
+ # #restore_collections. +obj+ must respond to #<< to add new elements
39
+ # (hashes do not work whild arrays or sets do)
40
+ def save_collection(obj)
41
+ original_collections << [obj, obj.dup]
42
+ end
43
+
44
+ # Restors the collections saved by #save_collection to their previous state
45
+ def restore_collections
46
+ original_collections.each do |col, backup|
47
+ col.clear
48
+ if col.kind_of?(Hash)
49
+ col.merge! backup
50
+ else
51
+ backup.each(&col.method(:<<))
52
+ end
53
+ end
54
+ end
55
+
56
+ def setup
57
+ @console_logger ||= false
58
+ if !defined? Roby::State
59
+ Roby.app.reset
60
+ end
61
+
62
+ @original_roby_logger_level = Roby.logger.level
63
+ @timings = { :start => Time.now }
64
+
65
+ @original_collections = []
66
+ Thread.abort_on_exception = true
67
+ @remote_processes = []
68
+
69
+ if Test.check_allocation_count
70
+ GC.start
71
+ GC.disable
72
+ end
73
+
74
+ unless DRb.primary_server
75
+ DRb.start_service 'druby://localhost:0'
76
+ end
77
+
78
+ if defined? Roby::Planning::Planner
79
+ Roby::Planning::Planner.last_id = 0
80
+ end
81
+
82
+ # Save and restore Control's global arrays
83
+ save_collection Roby::Control.event_processing
84
+ save_collection Roby::Control.structure_checks
85
+ save_collection Roby::Control.at_cycle_end_handlers
86
+ save_collection Roby::EventGenerator.event_gathering
87
+ Roby.control.abort_on_exception = true
88
+ Roby.control.abort_on_application_exception = true
89
+ Roby.control.abort_on_framework_exception = true
90
+
91
+ save_collection Roby::Propagation.event_ordering
92
+ save_collection Roby::Propagation.delayed_events
93
+
94
+ save_collection Roby.exception_handlers
95
+ timings[:setup] = Time.now
96
+ end
97
+
98
+ def teardown_plan
99
+ old_gc_roby_logger_level = Roby.logger.level
100
+ if debug_gc?
101
+ Roby.logger.level = Logger::DEBUG
102
+ end
103
+
104
+ if !Roby.control.running?
105
+ Roby.control.run :detach => true
106
+ end
107
+
108
+ Roby.control.quit
109
+ Roby.control.join
110
+ plan.clear
111
+
112
+ ensure
113
+ Roby.logger.level = old_gc_roby_logger_level
114
+ end
115
+
116
+ def teardown
117
+ timings[:quit] = Time.now
118
+ teardown_plan
119
+ timings[:teardown_plan] = Time.now
120
+
121
+ stop_remote_processes
122
+ DRb.stop_service if DRb.thread
123
+
124
+ restore_collections
125
+
126
+ # Clear all relation graphs in TaskStructure and EventStructure
127
+ spaces = []
128
+ if defined? Roby::TaskStructure
129
+ spaces << Roby::TaskStructure
130
+ end
131
+ if defined? Roby::EventStructure
132
+ spaces << Roby::EventStructure
133
+ end
134
+ spaces.each do |space|
135
+ space.relations.each do |rel|
136
+ vertices = rel.enum_for(:each_vertex).to_a
137
+ unless vertices.empty?
138
+ Roby.warn " the following vertices are still present in #{rel}: #{vertices.to_a}"
139
+ vertices.each { |v| v.clear_vertex }
140
+ end
141
+ end
142
+ end
143
+
144
+ Roby::TaskStructure::Hierarchy.interesting_events.clear
145
+ if defined? Roby::Control
146
+ Roby.control.abort_on_exception = false
147
+ Roby.control.abort_on_application_exception = false
148
+ Roby.control.abort_on_framework_exception = false
149
+ end
150
+
151
+ if defined? Roby::Log
152
+ Roby::Log.known_objects.clear
153
+ end
154
+
155
+ if Test.check_allocation_count
156
+ require 'utilrb/objectstats'
157
+ count = ObjectStats.count
158
+ GC.start
159
+ remains = ObjectStats.count
160
+ Roby.warn "#{count} -> #{remains} (#{count - remains})"
161
+ end
162
+ timings[:end] = Time.now
163
+
164
+ if display_timings?
165
+ begin
166
+ display_timings!
167
+ rescue
168
+ Roby.warn $!.full_message
169
+ end
170
+ end
171
+
172
+ rescue Exception => e
173
+ STDERR.puts "failed teardown: #{e.full_message}"
174
+
175
+ ensure
176
+ while Roby.control.running?
177
+ Roby.control.quit
178
+ Roby.control.join rescue nil
179
+ end
180
+ Roby.plan.clear
181
+
182
+ Roby.logger.level = @original_roby_logger_level
183
+ self.console_logger = false
184
+ end
185
+
186
+ # Process pending events
187
+ def process_events
188
+ Roby::Control.synchronize do
189
+ Roby.control.process_events
190
+ end
191
+ end
192
+
193
+ # The list of children started using #remote_process
194
+ attr_reader :remote_processes
195
+
196
+ # Creates a set of tasks and returns them. Each task is given an unique
197
+ # 'id' which allows to recognize it in a failed assertion.
198
+ #
199
+ # Known options are:
200
+ # missions:: how many mission to create [0]
201
+ # discover:: how many tasks should be discovered [0]
202
+ # tasks:: how many tasks to create outside the plan [0]
203
+ # model:: the task model [Roby::Task]
204
+ # plan:: the plan to apply on [plan]
205
+ #
206
+ # The return value is [missions, discovered, tasks]
207
+ # (t1, t2), (t3, t4, t5), (t6, t7) = prepare_plan :missions => 2,
208
+ # :discover => 3, :tasks => 2
209
+ #
210
+ # An empty set is omitted
211
+ # (t1, t2), (t6, t7) = prepare_plan :missions => 2, :tasks => 2
212
+ #
213
+ # If a set is a singleton, the only object of this singleton is returned
214
+ # t1, (t6, t7) = prepare_plan :missions => 1, :tasks => 2
215
+ #
216
+ def prepare_plan(options)
217
+ options = validate_options options,
218
+ :missions => 0, :discover => 0, :tasks => 0,
219
+ :permanent => 0,
220
+ :model => Roby::Task, :plan => plan
221
+
222
+ missions, permanent, discovered, tasks = [], [], [], []
223
+ (1..options[:missions]).each do |i|
224
+ options[:plan].insert(t = options[:model].new(:id => "mission-#{i}"))
225
+ missions << t
226
+ end
227
+ (1..options[:permanent]).each do |i|
228
+ options[:plan].permanent(t = options[:model].new(:id => "perm-#{i}"))
229
+ permanent << t
230
+ end
231
+ (1..options[:discover]).each do |i|
232
+ options[:plan].discover(t = options[:model].new(:id => "discover-#{i}"))
233
+ discovered << t
234
+ end
235
+ (1..options[:tasks]).each do |i|
236
+ tasks << options[:model].new(:id => "task-#{i}")
237
+ end
238
+
239
+ result = []
240
+ [missions, permanent, discovered, tasks].each do |set|
241
+ unless set.empty?
242
+ set = *set
243
+ result << set
244
+ end
245
+ end
246
+ if result.size == 1 then result.first
247
+ else result
248
+ end
249
+ end
250
+
251
+ # Start a new process and saves its PID in #remote_processes. If a block is
252
+ # given, it is called in the new child. #remote_process returns only after
253
+ # this block has returned.
254
+ def remote_process
255
+ start_r, start_w= IO.pipe
256
+ quit_r, quit_w = IO.pipe
257
+ remote_pid = fork do
258
+ start_r.close
259
+ yield
260
+ start_w.write('OK')
261
+ quit_r.read(2)
262
+ end
263
+ start_w.close
264
+ start_r.read(2)
265
+
266
+ remote_processes << [remote_pid, quit_w]
267
+ remote_pid
268
+
269
+ ensure
270
+ start_r.close
271
+ end
272
+
273
+ # Stop all the remote processes that have been started using #remote_process
274
+ def stop_remote_processes
275
+ remote_processes.reverse.each do |pid, quit_w|
276
+ begin
277
+ quit_w.write('OK')
278
+ rescue Errno::EPIPE
279
+ end
280
+ begin
281
+ Process.waitpid(pid)
282
+ rescue Errno::ECHILD
283
+ end
284
+ end
285
+ remote_processes.clear
286
+ end
287
+
288
+ # Exception raised in the block of assert_doesnt_timeout when the timeout
289
+ # is reached
290
+ class FailedTimeout < RuntimeError; end
291
+
292
+ def assert_original_error(klass, localized_error_type = LocalizedError)
293
+ old_level = Roby.logger.level
294
+ Roby.logger.level = Logger::FATAL
295
+ assert_nothing_raised do
296
+ begin
297
+ yield
298
+ rescue localized_error_type => e
299
+ assert_respond_to(e, :error)
300
+ assert_kind_of(klass, e.error)
301
+ end
302
+ end
303
+ ensure
304
+ Roby.logger.level = old_level
305
+ end
306
+
307
+ # Checks that the given block returns within +seconds+ seconds
308
+ def assert_doesnt_timeout(seconds, message = "watchdog #{seconds} failed")
309
+ watched_thread = Thread.current
310
+ watchdog = Thread.new do
311
+ sleep(seconds)
312
+ watched_thread.raise FailedTimeout
313
+ end
314
+
315
+ assert_block(message) do
316
+ begin
317
+ yield
318
+ true
319
+ rescue FailedTimeout
320
+ ensure
321
+ watchdog.kill
322
+ watchdog.join
323
+ end
324
+ end
325
+ end
326
+
327
+ def assert_marshallable(object)
328
+ begin
329
+ Marshal.dump(object)
330
+ true
331
+ rescue TypeError
332
+ end
333
+ end
334
+
335
+ # The console logger object. See #console_logger=
336
+ attr_reader :console_logger
337
+
338
+ attr_predicate :debug_gc?, true
339
+ attr_predicate :display_timings?, true
340
+ def display_timings!
341
+ timings = self.timings.sort_by { |_, t| t }
342
+ ref = timings[0].last
343
+
344
+ format, header, times = "", [], []
345
+ format << "%#{method_name.size}s"
346
+ header << method_name
347
+ times << ""
348
+ timings.each do |name, time|
349
+ name = name.to_s
350
+ time = "%.2f" % [time - ref]
351
+
352
+ col_size = [name.size, time.size].max
353
+ format << " % #{col_size}s"
354
+ header << name
355
+ times << time
356
+ end
357
+
358
+ puts
359
+ puts format % header
360
+ puts format % times
361
+ end
362
+
363
+ # Enable display of all plan events on the console
364
+ def console_logger=(value)
365
+ if value && !@console_logger
366
+ require 'roby/log/console'
367
+ @console_logger = Roby::Log::ConsoleLogger.new(STDERR)
368
+ Roby::Log.add_logger console_logger
369
+ elsif @console_logger
370
+ Roby::Log.remove_logger console_logger
371
+ @console_logger = nil
372
+ end
373
+ end
374
+
375
+ def wait_thread_stopped(thread)
376
+ while !thread.stop?
377
+ sleep(0.1)
378
+ raise "#{thread} died" unless thread.alive?
379
+ end
380
+ end
381
+
382
+ def display_event_structure(object, relation, indent = " ")
383
+ result = object.to_s
384
+ object.history.each do |event|
385
+ result << "#{indent}#{event.time.to_hms} #{event}"
386
+ end
387
+ children = object.child_objects(relation)
388
+ unless children.empty?
389
+ result << " ->\n" << indent
390
+ children.each do |child|
391
+ result << display_event_structure(child, relation, indent + " ")
392
+ end
393
+ end
394
+
395
+ result
396
+ end
397
+ end
398
+ end
399
+
@@ -0,0 +1,214 @@
1
+ require 'roby/test/common'
2
+ require 'roby/distributed'
3
+
4
+ module Roby
5
+ module Distributed
6
+ module Test
7
+ include ::Roby::Test
8
+ include ::Roby::Distributed
9
+
10
+ def setup
11
+ super
12
+
13
+ save_collection Distributed.new_neighbours_observers
14
+ @old_distributed_logger_level = Distributed.logger.level
15
+
16
+ timings[:setup] = Time.now
17
+
18
+ # Start the GC so that it does not kick in a test. On slow machines,
19
+ # it can trigger timeouts
20
+ GC.start
21
+ timings[:gc] = Time.now
22
+ end
23
+
24
+ def teardown
25
+ begin
26
+ if remote && remote.respond_to?(:cleanup)
27
+ remote.cleanup
28
+ end
29
+ rescue DRb::DRbConnError
30
+ end
31
+
32
+ super
33
+
34
+ unless Distributed.peers.empty?
35
+ Roby.warn " still referencing #{Distributed.peers.keys}"
36
+ Distributed.peers.clear
37
+ end
38
+
39
+ # This one is a nasty one ...
40
+ # The main plan is the only thing which remains. If we do not reset
41
+ # the cached drb_object, it will be kept in the next test and the forked
42
+ # child will therefore use it ... And it will fail
43
+ plan.instance_eval do
44
+ @__droby_remote_id__ = nil
45
+ @__droby_marshalled__ = nil
46
+ end
47
+
48
+ if Distributed.state
49
+ Distributed.state.quit
50
+ end
51
+
52
+ timings[:end] = Time.now
53
+
54
+ rescue Exception
55
+ STDERR.puts "failing teardown: #{$!.full_message}"
56
+ raise
57
+
58
+ ensure
59
+ Distributed.logger.level = @old_distributed_logger_level
60
+ end
61
+
62
+ module RemotePeerSupport
63
+ attr_accessor :testcase
64
+
65
+ def enable_communication
66
+ Roby::Distributed.state.synchronize do
67
+ local_peer.enable_rx
68
+ # make sure we wake up the communication thread
69
+ Roby::Distributed.state.finished_discovery.broadcast
70
+ end
71
+ end
72
+ def disable_communication
73
+ local_peer.disable_rx
74
+ end
75
+ def flush; local_peer.flush end
76
+ def process_events; Roby.control.process_events end
77
+ def local_peer
78
+ @local_peer ||= Distributed.peers.find { true }.last
79
+ end
80
+ def reset_local_peer; @local_peer = nil end
81
+ def send_local_peer(*args); local_peer.send(*args) end
82
+ def wait_one_cycle; Roby.control.wait_one_cycle end
83
+ def console_logger=(value); testcase.console_logger = value end
84
+ def log_level=(value); Roby.logger.level = value end
85
+ def cleanup
86
+ Roby.control.quit
87
+ Roby.control.join
88
+ end
89
+ end
90
+
91
+ # Start a central discovery service, a remote connectionspace and a local
92
+ # connection space. It yields the remote connection space *in the forked
93
+ # child* if a block is given.
94
+ def start_peers(detached_control = false)
95
+ DRb.stop_service
96
+ remote_process do
97
+ DRb.start_service DISCOVERY_SERVER, Rinda::TupleSpace.new
98
+ end
99
+
100
+ if detached_control && Roby.control.running?
101
+ begin
102
+ Roby.control.quit
103
+ Roby.control.join
104
+ rescue ControlQuitError
105
+ end
106
+ end
107
+
108
+ remote_process do
109
+ central_tuplespace = DRbObject.new_with_uri(DISCOVERY_SERVER)
110
+ cs = ConnectionSpace.new :ring_discovery => false,
111
+ :discovery_tuplespace => central_tuplespace, :name => "remote" do |remote|
112
+ getter = Class.new { def get; DRbObject.new(Distributed.state) end }.new
113
+ DRb.start_service REMOTE_SERVER, getter
114
+ end
115
+ cs.extend RemotePeerSupport
116
+ cs.testcase = self
117
+
118
+ def cs.start_control_thread
119
+ Control.event_processing << Distributed.state.method(:start_neighbour_discovery)
120
+ Roby.control.run :detach => true
121
+ end
122
+
123
+ Distributed.state = cs
124
+ yield(cs) if block_given?
125
+ end
126
+
127
+ DRb.start_service LOCAL_SERVER
128
+ @central_tuplespace = DRbObject.new_with_uri(DISCOVERY_SERVER)
129
+ @remote = DRbObject.new_with_uri(REMOTE_SERVER).get
130
+ @local = ConnectionSpace.new :ring_discovery => false,
131
+ :discovery_tuplespace => central_tuplespace, :name => 'local',
132
+ :plan => plan
133
+
134
+ Distributed.state = local
135
+
136
+ if detached_control
137
+ remote.start_control_thread
138
+ Control.event_processing << Distributed.state.method(:start_neighbour_discovery)
139
+ Roby.control.run :detach => true
140
+ end
141
+ end
142
+
143
+ def setup_connection
144
+ assert(remote_neighbour = local.neighbours.find { true })
145
+ Peer.initiate_connection(local, remote_neighbour) do |@remote_peer| end
146
+
147
+ while !remote_peer
148
+ process_events
149
+ end
150
+ assert(remote.send_local_peer(:connected?))
151
+ end
152
+
153
+ attr_reader :central_tuplespace, :remote, :remote_peer, :remote_plan, :local
154
+
155
+ # Establishes a peer to peer connection between two ConnectionSpace objects
156
+ def peer2peer(detached_control = false, &remote_init)
157
+ timings[:starting_peers] = Time.now
158
+ start_peers(detached_control, &remote_init)
159
+ setup_connection
160
+ timings[:started_peers] = Time.now
161
+ end
162
+
163
+ def process_events
164
+ if Roby.control.running?
165
+ remote.wait_one_cycle
166
+ Roby.control.wait_one_cycle
167
+ elsif remote_peer && !remote_peer.disconnected?
168
+ Roby::Control.synchronize do
169
+ remote.process_events
170
+ Roby.control.process_events
171
+ end
172
+ else
173
+ super
174
+ end
175
+ end
176
+
177
+ def remote_task(match)
178
+ set_permanent = match.delete(:permanent)
179
+
180
+ found = nil
181
+ remote_peer.find_tasks.with_arguments(match).each do |task|
182
+ assert(!found)
183
+ if set_permanent
184
+ plan.permanent(task)
185
+ end
186
+
187
+ found = if block_given? then yield(task)
188
+ else task
189
+ end
190
+ end
191
+ found
192
+ end
193
+ def subscribe_task(match)
194
+ remote_task(match) do |task|
195
+ remote_peer.subscribe(task)
196
+ task
197
+ end
198
+ end
199
+
200
+ def remote_server(&block)
201
+ DRb.stop_service
202
+ remote_process do
203
+ server = Class.new do
204
+ class_eval(&block)
205
+ end.new
206
+ DRb.start_service REMOTE_SERVER, server
207
+ end
208
+
209
+ DRb.start_service LOCAL_SERVER
210
+ DRbObject.new_with_uri(REMOTE_SERVER)
211
+ end
212
+ end
213
+ end
214
+ end