redshift 1.3.15
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 +8 -0
- data/README +5 -0
- data/RELEASE-NOTES +455 -0
- data/TODO +431 -0
- data/bench/alg-state.rb +61 -0
- data/bench/bench +26 -0
- data/bench/bench.rb +10 -0
- data/bench/continuous.rb +76 -0
- data/bench/diff-bench +86 -0
- data/bench/discrete.rb +101 -0
- data/bench/euler.rb +50 -0
- data/bench/formula.rb +78 -0
- data/bench/half-strict.rb +103 -0
- data/bench/inertness.rb +116 -0
- data/bench/queue.rb +92 -0
- data/bench/run +66 -0
- data/bench/simple.rb +74 -0
- data/bench/strictness.rb +86 -0
- data/examples/ball-tkar.rb +72 -0
- data/examples/ball.rb +123 -0
- data/examples/collide.rb +70 -0
- data/examples/connect-parallel.rb +48 -0
- data/examples/connect.rb +109 -0
- data/examples/constants.rb +27 -0
- data/examples/delay.rb +80 -0
- data/examples/derivative.rb +77 -0
- data/examples/euler.rb +46 -0
- data/examples/external-lib.rb +33 -0
- data/examples/guard-debugger.rb +77 -0
- data/examples/lotka-volterra.rb +33 -0
- data/examples/persist-ball.rb +68 -0
- data/examples/pid.rb +87 -0
- data/examples/ports.rb +60 -0
- data/examples/queue.rb +56 -0
- data/examples/queue2.rb +98 -0
- data/examples/reset-with-event-val.rb +28 -0
- data/examples/scheduler.rb +104 -0
- data/examples/set-dest.rb +23 -0
- data/examples/simulink/README +1 -0
- data/examples/simulink/delay.mdl +827 -0
- data/examples/simulink/derivative.mdl +655 -0
- data/examples/step-discrete-profiler.rb +103 -0
- data/examples/subsystem.rb +109 -0
- data/examples/sync-deadlock.rb +32 -0
- data/examples/sync-queue.rb +91 -0
- data/examples/sync-retry.rb +20 -0
- data/examples/sync.rb +51 -0
- data/examples/thermostat.rb +53 -0
- data/examples/zeno.rb +53 -0
- data/lib/accessible-index.rb +47 -0
- data/lib/redshift.rb +1 -0
- data/lib/redshift/component.rb +412 -0
- data/lib/redshift/meta.rb +183 -0
- data/lib/redshift/mixins/zeno-debugger.rb +69 -0
- data/lib/redshift/port.rb +57 -0
- data/lib/redshift/queue.rb +104 -0
- data/lib/redshift/redshift.rb +111 -0
- data/lib/redshift/state.rb +31 -0
- data/lib/redshift/syntax.rb +558 -0
- data/lib/redshift/target/c.rb +37 -0
- data/lib/redshift/target/c/component-gen.rb +1303 -0
- data/lib/redshift/target/c/flow-gen.rb +325 -0
- data/lib/redshift/target/c/flow/algebraic.rb +85 -0
- data/lib/redshift/target/c/flow/buffer.rb +74 -0
- data/lib/redshift/target/c/flow/delay.rb +203 -0
- data/lib/redshift/target/c/flow/derivative.rb +101 -0
- data/lib/redshift/target/c/flow/euler.rb +67 -0
- data/lib/redshift/target/c/flow/expr.rb +113 -0
- data/lib/redshift/target/c/flow/rk4.rb +80 -0
- data/lib/redshift/target/c/library.rb +85 -0
- data/lib/redshift/target/c/world-gen.rb +1370 -0
- data/lib/redshift/target/spec.rb +34 -0
- data/lib/redshift/world.rb +300 -0
- data/rakefile +37 -0
- data/test/test.rb +52 -0
- data/test/test_buffer.rb +58 -0
- data/test/test_connect.rb +242 -0
- data/test/test_connect_parallel.rb +47 -0
- data/test/test_connect_strict.rb +135 -0
- data/test/test_constant.rb +74 -0
- data/test/test_delay.rb +145 -0
- data/test/test_derivative.rb +48 -0
- data/test/test_discrete.rb +592 -0
- data/test/test_discrete_isolated.rb +92 -0
- data/test/test_exit.rb +59 -0
- data/test/test_flow.rb +200 -0
- data/test/test_flow_link.rb +288 -0
- data/test/test_flow_sub.rb +100 -0
- data/test/test_flow_trans.rb +292 -0
- data/test/test_inherit.rb +127 -0
- data/test/test_inherit_event.rb +74 -0
- data/test/test_inherit_flow.rb +139 -0
- data/test/test_inherit_link.rb +65 -0
- data/test/test_inherit_setup.rb +56 -0
- data/test/test_inherit_state.rb +66 -0
- data/test/test_inherit_transition.rb +168 -0
- data/test/test_numerics.rb +34 -0
- data/test/test_queue.rb +90 -0
- data/test/test_queue_alone.rb +115 -0
- data/test/test_reset.rb +209 -0
- data/test/test_setup.rb +119 -0
- data/test/test_strict_continuity.rb +410 -0
- data/test/test_strict_reset_error.rb +30 -0
- data/test/test_strictness_error.rb +32 -0
- data/test/test_sync.rb +185 -0
- data/test/test_world.rb +328 -0
- metadata +204 -0
@@ -0,0 +1,242 @@
|
|
1
|
+
require "redshift"
|
2
|
+
include RedShift
|
3
|
+
|
4
|
+
class A < Component
|
5
|
+
continuous :x
|
6
|
+
constant :k => 6.78
|
7
|
+
flow do
|
8
|
+
diff " x' = 2*x "
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class B < Component
|
13
|
+
input :y
|
14
|
+
input :yy # usually unused, so doesn't need to be connected
|
15
|
+
link :other => B
|
16
|
+
|
17
|
+
flow do
|
18
|
+
alg " z = y+1 "
|
19
|
+
alg " w = y + yy "
|
20
|
+
alg " u = other.y "
|
21
|
+
end
|
22
|
+
transition Enter => Exit do
|
23
|
+
guard "y < 0"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
#-----#
|
28
|
+
|
29
|
+
require 'test/unit'
|
30
|
+
|
31
|
+
class TestConnect < Test::Unit::TestCase
|
32
|
+
|
33
|
+
def setup
|
34
|
+
@world = World.new
|
35
|
+
@world.time_step = 0.1
|
36
|
+
@a = @world.create(A)
|
37
|
+
@b = @world.create(B)
|
38
|
+
end
|
39
|
+
|
40
|
+
def teardown
|
41
|
+
@world = nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_unconnected
|
45
|
+
assert_raises(UnconnectedInputError) do
|
46
|
+
@b.y
|
47
|
+
end
|
48
|
+
|
49
|
+
assert_raises(NoMethodError) do
|
50
|
+
@b.y = 1.23
|
51
|
+
end
|
52
|
+
|
53
|
+
assert_raises(UnconnectedInputError) do
|
54
|
+
@world.evolve 1
|
55
|
+
end
|
56
|
+
|
57
|
+
assert_raises(UnconnectedInputError) do
|
58
|
+
@b.z
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_not_connectable
|
63
|
+
assert_raises(TypeError) do
|
64
|
+
@a.port(:x) << @b.port(:y)
|
65
|
+
end
|
66
|
+
assert_raises(TypeError) do
|
67
|
+
@a.connect(:x, @b, :y)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_connect
|
72
|
+
@b.connect(:y, @a, :x)
|
73
|
+
@a.x = 4.56
|
74
|
+
|
75
|
+
assert_equal(@a.x, @b.y)
|
76
|
+
assert_equal(@b.y+1, @b.z)
|
77
|
+
@world.evolve 1 do
|
78
|
+
assert_equal(@a.x, @b.y)
|
79
|
+
assert_equal(@b.y+1, @b.z)
|
80
|
+
end
|
81
|
+
|
82
|
+
# reconnect to different var (actually a constant)
|
83
|
+
@b.connect(:y, @a, :k)
|
84
|
+
|
85
|
+
assert_equal(@a.k, @b.y)
|
86
|
+
assert_equal(@b.y+1, @b.z)
|
87
|
+
@world.evolve 1 do
|
88
|
+
assert_equal(@a.k, @b.y)
|
89
|
+
assert_equal(@b.y+1, @b.z)
|
90
|
+
end
|
91
|
+
|
92
|
+
# disconnect
|
93
|
+
@b.disconnect(:y)
|
94
|
+
|
95
|
+
assert_raises(UnconnectedInputError) do
|
96
|
+
@b.z
|
97
|
+
end
|
98
|
+
|
99
|
+
assert_raises(UnconnectedInputError) do
|
100
|
+
@b.y
|
101
|
+
end
|
102
|
+
|
103
|
+
# nothing raised:
|
104
|
+
@b.disconnect(:y)
|
105
|
+
|
106
|
+
# reconnect to different component (using ports)
|
107
|
+
@a = @world.create(A)
|
108
|
+
@b.port(:y) << @a.port(:x)
|
109
|
+
|
110
|
+
@a.x = 7.89
|
111
|
+
|
112
|
+
assert_equal(@a.x, @b.y)
|
113
|
+
assert_equal(@a.x, @b.port(:y).value)
|
114
|
+
assert_equal(@b.y+1, @b.z)
|
115
|
+
@world.evolve 1 do
|
116
|
+
assert_equal(@a.x, @b.y)
|
117
|
+
assert_equal(@b.y+1, @b.z)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_reflection
|
122
|
+
@b.connect(:y, @a, :x)
|
123
|
+
|
124
|
+
assert_equal(@a.port(:x), @b.port(:y).source)
|
125
|
+
assert_equal(@a, @b.port(:y).source_component)
|
126
|
+
assert_equal(:x, @b.port(:y).source_variable)
|
127
|
+
|
128
|
+
assert_equal(@a, @a.port(:x).component)
|
129
|
+
assert_equal(:x, @a.port(:x).variable)
|
130
|
+
end
|
131
|
+
|
132
|
+
def test_disconnect
|
133
|
+
@b.connect(:y, @a, :x)
|
134
|
+
@b.disconnect(:y)
|
135
|
+
assert_equal(nil, @b.port(:y).source)
|
136
|
+
|
137
|
+
@b.port(:y).disconnect
|
138
|
+
assert_equal(nil, @b.port(:y).source)
|
139
|
+
|
140
|
+
@b.port(:y) << nil
|
141
|
+
assert_equal(nil, @b.port(:y).source)
|
142
|
+
|
143
|
+
assert_raises(UnconnectedInputError) do
|
144
|
+
@b.y
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_connect_to_input
|
149
|
+
b2 = @world.create(B)
|
150
|
+
@b.port(:y) << @a.port(:x)
|
151
|
+
b2.port(:y) << @b.port(:y)
|
152
|
+
@a.x = 3.456
|
153
|
+
assert_equal(@b.y, b2.y)
|
154
|
+
assert_equal(@a.x, b2.y)
|
155
|
+
assert_equal(b2.y+1, b2.z)
|
156
|
+
end
|
157
|
+
|
158
|
+
def test_connect_input_chain
|
159
|
+
b = (0..99).map {@world.create(B)}
|
160
|
+
(0..98).each do |i|
|
161
|
+
b[i+1].port(:y) << b[i].port(:y)
|
162
|
+
end
|
163
|
+
b[0].port(:y) << @a.port(:x)
|
164
|
+
|
165
|
+
@a.x = 42.4242
|
166
|
+
assert_equal(@a.x, b[99].y)
|
167
|
+
assert_equal(@a.x, b[0].y)
|
168
|
+
|
169
|
+
old_b_50 = b[50]
|
170
|
+
b[50] = @world.create(B)
|
171
|
+
b[50].port(:y) << b[49].port(:y)
|
172
|
+
b[51].port(:y) << b[50].port(:y)
|
173
|
+
|
174
|
+
@a.x = 987.789
|
175
|
+
assert_equal(@a.x, b[99].y)
|
176
|
+
assert_equal(@a.x, b[0].y)
|
177
|
+
assert_equal(@a.x, old_b_50.y)
|
178
|
+
end
|
179
|
+
|
180
|
+
def make_circle n
|
181
|
+
b = (0...n).map {@world.create(B)}
|
182
|
+
n.times do |i|
|
183
|
+
b[i].port(:y) << b[(i+1)%n].port(:y)
|
184
|
+
end
|
185
|
+
b
|
186
|
+
end
|
187
|
+
|
188
|
+
def test_connect_input_circular
|
189
|
+
[1, 2, 3, 10].each do |n|
|
190
|
+
b = make_circle(n)
|
191
|
+
assert_raises(RedShift::CircularDefinitionError) do
|
192
|
+
b[0].y
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def test_connect_input_flow
|
198
|
+
b = (0..5).map {@world.create(B)}
|
199
|
+
(0..4).each do |i|
|
200
|
+
b[i+1].port(:y) << b[i].port(:y)
|
201
|
+
end
|
202
|
+
b[0].port(:y) << @a.port(:x)
|
203
|
+
|
204
|
+
@b.port(:y) << @a.port(:x) # ust so guard doesn't find y unconn.
|
205
|
+
|
206
|
+
@a.x = 1.0
|
207
|
+
assert_equal(@a.x, b[5].y)
|
208
|
+
assert_equal(@a.x, b[0].y)
|
209
|
+
|
210
|
+
@world.evolve 10
|
211
|
+
assert_equal(@a.x, b[5].y)
|
212
|
+
assert_equal(@a.x, b[0].y)
|
213
|
+
end
|
214
|
+
|
215
|
+
def test_connect_input_multiple
|
216
|
+
@b.port(:y) << @a.port(:x)
|
217
|
+
@b.port(:yy) << @b.port(:y) # Can connect to another var in self!
|
218
|
+
@a.x = 1.0
|
219
|
+
assert_equal(@a.x*2, @b.w)
|
220
|
+
end
|
221
|
+
|
222
|
+
def test_linked_input
|
223
|
+
@a.x = 99.876
|
224
|
+
|
225
|
+
@b.other = @world.create(B)
|
226
|
+
@b.other.port(:y) << @a.port(:k)
|
227
|
+
|
228
|
+
assert_equal(@b.other.y, @a.k)
|
229
|
+
assert_equal(@b.other.y, @b.u)
|
230
|
+
end
|
231
|
+
|
232
|
+
def test_marshal
|
233
|
+
@a.x = -3.21
|
234
|
+
@b.port(:y) << @a.port(:x)
|
235
|
+
assert_equal(@a.x, @b.y)
|
236
|
+
|
237
|
+
world2 = Marshal.load(Marshal.dump(@world))
|
238
|
+
a = world2.grep(A).first
|
239
|
+
b = world2.grep(B).first
|
240
|
+
assert_equal(a.x, b.y)
|
241
|
+
end
|
242
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require "redshift"
|
2
|
+
include RedShift
|
3
|
+
|
4
|
+
class A < Component
|
5
|
+
input :in
|
6
|
+
continuous :x => 1
|
7
|
+
link :comp
|
8
|
+
|
9
|
+
state :T
|
10
|
+
|
11
|
+
flow T do
|
12
|
+
diff " x' = in "
|
13
|
+
end
|
14
|
+
|
15
|
+
transition Enter => T do
|
16
|
+
reset :comp => nil
|
17
|
+
connect :in => proc {comp.port(:x)}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
#-----#
|
22
|
+
|
23
|
+
require 'test/unit'
|
24
|
+
|
25
|
+
class TestConnect < Test::Unit::TestCase
|
26
|
+
|
27
|
+
def setup
|
28
|
+
@world = World.new
|
29
|
+
@world.time_step = 0.001
|
30
|
+
@a0 = @world.create(A)
|
31
|
+
@a1 = @world.create(A)
|
32
|
+
@a0.comp = @a1
|
33
|
+
@a1.comp = @a0
|
34
|
+
end
|
35
|
+
|
36
|
+
def teardown
|
37
|
+
@world = nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_evolve
|
41
|
+
@world.evolve 1
|
42
|
+
[@a0, @a1].each do |a|
|
43
|
+
assert_in_delta(Math::E, a.in, 1.0e-12)
|
44
|
+
assert_equal(nil, a.comp)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require "redshift"
|
2
|
+
include RedShift
|
3
|
+
|
4
|
+
class A < Component
|
5
|
+
continuous :ns_x
|
6
|
+
strictly_continuous :x
|
7
|
+
|
8
|
+
constant :ns_k
|
9
|
+
strictly_constant :k => 6.78
|
10
|
+
|
11
|
+
flow do
|
12
|
+
diff " x' = 2*x "
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class B < Component
|
17
|
+
strict_input :y
|
18
|
+
transition Enter => Exit do
|
19
|
+
name "t"
|
20
|
+
guard "y > 5"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Dummy < Component
|
25
|
+
state :S, :T, :U
|
26
|
+
setup do
|
27
|
+
start S
|
28
|
+
end
|
29
|
+
|
30
|
+
flow S do
|
31
|
+
diff "time' = 1"
|
32
|
+
end
|
33
|
+
|
34
|
+
transition S => T do
|
35
|
+
guard "time > 0"
|
36
|
+
reset :time => 0 # so we do it again next timestep
|
37
|
+
end
|
38
|
+
|
39
|
+
transition T => U, U => S
|
40
|
+
end
|
41
|
+
|
42
|
+
# From test_strict_continuity.rb
|
43
|
+
class SCWorld < World
|
44
|
+
def num_checks
|
45
|
+
@num_checks ||= Hash.new do |h,comp|
|
46
|
+
h[comp] = Hash.new do |h1,trans|
|
47
|
+
h1[trans] = 0
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def hook_eval_guard(comp, guard, enabled, trans, dest)
|
53
|
+
num_checks[comp][trans.name] += 1
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
#-----#
|
58
|
+
|
59
|
+
require 'test/unit'
|
60
|
+
|
61
|
+
class TestConnectStrict < Test::Unit::TestCase
|
62
|
+
|
63
|
+
def setup
|
64
|
+
@world = SCWorld.new
|
65
|
+
@world.time_step = 0.1
|
66
|
+
@a = @world.create(A) {|a| a.x = 1 }
|
67
|
+
@b = @world.create(B)
|
68
|
+
@world.create(Dummy)
|
69
|
+
end
|
70
|
+
|
71
|
+
def teardown
|
72
|
+
@world = nil
|
73
|
+
end
|
74
|
+
|
75
|
+
# can connect strict_input *only* to strict var/const
|
76
|
+
def test_not_connectable
|
77
|
+
assert_raises(StrictnessError) do
|
78
|
+
@b.port(:y) << @a.port(:ns_x)
|
79
|
+
end
|
80
|
+
assert_raises(StrictnessError) do
|
81
|
+
@b.port(:y) << @a.port(:ns_k)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# can't disconnect strict_input that has been connected
|
86
|
+
def test_disconnect
|
87
|
+
@b.port(:y) << nil # ok
|
88
|
+
@b.port(:y) << @a.port(:x)
|
89
|
+
assert_raises(StrictnessError) do
|
90
|
+
@b.port(:y) << nil
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# can't connect to a different component
|
95
|
+
def test_reconnect_comp
|
96
|
+
@b.port(:y) << @a.port(:x)
|
97
|
+
@b.port(:y) << @a.port(:x) # ok
|
98
|
+
assert_raises(StrictnessError) do
|
99
|
+
@b.port(:y) << @world.create(A).port(:x)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# can't connect to a different var in same component
|
104
|
+
def test_reconnect_var
|
105
|
+
@b.port(:y) << @a.port(:x)
|
106
|
+
assert_raises(StrictnessError) do
|
107
|
+
@b.port(:y) << @a.port(:k)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# can reconnect if value is the same
|
112
|
+
def test_reconnect_same_value
|
113
|
+
a = @world.create(A) do |a|
|
114
|
+
a.x = a.k = 12.34
|
115
|
+
end
|
116
|
+
@b.port(:y) << a.port(:x)
|
117
|
+
assert_nothing_raised do
|
118
|
+
@b.port(:y) << a.port(:k)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# One check per step, despite the Dummy
|
123
|
+
def test_strict_guard
|
124
|
+
@b.port(:y) << @a.port(:x)
|
125
|
+
prev = nil
|
126
|
+
@world.evolve 2 do
|
127
|
+
if @b.state == Enter
|
128
|
+
if prev
|
129
|
+
assert_equal(prev + 1, @world.num_checks[@b]["t"])
|
130
|
+
end
|
131
|
+
prev = @world.num_checks[@b]["t"]
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'redshift'
|
4
|
+
|
5
|
+
include RedShift
|
6
|
+
|
7
|
+
class ConstantTestComponent < Component
|
8
|
+
def finish test
|
9
|
+
end
|
10
|
+
|
11
|
+
constant :k
|
12
|
+
strictly_constant :ks
|
13
|
+
|
14
|
+
link :other => ConstantTestComponent
|
15
|
+
|
16
|
+
state :HasOther, :Stop
|
17
|
+
|
18
|
+
transition Enter => HasOther do
|
19
|
+
guard "other"
|
20
|
+
end
|
21
|
+
|
22
|
+
flow HasOther do
|
23
|
+
diff "x' = other.k + k + other.ks + ks"
|
24
|
+
diff "t' = 1"
|
25
|
+
end
|
26
|
+
|
27
|
+
transition HasOther => Stop do
|
28
|
+
guard "t >= 1"
|
29
|
+
action do
|
30
|
+
@y = other.k + k + other.ks + ks
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def assert_consistent test
|
35
|
+
if t >= 1
|
36
|
+
test.assert_in_delta(44, x, 1E-10)
|
37
|
+
test.assert_equal(44, @y)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
#-----#
|
43
|
+
|
44
|
+
require 'test/unit'
|
45
|
+
|
46
|
+
class TestConstant < Test::Unit::TestCase
|
47
|
+
|
48
|
+
def setup
|
49
|
+
@world = World.new
|
50
|
+
end
|
51
|
+
|
52
|
+
def teardown
|
53
|
+
@world = nil
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_constant
|
57
|
+
comp1 = @world.create(ConstantTestComponent) do |c1|
|
58
|
+
c1.k = 1.0
|
59
|
+
c1.ks = 10.0
|
60
|
+
end
|
61
|
+
|
62
|
+
comp2 = @world.create(ConstantTestComponent) do |c2|
|
63
|
+
c2.k = 3.0
|
64
|
+
c2.ks = 30.0
|
65
|
+
end
|
66
|
+
|
67
|
+
comp1.other = comp2
|
68
|
+
testers = [comp1, comp2]
|
69
|
+
|
70
|
+
@world.run 20
|
71
|
+
comp1.assert_consistent self
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|