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
data/.travis.yml
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
before_install:
|
2
|
-
- gem install
|
2
|
+
- gem install mbj-inflector
|
3
3
|
language: ruby
|
4
4
|
script: 'bundle exec rake spec'
|
5
5
|
rvm:
|
6
6
|
- 1.8.7
|
7
7
|
- 1.9.2
|
8
8
|
- 1.9.3
|
9
|
-
- rbx-18mode
|
10
9
|
- rbx-19mode
|
11
10
|
matrix:
|
12
11
|
allow_failures:
|
13
|
-
|
12
|
+
# No mutators for 1.8 specifc AST nodes
|
13
|
+
- rvm: rbx-18mode
|
14
14
|
- rvm: 1.8.7
|
15
15
|
notifications:
|
16
16
|
email:
|
data/Changelog.md
CHANGED
@@ -1,11 +1,32 @@
|
|
1
|
+
# v0.2.5 xxxx
|
2
|
+
|
3
|
+
* [feature] Add --debug flag for showing killer output and mutation
|
4
|
+
* [feature] Run noop mutation per subject to guard against initial failing specs
|
5
|
+
* [feature] Mutate default into required arguments
|
6
|
+
* [feature] Mutate default literals
|
7
|
+
* [feature] Mutate unwinding of pattern args |(a, b), c] => |a, b, c|
|
8
|
+
* [feature] Mutate define and block arguments
|
9
|
+
* [feature] Mutate block arguments, inklusive pattern args
|
10
|
+
* [feature] Recurse into block bodies
|
11
|
+
* [change] Unvendor inflector use mbj-inflector from rubygems
|
12
|
+
* [fixed] Insert mutations at correct constant scope
|
13
|
+
* [fixed] Crash on mutating yield, added a noop for now
|
14
|
+
* [fixed] Crash on singleton methods defined on other than constants or self
|
15
|
+
|
16
|
+
[Compare v0.2.4..v0.2.5](https://github.com/mbj/mutant/compare/v0.2.4...v0.2.5)
|
17
|
+
|
1
18
|
# v0.2.4 2012-12-12
|
2
19
|
|
3
20
|
* [fixed] Correctly vendor inflector
|
4
21
|
|
22
|
+
[Compare v0.2.3..v0.2.4](https://github.com/mbj/mutant/compare/v0.2.3...v0.2.4)
|
23
|
+
|
5
24
|
# v0.2.3 2012-12-08
|
6
25
|
|
7
26
|
* [fixed] Prepend extra elements to hash and array instead of append. This fixes unkillable mutators in parallel assignments!
|
8
27
|
|
28
|
+
[Compare v0.2.2..v0.2.3](https://github.com/mbj/mutant/compare/v0.2.2...v0.2.3)
|
29
|
+
|
9
30
|
# v0.2.2 2012-12-07
|
10
31
|
|
11
32
|
* [feature] Add a shitload of operator expansions for dm2 strategy
|
data/Gemfile.devtools
CHANGED
data/Guardfile
CHANGED
@@ -4,7 +4,7 @@ guard :bundler do
|
|
4
4
|
watch('Gemfile')
|
5
5
|
end
|
6
6
|
|
7
|
-
guard :rspec, :all_on_start => false do
|
7
|
+
guard :rspec, :all_on_start => false, :all_after_pass => false do
|
8
8
|
# run all specs if the spec_helper or supporting files files are modified
|
9
9
|
watch('spec/spec_helper.rb') { 'spec/unit' }
|
10
10
|
watch(%r{\Aspec/(?:lib|support|shared)/.+\.rb\z}) { 'spec/unit' }
|
data/README.md
CHANGED
@@ -6,9 +6,14 @@ mutant
|
|
6
6
|
[](https://codeclimate.com/github/mbj/mutant)
|
7
7
|
|
8
8
|
Mutant is a mutation testing tool for ruby that aims to be better than existing mutation testers.
|
9
|
-
The idea is that if code can be changed and your tests don't notice, either that code isn't being covered or it doesn't do anything.
|
10
9
|
|
11
|
-
|
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).
|
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.
|
15
|
+
|
16
|
+
See this [ASCII-Cast](http://ascii.io/a/1707) for mutant in action! (v0.2.1)
|
12
17
|
|
13
18
|
Installation
|
14
19
|
------------
|
@@ -19,10 +24,49 @@ Examples
|
|
19
24
|
--------
|
20
25
|
|
21
26
|
```
|
22
|
-
cd
|
23
|
-
# Run mutant on virtus (that uses the dm-2 style spec layout)
|
27
|
+
cd virtus
|
28
|
+
# Run mutant on virtus namespace (that uses the dm-2 style spec layout)
|
24
29
|
mutant -I lib -r virtus --rspec-dm2 ::Virtus
|
30
|
+
# Run mutant on specific virtus class
|
31
|
+
mutant -I lib -r virtus --rspec-dm2 ::Virtus::Attribute
|
32
|
+
# Run mutant on specific virtus class method
|
33
|
+
mutant -I lib -r virtus --rspec-dm2 ::Virtus::Attribute.build
|
34
|
+
# Run mutant on specific virtus instance method
|
35
|
+
mutant -I lib -r virtus --rspec-dm2 ::Virtus::Attribute#name
|
36
|
+
```
|
37
|
+
|
38
|
+
Strategies
|
39
|
+
----------
|
40
|
+
|
41
|
+
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:
|
43
|
+
|
44
|
+
### --rspec-dm2
|
45
|
+
|
46
|
+
This strategy is the *fastest* but requires discipline in spec file naming.
|
47
|
+
|
48
|
+
The following specs are executed to kill a mutation on:
|
25
49
|
```
|
50
|
+
Public instance methods: spec/unit/#{namespace}/#{class_name}/#{method_name}_spec.rb
|
51
|
+
Public singleton methods: spec/unit/#{namespace}/#{class_name}/class_methods/#{method_name}_spec.rb
|
52
|
+
Public instance methods: spec/unit/#{namespace}/#{class_name}/
|
53
|
+
Public singleton methods: spec/unit/#{namespace}/#{class_name}/class_methods
|
54
|
+
```
|
55
|
+
|
56
|
+
### --rspec-unit
|
57
|
+
|
58
|
+
This strategy executes all specs under ``./spec/unit`` for each mutation.
|
59
|
+
|
60
|
+
### --rspec-integration
|
61
|
+
|
62
|
+
This strategy executes all specs under ``./spec/integration`` for each mutation.
|
63
|
+
|
64
|
+
### --rspec-full
|
65
|
+
|
66
|
+
This strategy executes all specs under ``./spec`` for each mutation.
|
67
|
+
|
68
|
+
It is also plannned to allow explicit selections on specs to run and to support other test frameworks.
|
69
|
+
Custom project specific strategies are also on the roadmap.
|
26
70
|
|
27
71
|
Credits
|
28
72
|
-------
|
data/config/flay.yml
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
---
|
2
|
-
threshold:
|
3
|
-
total_score:
|
2
|
+
threshold: 34 # Todo bring down to ~20
|
3
|
+
total_score: 913
|
data/config/flog.yml
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
---
|
2
|
-
threshold:
|
2
|
+
threshold: 47.0
|
data/config/site.reek
CHANGED
@@ -11,6 +11,7 @@ LargeClass:
|
|
11
11
|
max_methods: 10
|
12
12
|
exclude:
|
13
13
|
- "Mutant::Matcher::Method" # 13 methods
|
14
|
+
- "Mutant::Reporter::CLI" # 16 methods TODO Reduce!
|
14
15
|
enabled: true
|
15
16
|
max_instance_variables: 3
|
16
17
|
UncommunicativeMethodName:
|
@@ -40,7 +41,8 @@ IrresponsibleModule:
|
|
40
41
|
exclude: []
|
41
42
|
enabled: true
|
42
43
|
UncommunicativeModuleName:
|
43
|
-
accept:
|
44
|
+
accept:
|
45
|
+
- Mutant::Strategy::Rspec::DM2
|
44
46
|
exclude: []
|
45
47
|
enabled: true
|
46
48
|
reject:
|
data/lib/mutant.rb
CHANGED
@@ -6,13 +6,24 @@ require 'descendants_tracker'
|
|
6
6
|
require 'securerandom'
|
7
7
|
require 'equalizer'
|
8
8
|
require 'digest/sha1'
|
9
|
+
require 'inflector'
|
9
10
|
require 'to_source'
|
10
11
|
require 'ice_nine'
|
11
|
-
require 'ice_nine/core_ext/object'
|
12
12
|
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
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
16
27
|
# Library namespace
|
17
28
|
module Mutant
|
18
29
|
|
@@ -73,6 +84,8 @@ require 'mutant/mutator/node/send'
|
|
73
84
|
require 'mutant/mutator/node/arguments'
|
74
85
|
require 'mutant/mutator/node/define'
|
75
86
|
require 'mutant/mutator/node/return'
|
87
|
+
require 'mutant/mutator/node/local_variable_assignment'
|
88
|
+
require 'mutant/mutator/node/iter_19'
|
76
89
|
require 'mutant/mutator/node/if_statement'
|
77
90
|
require 'mutant/mutator/node/receiver_case'
|
78
91
|
require 'mutant/loader'
|
@@ -102,4 +115,3 @@ require 'mutant/reporter'
|
|
102
115
|
require 'mutant/reporter/stats'
|
103
116
|
require 'mutant/reporter/null'
|
104
117
|
require 'mutant/reporter/cli'
|
105
|
-
require 'mutant/inflector'
|
data/lib/mutant/cli.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Mutant
|
2
2
|
# Comandline parser
|
3
3
|
class CLI
|
4
|
-
include Adamantium::Flat, Equalizer.new(:matcher, :filter, :
|
4
|
+
include Adamantium::Flat, Equalizer.new(:matcher, :filter, :strategy, :reporter)
|
5
5
|
|
6
6
|
# Error raised when CLI argv is inalid
|
7
7
|
Error = Class.new(RuntimeError)
|
@@ -44,6 +44,20 @@ module Mutant
|
|
44
44
|
end
|
45
45
|
memoize :matcher
|
46
46
|
|
47
|
+
# Test for running in debug mode
|
48
|
+
#
|
49
|
+
# @return [true]
|
50
|
+
# if debug mode is active
|
51
|
+
#
|
52
|
+
# @return [false]
|
53
|
+
# otherwise
|
54
|
+
#
|
55
|
+
# @api private
|
56
|
+
#
|
57
|
+
def debug?
|
58
|
+
!!@debug
|
59
|
+
end
|
60
|
+
|
47
61
|
# Return mutation filter
|
48
62
|
#
|
49
63
|
# @return [Mutant::Matcher]
|
@@ -67,7 +81,9 @@ module Mutant
|
|
67
81
|
#
|
68
82
|
def strategy
|
69
83
|
@strategy || raise(Error, 'no strategy was set!')
|
84
|
+
@strategy.new(self)
|
70
85
|
end
|
86
|
+
memoize :strategy
|
71
87
|
|
72
88
|
# Return reporter
|
73
89
|
#
|
@@ -76,7 +92,7 @@ module Mutant
|
|
76
92
|
# @api private
|
77
93
|
#
|
78
94
|
def reporter
|
79
|
-
Mutant::Reporter::CLI.new(
|
95
|
+
Mutant::Reporter::CLI.new(self)
|
80
96
|
end
|
81
97
|
memoize :reporter
|
82
98
|
|
@@ -88,7 +104,8 @@ module Mutant
|
|
88
104
|
'--include' => [:add_load_path ],
|
89
105
|
'-r' => [:require_library ],
|
90
106
|
'--require' => [:require_library ],
|
91
|
-
|
107
|
+
'--debug' => [:set_debug ],
|
108
|
+
'-d' => [:set_debug ],
|
92
109
|
'--rspec-unit' => [:set_strategy, Strategy::Rspec::Unit ],
|
93
110
|
'--rspec-full' => [:set_strategy, Strategy::Rspec::Full ],
|
94
111
|
'--rspec-dm2' => [:set_strategy, Strategy::Rspec::DM2 ],
|
@@ -98,33 +115,6 @@ module Mutant
|
|
98
115
|
|
99
116
|
OPTION_PATTERN = %r(\A-(?:-)?[a-zA-Z0-9\-]+\z).freeze
|
100
117
|
|
101
|
-
# Return selected killer
|
102
|
-
#
|
103
|
-
# @return [Killer]
|
104
|
-
#
|
105
|
-
# @api private
|
106
|
-
#
|
107
|
-
def selected_killer
|
108
|
-
unless @rspec
|
109
|
-
raise Error, "Only rspec is supported currently use --rspec switch"
|
110
|
-
end
|
111
|
-
|
112
|
-
Mutant::Killer::Rspec
|
113
|
-
end
|
114
|
-
memoize :selected_killer
|
115
|
-
|
116
|
-
# Return option for argument with index
|
117
|
-
#
|
118
|
-
# @param [Fixnum] index
|
119
|
-
#
|
120
|
-
# @return [String]
|
121
|
-
#
|
122
|
-
# @api private
|
123
|
-
#
|
124
|
-
def option(index)
|
125
|
-
@arguments.fetch(index+1)
|
126
|
-
end
|
127
|
-
|
128
118
|
# Initialize CLI
|
129
119
|
#
|
130
120
|
# @param [Array<String>] arguments
|
@@ -146,6 +136,18 @@ module Mutant
|
|
146
136
|
matcher
|
147
137
|
end
|
148
138
|
|
139
|
+
# Return option for argument with index
|
140
|
+
#
|
141
|
+
# @param [Fixnum] index
|
142
|
+
#
|
143
|
+
# @return [String]
|
144
|
+
#
|
145
|
+
# @api private
|
146
|
+
#
|
147
|
+
def option(index)
|
148
|
+
@arguments.fetch(index+1)
|
149
|
+
end
|
150
|
+
|
149
151
|
# Return current argument
|
150
152
|
#
|
151
153
|
# @return [String]
|
@@ -233,7 +235,7 @@ module Mutant
|
|
233
235
|
|
234
236
|
# Add mutation filter
|
235
237
|
#
|
236
|
-
# @param [Class<Mutant::Filter>]
|
238
|
+
# @param [Class<Mutant::Filter>] klass
|
237
239
|
#
|
238
240
|
# @return [undefined]
|
239
241
|
#
|
@@ -268,22 +270,20 @@ module Mutant
|
|
268
270
|
@rspec = true
|
269
271
|
end
|
270
272
|
|
271
|
-
#
|
273
|
+
# Set debug mode
|
272
274
|
#
|
273
275
|
# @api private
|
274
276
|
#
|
275
|
-
# @return [
|
276
|
-
#
|
277
|
-
# @api private
|
277
|
+
# @return [undefined]
|
278
278
|
#
|
279
|
-
def
|
279
|
+
def set_debug
|
280
280
|
consume(1)
|
281
|
-
@
|
281
|
+
@debug = true
|
282
282
|
end
|
283
283
|
|
284
284
|
# Set strategy
|
285
285
|
#
|
286
|
-
# @param [Strategy]
|
286
|
+
# @param [Strategy] strategy
|
287
287
|
#
|
288
288
|
# @api private
|
289
289
|
#
|
@@ -304,6 +304,5 @@ module Mutant
|
|
304
304
|
require(current_option_value)
|
305
305
|
consume(2)
|
306
306
|
end
|
307
|
-
|
308
307
|
end
|
309
308
|
end
|
data/lib/mutant/context/scope.rb
CHANGED
@@ -2,54 +2,59 @@ module Mutant
|
|
2
2
|
class Context
|
3
3
|
# Scope context for mutation (Class or Module)
|
4
4
|
class Scope < self
|
5
|
-
include Adamantium::Flat,
|
5
|
+
include Adamantium::Flat, Equalizer.new(:scope, :source_path)
|
6
6
|
|
7
|
-
#
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
7
|
+
# Return AST wrapping mutated node
|
8
|
+
#
|
9
|
+
# @return [Rubinius::AST::Script]
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
#
|
13
|
+
def root(node)
|
14
|
+
nesting.reverse.inject(node) do |current, scope|
|
15
|
+
self.class.wrap(scope, current)
|
16
|
+
end
|
17
17
|
end
|
18
18
|
|
19
|
-
|
20
|
-
::Module => Module,
|
21
|
-
::Class => Class
|
22
|
-
}.freeze
|
23
|
-
|
24
|
-
# Build scope context from class or module
|
19
|
+
# Wrap node into ast node
|
25
20
|
#
|
26
|
-
# @param [
|
21
|
+
# @param [Class, Module] scope
|
22
|
+
# @param [Rubinius::AST::Node] node
|
27
23
|
#
|
28
|
-
# @
|
24
|
+
# @return [Rubinius::AST::Class]
|
25
|
+
# if scope is of kind Class
|
29
26
|
#
|
30
|
-
# @return [
|
27
|
+
# @return [Rubinius::AST::Module]
|
28
|
+
# if scope is of kind module
|
31
29
|
#
|
32
30
|
# @api private
|
33
31
|
#
|
34
|
-
def self.
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
32
|
+
def self.wrap(scope, node)
|
33
|
+
name = scope.name.split('::').last
|
34
|
+
case scope
|
35
|
+
when ::Class
|
36
|
+
::Rubinius::AST::Class.new(0, name.to_sym, nil, node)
|
37
|
+
when ::Module
|
38
|
+
::Rubinius::AST::Module.new(0, name.to_sym, node)
|
39
|
+
else
|
40
|
+
raise "Cannot wrap scope: #{scope.inspect}"
|
41
|
+
end
|
39
42
|
end
|
40
43
|
|
41
|
-
# Return
|
44
|
+
# Return nesting
|
42
45
|
#
|
43
|
-
# @return [
|
46
|
+
# @return [Enumerable<Class,Module>]
|
44
47
|
#
|
45
48
|
# @api private
|
46
49
|
#
|
47
|
-
def
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
50
|
+
def nesting
|
51
|
+
const = ::Object
|
52
|
+
name_nesting.each_with_object([]) do |name, nesting|
|
53
|
+
const = const.const_get(name)
|
54
|
+
nesting << const
|
55
|
+
end
|
52
56
|
end
|
57
|
+
memoize :nesting
|
53
58
|
|
54
59
|
# Return unqualified name of scope
|
55
60
|
#
|