mutant 0.7.3 → 0.7.4
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/.rubocop.yml +2 -0
- data/.travis.yml +3 -2
- data/Changelog.md +7 -2
- data/Gemfile +0 -1
- data/Gemfile.devtools +9 -37
- data/README.md +1 -1
- data/circle.yml +1 -1
- data/config/flay.yml +1 -1
- data/config/reek.yml +6 -19
- data/config/rubocop.yml +58 -63
- data/lib/mutant.rb +8 -4
- data/lib/mutant/ast.rb +1 -1
- data/lib/mutant/cli.rb +12 -6
- data/lib/mutant/env.rb +17 -1
- data/lib/mutant/isolation.rb +4 -2
- data/lib/mutant/loader.rb +4 -0
- data/lib/mutant/matcher/compiler.rb +2 -0
- data/lib/mutant/mutation.rb +2 -0
- data/lib/mutant/mutator/node/const.rb +1 -1
- data/lib/mutant/mutator/node/generic.rb +1 -1
- data/lib/mutant/mutator/node/if.rb +2 -0
- data/lib/mutant/parallel.rb +93 -0
- data/lib/mutant/{runner → parallel}/master.rb +90 -45
- data/lib/mutant/parallel/source.rb +73 -0
- data/lib/mutant/{runner → parallel}/worker.rb +13 -30
- data/lib/mutant/reporter/cli.rb +8 -11
- data/lib/mutant/reporter/cli/printer.rb +14 -8
- data/lib/mutant/result.rb +0 -10
- data/lib/mutant/runner.rb +49 -43
- data/lib/mutant/runner/{scheduler.rb → sink.rb} +9 -68
- data/lib/mutant/version.rb +1 -1
- data/lib/mutant/zombifier/file.rb +28 -9
- data/meta/if.rb +8 -0
- data/meta/match_current_line.rb +1 -0
- data/spec/integration/mutant/corpus_spec.rb +1 -1
- data/spec/integration/mutant/null_spec.rb +1 -1
- data/spec/integration/mutant/rspec_spec.rb +1 -1
- data/spec/integration/mutant/test_mutator_handles_types_spec.rb +1 -1
- data/spec/integration/mutant/zombie_spec.rb +1 -1
- data/spec/support/corpus.rb +2 -0
- data/spec/support/fake_actor.rb +20 -10
- data/spec/support/mutation_verifier.rb +2 -0
- data/spec/support/shared_context.rb +12 -19
- data/spec/unit/mutant/env_spec.rb +20 -2
- data/spec/unit/mutant/expression_spec.rb +4 -1
- data/spec/unit/mutant/parallel/master_spec.rb +339 -0
- data/spec/unit/mutant/parallel/source/array_spec.rb +47 -0
- data/spec/unit/mutant/{runner → parallel}/worker_spec.rb +23 -26
- data/spec/unit/mutant/parallel_spec.rb +16 -0
- data/spec/unit/mutant/reporter/cli_spec.rb +1 -1
- data/spec/unit/mutant/reporter/trace_spec.rb +9 -0
- data/spec/unit/mutant/result/env_spec.rb +0 -55
- data/spec/unit/mutant/runner/driver_spec.rb +26 -0
- data/spec/unit/mutant/runner/sink_spec.rb +162 -0
- data/spec/unit/mutant/runner_spec.rb +60 -63
- data/spec/unit/mutant/warning_filter_spec.rb +2 -1
- data/test_app/Gemfile.devtools +9 -37
- metadata +21 -11
- data/spec/unit/mutant/runner/master_spec.rb +0 -199
- data/spec/unit/mutant/runner/scheduler_spec.rb +0 -161
@@ -1,27 +1,23 @@
|
|
1
1
|
module Mutant
|
2
|
-
|
3
|
-
#
|
2
|
+
module Parallel
|
3
|
+
# Parallel execution worker
|
4
4
|
class Worker
|
5
|
-
include Adamantium::Flat, Anima.new(:
|
6
|
-
|
7
|
-
private_class_method :new
|
5
|
+
include Adamantium::Flat, Anima.new(:actor, :processor, :parent)
|
8
6
|
|
9
7
|
# Run worker
|
10
8
|
#
|
11
9
|
# @param [Hash<Symbol, Object] attributes
|
12
10
|
#
|
13
|
-
# @return [
|
11
|
+
# @return [self]
|
14
12
|
#
|
15
13
|
# @api private
|
16
14
|
#
|
17
15
|
def self.run(attributes)
|
18
|
-
attributes
|
19
|
-
|
20
|
-
worker.__send__(:run, actor)
|
21
|
-
end
|
16
|
+
new(attributes).run
|
17
|
+
self
|
22
18
|
end
|
23
19
|
|
24
|
-
|
20
|
+
private_class_method :new
|
25
21
|
|
26
22
|
# Worker loop
|
27
23
|
#
|
@@ -31,12 +27,14 @@ module Mutant
|
|
31
27
|
#
|
32
28
|
# rubocop:disable Lint/Loop
|
33
29
|
#
|
34
|
-
def run
|
30
|
+
def run
|
35
31
|
begin
|
36
32
|
parent.call(Actor::Message.new(:ready, actor.sender))
|
37
33
|
end until handle(actor.receiver.call)
|
38
34
|
end
|
39
35
|
|
36
|
+
private
|
37
|
+
|
40
38
|
# Handle job
|
41
39
|
#
|
42
40
|
# @param [Message] message
|
@@ -67,25 +65,10 @@ module Mutant
|
|
67
65
|
# @api private
|
68
66
|
#
|
69
67
|
def handle_job(job)
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
# Run mutation
|
74
|
-
#
|
75
|
-
# @param [Mutation] mutation
|
76
|
-
#
|
77
|
-
# @return [Result::Mutation]
|
78
|
-
#
|
79
|
-
# @api private
|
80
|
-
#
|
81
|
-
def run_mutation(mutation)
|
82
|
-
test_result = mutation.kill(config.isolation, config.integration)
|
83
|
-
Result::Mutation.new(
|
84
|
-
mutation: mutation,
|
85
|
-
test_result: test_result
|
86
|
-
)
|
68
|
+
result = processor.call(job.payload)
|
69
|
+
parent.call(Actor::Message.new(:result, JobResult.new(job: job, payload: result)))
|
87
70
|
end
|
88
71
|
|
89
72
|
end # Worker
|
90
|
-
end #
|
73
|
+
end # Parallel
|
91
74
|
end # Mutant
|
data/lib/mutant/reporter/cli.rb
CHANGED
@@ -14,13 +14,11 @@ module Mutant
|
|
14
14
|
#
|
15
15
|
def self.build(output)
|
16
16
|
tty = output.respond_to?(:tty?) && output.tty?
|
17
|
-
format =
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
end
|
23
|
-
|
17
|
+
format = if !Mutant.ci? && tty && Tput::INSTANCE.available
|
18
|
+
Format::Framed.new(tty: tty, tput: Tput::INSTANCE)
|
19
|
+
else
|
20
|
+
Format::Progressive.new(tty: tty)
|
21
|
+
end
|
24
22
|
new(output, format)
|
25
23
|
end
|
26
24
|
|
@@ -39,15 +37,14 @@ module Mutant
|
|
39
37
|
|
40
38
|
# Report progress object
|
41
39
|
#
|
42
|
-
# @param [
|
40
|
+
# @param [Parallel::Status] status
|
43
41
|
#
|
44
42
|
# @return [self]
|
45
43
|
#
|
46
44
|
# @api private
|
47
45
|
#
|
48
|
-
def progress(
|
49
|
-
write(format.progress(
|
50
|
-
|
46
|
+
def progress(status)
|
47
|
+
write(format.progress(status))
|
51
48
|
self
|
52
49
|
end
|
53
50
|
|
@@ -137,7 +137,7 @@ module Mutant
|
|
137
137
|
# Printer for runner status
|
138
138
|
class Status < self
|
139
139
|
|
140
|
-
delegate(:active_jobs, :
|
140
|
+
delegate(:active_jobs, :payload)
|
141
141
|
|
142
142
|
# Print progress for collector
|
143
143
|
#
|
@@ -146,7 +146,7 @@ module Mutant
|
|
146
146
|
# @api private
|
147
147
|
#
|
148
148
|
def run
|
149
|
-
visit(EnvProgress,
|
149
|
+
visit(EnvProgress, payload)
|
150
150
|
info('Active subjects: %d', active_subject_results.length)
|
151
151
|
visit_collection(SubjectProgress, active_subject_results)
|
152
152
|
job_status
|
@@ -164,8 +164,8 @@ module Mutant
|
|
164
164
|
def job_status
|
165
165
|
return if active_jobs.empty?
|
166
166
|
info('Active Jobs:')
|
167
|
-
|
168
|
-
info('%d: %s', job.index, job.
|
167
|
+
active_jobs.sort_by(&:index).each do |job|
|
168
|
+
info('%d: %s', job.index, job.payload.identification)
|
169
169
|
end
|
170
170
|
end
|
171
171
|
|
@@ -176,9 +176,10 @@ module Mutant
|
|
176
176
|
# @api private
|
177
177
|
#
|
178
178
|
def active_subject_results
|
179
|
-
|
179
|
+
active_mutation_jobs = active_jobs.select { |job| job.payload.kind_of?(Mutant::Mutation) }
|
180
|
+
active_subjects = active_mutation_jobs.map(&:payload).flat_map(&:subject).to_set
|
180
181
|
|
181
|
-
|
182
|
+
payload.subject_results.select do |subject_result|
|
182
183
|
active_subjects.include?(subject_result.subject)
|
183
184
|
end
|
184
185
|
end
|
@@ -196,6 +197,8 @@ module Mutant
|
|
196
197
|
#
|
197
198
|
# @api private
|
198
199
|
#
|
200
|
+
# rubocop:disable AbcSize
|
201
|
+
#
|
199
202
|
def run
|
200
203
|
info 'Mutant configuration:'
|
201
204
|
info 'Matcher: %s', object.matcher_config.inspect
|
@@ -230,6 +233,8 @@ module Mutant
|
|
230
233
|
#
|
231
234
|
# @api private
|
232
235
|
#
|
236
|
+
# rubocop:disable MethodLength
|
237
|
+
#
|
233
238
|
def run
|
234
239
|
visit(Config, env.config)
|
235
240
|
info 'Subjects: %s', amount_subjects
|
@@ -385,7 +390,7 @@ module Mutant
|
|
385
390
|
# @api private
|
386
391
|
#
|
387
392
|
def object
|
388
|
-
super().
|
393
|
+
super().payload
|
389
394
|
end
|
390
395
|
end
|
391
396
|
|
@@ -479,7 +484,8 @@ module Mutant
|
|
479
484
|
|
480
485
|
delegate :mutation, :test_result
|
481
486
|
|
482
|
-
DIFF_ERROR_MESSAGE =
|
487
|
+
DIFF_ERROR_MESSAGE =
|
488
|
+
'BUG: Mutation NOT resulted in exactly one diff hunk. Please report a reproduction!'.freeze
|
483
489
|
|
484
490
|
MAP = {
|
485
491
|
Mutant::Mutation::Evil => :evil_details,
|
data/lib/mutant/result.rb
CHANGED
@@ -98,16 +98,6 @@ module Mutant
|
|
98
98
|
end
|
99
99
|
memoize :success?
|
100
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
|
-
|
111
101
|
# Return failed subject results
|
112
102
|
#
|
113
103
|
# @return [Array<Result::Subject>]
|
data/lib/mutant/runner.rb
CHANGED
@@ -3,25 +3,6 @@ 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
6
|
# Initialize object
|
26
7
|
#
|
27
8
|
# @return [undefined]
|
@@ -32,37 +13,72 @@ module Mutant
|
|
32
13
|
super
|
33
14
|
|
34
15
|
reporter.start(env)
|
16
|
+
|
17
|
+
run_mutation_analysis
|
18
|
+
end
|
19
|
+
|
20
|
+
# Return result
|
21
|
+
#
|
22
|
+
# @return [Result::Env]
|
23
|
+
#
|
24
|
+
# @api private
|
25
|
+
#
|
26
|
+
attr_reader :result
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# Run mutation analysis
|
31
|
+
#
|
32
|
+
# @return [undefined]
|
33
|
+
#
|
34
|
+
# @api private
|
35
|
+
#
|
36
|
+
def run_mutation_analysis
|
35
37
|
config.integration.setup
|
36
38
|
|
37
|
-
@
|
39
|
+
@result = run_driver(Parallel.async(mutation_test_config))
|
40
|
+
reporter.report(@result)
|
41
|
+
end
|
38
42
|
|
43
|
+
# Run driver
|
44
|
+
#
|
45
|
+
# @param [Driver] driver
|
46
|
+
#
|
47
|
+
# @return [Object]
|
48
|
+
# the last returned status payload
|
49
|
+
#
|
50
|
+
# @api private
|
51
|
+
#
|
52
|
+
def run_driver(driver)
|
39
53
|
status = nil
|
40
54
|
|
41
55
|
loop do
|
42
|
-
status =
|
43
|
-
break if status.done
|
56
|
+
status = driver.status
|
44
57
|
reporter.progress(status)
|
58
|
+
break if status.done
|
45
59
|
Kernel.sleep(reporter.delay)
|
46
60
|
end
|
47
61
|
|
48
|
-
|
49
|
-
|
50
|
-
@master.call(:stop)
|
51
|
-
|
52
|
-
@result = status.env_result
|
62
|
+
driver.stop
|
53
63
|
|
54
|
-
|
64
|
+
status.payload
|
55
65
|
end
|
56
66
|
|
57
|
-
# Return
|
67
|
+
# Return mutation test config
|
58
68
|
#
|
59
|
-
# @return [
|
69
|
+
# @return [Parallell::Config]
|
60
70
|
#
|
61
71
|
# @api private
|
62
72
|
#
|
63
|
-
|
64
|
-
|
65
|
-
|
73
|
+
def mutation_test_config
|
74
|
+
Parallel::Config.new(
|
75
|
+
env: config.actor_env,
|
76
|
+
jobs: config.jobs,
|
77
|
+
source: Parallel::Source::Array.new(env.mutations),
|
78
|
+
sink: Sink.new(env),
|
79
|
+
processor: env.method(:kill_mutation)
|
80
|
+
)
|
81
|
+
end
|
66
82
|
|
67
83
|
# Return reporter
|
68
84
|
#
|
@@ -84,15 +100,5 @@ module Mutant
|
|
84
100
|
env.config
|
85
101
|
end
|
86
102
|
|
87
|
-
# Return current status
|
88
|
-
#
|
89
|
-
# @return [Status]
|
90
|
-
#
|
91
|
-
# @api private
|
92
|
-
#
|
93
|
-
def current_status
|
94
|
-
@master.call(:status)
|
95
|
-
end
|
96
|
-
|
97
103
|
end # Runner
|
98
104
|
end # Mutant
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Mutant
|
2
2
|
class Runner
|
3
|
-
#
|
4
|
-
class
|
3
|
+
# Mutation result sink
|
4
|
+
class Sink
|
5
5
|
include Concord.new(:env)
|
6
6
|
|
7
7
|
# Initialize object
|
@@ -12,9 +12,7 @@ module Mutant
|
|
12
12
|
#
|
13
13
|
def initialize(*)
|
14
14
|
super
|
15
|
-
@index = 0
|
16
15
|
@start = Time.now
|
17
|
-
@active_jobs = Set.new
|
18
16
|
@subject_results = Hash.new do |_hash, subject|
|
19
17
|
Result::Subject.new(
|
20
18
|
subject: subject,
|
@@ -30,58 +28,17 @@ module Mutant
|
|
30
28
|
# @api private
|
31
29
|
#
|
32
30
|
def status
|
33
|
-
|
34
|
-
env_result: env_result,
|
35
|
-
done: done?,
|
36
|
-
active_jobs: @active_jobs.dup
|
37
|
-
)
|
38
|
-
end
|
39
|
-
|
40
|
-
# Return next job
|
41
|
-
#
|
42
|
-
# @return [Job]
|
43
|
-
# in case there is a next job
|
44
|
-
#
|
45
|
-
# @return [nil]
|
46
|
-
# otherwise
|
47
|
-
#
|
48
|
-
# @api private
|
49
|
-
def next_job
|
50
|
-
return unless next_mutation?
|
51
|
-
|
52
|
-
Job.new(
|
53
|
-
mutation: mutations.fetch(@index),
|
54
|
-
index: @index
|
55
|
-
).tap do |job|
|
56
|
-
@index += 1
|
57
|
-
@active_jobs << job
|
58
|
-
end
|
31
|
+
env_result
|
59
32
|
end
|
60
33
|
|
61
|
-
#
|
62
|
-
#
|
63
|
-
# @param [JobResult] job_result
|
64
|
-
#
|
65
|
-
# @return [self]
|
66
|
-
#
|
67
|
-
# @api private
|
68
|
-
#
|
69
|
-
def job_result(job_result)
|
70
|
-
@active_jobs.delete(job_result.job)
|
71
|
-
mutation_result(job_result.result)
|
72
|
-
self
|
73
|
-
end
|
74
|
-
|
75
|
-
private
|
76
|
-
|
77
|
-
# Test if mutation run is done
|
34
|
+
# Test if scheduling stopped
|
78
35
|
#
|
79
36
|
# @return [Boolean]
|
80
37
|
#
|
81
38
|
# @api private
|
82
39
|
#
|
83
|
-
def
|
84
|
-
!env_result.
|
40
|
+
def stop?
|
41
|
+
env.config.fail_fast && !env_result.subject_results.all?(&:success?)
|
85
42
|
end
|
86
43
|
|
87
44
|
# Handle mutation finish
|
@@ -92,7 +49,7 @@ module Mutant
|
|
92
49
|
#
|
93
50
|
# @api private
|
94
51
|
#
|
95
|
-
def
|
52
|
+
def result(mutation_result)
|
96
53
|
mutation = mutation_result.mutation
|
97
54
|
|
98
55
|
original = @subject_results[mutation.subject]
|
@@ -100,27 +57,11 @@ module Mutant
|
|
100
57
|
@subject_results[mutation.subject] = original.update(
|
101
58
|
mutation_results: (original.mutation_results.dup << mutation_result)
|
102
59
|
)
|
103
|
-
end
|
104
60
|
|
105
|
-
|
106
|
-
#
|
107
|
-
# @return [Boolean]
|
108
|
-
#
|
109
|
-
# @api private
|
110
|
-
#
|
111
|
-
def next_mutation?
|
112
|
-
mutations.length > @index
|
61
|
+
self
|
113
62
|
end
|
114
63
|
|
115
|
-
|
116
|
-
#
|
117
|
-
# @return [Array<Mutation>]
|
118
|
-
#
|
119
|
-
# @api private
|
120
|
-
#
|
121
|
-
def mutations
|
122
|
-
env.mutations
|
123
|
-
end
|
64
|
+
private
|
124
65
|
|
125
66
|
# Return current result
|
126
67
|
#
|