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,37 @@
1
+ $LOAD_PATH.unshift File.expand_path('..', File.dirname(__FILE__))
2
+ require 'roby/test/common'
3
+ require 'flexmock'
4
+
5
+ class TC_Interface < Test::Unit::TestCase
6
+ include Roby::Test
7
+
8
+ include Roby::Planning
9
+ def test_method_missing
10
+ control = Roby.control
11
+ iface = Interface.new(control)
12
+
13
+ task_model = Class.new(Task)
14
+
15
+ result_task = nil
16
+ planner = Class.new(Planner) do
17
+ method(:null_task) { result_task = task_model.new }
18
+ end
19
+ control.planners << planner
20
+
21
+ control.run :detach => true
22
+ returned_task = iface.null_task! do |_, planner|
23
+ planner.start!
24
+ end
25
+
26
+ assert_kind_of(Roby::RemoteObjectProxy, returned_task)
27
+ marshalled = nil
28
+ assert_nothing_raised { marshalled = Marshal.dump(returned_task) }
29
+ unmarshalled = Marshal.load(marshalled)
30
+
31
+ assert_kind_of(Roby::Task, unmarshalled)
32
+ assert_equal(result_task, unmarshalled)
33
+ plan_task = plan.missions.find { true }
34
+ assert_equal(plan_task, unmarshalled)
35
+ end
36
+ end
37
+
data/test/test_log.rb ADDED
@@ -0,0 +1,114 @@
1
+ $LOAD_PATH.unshift File.expand_path('..', File.dirname(__FILE__))
2
+ require 'roby/test/common'
3
+ require 'roby/test/distributed'
4
+ require 'roby/test/tasks/simple_task'
5
+
6
+ require 'roby'
7
+ require 'roby/log'
8
+ require 'flexmock'
9
+
10
+ class TC_Log < Test::Unit::TestCase
11
+ include Roby::Test
12
+
13
+ def teardown
14
+ super
15
+ Log.clear_loggers
16
+ end
17
+
18
+ def test_start_stop_logger
19
+ FlexMock.use do |mock|
20
+ Log.add_logger mock
21
+ assert(Log.logging?)
22
+ assert_nothing_raised { Log.start_logging }
23
+
24
+ Log.remove_logger mock
25
+ assert(!Log.logging?)
26
+ assert_nothing_raised { Log.stop_logging }
27
+ end
28
+ end
29
+
30
+ def test_misc
31
+ FlexMock.use do |mock|
32
+ mock.should_receive(:splat?).and_return(true)
33
+ mock.should_receive(:event).with(1, 2)
34
+ mock.should_receive(:flush)
35
+ Log.add_logger mock
36
+
37
+ assert(Log.has_logger?(:flush))
38
+ assert(Log.has_logger?(:event))
39
+
40
+ assert_equal([mock], Log.enum_for(:each_logger, :event).to_a)
41
+ assert_equal([], Log.enum_for(:each_logger, :bla).to_a)
42
+ end
43
+ end
44
+
45
+ def test_message_splat
46
+ FlexMock.use do |mock|
47
+ mock.should_receive(:splat?).and_return(true).twice
48
+ mock.should_receive(:splat_event).with(FlexMock.any, 1, 2).once
49
+ mock.should_receive(:flush).once
50
+ Log.add_logger mock
51
+
52
+ Log.log(:splat_event) { [1, 2] }
53
+ Log.flush
54
+ end
55
+ end
56
+
57
+ def test_message_nonsplat
58
+ FlexMock.use do |mock|
59
+ mock.should_receive(:splat?).and_return(false).twice
60
+ mock.should_receive(:nonsplat_event).with(FlexMock.any, [1, 2]).once
61
+ mock.should_receive(:flush).once
62
+ Log.add_logger mock
63
+
64
+ Log.log(:nonsplat_event) { [1, 2] }
65
+ Log.flush
66
+ end
67
+ end
68
+
69
+ def on_marshalled_task(task)
70
+ FlexMock.on do |obj|
71
+ obj.remote_siblings[Roby::Distributed.droby_dump(nil)] == task.remote_id
72
+ end
73
+ end
74
+ def test_known_objects_management
75
+ t1, t2 = SimpleTask.new, SimpleTask.new
76
+ FlexMock.use do |mock|
77
+ mock.should_receive(:splat?).and_return(true)
78
+ mock.should_receive(:added_task_child).
79
+ with(FlexMock.any, on_marshalled_task(t1), [TaskStructure::Hierarchy].droby_dump(nil),
80
+ on_marshalled_task(t2), FlexMock.any).once
81
+
82
+ match_discovered_set = FlexMock.on do |task_set|
83
+ task_set.map { |obj| obj.remote_siblings[Roby::Distributed.droby_dump(nil)] }.to_set == [t1.remote_id, t2.remote_id].to_set
84
+ end
85
+
86
+ mock.should_receive(:discovered_tasks).
87
+ with(FlexMock.any, FlexMock.any, match_discovered_set).
88
+ once
89
+ mock.should_receive(:removed_task_child).
90
+ with(FlexMock.any, t1.remote_id, [TaskStructure::Hierarchy].droby_dump(nil), t2.remote_id).
91
+ once
92
+ mock.should_receive(:finalized_task).
93
+ with(FlexMock.any, FlexMock.any, t1.remote_id).
94
+ once
95
+
96
+ Log.add_logger mock
97
+ begin
98
+ t1.realized_by t2
99
+ assert(Log.known_objects.empty?)
100
+ plan.discover(t1)
101
+ assert_equal([t1, t2].to_value_set, Log.known_objects)
102
+ t1.remove_child t2
103
+ assert_equal([t1, t2].to_value_set, Log.known_objects)
104
+ plan.remove_object(t1)
105
+ assert_equal([t2].to_value_set, Log.known_objects)
106
+
107
+ Log.flush
108
+ ensure
109
+ Log.remove_logger mock
110
+ end
111
+ end
112
+ end
113
+ end
114
+
@@ -0,0 +1,132 @@
1
+ require 'roby/test/distributed'
2
+ require 'roby/log/server'
3
+
4
+ class TC_LogServer < Test::Unit::TestCase
5
+ include Roby::Distributed::Test
6
+ include Roby::Log
7
+
8
+ def test_discovery(remote = nil)
9
+ recursive = !remote
10
+ remote ||= remote_server do
11
+ def display_server
12
+ unless @display_server
13
+ @display_server = Log::Server.new
14
+ timings = DataStream.new 'robot', 'roby-timings'
15
+ @display_server.added_stream(timings)
16
+ end
17
+ DRbObject.new(@display_server)
18
+ end
19
+ end
20
+
21
+ remote_display = remote.display_server
22
+
23
+ Log::Server.enable_discovery 'localhost'
24
+ assert_raises(ArgumentError) { Log::Server.enable_discovery 'localhost' }
25
+ sleep(0.5)
26
+ assert_equal([remote_display], Log::Server.available_servers)
27
+
28
+ Log::Server.disable_discovery
29
+ if recursive
30
+ test_discovery(remote)
31
+ end
32
+ end
33
+
34
+ def test_connection(remote = nil)
35
+ recursive = !remote
36
+ remote ||= remote_server do
37
+ attr_reader :timings, :events
38
+ def display_server
39
+ unless @display_server
40
+ @display_server = Log::Server.new
41
+
42
+ @timings = DataStream.new 'robot', 'roby-timings'
43
+ @display_server.added_stream(timings)
44
+ end
45
+ DRbObject.new(@display_server)
46
+ end
47
+ def timings_id; timings.id end
48
+ def events_id; events.id end
49
+ def add_stream
50
+ @events = DataStream.new 'robot', 'roby-events'
51
+ @display_server.added_stream(events)
52
+ nil
53
+ end
54
+ def remove_stream
55
+ @display_server.removed_stream(events.id)
56
+ nil
57
+ end
58
+ def quit
59
+ @display_server.quit
60
+ nil
61
+ end
62
+ end
63
+
64
+ display_server = remote.display_server
65
+ source = Log::Client.new(display_server)
66
+ assert_equal([[remote.timings_id, 'robot', 'roby-timings']],
67
+ source.streams.map { |s| [s.id, s.name, s.type] })
68
+ assert(source.connected?)
69
+
70
+ remote.add_stream
71
+ sleep(0.5)
72
+ assert_equal([[remote.timings_id, 'robot', 'roby-timings'], [remote.events_id, 'robot', 'roby-events']].to_set,
73
+ source.streams.map { |s| [s.id, s.name, s.type] }.to_set)
74
+
75
+ remote.remove_stream
76
+ sleep(0.5)
77
+ assert_equal([['robot', 'roby-timings']], source.streams.map { |s| [s.name, s.type] })
78
+
79
+ source.disconnect
80
+ remote.add_stream
81
+ sleep(0.5)
82
+ assert(!source.connected?)
83
+
84
+ # try reconnection ...
85
+ if recursive
86
+ remote.remove_stream
87
+ test_connection(remote)
88
+ else
89
+ source.connect
90
+ assert_equal([['robot', 'roby-timings'], ['robot', 'roby-events']].to_set,
91
+ source.streams.map { |s| [s.name, s.type] }.to_set)
92
+
93
+ # ... or check what happens if the remote server quits
94
+ remote.quit
95
+ sleep(0.5)
96
+ assert(!source.connected?)
97
+ end
98
+ end
99
+
100
+ def test_subscription
101
+ remote = remote_server do
102
+ attr_reader :timings, :events
103
+ def display_server
104
+ unless @display_server
105
+ @display_server = Log::Server.new
106
+ @timings = DataStream.new 'robot', 'roby-timings'
107
+ @display_server.added_stream(timings)
108
+ end
109
+ DRbObject.new(@display_server)
110
+ end
111
+ def add_sample
112
+ @display_server.push(timings.id, Time.now, 42)
113
+ end
114
+ end
115
+
116
+ display_server = remote.display_server
117
+ source = Log::Client.new(display_server)
118
+ source.subscribe(stream = source.streams.first)
119
+
120
+ remote.add_sample
121
+ sleep(0.5)
122
+ stream = source.streams.find { |s| s.id == stream.id }
123
+ assert(stream.has_sample?)
124
+ assert_equal(42, stream.read, source.streams)
125
+ assert(!stream.has_sample?)
126
+
127
+ source.unsubscribe(stream)
128
+ remote.add_sample
129
+ sleep(0.5)
130
+ end
131
+ end
132
+
data/test/test_plan.rb ADDED
@@ -0,0 +1,584 @@
1
+ $LOAD_PATH.unshift File.expand_path('..', File.dirname(__FILE__))
2
+ require 'roby/test/common'
3
+ require 'roby/log'
4
+ require 'roby/state/information'
5
+ require 'roby/test/tasks/simple_task'
6
+
7
+ require 'flexmock'
8
+
9
+
10
+ module TC_PlanStatic
11
+ include Roby
12
+
13
+ def test_add_remove
14
+ t1 = Task.new
15
+
16
+ plan.discover(t1)
17
+ assert(plan.include?(t1))
18
+ assert(!plan.mission?(t1))
19
+ assert(!plan.permanent?(t1))
20
+
21
+ plan.remove_task(t1)
22
+ assert(!plan.include?(t1))
23
+ assert(!plan.mission?(t1))
24
+ assert(!plan.permanent?(t1))
25
+
26
+ plan.insert(t1 = Task.new)
27
+ assert(plan.include?(t1))
28
+ assert(plan.mission?(t1))
29
+ assert(t1.mission?)
30
+ assert(!plan.permanent?(t1))
31
+
32
+ plan.discard(t1)
33
+ assert(plan.include?(t1))
34
+ assert(!plan.mission?(t1))
35
+ assert(!t1.mission?)
36
+ assert(!plan.permanent?(t1))
37
+ plan.remove_task(t1)
38
+
39
+ plan.discover(t1 = Task.new)
40
+ assert(plan.include?(t1))
41
+ assert(!plan.mission?(t1))
42
+ plan.insert(t1)
43
+ assert(plan.mission?(t1))
44
+ assert(t1.mission?)
45
+ plan.remove_task(t1)
46
+
47
+ plan.permanent(t1 = Task.new)
48
+ assert(plan.include?(t1))
49
+ assert(!plan.mission?(t1))
50
+ assert(!t1.mission?)
51
+ assert(plan.permanent?(t1))
52
+ plan.auto(t1)
53
+ assert(plan.include?(t1))
54
+ assert(!plan.mission?(t1))
55
+ assert(!t1.mission?)
56
+ assert(!plan.permanent?(t1))
57
+
58
+ plan.permanent(t1)
59
+ plan.remove_task(t1)
60
+ assert(!plan.include?(t1))
61
+ assert(!plan.mission?(t1))
62
+ assert(!t1.mission?)
63
+ assert(!plan.permanent?(t1))
64
+ end
65
+
66
+ def test_plan_remove_object
67
+ t1, t2 = prepare_plan :tasks => 2
68
+ plan.discover(e = EventGenerator.new(true))
69
+
70
+ t1.realized_by(t2)
71
+ t1.on(:start, e)
72
+
73
+ plan.remove_object(e)
74
+ assert(!plan.free_events.include?(e))
75
+ assert(t1.event(:start).leaf?(EventStructure::Signal))
76
+ assert(!e.plan)
77
+ assert_raises(ArgumentError) { e.plan = plan }
78
+
79
+ plan.remove_object(t2)
80
+ assert(!plan.include?(e))
81
+ assert(!t1.realized_by?(t2))
82
+ assert(!t2.plan)
83
+ assert_raises(ArgumentError) { t2.plan = plan }
84
+ end
85
+
86
+ def test_discover
87
+ t1, t2, t3, t4 = prepare_plan :tasks => 4, :model => Roby::Test::SimpleTask
88
+ t1.realized_by t2
89
+ or_ev = OrGenerator.new
90
+ t2.event(:start).on or_ev
91
+ or_ev.on t3.event(:stop)
92
+ t2.planned_by t4
93
+
94
+ result = plan.discover(t1)
95
+ assert_equal(plan, result)
96
+ assert( plan.include?(t1) )
97
+ assert( plan.include?(t2) )
98
+ assert( plan.free_events.include?(or_ev))
99
+ assert( !plan.include?(t3) ) # t3 not related because of task structure
100
+ assert( plan.include?(t4) )
101
+
102
+ # Discover t3 to help plan cleanup
103
+ plan.discover(t3)
104
+
105
+ # Discover an AndGenerator and not its sources. The sources
106
+ # must be discovered automatically
107
+ a, b = (1..2).map { EventGenerator.new(true) }
108
+ and_event = a & b
109
+ plan.discover(and_event)
110
+ assert_equal(plan, a.plan)
111
+ end
112
+
113
+ def test_insert
114
+ t1, t2, t3, t4 = prepare_plan :tasks => 4, :model => Roby::Test::SimpleTask
115
+ t1.realized_by t2
116
+ t2.on(:start, t3, :stop)
117
+ t2.planned_by t4
118
+
119
+ result = plan.insert(t1)
120
+ assert_equal(plan, result)
121
+ assert( plan.include?(t1) )
122
+ assert( plan.include?(t2) )
123
+ assert( !plan.include?(t3) ) # t3 not related because of task structure
124
+ assert( plan.include?(t4) )
125
+
126
+ assert( plan.mission?(t1) )
127
+ assert( !plan.mission?(t2) )
128
+
129
+ # Discover t3 to help plan cleanup
130
+ plan.discover(t3)
131
+ end
132
+
133
+ def test_useful_task_components
134
+ t1, t2, t3, t4 = prepare_plan :tasks => 4, :model => Roby::Test::SimpleTask
135
+ t1.realized_by t2
136
+ t2.on(:start, t3, :stop)
137
+ t2.planned_by t4
138
+
139
+ plan.insert(t1)
140
+
141
+ assert_equal([t1, t2, t4].to_value_set, plan.locally_useful_tasks)
142
+ plan.insert(t3)
143
+ assert_equal([t1, t2, t3, t4].to_value_set, plan.locally_useful_tasks)
144
+ plan.discard(t1)
145
+ assert_equal([t3].to_value_set, plan.locally_useful_tasks)
146
+ assert_equal([t1, t2, t4].to_value_set, plan.unneeded_tasks)
147
+ end
148
+
149
+ def test_replace_task
150
+ (p, c1), (c11, c12, c2, c3) = prepare_plan :missions => 2, :tasks => 4, :model => Roby::Test::SimpleTask
151
+ p.realized_by c1
152
+ c1.realized_by c11
153
+ c1.realized_by c12
154
+ p.realized_by c2
155
+ c1.on(:stop, c2, :start)
156
+ c1.forward :start, c1, :stop
157
+ c11.forward :success, c1
158
+
159
+ # Replace c1 by c3 and check that the hooks are properly called
160
+ FlexMock.use do |mock|
161
+ p.singleton_class.class_eval do
162
+ define_method('removed_child_object') do |child, relations|
163
+ mock.removed_hook(p, child, relations)
164
+ end
165
+ end
166
+ c1.singleton_class.class_eval do
167
+ define_method('removed_parent_object') do |parent, relations|
168
+ mock.removed_hook(c1, parent, relations)
169
+ end
170
+ end
171
+
172
+ mock.should_receive(:removed_hook).with(p, c1, [TaskStructure::Hierarchy]).once
173
+ mock.should_receive(:removed_hook).with(c1, p, [TaskStructure::Hierarchy]).once
174
+ assert_nothing_raised { plan.replace_task(c1, c3) }
175
+ end
176
+
177
+ # Check that the external task and event structures have been
178
+ # transferred.
179
+ assert( !p.child_object?(c1, TaskStructure::Hierarchy) )
180
+ assert( p.child_object?(c3, TaskStructure::Hierarchy) )
181
+ assert( c3.child_object?(c11, TaskStructure::Hierarchy) )
182
+ assert( !c1.child_object?(c11, TaskStructure::Hierarchy) )
183
+
184
+ assert( !c1.event(:stop).child_object?(c2.event(:start), EventStructure::Signal) )
185
+ assert( c3.event(:stop).child_object?(c2.event(:start), EventStructure::Signal) )
186
+ assert( c3.event(:success).parent_object?(c11.event(:success), EventStructure::Forwarding) )
187
+ assert( !c1.event(:success).parent_object?(c11.event(:success), EventStructure::Forwarding) )
188
+ # Also check that the internal event structure has *not* been transferred
189
+ assert( !c3.event(:start).child_object?(c3.event(:stop), EventStructure::Forwarding) )
190
+ assert( c1.event(:start).child_object?(c1.event(:stop), EventStructure::Forwarding) )
191
+
192
+ # Check that +c1+ is no more marked as mission, and that c3 is marked. c1 should
193
+ # still be in the plan
194
+ assert(! plan.mission?(c1) )
195
+ assert( plan.mission?(c3) )
196
+ assert( plan.include?(c1) )
197
+
198
+ # Check that #replace_task keeps the permanent flag too
199
+ p, t = prepare_plan :permanent => 1, :tasks => 1, :model => Roby::Test::SimpleTask
200
+ plan.permanent(p)
201
+ plan.replace_task(p, t)
202
+ assert(!plan.permanent?(p))
203
+ assert(plan.permanent?(t))
204
+ end
205
+
206
+ def test_replace
207
+ (p, c1), (c11, c12, c2, c3) = prepare_plan :missions => 2, :tasks => 4, :model => Roby::Test::SimpleTask
208
+ p.realized_by c1
209
+ c1.realized_by c11
210
+ c1.realized_by c12
211
+ p.realized_by c2
212
+ c1.on(:stop, c2, :start)
213
+ c1.forward :start, c1, :stop
214
+ c11.forward :success, c1
215
+
216
+ # Replace c1 by c3 and check that the hooks are properly called
217
+ FlexMock.use do |mock|
218
+ p.singleton_class.class_eval do
219
+ define_method('removed_child_object') do |child, relations|
220
+ mock.removed_hook(p, child, relations)
221
+ end
222
+ end
223
+ c1.singleton_class.class_eval do
224
+ define_method('removed_parent_object') do |parent, relations|
225
+ mock.removed_hook(c1, parent, relations)
226
+ end
227
+ end
228
+
229
+ mock.should_receive(:removed_hook).with(p, c1, [TaskStructure::Hierarchy]).once
230
+ mock.should_receive(:removed_hook).with(c1, p, [TaskStructure::Hierarchy]).once
231
+ assert_nothing_raised { plan.replace(c1, c3) }
232
+ end
233
+
234
+ # Check that the external task and event structures have been
235
+ # transferred.
236
+ assert( !p.child_object?(c1, TaskStructure::Hierarchy) )
237
+ assert( p.child_object?(c3, TaskStructure::Hierarchy) )
238
+ assert( c1.child_object?(c11, TaskStructure::Hierarchy) )
239
+ assert( !c3.child_object?(c11, TaskStructure::Hierarchy) )
240
+
241
+ assert( !c1.event(:stop).child_object?(c2.event(:start), EventStructure::Signal) )
242
+ assert( c3.event(:stop).child_object?(c2.event(:start), EventStructure::Signal) )
243
+ assert( c1.event(:success).parent_object?(c11.event(:success), EventStructure::Forwarding) )
244
+ assert( !c3.event(:success).parent_object?(c11.event(:success), EventStructure::Forwarding) )
245
+ # Also check that the internal event structure has *not* been transferred
246
+ assert( !c3.event(:start).child_object?(c3.event(:stop), EventStructure::Forwarding) )
247
+ assert( c1.event(:start).child_object?(c1.event(:stop), EventStructure::Forwarding) )
248
+
249
+ # Check that +c1+ is no more marked as mission, and that c3 is marked. c1 should
250
+ # still be in the plan
251
+ assert(! plan.mission?(c1) )
252
+ assert( plan.mission?(c3) )
253
+ assert( plan.include?(c1) )
254
+ end
255
+
256
+ def test_remove_task
257
+ t1, t2, t3 = (1..3).map { Roby::Task.new }
258
+ t1.realized_by t2
259
+ t1.on(:stop, t3, :start)
260
+
261
+ plan.insert(t1)
262
+ plan.insert(t3)
263
+
264
+ assert(!t1.leaf?)
265
+ plan.remove_task(t2)
266
+ assert(t1.leaf?)
267
+ assert(!plan.include?(t2))
268
+
269
+ assert(!t1.event(:stop).leaf?(EventStructure::Signal))
270
+ plan.remove_task(t3)
271
+ assert(t1.event(:stop).leaf?(EventStructure::Signal))
272
+ assert(!plan.include?(t3))
273
+ end
274
+
275
+ def test_free_events
276
+ t1, t2, t3 = (1..3).map { Roby::Task.new }
277
+ plan.insert(t1)
278
+ t1.realized_by t2
279
+ assert_equal(plan, t2.plan)
280
+ assert_equal(plan, t1.event(:start).plan)
281
+
282
+ or_generator = (t1.event(:stop) | t2.event(:stop))
283
+ assert_equal(plan, or_generator.plan)
284
+ assert(plan.free_events.include?(or_generator))
285
+ or_generator.on t3.event(:start)
286
+ assert_equal(plan, t3.plan)
287
+
288
+ and_generator = (t1.event(:stop) & t2.event(:stop))
289
+ assert_equal(plan, and_generator.plan)
290
+ assert(plan.free_events.include?(and_generator))
291
+ end
292
+
293
+ def test_plan_synchronization
294
+ t1, t2 = prepare_plan :tasks => 2
295
+ plan.insert(t1)
296
+ assert_equal(plan, t1.plan)
297
+ assert_equal(nil, t2.plan)
298
+ t1.realized_by t2
299
+ assert_equal(plan, t1.plan)
300
+ assert_equal(plan, t2.plan)
301
+ assert(plan.include?(t2))
302
+
303
+ e = EventGenerator.new(true)
304
+ assert_equal(nil, e.plan)
305
+ t1.on(:start, e)
306
+ assert_equal(plan, e.plan)
307
+ assert(plan.free_events.include?(e))
308
+
309
+ # Now, make sure a PlanObject don't get included in the plan if add_child_object
310
+ # raises
311
+ adding_child_failure = Module.new do
312
+ def adding_child_object(child, relation, info)
313
+ raise RuntimeError
314
+ end
315
+ end
316
+ model = Class.new(Task) do
317
+ include adding_child_failure
318
+ end
319
+ t1, t2 = model.new, model.new
320
+ plan.insert(t1)
321
+ assert_equal(plan, t1.plan)
322
+ assert_equal(nil, t2.plan)
323
+ assert_raises(RuntimeError) { t1.realized_by t2 }
324
+ assert_equal(plan, t1.plan)
325
+ assert_equal(plan, t2.plan)
326
+ assert(plan.include?(t2))
327
+ end
328
+ end
329
+
330
+ class TC_Plan < Test::Unit::TestCase
331
+ include TC_PlanStatic
332
+ include Roby::Test
333
+
334
+ def clear_finalized
335
+ Roby::Log.flush
336
+ @finalized_tasks_recorder.clear
337
+ end
338
+ def finalized_tasks; @finalized_tasks_recorder.tasks end
339
+ def finalized_events; @finalized_tasks_recorder.events end
340
+ class FinalizedTaskRecorder
341
+ attribute(:tasks) { Array.new }
342
+ attribute(:events) { Array.new }
343
+ def finalized_task(time, plan, task)
344
+ tasks << task
345
+ end
346
+ def finalized_event(time, plan, event)
347
+ events << event unless event.respond_to?(:task)
348
+ end
349
+ def clear
350
+ tasks.clear
351
+ events.clear
352
+ end
353
+ def splat?; true end
354
+ end
355
+
356
+ def setup
357
+ super
358
+ Roby::Log.add_logger(@finalized_tasks_recorder = FinalizedTaskRecorder.new)
359
+ end
360
+ def teardown
361
+ Roby::Log.remove_logger @finalized_tasks_recorder
362
+ super
363
+ end
364
+
365
+ def assert_finalizes(plan, unneeded, finalized = nil)
366
+ finalized ||= unneeded
367
+ finalized = finalized.map { |obj| obj.remote_id }
368
+ clear_finalized
369
+
370
+ yield if block_given?
371
+
372
+ assert_equal(unneeded.to_set, plan.unneeded_tasks.to_set)
373
+ plan.garbage_collect
374
+ process_events
375
+ plan.garbage_collect
376
+
377
+ # !!! We are actually relying on the logging queue for this to work.
378
+ # make sure it is empty before testing anything
379
+ Roby::Log.flush
380
+
381
+ assert_equal(finalized.to_set, (finalized_tasks.to_set | finalized_events.to_set))
382
+ assert(! finalized.any? { |t| plan.include?(t) })
383
+ end
384
+
385
+ def test_garbage_collect_tasks
386
+ klass = Class.new(Task) do
387
+ attr_accessor :delays
388
+
389
+ event(:start, :command => true)
390
+ event(:stop) do |context|
391
+ if delays
392
+ return
393
+ else
394
+ emit(:stop)
395
+ end
396
+ end
397
+ end
398
+
399
+ t1, t2, t3, t4, t5, t6, t7, t8, p1 = (1..9).map { |i| klass.new(:id => i) }
400
+ t1.realized_by t3
401
+ t2.realized_by t3
402
+ t3.realized_by t4
403
+ t5.realized_by t4
404
+ t5.planned_by p1
405
+ p1.realized_by t6
406
+
407
+ t7.realized_by t8
408
+
409
+ [t1, t2, t5].each { |t| plan.insert(t) }
410
+ plan.permanent(t7)
411
+
412
+ assert_finalizes(plan, [])
413
+ assert_finalizes(plan, [t1]) { plan.discard(t1) }
414
+ assert_finalizes(plan, [t2, t3]) do
415
+ t2.start!(nil)
416
+ plan.discard(t2)
417
+ end
418
+ assert_finalizes(plan, [t5, t4, p1, t6], []) do
419
+ t5.delays = true
420
+ t5.start!(nil)
421
+ plan.discard(t5)
422
+ end
423
+ assert(t5.event(:stop).pending?)
424
+ assert_finalizes(plan, [t5, t4, p1, t6]) do
425
+ t5.event(:stop).emit(nil)
426
+ end
427
+ end
428
+
429
+ def test_force_garbage_collect_tasks
430
+ t1 = Class.new(Task) do
431
+ event(:stop) { |context| }
432
+ end.new
433
+ t2 = Task.new
434
+ t1.realized_by t2
435
+
436
+ plan.insert(t1)
437
+ t1.start!
438
+ assert_finalizes(plan, []) do
439
+ plan.garbage_collect([t1])
440
+ end
441
+ assert(t1.event(:stop).pending?)
442
+
443
+ assert_finalizes(plan, [t1, t2], [t1, t2]) do
444
+ # This stops the mission, which will be automatically discarded
445
+ t1.event(:stop).emit(nil)
446
+ end
447
+ end
448
+
449
+ def test_gc_ignores_incoming_events
450
+ Roby::Plan.logger.level = Logger::WARN
451
+ a, b = prepare_plan :discover => 2, :model => SimpleTask
452
+ a.on(:stop, b, :start)
453
+ a.start!
454
+
455
+ process_events
456
+ process_events
457
+ assert(!a.plan)
458
+ assert(!b.plan)
459
+ assert(!b.event(:start).happened?)
460
+ end
461
+
462
+ # Test a setup where there is both pending tasks and running tasks. This
463
+ # checks that #stop! is called on all the involved tasks. This tracks
464
+ # problems related to bindings in the implementation of #garbage_collect:
465
+ # the killed task bound to the Control.once block must remain the same.
466
+ def test_gc_stopping
467
+ Roby::Plan.logger.level = Logger::WARN
468
+ running_task = nil
469
+ FlexMock.use do |mock|
470
+ task_model = Class.new(Task) do
471
+ event :start, :command => true
472
+ event :stop do
473
+ mock.stop(self)
474
+ end
475
+ end
476
+
477
+ running_tasks = (1..5).map do
478
+ task_model.new
479
+ end
480
+
481
+ plan.discover(running_tasks)
482
+ t1, t2 = Roby::Task.new, Roby::Task.new
483
+ t1.realized_by t2
484
+ plan.discover(t1)
485
+
486
+ running_tasks.each do |t|
487
+ t.start!
488
+ mock.should_receive(:stop).with(t).once
489
+ end
490
+
491
+ plan.garbage_collect
492
+ process_events
493
+
494
+ assert(!plan.include?(t1))
495
+ assert(!plan.include?(t2))
496
+ running_tasks.each do |t|
497
+ assert(t.finishing?)
498
+ t.emit(:stop)
499
+ end
500
+
501
+ plan.garbage_collect
502
+ running_tasks.each do |t|
503
+ assert(!plan.include?(t))
504
+ end
505
+ end
506
+
507
+ ensure
508
+ running_task.emit(:stop) if running_task && !running_task.finished?
509
+ end
510
+
511
+ def test_garbage_collect_events
512
+ t = SimpleTask.new
513
+ e1 = EventGenerator.new(true)
514
+
515
+ plan.insert(t)
516
+ plan.discover(e1)
517
+ assert_equal([e1], plan.unneeded_events.to_a)
518
+ t.event(:start).on e1
519
+ assert_equal([], plan.unneeded_events.to_a)
520
+
521
+ e2 = EventGenerator.new(true)
522
+ plan.discover(e2)
523
+ assert_equal([e2], plan.unneeded_events.to_a)
524
+ e1.forward e2
525
+ assert_equal([], plan.unneeded_events.to_a)
526
+
527
+ plan.remove_object(t)
528
+ assert_equal([e1, e2].to_value_set, plan.unneeded_events)
529
+ end
530
+
531
+ # Checks that a garbage collected object (event or task) cannot be added back into the plan
532
+ def test_garbage_collection_final
533
+ t = SimpleTask.new
534
+ e = EventGenerator.new(true)
535
+ plan.discover [t, e]
536
+ plan.garbage_collect
537
+ assert_raises(ArgumentError) { plan.discover(t) }
538
+ assert_raises(ArgumentError) { plan.discover(e) }
539
+ end
540
+
541
+ def test_garbage_collect_weak
542
+ Roby.control.run :detach => true
543
+
544
+ Roby.execute do
545
+ planning, planned, influencing = prepare_plan :discover => 3, :model => SimpleTask
546
+
547
+ planned.planned_by planning
548
+ influencing.realized_by planned
549
+ planning.influenced_by influencing
550
+
551
+ planned.start!
552
+ planning.start!
553
+ influencing.start!
554
+ end
555
+
556
+ Roby.wait_one_cycle
557
+ Roby.wait_one_cycle
558
+ Roby.wait_one_cycle
559
+
560
+ assert(plan.known_tasks.empty?)
561
+ end
562
+
563
+ def test_mission_failed
564
+ model = Class.new(SimpleTask) do
565
+ event :specialized_failure, :command => true
566
+ forward :specialized_failure => :failed
567
+ end
568
+
569
+ task = prepare_plan :missions => 1, :model => model
570
+ task.start!
571
+ task.specialized_failure!
572
+
573
+ error = Roby.check_failed_missions(plan).first.exception
574
+ assert_kind_of(Roby::MissionFailedError, error)
575
+ assert_equal(task.event(:specialized_failure).last, error.failure_point)
576
+ assert_nothing_raised do
577
+ Roby.format_exception error
578
+ end
579
+
580
+ # Makes teardown happy
581
+ plan.remove_object(task)
582
+ end
583
+ end
584
+