mutant 0.6.7 → 0.7.1
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.
- 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
|