mutant 0.5.19 → 0.5.20
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -4
- data/Changelog.md +9 -0
- data/Gemfile.devtools +15 -11
- data/README.md +10 -6
- data/config/flay.yml +1 -1
- data/config/reek.yml +3 -1
- data/lib/mutant.rb +41 -41
- data/lib/mutant/cache.rb +0 -2
- data/lib/mutant/cli.rb +6 -8
- data/lib/mutant/color.rb +0 -2
- data/lib/mutant/config.rb +0 -2
- data/lib/mutant/context.rb +0 -2
- data/lib/mutant/context/scope.rb +0 -2
- data/lib/mutant/delegator.rb +2 -0
- data/lib/mutant/diff.rb +4 -16
- data/lib/mutant/expression.rb +8 -7
- data/lib/mutant/expression/method.rb +3 -9
- data/lib/mutant/isolation.rb +65 -0
- data/lib/mutant/killer.rb +1 -3
- data/lib/mutant/loader.rb +0 -2
- data/lib/mutant/matcher.rb +0 -2
- data/lib/mutant/matcher/chain.rb +0 -2
- data/lib/mutant/matcher/filter.rb +0 -2
- data/lib/mutant/matcher/method.rb +1 -2
- data/lib/mutant/matcher/method/finder.rb +0 -2
- data/lib/mutant/matcher/method/instance.rb +1 -3
- data/lib/mutant/matcher/method/singleton.rb +0 -2
- data/lib/mutant/matcher/methods.rb +0 -2
- data/lib/mutant/matcher/namespace.rb +2 -6
- data/lib/mutant/matcher/null.rb +0 -2
- data/lib/mutant/matcher/scope.rb +0 -2
- data/lib/mutant/meta/example.rb +57 -18
- data/lib/mutant/meta/example/dsl.rb +10 -13
- data/lib/mutant/mutation.rb +0 -2
- data/lib/mutant/mutation/evil.rb +0 -2
- data/lib/mutant/mutation/neutral.rb +0 -2
- data/lib/mutant/mutator.rb +4 -31
- data/lib/mutant/mutator/node.rb +19 -18
- data/lib/mutant/mutator/node/and_asgn.rb +3 -6
- data/lib/mutant/mutator/node/argument.rb +0 -2
- data/lib/mutant/mutator/node/arguments.rb +47 -4
- data/lib/mutant/mutator/node/begin.rb +3 -10
- data/lib/mutant/mutator/node/binary.rb +0 -2
- data/lib/mutant/mutator/node/block.rb +0 -2
- data/lib/mutant/mutator/node/blockarg.rb +0 -2
- data/lib/mutant/mutator/node/break.rb +0 -2
- data/lib/mutant/mutator/node/case.rb +3 -6
- data/lib/mutant/mutator/node/conditional_loop.rb +0 -2
- data/lib/mutant/mutator/node/const.rb +1 -3
- data/lib/mutant/mutator/node/define.rb +0 -2
- data/lib/mutant/mutator/node/defined.rb +1 -3
- data/lib/mutant/mutator/node/dstr.rb +0 -2
- data/lib/mutant/mutator/node/dsym.rb +0 -2
- data/lib/mutant/mutator/node/generic.rb +0 -2
- data/lib/mutant/mutator/node/if.rb +8 -12
- data/lib/mutant/mutator/node/kwbegin.rb +0 -2
- data/lib/mutant/mutator/node/literal.rb +0 -2
- data/lib/mutant/mutator/node/literal/array.rb +2 -5
- data/lib/mutant/mutator/node/literal/boolean.rb +0 -2
- data/lib/mutant/mutator/node/literal/fixnum.rb +0 -2
- data/lib/mutant/mutator/node/literal/float.rb +0 -2
- data/lib/mutant/mutator/node/literal/hash.rb +0 -2
- data/lib/mutant/mutator/node/literal/nil.rb +0 -2
- data/lib/mutant/mutator/node/literal/range.rb +0 -2
- data/lib/mutant/mutator/node/literal/regex.rb +2 -4
- data/lib/mutant/mutator/node/literal/string.rb +0 -2
- data/lib/mutant/mutator/node/literal/symbol.rb +0 -2
- data/lib/mutant/mutator/node/masgn.rb +0 -2
- data/lib/mutant/mutator/node/mlhs.rb +0 -2
- data/lib/mutant/mutator/node/named_value/access.rb +0 -2
- data/lib/mutant/mutator/node/named_value/constant_assignment.rb +0 -2
- data/lib/mutant/mutator/node/named_value/variable_assignment.rb +0 -2
- data/lib/mutant/mutator/node/next.rb +0 -3
- data/lib/mutant/mutator/node/noop.rb +0 -2
- data/lib/mutant/mutator/node/nthref.rb +0 -2
- data/lib/mutant/mutator/node/op_asgn.rb +2 -4
- data/lib/mutant/mutator/node/or_asgn.rb +4 -7
- data/lib/mutant/mutator/node/resbody.rb +1 -1
- data/lib/mutant/mutator/node/rescue.rb +0 -2
- data/lib/mutant/mutator/node/restarg.rb +0 -2
- data/lib/mutant/mutator/node/return.rb +3 -6
- data/lib/mutant/mutator/node/send.rb +14 -8
- data/lib/mutant/mutator/node/send/attribute_assignment.rb +0 -2
- data/lib/mutant/mutator/node/send/binary.rb +1 -3
- data/lib/mutant/mutator/node/splat.rb +0 -2
- data/lib/mutant/mutator/node/super.rb +0 -2
- data/lib/mutant/mutator/node/when.rb +0 -2
- data/lib/mutant/mutator/node/yield.rb +0 -2
- data/lib/mutant/mutator/node/zsuper.rb +0 -2
- data/lib/mutant/mutator/registry.rb +0 -2
- data/lib/mutant/mutator/util.rb +0 -2
- data/lib/mutant/mutator/util/array.rb +0 -2
- data/lib/mutant/mutator/util/symbol.rb +0 -2
- data/lib/mutant/node_helpers.rb +11 -2
- data/lib/mutant/reporter.rb +0 -2
- data/lib/mutant/reporter/cli.rb +0 -2
- data/lib/mutant/reporter/cli/printer.rb +0 -2
- data/lib/mutant/reporter/cli/progress.rb +0 -2
- data/lib/mutant/reporter/cli/report.rb +0 -2
- data/lib/mutant/reporter/cli/report/config.rb +0 -2
- data/lib/mutant/reporter/cli/report/mutation.rb +5 -5
- data/lib/mutant/reporter/cli/report/subject.rb +0 -2
- data/lib/mutant/reporter/null.rb +0 -2
- data/lib/mutant/runner.rb +0 -2
- data/lib/mutant/runner/config.rb +0 -2
- data/lib/mutant/runner/mutation.rb +0 -2
- data/lib/mutant/runner/subject.rb +0 -2
- data/lib/mutant/strategy.rb +0 -2
- data/lib/mutant/subject.rb +0 -2
- data/lib/mutant/subject/method.rb +0 -2
- data/lib/mutant/subject/method/instance.rb +1 -3
- data/lib/mutant/subject/method/singleton.rb +0 -2
- data/lib/mutant/version.rb +1 -3
- data/lib/mutant/zombifier.rb +0 -2
- data/meta/begin.rb +10 -0
- data/meta/case.rb +0 -2
- data/meta/def.rb +19 -0
- data/mutant-rspec.gemspec +1 -1
- data/mutant.gemspec +1 -1
- data/spec/integration/mutant/corpus_spec.rb +81 -22
- data/spec/integration/mutant/null_spec.rb +1 -3
- data/spec/integration/mutant/rspec_spec.rb +6 -8
- data/spec/integration/mutant/test_mutator_handles_types_spec.rb +0 -2
- data/spec/integration/mutant/zombie_spec.rb +0 -2
- data/spec/integrations.yml +12 -1
- data/spec/shared/method_matcher_behavior.rb +0 -2
- data/spec/spec_helper.rb +0 -2
- data/spec/support/compress_helper.rb +0 -2
- data/spec/support/ice_nine_config.rb +0 -2
- data/spec/support/rspec.rb +0 -2
- data/spec/support/test_app.rb +0 -2
- data/spec/unit/mutant/cli_new_spec.rb +0 -2
- data/spec/unit/mutant/cli_run_spec.rb +0 -2
- data/spec/unit/mutant/context/root_spec.rb +0 -2
- data/spec/unit/mutant/context/scope/root_spec.rb +0 -2
- data/spec/unit/mutant/context/scope/unqualified_name_spec.rb +0 -2
- data/spec/unit/mutant/diff_spec.rb +0 -2
- data/spec/unit/mutant/expression/method_spec.rb +2 -4
- data/spec/unit/mutant/expression/namespace/flat_spec.rb +1 -3
- data/spec/unit/mutant/expression/namespace/recursive_spec.rb +10 -6
- data/spec/unit/mutant/isolation_spec.rb +61 -0
- data/spec/unit/mutant/loader/eval_spec.rb +0 -2
- data/spec/unit/mutant/matcher/chain_spec.rb +4 -15
- data/spec/unit/mutant/matcher/method/instance_spec.rb +0 -2
- data/spec/unit/mutant/matcher/method/singleton_spec.rb +0 -2
- data/spec/unit/mutant/matcher/methods/instance_spec.rb +0 -2
- data/spec/unit/mutant/matcher/methods/singleton_spec.rb +0 -2
- data/spec/unit/mutant/matcher/namespace_spec.rb +33 -31
- data/spec/unit/mutant/mutation_spec.rb +0 -2
- data/spec/unit/mutant/runner/config_spec.rb +0 -2
- data/spec/unit/mutant/runner/mutation_spec.rb +0 -2
- data/spec/unit/mutant/runner/subject_spec.rb +0 -2
- data/spec/unit/mutant/strategy_spec.rb +0 -2
- data/spec/unit/mutant/subject/context_spec.rb +0 -2
- data/spec/unit/mutant/subject/mutations_spec.rb +0 -2
- data/spec/unit/mutant/subject/node_spec.rb +0 -2
- data/spec/unit/mutant/subject_spec.rb +0 -2
- data/spec/unit/mutant/warning_filter_spec.rb +6 -0
- data/spec/unit/mutant_spec.rb +0 -55
- data/test_app/Gemfile.devtools +15 -11
- data/test_app/Gemfile.rspec3 +2 -2
- metadata +7 -7
- data/lib/mutant/constants.rb +0 -45
- data/spec/unit/mutant/mutator_spec.rb +0 -29
@@ -0,0 +1,65 @@
|
|
1
|
+
module Mutant
|
2
|
+
# Module providing isolationg
|
3
|
+
module Isolation
|
4
|
+
Error = Class.new(RuntimeError)
|
5
|
+
|
6
|
+
# Call block in isolation
|
7
|
+
#
|
8
|
+
# This isolation implements the fork strategy.
|
9
|
+
# Future strategies will probably use a process pool that can
|
10
|
+
# handle multiple mutation kills, in-isolation at once.
|
11
|
+
#
|
12
|
+
# @return [Object]
|
13
|
+
#
|
14
|
+
# @raise [Error]
|
15
|
+
#
|
16
|
+
# @api private
|
17
|
+
#
|
18
|
+
def self.call(&block)
|
19
|
+
reader, writer = IO.pipe.each(&:binmode)
|
20
|
+
|
21
|
+
pid = fork
|
22
|
+
|
23
|
+
if pid.nil?
|
24
|
+
begin
|
25
|
+
reader.close
|
26
|
+
writer.write(Marshal.dump(block.call))
|
27
|
+
Kernel.exit!(0)
|
28
|
+
ensure
|
29
|
+
Kernel.exit!(1)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
writer.close
|
34
|
+
|
35
|
+
read_result(reader, pid)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Read result from child process
|
39
|
+
#
|
40
|
+
# @param [IO] reader
|
41
|
+
# @param [Fixnum] pid
|
42
|
+
#
|
43
|
+
# @return [Object]
|
44
|
+
#
|
45
|
+
# @raise [Error]
|
46
|
+
#
|
47
|
+
def self.read_result(reader, pid)
|
48
|
+
begin
|
49
|
+
data = Marshal.load(reader.read)
|
50
|
+
rescue ArgumentError, TypeError
|
51
|
+
raise Error, 'Childprocess wrote un-unmarshallable data'
|
52
|
+
end
|
53
|
+
|
54
|
+
status = Process.waitpid2(pid).last
|
55
|
+
|
56
|
+
unless status.exitstatus.zero?
|
57
|
+
raise Error, "Childprocess exited with nonzero exit status: #{status.exitstatus}"
|
58
|
+
end
|
59
|
+
|
60
|
+
data
|
61
|
+
end
|
62
|
+
private_class_method :read_result
|
63
|
+
|
64
|
+
end # Isolator
|
65
|
+
end # Mutant
|
data/lib/mutant/killer.rb
CHANGED
data/lib/mutant/loader.rb
CHANGED
data/lib/mutant/matcher.rb
CHANGED
data/lib/mutant/matcher/chain.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module Mutant
|
4
2
|
class Matcher
|
5
3
|
# Matcher for subjects that are a specific method
|
6
4
|
class Method < self
|
7
5
|
include Adamantium::Flat, Concord::Public.new(:cache, :scope, :method)
|
6
|
+
include Equalizer.new(:identification)
|
8
7
|
|
9
8
|
# Methods within rbx kernel directory are precompiled and their source
|
10
9
|
# cannot be accessed via reading source location
|
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module Mutant
|
4
2
|
class Matcher
|
5
3
|
class Method
|
@@ -19,7 +17,7 @@ module Mutant
|
|
19
17
|
#
|
20
18
|
def self.build(cache, scope, method)
|
21
19
|
name = method.name
|
22
|
-
if scope.ancestors.include?(::Memoizable)
|
20
|
+
if scope.ancestors.include?(::Memoizable) && scope.memoized?(name)
|
23
21
|
return Memoized.new(cache, scope, method)
|
24
22
|
end
|
25
23
|
super
|
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module Mutant
|
4
2
|
class Matcher
|
5
3
|
|
@@ -36,7 +34,7 @@ module Mutant
|
|
36
34
|
# @api private
|
37
35
|
#
|
38
36
|
def pattern
|
39
|
-
/\A#{Regexp.escape(namespace)}(
|
37
|
+
/\A#{Regexp.escape(namespace)}(?:\z|::)/
|
40
38
|
end
|
41
39
|
memoize :pattern
|
42
40
|
|
@@ -95,9 +93,7 @@ Fix your lib to support normal ruby semantics!
|
|
95
93
|
MESSAGE
|
96
94
|
return
|
97
95
|
end
|
98
|
-
if pattern =~ name
|
99
|
-
yield scope
|
100
|
-
end
|
96
|
+
yield scope if pattern =~ name
|
101
97
|
end
|
102
98
|
|
103
99
|
end # Namespace
|
data/lib/mutant/matcher/null.rb
CHANGED
data/lib/mutant/matcher/scope.rb
CHANGED
data/lib/mutant/meta/example.rb
CHANGED
@@ -15,20 +15,33 @@ module Mutant
|
|
15
15
|
Verification.new(self, generated)
|
16
16
|
end
|
17
17
|
|
18
|
+
# Return source
|
19
|
+
#
|
20
|
+
# @return [String]
|
21
|
+
#
|
22
|
+
# @api private
|
23
|
+
#
|
24
|
+
def source
|
25
|
+
Unparser.unparse(node)
|
26
|
+
end
|
27
|
+
memoize :source
|
28
|
+
|
18
29
|
# Return generated mutations
|
19
30
|
#
|
20
|
-
# @return [Emumerable<
|
31
|
+
# @return [Emumerable<Mutant::Mutation>]
|
21
32
|
#
|
22
33
|
# @api private
|
23
34
|
#
|
24
35
|
def generated
|
25
|
-
Mutant::Mutator.each(node).
|
36
|
+
Mutant::Mutator.each(node).map do |node|
|
37
|
+
Mutant::Mutation::Evil.new(self, node)
|
38
|
+
end
|
26
39
|
end
|
27
40
|
memoize :generated
|
28
41
|
|
29
42
|
# Example verification
|
30
43
|
class Verification
|
31
|
-
include Adamantium::Flat, Concord.new(:example, :
|
44
|
+
include Adamantium::Flat, Concord.new(:example, :mutations)
|
32
45
|
|
33
46
|
# Test if mutation was verified successfully
|
34
47
|
#
|
@@ -37,7 +50,7 @@ module Mutant
|
|
37
50
|
# @api private
|
38
51
|
#
|
39
52
|
def success?
|
40
|
-
unparser.success? && missing.empty? && unexpected.empty?
|
53
|
+
unparser.success? && missing.empty? && unexpected.empty? && no_diffs.empty?
|
41
54
|
end
|
42
55
|
|
43
56
|
# Return error report
|
@@ -62,10 +75,21 @@ module Mutant
|
|
62
75
|
# @api private
|
63
76
|
#
|
64
77
|
def unexpected
|
65
|
-
|
78
|
+
mutations.map(&:node) - example.mutations
|
66
79
|
end
|
67
80
|
memoize :unexpected
|
68
81
|
|
82
|
+
# Return mutations with no diff to original
|
83
|
+
#
|
84
|
+
# @return [Enumerable<Mutation>]
|
85
|
+
#
|
86
|
+
# @api private
|
87
|
+
#
|
88
|
+
def no_diffs
|
89
|
+
mutations.select { |mutation| mutation.source.eql?(example.source) }
|
90
|
+
end
|
91
|
+
memoize :no_diffs
|
92
|
+
|
69
93
|
# Return mutation report
|
70
94
|
#
|
71
95
|
# @return [String]
|
@@ -78,9 +102,10 @@ module Mutant
|
|
78
102
|
'Original-AST:',
|
79
103
|
original_node.inspect,
|
80
104
|
'Original-Source:',
|
81
|
-
|
105
|
+
example.source,
|
82
106
|
*missing_report,
|
83
|
-
*unexpected_report
|
107
|
+
*unexpected_report,
|
108
|
+
*no_diff_report
|
84
109
|
].join("\n======\n")
|
85
110
|
end
|
86
111
|
|
@@ -91,9 +116,25 @@ module Mutant
|
|
91
116
|
# @api private
|
92
117
|
#
|
93
118
|
def missing_report
|
94
|
-
|
95
|
-
|
96
|
-
|
119
|
+
[
|
120
|
+
'Missing mutations:',
|
121
|
+
missing.map(&method(:format_mutation)).join("\n-----\n")
|
122
|
+
] if missing.any?
|
123
|
+
end
|
124
|
+
|
125
|
+
# Return no diff report
|
126
|
+
#
|
127
|
+
# @return [Array, nil]
|
128
|
+
#
|
129
|
+
# @api private
|
130
|
+
#
|
131
|
+
def no_diff_report
|
132
|
+
[
|
133
|
+
'No source diffs to original:',
|
134
|
+
no_diffs.map do |mutation|
|
135
|
+
"#{mutation.node.inspect}\n#{mutation.source}"
|
136
|
+
end
|
137
|
+
] if no_diffs.any?
|
97
138
|
end
|
98
139
|
|
99
140
|
# Return unexpected report
|
@@ -103,12 +144,10 @@ module Mutant
|
|
103
144
|
# @api private
|
104
145
|
#
|
105
146
|
def unexpected_report
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
]
|
111
|
-
end
|
147
|
+
[
|
148
|
+
'Unexpected mutations:',
|
149
|
+
unexpected.map(&method(:format_mutation)).join("\n-----\n")
|
150
|
+
] if unexpected.any?
|
112
151
|
end
|
113
152
|
|
114
153
|
# Format mutation
|
@@ -124,14 +163,14 @@ module Mutant
|
|
124
163
|
].join("\n")
|
125
164
|
end
|
126
165
|
|
127
|
-
# Return missing
|
166
|
+
# Return missing mutations
|
128
167
|
#
|
129
168
|
# @return [Array<Parser::AST::Node>]
|
130
169
|
#
|
131
170
|
# @api private
|
132
171
|
#
|
133
172
|
def missing
|
134
|
-
example.mutations -
|
173
|
+
example.mutations - mutations.map(&:node)
|
135
174
|
end
|
136
175
|
memoize :missing
|
137
176
|
|
@@ -81,8 +81,8 @@ module Mutant
|
|
81
81
|
# @api private
|
82
82
|
#
|
83
83
|
def singleton_mutations
|
84
|
-
mutation
|
85
|
-
mutation
|
84
|
+
mutation('nil')
|
85
|
+
mutation('self')
|
86
86
|
end
|
87
87
|
|
88
88
|
# Helper method to coerce input to node
|
@@ -97,17 +97,14 @@ module Mutant
|
|
97
97
|
# @api private
|
98
98
|
#
|
99
99
|
def node(input)
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
end
|
109
|
-
|
110
|
-
Unparser::Preprocessor.run(node)
|
100
|
+
case input
|
101
|
+
when String
|
102
|
+
Unparser::Preprocessor.run(Parser::CurrentRuby.parse(input))
|
103
|
+
when Parser::AST::Node
|
104
|
+
input
|
105
|
+
else
|
106
|
+
raise "Cannot coerce to node: #{source.inspect}"
|
107
|
+
end
|
111
108
|
end
|
112
109
|
|
113
110
|
end # DSL
|
data/lib/mutant/mutation.rb
CHANGED
data/lib/mutant/mutation/evil.rb
CHANGED
data/lib/mutant/mutator.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module Mutant
|
4
2
|
# Generator for mutations
|
5
3
|
class Mutator
|
@@ -7,7 +5,10 @@ module Mutant
|
|
7
5
|
|
8
6
|
# Run mutator on input
|
9
7
|
#
|
10
|
-
# @param [
|
8
|
+
# @param [Object] input
|
9
|
+
# the input to mutate
|
10
|
+
#
|
11
|
+
# @param [Mutator] parent
|
11
12
|
#
|
12
13
|
# @return [self]
|
13
14
|
#
|
@@ -119,34 +120,6 @@ module Mutant
|
|
119
120
|
emit!(object)
|
120
121
|
end
|
121
122
|
|
122
|
-
# Maximum amount of tries to generate a new object
|
123
|
-
MAX_TRIES = 3
|
124
|
-
|
125
|
-
# Call block until it generates a mutation
|
126
|
-
#
|
127
|
-
# @yield
|
128
|
-
# Execute block until object is generated where new?(object) returns true
|
129
|
-
#
|
130
|
-
# @return [self]
|
131
|
-
#
|
132
|
-
# @raise [RuntimeError]
|
133
|
-
# raises RuntimeError when no new node can be generated after MAX_TRIES.
|
134
|
-
#
|
135
|
-
# @api private
|
136
|
-
#
|
137
|
-
def emit_new
|
138
|
-
MAX_TRIES.times do
|
139
|
-
object = yield
|
140
|
-
|
141
|
-
if new?(object)
|
142
|
-
emit!(object)
|
143
|
-
return
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
raise "New AST could not be generated after #{MAX_TRIES} attempts"
|
148
|
-
end
|
149
|
-
|
150
123
|
# Call block with node
|
151
124
|
#
|
152
125
|
# @param [Parser::AST::Node] node
|