roby 0.7.3 → 0.8.0

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 (236) hide show
  1. data/History.txt +7 -5
  2. data/Manifest.txt +91 -16
  3. data/README.txt +24 -24
  4. data/Rakefile +92 -64
  5. data/app/config/app.yml +42 -43
  6. data/app/config/init.rb +26 -0
  7. data/benchmark/alloc_misc.rb +123 -0
  8. data/benchmark/discovery_latency.rb +67 -0
  9. data/benchmark/garbage_collection.rb +48 -0
  10. data/benchmark/genom.rb +31 -0
  11. data/benchmark/transactions.rb +62 -0
  12. data/bin/roby +1 -1
  13. data/bin/roby-log +16 -6
  14. data/doc/guide/.gitignore +2 -0
  15. data/doc/guide/config.yaml +34 -0
  16. data/doc/guide/ext/init.rb +14 -0
  17. data/doc/guide/ext/previous_next.rb +40 -0
  18. data/doc/guide/ext/rdoc_links.rb +33 -0
  19. data/doc/guide/index.rdoc +16 -0
  20. data/doc/guide/overview.rdoc +62 -0
  21. data/doc/guide/plan_modifications.rdoc +67 -0
  22. data/doc/guide/src/abstraction/achieve_with.page +8 -0
  23. data/doc/guide/src/abstraction/forwarding.page +8 -0
  24. data/doc/guide/src/abstraction/hierarchy.page +19 -0
  25. data/doc/guide/src/abstraction/index.page +28 -0
  26. data/doc/guide/src/abstraction/task_models.page +13 -0
  27. data/doc/guide/src/basics.template +6 -0
  28. data/doc/guide/src/basics/app.page +139 -0
  29. data/doc/guide/src/basics/code_examples.page +33 -0
  30. data/doc/guide/src/basics/dry.page +69 -0
  31. data/doc/guide/src/basics/errors.page +443 -0
  32. data/doc/guide/src/basics/events.page +179 -0
  33. data/doc/guide/src/basics/hierarchy.page +275 -0
  34. data/doc/guide/src/basics/index.page +11 -0
  35. data/doc/guide/src/basics/log_replay/goForward_1.png +0 -0
  36. data/doc/guide/src/basics/log_replay/goForward_2.png +0 -0
  37. data/doc/guide/src/basics/log_replay/goForward_3.png +0 -0
  38. data/doc/guide/src/basics/log_replay/goForward_4.png +0 -0
  39. data/doc/guide/src/basics/log_replay/goForward_5.png +0 -0
  40. data/doc/guide/src/basics/log_replay/hierarchy_error_1.png +0 -0
  41. data/doc/guide/src/basics/log_replay/hierarchy_error_2.png +0 -0
  42. data/doc/guide/src/basics/log_replay/hierarchy_error_3.png +0 -0
  43. data/doc/guide/src/basics/log_replay/plan_repair_1.png +0 -0
  44. data/doc/guide/src/basics/log_replay/plan_repair_2.png +0 -0
  45. data/doc/guide/src/basics/log_replay/plan_repair_3.png +0 -0
  46. data/doc/guide/src/basics/log_replay/plan_repair_4.png +0 -0
  47. data/doc/guide/src/basics/log_replay/roby_log_main_window.png +0 -0
  48. data/doc/guide/src/basics/log_replay/roby_log_relation_window.png +0 -0
  49. data/doc/guide/src/basics/log_replay/roby_replay_event_representation.png +0 -0
  50. data/doc/guide/src/basics/plan_objects.page +71 -0
  51. data/doc/guide/src/basics/relations_display.page +203 -0
  52. data/doc/guide/src/basics/roby_cycle_overview.png +0 -0
  53. data/doc/guide/src/basics/shell.page +102 -0
  54. data/doc/guide/src/basics/summary.page +32 -0
  55. data/doc/guide/src/basics/tasks.page +357 -0
  56. data/doc/guide/src/basics_shell_header.txt +16 -0
  57. data/doc/guide/src/cycle/cycle-overview.png +0 -0
  58. data/doc/guide/src/cycle/cycle-overview.svg +208 -0
  59. data/doc/guide/src/cycle/error_handling.page +168 -0
  60. data/doc/guide/src/cycle/error_instantaneous_repair.png +0 -0
  61. data/doc/guide/src/cycle/error_instantaneous_repair.svg +1224 -0
  62. data/doc/guide/src/cycle/garbage_collection.page +10 -0
  63. data/doc/guide/src/cycle/index.page +23 -0
  64. data/doc/guide/src/cycle/propagation.page +154 -0
  65. data/doc/guide/src/cycle/propagation_diamond.png +0 -0
  66. data/doc/guide/src/cycle/propagation_diamond.svg +1279 -0
  67. data/doc/guide/src/default.css +319 -0
  68. data/doc/guide/src/default.template +74 -0
  69. data/doc/guide/src/htmldoc.metainfo +20 -0
  70. data/doc/guide/src/htmldoc.virtual +18 -0
  71. data/doc/guide/src/images/bodybg.png +0 -0
  72. data/doc/guide/src/images/contbg.png +0 -0
  73. data/doc/guide/src/images/footerbg.png +0 -0
  74. data/doc/guide/src/images/gradient1.png +0 -0
  75. data/doc/guide/src/images/gradient2.png +0 -0
  76. data/doc/guide/src/index.page +7 -0
  77. data/doc/guide/src/introduction/index.page +29 -0
  78. data/doc/guide/src/introduction/install.page +133 -0
  79. data/doc/{papers.rdoc → guide/src/introduction/publications.page} +5 -2
  80. data/doc/{videos.rdoc → guide/src/introduction/videos.page} +4 -2
  81. data/doc/guide/src/plugins/fault_tolerance.page +44 -0
  82. data/doc/guide/src/plugins/index.page +11 -0
  83. data/doc/guide/src/plugins/subsystems.page +45 -0
  84. data/doc/guide/src/relations/dependency.page +89 -0
  85. data/doc/guide/src/relations/index.page +12 -0
  86. data/doc/misc/update_github +24 -0
  87. data/doc/tutorials/02-GoForward.rdoc +3 -3
  88. data/ext/graph/graph.cc +46 -0
  89. data/lib/roby.rb +57 -22
  90. data/lib/roby/app.rb +132 -112
  91. data/lib/roby/app/plugins/rake.rb +21 -0
  92. data/lib/roby/app/rake.rb +0 -7
  93. data/lib/roby/app/run.rb +1 -1
  94. data/lib/roby/app/scripts/distributed.rb +1 -2
  95. data/lib/roby/app/scripts/generate/bookmarks.rb +1 -1
  96. data/lib/roby/app/scripts/results.rb +2 -1
  97. data/lib/roby/app/scripts/run.rb +6 -2
  98. data/lib/roby/app/scripts/shell.rb +11 -11
  99. data/lib/roby/config.rb +1 -1
  100. data/lib/roby/decision_control.rb +62 -3
  101. data/lib/roby/distributed.rb +4 -0
  102. data/lib/roby/distributed/base.rb +8 -0
  103. data/lib/roby/distributed/communication.rb +12 -8
  104. data/lib/roby/distributed/connection_space.rb +61 -44
  105. data/lib/roby/distributed/distributed_object.rb +1 -1
  106. data/lib/roby/distributed/notifications.rb +22 -30
  107. data/lib/roby/distributed/peer.rb +13 -8
  108. data/lib/roby/distributed/proxy.rb +5 -5
  109. data/lib/roby/distributed/subscription.rb +4 -4
  110. data/lib/roby/distributed/transaction.rb +3 -3
  111. data/lib/roby/event.rb +176 -110
  112. data/lib/roby/exceptions.rb +12 -4
  113. data/lib/roby/execution_engine.rb +1604 -0
  114. data/lib/roby/external_process_task.rb +225 -0
  115. data/lib/roby/graph.rb +0 -6
  116. data/lib/roby/interface.rb +221 -137
  117. data/lib/roby/log/console.rb +5 -3
  118. data/lib/roby/log/data_stream.rb +94 -16
  119. data/lib/roby/log/dot.rb +8 -8
  120. data/lib/roby/log/event_stream.rb +13 -3
  121. data/lib/roby/log/file.rb +43 -18
  122. data/lib/roby/log/gui/basic_display_ui.rb +89 -0
  123. data/lib/roby/log/gui/chronicle_view_ui.rb +90 -0
  124. data/lib/roby/log/gui/data_displays.rb +4 -5
  125. data/lib/roby/log/gui/data_displays_ui.rb +146 -0
  126. data/lib/roby/log/gui/relations.rb +18 -18
  127. data/lib/roby/log/gui/relations_ui.rb +120 -0
  128. data/lib/roby/log/gui/relations_view_ui.rb +144 -0
  129. data/lib/roby/log/gui/replay.rb +41 -13
  130. data/lib/roby/log/gui/replay_controls.rb +3 -0
  131. data/lib/roby/log/gui/replay_controls.ui +133 -110
  132. data/lib/roby/log/gui/replay_controls_ui.rb +249 -0
  133. data/lib/roby/log/hooks.rb +19 -18
  134. data/lib/roby/log/logger.rb +7 -6
  135. data/lib/roby/log/notifications.rb +4 -4
  136. data/lib/roby/log/plan_rebuilder.rb +20 -22
  137. data/lib/roby/log/relations.rb +44 -16
  138. data/lib/roby/log/server.rb +1 -4
  139. data/lib/roby/log/timings.rb +88 -19
  140. data/lib/roby/plan-object.rb +135 -11
  141. data/lib/roby/plan.rb +408 -224
  142. data/lib/roby/planning/loops.rb +32 -25
  143. data/lib/roby/planning/model.rb +157 -51
  144. data/lib/roby/planning/task.rb +47 -20
  145. data/lib/roby/query.rb +128 -92
  146. data/lib/roby/relations.rb +254 -136
  147. data/lib/roby/relations/conflicts.rb +6 -9
  148. data/lib/roby/relations/dependency.rb +358 -0
  149. data/lib/roby/relations/ensured.rb +0 -1
  150. data/lib/roby/relations/error_handling.rb +0 -1
  151. data/lib/roby/relations/events.rb +0 -2
  152. data/lib/roby/relations/executed_by.rb +26 -11
  153. data/lib/roby/relations/planned_by.rb +14 -14
  154. data/lib/roby/robot.rb +46 -0
  155. data/lib/roby/schedulers/basic.rb +34 -0
  156. data/lib/roby/standalone.rb +4 -0
  157. data/lib/roby/standard_errors.rb +21 -15
  158. data/lib/roby/state/events.rb +5 -4
  159. data/lib/roby/support.rb +107 -6
  160. data/lib/roby/task-operations.rb +23 -19
  161. data/lib/roby/task.rb +522 -148
  162. data/lib/roby/task_index.rb +80 -0
  163. data/lib/roby/test/common.rb +283 -44
  164. data/lib/roby/test/distributed.rb +53 -37
  165. data/lib/roby/test/testcase.rb +9 -204
  166. data/lib/roby/test/tools.rb +3 -3
  167. data/lib/roby/transactions.rb +154 -111
  168. data/lib/roby/transactions/proxy.rb +40 -7
  169. data/manifest.xml +20 -0
  170. data/plugins/fault_injection/README.txt +0 -3
  171. data/plugins/fault_injection/Rakefile +2 -8
  172. data/plugins/fault_injection/app.rb +1 -1
  173. data/plugins/fault_injection/fault_injection.rb +3 -3
  174. data/plugins/fault_injection/test/test_fault_injection.rb +19 -25
  175. data/plugins/subsystems/README.txt +0 -3
  176. data/plugins/subsystems/Rakefile +2 -7
  177. data/plugins/subsystems/app.rb +27 -16
  178. data/plugins/subsystems/test/app/config/init.rb +3 -0
  179. data/plugins/subsystems/test/app/planners/main.rb +1 -1
  180. data/plugins/subsystems/test/app/tasks/services.rb +1 -1
  181. data/plugins/subsystems/test/test_subsystems.rb +23 -16
  182. data/test/distributed/test_communication.rb +32 -15
  183. data/test/distributed/test_connection.rb +28 -26
  184. data/test/distributed/test_execution.rb +59 -54
  185. data/test/distributed/test_mixed_plan.rb +34 -34
  186. data/test/distributed/test_plan_notifications.rb +26 -26
  187. data/test/distributed/test_protocol.rb +57 -48
  188. data/test/distributed/test_query.rb +11 -7
  189. data/test/distributed/test_remote_plan.rb +71 -71
  190. data/test/distributed/test_transaction.rb +50 -47
  191. data/test/mockups/external_process +28 -0
  192. data/test/planning/test_loops.rb +163 -119
  193. data/test/planning/test_model.rb +3 -3
  194. data/test/planning/test_task.rb +27 -7
  195. data/test/relations/test_conflicts.rb +3 -3
  196. data/test/relations/test_dependency.rb +324 -0
  197. data/test/relations/test_ensured.rb +2 -2
  198. data/test/relations/test_executed_by.rb +94 -19
  199. data/test/relations/test_planned_by.rb +11 -9
  200. data/test/suite_core.rb +6 -3
  201. data/test/suite_distributed.rb +1 -0
  202. data/test/suite_planning.rb +1 -0
  203. data/test/suite_relations.rb +2 -2
  204. data/test/tasks/test_external_process.rb +126 -0
  205. data/test/{test_thread_task.rb → tasks/test_thread_task.rb} +17 -20
  206. data/test/test_bgl.rb +21 -1
  207. data/test/test_event.rb +229 -155
  208. data/test/test_exceptions.rb +79 -80
  209. data/test/test_execution_engine.rb +987 -0
  210. data/test/test_gui.rb +1 -1
  211. data/test/test_interface.rb +11 -5
  212. data/test/test_log.rb +18 -7
  213. data/test/test_log_server.rb +1 -0
  214. data/test/test_plan.rb +229 -395
  215. data/test/test_query.rb +193 -35
  216. data/test/test_relations.rb +88 -8
  217. data/test/test_state.rb +55 -37
  218. data/test/test_support.rb +1 -1
  219. data/test/test_task.rb +371 -218
  220. data/test/test_testcase.rb +32 -16
  221. data/test/test_transactions.rb +211 -170
  222. data/test/test_transactions_proxy.rb +37 -19
  223. metadata +169 -71
  224. data/.gitignore +0 -29
  225. data/doc/styles/allison.css +0 -314
  226. data/doc/styles/allison.js +0 -316
  227. data/doc/styles/allison.rb +0 -276
  228. data/doc/styles/jamis.rb +0 -593
  229. data/lib/roby/control.rb +0 -746
  230. data/lib/roby/executives/simple.rb +0 -30
  231. data/lib/roby/propagation.rb +0 -562
  232. data/lib/roby/relations/hierarchy.rb +0 -239
  233. data/lib/roby/transactions/updates.rb +0 -139
  234. data/test/relations/test_hierarchy.rb +0 -158
  235. data/test/test_control.rb +0 -399
  236. data/test/test_propagation.rb +0 -210
@@ -0,0 +1,80 @@
1
+ module Roby
2
+ # TaskIndex objects are used to maintain a set of tasks as classified sets,
3
+ # speeding up query operations. See Plan#task_index.
4
+ class TaskIndex
5
+ # A model => ValueSet map of the tasks for each model
6
+ attr_reader :by_model
7
+ # A state => ValueSet map of tasks given their state. The state is
8
+ # a symbol in [:pending, :starting, :running, :finishing,
9
+ # :finished]
10
+ attr_reader :by_state
11
+ # A peer => ValueSet map of tasks given their owner.
12
+ attr_reader :by_owner
13
+ # The set of tasks which have an event which is being repaired
14
+ attr_reader :repaired_tasks
15
+
16
+ STATE_PREDICATES = [:pending?, :running?, :finished?, :success?, :failed?].to_value_set
17
+
18
+ def initialize
19
+ @by_model = Hash.new { |h, k| h[k] = ValueSet.new }
20
+ @by_state = Hash.new
21
+ STATE_PREDICATES.each do |state_name|
22
+ by_state[state_name] = ValueSet.new
23
+ end
24
+ @by_owner = Hash.new
25
+ @task_state = Hash.new
26
+ @repaired_tasks = ValueSet.new
27
+ end
28
+
29
+ # Add a new task to this index
30
+ def add(task)
31
+ for klass in task.model.ancestors
32
+ by_model[klass] << task
33
+ end
34
+ by_state[:pending?] << task
35
+ for owner in task.owners
36
+ add_owner(task, owner)
37
+ end
38
+ end
39
+
40
+ # Updates the index to reflect that +new_owner+ now owns +task+
41
+ def add_owner(task, new_owner)
42
+ (by_owner[new_owner] ||= ValueSet.new) << task
43
+ end
44
+
45
+ # Updates the index to reflect that +peer+ no more owns +task+
46
+ def remove_owner(task, peer)
47
+ if set = by_owner[peer]
48
+ set.delete(task)
49
+ if set.empty?
50
+ by_owner.delete(peer)
51
+ end
52
+ end
53
+ end
54
+
55
+ # Updates the index to reflect a change of state for +task+
56
+ def set_state(task, new_state)
57
+ for state_set in by_state
58
+ state_set.last.delete(task)
59
+ end
60
+ by_state[new_state] << task
61
+ if new_state == :success? || new_state == :failed?
62
+ by_state[:finished?] << task
63
+ end
64
+ end
65
+
66
+ # Remove all references of +task+ from the index.
67
+ def remove(task)
68
+ for klass in task.model.ancestors
69
+ by_model[klass].delete(task)
70
+ end
71
+ for state_set in by_state
72
+ state_set.last.delete(task)
73
+ end
74
+ for owner in task.owners
75
+ remove_owner(task, owner)
76
+ end
77
+ end
78
+ end
79
+ end
80
+
@@ -22,14 +22,24 @@ module Roby
22
22
  end
23
23
 
24
24
  # The plan used by the tests
25
- def plan; Roby.plan end
25
+ attr_reader :plan
26
+ # The decision control component used by the tests
27
+ attr_reader :control
28
+ def engine; plan.engine end
26
29
 
27
30
  # Clear the plan and return it
28
31
  def new_plan
29
- Roby.plan.clear
32
+ plan.clear
30
33
  plan
31
34
  end
32
35
 
36
+ def deprecated_feature
37
+ Roby.enable_deprecation_warnings = false
38
+ yield
39
+ ensure
40
+ Roby.enable_deprecation_warnings = true
41
+ end
42
+
33
43
  # a [collection, collection_backup] array of the collections saved
34
44
  # by #original_collections
35
45
  attr_reader :original_collections
@@ -54,18 +64,25 @@ module Roby
54
64
  end
55
65
 
56
66
  def setup
67
+ super if defined? super
68
+
57
69
  @console_logger ||= false
70
+ @event_logger ||= false
58
71
  if !defined? Roby::State
59
- Roby.app.reset
72
+ Roby.const_set(:State, StateSpace.new)
73
+ else
74
+ Roby::State.clear
60
75
  end
61
76
 
62
77
  @original_roby_logger_level = Roby.logger.level
63
78
  @timings = { :start => Time.now }
64
79
 
65
80
  @original_collections = []
66
- Thread.abort_on_exception = true
81
+ Thread.abort_on_exception = false
67
82
  @remote_processes = []
68
83
 
84
+ Roby.app.setup_loggers
85
+
69
86
  if Test.check_allocation_count
70
87
  GC.start
71
88
  GC.disable
@@ -79,34 +96,48 @@ module Roby
79
96
  Roby::Planning::Planner.last_id = 0
80
97
  end
81
98
 
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
99
+ @plan ||= Plan.new
100
+ @control ||= DecisionControl.new
101
+ if !plan.engine
102
+ ExecutionEngine.new(@plan, @control)
103
+ end
104
+ plan.engine.gc_warning = false
90
105
 
91
- save_collection Roby::Propagation.event_ordering
92
- save_collection Roby::Propagation.delayed_events
106
+ # Save and restore some arrays
107
+ save_collection engine.propagation_handlers
108
+ save_collection Roby::ExecutionEngine.propagation_handlers
109
+ save_collection plan.structure_checks
110
+ save_collection Roby::Plan.structure_checks
111
+ save_collection engine.at_cycle_end_handlers
112
+ save_collection Roby::EventGenerator.event_gathering
113
+ Roby.app.abort_on_exception = true
114
+ Roby.app.abort_on_application_exception = true
93
115
 
94
- save_collection Roby.exception_handlers
116
+ save_collection engine.event_ordering
117
+ save_collection engine.delayed_events
118
+ save_collection plan.exception_handlers
95
119
  timings[:setup] = Time.now
120
+
121
+ engine.at_cycle_end(&Test.method(:check_event_assertions))
122
+ engine.finalizers << Test.method(:finalize_event_assertions)
96
123
  end
97
124
 
125
+
98
126
  def teardown_plan
127
+ engine.at_cycle_end_handlers.delete(Test.method(:check_event_assertions))
128
+ engine.finalizers.delete(Test.method(:finalize_event_assertions))
129
+
99
130
  old_gc_roby_logger_level = Roby.logger.level
100
131
  if debug_gc?
101
132
  Roby.logger.level = Logger::DEBUG
102
133
  end
103
134
 
104
- if !Roby.control.running?
105
- Roby.control.run :detach => true
135
+ if !engine.running?
136
+ engine.run
106
137
  end
107
138
 
108
- Roby.control.quit
109
- Roby.control.join
139
+ engine.quit
140
+ engine.join
110
141
  plan.clear
111
142
 
112
143
  ensure
@@ -142,10 +173,9 @@ module Roby
142
173
  end
143
174
 
144
175
  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
176
+ if defined? Roby::Application
177
+ Roby.app.abort_on_exception = false
178
+ Roby.app.abort_on_application_exception = false
149
179
  end
150
180
 
151
181
  if defined? Roby::Log
@@ -169,24 +199,28 @@ module Roby
169
199
  end
170
200
  end
171
201
 
202
+ super if defined? super
203
+
172
204
  rescue Exception => e
173
205
  STDERR.puts "failed teardown: #{e.full_message}"
174
206
 
175
207
  ensure
176
- while Roby.control.running?
177
- Roby.control.quit
178
- Roby.control.join rescue nil
208
+ while engine.running?
209
+ engine.quit
210
+ engine.join rescue nil
179
211
  end
180
- Roby.plan.clear
212
+ plan.clear
213
+ @plan = nil
181
214
 
182
215
  Roby.logger.level = @original_roby_logger_level
183
216
  self.console_logger = false
217
+ self.event_logger = false
184
218
  end
185
219
 
186
220
  # Process pending events
187
221
  def process_events
188
- Roby::Control.synchronize do
189
- Roby.control.process_events
222
+ Roby.synchronize do
223
+ engine.process_events
190
224
  end
191
225
  end
192
226
 
@@ -215,29 +249,29 @@ module Roby
215
249
  #
216
250
  def prepare_plan(options)
217
251
  options = validate_options options,
218
- :missions => 0, :discover => 0, :tasks => 0,
252
+ :missions => 0, :add => 0, :discover => 0, :tasks => 0,
219
253
  :permanent => 0,
220
254
  :model => Roby::Task, :plan => plan
221
255
 
222
- missions, permanent, discovered, tasks = [], [], [], []
256
+ missions, permanent, added, tasks = [], [], [], []
223
257
  (1..options[:missions]).each do |i|
224
- options[:plan].insert(t = options[:model].new(:id => "mission-#{i}"))
258
+ options[:plan].add_mission(t = options[:model].new(:id => "mission-#{i}"))
225
259
  missions << t
226
260
  end
227
261
  (1..options[:permanent]).each do |i|
228
- options[:plan].permanent(t = options[:model].new(:id => "perm-#{i}"))
262
+ options[:plan].add_permanent(t = options[:model].new(:id => "perm-#{i}"))
229
263
  permanent << t
230
264
  end
231
- (1..options[:discover]).each do |i|
232
- options[:plan].discover(t = options[:model].new(:id => "discover-#{i}"))
233
- discovered << t
265
+ (1..(options[:discover] + options[:add])).each do |i|
266
+ options[:plan].add(t = options[:model].new(:id => "discover-#{i}"))
267
+ added << t
234
268
  end
235
269
  (1..options[:tasks]).each do |i|
236
270
  tasks << options[:model].new(:id => "task-#{i}")
237
271
  end
238
272
 
239
273
  result = []
240
- [missions, permanent, discovered, tasks].each do |set|
274
+ [missions, permanent, added, tasks].each do |set|
241
275
  unless set.empty?
242
276
  set = *set
243
277
  result << set
@@ -255,19 +289,24 @@ module Roby
255
289
  start_r, start_w= IO.pipe
256
290
  quit_r, quit_w = IO.pipe
257
291
  remote_pid = fork do
258
- start_r.close
259
- yield
260
- start_w.write('OK')
261
- quit_r.read(2)
292
+ begin
293
+ start_r.close
294
+ yield
295
+ rescue Exception => e
296
+ puts e.full_message
297
+ end
298
+
299
+ start_w.write('OK')
300
+ quit_r.read(2)
262
301
  end
263
302
  start_w.close
264
- start_r.read(2)
303
+ result = start_r.read(2)
265
304
 
266
305
  remote_processes << [remote_pid, quit_w]
267
306
  remote_pid
268
307
 
269
308
  ensure
270
- start_r.close
309
+ # start_r.close
271
310
  end
272
311
 
273
312
  # Stop all the remote processes that have been started using #remote_process
@@ -295,7 +334,8 @@ module Roby
295
334
  assert_nothing_raised do
296
335
  begin
297
336
  yield
298
- rescue localized_error_type => e
337
+ rescue Exception => e
338
+ assert_kind_of(localized_error_type, e)
299
339
  assert_respond_to(e, :error)
300
340
  assert_kind_of(klass, e.error)
301
341
  end
@@ -372,6 +412,21 @@ module Roby
372
412
  end
373
413
  end
374
414
 
415
+ attr_reader :event_logger
416
+ def event_logger=(value)
417
+ if value && !@event_logger
418
+ require 'roby/log/file'
419
+ logfile = @method_name + ".log"
420
+ logger = Roby::Log::FileLogger.new(logfile)
421
+ logger.stats_mode = false
422
+ Roby::Log.add_logger logger
423
+ @event_logger = logger
424
+ elsif !value && @event_logger
425
+ Roby::Log.remove_logger @event_logger
426
+ @event_logger = nil
427
+ end
428
+ end
429
+
375
430
  def wait_thread_stopped(thread)
376
431
  while !thread.stop?
377
432
  sleep(0.1)
@@ -394,6 +449,190 @@ module Roby
394
449
 
395
450
  result
396
451
  end
452
+
453
+ @event_assertions = []
454
+ @waiting_threads = []
455
+
456
+ ASSERT_ANY_EVENTS_TLS = :assert_any_events
457
+
458
+ class << self
459
+ # A [thread, cv, positive, negative] list of event assertions
460
+ attr_reader :event_assertions
461
+ end
462
+
463
+ # Tests for events in +positive+ and +negative+ and returns
464
+ # the set of failing events if the assertion has finished.
465
+ # If the set is empty, it means that the assertion finished
466
+ # successfully
467
+ def self.assert_any_event_result(positive, negative)
468
+ if positive_ev = positive.find { |ev| ev.happened? }
469
+ return false, "#{positive_ev} happened"
470
+ end
471
+ failure = negative.find_all { |ev| ev.happened? }
472
+ unless failure.empty?
473
+ return true, "#{failure} happened"
474
+ end
475
+
476
+ if positive.all? { |ev| ev.unreachable? }
477
+ positive.each do |ev|
478
+ Robot.info "#{ev} is unreachable because of the following emission:"
479
+ Roby.log_exception(ev.unreachability_reason, Robot.logger, :info)
480
+ end
481
+ return true, "all positive events are unreachable"
482
+ end
483
+
484
+ nil
485
+ end
486
+
487
+ # This method is inserted in the control thread to implement
488
+ # Assertions#assert_events
489
+ def self.check_event_assertions
490
+ event_assertions.delete_if do |thread, cv, positive, negative|
491
+ error, result = Test.assert_any_event_result(positive, negative)
492
+ if !error.nil?
493
+ thread[ASSERT_ANY_EVENTS_TLS] = [error, result]
494
+ cv.broadcast
495
+ true
496
+ end
497
+ end
498
+ end
499
+
500
+ def self.finalize_event_assertions
501
+ check_event_assertions
502
+ event_assertions.dup.each do |thread, *_|
503
+ thread.raise ControlQuitError
504
+ end
505
+ end
506
+
507
+ module Assertions
508
+ # Wait for any event in +positive+ to happen. If +negative+ is
509
+ # non-empty, any event happening in this set will make the
510
+ # assertion fail. If events in +positive+ are task events, the
511
+ # :stop events of the corresponding tasks are added to negative
512
+ # automatically.
513
+ #
514
+ # If a block is given, it is called from within the control thread
515
+ # after the checks are in place
516
+ #
517
+ # So, to check that a task fails, do
518
+ #
519
+ # assert_events(task.event(:fail)) do
520
+ # task.start!
521
+ # end
522
+ #
523
+ def assert_any_event(positive, negative = [], msg = nil, &block)
524
+ control_priority do
525
+ engine.waiting_threads << Thread.current
526
+ Roby.condition_variable(false) do |cv|
527
+ positive = Array[*positive].to_value_set
528
+ negative = Array[*negative].to_value_set
529
+
530
+ unreachability_reason = ValueSet.new
531
+ Roby.synchronize do
532
+ positive.each do |ev|
533
+ ev.if_unreachable(true) do |reason|
534
+ unreachability_reason << reason if reason
535
+ end
536
+ end
537
+
538
+ error, result = Test.assert_any_event_result(positive, negative)
539
+ if error.nil?
540
+ this_thread = Thread.current
541
+
542
+ Test.event_assertions << [this_thread, cv, positive, negative]
543
+ engine.once(&block) if block_given?
544
+ begin
545
+ cv.wait(Roby.global_lock)
546
+ ensure
547
+ Test.event_assertions.delete_if { |thread, _| thread == this_thread }
548
+ end
549
+
550
+ error, result = this_thread[ASSERT_ANY_EVENTS_TLS]
551
+ end
552
+
553
+ if error
554
+ if !unreachability_reason.empty?
555
+ msg = unreachability_reason.map do |reason|
556
+ if reason.respond_to?(:context)
557
+ context = (reason.context || []).map do |obj|
558
+ if obj.kind_of?(Exception)
559
+ obj.full_message
560
+ else
561
+ obj.to_s
562
+ end
563
+ end
564
+ reason.to_s + context.join("\n ")
565
+ end
566
+ end
567
+ msg.join("\n ")
568
+
569
+ flunk("#{msg} all positive events are unreachable for the following reason:\n #{msg}")
570
+ elsif msg
571
+ flunk("#{msg} failed: #{result}")
572
+ else
573
+ flunk(result)
574
+ end
575
+ end
576
+ end
577
+ end
578
+ end
579
+ ensure
580
+ engine.waiting_threads.delete(Thread.current)
581
+ end
582
+
583
+ # Starts +task+ and checks it succeeds
584
+ def assert_succeeds(task, *args)
585
+ control_priority do
586
+ if !task.kind_of?(Roby::Task)
587
+ engine.execute do
588
+ plan.add_mission(task = planner.send(task, *args))
589
+ end
590
+ end
591
+
592
+ assert_any_event([task.event(:success)], [], nil) do
593
+ plan.add_permanent(task)
594
+ task.start! if task.pending?
595
+ yield if block_given?
596
+ end
597
+ end
598
+ end
599
+
600
+ def control_priority
601
+ if !engine.thread
602
+ return yield
603
+ end
604
+
605
+ old_priority = Thread.current.priority
606
+ Thread.current.priority = engine.thread.priority + 1
607
+
608
+ yield
609
+ ensure
610
+ Thread.current.priority = old_priority if old_priority
611
+ end
612
+
613
+ # This assertion fails if the relative error between +found+ and
614
+ # +expected+is more than +error+
615
+ def assert_relative_error(expected, found, error, msg = "")
616
+ if expected == 0
617
+ assert_in_delta(0, found, error, "comparing #{found} to #{expected} in #{msg}")
618
+ else
619
+ assert_in_delta(0, (found - expected) / expected, error, "comparing #{found} to #{expected} in #{msg}")
620
+ end
621
+ end
622
+
623
+ # This assertion fails if +found+ and +expected+ are more than +dl+
624
+ # meters apart in the x, y and z coordinates, or +dt+ radians apart
625
+ # in angles
626
+ def assert_same_position(expected, found, dl = 0.01, dt = 0.01, msg = "")
627
+ assert_relative_error(expected.x, found.x, dl, msg)
628
+ assert_relative_error(expected.y, found.y, dl, msg)
629
+ assert_relative_error(expected.z, found.z, dl, msg)
630
+ assert_relative_error(expected.yaw, found.yaw, dt, msg)
631
+ assert_relative_error(expected.pitch, found.pitch, dt, msg)
632
+ assert_relative_error(expected.roll, found.roll, dt, msg)
633
+ end
634
+ end
635
+
397
636
  end
398
637
  end
399
638