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.
Files changed (58) hide show
  1. data/.travis.yml +3 -3
  2. data/Changelog.md +21 -0
  3. data/Gemfile.devtools +1 -0
  4. data/Guardfile +1 -1
  5. data/README.md +48 -4
  6. data/config/flay.yml +2 -2
  7. data/config/flog.yml +1 -1
  8. data/config/site.reek +3 -1
  9. data/lib/mutant.rb +14 -2
  10. data/lib/mutant/cli.rb +38 -39
  11. data/lib/mutant/context/scope.rb +37 -32
  12. data/lib/mutant/killer/forking.rb +53 -0
  13. data/lib/mutant/killer/rspec.rb +1 -1
  14. data/lib/mutant/killer/static.rb +14 -0
  15. data/lib/mutant/matcher.rb +2 -0
  16. data/lib/mutant/matcher/method.rb +2 -2
  17. data/lib/mutant/matcher/method/singleton.rb +2 -1
  18. data/lib/mutant/matcher/object_space.rb +1 -1
  19. data/lib/mutant/matcher/scope_methods.rb +2 -0
  20. data/lib/mutant/mutation.rb +26 -0
  21. data/lib/mutant/mutation/filter/whitelist.rb +1 -1
  22. data/lib/mutant/mutator.rb +52 -9
  23. data/lib/mutant/mutator/node.rb +18 -19
  24. data/lib/mutant/mutator/node/arguments.rb +156 -0
  25. data/lib/mutant/mutator/node/block.rb +7 -20
  26. data/lib/mutant/mutator/node/define.rb +18 -1
  27. data/lib/mutant/mutator/node/iter_19.rb +26 -0
  28. data/lib/mutant/mutator/node/local_variable_assignment.rb +25 -0
  29. data/lib/mutant/mutator/node/noop.rb +4 -0
  30. data/lib/mutant/mutator/node/send.rb +24 -10
  31. data/lib/mutant/mutator/util.rb +28 -1
  32. data/lib/mutant/random.rb +1 -0
  33. data/lib/mutant/reporter.rb +28 -0
  34. data/lib/mutant/reporter/cli.rb +90 -19
  35. data/lib/mutant/reporter/null.rb +5 -3
  36. data/lib/mutant/reporter/stats.rb +65 -9
  37. data/lib/mutant/runner.rb +41 -2
  38. data/lib/mutant/strategy.rb +46 -5
  39. data/lib/mutant/strategy/rspec.rb +11 -4
  40. data/lib/mutant/strategy/rspec/example_lookup.rb +30 -30
  41. data/lib/mutant/subject.rb +11 -0
  42. data/mutant.gemspec +3 -2
  43. data/spec/integration/mutant/loader_spec.rb +4 -4
  44. data/spec/shared/mutator_behavior.rb +13 -1
  45. data/spec/unit/mutant/context/scope/root_spec.rb +20 -8
  46. data/spec/unit/mutant/context/scope/unqualified_name_spec.rb +2 -2
  47. data/spec/unit/mutant/killer/rspec/class_methods/new_spec.rb +1 -1
  48. data/spec/unit/mutant/matcher/chain/each_spec.rb +6 -2
  49. data/spec/unit/mutant/mutator/node/define/mutation_spec.rb +76 -0
  50. data/spec/unit/mutant/mutator/node/send/mutation_spec.rb +80 -21
  51. data/spec/unit/mutant/strategy/rspec/example_lookup/spec_file_spec.rb +3 -3
  52. metadata +21 -10
  53. data/lib/mutant/inflector/defaults.rb +0 -64
  54. data/lib/mutant/inflector/inflections.rb +0 -211
  55. data/lib/mutant/inflector/methods.rb +0 -151
  56. data/lib/mutant/inflector/version.rb +0 -5
  57. data/locator.rb +0 -87
  58. 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
- end
21
-
22
- # Test if node is new
23
- #
24
- # FIXME: Remove this hack and make sure empty bodies are not generated
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)
@@ -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
@@ -1,6 +1,7 @@
1
1
  module Mutant
2
2
  # Module for generating random values
3
3
  module Random
4
+
4
5
  # Return random hex string
5
6
  #
6
7
  # @return [String]
@@ -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
@@ -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
- #colorized_diff(mutation.original_source, mutation.source)
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
- puts(colorize(color, "#{word}: #{killer.identification} (%02.2fs)" % killer.runtime))
115
+ print_killer(color, word, killer)
67
116
 
68
117
  if killer.fail?
69
- mutation = killer.mutation
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: #{stats.subject}"
91
- puts "mutations: #{stats.mutation}"
92
- puts "kills: #{stats.kill}"
93
- puts "alive: #{stats.alive}"
94
- puts "mtime: %02.2fs" % stats.time
95
- puts "rtime: %02.2fs" % stats.runtime
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 [IO] io
167
+ # @param [Config] config
119
168
  #
120
169
  # @return [undefined]
121
170
  #
122
171
  # @api private
123
172
  #
124
- def initialize(io)
125
- @io = io
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.original_source, killer.mutation_source)
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 [String] original
188
- # @param [String] current
237
+ # @param [Mutation] mutation
189
238
  #
190
- # @return [self]
239
+ # @return [undefined]
191
240
  #
192
241
  # @api private
193
242
  #
194
- def colorized_diff(original, current)
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]