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,102 @@
1
+ $LOAD_PATH.unshift File.expand_path('../..', File.dirname(__FILE__))
2
+ require 'roby/test/distributed'
3
+ require 'roby/test/tasks/simple_task'
4
+ require 'flexmock'
5
+
6
+ class TC_DistributedQuery < Test::Unit::TestCase
7
+ include Roby::Distributed::Test
8
+
9
+ def test_ownership
10
+ DRb.start_service
11
+ FlexMock.use do |fake_peer|
12
+ fake_peer.should_receive(:remote_name).and_return('fake_peer')
13
+ fake_peer.should_receive(:subscribed_plan?).and_return(false)
14
+ fake_peer.should_receive(:subscribed?).and_return(false)
15
+
16
+ t1 = Class.new(Task).new
17
+ t2 = Class.new(Task).new
18
+ t2.owners << fake_peer
19
+ plan.discover [t1, t2]
20
+
21
+ assert_equal([t1].to_set, TaskMatcher.owned_by(Distributed).enum_for(:each, plan).to_set)
22
+ assert_equal([t1].to_set, TaskMatcher.self_owned.enum_for(:each, plan).to_set)
23
+ assert_equal([t2].to_set, TaskMatcher.owned_by(fake_peer, Distributed).enum_for(:each, plan).to_set, plan.task_index.by_owner)
24
+ assert_equal([].to_set, TaskMatcher.owned_by(fake_peer).enum_for(:each, plan).to_set, plan.task_index.by_owner)
25
+ end
26
+ end
27
+
28
+ def test_marshal_query
29
+ peer2peer(true) do |remote|
30
+ PeerServer.class_eval do
31
+ def query
32
+ plan.find_tasks
33
+ end
34
+ end
35
+ end
36
+
37
+ m_query = remote_peer.call(:query)
38
+ assert_kind_of(Query::DRoby, m_query)
39
+ query = remote_peer.local_object(m_query)
40
+ assert_kind_of(Query, query)
41
+ end
42
+
43
+ # Check that we can query the remote plan database
44
+ def test_query
45
+ peer2peer(true) do |remote|
46
+ local_model = Class.new(SimpleTask)
47
+
48
+ mission, subtask = Task.new(:id => 1), local_model.new(:id => 2)
49
+ mission.realized_by subtask
50
+ remote.plan.insert(mission)
51
+ end
52
+
53
+ # Get the remote missions
54
+ r_missions = remote_peer.find_tasks.mission.to_a
55
+ assert(r_missions.find { |t| t.arguments[:id] == 1 }, r_missions)
56
+
57
+ # Test queries
58
+ result = remote_peer.find_tasks.to_a
59
+ assert_equal(2, result.size)
60
+
61
+ result = remote_peer.find_tasks.
62
+ with_arguments(:id => 1).to_a
63
+ assert_equal(1, result.size)
64
+ assert_equal(1, result[0].arguments[:id])
65
+
66
+ result = remote_peer.find_tasks.
67
+ with_arguments(:id => 2).to_a
68
+ assert_equal(1, result.size)
69
+ assert(2, result[0].arguments[:id])
70
+
71
+ result = (TaskMatcher.with_arguments(:id => 1) | TaskMatcher.with_arguments(:id => 2)).enum_for(:each, remote_peer).to_a
72
+ assert_equal(2, result.size)
73
+
74
+ result = (TaskMatcher.with_arguments(:id => 1) & TaskMatcher.with_model(SimpleTask)).enum_for(:each, remote_peer).to_a
75
+ assert_equal(0, result.size)
76
+
77
+ result = TaskMatcher.with_arguments(:id => 1).negate.enum_for(:each, remote_peer).to_a
78
+ assert_equal(1, result.size, result)
79
+
80
+ result = remote_peer.find_tasks.
81
+ with_model(Roby::Task).to_a
82
+ assert_equal(2, result.size)
83
+
84
+ result = remote_peer.find_tasks.
85
+ with_model(SimpleTask).to_a
86
+ assert_equal(1, result.size)
87
+ assert(2, result[0].arguments[:id])
88
+
89
+ r_subtask = *remote_peer.find_tasks.
90
+ with_arguments(:id => 2).to_a
91
+ result = remote_peer.find_tasks.
92
+ with_model(r_subtask.model).to_a
93
+ assert_equal(1, result.size)
94
+ assert(2, result[0].arguments[:id])
95
+
96
+ assert_equal([], remote_peer.find_tasks.
97
+ self_owned.to_a)
98
+ assert_equal(2, remote_peer.find_tasks.
99
+ owned_by(remote_peer).to_a.size)
100
+ end
101
+ end
102
+
@@ -0,0 +1,491 @@
1
+ $LOAD_PATH.unshift File.expand_path('../..', File.dirname(__FILE__))
2
+ require 'roby/test/distributed'
3
+ require 'roby/test/tasks/simple_task'
4
+
5
+ # This testcase tests local views of remote plans
6
+ class TC_DistributedRemotePlan < Test::Unit::TestCase
7
+ include Roby::Distributed::Test
8
+
9
+ def test_distributed_update
10
+ objects = (1..10).map { |i| SimpleTask.new(:id => i) }
11
+ obj = Object.new
12
+
13
+ Distributed.update(obj) do
14
+ assert(Distributed.updating?(obj))
15
+ end
16
+ assert(!Distributed.updating?(obj))
17
+
18
+ Distributed.update_all(objects) do
19
+ objects.each { |o| assert(Distributed.updating?(o)) }
20
+ assert(Distributed.updating_all?(objects))
21
+ assert(!Distributed.updating?(obj))
22
+ assert(!Distributed.updating_all?(objects.dup << obj))
23
+ end
24
+ objects.each { |o| assert(!Distributed.updating?(o)) }
25
+ assert(!Distributed.updating_all?(objects))
26
+
27
+ # Recursive behaviour
28
+ Distributed.update(obj) do
29
+ Distributed.update(obj) do
30
+ assert(Distributed.updating?(obj))
31
+ end
32
+ assert(Distributed.updating?(obj))
33
+
34
+ Distributed.update_all(objects) do
35
+ objects.each { |o| assert(Distributed.updating?(o)) }
36
+ assert(Distributed.updating_all?(objects))
37
+ assert(Distributed.updating?(obj))
38
+ assert(Distributed.updating_all?(objects.dup << obj))
39
+ end
40
+ objects.each { |o| assert(!Distributed.updating?(o), o) }
41
+ assert(!Distributed.updating_all?(objects))
42
+ assert(!Distributed.updating_all?(objects.dup << obj))
43
+ assert(Distributed.updating?(obj))
44
+ end
45
+ assert(!Distributed.updating?(obj))
46
+
47
+ # Recursive behaviour
48
+ Distributed.update_all(objects) do
49
+ Distributed.update(obj) do
50
+ assert(Distributed.updating?(obj))
51
+ assert(Distributed.updating_all?(objects.dup << obj))
52
+ end
53
+ assert(!Distributed.updating?(obj))
54
+ assert(Distributed.updating_all?(objects))
55
+
56
+ Distributed.update_all(objects[1..4]) do
57
+ assert(Distributed.updating_all?(objects))
58
+ assert(!Distributed.updating?(obj))
59
+ end
60
+ assert(Distributed.updating_all?(objects))
61
+ assert(!Distributed.updating_all?(objects.dup << obj))
62
+ assert(!Distributed.updating?(obj))
63
+ end
64
+ assert(!Distributed.updating?(obj))
65
+ assert(!Distributed.updating_all?(objects))
66
+ end
67
+
68
+ def test_remote_proxy_update
69
+ peer2peer(true) do |remote|
70
+ remote.plan.insert(SimpleTask.new(:id => 'simple_task'))
71
+ remote.plan.permanent(SimpleTask.new(:id => 'task'))
72
+ remote.plan.permanent(SimpleTask.new(:id => 'other_task'))
73
+ end
74
+
75
+ r_simple_task = remote_task(:id => 'simple_task', :permanent => true)
76
+ r_task = remote_task(:id => 'task', :permanent => true)
77
+ r_other_task = remote_task(:id => 'other_task', :permanent => true)
78
+
79
+ task = SimpleTask.new
80
+ assert(!r_simple_task.read_write?, r_simple_task.plan)
81
+ Distributed.update(r_simple_task) do
82
+ assert(r_simple_task.read_write?)
83
+ assert_nothing_raised do
84
+ r_simple_task.realized_by task
85
+ r_simple_task.remove_child task
86
+ task.realized_by r_simple_task
87
+ task.remove_child r_simple_task
88
+ end
89
+ end
90
+ assert(!r_simple_task.read_write?)
91
+
92
+ assert_raises(OwnershipError) { r_simple_task.realized_by task }
93
+ assert_raises(OwnershipError) { task.realized_by r_simple_task }
94
+ Distributed.update(r_simple_task) { r_simple_task.realized_by task }
95
+ assert_nothing_raised { r_simple_task.remove_child task }
96
+ Distributed.update(r_simple_task) { task.realized_by r_simple_task }
97
+ assert_nothing_raised { task.remove_child r_simple_task }
98
+
99
+ assert_raises(OwnershipError) { r_simple_task.realized_by r_other_task }
100
+ assert_raises(OwnershipError) { r_other_task.realized_by r_simple_task }
101
+ Distributed.update_all([r_simple_task, r_other_task]) { r_simple_task.realized_by r_other_task }
102
+ assert_raises(OwnershipError) { r_simple_task.remove_child r_other_task }
103
+ Distributed.update_all([r_simple_task, r_other_task]) do
104
+ r_simple_task.remove_child r_other_task
105
+ r_other_task.realized_by r_simple_task
106
+ end
107
+ assert_raises(OwnershipError) { r_other_task.remove_child r_simple_task }
108
+
109
+ # Force a synchro point, or we will have a conflict between the remote
110
+ # GC process and the pending messages
111
+ #
112
+ # ... did I already told that distributed transactions were here for
113
+ # something ?
114
+ remote_peer.synchro_point
115
+ end
116
+
117
+ # Test that the remote plan structure is properly mapped to the local
118
+ # plan database
119
+ def test_discover_neighborhood
120
+ peer2peer(true) do |remote|
121
+ mission, subtask, next_mission =
122
+ SimpleTask.new(:id => 'mission'),
123
+ SimpleTask.new(:id => 'subtask'),
124
+ SimpleTask.new(:id => 'next_mission')
125
+ mission.realized_by subtask
126
+ mission.on(:stop, next_mission, :start)
127
+
128
+ remote.plan.insert(mission)
129
+ remote.plan.insert(next_mission)
130
+ end
131
+
132
+ r_mission = remote_task(:id => 'mission', :permanent => true)
133
+ r_subtask = remote_task(:id => 'subtask', :permanent => true)
134
+ r_next_mission = remote_task(:id => 'next_mission', :permanent => true)
135
+
136
+ # We don't know about the remote relations
137
+ assert_equal([], r_mission.children.to_a)
138
+ assert_equal([], r_mission.event(:stop).child_objects(EventStructure::Signal).to_a)
139
+
140
+ # Discover remote relations
141
+ remote_peer.discover_neighborhood(r_mission, 1) do |r_mission|
142
+ proxies = r_mission.children.to_a
143
+ assert_equal(1, proxies.to_a.size)
144
+ assert_equal(r_subtask, proxies.first)
145
+ proxies = r_mission.event(:stop).child_objects(EventStructure::Signal).to_a
146
+ assert_equal(r_next_mission.event(:start), proxies.first)
147
+ end
148
+
149
+ plan.auto(r_mission)
150
+ plan.auto(r_subtask)
151
+ plan.auto(r_next_mission)
152
+ Roby.control.wait_one_cycle
153
+ assert_equal([remote_peer.task], plan.keepalive.to_a)
154
+ end
155
+
156
+ def test_subscribing_old_objects
157
+ peer2peer(true) do |remote|
158
+ plan.insert(@task = SimpleTask.new(:id => 1))
159
+ end
160
+
161
+ r_task, r_task_id = nil
162
+ r_task = remote_task(:id => 1) do |t|
163
+ assert(r_task_id = t.remote_siblings[remote_peer])
164
+ t
165
+ end
166
+
167
+ process_events
168
+ assert(!r_task.plan)
169
+ assert_raises(Roby::RemotePeerMismatch) { remote_peer.subscribe(r_task) }
170
+ end
171
+
172
+ def test_subscription
173
+ peer2peer(true) do |remote|
174
+ root, mission, subtask, next_mission =
175
+ SimpleTask.new(:id => 'root'),
176
+ SimpleTask.new(:id => 'mission'),
177
+ SimpleTask.new(:id => 'subtask'),
178
+ SimpleTask.new(:id => 'next_mission')
179
+ root.realized_by mission
180
+ mission.realized_by subtask
181
+ mission.on(:stop, next_mission, :start)
182
+
183
+ remote.plan.permanent(subtask)
184
+ remote.plan.insert(root)
185
+ remote.plan.insert(mission)
186
+ remote.plan.insert(next_mission)
187
+
188
+ remote.singleton_class.class_eval do
189
+ include Test::Unit::Assertions
190
+ def check_local_updated(m_task)
191
+ task = local_peer.local_object(m_task)
192
+ sibling = nil
193
+ assert_nothing_raised { sibling = task.sibling_on(local_peer) }
194
+ assert(!task.subscribed?)
195
+ assert(task.updated?)
196
+ assert(task.update_on?(local_peer))
197
+ assert(task.updated_by?(local_peer))
198
+ assert_equal([local_peer], Distributed.enum_for(:each_updated_peer, task).to_a)
199
+ assert_equal([local_peer], task.updated_peers)
200
+ assert(task.remotely_useful?)
201
+ end
202
+
203
+ define_method(:remove_mission_subtask) do
204
+ mission.remove_child subtask
205
+ end
206
+ define_method(:add_mission_subtask) do
207
+ mission.realized_by subtask
208
+ end
209
+ end
210
+ end
211
+
212
+ r_root = subscribe_task(:id => 'root')
213
+ # Check that the task index has been updated
214
+ assert(plan.task_index.by_owner[remote_peer].include?(r_root))
215
+
216
+ # No need to explicitely synchronize here. #subscribe is supposed to return only
217
+ # when the subscription is completely done (i.e. the remote peer knows we have
218
+ # a sibling)
219
+ remote.check_local_updated(Distributed.format(r_root))
220
+ assert(r_root.subscribed?, remote_peer.subscriptions)
221
+ assert(r_root.updated_by?(remote_peer))
222
+ assert(r_root.update_on?(remote_peer))
223
+ assert_equal([remote_peer], r_root.updated_peers)
224
+ assert(!r_root.remotely_useful?)
225
+ assert_equal([remote_peer], Distributed.enum_for(:each_updated_peer, r_root).to_a)
226
+
227
+ assert(r_root.mission?)
228
+ r_mission = remote_task(:id => 'mission')
229
+ assert_equal([r_mission], r_root.children.to_a)
230
+ assert_equal([], r_mission.children.to_a)
231
+ assert_equal([], r_mission.event(:stop).child_objects(EventStructure::Signal).to_a)
232
+
233
+ assert(plan.useful_task?(r_mission))
234
+ r_next_mission = remote_task(:id => 'next_mission')
235
+ r_subtask = remote_task(:id => 'subtask')
236
+ Roby::Control.synchronize do
237
+ assert(!r_next_mission.plan || !plan.useful_task?(r_next_mission))
238
+ assert(!r_subtask.plan || !plan.useful_task?(r_subtask))
239
+ end
240
+ Roby.control.wait_one_cycle
241
+
242
+ # Check that the task index has been updated
243
+ assert(!plan.task_index.by_owner[remote_peer].include?(r_subtask))
244
+
245
+ # Check that subscribing again is handled nicely
246
+ assert_same(r_root, remote_peer.subscribe(r_root))
247
+ assert_same(r_mission, remote_peer.subscribe(r_mission))
248
+ r_subtask = remote_task(:id => 'subtask')
249
+ assert(!plan.unneeded_tasks.include?(r_subtask))
250
+ r_next_mission = remote_task(:id => 'next_mission')
251
+ Roby::Control.synchronize do
252
+ assert(!r_next_mission.plan || plan.unneeded_tasks.include?(r_next_mission))
253
+ end
254
+
255
+ assert_equal([r_subtask], r_mission.children.to_a)
256
+
257
+ ## Check plan GC after we have unsubscribed from mission
258
+ remote_peer.unsubscribe(r_mission)
259
+ Roby::Control.synchronize do
260
+ assert(r_mission.plan)
261
+ assert(!plan.unneeded_tasks.include?(r_mission))
262
+ assert(!remote_peer.subscribed?(r_mission))
263
+ assert(plan.unneeded_tasks.include?(r_subtask))
264
+ end
265
+ Roby.control.wait_one_cycle
266
+
267
+ # Check that subtask and next_mission are removed from the plan
268
+ assert(!r_subtask.plan)
269
+ assert_raises(RemotePeerMismatch) { r_subtask.sibling_on(remote_peer) }
270
+ subtask_proxy = remote_peer.proxies.find do |_, obj|
271
+ obj == r_subtask ||
272
+ (obj.root_object == r_subtask if obj.respond_to?(:root_object))
273
+ end
274
+ assert(!subtask_proxy)
275
+ assert_raises(RemotePeerMismatch) { r_next_mission.sibling_on(remote_peer) }
276
+ next_mission_proxy = remote_peer.proxies.find do |_, obj|
277
+ obj == r_next_mission ||
278
+ (obj.root_object == r_next_mission if obj.respond_to?(:root_object))
279
+ end
280
+ assert(!next_mission_proxy)
281
+ # Check that mission is still included, and is still linked to root
282
+ assert(r_mission.plan)
283
+ assert(r_root.child_object?(r_mission, TaskStructure::Hierarchy))
284
+
285
+ ## Check that #subscribe takes the plan modification into account
286
+ remote.remove_mission_subtask
287
+ assert_same(r_mission, remote_peer.subscribe(r_mission))
288
+ proxies = r_mission.children.to_a
289
+ assert(proxies.empty?, proxies)
290
+ assert(!plan.unneeded_tasks.include?(r_mission))
291
+ assert(!plan.unneeded_tasks.include?(r_mission.event(:stop)))
292
+
293
+ ## Re-add the child relation and test #unsubscribe
294
+ remote_peer.unsubscribe(r_mission)
295
+ process_events
296
+ remote.add_mission_subtask
297
+ process_events
298
+ assert(r_mission.plan)
299
+ assert(r_mission.leaf?(TaskStructure::Hierarchy))
300
+
301
+ r_mission = remote_peer.subscribe(r_mission)
302
+ r_subtask = remote_task(:id => 'subtask')
303
+ r_next_mission = remote_task(:id => 'next_mission')
304
+ Roby.control.wait_one_cycle
305
+
306
+ proxies = r_mission.children.to_a
307
+ assert(! proxies.empty?)
308
+ end
309
+
310
+ def test_remove_not_needed
311
+ peer2peer(true) do |remote|
312
+ left, right, middle =
313
+ SimpleTask.new(:id => 'left'),
314
+ SimpleTask.new(:id => 'right'),
315
+ SimpleTask.new(:id => 'middle')
316
+ remote.plan.insert(left)
317
+ remote.plan.insert(right)
318
+
319
+ left.realized_by middle
320
+ right.realized_by middle
321
+
322
+ remote.singleton_class.class_eval do
323
+ include Test::Unit::Assertions
324
+ define_method(:remove_last_link) do
325
+ assert(left.update_on?(local_peer))
326
+ assert(middle.update_on?(local_peer))
327
+ left.remove_child(middle)
328
+ nil
329
+ end
330
+ end
331
+ end
332
+
333
+ left = subscribe_task(:id => 'left')
334
+ right = subscribe_task(:id => 'right')
335
+ middle = remote_task(:id => 'middle')
336
+ assert(!middle.subscribed?)
337
+ assert(!plan.unneeded_tasks.include?(left))
338
+ assert(!plan.unneeded_tasks.include?(right))
339
+ assert(!plan.unneeded_tasks.include?(middle))
340
+
341
+ Roby::Control.synchronize do
342
+ remote_peer.unsubscribe(right)
343
+ assert(!right.remotely_useful?)
344
+ assert(!right.subscribed?)
345
+ assert(plan.unneeded_tasks.include?(right))
346
+ assert(!plan.unneeded_tasks.include?(middle))
347
+ end
348
+ process_events
349
+ assert(!right.plan)
350
+
351
+ assert(middle.plan)
352
+ assert_equal(1, middle.parent_objects(TaskStructure::Hierarchy).to_a.size)
353
+
354
+ remote.remove_last_link
355
+ process_events
356
+ assert(!middle.plan)
357
+ end
358
+
359
+ def test_data_update
360
+ peer2peer(true) do |remote|
361
+ task = SimpleTask.new(:id => 'task')
362
+ task.data = [4, 2]
363
+ remote.plan.insert(task)
364
+
365
+ remote.singleton_class.class_eval do
366
+ define_method(:change_data) { task.data = 42 }
367
+ end
368
+ end
369
+ task = subscribe_task(:id => 'task')
370
+ assert_equal([4, 2], task.data)
371
+
372
+ remote.change_data
373
+ process_events
374
+ assert_equal(42, task.data)
375
+ end
376
+
377
+ def test_mission_notifications
378
+ peer2peer(true) do |remote|
379
+ plan.insert(mission = SimpleTask.new(:id => 'mission'))
380
+
381
+ remote.class.class_eval do
382
+ define_method(:discard_mission) do
383
+ Roby::Control.synchronize do
384
+ remote.plan.discard(mission)
385
+ remote.plan.permanent(mission)
386
+ end
387
+ end
388
+ define_method(:insert_mission) do
389
+ Roby::Control.synchronize do
390
+ remote.plan.auto(mission)
391
+ remote.plan.insert(mission)
392
+ end
393
+ end
394
+ end
395
+ end
396
+ r_mission = subscribe_task(:id => 'mission')
397
+ assert(r_mission.mission?)
398
+ assert(!plan.mission?(r_mission))
399
+
400
+ remote.discard_mission
401
+ process_events
402
+ assert(!r_mission.mission?)
403
+ assert(!plan.mission?(r_mission))
404
+
405
+ remote.insert_mission
406
+ process_events
407
+ assert(r_mission.mission?)
408
+ assert(!plan.mission?(r_mission))
409
+ end
410
+
411
+ def test_relation_updates
412
+ peer2peer(true) do |remote|
413
+ mission, subtask, next_mission =
414
+ SimpleTask.new(:id => 'mission'),
415
+ SimpleTask.new(:id => 'subtask'),
416
+ SimpleTask.new(:id => 'next_mission')
417
+
418
+ remote.plan.insert(mission)
419
+ remote.plan.insert(next_mission)
420
+ remote.plan.permanent(subtask)
421
+
422
+ remote.singleton_class.class_eval do
423
+ define_method(:add_mission_subtask) do
424
+ mission.realized_by subtask
425
+ end
426
+ define_method(:remove_mission_subtask) do
427
+ mission.remove_child subtask
428
+ end
429
+ define_method(:add_mission_stop_next_start) do
430
+ mission.on(:stop, next_mission, :start)
431
+ end
432
+ define_method(:remove_mission_stop_next_start) do
433
+ mission.event(:stop).remove_signal(next_mission.event(:start))
434
+ end
435
+ end
436
+ end
437
+
438
+ r_mission = subscribe_task(:id => 'mission')
439
+ r_subtask = subscribe_task(:id => 'subtask')
440
+ r_next_mission = subscribe_task(:id => 'next_mission')
441
+
442
+ remote.add_mission_subtask
443
+ process_events
444
+ assert_equal([r_subtask], r_mission.children.to_a)
445
+
446
+ remote.remove_mission_subtask
447
+ process_events
448
+ assert(r_mission.leaf?(TaskStructure::Hierarchy))
449
+
450
+ remote.add_mission_stop_next_start
451
+ process_events
452
+ assert_equal([r_next_mission.event(:start)], r_mission.event(:stop).child_objects(EventStructure::Signal).to_a)
453
+
454
+ remote.remove_mission_stop_next_start
455
+ process_events
456
+ assert(r_mission.event(:stop).leaf?(EventStructure::Signal))
457
+ end
458
+
459
+ # Check that remote events that are unknown locally are properly ignored
460
+ def test_ignored_events
461
+ peer2peer(true) do |remote|
462
+ model = Class.new(SimpleTask) do
463
+ event :unknown, :command => true
464
+ end
465
+ remote.plan.insert(t1 = SimpleTask.new(:id => 1))
466
+ remote.plan.insert(t2 = SimpleTask.new(:id => 2))
467
+ remote.plan.insert(u = model.new(:id => 0))
468
+
469
+ t1.event(:start).on u.event(:unknown)
470
+ t2.event(:start).emit_on u.event(:unknown)
471
+
472
+ remote.singleton_class.class_eval do
473
+ define_method(:remove_relations) do
474
+ t1.event(:start).remove_signal u.event(:unknown)
475
+ u.event(:unknown).remove_forwarding t2.event(:start)
476
+ end
477
+ end
478
+ end
479
+
480
+ u = subscribe_task(:id => 0)
481
+ t1 = remote_task(:id => 1)
482
+ t2 = remote_task(:id => 2)
483
+
484
+ assert(remote_peer.connected?)
485
+
486
+ remote.remove_relations
487
+ assert_nothing_raised { process_events }
488
+ assert(remote_peer.connected?)
489
+ end
490
+ end
491
+