mutant 0.2.12 → 0.2.13
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.
- data/Changelog.md +4 -0
- data/Gemfile +1 -1
- data/Gemfile.devtools +39 -19
- data/README.md +3 -2
- data/TODO +3 -1
- data/config/flay.yml +1 -1
- data/config/site.reek +10 -3
- data/lib/mutant.rb +6 -73
- data/lib/mutant/constants.rb +49 -0
- data/lib/mutant/killer/forking.rb +6 -2
- data/lib/mutant/loader.rb +6 -79
- data/lib/mutant/matcher/scope_methods.rb +2 -2
- data/lib/mutant/mutation.rb +3 -2
- data/lib/mutant/mutation/filter/whitelist.rb +2 -0
- data/lib/mutant/mutator/node.rb +20 -9
- data/lib/mutant/mutator/node/assignment.rb +8 -1
- data/lib/mutant/mutator/node/block.rb +1 -0
- data/lib/mutant/mutator/node/default_arguments.rb +1 -0
- data/lib/mutant/mutator/node/formal_arguments_19.rb +1 -0
- data/lib/mutant/mutator/node/formal_arguments_19/default_mutations.rb +1 -0
- data/lib/mutant/mutator/node/{if_statement.rb → if.rb} +21 -6
- data/lib/mutant/mutator/node/iter_19.rb +1 -0
- data/lib/mutant/mutator/node/literal.rb +4 -3
- data/lib/mutant/mutator/node/literal/hash.rb +2 -1
- data/lib/mutant/mutator/node/literal/range.rb +2 -1
- data/lib/mutant/mutator/node/noop.rb +2 -0
- data/lib/mutant/mutator/node/receiver_case.rb +1 -19
- data/lib/mutant/mutator/node/send.rb +8 -136
- data/lib/mutant/mutator/node/send/binary_operator_method.rb +61 -0
- data/lib/mutant/mutator/node/send/with_arguments.rb +81 -0
- data/lib/mutant/mutator/node/super.rb +2 -0
- data/lib/mutant/mutator/node/{arguments.rb → when.rb} +4 -4
- data/lib/mutant/mutator/node/while.rb +2 -0
- data/lib/mutant/mutator/util/array.rb +2 -1
- data/lib/mutant/mutator/util/symbol.rb +1 -1
- data/lib/mutant/reporter/null.rb +1 -0
- data/lib/mutant/runner.rb +3 -4
- data/lib/mutant/singleton_methods.rb +28 -0
- data/lib/mutant/strategy.rb +2 -0
- data/lib/mutant/strategy/rspec/example_lookup.rb +4 -2
- data/mutant.gemspec +4 -4
- data/spec/shared/mutator_behavior.rb +1 -2
- data/spec/support/zombie.rb +35 -2
- data/spec/unit/mutant/loader/eval/class_methods/run_spec.rb +5 -6
- data/spec/unit/mutant/mutator/node/literal/float_spec.rb +1 -1
- data/spec/unit/mutant/mutator/node/send/mutation_spec.rb +61 -61
- metadata +12 -10
- data/spec/unit/mutant/loader/rubinius/class_methods/run_spec.rb +0 -42
@@ -0,0 +1,61 @@
|
|
1
|
+
module Mutant
|
2
|
+
class Mutator
|
3
|
+
class Node
|
4
|
+
class Send
|
5
|
+
|
6
|
+
# Mutator for sends that correspond to a binary operator
|
7
|
+
class BinaryOperatorMethod < Node
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
# Emit mutations
|
12
|
+
#
|
13
|
+
# @return [undefined]
|
14
|
+
#
|
15
|
+
# @api private
|
16
|
+
#
|
17
|
+
def dispatch
|
18
|
+
emit_left_mutations
|
19
|
+
emit_right_mutations
|
20
|
+
emit(right)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Emit left mutations
|
24
|
+
#
|
25
|
+
# @return [undefined]
|
26
|
+
#
|
27
|
+
# @api private
|
28
|
+
#
|
29
|
+
def emit_left_mutations
|
30
|
+
emit_attribute_mutations(:receiver)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Return right
|
34
|
+
#
|
35
|
+
# @return [Rubinius::AST::Node]
|
36
|
+
#
|
37
|
+
# @api private
|
38
|
+
#
|
39
|
+
def right
|
40
|
+
node.arguments.array.first
|
41
|
+
end
|
42
|
+
|
43
|
+
# Emit right mutations
|
44
|
+
#
|
45
|
+
# @return [undefined]
|
46
|
+
#
|
47
|
+
# @api private
|
48
|
+
#
|
49
|
+
def emit_right_mutations
|
50
|
+
Mutator.each(right).each do |mutated|
|
51
|
+
dup = dup_node
|
52
|
+
dup.arguments.array[0] = mutated
|
53
|
+
emit(dup)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Mutant
|
2
|
+
class Mutator
|
3
|
+
class Node
|
4
|
+
class Send
|
5
|
+
|
6
|
+
# Mutator for send with arguments
|
7
|
+
class WithArguments < self
|
8
|
+
|
9
|
+
handle(Rubinius::AST::SendWithArguments)
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
# Emit mutations
|
14
|
+
#
|
15
|
+
# @return [undefined]
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
#
|
19
|
+
def dispatch
|
20
|
+
super
|
21
|
+
|
22
|
+
if binary_operator?
|
23
|
+
run(BinaryOperatorMethod)
|
24
|
+
return
|
25
|
+
end
|
26
|
+
|
27
|
+
emit_send_remove_mutation
|
28
|
+
emit_argument_mutations
|
29
|
+
end
|
30
|
+
|
31
|
+
# Test if message is a binary operator
|
32
|
+
#
|
33
|
+
# @return [true]
|
34
|
+
# if message is a binary operator
|
35
|
+
#
|
36
|
+
# @return [false]
|
37
|
+
# otherwise
|
38
|
+
#
|
39
|
+
# @api private
|
40
|
+
#
|
41
|
+
def binary_operator?
|
42
|
+
Mutant::BINARY_METHOD_OPERATORS.include?(node.name)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Emit argument mutations
|
46
|
+
#
|
47
|
+
# @api private
|
48
|
+
#
|
49
|
+
# @return [undefined]
|
50
|
+
#
|
51
|
+
# @api private
|
52
|
+
#
|
53
|
+
def emit_argument_mutations
|
54
|
+
emit_attribute_mutations(:arguments) do |mutation|
|
55
|
+
if mutation.arguments.array.empty?
|
56
|
+
mutation = new_send(receiver, node.name)
|
57
|
+
mutation.privately = node.privately
|
58
|
+
mutation
|
59
|
+
else
|
60
|
+
mutation
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Emit send remove mutation
|
66
|
+
#
|
67
|
+
# @return [undefined]
|
68
|
+
#
|
69
|
+
# @api private
|
70
|
+
#
|
71
|
+
def emit_send_remove_mutation
|
72
|
+
array = node.arguments.array
|
73
|
+
return unless array.length == 1
|
74
|
+
emit(array.first)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -2,6 +2,7 @@ module Mutant
|
|
2
2
|
class Mutator
|
3
3
|
class Node
|
4
4
|
|
5
|
+
# Mutator for super without parantheses
|
5
6
|
class ZSuper < self
|
6
7
|
|
7
8
|
handle(Rubinius::AST::ZSuper)
|
@@ -18,6 +19,7 @@ module Mutant
|
|
18
19
|
|
19
20
|
end
|
20
21
|
|
22
|
+
# Mutator for super with parantheses
|
21
23
|
class Super < self
|
22
24
|
handle(Rubinius::AST::Super)
|
23
25
|
|
@@ -2,10 +2,10 @@ module Mutant
|
|
2
2
|
class Mutator
|
3
3
|
class Node
|
4
4
|
|
5
|
-
# Mutator for
|
6
|
-
class
|
5
|
+
# Mutator for Rubinius::AST::When nodes
|
6
|
+
class When < self
|
7
7
|
|
8
|
-
handle(Rubinius::AST::
|
8
|
+
handle(Rubinius::AST::When)
|
9
9
|
|
10
10
|
private
|
11
11
|
|
@@ -16,7 +16,7 @@ module Mutant
|
|
16
16
|
# @api private
|
17
17
|
#
|
18
18
|
def dispatch
|
19
|
-
emit_attribute_mutations(:
|
19
|
+
emit_attribute_mutations(:body)
|
20
20
|
end
|
21
21
|
|
22
22
|
end
|
@@ -7,6 +7,7 @@ module Mutant
|
|
7
7
|
|
8
8
|
handle(::Array)
|
9
9
|
|
10
|
+
# Element presence mutator
|
10
11
|
class Presence < Util
|
11
12
|
|
12
13
|
private
|
@@ -27,6 +28,7 @@ module Mutant
|
|
27
28
|
|
28
29
|
end
|
29
30
|
|
31
|
+
# Array element mutator
|
30
32
|
class Element < Util
|
31
33
|
|
32
34
|
private
|
@@ -40,7 +42,6 @@ module Mutant
|
|
40
42
|
def dispatch
|
41
43
|
input.each_with_index do |element, index|
|
42
44
|
dup = dup_input
|
43
|
-
|
44
45
|
Mutator.each(element).each do |mutation|
|
45
46
|
dup[index]=mutation
|
46
47
|
emit(dup)
|
data/lib/mutant/reporter/null.rb
CHANGED
data/lib/mutant/runner.rb
CHANGED
@@ -47,11 +47,10 @@ module Mutant
|
|
47
47
|
def initialize(config)
|
48
48
|
@config, @errors = config, []
|
49
49
|
|
50
|
-
reporter
|
51
|
-
|
50
|
+
util_reporter = reporter
|
51
|
+
util_reporter.config(config)
|
52
52
|
run
|
53
|
-
|
54
|
-
reporter.errors(@errors)
|
53
|
+
util_reporter.errors(@errors)
|
55
54
|
end
|
56
55
|
|
57
56
|
# Return reporter
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# Singleton methods are defined here so zombie can pick them up
|
2
|
+
module Mutant
|
3
|
+
|
4
|
+
# Define instance of subclassed superclass as constant
|
5
|
+
#
|
6
|
+
# @param [Class] superclass
|
7
|
+
# @param [Symbol] name
|
8
|
+
#
|
9
|
+
# @return [self]
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
#
|
13
|
+
def self.define_singleton_subclass(name, superclass, &block)
|
14
|
+
klass = Class.new(superclass) do
|
15
|
+
|
16
|
+
def inspect; self.class.name; end
|
17
|
+
|
18
|
+
define_singleton_method(:name) do
|
19
|
+
"#{superclass.name}::#{name}".freeze
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
klass.class_eval(&block)
|
24
|
+
superclass.const_set(name, klass.new)
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
data/lib/mutant/strategy.rb
CHANGED
@@ -116,10 +116,12 @@ module Mutant
|
|
116
116
|
# @api private
|
117
117
|
#
|
118
118
|
def glob_expression
|
119
|
+
base = base_path
|
120
|
+
|
119
121
|
if mutation.subject.matcher.public?
|
120
|
-
"#{
|
122
|
+
"#{base}/#{spec_file}"
|
121
123
|
else
|
122
|
-
"#{
|
124
|
+
"#{base}/*_spec.rb"
|
123
125
|
end
|
124
126
|
end
|
125
127
|
|
data/mutant.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |gem|
|
4
4
|
gem.name = 'mutant'
|
5
|
-
gem.version = '0.2.
|
5
|
+
gem.version = '0.2.13'
|
6
6
|
gem.authors = [ 'Markus Schirp' ]
|
7
7
|
gem.email = [ 'mbj@seonic.net' ]
|
8
|
-
gem.description = 'Mutation testing for ruby
|
9
|
-
gem.summary =
|
8
|
+
gem.description = 'Mutation testing for ruby'
|
9
|
+
gem.summary = 'Mutation testing tool for ruby under MRI and Rubinius'
|
10
10
|
gem.homepage = 'https://github.com/mbj/mutant'
|
11
11
|
|
12
12
|
gem.require_paths = [ 'lib' ]
|
@@ -15,7 +15,7 @@ Gem::Specification.new do |gem|
|
|
15
15
|
gem.extra_rdoc_files = %w[TODO LICENSE]
|
16
16
|
gem.executables = [ 'mutant' ]
|
17
17
|
|
18
|
-
gem.add_runtime_dependency('to_source', '~> 0.2.
|
18
|
+
gem.add_runtime_dependency('to_source', '~> 0.2.13')
|
19
19
|
gem.add_runtime_dependency('ice_nine', '~> 0.6.0')
|
20
20
|
gem.add_runtime_dependency('descendants_tracker', '~> 0.0.1')
|
21
21
|
gem.add_runtime_dependency('backports', '~> 2.6')
|
@@ -51,8 +51,7 @@ shared_examples_for 'a mutator' do
|
|
51
51
|
unexpected = (generated - expected_mutations).to_a
|
52
52
|
|
53
53
|
unless generated == expected_mutations
|
54
|
-
|
55
|
-
fail message
|
54
|
+
fail "Missing mutations:\n%s\nUnexpected mutations:\n%s" % [missing.join("\n----\n"), unexpected.join("\n----\n")]
|
56
55
|
end
|
57
56
|
end
|
58
57
|
end
|
data/spec/support/zombie.rb
CHANGED
@@ -26,6 +26,39 @@ module Zombie
|
|
26
26
|
end
|
27
27
|
private_class_method :root
|
28
28
|
|
29
|
+
class DummySubject
|
30
|
+
|
31
|
+
# Return line
|
32
|
+
#
|
33
|
+
# @return [Fixnum]
|
34
|
+
#
|
35
|
+
# @api private
|
36
|
+
#
|
37
|
+
attr_reader :source_line
|
38
|
+
|
39
|
+
# Return path
|
40
|
+
#
|
41
|
+
# @return [String]
|
42
|
+
#
|
43
|
+
# @api private
|
44
|
+
#
|
45
|
+
attr_reader :source_path
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# Initialize object
|
50
|
+
#
|
51
|
+
# @param [String] path
|
52
|
+
# @param [Fixnum] line
|
53
|
+
#
|
54
|
+
# @return [undefined]
|
55
|
+
#
|
56
|
+
# @api private
|
57
|
+
#
|
58
|
+
def initialize(path, line)
|
59
|
+
@source_path, @source_line = path, line
|
60
|
+
end
|
61
|
+
end
|
29
62
|
|
30
63
|
# Replace Mutant with Zombie namespace
|
31
64
|
#
|
@@ -51,7 +84,7 @@ module Zombie
|
|
51
84
|
node.body = Rubinius::AST::ModuleScope.new(scope.line, node.name, scope.body)
|
52
85
|
end
|
53
86
|
|
54
|
-
::Mutant::Loader::Eval.run(root, path, 1)
|
87
|
+
::Mutant::Loader::Eval.run(root, DummySubject.new(path, 1))
|
55
88
|
end
|
56
89
|
private_class_method :zombify
|
57
90
|
|
@@ -122,7 +155,7 @@ module Zombie
|
|
122
155
|
# Yeah looks very ugly but im currently to exited to do a cleanup.
|
123
156
|
#
|
124
157
|
def self.files
|
125
|
-
block = File.read(
|
158
|
+
block = File.read('lib/mutant.rb').to_ast
|
126
159
|
files = block.array.select do |node|
|
127
160
|
node.class == Rubinius::AST::SendWithArguments &&
|
128
161
|
node.receiver.class == Rubinius::AST::Self &&
|
@@ -2,15 +2,14 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Mutant::Loader::Eval, '.run' do
|
4
4
|
|
5
|
-
subject { object.run(node,
|
5
|
+
subject { object.run(node, mutation_subject) }
|
6
6
|
|
7
|
-
let(:object)
|
8
|
-
let(:
|
9
|
-
let(:
|
7
|
+
let(:object) { described_class }
|
8
|
+
let(:mutation_subject) { mock('Subject', :source_path => path, :source_line => line) }
|
9
|
+
let(:path) { 'test.rb' }
|
10
|
+
let(:line) { 1 }
|
10
11
|
|
11
12
|
let(:source) do
|
12
|
-
# This test case will blow up when not executed
|
13
|
-
# under toplevel binding.
|
14
13
|
<<-RUBY
|
15
14
|
class SomeNamespace
|
16
15
|
class Bar
|
@@ -55,6 +55,66 @@ describe Mutant::Mutator, 'send' do
|
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
+
context 'with block' do
|
59
|
+
let(:source) { 'foo() { a; b }' }
|
60
|
+
|
61
|
+
let(:mutations) do
|
62
|
+
mutations = []
|
63
|
+
mutations << 'foo() { a }'
|
64
|
+
mutations << 'foo() { b }'
|
65
|
+
mutations << 'foo() { }'
|
66
|
+
mutations << 'foo'
|
67
|
+
end
|
68
|
+
|
69
|
+
it_should_behave_like 'a mutator'
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'with block args' do
|
73
|
+
|
74
|
+
let(:source) { 'foo { |a, b| }' }
|
75
|
+
|
76
|
+
before do
|
77
|
+
Mutant::Random.stub(:hex_string => :random)
|
78
|
+
end
|
79
|
+
|
80
|
+
let(:mutations) do
|
81
|
+
mutations = []
|
82
|
+
mutations << 'foo'
|
83
|
+
mutations << 'foo { |a, b| Object.new }'
|
84
|
+
mutations << 'foo { |a, srandom| }'
|
85
|
+
mutations << 'foo { |srandom, b| }'
|
86
|
+
mutations << 'foo { |a| }'
|
87
|
+
mutations << 'foo { |b| }'
|
88
|
+
mutations << 'foo { || }'
|
89
|
+
end
|
90
|
+
|
91
|
+
it_should_behave_like 'a mutator'
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'with block pattern args' do
|
95
|
+
|
96
|
+
before do
|
97
|
+
Mutant::Random.stub(:hex_string => :random)
|
98
|
+
end
|
99
|
+
|
100
|
+
let(:source) { 'foo { |(a, b), c| }' }
|
101
|
+
|
102
|
+
let(:mutations) do
|
103
|
+
mutations = []
|
104
|
+
mutations << 'foo { || }'
|
105
|
+
mutations << 'foo { |a, b, c| }'
|
106
|
+
mutations << 'foo { |(a, b), c| Object.new }'
|
107
|
+
mutations << 'foo { |(a, b)| }'
|
108
|
+
mutations << 'foo { |c| }'
|
109
|
+
mutations << 'foo { |(srandom, b), c| }'
|
110
|
+
mutations << 'foo { |(a, srandom), c| }'
|
111
|
+
mutations << 'foo { |(a, b), srandom| }'
|
112
|
+
mutations << 'foo'
|
113
|
+
end
|
114
|
+
|
115
|
+
it_should_behave_like 'a mutator'
|
116
|
+
end
|
117
|
+
|
58
118
|
context 'send with arguments' do
|
59
119
|
|
60
120
|
context 'one argument' do
|
@@ -62,7 +122,7 @@ describe Mutant::Mutator, 'send' do
|
|
62
122
|
|
63
123
|
let(:mutations) do
|
64
124
|
mutations = []
|
65
|
-
mutations << 'foo
|
125
|
+
mutations << 'foo'
|
66
126
|
mutations << 'nil'
|
67
127
|
mutations << 'foo(Object.new)'
|
68
128
|
end
|
@@ -134,65 +194,5 @@ describe Mutant::Mutator, 'send' do
|
|
134
194
|
it_should_behave_like 'a mutator'
|
135
195
|
end
|
136
196
|
|
137
|
-
context 'with block' do
|
138
|
-
let(:source) { 'foo() { a; b }' }
|
139
|
-
|
140
|
-
let(:mutations) do
|
141
|
-
mutations = []
|
142
|
-
mutations << 'foo() { a }'
|
143
|
-
mutations << 'foo() { b }'
|
144
|
-
mutations << 'foo() { }'
|
145
|
-
mutations << 'foo'
|
146
|
-
end
|
147
|
-
|
148
|
-
it_should_behave_like 'a mutator'
|
149
|
-
end
|
150
|
-
|
151
|
-
context 'with block args' do
|
152
|
-
|
153
|
-
let(:source) { 'foo { |a, b| }' }
|
154
|
-
|
155
|
-
before do
|
156
|
-
Mutant::Random.stub(:hex_string => :random)
|
157
|
-
end
|
158
|
-
|
159
|
-
let(:mutations) do
|
160
|
-
mutations = []
|
161
|
-
mutations << 'foo'
|
162
|
-
mutations << 'foo() { |a, b| Object.new }'
|
163
|
-
mutations << 'foo() { |a, srandom| }'
|
164
|
-
mutations << 'foo() { |srandom, b| }'
|
165
|
-
mutations << 'foo() { |a| }'
|
166
|
-
mutations << 'foo() { |b| }'
|
167
|
-
mutations << 'foo() { || }'
|
168
|
-
end
|
169
|
-
|
170
|
-
it_should_behave_like 'a mutator'
|
171
|
-
end
|
172
|
-
|
173
|
-
context 'with block pattern args' do
|
174
|
-
|
175
|
-
before do
|
176
|
-
Mutant::Random.stub(:hex_string => :random)
|
177
|
-
end
|
178
|
-
|
179
|
-
let(:source) { 'foo { |(a, b), c| }' }
|
180
|
-
|
181
|
-
let(:mutations) do
|
182
|
-
mutations = []
|
183
|
-
mutations << 'foo() { || }'
|
184
|
-
mutations << 'foo() { |a, b, c| }'
|
185
|
-
mutations << 'foo() { |(a, b), c| Object.new }'
|
186
|
-
mutations << 'foo() { |(a, b)| }'
|
187
|
-
mutations << 'foo() { |c| }'
|
188
|
-
mutations << 'foo() { |(srandom, b), c| }'
|
189
|
-
mutations << 'foo() { |(a, srandom), c| }'
|
190
|
-
mutations << 'foo() { |(a, b), srandom| }'
|
191
|
-
mutations << 'foo'
|
192
|
-
end
|
193
|
-
|
194
|
-
it_should_behave_like 'a mutator'
|
195
|
-
end
|
196
|
-
|
197
197
|
end
|
198
198
|
end
|