mutant 0.5.26 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +1 -0
- data/.travis.yml +1 -0
- data/Changelog.md +16 -3
- data/Gemfile +0 -2
- data/Gemfile.devtools +2 -2
- data/README.md +9 -15
- data/bin/mutant +0 -1
- data/config/flay.yml +1 -1
- data/config/flog.yml +1 -1
- data/config/mutant.yml +1 -1
- data/config/reek.yml +14 -11
- data/config/rubocop.yml +1 -1
- data/lib/mutant.rb +22 -21
- data/lib/mutant/ast.rb +47 -0
- data/lib/mutant/cli.rb +7 -4
- data/lib/mutant/config.rb +1 -0
- data/lib/mutant/context.rb +1 -1
- data/lib/mutant/diff.rb +38 -7
- data/lib/mutant/env.rb +22 -3
- data/lib/mutant/expression.rb +15 -4
- data/lib/mutant/integration.rb +1 -1
- data/lib/mutant/isolation.rb +2 -4
- data/lib/mutant/matcher.rb +1 -1
- data/lib/mutant/matcher/method.rb +1 -1
- data/lib/mutant/matcher/method/singleton.rb +1 -1
- data/lib/mutant/matcher/methods.rb +0 -2
- data/lib/mutant/meta/example.rb +0 -2
- data/lib/mutant/meta/example/dsl.rb +1 -1
- data/lib/mutant/mutator.rb +1 -1
- data/lib/mutant/mutator/node.rb +3 -3
- data/lib/mutant/mutator/node/begin.rb +1 -1
- data/lib/mutant/mutator/node/block.rb +16 -3
- data/lib/mutant/mutator/node/if.rb +1 -1
- data/lib/mutant/mutator/node/literal/fixnum.rb +1 -1
- data/lib/mutant/mutator/node/resbody.rb +0 -2
- data/lib/mutant/mutator/node/send.rb +17 -7
- data/lib/mutant/mutator/node/send/index.rb +0 -2
- data/lib/mutant/mutator/registry.rb +1 -1
- data/lib/mutant/mutator/util.rb +1 -1
- data/lib/mutant/mutator/util/array.rb +1 -1
- data/lib/mutant/reporter.rb +13 -3
- data/lib/mutant/reporter/cli.rb +54 -8
- data/lib/mutant/reporter/cli/format.rb +197 -0
- data/lib/mutant/reporter/cli/printer.rb +402 -22
- data/lib/mutant/reporter/cli/tput.rb +27 -0
- data/lib/mutant/reporter/null.rb +4 -34
- data/lib/mutant/reporter/trace.rb +6 -38
- data/lib/mutant/result.rb +44 -56
- data/lib/mutant/runner.rb +99 -52
- data/lib/mutant/runner/collector.rb +134 -0
- data/lib/mutant/subject/method/instance.rb +12 -4
- data/lib/mutant/version.rb +1 -1
- data/lib/mutant/warning_filter.rb +0 -2
- data/lib/mutant/zombifier/file.rb +1 -1
- data/meta/block.rb +17 -1
- data/meta/send.rb +123 -1
- data/mutant-rspec.gemspec +3 -3
- data/mutant.gemspec +1 -1
- data/spec/integration/mutant/corpus_spec.rb +4 -195
- data/spec/integration/mutant/null_spec.rb +1 -3
- data/spec/integration/mutant/rspec_spec.rb +1 -3
- data/spec/integration/mutant/test_mutator_handles_types_spec.rb +1 -3
- data/spec/integration/mutant/zombie_spec.rb +1 -3
- data/spec/integrations.yml +7 -0
- data/spec/shared/method_matcher_behavior.rb +1 -1
- data/spec/spec_helper.rb +1 -0
- data/spec/support/compress_helper.rb +1 -0
- data/spec/support/corpus.rb +239 -0
- data/spec/support/mutation_verifier.rb +2 -4
- data/spec/unit/mutant/cli_spec.rb +20 -13
- data/spec/unit/mutant/context/root_spec.rb +1 -3
- data/spec/unit/mutant/context/scope/root_spec.rb +1 -3
- data/spec/unit/mutant/context/scope/unqualified_name_spec.rb +1 -3
- data/spec/unit/mutant/diff_spec.rb +37 -19
- data/spec/unit/mutant/expression/method_spec.rb +5 -7
- data/spec/unit/mutant/expression/methods_spec.rb +5 -7
- data/spec/unit/mutant/expression/namespace/flat_spec.rb +6 -8
- data/spec/unit/mutant/expression/namespace/recursive_spec.rb +6 -7
- data/spec/unit/mutant/expression_spec.rb +14 -5
- data/spec/unit/mutant/integration_spec.rb +14 -3
- data/spec/unit/mutant/isolation_spec.rb +2 -4
- data/spec/unit/mutant/loader/eval_spec.rb +1 -3
- data/spec/unit/mutant/matcher/chain_spec.rb +1 -3
- data/spec/unit/mutant/matcher/compiler/subject_prefix_spec.rb +21 -0
- data/spec/unit/mutant/matcher/compiler_spec.rb +28 -3
- data/spec/unit/mutant/matcher/filter_spec.rb +1 -3
- data/spec/unit/mutant/matcher/method/instance_spec.rb +3 -5
- data/spec/unit/mutant/matcher/method/singleton_spec.rb +22 -4
- data/spec/unit/mutant/matcher/methods/instance_spec.rb +7 -6
- data/spec/unit/mutant/matcher/methods/singleton_spec.rb +4 -6
- data/spec/unit/mutant/matcher/namespace_spec.rb +1 -3
- data/spec/unit/mutant/matcher/null_spec.rb +1 -3
- data/spec/unit/mutant/mutation_spec.rb +1 -3
- data/spec/unit/mutant/mutator/node_spec.rb +1 -3
- data/spec/unit/mutant/reporter/cli_spec.rb +444 -206
- data/spec/unit/mutant/reporter/null_spec.rb +1 -3
- data/spec/unit/mutant/require_highjack_spec.rb +1 -3
- data/spec/unit/mutant/runner_spec.rb +42 -28
- data/spec/unit/mutant/subject/context_spec.rb +1 -3
- data/spec/unit/mutant/subject/method/instance_spec.rb +27 -19
- data/spec/unit/mutant/subject/method/singleton_spec.rb +49 -17
- data/spec/unit/mutant/subject_spec.rb +1 -3
- data/spec/unit/mutant/test_spec.rb +1 -3
- data/spec/unit/mutant/warning_expectation.rb +1 -3
- data/spec/unit/mutant/warning_filter_spec.rb +1 -3
- data/spec/unit/mutant_spec.rb +13 -3
- data/test_app/Gemfile.devtools +2 -2
- data/test_app/spec/unit/test_app/literal/string_spec.rb +1 -1
- metadata +10 -21
- data/lib/mutant/matcher/method/finder.rb +0 -72
- data/lib/mutant/reporter/cli/progress.rb +0 -10
- data/lib/mutant/reporter/cli/progress/config.rb +0 -30
- data/lib/mutant/reporter/cli/progress/env.rb +0 -30
- data/lib/mutant/reporter/cli/progress/noop.rb +0 -27
- data/lib/mutant/reporter/cli/progress/result.rb +0 -12
- data/lib/mutant/reporter/cli/progress/result/mutation.rb +0 -45
- data/lib/mutant/reporter/cli/progress/result/subject.rb +0 -54
- data/lib/mutant/reporter/cli/progress/subject.rb +0 -27
- data/lib/mutant/reporter/cli/registry.rb +0 -81
- data/lib/mutant/reporter/cli/report.rb +0 -10
- data/lib/mutant/reporter/cli/report/env.rb +0 -92
- data/lib/mutant/reporter/cli/report/mutation.rb +0 -103
- data/lib/mutant/reporter/cli/report/subject.rb +0 -32
- data/lib/mutant/reporter/cli/report/test.rb +0 -28
- data/lib/mutant/walker.rb +0 -53
- data/spec/shared/mutator_behavior.rb +0 -55
@@ -0,0 +1,134 @@
|
|
1
|
+
module Mutant
|
2
|
+
class Runner
|
3
|
+
# Parallel process collector
|
4
|
+
class Collector
|
5
|
+
include Concord::Public.new(:env)
|
6
|
+
|
7
|
+
# Initialize object
|
8
|
+
#
|
9
|
+
# @return [undefined]
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
#
|
13
|
+
def initialize(*)
|
14
|
+
super
|
15
|
+
@start = Time.now
|
16
|
+
@aggregate = Hash.new { |hash, key| hash[key] = [] }
|
17
|
+
@activity = Hash.new(0)
|
18
|
+
@last_mutation_result = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
# Return last mutation result
|
22
|
+
#
|
23
|
+
# @return [Result::Mutation]
|
24
|
+
# if there is a last mutation result
|
25
|
+
#
|
26
|
+
# @return [nil]
|
27
|
+
# otherwise
|
28
|
+
#
|
29
|
+
# @api private
|
30
|
+
#
|
31
|
+
attr_reader :last_mutation_result
|
32
|
+
|
33
|
+
# Return active subject results
|
34
|
+
#
|
35
|
+
# @return [Array<Result::Subject>]
|
36
|
+
#
|
37
|
+
# @api private
|
38
|
+
#
|
39
|
+
def active_subject_results
|
40
|
+
active_subjects.map(&method(:subject_result))
|
41
|
+
end
|
42
|
+
|
43
|
+
# Return current result
|
44
|
+
#
|
45
|
+
# @return [Result::Env]
|
46
|
+
#
|
47
|
+
# @api private
|
48
|
+
#
|
49
|
+
def result
|
50
|
+
Result::Env.new(
|
51
|
+
env: env,
|
52
|
+
runtime: Time.now - @start,
|
53
|
+
subject_results: subject_results,
|
54
|
+
done: false
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Handle mutation start
|
59
|
+
#
|
60
|
+
# @param [Mutation] mutation
|
61
|
+
#
|
62
|
+
# @return [self]
|
63
|
+
#
|
64
|
+
# @api private
|
65
|
+
#
|
66
|
+
def start(mutation)
|
67
|
+
@activity[mutation.subject] += 1
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
# Handle mutation finish
|
72
|
+
#
|
73
|
+
# @param [Result::Mutation] mutation_result
|
74
|
+
#
|
75
|
+
# @return [self]
|
76
|
+
#
|
77
|
+
# @api private
|
78
|
+
#
|
79
|
+
def finish(mutation_result)
|
80
|
+
@last_mutation_result = mutation_result
|
81
|
+
|
82
|
+
subject = mutation_result.mutation.subject
|
83
|
+
|
84
|
+
@activity[subject] -= 1
|
85
|
+
@aggregate[subject] << mutation_result
|
86
|
+
|
87
|
+
self
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
# Return current subject results
|
93
|
+
#
|
94
|
+
# @return [Array<Result::Subject>]
|
95
|
+
#
|
96
|
+
# @api private
|
97
|
+
#
|
98
|
+
def subject_results
|
99
|
+
env.subjects.map(&method(:subject_result))
|
100
|
+
end
|
101
|
+
|
102
|
+
# Return active subjects
|
103
|
+
#
|
104
|
+
# @return [Array<Subject>]
|
105
|
+
#
|
106
|
+
# @api private
|
107
|
+
#
|
108
|
+
def active_subjects
|
109
|
+
@activity.select do |_subject, count|
|
110
|
+
count > 0
|
111
|
+
end.map(&:first)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Return current subject result
|
115
|
+
#
|
116
|
+
# @param [Subject] subject
|
117
|
+
#
|
118
|
+
# @return [Array<Subject::Result>]
|
119
|
+
#
|
120
|
+
# @api private
|
121
|
+
#
|
122
|
+
def subject_result(subject)
|
123
|
+
mutation_results = @aggregate[subject].sort_by(&:index)
|
124
|
+
|
125
|
+
Result::Subject.new(
|
126
|
+
subject: subject,
|
127
|
+
runtime: mutation_results.map(&:runtime).inject(0.0, :+),
|
128
|
+
mutation_results: mutation_results
|
129
|
+
)
|
130
|
+
end
|
131
|
+
|
132
|
+
end # Collector
|
133
|
+
end # Runner
|
134
|
+
end # Mutant
|
@@ -5,7 +5,15 @@ module Mutant
|
|
5
5
|
class Instance < self
|
6
6
|
|
7
7
|
NAME_INDEX = 0
|
8
|
-
SYMBOL
|
8
|
+
SYMBOL = '#'.freeze
|
9
|
+
|
10
|
+
# A list of methods that will warn when they are undefined
|
11
|
+
WARN_METHODS_UNDEFINED =
|
12
|
+
if RUBY_ENGINE.eql?('ruby')
|
13
|
+
[:initialize, :__send__, :object_id].freeze
|
14
|
+
else
|
15
|
+
EMPTY_ARRAY
|
16
|
+
end
|
9
17
|
|
10
18
|
# Test if method is public
|
11
19
|
#
|
@@ -26,10 +34,10 @@ module Mutant
|
|
26
34
|
#
|
27
35
|
def prepare
|
28
36
|
expected_warnings =
|
29
|
-
if
|
30
|
-
["#{__FILE__}:#{__LINE__ + 5}: warning: undefining
|
37
|
+
if WARN_METHODS_UNDEFINED.include?(name)
|
38
|
+
["#{__FILE__}:#{__LINE__ + 5}: warning: undefining `#{name}' may cause serious problems\n"]
|
31
39
|
else
|
32
|
-
|
40
|
+
EMPTY_ARRAY
|
33
41
|
end
|
34
42
|
WarningExpectation.new(expected_warnings).execute do
|
35
43
|
scope.send(:undef_method, name)
|
data/lib/mutant/version.rb
CHANGED
data/meta/block.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
Mutant::Meta::Example.add do
|
4
|
-
source 'foo
|
4
|
+
source 'foo { a; b }'
|
5
5
|
|
6
6
|
singleton_mutations
|
7
7
|
mutation 'foo { a }'
|
@@ -13,6 +13,7 @@ Mutant::Meta::Example.add do
|
|
13
13
|
mutation 'foo { nil; b }'
|
14
14
|
mutation 'foo { self; b }'
|
15
15
|
mutation 'foo'
|
16
|
+
mutation 'a; b'
|
16
17
|
end
|
17
18
|
|
18
19
|
Mutant::Meta::Example.add do
|
@@ -45,6 +46,21 @@ Mutant::Meta::Example.add do
|
|
45
46
|
mutation 'foo'
|
46
47
|
end
|
47
48
|
|
49
|
+
Mutant::Meta::Example.add do
|
50
|
+
source 'foo(a, b) {}'
|
51
|
+
|
52
|
+
singleton_mutations
|
53
|
+
mutation 'foo(a, nil) {}'
|
54
|
+
mutation 'foo(nil, b) {}'
|
55
|
+
mutation 'foo(self, b) {}'
|
56
|
+
mutation 'foo(a, self) {}'
|
57
|
+
mutation 'foo(a, b)'
|
58
|
+
mutation 'foo(a, b) { raise }'
|
59
|
+
mutation 'foo(a) {}'
|
60
|
+
mutation 'foo(b) {}'
|
61
|
+
mutation 'foo {}'
|
62
|
+
end
|
63
|
+
|
48
64
|
Mutant::Meta::Example.add do
|
49
65
|
source 'foo { |(a)| }'
|
50
66
|
|
data/meta/send.rb
CHANGED
@@ -1,5 +1,67 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
+
Mutant::Meta::Example.add do
|
4
|
+
source 'a > b'
|
5
|
+
|
6
|
+
singleton_mutations
|
7
|
+
mutation 'a == b'
|
8
|
+
mutation 'a.eql?(b)'
|
9
|
+
mutation 'a.equal?(b)'
|
10
|
+
mutation 'nil > b'
|
11
|
+
mutation 'self > b'
|
12
|
+
mutation 'a > nil'
|
13
|
+
mutation 'a > self'
|
14
|
+
mutation 'a'
|
15
|
+
mutation 'b'
|
16
|
+
end
|
17
|
+
|
18
|
+
Mutant::Meta::Example.add do
|
19
|
+
source 'a >= b'
|
20
|
+
|
21
|
+
singleton_mutations
|
22
|
+
mutation 'a > b'
|
23
|
+
mutation 'a == b'
|
24
|
+
mutation 'a.eql?(b)'
|
25
|
+
mutation 'a.equal?(b)'
|
26
|
+
mutation 'nil >= b'
|
27
|
+
mutation 'self >= b'
|
28
|
+
mutation 'a >= nil'
|
29
|
+
mutation 'a >= self'
|
30
|
+
mutation 'a'
|
31
|
+
mutation 'b'
|
32
|
+
end
|
33
|
+
|
34
|
+
Mutant::Meta::Example.add do
|
35
|
+
source 'a <= b'
|
36
|
+
|
37
|
+
singleton_mutations
|
38
|
+
mutation 'a < b'
|
39
|
+
mutation 'a == b'
|
40
|
+
mutation 'a.eql?(b)'
|
41
|
+
mutation 'a.equal?(b)'
|
42
|
+
mutation 'nil <= b'
|
43
|
+
mutation 'self <= b'
|
44
|
+
mutation 'a <= nil'
|
45
|
+
mutation 'a <= self'
|
46
|
+
mutation 'a'
|
47
|
+
mutation 'b'
|
48
|
+
end
|
49
|
+
|
50
|
+
Mutant::Meta::Example.add do
|
51
|
+
source 'a < b'
|
52
|
+
|
53
|
+
singleton_mutations
|
54
|
+
mutation 'a == b'
|
55
|
+
mutation 'a.eql?(b)'
|
56
|
+
mutation 'a.equal?(b)'
|
57
|
+
mutation 'nil < b'
|
58
|
+
mutation 'self < b'
|
59
|
+
mutation 'a < nil'
|
60
|
+
mutation 'a < self'
|
61
|
+
mutation 'a'
|
62
|
+
mutation 'b'
|
63
|
+
end
|
64
|
+
|
3
65
|
Mutant::Meta::Example.add do
|
4
66
|
source 'reverse_each'
|
5
67
|
|
@@ -7,6 +69,13 @@ Mutant::Meta::Example.add do
|
|
7
69
|
mutation 'each'
|
8
70
|
end
|
9
71
|
|
72
|
+
Mutant::Meta::Example.add do
|
73
|
+
source 'reverse_merge'
|
74
|
+
|
75
|
+
singleton_mutations
|
76
|
+
mutation 'merge'
|
77
|
+
end
|
78
|
+
|
10
79
|
Mutant::Meta::Example.add do
|
11
80
|
source 'reverse_map'
|
12
81
|
|
@@ -22,6 +91,33 @@ Mutant::Meta::Example.add do
|
|
22
91
|
mutation 'each'
|
23
92
|
end
|
24
93
|
|
94
|
+
Mutant::Meta::Example.add do
|
95
|
+
source 'foo.to_s'
|
96
|
+
|
97
|
+
singleton_mutations
|
98
|
+
mutation 'foo'
|
99
|
+
mutation 'self.to_s'
|
100
|
+
mutation 'foo.to_str'
|
101
|
+
end
|
102
|
+
|
103
|
+
Mutant::Meta::Example.add do
|
104
|
+
source 'foo.to_a'
|
105
|
+
|
106
|
+
singleton_mutations
|
107
|
+
mutation 'foo'
|
108
|
+
mutation 'self.to_a'
|
109
|
+
mutation 'foo.to_ary'
|
110
|
+
end
|
111
|
+
|
112
|
+
Mutant::Meta::Example.add do
|
113
|
+
source 'foo.to_i'
|
114
|
+
|
115
|
+
singleton_mutations
|
116
|
+
mutation 'foo'
|
117
|
+
mutation 'self.to_i'
|
118
|
+
mutation 'foo.to_int'
|
119
|
+
end
|
120
|
+
|
25
121
|
Mutant::Meta::Example.add do
|
26
122
|
source 'foo == bar'
|
27
123
|
|
@@ -36,6 +132,32 @@ Mutant::Meta::Example.add do
|
|
36
132
|
mutation 'foo.equal?(bar)'
|
37
133
|
end
|
38
134
|
|
135
|
+
Mutant::Meta::Example.add do
|
136
|
+
source 'foo.is_a?(bar)'
|
137
|
+
|
138
|
+
singleton_mutations
|
139
|
+
mutation 'foo'
|
140
|
+
mutation 'bar'
|
141
|
+
mutation 'foo.is_a?'
|
142
|
+
mutation 'foo.is_a?(nil)'
|
143
|
+
mutation 'foo.is_a?(self)'
|
144
|
+
mutation 'self.is_a?(bar)'
|
145
|
+
mutation 'foo.instance_of?(bar)'
|
146
|
+
end
|
147
|
+
|
148
|
+
Mutant::Meta::Example.add do
|
149
|
+
source 'foo.kind_of?(bar)'
|
150
|
+
|
151
|
+
singleton_mutations
|
152
|
+
mutation 'foo'
|
153
|
+
mutation 'bar'
|
154
|
+
mutation 'foo.kind_of?'
|
155
|
+
mutation 'foo.kind_of?(nil)'
|
156
|
+
mutation 'foo.kind_of?(self)'
|
157
|
+
mutation 'self.kind_of?(bar)'
|
158
|
+
mutation 'foo.instance_of?(bar)'
|
159
|
+
end
|
160
|
+
|
39
161
|
Mutant::Meta::Example.add do
|
40
162
|
source 'foo.gsub(a, b)'
|
41
163
|
|
@@ -259,7 +381,7 @@ Mutant::Meta::Example.add do
|
|
259
381
|
mutation 'self[*bar]'
|
260
382
|
end
|
261
383
|
|
262
|
-
(Mutant::AST::Types::BINARY_METHOD_OPERATORS - [:==, :eql?]).each do |operator|
|
384
|
+
(Mutant::AST::Types::BINARY_METHOD_OPERATORS - [:<=, :>=, :<, :>, :==, :eql?]).each do |operator|
|
263
385
|
Mutant::Meta::Example.add do
|
264
386
|
source "true #{operator} false"
|
265
387
|
|
data/mutant-rspec.gemspec
CHANGED
@@ -13,12 +13,12 @@ Gem::Specification.new do |gem|
|
|
13
13
|
gem.license = 'MIT'
|
14
14
|
|
15
15
|
gem.require_paths = %w[lib]
|
16
|
-
gem.files = `git ls-files -- lib/mutant/integration/rspec
|
17
|
-
gem.test_files = `git ls-files -- spec/{unit/mutant/rspec
|
16
|
+
gem.files = `git ls-files -- lib/mutant/integration/rspec.rb`.split("\n")
|
17
|
+
gem.test_files = `git ls-files -- spec/{unit,integration}/mutant/rspec/**/*.rb}`.split("\n")
|
18
18
|
gem.extra_rdoc_files = %w[TODO LICENSE]
|
19
19
|
|
20
20
|
gem.add_runtime_dependency('mutant', "~> #{gem.version}")
|
21
|
-
gem.add_runtime_dependency('rspec-core', '>= 2.14.1', '
|
21
|
+
gem.add_runtime_dependency('rspec-core', '>= 2.14.1', '< 3.1.0')
|
22
22
|
|
23
23
|
gem.add_development_dependency('bundler', '~> 1.3', '>= 1.3.5')
|
24
24
|
end
|
data/mutant.gemspec
CHANGED
@@ -37,7 +37,7 @@ Gem::Specification.new do |gem|
|
|
37
37
|
gem.add_runtime_dependency('inflecto', '~> 0.0.2')
|
38
38
|
gem.add_runtime_dependency('anima', '~> 0.2.0')
|
39
39
|
gem.add_runtime_dependency('concord', '~> 0.1.5')
|
40
|
-
gem.add_runtime_dependency('parallel', '~> 1.
|
40
|
+
gem.add_runtime_dependency('parallel', '~> 1.2.0')
|
41
41
|
|
42
42
|
gem.add_development_dependency('bundler', '~> 1.3', '>= 1.3.5')
|
43
43
|
end
|
@@ -1,209 +1,18 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require 'parallel'
|
4
|
-
require 'spec_helper'
|
5
|
-
|
6
|
-
describe 'Mutant on ruby corpus' do
|
7
|
-
|
8
|
-
ROOT = Pathname.new(__FILE__).parent.parent.parent.parent
|
9
|
-
|
10
|
-
TMP = ROOT.join('tmp').freeze
|
1
|
+
RSpec.describe 'Mutant on ruby corpus' do
|
11
2
|
|
12
3
|
before do
|
13
4
|
skip 'Corpus test is deactivated on 1.9.3' if RUBY_VERSION.eql?('1.9.3')
|
14
5
|
skip 'Corpus test is deactivated on RBX' if RUBY_ENGINE.eql?('rbx')
|
15
6
|
end
|
16
7
|
|
17
|
-
|
18
|
-
|
19
|
-
class Project
|
20
|
-
include Adamantium, Anima.new(
|
21
|
-
:name,
|
22
|
-
:repo_uri,
|
23
|
-
:exclude,
|
24
|
-
:mutation_coverage,
|
25
|
-
:mutation_generation,
|
26
|
-
:namespace,
|
27
|
-
:expect_coverage
|
28
|
-
)
|
29
|
-
|
30
|
-
# Verify mutation coverage
|
31
|
-
#
|
32
|
-
# @return [self]
|
33
|
-
# if successufl
|
34
|
-
#
|
35
|
-
# @raise [Exception]
|
36
|
-
#
|
37
|
-
def verify_mutation_coverage
|
38
|
-
checkout
|
39
|
-
Dir.chdir(repo_path) do
|
40
|
-
relative = ROOT.relative_path_from(repo_path)
|
41
|
-
devtools = ROOT.join('Gemfile.devtools').read
|
42
|
-
devtools << "gem 'mutant', path: '#{relative}'\n"
|
43
|
-
devtools << "gem 'mutant-rspec', path: '#{relative}'\n"
|
44
|
-
File.write(repo_path.join('Gemfile.devtools'), devtools)
|
45
|
-
lockfile = repo_path.join('Gemfile.lock')
|
46
|
-
lockfile.delete if lockfile.exist?
|
47
|
-
Bundler.with_clean_env do
|
48
|
-
system('bundle install')
|
49
|
-
system(%W[bundle exec mutant -I lib -r #{name} --score #{expect_coverage} --use rspec #{namespace}*])
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
# Verify mutation generation
|
55
|
-
#
|
56
|
-
# @return [self]
|
57
|
-
# if successful
|
58
|
-
#
|
59
|
-
# @raise [Exception]
|
60
|
-
# otherwise
|
61
|
-
#
|
62
|
-
# rubocop:disable MethodLength
|
63
|
-
def verify_mutation_generation
|
64
|
-
checkout
|
65
|
-
start = Time.now
|
66
|
-
paths = Pathname.glob(repo_path.join('**/*.rb')).sort_by(&:size).reverse
|
67
|
-
total = Parallel.map(paths, finish: method(:finish), start: method(:start)) do |path|
|
68
|
-
count = 0
|
69
|
-
node =
|
70
|
-
begin
|
71
|
-
Parser::CurrentRuby.parse(path.read)
|
72
|
-
rescue EncodingError, ArgumentError
|
73
|
-
nil # Make rubocop happy
|
74
|
-
end
|
75
|
-
if node
|
76
|
-
Mutant::Mutator::Node.each(node) do
|
77
|
-
count += 1
|
78
|
-
end
|
79
|
-
end
|
80
|
-
count
|
81
|
-
end.inject(0, :+)
|
82
|
-
took = Time.now - start
|
83
|
-
puts format(
|
84
|
-
'Total Mutations/Time/Parse-Errors: %s/%0.2fs - %0.2f/s',
|
85
|
-
total,
|
86
|
-
took,
|
87
|
-
total / took
|
88
|
-
)
|
89
|
-
self
|
90
|
-
end
|
91
|
-
|
92
|
-
# Checkout repository
|
93
|
-
#
|
94
|
-
# @return [self]
|
95
|
-
#
|
96
|
-
# @api private
|
97
|
-
#
|
98
|
-
def checkout
|
99
|
-
TMP.mkdir unless TMP.directory?
|
100
|
-
if repo_path.exist?
|
101
|
-
Dir.chdir(repo_path) do
|
102
|
-
system(%w[git pull -f origin master])
|
103
|
-
system(%w[git clean -f -d -x])
|
104
|
-
end
|
105
|
-
else
|
106
|
-
system(%W[git clone #{repo_uri} #{repo_path}])
|
107
|
-
end
|
108
|
-
self
|
109
|
-
end
|
110
|
-
memoize :checkout
|
111
|
-
|
112
|
-
private
|
113
|
-
|
114
|
-
# Return repository path
|
115
|
-
#
|
116
|
-
# @return [Pathname]
|
117
|
-
#
|
118
|
-
# @api private
|
119
|
-
#
|
120
|
-
def repo_path
|
121
|
-
TMP.join(name)
|
122
|
-
end
|
123
|
-
|
124
|
-
# Print start progress
|
125
|
-
#
|
126
|
-
# @param [Pathname] path
|
127
|
-
# @param [Fixnum] _index
|
128
|
-
# @param [Fixnum] count
|
129
|
-
#
|
130
|
-
# @return [undefined]
|
131
|
-
#
|
132
|
-
def start(path, _index)
|
133
|
-
MUTEX.synchronize do
|
134
|
-
puts format('Starting - %s', path)
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
# Print finish progress
|
139
|
-
#
|
140
|
-
# @param [Pathname] path
|
141
|
-
# @param [Fixnum] _index
|
142
|
-
# @param [Fixnum] count
|
143
|
-
#
|
144
|
-
# @return [undefined]
|
145
|
-
#
|
146
|
-
def finish(path, _index, count)
|
147
|
-
MUTEX.synchronize do
|
148
|
-
puts format('Mutations - %4i - %s', count, path)
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
# Helper method to execute system commands
|
153
|
-
#
|
154
|
-
# @param [Array<String>] arguments
|
155
|
-
#
|
156
|
-
# @api private
|
157
|
-
#
|
158
|
-
def system(arguments)
|
159
|
-
return if Kernel.system(*arguments)
|
160
|
-
if block_given?
|
161
|
-
yield
|
162
|
-
else
|
163
|
-
fail 'System command failed!'
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
LOADER = Morpher.build do
|
168
|
-
s(:block,
|
169
|
-
s(:guard, s(:primitive, Array)),
|
170
|
-
s(:map,
|
171
|
-
s(:block,
|
172
|
-
s(:guard, s(:primitive, Hash)),
|
173
|
-
s(:hash_transform,
|
174
|
-
s(:key_symbolize, :repo_uri, s(:guard, s(:primitive, String))),
|
175
|
-
s(:key_symbolize, :name, s(:guard, s(:primitive, String))),
|
176
|
-
s(:key_symbolize, :namespace, s(:guard, s(:primitive, String))),
|
177
|
-
s(:key_symbolize, :expect_coverage, s(:guard, s(:primitive, Float))),
|
178
|
-
s(:key_symbolize, :mutation_coverage,
|
179
|
-
s(:guard, s(:or, s(:primitive, TrueClass), s(:primitive, FalseClass)))),
|
180
|
-
s(:key_symbolize, :mutation_generation,
|
181
|
-
s(:guard, s(:or, s(:primitive, TrueClass), s(:primitive, FalseClass)))),
|
182
|
-
s(:key_symbolize, :exclude, s(:map, s(:guard, s(:primitive, String))))
|
183
|
-
),
|
184
|
-
s(:load_attribute_hash,
|
185
|
-
# NOTE: The domain param has no DSL currently!
|
186
|
-
Morpher::Evaluator::Transformer::Domain::Param.new(
|
187
|
-
Project,
|
188
|
-
[:repo_uri, :name, :exclude, :mutation_coverage, :mutation_generation]
|
189
|
-
)
|
190
|
-
)
|
191
|
-
)
|
192
|
-
)
|
193
|
-
)
|
194
|
-
end
|
195
|
-
|
196
|
-
ALL = LOADER.call(YAML.load_file(ROOT.join('spec', 'integrations.yml')))
|
197
|
-
end
|
198
|
-
|
199
|
-
Project::ALL.select(&:mutation_generation).each do |project|
|
8
|
+
Corpus::Project::ALL.select(&:mutation_generation).each do |project|
|
200
9
|
specify "#{project.name} does not fail on mutation generation" do
|
201
10
|
project.verify_mutation_generation
|
202
11
|
end
|
203
12
|
end
|
204
13
|
|
205
|
-
Project::ALL.select(&:mutation_coverage).each do |project|
|
206
|
-
specify "#{project.name} does have expected
|
14
|
+
Corpus::Project::ALL.select(&:mutation_coverage).each do |project|
|
15
|
+
specify "#{project.name} does have expected mutation coverage" do
|
207
16
|
project.verify_mutation_coverage
|
208
17
|
end
|
209
18
|
end
|