mutant 0.7.4 → 0.7.5
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/.travis.yml +3 -2
- data/Changelog.md +6 -2
- data/Gemfile +1 -0
- data/config/flay.yml +1 -1
- data/config/reek.yml +1 -1
- data/lib/mutant.rb +4 -3
- data/lib/mutant/cli.rb +2 -2
- data/lib/mutant/config.rb +2 -3
- data/lib/mutant/env.rb +29 -132
- data/lib/mutant/env/bootstrap.rb +155 -0
- data/lib/mutant/expression.rb +27 -0
- data/lib/mutant/expression/namespace.rb +2 -2
- data/lib/mutant/matcher/method.rb +1 -1
- data/lib/mutant/matcher/method/instance.rb +1 -1
- data/lib/mutant/mutation.rb +2 -30
- data/lib/mutant/mutator/node/begin.rb +1 -3
- data/lib/mutant/mutator/node/if.rb +2 -2
- data/lib/mutant/reporter/cli/printer.rb +5 -4
- data/lib/mutant/result.rb +1 -1
- data/lib/mutant/runner.rb +3 -3
- data/lib/mutant/runner/sink.rb +86 -53
- data/lib/mutant/selector.rb +17 -0
- data/lib/mutant/selector/expression.rb +28 -0
- data/lib/mutant/subject.rb +1 -19
- data/lib/mutant/version.rb +1 -1
- data/mutant.gemspec +3 -3
- data/spec/spec_helper.rb +1 -1
- data/spec/support/corpus.rb +5 -5
- data/spec/support/shared_context.rb +1 -6
- data/spec/unit/mutant/cli_spec.rb +2 -2
- data/spec/unit/mutant/env/boostrap_spec.rb +130 -0
- data/spec/unit/mutant/env_spec.rb +61 -49
- data/spec/unit/mutant/expression_spec.rb +13 -0
- data/spec/unit/mutant/matcher/filter_spec.rb +1 -1
- data/spec/unit/mutant/matcher/method/instance_spec.rb +1 -1
- data/spec/unit/mutant/matcher/method/singleton_spec.rb +1 -1
- data/spec/unit/mutant/mutation_spec.rb +0 -36
- data/spec/unit/mutant/reporter/trace_spec.rb +1 -1
- data/spec/unit/mutant/require_highjack_spec.rb +2 -4
- data/spec/unit/mutant/result/subject_spec.rb +2 -1
- data/spec/unit/mutant/runner/{sink_spec.rb → sink/mutation_spec.rb} +1 -3
- data/spec/unit/mutant/runner_spec.rb +4 -3
- data/spec/unit/mutant/selector/expression_spec.rb +60 -0
- data/spec/unit/mutant/subject/method/instance_spec.rb +3 -5
- data/spec/unit/mutant/subject/method/singleton_spec.rb +2 -3
- data/spec/unit/mutant/subject_spec.rb +1 -53
- data/spec/unit/mutant/warning_filter_spec.rb +2 -4
- metadata +18 -15
- data/lib/mutant/line_trace.rb +0 -34
- data/spec/unit/mutant/line_trace_spec.rb +0 -38
- data/spec/unit/mutant/subject/context_spec.rb +0 -16
data/lib/mutant/expression.rb
CHANGED
@@ -37,6 +37,33 @@ module Mutant
|
|
37
37
|
@inspect = format(INSPECT_FORMAT, syntax)
|
38
38
|
end
|
39
39
|
|
40
|
+
# Return marshallable representation
|
41
|
+
#
|
42
|
+
# FIXME: Remove the need for this.
|
43
|
+
#
|
44
|
+
# Refactoring Expression objects not to reference a MatchData instance.
|
45
|
+
# This will make this hack unneeded.
|
46
|
+
#
|
47
|
+
# @return [String]
|
48
|
+
#
|
49
|
+
# @api private
|
50
|
+
#
|
51
|
+
def _dump(_level)
|
52
|
+
syntax
|
53
|
+
end
|
54
|
+
|
55
|
+
# Load serializable representation
|
56
|
+
#
|
57
|
+
# @return [String]
|
58
|
+
#
|
59
|
+
# @return [Expression]
|
60
|
+
#
|
61
|
+
# @api private
|
62
|
+
#
|
63
|
+
def self._load(syntax)
|
64
|
+
parse(syntax)
|
65
|
+
end
|
66
|
+
|
40
67
|
# Return inspection
|
41
68
|
#
|
42
69
|
# @return [String]
|
@@ -37,7 +37,7 @@ module Mutant
|
|
37
37
|
|
38
38
|
# Return matcher
|
39
39
|
#
|
40
|
-
# @param [Env] env
|
40
|
+
# @param [Env::Bootstrap] env
|
41
41
|
#
|
42
42
|
# @return [Matcher]
|
43
43
|
#
|
@@ -74,7 +74,7 @@ module Mutant
|
|
74
74
|
|
75
75
|
# Return matcher
|
76
76
|
#
|
77
|
-
# @param [
|
77
|
+
# @param [Env::Bootstrap] env
|
78
78
|
#
|
79
79
|
# @return [Matcher]
|
80
80
|
#
|
data/lib/mutant/mutation.rb
CHANGED
@@ -7,34 +7,6 @@ module Mutant
|
|
7
7
|
CODE_DELIMITER = "\0".freeze
|
8
8
|
CODE_RANGE = (0..4).freeze
|
9
9
|
|
10
|
-
# Kill mutation under isolation with integration
|
11
|
-
#
|
12
|
-
# @param [Isolation] isolation
|
13
|
-
# @param [Integration] integration
|
14
|
-
#
|
15
|
-
# @return [Result::Test]
|
16
|
-
#
|
17
|
-
# @api private
|
18
|
-
#
|
19
|
-
# rubocop:disable MethodLength
|
20
|
-
#
|
21
|
-
def kill(isolation, integration)
|
22
|
-
start = Time.now
|
23
|
-
tests = subject.tests
|
24
|
-
|
25
|
-
isolation.call do
|
26
|
-
insert
|
27
|
-
integration.call(tests)
|
28
|
-
end.update(tests: tests)
|
29
|
-
rescue Isolation::Error => error
|
30
|
-
Result::Test.new(
|
31
|
-
tests: tests,
|
32
|
-
output: error.message,
|
33
|
-
runtime: Time.now - start,
|
34
|
-
passed: false
|
35
|
-
)
|
36
|
-
end
|
37
|
-
|
38
10
|
# Return identification
|
39
11
|
#
|
40
12
|
# @return [String]
|
@@ -90,8 +62,6 @@ module Mutant
|
|
90
62
|
self::TEST_PASS_SUCCESS.equal?(test_result.passed)
|
91
63
|
end
|
92
64
|
|
93
|
-
private
|
94
|
-
|
95
65
|
# Insert mutated node
|
96
66
|
#
|
97
67
|
# FIXME: Cache subject visibility in a better way! Ideally dont mutate it
|
@@ -109,6 +79,8 @@ module Mutant
|
|
109
79
|
self
|
110
80
|
end
|
111
81
|
|
82
|
+
private
|
83
|
+
|
112
84
|
# Return sha1 sum of source and subject identification
|
113
85
|
#
|
114
86
|
# @return [String]
|
@@ -16,9 +16,7 @@ module Mutant
|
|
16
16
|
# @api private
|
17
17
|
#
|
18
18
|
def dispatch
|
19
|
-
Util::Array.each(children, self)
|
20
|
-
emit_child_subset(children)
|
21
|
-
end
|
19
|
+
Util::Array.each(children, self, &method(:emit_child_subset))
|
22
20
|
children.each_with_index do |child, index|
|
23
21
|
mutate_child(index)
|
24
22
|
emit(child) unless children.eql?([child])
|
@@ -30,8 +30,8 @@ module Mutant
|
|
30
30
|
# @api private
|
31
31
|
#
|
32
32
|
def mutate_condition
|
33
|
-
emit_condition_mutations do |
|
34
|
-
!n_self?(
|
33
|
+
emit_condition_mutations do |node|
|
34
|
+
!n_self?(node)
|
35
35
|
end
|
36
36
|
emit_type(n_not(condition), if_branch, else_branch) unless n_match_current_line?(condition)
|
37
37
|
emit_type(N_TRUE, if_branch, else_branch)
|
@@ -201,7 +201,7 @@ module Mutant
|
|
201
201
|
#
|
202
202
|
def run
|
203
203
|
info 'Mutant configuration:'
|
204
|
-
info 'Matcher: %s', object.
|
204
|
+
info 'Matcher: %s', object.matcher.inspect
|
205
205
|
info 'Integration: %s', object.integration.name
|
206
206
|
info 'Expect Coverage: %0.2f%%', object.expected_coverage.inspect
|
207
207
|
info 'Jobs: %d', object.jobs
|
@@ -295,7 +295,7 @@ module Mutant
|
|
295
295
|
# Subject report printer
|
296
296
|
class SubjectResult < self
|
297
297
|
|
298
|
-
delegate :subject, :failed_mutations
|
298
|
+
delegate :subject, :failed_mutations, :tests
|
299
299
|
|
300
300
|
# Run report printer
|
301
301
|
#
|
@@ -305,7 +305,7 @@ module Mutant
|
|
305
305
|
#
|
306
306
|
def run
|
307
307
|
status(subject.identification)
|
308
|
-
|
308
|
+
tests.each do |test|
|
309
309
|
puts("- #{test.identification}")
|
310
310
|
end
|
311
311
|
visit_collection(MutationResult, object.alive_mutation_results)
|
@@ -400,6 +400,7 @@ module Mutant
|
|
400
400
|
FORMAT = '(%02d/%02d) %3d%% - killtime: %0.02fs runtime: %0.02fs overhead: %0.02fs'.freeze
|
401
401
|
|
402
402
|
delegate(
|
403
|
+
:tests,
|
403
404
|
:subject,
|
404
405
|
:coverage,
|
405
406
|
:runtime,
|
@@ -452,7 +453,7 @@ module Mutant
|
|
452
453
|
# @api private
|
453
454
|
#
|
454
455
|
def print_tests
|
455
|
-
|
456
|
+
tests.each do |test|
|
456
457
|
puts "- #{test.identification}"
|
457
458
|
end
|
458
459
|
end
|
data/lib/mutant/result.rb
CHANGED
@@ -147,7 +147,7 @@ module Mutant
|
|
147
147
|
|
148
148
|
# Subject result
|
149
149
|
class Subject
|
150
|
-
include Coverage, Result, Anima.new(:subject, :mutation_results)
|
150
|
+
include Coverage, Result, Anima.new(:subject, :tests, :mutation_results)
|
151
151
|
|
152
152
|
sum :killtime, :mutation_results
|
153
153
|
sum :runtime, :mutation_results
|
data/lib/mutant/runner.rb
CHANGED
@@ -72,11 +72,11 @@ module Mutant
|
|
72
72
|
#
|
73
73
|
def mutation_test_config
|
74
74
|
Parallel::Config.new(
|
75
|
-
env:
|
75
|
+
env: env.actor_env,
|
76
76
|
jobs: config.jobs,
|
77
77
|
source: Parallel::Source::Array.new(env.mutations),
|
78
|
-
sink: Sink.new(env),
|
79
|
-
processor: env.method(:
|
78
|
+
sink: Sink::Mutation.new(env),
|
79
|
+
processor: env.method(:kill)
|
80
80
|
)
|
81
81
|
end
|
82
82
|
|
data/lib/mutant/runner/sink.rb
CHANGED
@@ -1,82 +1,115 @@
|
|
1
1
|
module Mutant
|
2
2
|
class Runner
|
3
|
-
#
|
3
|
+
# Abstract base class for computation sinks
|
4
4
|
class Sink
|
5
|
-
include
|
5
|
+
include AbstractType
|
6
6
|
|
7
|
-
#
|
7
|
+
# Return sink status
|
8
8
|
#
|
9
|
-
# @return [
|
9
|
+
# @return [Object]
|
10
10
|
#
|
11
11
|
# @api private
|
12
12
|
#
|
13
|
-
|
14
|
-
super
|
15
|
-
@start = Time.now
|
16
|
-
@subject_results = Hash.new do |_hash, subject|
|
17
|
-
Result::Subject.new(
|
18
|
-
subject: subject,
|
19
|
-
mutation_results: []
|
20
|
-
)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
# Return runner status
|
25
|
-
#
|
26
|
-
# @return [Status]
|
27
|
-
#
|
28
|
-
# @api private
|
29
|
-
#
|
30
|
-
def status
|
31
|
-
env_result
|
32
|
-
end
|
13
|
+
abstract_method :status
|
33
14
|
|
34
|
-
# Test if
|
15
|
+
# Test if computation should be stopped
|
35
16
|
#
|
36
17
|
# @return [Boolean]
|
37
18
|
#
|
38
19
|
# @api private
|
39
20
|
#
|
40
|
-
|
41
|
-
env.config.fail_fast && !env_result.subject_results.all?(&:success?)
|
42
|
-
end
|
21
|
+
abstract_method :stop?
|
43
22
|
|
44
|
-
#
|
23
|
+
# Consume result
|
45
24
|
#
|
46
|
-
# @param [
|
25
|
+
# @param [Object] result
|
47
26
|
#
|
48
27
|
# @return [self]
|
49
28
|
#
|
50
29
|
# @api private
|
51
30
|
#
|
52
|
-
|
53
|
-
mutation = mutation_result.mutation
|
31
|
+
abstract_method :result
|
54
32
|
|
55
|
-
|
33
|
+
# Mutation result sink
|
34
|
+
class Mutation < self
|
35
|
+
include Concord.new(:env)
|
56
36
|
|
57
|
-
|
58
|
-
|
59
|
-
|
37
|
+
# Initialize object
|
38
|
+
#
|
39
|
+
# @return [undefined]
|
40
|
+
#
|
41
|
+
# @api private
|
42
|
+
#
|
43
|
+
def initialize(*)
|
44
|
+
super
|
45
|
+
@start = Time.now
|
46
|
+
@subject_results = Hash.new do |_hash, subject|
|
47
|
+
Result::Subject.new(
|
48
|
+
subject: subject,
|
49
|
+
tests: [],
|
50
|
+
mutation_results: []
|
51
|
+
)
|
52
|
+
end
|
53
|
+
end
|
60
54
|
|
61
|
-
|
62
|
-
|
55
|
+
# Return runner status
|
56
|
+
#
|
57
|
+
# @return [Status]
|
58
|
+
#
|
59
|
+
# @api private
|
60
|
+
#
|
61
|
+
def status
|
62
|
+
env_result
|
63
|
+
end
|
63
64
|
|
64
|
-
|
65
|
+
# Test if scheduling stopped
|
66
|
+
#
|
67
|
+
# @return [Boolean]
|
68
|
+
#
|
69
|
+
# @api private
|
70
|
+
#
|
71
|
+
def stop?
|
72
|
+
env.config.fail_fast && !env_result.subject_results.all?(&:success?)
|
73
|
+
end
|
65
74
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
75
|
+
# Handle mutation finish
|
76
|
+
#
|
77
|
+
# @param [Result::Mutation] mutation_result
|
78
|
+
#
|
79
|
+
# @return [self]
|
80
|
+
#
|
81
|
+
# @api private
|
82
|
+
#
|
83
|
+
def result(mutation_result)
|
84
|
+
mutation = mutation_result.mutation
|
85
|
+
|
86
|
+
original = @subject_results[mutation.subject]
|
87
|
+
|
88
|
+
@subject_results[mutation.subject] = original.update(
|
89
|
+
mutation_results: (original.mutation_results.dup << mutation_result),
|
90
|
+
tests: mutation_result.test_result.tests
|
91
|
+
)
|
92
|
+
|
93
|
+
self
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
# Return current result
|
99
|
+
#
|
100
|
+
# @return [Result::Env]
|
101
|
+
#
|
102
|
+
# @api private
|
103
|
+
#
|
104
|
+
def env_result
|
105
|
+
Result::Env.new(
|
106
|
+
env: env,
|
107
|
+
runtime: Time.now - @start,
|
108
|
+
subject_results: @subject_results.values
|
109
|
+
)
|
110
|
+
end
|
79
111
|
|
80
|
-
|
112
|
+
end # Mutation
|
113
|
+
end # Sink
|
81
114
|
end # Runner
|
82
115
|
end # Mutant
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Mutant
|
2
|
+
# Abstract base class for test selectors
|
3
|
+
class Selector
|
4
|
+
include AbstractType, Adamantium::Flat
|
5
|
+
|
6
|
+
# Return tests for subject
|
7
|
+
#
|
8
|
+
# @param [Subject] subjecto
|
9
|
+
#
|
10
|
+
# @return [Enumerable<Test>]
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
#
|
14
|
+
abstract_method :call
|
15
|
+
|
16
|
+
end # Selector
|
17
|
+
end # Mutant
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Mutant
|
2
|
+
class Selector
|
3
|
+
# Expression based test selector
|
4
|
+
class Expression < self
|
5
|
+
include Concord.new(:integration)
|
6
|
+
|
7
|
+
# Return tests for subject
|
8
|
+
#
|
9
|
+
# @param [Subject] subject
|
10
|
+
#
|
11
|
+
# @return [Enumerable<Test>]
|
12
|
+
#
|
13
|
+
# @api private
|
14
|
+
#
|
15
|
+
def call(subject)
|
16
|
+
subject.match_expressions.each do |match_expression|
|
17
|
+
subject_tests = integration.all_tests.select do |test|
|
18
|
+
match_expression.prefix?(test.expression)
|
19
|
+
end
|
20
|
+
return subject_tests if subject_tests.any?
|
21
|
+
end
|
22
|
+
|
23
|
+
EMPTY_ARRAY
|
24
|
+
end
|
25
|
+
|
26
|
+
end # Expression
|
27
|
+
end # Selector
|
28
|
+
end # Mutant
|
data/lib/mutant/subject.rb
CHANGED
@@ -2,7 +2,7 @@ module Mutant
|
|
2
2
|
# Subject of a mutation
|
3
3
|
class Subject
|
4
4
|
include AbstractType, Adamantium::Flat, Enumerable
|
5
|
-
include Concord::Public.new(:
|
5
|
+
include Concord::Public.new(:context, :node)
|
6
6
|
|
7
7
|
# Return mutations
|
8
8
|
#
|
@@ -30,24 +30,6 @@ module Mutant
|
|
30
30
|
context.source_path
|
31
31
|
end
|
32
32
|
|
33
|
-
# Return tests for mutation
|
34
|
-
#
|
35
|
-
# @return [Array<Test>]
|
36
|
-
#
|
37
|
-
# @api private
|
38
|
-
#
|
39
|
-
def tests
|
40
|
-
match_expressions.each do |match_expression|
|
41
|
-
tests = config.integration.all_tests.select do |test|
|
42
|
-
match_expression.prefix?(test.expression)
|
43
|
-
end
|
44
|
-
return tests if tests.any?
|
45
|
-
end
|
46
|
-
|
47
|
-
EMPTY_ARRAY
|
48
|
-
end
|
49
|
-
memoize :tests
|
50
|
-
|
51
33
|
# Prepare the subject for the insertion of mutation
|
52
34
|
#
|
53
35
|
# @return [self]
|