mutant 0.7.4 → 0.7.5
Sign up to get free protection for your applications and to get access to all the features.
- 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]
|