mutant 0.5.19 → 0.5.20
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 +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
|