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.
Files changed (48) hide show
  1. data/Changelog.md +4 -0
  2. data/Gemfile +1 -1
  3. data/Gemfile.devtools +39 -19
  4. data/README.md +3 -2
  5. data/TODO +3 -1
  6. data/config/flay.yml +1 -1
  7. data/config/site.reek +10 -3
  8. data/lib/mutant.rb +6 -73
  9. data/lib/mutant/constants.rb +49 -0
  10. data/lib/mutant/killer/forking.rb +6 -2
  11. data/lib/mutant/loader.rb +6 -79
  12. data/lib/mutant/matcher/scope_methods.rb +2 -2
  13. data/lib/mutant/mutation.rb +3 -2
  14. data/lib/mutant/mutation/filter/whitelist.rb +2 -0
  15. data/lib/mutant/mutator/node.rb +20 -9
  16. data/lib/mutant/mutator/node/assignment.rb +8 -1
  17. data/lib/mutant/mutator/node/block.rb +1 -0
  18. data/lib/mutant/mutator/node/default_arguments.rb +1 -0
  19. data/lib/mutant/mutator/node/formal_arguments_19.rb +1 -0
  20. data/lib/mutant/mutator/node/formal_arguments_19/default_mutations.rb +1 -0
  21. data/lib/mutant/mutator/node/{if_statement.rb → if.rb} +21 -6
  22. data/lib/mutant/mutator/node/iter_19.rb +1 -0
  23. data/lib/mutant/mutator/node/literal.rb +4 -3
  24. data/lib/mutant/mutator/node/literal/hash.rb +2 -1
  25. data/lib/mutant/mutator/node/literal/range.rb +2 -1
  26. data/lib/mutant/mutator/node/noop.rb +2 -0
  27. data/lib/mutant/mutator/node/receiver_case.rb +1 -19
  28. data/lib/mutant/mutator/node/send.rb +8 -136
  29. data/lib/mutant/mutator/node/send/binary_operator_method.rb +61 -0
  30. data/lib/mutant/mutator/node/send/with_arguments.rb +81 -0
  31. data/lib/mutant/mutator/node/super.rb +2 -0
  32. data/lib/mutant/mutator/node/{arguments.rb → when.rb} +4 -4
  33. data/lib/mutant/mutator/node/while.rb +2 -0
  34. data/lib/mutant/mutator/util/array.rb +2 -1
  35. data/lib/mutant/mutator/util/symbol.rb +1 -1
  36. data/lib/mutant/reporter/null.rb +1 -0
  37. data/lib/mutant/runner.rb +3 -4
  38. data/lib/mutant/singleton_methods.rb +28 -0
  39. data/lib/mutant/strategy.rb +2 -0
  40. data/lib/mutant/strategy/rspec/example_lookup.rb +4 -2
  41. data/mutant.gemspec +4 -4
  42. data/spec/shared/mutator_behavior.rb +1 -2
  43. data/spec/support/zombie.rb +35 -2
  44. data/spec/unit/mutant/loader/eval/class_methods/run_spec.rb +5 -6
  45. data/spec/unit/mutant/mutator/node/literal/float_spec.rb +1 -1
  46. data/spec/unit/mutant/mutator/node/send/mutation_spec.rb +61 -61
  47. metadata +12 -10
  48. 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 arguments
6
- class Arguments < self
5
+ # Mutator for Rubinius::AST::When nodes
6
+ class When < self
7
7
 
8
- handle(Rubinius::AST::ActualArguments)
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(:array)
19
+ emit_attribute_mutations(:body)
20
20
  end
21
21
 
22
22
  end
@@ -1,6 +1,8 @@
1
1
  module Mutant
2
2
  class Mutator
3
3
  class Node
4
+
5
+ # Mutator for while expressions
4
6
  class While < self
5
7
 
6
8
  handle(Rubinius::AST::While)
@@ -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)
@@ -27,7 +27,7 @@ module Mutant
27
27
  # @return [false]
28
28
  # otherwise
29
29
  #
30
- # @pai private
30
+ # @api private
31
31
  #
32
32
  def ignore?
33
33
  input.to_s[0] == '_'
@@ -1,6 +1,7 @@
1
1
  module Mutant
2
2
  class Reporter
3
3
 
4
+ # Null reporter
4
5
  class Null < self
5
6
 
6
7
  # Report subject
@@ -47,11 +47,10 @@ module Mutant
47
47
  def initialize(config)
48
48
  @config, @errors = config, []
49
49
 
50
- reporter.config(config)
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
@@ -1,4 +1,6 @@
1
1
  module Mutant
2
+
3
+ # Abstract base class for killing strategies
2
4
  class Strategy
3
5
  include AbstractType, Adamantium::Flat, Equalizer.new
4
6
 
@@ -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
- "#{base_path}/#{spec_file}"
122
+ "#{base}/#{spec_file}"
121
123
  else
122
- "#{base_path}/*_spec.rb"
124
+ "#{base}/*_spec.rb"
123
125
  end
124
126
  end
125
127
 
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |gem|
4
4
  gem.name = 'mutant'
5
- gem.version = '0.2.12'
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 under rubinius'
9
- gem.summary = gem.description
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.8')
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
- message ="Missing mutations:\n%s\nUnexpected mutations:\n%s" % [missing.join("\n----\n"), unexpected.join("\n----\n")]
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
@@ -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("lib/mutant.rb").to_ast
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, file, line) }
5
+ subject { object.run(node, mutation_subject) }
6
6
 
7
- let(:object) { described_class }
8
- let(:file) { 'test.rb' }
9
- let(:line) { 1 }
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
@@ -11,7 +11,7 @@ describe Mutant::Mutator::Node::Literal, 'float' do
11
11
  mutations << random_float.to_s
12
12
  mutations << '0.0/0.0'
13
13
  mutations << '1.0/0.0'
14
- mutations << '-(1.0 / 0.0)'
14
+ mutations << '(-1.0 / 0.0)'
15
15
  mutations << '-10.0'
16
16
  end
17
17
 
@@ -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