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