mutant 0.2.4 → 0.2.5
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/.travis.yml +3 -3
- data/Changelog.md +21 -0
- data/Gemfile.devtools +1 -0
- data/Guardfile +1 -1
- data/README.md +48 -4
- data/config/flay.yml +2 -2
- data/config/flog.yml +1 -1
- data/config/site.reek +3 -1
- data/lib/mutant.rb +14 -2
- data/lib/mutant/cli.rb +38 -39
- data/lib/mutant/context/scope.rb +37 -32
- data/lib/mutant/killer/forking.rb +53 -0
- data/lib/mutant/killer/rspec.rb +1 -1
- data/lib/mutant/killer/static.rb +14 -0
- data/lib/mutant/matcher.rb +2 -0
- data/lib/mutant/matcher/method.rb +2 -2
- data/lib/mutant/matcher/method/singleton.rb +2 -1
- data/lib/mutant/matcher/object_space.rb +1 -1
- data/lib/mutant/matcher/scope_methods.rb +2 -0
- data/lib/mutant/mutation.rb +26 -0
- data/lib/mutant/mutation/filter/whitelist.rb +1 -1
- data/lib/mutant/mutator.rb +52 -9
- data/lib/mutant/mutator/node.rb +18 -19
- data/lib/mutant/mutator/node/arguments.rb +156 -0
- data/lib/mutant/mutator/node/block.rb +7 -20
- data/lib/mutant/mutator/node/define.rb +18 -1
- data/lib/mutant/mutator/node/iter_19.rb +26 -0
- data/lib/mutant/mutator/node/local_variable_assignment.rb +25 -0
- data/lib/mutant/mutator/node/noop.rb +4 -0
- data/lib/mutant/mutator/node/send.rb +24 -10
- data/lib/mutant/mutator/util.rb +28 -1
- data/lib/mutant/random.rb +1 -0
- data/lib/mutant/reporter.rb +28 -0
- data/lib/mutant/reporter/cli.rb +90 -19
- data/lib/mutant/reporter/null.rb +5 -3
- data/lib/mutant/reporter/stats.rb +65 -9
- data/lib/mutant/runner.rb +41 -2
- data/lib/mutant/strategy.rb +46 -5
- data/lib/mutant/strategy/rspec.rb +11 -4
- data/lib/mutant/strategy/rspec/example_lookup.rb +30 -30
- data/lib/mutant/subject.rb +11 -0
- data/mutant.gemspec +3 -2
- data/spec/integration/mutant/loader_spec.rb +4 -4
- data/spec/shared/mutator_behavior.rb +13 -1
- data/spec/unit/mutant/context/scope/root_spec.rb +20 -8
- data/spec/unit/mutant/context/scope/unqualified_name_spec.rb +2 -2
- data/spec/unit/mutant/killer/rspec/class_methods/new_spec.rb +1 -1
- data/spec/unit/mutant/matcher/chain/each_spec.rb +6 -2
- data/spec/unit/mutant/mutator/node/define/mutation_spec.rb +76 -0
- data/spec/unit/mutant/mutator/node/send/mutation_spec.rb +80 -21
- data/spec/unit/mutant/strategy/rspec/example_lookup/spec_file_spec.rb +3 -3
- metadata +21 -10
- data/lib/mutant/inflector/defaults.rb +0 -64
- data/lib/mutant/inflector/inflections.rb +0 -211
- data/lib/mutant/inflector/methods.rb +0 -151
- data/lib/mutant/inflector/version.rb +0 -5
- data/locator.rb +0 -87
- data/spec/unit/mutant/context/scope/class_methods/build_spec.rb +0 -29
@@ -16,28 +16,15 @@ module Mutant
|
|
16
16
|
#
|
17
17
|
def dispatch
|
18
18
|
array = input.array
|
19
|
-
emit_attribute_mutations(:array)
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
#
|
26
|
-
# @param [Rubinius::AST::Node]
|
27
|
-
#
|
28
|
-
# @return [true]
|
29
|
-
# if node is new
|
30
|
-
#
|
31
|
-
# @return [false]
|
32
|
-
# otherwise
|
33
|
-
#
|
34
|
-
def new?(node)
|
35
|
-
if node.array.empty?
|
36
|
-
node.array << new_nil
|
19
|
+
emit_attribute_mutations(:array) do |mutation|
|
20
|
+
array = mutation.array
|
21
|
+
# Do not generate empty bodies
|
22
|
+
if array.empty?
|
23
|
+
array << new_nil
|
24
|
+
end
|
37
25
|
end
|
38
|
-
|
39
|
-
super
|
40
26
|
end
|
27
|
+
|
41
28
|
end
|
42
29
|
end
|
43
30
|
end
|
@@ -4,9 +4,26 @@ module Mutant
|
|
4
4
|
class Define < self
|
5
5
|
|
6
6
|
handle(Rubinius::AST::Define)
|
7
|
-
handle(Rubinius::AST::DefineSingleton)
|
8
7
|
handle(Rubinius::AST::DefineSingletonScope)
|
9
8
|
|
9
|
+
private
|
10
|
+
|
11
|
+
# Emit mutations
|
12
|
+
#
|
13
|
+
# @return [undefined]
|
14
|
+
#
|
15
|
+
# @api private
|
16
|
+
#
|
17
|
+
def dispatch
|
18
|
+
emit_attribute_mutations(:body)
|
19
|
+
emit_attribute_mutations(:arguments)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class DefineSingleton < self
|
24
|
+
|
25
|
+
handle(Rubinius::AST::DefineSingleton)
|
26
|
+
|
10
27
|
private
|
11
28
|
|
12
29
|
# Emit mutations
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Mutant
|
2
|
+
class Mutator
|
3
|
+
class Node
|
4
|
+
# Emitter for mutations on 19 blocks
|
5
|
+
class Iter19 < self
|
6
|
+
|
7
|
+
handle(Rubinius::AST::Iter19)
|
8
|
+
|
9
|
+
# Emit mutants
|
10
|
+
#
|
11
|
+
# @return [undefined]
|
12
|
+
#
|
13
|
+
# @api private
|
14
|
+
#
|
15
|
+
def dispatch
|
16
|
+
emit_attribute_mutations(:body)
|
17
|
+
emit_attribute_mutations(:arguments) do |mutation|
|
18
|
+
arguments = mutation.arguments
|
19
|
+
arguments.names = arguments.required + arguments.optional
|
20
|
+
end if node.arguments
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Mutant
|
2
|
+
class Mutator
|
3
|
+
class Node
|
4
|
+
class LocalVariableAssignment < self
|
5
|
+
|
6
|
+
handle(Rubinius::AST::LocalVariableAssignment)
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
# Emit mutants
|
11
|
+
#
|
12
|
+
# @return [undefined]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
#
|
16
|
+
def dispatch
|
17
|
+
emit_attribute_mutations(:name)
|
18
|
+
emit_attribute_mutations(:value)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
@@ -16,6 +16,8 @@ module Mutant
|
|
16
16
|
handle(Rubinius::AST::And)
|
17
17
|
handle(Rubinius::AST::Defined)
|
18
18
|
handle(Rubinius::AST::Super)
|
19
|
+
handle(Rubinius::AST::Next)
|
20
|
+
handle(Rubinius::AST::Break)
|
19
21
|
handle(Rubinius::AST::Match3)
|
20
22
|
handle(Rubinius::AST::ZSuper)
|
21
23
|
handle(Rubinius::AST::MultipleAssignment)
|
@@ -34,9 +36,11 @@ module Mutant
|
|
34
36
|
handle(Rubinius::AST::File)
|
35
37
|
handle(Rubinius::AST::DynamicRegex)
|
36
38
|
handle(Rubinius::AST::OpAssignOr19)
|
39
|
+
handle(Rubinius::AST::BlockPass19)
|
37
40
|
handle(Rubinius::AST::OpAssign1)
|
38
41
|
handle(Rubinius::AST::Or)
|
39
42
|
handle(Rubinius::AST::ConstantAccess)
|
43
|
+
handle(Rubinius::AST::Yield)
|
40
44
|
|
41
45
|
private
|
42
46
|
|
@@ -8,6 +8,30 @@ module Mutant
|
|
8
8
|
|
9
9
|
private
|
10
10
|
|
11
|
+
# Emit mutations
|
12
|
+
#
|
13
|
+
# @return [undefined]
|
14
|
+
#
|
15
|
+
# @api private
|
16
|
+
#
|
17
|
+
def dispatch
|
18
|
+
emit_implicit_self_receiver
|
19
|
+
emit_attribute_mutations(:block) if node.block
|
20
|
+
emit_block_absence_mutation
|
21
|
+
end
|
22
|
+
|
23
|
+
# Emit block absence mutation
|
24
|
+
#
|
25
|
+
# @return [undefined]
|
26
|
+
#
|
27
|
+
# @api private
|
28
|
+
#
|
29
|
+
def emit_block_absence_mutation
|
30
|
+
dup = dup_node
|
31
|
+
dup.block = nil
|
32
|
+
emit(dup)
|
33
|
+
end
|
34
|
+
|
11
35
|
# Return receiver AST node
|
12
36
|
#
|
13
37
|
# @return [Rubinius::AST::Node]
|
@@ -78,16 +102,6 @@ module Mutant
|
|
78
102
|
emit(mutant)
|
79
103
|
end
|
80
104
|
|
81
|
-
# Emit mutations
|
82
|
-
#
|
83
|
-
# @return [undefined]
|
84
|
-
#
|
85
|
-
# @api private
|
86
|
-
#
|
87
|
-
def dispatch
|
88
|
-
emit_implicit_self_receiver
|
89
|
-
end
|
90
|
-
|
91
105
|
class SendWithArguments < self
|
92
106
|
|
93
107
|
handle(Rubinius::AST::SendWithArguments)
|
data/lib/mutant/mutator/util.rb
CHANGED
@@ -5,7 +5,13 @@ module Mutant
|
|
5
5
|
|
6
6
|
# Run ulitity mutator
|
7
7
|
#
|
8
|
-
# @param [Object]
|
8
|
+
# @param [Object] object
|
9
|
+
#
|
10
|
+
# @return [Enumerator<Object>]
|
11
|
+
# if no block given
|
12
|
+
#
|
13
|
+
# @return [self]
|
14
|
+
# otherwise
|
9
15
|
#
|
10
16
|
# @api private
|
11
17
|
#
|
@@ -27,10 +33,31 @@ module Mutant
|
|
27
33
|
# @return [false]
|
28
34
|
# otherwise
|
29
35
|
#
|
36
|
+
# @api private
|
37
|
+
#
|
30
38
|
def new?(generated)
|
31
39
|
input != generated
|
32
40
|
end
|
33
41
|
|
42
|
+
# Mutators that mutates symbol inputs
|
43
|
+
class Symbol < self
|
44
|
+
|
45
|
+
handle(::Symbol)
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# Emit mutations
|
50
|
+
#
|
51
|
+
# @return [undefined]
|
52
|
+
#
|
53
|
+
# @api private
|
54
|
+
#
|
55
|
+
def dispatch
|
56
|
+
emit_new { :"s#{Random.hex_string}" }
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
34
61
|
# Mutators that mutates an array of inputs
|
35
62
|
class Array < self
|
36
63
|
|
data/lib/mutant/random.rb
CHANGED
data/lib/mutant/reporter.rb
CHANGED
@@ -52,5 +52,33 @@ module Mutant
|
|
52
52
|
# @api private
|
53
53
|
#
|
54
54
|
abstract_method :config
|
55
|
+
|
56
|
+
# Return output stream
|
57
|
+
#
|
58
|
+
# @return [IO]
|
59
|
+
#
|
60
|
+
# @api private
|
61
|
+
#
|
62
|
+
abstract_method :output_stream
|
63
|
+
|
64
|
+
# Return error stream
|
65
|
+
#
|
66
|
+
# @return [IO]
|
67
|
+
#
|
68
|
+
# @api private
|
69
|
+
#
|
70
|
+
abstract_method :error_stream
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
# Initialize reporter
|
75
|
+
#
|
76
|
+
# @param [Config] config
|
77
|
+
#
|
78
|
+
# @api private
|
79
|
+
#
|
80
|
+
def initialize(config)
|
81
|
+
@config = config
|
82
|
+
end
|
55
83
|
end
|
56
84
|
end
|
data/lib/mutant/reporter/cli.rb
CHANGED
@@ -17,6 +17,26 @@ module Mutant
|
|
17
17
|
puts("Subject: #{subject.identification}")
|
18
18
|
end
|
19
19
|
|
20
|
+
# Return error stream
|
21
|
+
#
|
22
|
+
# @return [IO]
|
23
|
+
#
|
24
|
+
# @api private
|
25
|
+
#
|
26
|
+
def error_stream
|
27
|
+
@config.debug? ? io : StringIO.new
|
28
|
+
end
|
29
|
+
|
30
|
+
# Return output stream
|
31
|
+
#
|
32
|
+
# @return [IO]
|
33
|
+
#
|
34
|
+
# @api private
|
35
|
+
#
|
36
|
+
def output_stream
|
37
|
+
@config.debug? ? io : StringIO.new
|
38
|
+
end
|
39
|
+
|
20
40
|
# Report mutation
|
21
41
|
#
|
22
42
|
# @param [Mutation] mutation
|
@@ -26,7 +46,10 @@ module Mutant
|
|
26
46
|
# @api private
|
27
47
|
#
|
28
48
|
def mutation(mutation)
|
29
|
-
|
49
|
+
if @config.debug?
|
50
|
+
colorized_diff(mutation)
|
51
|
+
end
|
52
|
+
|
30
53
|
self
|
31
54
|
end
|
32
55
|
|
@@ -45,6 +68,32 @@ module Mutant
|
|
45
68
|
puts "Strategy: #{config.strategy.inspect}"
|
46
69
|
end
|
47
70
|
|
71
|
+
# Report noop
|
72
|
+
#
|
73
|
+
# @param [Killer] killer
|
74
|
+
#
|
75
|
+
# @return [self]
|
76
|
+
#
|
77
|
+
# @api private
|
78
|
+
#
|
79
|
+
def noop(killer)
|
80
|
+
color, word =
|
81
|
+
if killer.fail?
|
82
|
+
[Color::GREEN, 'Alive']
|
83
|
+
else
|
84
|
+
[Color::RED, 'Killed']
|
85
|
+
end
|
86
|
+
|
87
|
+
print_killer(color, word, killer)
|
88
|
+
|
89
|
+
unless killer.fail?
|
90
|
+
puts(killer.mutation.source)
|
91
|
+
stats.noop_fail(killer)
|
92
|
+
end
|
93
|
+
|
94
|
+
self
|
95
|
+
end
|
96
|
+
|
48
97
|
# Reporter killer
|
49
98
|
#
|
50
99
|
# @param [Killer] killer
|
@@ -63,11 +112,10 @@ module Mutant
|
|
63
112
|
[Color::GREEN, 'Killed']
|
64
113
|
end
|
65
114
|
|
66
|
-
|
115
|
+
print_killer(color, word, killer)
|
67
116
|
|
68
117
|
if killer.fail?
|
69
|
-
|
70
|
-
colorized_diff(mutation.original_source, mutation.source)
|
118
|
+
colorized_diff(killer.mutation)
|
71
119
|
end
|
72
120
|
|
73
121
|
self
|
@@ -75,7 +123,7 @@ module Mutant
|
|
75
123
|
|
76
124
|
# Report errors
|
77
125
|
#
|
78
|
-
# @param [Enumerable<Killer>]
|
126
|
+
# @param [Enumerable<Killer>] errors
|
79
127
|
#
|
80
128
|
# @api private
|
81
129
|
#
|
@@ -87,12 +135,13 @@ module Mutant
|
|
87
135
|
end
|
88
136
|
|
89
137
|
puts
|
90
|
-
puts "subjects:
|
91
|
-
puts "mutations:
|
92
|
-
puts "
|
93
|
-
puts "
|
94
|
-
puts "
|
95
|
-
puts "
|
138
|
+
puts "subjects: #{stats.subjects}"
|
139
|
+
puts "mutations: #{stats.mutations}"
|
140
|
+
puts "noop_fails: #{stats.noop_fails}"
|
141
|
+
puts "kills: #{stats.kills}"
|
142
|
+
puts "alive: #{stats.alive}"
|
143
|
+
puts "mtime: %02.2fs" % stats.time
|
144
|
+
puts "rtime: %02.2fs" % stats.runtime
|
96
145
|
end
|
97
146
|
|
98
147
|
# Return IO stream
|
@@ -115,14 +164,15 @@ module Mutant
|
|
115
164
|
|
116
165
|
# Initialize reporter
|
117
166
|
#
|
118
|
-
# @param [
|
167
|
+
# @param [Config] config
|
119
168
|
#
|
120
169
|
# @return [undefined]
|
121
170
|
#
|
122
171
|
# @api private
|
123
172
|
#
|
124
|
-
def initialize(
|
125
|
-
|
173
|
+
def initialize(config)
|
174
|
+
super
|
175
|
+
@io = $stdout
|
126
176
|
@stats = Stats.new
|
127
177
|
end
|
128
178
|
|
@@ -136,7 +186,7 @@ module Mutant
|
|
136
186
|
#
|
137
187
|
def failure(killer)
|
138
188
|
puts(colorize(Color::RED, "!!! Mutant alive: #{killer.identification} !!!"))
|
139
|
-
colorized_diff(killer.
|
189
|
+
colorized_diff(killer.mutation)
|
140
190
|
puts("Took: (%02.2fs)" % killer.runtime)
|
141
191
|
end
|
142
192
|
|
@@ -184,24 +234,45 @@ module Mutant
|
|
184
234
|
|
185
235
|
# Write colorized diff
|
186
236
|
#
|
187
|
-
# @param [
|
188
|
-
# @param [String] current
|
237
|
+
# @param [Mutation] mutation
|
189
238
|
#
|
190
|
-
# @return [
|
239
|
+
# @return [undefined]
|
191
240
|
#
|
192
241
|
# @api private
|
193
242
|
#
|
194
|
-
def colorized_diff(
|
243
|
+
def colorized_diff(mutation)
|
244
|
+
if mutation.kind_of?(Mutation::Noop)
|
245
|
+
puts mutation.original_source
|
246
|
+
return
|
247
|
+
end
|
248
|
+
|
249
|
+
original, current = mutation.original_source, mutation.source
|
195
250
|
differ = Differ.new(original, current)
|
196
251
|
diff = color? ? differ.colorized_diff : differ.diff
|
252
|
+
|
197
253
|
# FIXME remove this branch before release
|
198
254
|
if diff.empty?
|
199
255
|
raise "Unable to create a diff, so ast mutation or to_source has an error!"
|
200
256
|
end
|
257
|
+
|
201
258
|
puts(diff)
|
202
259
|
self
|
203
260
|
end
|
204
261
|
|
262
|
+
# Print killer
|
263
|
+
#
|
264
|
+
# @param [Color] color
|
265
|
+
# @param [String] word
|
266
|
+
# @param [Killer] killer
|
267
|
+
#
|
268
|
+
# @return [undefined]
|
269
|
+
#
|
270
|
+
# @api private
|
271
|
+
#
|
272
|
+
def print_killer(color, word, killer)
|
273
|
+
puts(colorize(color, "#{word}: #{killer.identification} (%02.2fs)" % killer.runtime))
|
274
|
+
end
|
275
|
+
|
205
276
|
# Test for output to tty
|
206
277
|
#
|
207
278
|
# @return [true]
|