mutant 0.2.7 → 0.2.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. data/Changelog.md +9 -1
  2. data/Gemfile.devtools +5 -12
  3. data/LICENSE +20 -0
  4. data/README.md +25 -24
  5. data/TODO +10 -2
  6. data/config/flay.yml +2 -2
  7. data/config/flog.yml +1 -1
  8. data/config/site.reek +4 -2
  9. data/lib/mutant.rb +18 -9
  10. data/lib/mutant/context/scope.rb +3 -3
  11. data/lib/mutant/differ.rb +13 -1
  12. data/lib/mutant/killer.rb +1 -0
  13. data/lib/mutant/matcher/scope_methods.rb +16 -11
  14. data/lib/mutant/mutator.rb +10 -0
  15. data/lib/mutant/mutator/node.rb +5 -18
  16. data/lib/mutant/mutator/node/actual_arguments.rb +25 -0
  17. data/lib/mutant/mutator/node/arguments.rb +0 -155
  18. data/lib/mutant/mutator/node/default_arguments.rb +24 -0
  19. data/lib/mutant/mutator/node/formal_arguments_19.rb +40 -0
  20. data/lib/mutant/mutator/node/formal_arguments_19/default_mutations.rb +32 -0
  21. data/lib/mutant/mutator/node/formal_arguments_19/pattern_argument_expansion.rb +35 -0
  22. data/lib/mutant/mutator/node/formal_arguments_19/require_defaults.rb +37 -0
  23. data/lib/mutant/mutator/node/literal.rb +14 -0
  24. data/lib/mutant/mutator/node/literal/hash.rb +1 -7
  25. data/lib/mutant/mutator/node/pattern_arguments.rb +41 -0
  26. data/lib/mutant/mutator/node/pattern_variable.rb +23 -0
  27. data/lib/mutant/mutator/node/send.rb +76 -6
  28. data/lib/mutant/mutator/util.rb +0 -71
  29. data/lib/mutant/mutator/util/array.rb +70 -0
  30. data/lib/mutant/mutator/util/symbol.rb +39 -0
  31. data/mutant.gemspec +2 -2
  32. data/spec/shared/mutator_behavior.rb +3 -2
  33. data/spec/unit/mutant/mutator/node/define/mutation_spec.rb +12 -0
  34. data/spec/unit/mutant/mutator/node/send/mutation_spec.rb +24 -17
  35. metadata +15 -3
@@ -1,3 +1,11 @@
1
+ # v0.2.8 2012-12-29
2
+
3
+ * [feature] Do not mutate argument or local variable names beginning with an underscore
4
+ * [feature] Mutate unary calls ```coerce(object)``` => ```object```
5
+ * [feature] Mutate method call receivers ```foo.bar``` => ```foo```
6
+
7
+ [Compare v0.2.7..v0.2.8](https://github.com/mbj/mutant/compare/v0.2.7...v0.2.8)
8
+
1
9
  # v0.2.7 2012-12-21
2
10
 
3
11
  * [fixed] Use latest adamantium and ice_nine
@@ -16,7 +24,7 @@
16
24
  * [feature] Run noop mutation per subject to guard against initial failing specs
17
25
  * [feature] Mutate default into required arguments
18
26
  * [feature] Mutate default literals
19
- * [feature] Mutate unwinding of pattern args |(a, b), c] => |a, b, c|
27
+ * [feature] Mutate unwinding of pattern args ```|(a, b), c|``` => ```|a, b, c|```
20
28
  * [feature] Mutate define and block arguments
21
29
  * [feature] Mutate block arguments, inklusive pattern args
22
30
  * [feature] Recurse into block bodies
@@ -1,5 +1,5 @@
1
- group :development do
2
- gem 'rake', '~> 0.9.2'
1
+ group :development do
2
+ gem 'rake', '~> 10.0'
3
3
  gem 'rspec', '~> 2.12.0'
4
4
  gem 'yard', '~> 0.8.3'
5
5
  end
@@ -26,22 +26,15 @@ group :metrics do
26
26
  gem 'flog', '~> 2.5.1'
27
27
  gem 'reek', '~> 1.2.8', :git => 'https://github.com/dkubb/reek.git'
28
28
  gem 'roodi', '~> 2.1.0'
29
- gem 'yardstick', '~> 0.7.0'
30
- gem 'simplecov'
29
+ gem 'yardstick', '~> 0.8.0'
31
30
 
32
31
  platforms :ruby_18, :ruby_19 do
33
32
  # this indirectly depends on ffi which does not build on ruby-head
34
33
  gem 'yard-spellcheck', '~> 0.1.5'
35
34
  end
36
35
 
37
- platforms :mri_18 do
38
- gem 'arrayfields', '~> 4.7.4' # for metric_fu
39
- gem 'fattr', '~> 2.2.0' # for metric_fu
40
- gem 'json', '~> 1.7.3' # for metric_fu rake task
41
- gem 'map', '~> 6.0.1' # for metric_fu
42
- gem 'metric_fu', '~> 2.1.1'
43
- gem 'mspec', '~> 1.5.17'
44
- gem 'rcov', '~> 1.0.0'
36
+ platforms :mri_19 do
37
+ gem 'simplecov', '~> 0.7'
45
38
  end
46
39
 
47
40
  platforms :rbx do
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Markus Schirp
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -8,13 +8,26 @@ mutant
8
8
  Mutant is a mutation testing tool for ruby that aims to be better than existing mutation testers.
9
9
 
10
10
  The idea is that if code can be changed and your tests do not notice, either that code isn't being covered
11
- or it doesn't do anything (useful).
11
+ or it does not have a speced side effect.
12
12
 
13
- Mutant does currently only support 1.9 mode under rubinius or mri. It is a young project but already
14
- used in the DataMapper-2.0 project.
13
+ Mutant does currently only support 1.9 mode under Rubinius or MRI. Support for JRuby is planned.
14
+
15
+ Also it is easy to write a mutation killer for other test/spec frameworks than rspec2.
16
+ Just create your own Mutant::Killer subclass, and make sure I get a PR!
15
17
 
16
18
  See this [ASCII-Cast](http://ascii.io/a/1707) for mutant in action! (v0.2.1)
17
19
 
20
+ Using Mutant
21
+ ------------
22
+
23
+ The following projects adopted mutant.
24
+
25
+ * [dm-mapper](https://github.com/datamapper/dm-mapper)
26
+ * [virtus](https://github.com/solnic/virtus)
27
+ * various small/minor stuff under https://github.com/mbj
28
+
29
+ Feel free to ping me to add your project to the list!
30
+
18
31
  Installation
19
32
  ------------
20
33
 
@@ -23,6 +36,8 @@ Install the gem ``mutant`` via your preferred method.
23
36
  Examples
24
37
  --------
25
38
 
39
+ CLI will be simplified in the next releases, but currently stick with this:
40
+
26
41
  ```
27
42
  cd virtus
28
43
  # Run mutant on virtus namespace (that uses the dm-2 style spec layout)
@@ -39,7 +54,7 @@ Strategies
39
54
  ----------
40
55
 
41
56
  Mutation testing is slow. To make it fast the selection of the correct set of tests to run is the key.
42
- Mutant currently supports the following buildin strategies:
57
+ Mutant currently supports the following buildin strategies for selecting tests/specs.
43
58
 
44
59
  ### --rspec-dm2
45
60
 
@@ -68,6 +83,11 @@ This strategy executes all specs under ``./spec`` for each mutation.
68
83
  It is also plannned to allow explicit selections on specs to run and to support other test frameworks.
69
84
  Custom project specific strategies are also on the roadmap.
70
85
 
86
+ Alternatives
87
+ ------------
88
+
89
+ * [heckle](https://github.com/seattlerb/heckle)
90
+
71
91
  Credits
72
92
  -------
73
93
 
@@ -89,23 +109,4 @@ Contributing
89
109
  License
90
110
  -------
91
111
 
92
- Copyright (c) 2012 Markus Schirp
93
-
94
- Permission is hereby granted, free of charge, to any person obtaining
95
- a copy of this software and associated documentation files (the
96
- "Software"), to deal in the Software without restriction, including
97
- without limitation the rights to use, copy, modify, merge, publish,
98
- distribute, sublicense, and/or sell copies of the Software, and to
99
- permit persons to whom the Software is furnished to do so, subject to
100
- the following conditions:
101
-
102
- The above copyright notice and this permission notice shall be
103
- included in all copies or substantial portions of the Software.
104
-
105
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
106
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
107
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
108
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
109
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
110
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
111
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
112
+ See LICENSE file.
data/TODO CHANGED
@@ -1,6 +1,5 @@
1
1
  Code:
2
2
  * Test mutant with dynamically created zombie.
3
- * Replace nil or add "do not touch me object" to literal mutations.
4
3
  * Fix ugly code within default parameters
5
4
 
6
5
  AST:
@@ -9,9 +8,18 @@ AST:
9
8
  Mutations:
10
9
  * Add some kind of a "do not touch me object" that raises on all messages.
11
10
  It can be used to make sure each literal value is touched.
11
+ * Replace nil or add "do not touch me object" to literal mutations.
12
12
  * Mutate options on Regexp literals
13
- * Make sure loader does not change visibility of injected mutants
14
13
  * Add mutations for dynamic regexp symbol and string literals
14
+ * Mutate "def foo; bar; end" to "def foo; self; end"?
15
+ * Emit negative and positive mutations
16
+
17
+ Example of a negative mutation:
18
+ Mutations on local variables and arguments prefixed with an underscore would be emitted as
19
+ negative mutations that must be alive.
20
+
21
+ Loader:
22
+ * Make sure loader does not change visibility of injected mutants
15
23
 
16
24
  Killers:
17
25
  * Aggregate warnings on missing spec files
@@ -1,3 +1,3 @@
1
1
  ---
2
- threshold: 34 # Todo bring down to ~20
3
- total_score: 913
2
+ threshold: 25 # Todo bring down to ~20
3
+ total_score: 902
@@ -1,2 +1,2 @@
1
1
  ---
2
- threshold: 47.0
2
+ threshold: 44.0
@@ -10,8 +10,9 @@ UncommunicativeParameterName:
10
10
  LargeClass:
11
11
  max_methods: 10
12
12
  exclude:
13
- - "Mutant::Matcher::Method" # 13 methods
13
+ #- "Mutant::Matcher::Method" # 13 methods
14
14
  - "Mutant::Reporter::CLI" # 16 methods TODO Reduce!
15
+ - "Mutant::CLI" # 19 methods and 7 ivars, TODO Reduce!
15
16
  enabled: true
16
17
  max_instance_variables: 3
17
18
  UncommunicativeMethodName:
@@ -50,7 +51,8 @@ UncommunicativeModuleName:
50
51
  - !ruby/regexp /[0-9]$/
51
52
  NestedIterators:
52
53
  ignore_iterators: []
53
- exclude: []
54
+ exclude:
55
+ - Mutant#self.define_singleton_subclass
54
56
  enabled: true
55
57
  max_allowed_nesting: 1
56
58
  LongMethod:
@@ -13,12 +13,14 @@ require 'diff/lcs'
13
13
  require 'diff/lcs/hunk'
14
14
  require 'rspec'
15
15
 
16
- module IceNine
17
- class Freezer
18
- class Rubinius
19
- class AST < IceNine::Freezer::Object
20
- class Node < IceNine::Freezer::Object
21
- end
16
+ # Patch ice none to freeze nodes correctly
17
+ class IceNine::Freezer
18
+ # Rubinius namsepace
19
+ class Rubinius
20
+ # AST namespace
21
+ class AST < IceNine::Freezer::Object
22
+ # Node configuration
23
+ class Node < IceNine::Freezer::Object
22
24
  end
23
25
  end
24
26
  end
@@ -51,8 +53,6 @@ module Mutant
51
53
  self
52
54
  end
53
55
 
54
- PID = Process.pid
55
-
56
56
  end
57
57
 
58
58
  require 'mutant/support/method_object'
@@ -65,6 +65,8 @@ require 'mutant/mutation/filter/code'
65
65
  require 'mutant/mutation/filter/whitelist'
66
66
  require 'mutant/mutator/registry'
67
67
  require 'mutant/mutator/util'
68
+ require 'mutant/mutator/util/array'
69
+ require 'mutant/mutator/util/symbol'
68
70
  require 'mutant/mutator/node'
69
71
  require 'mutant/mutator/node/literal'
70
72
  require 'mutant/mutator/node/literal/boolean'
@@ -81,8 +83,15 @@ require 'mutant/mutator/node/literal/nil'
81
83
  require 'mutant/mutator/node/block'
82
84
  require 'mutant/mutator/node/noop'
83
85
  require 'mutant/mutator/node/send'
84
- require 'mutant/mutator/node/arguments'
85
86
  require 'mutant/mutator/node/define'
87
+ require 'mutant/mutator/node/formal_arguments_19'
88
+ require 'mutant/mutator/node/formal_arguments_19/default_mutations'
89
+ require 'mutant/mutator/node/formal_arguments_19/require_defaults'
90
+ require 'mutant/mutator/node/formal_arguments_19/pattern_argument_expansion'
91
+ require 'mutant/mutator/node/actual_arguments'
92
+ require 'mutant/mutator/node/pattern_arguments'
93
+ require 'mutant/mutator/node/pattern_variable'
94
+ require 'mutant/mutator/node/default_arguments'
86
95
  require 'mutant/mutator/node/return'
87
96
  require 'mutant/mutator/node/local_variable_assignment'
88
97
  require 'mutant/mutator/node/iter_19'
@@ -30,12 +30,12 @@ module Mutant
30
30
  # @api private
31
31
  #
32
32
  def self.wrap(scope, node)
33
- name = scope.name.split('::').last
33
+ name = scope.name.split('::').last.to_sym
34
34
  case scope
35
35
  when ::Class
36
- ::Rubinius::AST::Class.new(0, name.to_sym, nil, node)
36
+ ::Rubinius::AST::Class.new(0, name, nil, node)
37
37
  when ::Module
38
- ::Rubinius::AST::Module.new(0, name.to_sym, node)
38
+ ::Rubinius::AST::Module.new(0, name, node)
39
39
  else
40
40
  raise "Cannot wrap scope: #{scope.inspect}"
41
41
  end
@@ -48,10 +48,22 @@ module Mutant
48
48
  # @api private
49
49
  #
50
50
  def initialize(old, new)
51
- @old, @new = self.class.lines(old), self.class.lines(new)
51
+ @old, @new = lines(old), lines(new)
52
52
  @diffs = Diff::LCS.diff(@old, @new)
53
53
  end
54
54
 
55
+ # Break up sorce into lines
56
+ #
57
+ # @param [String] source
58
+ #
59
+ # @return [Array<String>]
60
+ #
61
+ # @api private
62
+ #
63
+ def lines(source)
64
+ self.class.lines(source)
65
+ end
66
+
55
67
  # Return length difference
56
68
  #
57
69
  # @return [Fixnum]
@@ -132,5 +132,6 @@ module Mutant
132
132
  # @api private
133
133
  #
134
134
  abstract_method :run
135
+
135
136
  end
136
137
  end
@@ -55,6 +55,20 @@ module Mutant
55
55
  self.class::MATCHER
56
56
  end
57
57
 
58
+ # Return method names
59
+ #
60
+ # @param [Object] object
61
+ #
62
+ # @return [Enumerable<Symbol>]
63
+ #
64
+ # @api private
65
+ #
66
+ def self.method_names(object)
67
+ object.public_instance_methods(false) +
68
+ object.private_instance_methods(false) +
69
+ object.protected_instance_methods(false)
70
+ end
71
+
58
72
  private
59
73
 
60
74
  # Initialize object
@@ -118,11 +132,7 @@ module Mutant
118
132
  #
119
133
  def method_names
120
134
  singleton_class = scope.singleton_class
121
-
122
- names =
123
- singleton_class.public_instance_methods(false) +
124
- singleton_class.private_instance_methods(false) +
125
- singleton_class.protected_instance_methods(false)
135
+ names = self.class.method_names(singleton_class)
126
136
 
127
137
  names.sort.reject do |name|
128
138
  name.to_sym == :__class_init__
@@ -158,12 +168,7 @@ module Mutant
158
168
  def method_names
159
169
  scope = self.scope
160
170
  return [] unless scope.kind_of?(Module)
161
-
162
- names =
163
- scope.public_instance_methods(false) +
164
- scope.private_instance_methods(false) +
165
- scope.protected_instance_methods(false)
166
-
171
+ names = self.class.method_names(scope)
167
172
  names.uniq.sort
168
173
  end
169
174
  end
@@ -182,6 +182,16 @@ module Mutant
182
182
  self
183
183
  end
184
184
 
185
+ # Run input with mutator
186
+ #
187
+ # @return [undefined]
188
+ #
189
+ # @api private
190
+ #
191
+ def run(mutator)
192
+ mutator.new(input, method(:emit))
193
+ end
194
+
185
195
  # Shortcut to create a new unfrozen duplicate of input
186
196
  #
187
197
  # @return [Object]
@@ -17,7 +17,7 @@ module Mutant
17
17
  ToSource.to_source(node)
18
18
  end
19
19
 
20
- private
20
+ private
21
21
 
22
22
  # Return mutated node
23
23
  #
@@ -89,20 +89,6 @@ module Mutant
89
89
  emit(new_self(*arguments))
90
90
  end
91
91
 
92
- # Emit a new node with wrapping class for each entry in values
93
- #
94
- # @param [Array] values
95
- #
96
- # @return [undefined]
97
- #
98
- # @api private
99
- #
100
- def emit_values(values)
101
- values.each do |value|
102
- emit_self(value)
103
- end
104
- end
105
-
106
92
  # Emit body mutations
107
93
  #
108
94
  # @param [Symbol] name
@@ -111,10 +97,10 @@ module Mutant
111
97
  #
112
98
  # @api private
113
99
  #
114
- def emit_attribute_mutations(name)
115
- body = node.public_send(name)
100
+ def emit_attribute_mutations(name, mutator = Mutator)
101
+ value = node.public_send(name)
116
102
 
117
- Mutator.each(body) do |mutation|
103
+ mutator.each(value) do |mutation|
118
104
  dup = dup_node
119
105
  dup.public_send(:"#{name}=", mutation)
120
106
  yield dup if block_given?
@@ -157,6 +143,7 @@ module Mutant
157
143
  # @api private
158
144
  #
159
145
  alias_method :dup_node, :dup_input
146
+
160
147
  end
161
148
  end
162
149
  end
@@ -0,0 +1,25 @@
1
+ module Mutant
2
+ class Mutator
3
+ class Node
4
+
5
+ # Mutator for arguments
6
+ class Arguments < self
7
+
8
+ handle(Rubinius::AST::ActualArguments)
9
+
10
+ private
11
+
12
+ # Emit mutations
13
+ #
14
+ # @return [undefined]
15
+ #
16
+ # @api private
17
+ #
18
+ def dispatch
19
+ emit_attribute_mutations(:array)
20
+ end
21
+
22
+ end
23
+ end
24
+ end
25
+ end