mutant 0.2.7 → 0.2.8
Sign up to get free protection for your applications and to get access to all the features.
- data/Changelog.md +9 -1
- data/Gemfile.devtools +5 -12
- data/LICENSE +20 -0
- data/README.md +25 -24
- data/TODO +10 -2
- data/config/flay.yml +2 -2
- data/config/flog.yml +1 -1
- data/config/site.reek +4 -2
- data/lib/mutant.rb +18 -9
- data/lib/mutant/context/scope.rb +3 -3
- data/lib/mutant/differ.rb +13 -1
- data/lib/mutant/killer.rb +1 -0
- data/lib/mutant/matcher/scope_methods.rb +16 -11
- data/lib/mutant/mutator.rb +10 -0
- data/lib/mutant/mutator/node.rb +5 -18
- data/lib/mutant/mutator/node/actual_arguments.rb +25 -0
- data/lib/mutant/mutator/node/arguments.rb +0 -155
- data/lib/mutant/mutator/node/default_arguments.rb +24 -0
- data/lib/mutant/mutator/node/formal_arguments_19.rb +40 -0
- data/lib/mutant/mutator/node/formal_arguments_19/default_mutations.rb +32 -0
- data/lib/mutant/mutator/node/formal_arguments_19/pattern_argument_expansion.rb +35 -0
- data/lib/mutant/mutator/node/formal_arguments_19/require_defaults.rb +37 -0
- data/lib/mutant/mutator/node/literal.rb +14 -0
- data/lib/mutant/mutator/node/literal/hash.rb +1 -7
- data/lib/mutant/mutator/node/pattern_arguments.rb +41 -0
- data/lib/mutant/mutator/node/pattern_variable.rb +23 -0
- data/lib/mutant/mutator/node/send.rb +76 -6
- data/lib/mutant/mutator/util.rb +0 -71
- data/lib/mutant/mutator/util/array.rb +70 -0
- data/lib/mutant/mutator/util/symbol.rb +39 -0
- data/mutant.gemspec +2 -2
- data/spec/shared/mutator_behavior.rb +3 -2
- data/spec/unit/mutant/mutator/node/define/mutation_spec.rb +12 -0
- data/spec/unit/mutant/mutator/node/send/mutation_spec.rb +24 -17
- metadata +15 -3
data/Changelog.md
CHANGED
@@ -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
|
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
|
data/Gemfile.devtools
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
group :development do
|
2
|
-
gem 'rake', '~> 0
|
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.
|
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 :
|
38
|
-
gem '
|
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
|
11
|
+
or it does not have a speced side effect.
|
12
12
|
|
13
|
-
Mutant does currently only support 1.9 mode under
|
14
|
-
|
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
|
-
|
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
|
data/config/flay.yml
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
---
|
2
|
-
threshold:
|
3
|
-
total_score:
|
2
|
+
threshold: 25 # Todo bring down to ~20
|
3
|
+
total_score: 902
|
data/config/flog.yml
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
---
|
2
|
-
threshold:
|
2
|
+
threshold: 44.0
|
data/config/site.reek
CHANGED
@@ -10,8 +10,9 @@ UncommunicativeParameterName:
|
|
10
10
|
LargeClass:
|
11
11
|
max_methods: 10
|
12
12
|
exclude:
|
13
|
-
|
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:
|
data/lib/mutant.rb
CHANGED
@@ -13,12 +13,14 @@ require 'diff/lcs'
|
|
13
13
|
require 'diff/lcs/hunk'
|
14
14
|
require 'rspec'
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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'
|
data/lib/mutant/context/scope.rb
CHANGED
@@ -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
|
36
|
+
::Rubinius::AST::Class.new(0, name, nil, node)
|
37
37
|
when ::Module
|
38
|
-
::Rubinius::AST::Module.new(0, name
|
38
|
+
::Rubinius::AST::Module.new(0, name, node)
|
39
39
|
else
|
40
40
|
raise "Cannot wrap scope: #{scope.inspect}"
|
41
41
|
end
|
data/lib/mutant/differ.rb
CHANGED
@@ -48,10 +48,22 @@ module Mutant
|
|
48
48
|
# @api private
|
49
49
|
#
|
50
50
|
def initialize(old, new)
|
51
|
-
@old, @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]
|
data/lib/mutant/killer.rb
CHANGED
@@ -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
|
data/lib/mutant/mutator.rb
CHANGED
@@ -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]
|
data/lib/mutant/mutator/node.rb
CHANGED
@@ -17,7 +17,7 @@ module Mutant
|
|
17
17
|
ToSource.to_source(node)
|
18
18
|
end
|
19
19
|
|
20
|
-
|
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
|
-
|
100
|
+
def emit_attribute_mutations(name, mutator = Mutator)
|
101
|
+
value = node.public_send(name)
|
116
102
|
|
117
|
-
|
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
|