mutant 0.6.7 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Changelog.md +10 -0
- data/README.md +1 -1
- data/config/flay.yml +1 -1
- data/config/reek.yml +11 -40
- data/config/rubocop.yml +1 -1
- data/lib/mutant.rb +10 -2
- data/lib/mutant/actor.rb +64 -0
- data/lib/mutant/actor/actor.rb +50 -0
- data/lib/mutant/actor/env.rb +35 -0
- data/lib/mutant/actor/mailbox.rb +53 -0
- data/lib/mutant/actor/receiver.rb +48 -0
- data/lib/mutant/actor/sender.rb +27 -0
- data/lib/mutant/ast/types.rb +1 -1
- data/lib/mutant/cli.rb +2 -0
- data/lib/mutant/config.rb +2 -1
- data/lib/mutant/env.rb +6 -2
- data/lib/mutant/expression/methods.rb +1 -1
- data/lib/mutant/integration.rb +11 -1
- data/lib/mutant/isolation.rb +1 -2
- data/lib/mutant/meta/example.rb +1 -1
- data/lib/mutant/mutation.rb +47 -21
- data/lib/mutant/mutator/node.rb +1 -1
- data/lib/mutant/mutator/node/literal/symbol.rb +1 -1
- data/lib/mutant/mutator/node/send.rb +4 -3
- data/lib/mutant/reporter/cli.rb +2 -0
- data/lib/mutant/reporter/cli/format.rb +23 -36
- data/lib/mutant/reporter/cli/printer.rb +66 -27
- data/lib/mutant/result.rb +45 -58
- data/lib/mutant/runner.rb +47 -154
- data/lib/mutant/runner/master.rb +174 -0
- data/lib/mutant/runner/scheduler.rb +141 -0
- data/lib/mutant/runner/worker.rb +93 -0
- data/lib/mutant/subject/method/instance.rb +1 -15
- data/lib/mutant/test.rb +2 -15
- data/lib/mutant/version.rb +1 -1
- data/meta/send.rb +16 -0
- data/mutant-rspec.gemspec +1 -1
- data/mutant.gemspec +1 -1
- data/spec/integration/mutant/rspec_spec.rb +0 -6
- data/spec/spec_helper.rb +9 -1
- data/spec/support/fake_actor.rb +93 -0
- data/spec/support/shared_context.rb +135 -0
- data/spec/unit/mutant/actor/actor_spec.rb +35 -0
- data/spec/unit/mutant/actor/binding_spec.rb +32 -0
- data/spec/unit/mutant/actor/env_spec.rb +49 -0
- data/spec/unit/mutant/actor/message_spec.rb +23 -0
- data/spec/unit/mutant/actor/receiver_spec.rb +60 -0
- data/spec/unit/mutant/actor/sender_spec.rb +22 -0
- data/spec/unit/mutant/cli_spec.rb +17 -4
- data/spec/unit/mutant/env_spec.rb +2 -2
- data/spec/unit/mutant/mailbox_spec.rb +33 -0
- data/spec/unit/mutant/mutation_spec.rb +52 -18
- data/spec/unit/mutant/mutator/registry_spec.rb +4 -4
- data/spec/unit/mutant/reporter/cli_spec.rb +131 -249
- data/spec/unit/mutant/result/env_spec.rb +55 -0
- data/spec/unit/mutant/result/subject_spec.rb +43 -0
- data/spec/unit/mutant/runner/master_spec.rb +199 -0
- data/spec/unit/mutant/runner/scheduler_spec.rb +161 -0
- data/spec/unit/mutant/runner/worker_spec.rb +73 -0
- data/spec/unit/mutant/runner_spec.rb +60 -118
- data/spec/unit/mutant/subject/method/instance_spec.rb +18 -31
- data/spec/unit/mutant/warning_filter_spec.rb +1 -1
- metadata +39 -14
- data/lib/mutant/runner/collector.rb +0 -133
- data/lib/mutant/warning_expectation.rb +0 -47
- data/spec/unit/mutant/runner/collector_spec.rb +0 -198
- data/spec/unit/mutant/test_spec.rb +0 -23
- data/spec/unit/mutant/warning_expectation_spec.rb +0 -80
- data/test_app/Gemfile.rspec2 +0 -6
data/lib/mutant/result.rb
CHANGED
@@ -54,30 +54,8 @@ module Mutant
|
|
54
54
|
end
|
55
55
|
memoize name
|
56
56
|
end
|
57
|
-
|
58
|
-
# Compute result tracking runtime
|
59
|
-
#
|
60
|
-
# @return [Result]
|
61
|
-
#
|
62
|
-
# @api private
|
63
|
-
#
|
64
|
-
def compute
|
65
|
-
start = Time.now
|
66
|
-
new(yield.merge(runtime: Time.now - start))
|
67
|
-
end
|
68
|
-
|
69
57
|
end # ClassMethods
|
70
58
|
|
71
|
-
# Test if operation is failing
|
72
|
-
#
|
73
|
-
# @return [Boolean]
|
74
|
-
#
|
75
|
-
# @api private
|
76
|
-
#
|
77
|
-
def fail?
|
78
|
-
!success?
|
79
|
-
end
|
80
|
-
|
81
59
|
# Return overhead
|
82
60
|
#
|
83
61
|
# @return [Float]
|
@@ -98,7 +76,7 @@ module Mutant
|
|
98
76
|
#
|
99
77
|
def self.included(host)
|
100
78
|
host.class_eval do
|
101
|
-
include Adamantium
|
79
|
+
include Adamantium, Anima::Update
|
102
80
|
extend ClassMethods
|
103
81
|
end
|
104
82
|
end
|
@@ -120,6 +98,16 @@ module Mutant
|
|
120
98
|
end
|
121
99
|
memoize :success?
|
122
100
|
|
101
|
+
# Test if runner should continue on env
|
102
|
+
#
|
103
|
+
# @return [Boolean]
|
104
|
+
#
|
105
|
+
# @api private
|
106
|
+
#
|
107
|
+
def continue?
|
108
|
+
!env.config.fail_fast || subject_results.all?(&:success?)
|
109
|
+
end
|
110
|
+
|
123
111
|
# Return failed subject results
|
124
112
|
#
|
125
113
|
# @return [Array<Result::Subject>]
|
@@ -130,12 +118,21 @@ module Mutant
|
|
130
118
|
subject_results.reject(&:success?)
|
131
119
|
end
|
132
120
|
|
133
|
-
sum :amount_mutations, :subject_results
|
134
121
|
sum :amount_mutation_results, :subject_results
|
135
122
|
sum :amount_mutations_alive, :subject_results
|
136
123
|
sum :amount_mutations_killed, :subject_results
|
137
124
|
sum :killtime, :subject_results
|
138
125
|
|
126
|
+
# Return amount of mutations
|
127
|
+
#
|
128
|
+
# @return [Fixnum]
|
129
|
+
#
|
130
|
+
# @api private
|
131
|
+
#
|
132
|
+
def amount_mutations
|
133
|
+
env.mutations.length
|
134
|
+
end
|
135
|
+
|
139
136
|
# Return amount of subjects
|
140
137
|
#
|
141
138
|
# @return [Fixnum]
|
@@ -151,38 +148,19 @@ module Mutant
|
|
151
148
|
# Test result
|
152
149
|
class Test
|
153
150
|
include Result, Anima.new(
|
154
|
-
:
|
151
|
+
:tests,
|
155
152
|
:output,
|
156
|
-
:mutation,
|
157
153
|
:passed,
|
158
154
|
:runtime
|
159
155
|
)
|
160
|
-
|
161
|
-
# Return killtime
|
162
|
-
#
|
163
|
-
# @return [Float]
|
164
|
-
#
|
165
|
-
# @api private
|
166
|
-
#
|
167
|
-
alias_method :killtime, :runtime
|
168
|
-
|
169
|
-
# Test if mutation test result is successful
|
170
|
-
#
|
171
|
-
# @return [Boolean]
|
172
|
-
#
|
173
|
-
# @api private
|
174
|
-
#
|
175
|
-
def success?
|
176
|
-
mutation.killed_by?(self)
|
177
|
-
end
|
178
|
-
|
179
156
|
end # Test
|
180
157
|
|
181
158
|
# Subject result
|
182
159
|
class Subject
|
183
|
-
include Coverage, Result, Anima.new(:subject, :mutation_results
|
160
|
+
include Coverage, Result, Anima.new(:subject, :mutation_results)
|
184
161
|
|
185
162
|
sum :killtime, :mutation_results
|
163
|
+
sum :runtime, :mutation_results
|
186
164
|
|
187
165
|
# Test if subject was processed successful
|
188
166
|
#
|
@@ -194,6 +172,16 @@ module Mutant
|
|
194
172
|
alive_mutation_results.empty?
|
195
173
|
end
|
196
174
|
|
175
|
+
# Test if runner should continue on subject
|
176
|
+
#
|
177
|
+
# @return [Boolean]
|
178
|
+
#
|
179
|
+
# @api private
|
180
|
+
#
|
181
|
+
def continue?
|
182
|
+
mutation_results.all?(&:success?)
|
183
|
+
end
|
184
|
+
|
197
185
|
# Return killed mutations
|
198
186
|
#
|
199
187
|
# @return [Array<Result::Mutation>]
|
@@ -260,31 +248,30 @@ module Mutant
|
|
260
248
|
|
261
249
|
# Mutation result
|
262
250
|
class Mutation
|
263
|
-
include Result, Anima.new(:
|
251
|
+
include Result, Anima.new(:mutation, :test_result, :index)
|
264
252
|
|
265
|
-
#
|
253
|
+
# Return runtime
|
266
254
|
#
|
267
|
-
# @return [
|
255
|
+
# @return [Float]
|
268
256
|
#
|
269
257
|
# @api private
|
270
258
|
#
|
271
|
-
def
|
272
|
-
|
259
|
+
def runtime
|
260
|
+
test_result.runtime
|
273
261
|
end
|
274
262
|
|
275
|
-
|
263
|
+
alias_method :killtime, :runtime
|
264
|
+
|
265
|
+
# Test if mutation was handled successfully
|
276
266
|
#
|
277
|
-
# @return [
|
267
|
+
# @return [Boolean]
|
278
268
|
#
|
279
269
|
# @api private
|
280
270
|
#
|
281
|
-
def
|
282
|
-
|
271
|
+
def success?
|
272
|
+
mutation.class.success?(test_result)
|
283
273
|
end
|
284
274
|
|
285
|
-
sum :killtime, :test_results
|
286
|
-
|
287
275
|
end # Mutation
|
288
|
-
|
289
276
|
end # Result
|
290
277
|
end # Mutant
|
data/lib/mutant/runner.rb
CHANGED
@@ -3,163 +3,78 @@ module Mutant
|
|
3
3
|
class Runner
|
4
4
|
include Adamantium::Flat, Concord.new(:env), Procto.call(:result)
|
5
5
|
|
6
|
+
# Status of the runner execution
|
7
|
+
class Status
|
8
|
+
include Adamantium, Anima::Update, Anima.new(
|
9
|
+
:env_result,
|
10
|
+
:active_jobs,
|
11
|
+
:done
|
12
|
+
)
|
13
|
+
end # Status
|
14
|
+
|
15
|
+
# Job to push to workers
|
16
|
+
class Job
|
17
|
+
include Adamantium::Flat, Anima.new(:index, :mutation)
|
18
|
+
end # Job
|
19
|
+
|
20
|
+
# Job result object received from workers
|
21
|
+
class JobResult
|
22
|
+
include Adamantium::Flat, Anima.new(:job, :result)
|
23
|
+
end
|
24
|
+
|
25
|
+
REPORT_FREQUENCY = 20.0
|
26
|
+
REPORT_DELAY = 1 / REPORT_FREQUENCY
|
27
|
+
|
6
28
|
# Initialize object
|
7
29
|
#
|
8
30
|
# @return [undefined]
|
9
31
|
#
|
10
32
|
# @api private
|
11
33
|
#
|
12
|
-
def initialize(
|
34
|
+
def initialize(*)
|
13
35
|
super
|
14
36
|
|
15
|
-
@collector = Collector.new(env)
|
16
|
-
@mutex = Mutex.new
|
17
|
-
@mutations = env.mutations.dup
|
18
|
-
@index = 0
|
19
|
-
@continue = true
|
20
|
-
|
21
|
-
config.integration.setup
|
22
|
-
|
23
37
|
reporter.start(env)
|
38
|
+
config.integration.setup
|
24
39
|
|
25
|
-
|
26
|
-
|
27
|
-
@result = @collector.result
|
28
|
-
|
29
|
-
reporter.report(result)
|
30
|
-
end
|
31
|
-
|
32
|
-
# Return result
|
33
|
-
#
|
34
|
-
# @return [Result::Env]
|
35
|
-
#
|
36
|
-
# @api private
|
37
|
-
#
|
38
|
-
attr_reader :result
|
39
|
-
|
40
|
-
private
|
40
|
+
@master = config.actor_env.current.bind(Master.call(env))
|
41
41
|
|
42
|
-
|
43
|
-
#
|
44
|
-
# @return [Report::Subject]
|
45
|
-
#
|
46
|
-
# @api private
|
47
|
-
#
|
48
|
-
def run
|
49
|
-
Parallel.map(
|
50
|
-
method(:next),
|
51
|
-
in_threads: config.jobs,
|
52
|
-
finish: method(:finish),
|
53
|
-
start: method(:start),
|
54
|
-
&method(:run_mutation)
|
55
|
-
)
|
56
|
-
end
|
42
|
+
status = nil
|
57
43
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
# @return [Parallel::Stop]
|
64
|
-
# in case there is no next mutation or runner should stop early
|
65
|
-
#
|
66
|
-
#
|
67
|
-
# @api private
|
68
|
-
def next
|
69
|
-
@mutex.synchronize do
|
70
|
-
mutation = @mutations.at(@index)
|
71
|
-
if @continue && mutation
|
72
|
-
@index += 1
|
73
|
-
mutation
|
74
|
-
else
|
75
|
-
Parallel::Stop
|
76
|
-
end
|
44
|
+
loop do
|
45
|
+
status = current_status
|
46
|
+
break if status.done
|
47
|
+
reporter.progress(status)
|
48
|
+
Kernel.sleep(REPORT_DELAY)
|
77
49
|
end
|
78
|
-
end
|
79
50
|
|
80
|
-
|
81
|
-
#
|
82
|
-
# @param [Mutation] mutation
|
83
|
-
# @param [Fixnum] _index
|
84
|
-
#
|
85
|
-
# @return [undefined]
|
86
|
-
#
|
87
|
-
# @api private
|
88
|
-
#
|
89
|
-
def start(mutation, _index)
|
90
|
-
@mutex.synchronize do
|
91
|
-
@collector.start(mutation)
|
92
|
-
end
|
93
|
-
end
|
51
|
+
reporter.progress(status)
|
94
52
|
|
95
|
-
|
96
|
-
#
|
97
|
-
# @param [Mutation] mutation
|
98
|
-
# @param [Fixnum] index
|
99
|
-
# @param [Object] result
|
100
|
-
#
|
101
|
-
# @return [undefined]
|
102
|
-
#
|
103
|
-
# @api private
|
104
|
-
#
|
105
|
-
def finish(mutation, index, result)
|
106
|
-
return unless result.is_a?(Mutant::Result::Mutation)
|
53
|
+
@master.call(:stop)
|
107
54
|
|
108
|
-
|
109
|
-
test_result.update(test: test, mutation: mutation) if test_result
|
110
|
-
end.compact
|
55
|
+
@result = status.env_result
|
111
56
|
|
112
|
-
@
|
113
|
-
process_result(result.update(index: index, mutation: mutation, test_results: test_results))
|
114
|
-
end
|
57
|
+
reporter.report(@result)
|
115
58
|
end
|
116
59
|
|
117
|
-
#
|
118
|
-
#
|
119
|
-
# @param [Result::Mutation] result
|
60
|
+
# Return result
|
120
61
|
#
|
121
|
-
# @return [
|
62
|
+
# @return [Result::Env]
|
122
63
|
#
|
123
64
|
# @api private
|
124
65
|
#
|
125
|
-
|
126
|
-
@collector.finish(result)
|
127
|
-
reporter.progress(@collector)
|
128
|
-
return unless config.fail_fast && !result.success?
|
129
|
-
@continue = false
|
130
|
-
end
|
66
|
+
attr_reader :result
|
131
67
|
|
132
|
-
|
133
|
-
#
|
134
|
-
# @param [Mutation] mutation
|
135
|
-
#
|
136
|
-
# @return [Report::Mutation]
|
137
|
-
#
|
138
|
-
# @api private
|
139
|
-
#
|
140
|
-
def run_mutation(mutation)
|
141
|
-
Result::Mutation.compute do
|
142
|
-
{
|
143
|
-
index: nil,
|
144
|
-
mutation: nil,
|
145
|
-
test_results: kill_mutation(mutation)
|
146
|
-
}
|
147
|
-
end
|
148
|
-
end
|
68
|
+
private
|
149
69
|
|
150
|
-
#
|
151
|
-
#
|
152
|
-
# @param [Mutation] mutation
|
70
|
+
# Return reporter
|
153
71
|
#
|
154
|
-
# @return [
|
72
|
+
# @return [Reporter]
|
155
73
|
#
|
156
74
|
# @api private
|
157
75
|
#
|
158
|
-
def
|
159
|
-
|
160
|
-
results << result = run_mutation_test(mutation, test)
|
161
|
-
return results if mutation.killed_by?(result)
|
162
|
-
end
|
76
|
+
def reporter
|
77
|
+
env.config.reporter
|
163
78
|
end
|
164
79
|
|
165
80
|
# Return config
|
@@ -172,36 +87,14 @@ module Mutant
|
|
172
87
|
env.config
|
173
88
|
end
|
174
89
|
|
175
|
-
# Return
|
176
|
-
#
|
177
|
-
# @return [Report::Test]
|
178
|
-
#
|
179
|
-
# @api private
|
180
|
-
#
|
181
|
-
def run_mutation_test(mutation, test)
|
182
|
-
time = Time.now
|
183
|
-
config.isolation.call do
|
184
|
-
mutation.insert
|
185
|
-
test.run
|
186
|
-
end
|
187
|
-
rescue Isolation::Error => exception
|
188
|
-
Result::Test.new(
|
189
|
-
test: test,
|
190
|
-
mutation: mutation,
|
191
|
-
runtime: Time.now - time,
|
192
|
-
output: exception.message,
|
193
|
-
passed: false
|
194
|
-
)
|
195
|
-
end
|
196
|
-
|
197
|
-
# Return reporter
|
90
|
+
# Return current status
|
198
91
|
#
|
199
|
-
# @return [
|
92
|
+
# @return [Status]
|
200
93
|
#
|
201
94
|
# @api private
|
202
95
|
#
|
203
|
-
def
|
204
|
-
|
96
|
+
def current_status
|
97
|
+
@master.call(:status)
|
205
98
|
end
|
206
99
|
|
207
100
|
end # Runner
|
@@ -0,0 +1,174 @@
|
|
1
|
+
module Mutant
|
2
|
+
class Runner
|
3
|
+
# Master actor to control workers
|
4
|
+
class Master
|
5
|
+
include Concord.new(:env, :actor)
|
6
|
+
|
7
|
+
private_class_method :new
|
8
|
+
|
9
|
+
# Run master runner component
|
10
|
+
#
|
11
|
+
# @param [Env] env
|
12
|
+
#
|
13
|
+
# @return [Actor::Sender]
|
14
|
+
#
|
15
|
+
# @api private
|
16
|
+
#
|
17
|
+
def self.call(env)
|
18
|
+
env.config.actor_env.spawn do |actor|
|
19
|
+
new(env, actor).__send__(:run)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
# Initialize object
|
26
|
+
#
|
27
|
+
# @return [undefined]
|
28
|
+
#
|
29
|
+
# @api private
|
30
|
+
#
|
31
|
+
def initialize(*)
|
32
|
+
super
|
33
|
+
|
34
|
+
@scheduler = Scheduler.new(env)
|
35
|
+
@workers = env.config.jobs
|
36
|
+
@stop = false
|
37
|
+
@stopping = false
|
38
|
+
end
|
39
|
+
|
40
|
+
# Run work loop
|
41
|
+
#
|
42
|
+
# @return [self]
|
43
|
+
#
|
44
|
+
# @api private
|
45
|
+
#
|
46
|
+
def run
|
47
|
+
@workers.times do |id|
|
48
|
+
Worker.run(
|
49
|
+
id: id,
|
50
|
+
config: env.config,
|
51
|
+
parent: actor.sender
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
receive_loop
|
56
|
+
end
|
57
|
+
|
58
|
+
# Handle messages
|
59
|
+
#
|
60
|
+
# @param [Actor::Message] message
|
61
|
+
#
|
62
|
+
# @return [undefined]
|
63
|
+
#
|
64
|
+
# @api private
|
65
|
+
#
|
66
|
+
def handle(message)
|
67
|
+
type, payload = message.type, message.payload
|
68
|
+
case type
|
69
|
+
when :ready
|
70
|
+
ready_worker(payload)
|
71
|
+
when :status
|
72
|
+
handle_status(payload)
|
73
|
+
when :result
|
74
|
+
handle_result(payload)
|
75
|
+
when :stop
|
76
|
+
handle_stop(payload)
|
77
|
+
else
|
78
|
+
fail Actor::ProtocolError, "Unexpected message: #{type.inspect}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Run receive loop
|
83
|
+
#
|
84
|
+
# @return [undefined]
|
85
|
+
#
|
86
|
+
# @api private
|
87
|
+
#
|
88
|
+
def receive_loop
|
89
|
+
loop do
|
90
|
+
break if @workers.zero? && @stop
|
91
|
+
handle(actor.receiver.call)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Handle status
|
96
|
+
#
|
97
|
+
# @param [Actor::Sender] sender
|
98
|
+
#
|
99
|
+
# @return [undefined]
|
100
|
+
#
|
101
|
+
# @api private
|
102
|
+
#
|
103
|
+
def handle_status(sender)
|
104
|
+
sender.call(Actor::Message.new(:status, @scheduler.status))
|
105
|
+
end
|
106
|
+
|
107
|
+
# Handle result
|
108
|
+
#
|
109
|
+
# @param [JobResult] job_result
|
110
|
+
#
|
111
|
+
# @return [undefined]
|
112
|
+
#
|
113
|
+
# @api private
|
114
|
+
#
|
115
|
+
def handle_result(job_result)
|
116
|
+
return if @stopping
|
117
|
+
@scheduler.job_result(job_result)
|
118
|
+
@stopping = env.config.fail_fast && @scheduler.status.done
|
119
|
+
end
|
120
|
+
|
121
|
+
# Handle stop
|
122
|
+
#
|
123
|
+
# @param [Actor::Sender] sender
|
124
|
+
#
|
125
|
+
# @return [undefined]
|
126
|
+
#
|
127
|
+
# @api private
|
128
|
+
#
|
129
|
+
def handle_stop(sender)
|
130
|
+
@stopping = true
|
131
|
+
@stop = true
|
132
|
+
receive_loop
|
133
|
+
sender.call(Actor::Message.new(:stop))
|
134
|
+
end
|
135
|
+
|
136
|
+
# Handle ready worker
|
137
|
+
#
|
138
|
+
# @param [Actor::Sender] sender
|
139
|
+
#
|
140
|
+
# @return [undefined]
|
141
|
+
#
|
142
|
+
# @api private
|
143
|
+
#
|
144
|
+
def ready_worker(sender)
|
145
|
+
if @stopping
|
146
|
+
stop_worker(sender)
|
147
|
+
return
|
148
|
+
end
|
149
|
+
|
150
|
+
job = @scheduler.next_job
|
151
|
+
|
152
|
+
if job
|
153
|
+
sender.call(Actor::Message.new(:job, job))
|
154
|
+
else
|
155
|
+
stop_worker(sender)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# Stop worker
|
160
|
+
#
|
161
|
+
# @param [Actor::Sender] sender
|
162
|
+
#
|
163
|
+
# @return [undefined]
|
164
|
+
#
|
165
|
+
# @api private
|
166
|
+
#
|
167
|
+
def stop_worker(sender)
|
168
|
+
@workers -= 1
|
169
|
+
sender.call(Actor::Message.new(:stop))
|
170
|
+
end
|
171
|
+
|
172
|
+
end # Master
|
173
|
+
end # Runner
|
174
|
+
end # Mutant
|