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.
- data/.gitignore +29 -0
- data/History.txt +4 -0
- data/License-fr.txt +519 -0
- data/License.txt +515 -0
- data/Manifest.txt +245 -0
- data/NOTES +4 -0
- data/README.txt +163 -0
- data/Rakefile +161 -0
- data/TODO.txt +146 -0
- data/app/README.txt +24 -0
- data/app/Rakefile +8 -0
- data/app/config/ROBOT.rb +5 -0
- data/app/config/app.yml +91 -0
- data/app/config/init.rb +7 -0
- data/app/config/roby.yml +3 -0
- data/app/controllers/.gitattributes +0 -0
- data/app/controllers/ROBOT.rb +2 -0
- data/app/data/.gitattributes +0 -0
- data/app/planners/ROBOT/main.rb +6 -0
- data/app/planners/main.rb +5 -0
- data/app/scripts/distributed +3 -0
- data/app/scripts/generate/bookmarks +3 -0
- data/app/scripts/replay +3 -0
- data/app/scripts/results +3 -0
- data/app/scripts/run +3 -0
- data/app/scripts/server +3 -0
- data/app/scripts/shell +3 -0
- data/app/scripts/test +3 -0
- data/app/tasks/.gitattributes +0 -0
- data/app/tasks/ROBOT/.gitattributes +0 -0
- data/bin/roby +210 -0
- data/bin/roby-log +168 -0
- data/bin/roby-shell +25 -0
- data/doc/images/event_generalization.png +0 -0
- data/doc/images/exception_propagation_1.png +0 -0
- data/doc/images/exception_propagation_2.png +0 -0
- data/doc/images/exception_propagation_3.png +0 -0
- data/doc/images/exception_propagation_4.png +0 -0
- data/doc/images/exception_propagation_5.png +0 -0
- data/doc/images/replay_handler_error.png +0 -0
- data/doc/images/replay_handler_error_0.png +0 -0
- data/doc/images/replay_handler_error_1.png +0 -0
- data/doc/images/roby_cycle_overview.png +0 -0
- data/doc/images/roby_replay_02.png +0 -0
- data/doc/images/roby_replay_03.png +0 -0
- data/doc/images/roby_replay_04.png +0 -0
- data/doc/images/roby_replay_event_representation.png +0 -0
- data/doc/images/roby_replay_first_state.png +0 -0
- data/doc/images/roby_replay_relations.png +0 -0
- data/doc/images/roby_replay_startup.png +0 -0
- data/doc/images/task_event_generalization.png +0 -0
- data/doc/papers.rdoc +11 -0
- data/doc/styles/allison.css +314 -0
- data/doc/styles/allison.js +316 -0
- data/doc/styles/allison.rb +276 -0
- data/doc/styles/jamis.rb +593 -0
- data/doc/tutorials/01-GettingStarted.rdoc +86 -0
- data/doc/tutorials/02-GoForward.rdoc +220 -0
- data/doc/tutorials/03-PlannedPath.rdoc +268 -0
- data/doc/tutorials/04-EventPropagation.rdoc +236 -0
- data/doc/tutorials/05-ErrorHandling.rdoc +319 -0
- data/doc/tutorials/06-Overview.rdoc +40 -0
- data/doc/videos.rdoc +69 -0
- data/ext/droby/dump.cc +175 -0
- data/ext/droby/extconf.rb +3 -0
- data/ext/graph/algorithm.cc +746 -0
- data/ext/graph/extconf.rb +7 -0
- data/ext/graph/graph.cc +529 -0
- data/ext/graph/graph.hh +183 -0
- data/ext/graph/iterator_sequence.hh +102 -0
- data/ext/graph/undirected_dfs.hh +226 -0
- data/ext/graph/undirected_graph.hh +421 -0
- data/lib/roby.rb +41 -0
- data/lib/roby/app.rb +870 -0
- data/lib/roby/app/rake.rb +56 -0
- data/lib/roby/app/run.rb +14 -0
- data/lib/roby/app/scripts/distributed.rb +13 -0
- data/lib/roby/app/scripts/generate/bookmarks.rb +162 -0
- data/lib/roby/app/scripts/replay.rb +31 -0
- data/lib/roby/app/scripts/results.rb +15 -0
- data/lib/roby/app/scripts/run.rb +26 -0
- data/lib/roby/app/scripts/server.rb +18 -0
- data/lib/roby/app/scripts/shell.rb +88 -0
- data/lib/roby/app/scripts/test.rb +40 -0
- data/lib/roby/basic_object.rb +151 -0
- data/lib/roby/config.rb +5 -0
- data/lib/roby/control.rb +747 -0
- data/lib/roby/decision_control.rb +17 -0
- data/lib/roby/distributed.rb +32 -0
- data/lib/roby/distributed/base.rb +440 -0
- data/lib/roby/distributed/communication.rb +871 -0
- data/lib/roby/distributed/connection_space.rb +592 -0
- data/lib/roby/distributed/distributed_object.rb +206 -0
- data/lib/roby/distributed/drb.rb +62 -0
- data/lib/roby/distributed/notifications.rb +539 -0
- data/lib/roby/distributed/peer.rb +550 -0
- data/lib/roby/distributed/protocol.rb +529 -0
- data/lib/roby/distributed/proxy.rb +343 -0
- data/lib/roby/distributed/subscription.rb +311 -0
- data/lib/roby/distributed/transaction.rb +498 -0
- data/lib/roby/event.rb +897 -0
- data/lib/roby/exceptions.rb +234 -0
- data/lib/roby/executives/simple.rb +30 -0
- data/lib/roby/graph.rb +166 -0
- data/lib/roby/interface.rb +390 -0
- data/lib/roby/log.rb +3 -0
- data/lib/roby/log/chronicle.rb +303 -0
- data/lib/roby/log/console.rb +72 -0
- data/lib/roby/log/data_stream.rb +197 -0
- data/lib/roby/log/dot.rb +279 -0
- data/lib/roby/log/event_stream.rb +151 -0
- data/lib/roby/log/file.rb +340 -0
- data/lib/roby/log/gui/basic_display.ui +83 -0
- data/lib/roby/log/gui/chronicle.rb +26 -0
- data/lib/roby/log/gui/chronicle_view.rb +40 -0
- data/lib/roby/log/gui/chronicle_view.ui +70 -0
- data/lib/roby/log/gui/data_displays.rb +172 -0
- data/lib/roby/log/gui/data_displays.ui +155 -0
- data/lib/roby/log/gui/notifications.rb +26 -0
- data/lib/roby/log/gui/relations.rb +248 -0
- data/lib/roby/log/gui/relations.ui +123 -0
- data/lib/roby/log/gui/relations_view.rb +185 -0
- data/lib/roby/log/gui/relations_view.ui +149 -0
- data/lib/roby/log/gui/replay.rb +327 -0
- data/lib/roby/log/gui/replay_controls.rb +200 -0
- data/lib/roby/log/gui/replay_controls.ui +259 -0
- data/lib/roby/log/gui/runtime.rb +130 -0
- data/lib/roby/log/hooks.rb +185 -0
- data/lib/roby/log/logger.rb +202 -0
- data/lib/roby/log/notifications.rb +244 -0
- data/lib/roby/log/plan_rebuilder.rb +470 -0
- data/lib/roby/log/relations.rb +1056 -0
- data/lib/roby/log/server.rb +550 -0
- data/lib/roby/log/sqlite.rb +47 -0
- data/lib/roby/log/timings.rb +164 -0
- data/lib/roby/plan-object.rb +247 -0
- data/lib/roby/plan.rb +762 -0
- data/lib/roby/planning.rb +13 -0
- data/lib/roby/planning/loops.rb +302 -0
- data/lib/roby/planning/model.rb +906 -0
- data/lib/roby/planning/task.rb +151 -0
- data/lib/roby/propagation.rb +562 -0
- data/lib/roby/query.rb +619 -0
- data/lib/roby/relations.rb +583 -0
- data/lib/roby/relations/conflicts.rb +70 -0
- data/lib/roby/relations/ensured.rb +20 -0
- data/lib/roby/relations/error_handling.rb +23 -0
- data/lib/roby/relations/events.rb +9 -0
- data/lib/roby/relations/executed_by.rb +193 -0
- data/lib/roby/relations/hierarchy.rb +239 -0
- data/lib/roby/relations/influence.rb +10 -0
- data/lib/roby/relations/planned_by.rb +63 -0
- data/lib/roby/robot.rb +7 -0
- data/lib/roby/standard_errors.rb +218 -0
- data/lib/roby/state.rb +5 -0
- data/lib/roby/state/events.rb +221 -0
- data/lib/roby/state/information.rb +55 -0
- data/lib/roby/state/pos.rb +110 -0
- data/lib/roby/state/shapes.rb +32 -0
- data/lib/roby/state/state.rb +353 -0
- data/lib/roby/support.rb +92 -0
- data/lib/roby/task-operations.rb +182 -0
- data/lib/roby/task.rb +1618 -0
- data/lib/roby/test/common.rb +399 -0
- data/lib/roby/test/distributed.rb +214 -0
- data/lib/roby/test/tasks/empty_task.rb +9 -0
- data/lib/roby/test/tasks/goto.rb +36 -0
- data/lib/roby/test/tasks/simple_task.rb +23 -0
- data/lib/roby/test/testcase.rb +519 -0
- data/lib/roby/test/tools.rb +160 -0
- data/lib/roby/thread_task.rb +87 -0
- data/lib/roby/transactions.rb +462 -0
- data/lib/roby/transactions/proxy.rb +292 -0
- data/lib/roby/transactions/updates.rb +139 -0
- data/plugins/fault_injection/History.txt +4 -0
- data/plugins/fault_injection/README.txt +37 -0
- data/plugins/fault_injection/Rakefile +18 -0
- data/plugins/fault_injection/TODO.txt +0 -0
- data/plugins/fault_injection/app.rb +52 -0
- data/plugins/fault_injection/fault_injection.rb +89 -0
- data/plugins/fault_injection/test/test_fault_injection.rb +84 -0
- data/plugins/subsystems/README.txt +40 -0
- data/plugins/subsystems/Rakefile +18 -0
- data/plugins/subsystems/app.rb +171 -0
- data/plugins/subsystems/test/app/README +24 -0
- data/plugins/subsystems/test/app/Rakefile +8 -0
- data/plugins/subsystems/test/app/config/app.yml +71 -0
- data/plugins/subsystems/test/app/config/init.rb +9 -0
- data/plugins/subsystems/test/app/config/roby.yml +3 -0
- data/plugins/subsystems/test/app/planners/main.rb +20 -0
- data/plugins/subsystems/test/app/scripts/distributed +3 -0
- data/plugins/subsystems/test/app/scripts/replay +3 -0
- data/plugins/subsystems/test/app/scripts/results +3 -0
- data/plugins/subsystems/test/app/scripts/run +3 -0
- data/plugins/subsystems/test/app/scripts/server +3 -0
- data/plugins/subsystems/test/app/scripts/shell +3 -0
- data/plugins/subsystems/test/app/scripts/test +3 -0
- data/plugins/subsystems/test/app/tasks/services.rb +15 -0
- data/plugins/subsystems/test/test_subsystems.rb +71 -0
- data/test/distributed/test_communication.rb +178 -0
- data/test/distributed/test_connection.rb +282 -0
- data/test/distributed/test_execution.rb +373 -0
- data/test/distributed/test_mixed_plan.rb +341 -0
- data/test/distributed/test_plan_notifications.rb +238 -0
- data/test/distributed/test_protocol.rb +516 -0
- data/test/distributed/test_query.rb +102 -0
- data/test/distributed/test_remote_plan.rb +491 -0
- data/test/distributed/test_transaction.rb +463 -0
- data/test/mockups/tasks.rb +27 -0
- data/test/planning/test_loops.rb +380 -0
- data/test/planning/test_model.rb +427 -0
- data/test/planning/test_task.rb +106 -0
- data/test/relations/test_conflicts.rb +42 -0
- data/test/relations/test_ensured.rb +38 -0
- data/test/relations/test_executed_by.rb +149 -0
- data/test/relations/test_hierarchy.rb +158 -0
- data/test/relations/test_planned_by.rb +54 -0
- data/test/suite_core.rb +24 -0
- data/test/suite_distributed.rb +9 -0
- data/test/suite_planning.rb +3 -0
- data/test/suite_relations.rb +8 -0
- data/test/test_bgl.rb +508 -0
- data/test/test_control.rb +399 -0
- data/test/test_event.rb +894 -0
- data/test/test_exceptions.rb +592 -0
- data/test/test_interface.rb +37 -0
- data/test/test_log.rb +114 -0
- data/test/test_log_server.rb +132 -0
- data/test/test_plan.rb +584 -0
- data/test/test_propagation.rb +210 -0
- data/test/test_query.rb +266 -0
- data/test/test_relations.rb +180 -0
- data/test/test_state.rb +414 -0
- data/test/test_support.rb +16 -0
- data/test/test_task.rb +938 -0
- data/test/test_testcase.rb +122 -0
- data/test/test_thread_task.rb +73 -0
- data/test/test_transactions.rb +569 -0
- data/test/test_transactions_proxy.rb +198 -0
- metadata +570 -0
|
@@ -0,0 +1,341 @@
|
|
|
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 buildings plans where local tasks are interacting with remote tasks
|
|
6
|
+
#
|
|
7
|
+
# Naming scheme:
|
|
8
|
+
# test_r<type of remote object>_realizes_l<type of local object>(_dynamic)
|
|
9
|
+
#
|
|
10
|
+
# For instance, test_rproxy_realizes_lproxy means that we are building a
|
|
11
|
+
# transaction where a remote transaction proxy is linked to a local transaction
|
|
12
|
+
# proxy. If _dynamic is appended, #propose is called when the transaction is still
|
|
13
|
+
# empty. Otherwise, #propose is called after all modifications have been put into
|
|
14
|
+
# the transaction (propose then build or build then propose)
|
|
15
|
+
#
|
|
16
|
+
# The transaction is always built locally
|
|
17
|
+
class TC_DistributedMixedPlan < Test::Unit::TestCase
|
|
18
|
+
include Roby::Distributed::Test
|
|
19
|
+
|
|
20
|
+
# Creates in +plan+ a task which is a child in a realized_by relation and a parent
|
|
21
|
+
# in a planned_by relation. All tasks have an ID of "#{name}-#{number}", with
|
|
22
|
+
# 2 for the central task, 1 for its parent task and 3 for its planning task.
|
|
23
|
+
#
|
|
24
|
+
# Returns [-1, -2, -3]
|
|
25
|
+
def add_tasks(plan, name)
|
|
26
|
+
t1, t2, t3 = (1..3).map { |i| SimpleTask.new(:id => "#{name}-#{i}") }
|
|
27
|
+
t1.realized_by t2
|
|
28
|
+
t2.planned_by t3
|
|
29
|
+
plan.insert(t1)
|
|
30
|
+
plan.discover(t2)
|
|
31
|
+
plan.discover(t3)
|
|
32
|
+
|
|
33
|
+
[t1, t2, t3]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def check_local_center_structure(node, removed_planner)
|
|
37
|
+
if node.respond_to?(:__getobj__)
|
|
38
|
+
assert_equal(["remote-2"], node.parents.map { |obj| obj.arguments[:id] })
|
|
39
|
+
else
|
|
40
|
+
assert_equal(["local-1", "remote-2"].to_set, node.parents.map { |obj| obj.arguments[:id] }.to_set)
|
|
41
|
+
unless removed_planner
|
|
42
|
+
assert_equal(["local-3"], node.enum_for(:each_planning_task).map { |obj| obj.arguments[:id] })
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def check_remote_center_structure(node, removed_planner)
|
|
48
|
+
assert_equal(["local-2"], node.children.map { |obj| obj.arguments[:id] })
|
|
49
|
+
unless node.respond_to?(:__getobj__)
|
|
50
|
+
assert_equal(["remote-1"], node.parents.map { |obj| obj.arguments[:id] })
|
|
51
|
+
unless removed_planner
|
|
52
|
+
assert_equal(["remote-3"], node.enum_for(:each_planning_task).map { |obj| obj.arguments[:id] })
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Checks that +plan+ has all 6 tasks with remote-2 and local-2 linked as
|
|
58
|
+
# expected +plan+ may be either the transaction or the plan
|
|
59
|
+
#
|
|
60
|
+
# Tests are in two parts: first we build the transaction and check the
|
|
61
|
+
# relations of the resulting proxies. Then, we remove all relations of
|
|
62
|
+
# tasks *in the plan*. Since we have added a realized_by between the
|
|
63
|
+
# central tasks, the realized_by relations are kept inside the transaction.
|
|
64
|
+
# However, this is not the case for planning relations. Thus, the planning
|
|
65
|
+
# relation does not exist anymore in the transaction after they have been
|
|
66
|
+
# removed from the plan.
|
|
67
|
+
def check_resulting_plan(plan, removed_planner)
|
|
68
|
+
assert(remote_center_node = plan.known_tasks.find { |t| t.arguments[:id] == "remote-2" }, plan.known_tasks)
|
|
69
|
+
check_remote_center_structure(remote_center_node, removed_planner)
|
|
70
|
+
assert(local_center_node = plan.known_tasks.find { |t| t.arguments[:id] == "local-2" }, plan.known_tasks)
|
|
71
|
+
check_local_center_structure(local_center_node, removed_planner)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def assert_cleared_relations(plan)
|
|
75
|
+
if remote_center_node = plan.known_tasks.find { |t| t.arguments[:id] == "remote-2" }
|
|
76
|
+
assert_equal([], remote_center_node.enum_for(:each_planning_task).to_a)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
if local_center_node = plan.known_tasks.find { |t| t.arguments[:id] == "local-2" }
|
|
80
|
+
assert_equal([], local_center_node.enum_for(:each_planning_task).to_a)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Common setup of the remote peer
|
|
85
|
+
def common_setup(propose_first)
|
|
86
|
+
peer2peer(true) do |remote|
|
|
87
|
+
testcase = self
|
|
88
|
+
remote.singleton_class.class_eval do
|
|
89
|
+
define_method(:add_tasks) do |plan|
|
|
90
|
+
plan = local_peer.proxy(plan)
|
|
91
|
+
plan.edit do
|
|
92
|
+
testcase.add_tasks(plan, "remote")
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
define_method(:check_resulting_plan) do |plan, removed_planner|
|
|
96
|
+
plan = local_peer.proxy(plan)
|
|
97
|
+
plan.edit do
|
|
98
|
+
testcase.check_resulting_plan(local_peer.proxy(plan), removed_planner)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
define_method(:assert_cleared_relations) do |plan|
|
|
102
|
+
plan = local_peer.proxy(plan)
|
|
103
|
+
plan.edit do
|
|
104
|
+
testcase.assert_cleared_relations(local_peer.proxy(plan))
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
def remove_relations(t2)
|
|
108
|
+
plan.edit do
|
|
109
|
+
t2 = local_peer.local_object(t2)
|
|
110
|
+
raise unless t3 = t2.planning_task
|
|
111
|
+
t2.remove_planning_task(t3)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
def subscribe(remote_task)
|
|
115
|
+
local_peer.subscribe(remote_task)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Create the transaction, and do the necessary modifications
|
|
121
|
+
trsc = Distributed::Transaction.new(plan, :conflict_solver => SolverIgnoreUpdate.new)
|
|
122
|
+
|
|
123
|
+
trsc.add_owner remote_peer
|
|
124
|
+
trsc.self_owned
|
|
125
|
+
trsc.propose(remote_peer) if propose_first
|
|
126
|
+
|
|
127
|
+
yield(trsc)
|
|
128
|
+
|
|
129
|
+
# Check the transaction is still valid, regardless of the
|
|
130
|
+
# changes we made to the plan
|
|
131
|
+
check_resulting_plan(trsc, true)
|
|
132
|
+
trsc.release(false)
|
|
133
|
+
remote.check_resulting_plan(Distributed.format(trsc), true)
|
|
134
|
+
trsc.edit
|
|
135
|
+
|
|
136
|
+
# Commit and check the result
|
|
137
|
+
trsc.commit_transaction
|
|
138
|
+
|
|
139
|
+
check_resulting_plan(plan, true)
|
|
140
|
+
remote.check_resulting_plan(Distributed.format(plan), true)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def test_rproxy_realizes_lproxy(propose_first = false)
|
|
144
|
+
common_setup(propose_first) do |trsc|
|
|
145
|
+
# First, add relations between two nodes that are already existing
|
|
146
|
+
remote.add_tasks(Distributed.format(plan))
|
|
147
|
+
r_t2 = subscribe_task(:id => 'remote-2')
|
|
148
|
+
assert(1, r_t2.parents.to_a.size)
|
|
149
|
+
r_t1 = r_t2.parents.find { true }
|
|
150
|
+
t1, t2, t3 = Control.synchronize { add_tasks(plan, "local") }
|
|
151
|
+
|
|
152
|
+
assert(plan.useful_task?(r_t1))
|
|
153
|
+
trsc[r_t2].realized_by trsc[t2]
|
|
154
|
+
trsc[r_t2].on(:start, trsc[t2])
|
|
155
|
+
assert(plan.useful_task?(r_t1))
|
|
156
|
+
check_resulting_plan(trsc, false)
|
|
157
|
+
if propose_first
|
|
158
|
+
trsc.release(false)
|
|
159
|
+
remote.check_resulting_plan(Distributed.format(trsc), false)
|
|
160
|
+
trsc.edit
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Remove the relations in the real tasks (not the proxies)
|
|
164
|
+
Control.synchronize do
|
|
165
|
+
t2.remove_planning_task(t3)
|
|
166
|
+
end
|
|
167
|
+
remote.remove_relations(Distributed.format(r_t2))
|
|
168
|
+
remote.subscribe(Distributed.format(t2))
|
|
169
|
+
|
|
170
|
+
process_events
|
|
171
|
+
assert(plan.useful_task?(r_t1))
|
|
172
|
+
assert_cleared_relations(plan)
|
|
173
|
+
|
|
174
|
+
unless propose_first
|
|
175
|
+
trsc.propose(remote_peer)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
process_events
|
|
179
|
+
remote.assert_cleared_relations(Distributed.format(plan))
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
def test_rproxy_realizes_lproxy_dynamic; test_rproxy_realizes_lproxy(true) end
|
|
183
|
+
|
|
184
|
+
def test_rproxy_realizes_ltask(propose_first = false)
|
|
185
|
+
common_setup(propose_first) do |trsc|
|
|
186
|
+
remote.add_tasks(Distributed.format(plan))
|
|
187
|
+
r_t2 = subscribe_task(:id => 'remote-2')
|
|
188
|
+
t1, t2, t3 = Control.synchronize { add_tasks(trsc, "local") }
|
|
189
|
+
|
|
190
|
+
trsc[r_t2].realized_by t2
|
|
191
|
+
trsc[r_t2].on(:start, t2)
|
|
192
|
+
check_resulting_plan(trsc, false)
|
|
193
|
+
process_events
|
|
194
|
+
if propose_first
|
|
195
|
+
remote_peer.push_subscription(t2)
|
|
196
|
+
trsc.release(false)
|
|
197
|
+
remote.check_resulting_plan(Distributed.format(trsc), false)
|
|
198
|
+
trsc.edit
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# remove the relations in the real tasks (not the proxies)
|
|
202
|
+
remote.remove_relations(Distributed.format(r_t2))
|
|
203
|
+
|
|
204
|
+
unless propose_first
|
|
205
|
+
trsc.propose(remote_peer)
|
|
206
|
+
remote_peer.push_subscription(t2)
|
|
207
|
+
end
|
|
208
|
+
process_events
|
|
209
|
+
remote.assert_cleared_relations(Distributed.format(plan))
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
def test_rproxy_realizes_ltask_dynamic; test_rproxy_realizes_ltask(true) end
|
|
213
|
+
|
|
214
|
+
# no non-dynamic version for that since we need the transactio to be
|
|
215
|
+
# present on both sides if we want to have remote tasks in it
|
|
216
|
+
def test_rtask_realizes_lproxy
|
|
217
|
+
common_setup(true) do |trsc|
|
|
218
|
+
trsc.release(false)
|
|
219
|
+
r_t1, r_t2, r_t3 = remote.add_tasks(Distributed.format(trsc)).map { |t| remote_peer.proxy(t) }
|
|
220
|
+
trsc.edit
|
|
221
|
+
|
|
222
|
+
assert(r_t2.subscribed?)
|
|
223
|
+
t1, t2, t3 = Control.synchronize { add_tasks(plan, "local") }
|
|
224
|
+
r_t2.realized_by trsc[t2]
|
|
225
|
+
r_t2.on(:start, trsc[t2])
|
|
226
|
+
remote_peer.subscribe(r_t2)
|
|
227
|
+
remote_peer.push_subscription(t2)
|
|
228
|
+
|
|
229
|
+
check_resulting_plan(trsc, false)
|
|
230
|
+
trsc.release(false)
|
|
231
|
+
remote.check_resulting_plan(Distributed.format(trsc), false)
|
|
232
|
+
trsc.edit
|
|
233
|
+
|
|
234
|
+
# remove the relations in the real tasks (not the proxies)
|
|
235
|
+
t2.remove_planning_task(t3)
|
|
236
|
+
process_events
|
|
237
|
+
remote.assert_cleared_relations(Distributed.format(plan))
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def test_garbage_collect
|
|
242
|
+
peer2peer(true) do |remote|
|
|
243
|
+
remote.plan.insert(SimpleTask.new(:id => 'remote-1'))
|
|
244
|
+
def remote.insert_children(trsc, root_task)
|
|
245
|
+
trsc = local_peer.local_object(trsc)
|
|
246
|
+
root_task = local_peer.local_object(root_task)
|
|
247
|
+
trsc.edit
|
|
248
|
+
|
|
249
|
+
root_task.realized_by(r2 = SimpleTask.new(:id => 'remote-2'))
|
|
250
|
+
r2.realized_by(r3 = SimpleTask.new(:id => 'remote-3'))
|
|
251
|
+
trsc.release(false)
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
r1 = subscribe_task(:id => 'remote-1')
|
|
256
|
+
assert(!plan.unneeded_tasks.include?(r1))
|
|
257
|
+
|
|
258
|
+
t1 = SimpleTask.new
|
|
259
|
+
|
|
260
|
+
# Add a local child to r1. This local child, and r1, must be kept event
|
|
261
|
+
# we are not subscribed to r1 anymore
|
|
262
|
+
trsc = Distributed::Transaction.new(plan)
|
|
263
|
+
trsc.add_owner(remote_peer)
|
|
264
|
+
trsc[r1].realized_by t1
|
|
265
|
+
Roby::Control.synchronize do
|
|
266
|
+
remote_peer.unsubscribe(r1)
|
|
267
|
+
assert(!plan.unneeded_tasks.include?(r1))
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
trsc.propose(remote_peer)
|
|
271
|
+
trsc.commit_transaction
|
|
272
|
+
assert(!plan.unneeded_tasks.include?(r1))
|
|
273
|
+
assert(!plan.unneeded_tasks.include?(t1), plan.unneeded_tasks)
|
|
274
|
+
|
|
275
|
+
# Ok, we now create a r1 => t1 => t2 => t3 chain
|
|
276
|
+
# * t2 and t3 are kept because they are useful for r1
|
|
277
|
+
t2, t3 = nil
|
|
278
|
+
Roby::Control.synchronize do
|
|
279
|
+
t1.realized_by(t2 = SimpleTask.new)
|
|
280
|
+
assert(!plan.unneeded_tasks.include?(t2))
|
|
281
|
+
t2.realized_by(t3 = SimpleTask.new)
|
|
282
|
+
assert(!plan.unneeded_tasks.include?(t3))
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
# Now, create a t3 => r2 => r3 chain
|
|
286
|
+
# * r2 should be kept since it is related to a task which is kept
|
|
287
|
+
# * r3 should not be kept
|
|
288
|
+
trsc = Distributed::Transaction.new(plan)
|
|
289
|
+
trsc.add_owner(remote_peer)
|
|
290
|
+
trsc.propose(remote_peer)
|
|
291
|
+
trsc.release
|
|
292
|
+
remote.insert_children(Distributed.format(trsc), Distributed.format(trsc[t3]))
|
|
293
|
+
trsc.edit
|
|
294
|
+
trsc.commit_transaction
|
|
295
|
+
process_events
|
|
296
|
+
|
|
297
|
+
r2 = remote_task(:id => 'remote-2')
|
|
298
|
+
Roby::Control.synchronize do
|
|
299
|
+
assert(r2.plan && !plan.unneeded_tasks.include?(r2))
|
|
300
|
+
assert(t3.child_object?(r2, TaskStructure::Hierarchy))
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
r3 = remote_task(:id => 'remote-3')
|
|
304
|
+
Roby::Control.synchronize do
|
|
305
|
+
assert(!r3.plan || plan.unneeded_tasks.include?(r3))
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# This tests that the race condition between transaction commit and plan GC
|
|
310
|
+
# is handled properly: if a task inside a transaction will be GCed just
|
|
311
|
+
# after the commit, there is a race condition possibility if the other
|
|
312
|
+
# peers do not have committed the transaction yet
|
|
313
|
+
def test_commit_race_condition
|
|
314
|
+
peer2peer(true) do |remote|
|
|
315
|
+
def remote.add_task(trsc)
|
|
316
|
+
trsc = local_peer.local_object(trsc)
|
|
317
|
+
trsc.edit
|
|
318
|
+
trsc.discover(SimpleTask.new(:id => 'remote'))
|
|
319
|
+
trsc.release(false)
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# Create an empty transaction and send it to our peer
|
|
324
|
+
# The peer will then discover a task, which
|
|
325
|
+
# will be GCed as soon as the transaction is committed
|
|
326
|
+
trsc = Distributed::Transaction.new(plan)
|
|
327
|
+
trsc.add_owner(remote_peer)
|
|
328
|
+
trsc.propose(remote_peer)
|
|
329
|
+
trsc.release
|
|
330
|
+
remote.add_task(Distributed.format(trsc))
|
|
331
|
+
trsc.edit
|
|
332
|
+
|
|
333
|
+
assert_nothing_raised do
|
|
334
|
+
trsc.commit_transaction
|
|
335
|
+
process_events
|
|
336
|
+
end
|
|
337
|
+
assert(remote_peer.connected?)
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
|
|
@@ -0,0 +1,238 @@
|
|
|
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_DistributedPlanNotifications < Test::Unit::TestCase
|
|
7
|
+
include Roby::Distributed::Test
|
|
8
|
+
|
|
9
|
+
def test_triggers
|
|
10
|
+
peer2peer(true) do |remote|
|
|
11
|
+
def remote.new_task(kind, args)
|
|
12
|
+
Roby.execute do
|
|
13
|
+
new_task = kind.proxy(local_peer).new(args)
|
|
14
|
+
yield(new_task.remote_id) if block_given?
|
|
15
|
+
plan.insert(new_task)
|
|
16
|
+
end
|
|
17
|
+
nil
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
notification = TaskMatcher.new.
|
|
22
|
+
with_model(SimpleTask).
|
|
23
|
+
with_arguments(:id => 2)
|
|
24
|
+
|
|
25
|
+
FlexMock.use do |mock|
|
|
26
|
+
remote_peer.on(notification) do |task|
|
|
27
|
+
assert(plan.useful_task?(task))
|
|
28
|
+
assert(task.plan)
|
|
29
|
+
mock.notified(task.sibling_on(remote_peer))
|
|
30
|
+
nil
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
simple_task = Distributed.format(SimpleTask)
|
|
34
|
+
roby_task = Distributed.format(Roby::Task)
|
|
35
|
+
|
|
36
|
+
remote.new_task(simple_task, :id => 3)
|
|
37
|
+
remote.new_task(roby_task, :id => 2)
|
|
38
|
+
remote.new_task(simple_task, :id => 2) do |inserted_id|
|
|
39
|
+
mock.should_receive(:notified).with(inserted_id).once.ordered
|
|
40
|
+
nil
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
remote.new_task(simple_task, :id => 3)
|
|
44
|
+
remote.new_task(roby_task, :id => 2)
|
|
45
|
+
remote.new_task(simple_task, :id => 2) do |inserted_id|
|
|
46
|
+
mock.should_receive(:notified).with(inserted_id).once.ordered
|
|
47
|
+
nil
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
remote_peer.synchro_point
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def test_trigger_subscribe
|
|
55
|
+
peer2peer(true) do |remote|
|
|
56
|
+
def remote.new_task
|
|
57
|
+
plan.insert(SimpleTask.new(:id => 1))
|
|
58
|
+
nil
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
notification = TaskMatcher.new.
|
|
63
|
+
with_model(SimpleTask).
|
|
64
|
+
with_arguments(:id => 1)
|
|
65
|
+
|
|
66
|
+
task = nil
|
|
67
|
+
remote_peer.on(notification) do |t|
|
|
68
|
+
remote_peer.subscribe(t)
|
|
69
|
+
task = t
|
|
70
|
+
end
|
|
71
|
+
remote.new_task
|
|
72
|
+
while !task
|
|
73
|
+
remote_peer.synchro_point
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
assert(task)
|
|
77
|
+
assert_equal([task], plan.find_tasks.with_arguments(:id => 1).to_a)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def test_subscribe_plan
|
|
81
|
+
peer2peer(true) do |remote|
|
|
82
|
+
plan.insert(mission = Task.new(:id => 'mission'))
|
|
83
|
+
subtask = Task.new :id => 'subtask'
|
|
84
|
+
plan.insert(next_mission = Task.new(:id => 'next_mission'))
|
|
85
|
+
mission.realized_by subtask
|
|
86
|
+
mission.on(:start, next_mission, :start)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Subscribe to the remote plan
|
|
90
|
+
remote_peer.subscribe_plan
|
|
91
|
+
assert(remote_peer.subscribed_plan?)
|
|
92
|
+
|
|
93
|
+
# Check that the remote plan has been mapped locally
|
|
94
|
+
process_events
|
|
95
|
+
tasks = plan.known_tasks
|
|
96
|
+
assert_equal(4, tasks.size)
|
|
97
|
+
assert(p_mission = tasks.find { |t| t.arguments[:id] == 'mission' })
|
|
98
|
+
assert(p_subtask = tasks.find { |t| t.arguments[:id] == 'subtask' })
|
|
99
|
+
assert(p_next_mission = tasks.find { |t| t.arguments[:id] == 'next_mission' })
|
|
100
|
+
|
|
101
|
+
assert(p_mission.child_object?(p_subtask, TaskStructure::Hierarchy))
|
|
102
|
+
assert(p_mission.event(:start).child_object?(p_next_mission.event(:start), EventStructure::Signal))
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def test_plan_updates
|
|
106
|
+
peer2peer(true) do |remote|
|
|
107
|
+
class << remote
|
|
108
|
+
attr_reader :mission, :subtask, :next_mission, :free_event
|
|
109
|
+
def create_mission
|
|
110
|
+
@mission = Roby::Task.new :id => 'mission'
|
|
111
|
+
plan.insert(mission)
|
|
112
|
+
end
|
|
113
|
+
def create_subtask
|
|
114
|
+
plan.permanent(@subtask = Roby::Task.new(:id => 'subtask'))
|
|
115
|
+
mission.realized_by subtask
|
|
116
|
+
end
|
|
117
|
+
def create_next_mission
|
|
118
|
+
@next_mission = Roby::Task.new :id => 'next_mission'
|
|
119
|
+
mission.on(:start, next_mission, :start)
|
|
120
|
+
plan.insert(next_mission)
|
|
121
|
+
end
|
|
122
|
+
def create_free_event
|
|
123
|
+
@free_event = Roby::EventGenerator.new(true)
|
|
124
|
+
# Link the event to a task to protect it from GC
|
|
125
|
+
@next_mission.on(:start, @free_event)
|
|
126
|
+
plan.discover(free_event)
|
|
127
|
+
end
|
|
128
|
+
def remove_free_event
|
|
129
|
+
plan.remove_object(free_event)
|
|
130
|
+
end
|
|
131
|
+
def unlink_next_mission; mission.event(:start).remove_signal(next_mission.event(:start)) end
|
|
132
|
+
def remove_next_mission; plan.remove_object(next_mission) end
|
|
133
|
+
def unlink_subtask; mission.remove_child(subtask) end
|
|
134
|
+
def remove_subtask; plan.remove_object(subtask) end
|
|
135
|
+
def discard_mission
|
|
136
|
+
plan.permanent(mission)
|
|
137
|
+
plan.discard(mission)
|
|
138
|
+
end
|
|
139
|
+
def remove_mission; plan.remove_object(mission) end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Subscribe to the remote plan
|
|
144
|
+
remote_peer.subscribe_plan
|
|
145
|
+
|
|
146
|
+
remote.create_mission
|
|
147
|
+
process_events
|
|
148
|
+
p_mission = remote_task(:id => 'mission')
|
|
149
|
+
# NOTE: the count is always remote_tasks + 1 since we have the ConnectionTask for our connection
|
|
150
|
+
assert_equal(2, plan.size, plan.known_tasks.to_a)
|
|
151
|
+
assert(p_mission.mission?)
|
|
152
|
+
process_events
|
|
153
|
+
assert(p_mission.plan)
|
|
154
|
+
|
|
155
|
+
remote.create_subtask
|
|
156
|
+
process_events
|
|
157
|
+
p_subtask = remote_task(:id => 'subtask')
|
|
158
|
+
assert_equal(3, plan.size)
|
|
159
|
+
assert(p_mission.child_object?(p_subtask, TaskStructure::Hierarchy))
|
|
160
|
+
|
|
161
|
+
remote.create_next_mission
|
|
162
|
+
process_events
|
|
163
|
+
p_next_mission = remote_task(:id => 'next_mission')
|
|
164
|
+
assert_equal(4, plan.size)
|
|
165
|
+
assert(p_mission.event(:start).child_object?(p_next_mission.event(:start), EventStructure::Signal))
|
|
166
|
+
|
|
167
|
+
remote.create_free_event
|
|
168
|
+
process_events
|
|
169
|
+
assert_equal(1, plan.free_events.size)
|
|
170
|
+
process_events
|
|
171
|
+
assert_equal(1, plan.free_events.size)
|
|
172
|
+
|
|
173
|
+
remote.remove_free_event
|
|
174
|
+
process_events
|
|
175
|
+
assert_equal(0, plan.free_events.size)
|
|
176
|
+
|
|
177
|
+
remote.unlink_next_mission
|
|
178
|
+
process_events
|
|
179
|
+
assert_equal(4, plan.size)
|
|
180
|
+
assert(!p_mission.event(:start).child_object?(p_next_mission.event(:start), EventStructure::Signal))
|
|
181
|
+
|
|
182
|
+
remote.remove_next_mission
|
|
183
|
+
process_events
|
|
184
|
+
assert_equal(3, plan.size)
|
|
185
|
+
assert(!p_next_mission.plan)
|
|
186
|
+
|
|
187
|
+
remote.unlink_subtask
|
|
188
|
+
assert(p_subtask.subscribed?)
|
|
189
|
+
process_events
|
|
190
|
+
assert_equal(3, plan.size, plan.known_tasks)
|
|
191
|
+
assert(!p_mission.child_object?(p_subtask, TaskStructure::Hierarchy))
|
|
192
|
+
|
|
193
|
+
remote.remove_subtask
|
|
194
|
+
process_events
|
|
195
|
+
assert_equal(2, plan.size)
|
|
196
|
+
assert(!p_subtask.plan)
|
|
197
|
+
|
|
198
|
+
remote.discard_mission
|
|
199
|
+
process_events
|
|
200
|
+
assert(!p_mission.mission?)
|
|
201
|
+
|
|
202
|
+
remote.remove_mission
|
|
203
|
+
process_events
|
|
204
|
+
assert_equal(1, plan.size)
|
|
205
|
+
assert(!p_mission.plan)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def test_unsubscribe_plan
|
|
209
|
+
peer2peer(true) do |remote|
|
|
210
|
+
remote.plan.insert(Task.new(:id => 'remote-1'))
|
|
211
|
+
remote.plan.insert(Task.new(:id => 'remote-2'))
|
|
212
|
+
|
|
213
|
+
def remote.new_task
|
|
214
|
+
plan.insert(Task.new(:id => 'remote-3'))
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
remote_peer.subscribe_plan
|
|
219
|
+
assert_equal(3, plan.size)
|
|
220
|
+
|
|
221
|
+
# Subscribe to the remote-1 task and unsubscribe to the plan
|
|
222
|
+
r1 = *plan.find_tasks.with_arguments(:id => 'remote-1').to_a
|
|
223
|
+
remote_peer.subscribe(r1)
|
|
224
|
+
|
|
225
|
+
remote_peer.unsubscribe_plan
|
|
226
|
+
assert(!remote_peer.subscribed_plan?)
|
|
227
|
+
|
|
228
|
+
# Start plan GC, the subscribed task should remain
|
|
229
|
+
process_events
|
|
230
|
+
assert_equal(2, plan.size, plan.known_tasks)
|
|
231
|
+
|
|
232
|
+
# Add a new task in the remote plan, check we do not get the updates
|
|
233
|
+
# anymore
|
|
234
|
+
remote.new_task
|
|
235
|
+
process_events
|
|
236
|
+
assert_equal(2, plan.size)
|
|
237
|
+
end
|
|
238
|
+
end
|