mutant 0.3.0.beta4 → 0.3.0.beta5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/README.md +4 -4
- data/TODO +0 -9
- data/bin/mutant +2 -2
- data/bin/zombie +2 -2
- data/config/flay.yml +1 -1
- data/config/reek.yml +1 -2
- data/lib/mutant.rb +3 -0
- data/lib/mutant/cache.rb +30 -0
- data/lib/mutant/cli.rb +4 -1
- data/lib/mutant/cli/classifier.rb +17 -34
- data/lib/mutant/cli/classifier/method.rb +2 -2
- data/lib/mutant/cli/classifier/namespace.rb +1 -1
- data/lib/mutant/config.rb +1 -1
- data/lib/mutant/constants.rb +2 -0
- data/lib/mutant/differ.rb +3 -3
- data/lib/mutant/loader.rb +1 -1
- data/lib/mutant/matcher.rb +3 -3
- data/lib/mutant/matcher/method.rb +6 -73
- data/lib/mutant/matcher/method/finder.rb +72 -0
- data/lib/mutant/matcher/method/singleton.rb +2 -2
- data/lib/mutant/matcher/methods.rb +2 -2
- data/lib/mutant/matcher/namespace.rb +2 -2
- data/lib/mutant/matcher/scope.rb +2 -2
- data/lib/mutant/mutation.rb +4 -1
- data/lib/mutant/mutator/node.rb +1 -1
- data/lib/mutant/zombifier.rb +255 -0
- data/mutant.gemspec +3 -3
- data/spec/integration/mutant/zombie_spec.rb +3 -3
- data/spec/shared/method_matcher_behavior.rb +3 -4
- data/spec/spec_helper.rb +4 -0
- data/spec/unit/mutant/cli/class_methods/new_spec.rb +6 -6
- data/spec/unit/mutant/cli/classifier/class_methods/build_spec.rb +9 -8
- data/spec/unit/mutant/context/scope/root_spec.rb +3 -3
- data/spec/unit/mutant/killer/rspec/class_methods/new_spec.rb +7 -5
- data/spec/unit/mutant/matcher/method/instance/each_spec.rb +8 -7
- data/spec/unit/mutant/matcher/method/singleton/each_spec.rb +5 -4
- data/spec/unit/mutant/matcher/methods/instance/each_spec.rb +5 -4
- data/spec/unit/mutant/matcher/methods/singleton/each_spec.rb +5 -4
- data/spec/unit/mutant/matcher/namespace/each_spec.rb +5 -3
- data/spec/unit/mutant/strategy/rspec/dm2/lookup/method/instance/spec_files_spec.rb +1 -1
- data/spec/unit/mutant/strategy/rspec/dm2/lookup/method/singleton/spec_files_spec.rb +2 -2
- data/spec/unit/mutant/subject/context_spec.rb +2 -2
- data/spec/unit/mutant/subject/each_spec.rb +1 -1
- data/spec/unit/mutant/subject/node_spec.rb +2 -2
- metadata +9 -8
- data/spec/support/zombie.rb +0 -174
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f3ab83b3fe3623d77e0bc897128c0790b6ebcd13
|
4
|
+
data.tar.gz: aabf8cc62a8349f0891cbcca381c682d18055384
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e816a8fd75024969b7a286d4b0e10b122526a768c5746f3615ac4b341d6538794195be8d2dff53f97fab916632e26111edd9156050787642f4197f131c654a0c
|
7
|
+
data.tar.gz: 393035855ab0e2d9b81203e221ae238edf54e92af5b911a290b04ef086fa7c4b7ba4757621141eb442f184bb80686c9f9ecf6ddce1455d717753584a48b28b21
|
data/.travis.yml
CHANGED
@@ -10,7 +10,7 @@ rvm:
|
|
10
10
|
matrix:
|
11
11
|
allow_failures:
|
12
12
|
- rvm: jruby-19mode # No fork(2) support, workaround planned
|
13
|
-
- rvm: ruby-head # Broken at this time
|
13
|
+
- rvm: ruby-head # Broken at this time, bundler issue on travis
|
14
14
|
- rvm: rbx-19mode # Broken at this time, yard/yardstick issue
|
15
15
|
notifications:
|
16
16
|
irc:
|
data/README.md
CHANGED
@@ -10,8 +10,8 @@ Mutant is a mutation testing tool for ruby. It aims to be better than existing m
|
|
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
11
|
or it does not have a speced side effect.
|
12
12
|
|
13
|
-
Mutant supports MRI and RBX
|
14
|
-
engine that supports POSIX-fork(2) semantics.
|
13
|
+
Mutant supports MRI and RBX 1.9 and 2.0, while support for jruby is planned. It should also work under
|
14
|
+
any ruby engine that supports POSIX-fork(2) semantics.
|
15
15
|
|
16
16
|
Only rspec2 is supported currently. This is subject to change.
|
17
17
|
|
@@ -27,8 +27,8 @@ The following projects adopted mutant, and aim 100% mutation coverage:
|
|
27
27
|
|
28
28
|
* [axiom](https://github.com/dkubb/axiom)
|
29
29
|
* [axiom-types](https://github.com/dkubb/axiom-types)
|
30
|
-
* [rom-mapper](https://github.com/rom/rom-mapper)
|
31
|
-
* [rom-session](https://github.com/rom/rom-session)
|
30
|
+
* [rom-mapper](https://github.com/rom-rb/rom-mapper)
|
31
|
+
* [rom-session](https://github.com/rom-rb/rom-session)
|
32
32
|
* [event_bus](https://github.com/kevinrutherford/event_bus)
|
33
33
|
* [virtus](https://github.com/solnic/virtus)
|
34
34
|
* [quacky](https://github.com/benmoss/quacky)
|
data/TODO
CHANGED
@@ -1,12 +1,7 @@
|
|
1
1
|
Code:
|
2
2
|
* Test mutant with dynamically created zombie.
|
3
|
-
* Fix ugly code within default parameter mutations
|
4
3
|
* Break up lib/mutant/mutator/node/send.rb in class specific files
|
5
4
|
* Log all warnings through reporter, so remove random $stderr.puts calls
|
6
|
-
* Use rational to represent percent values
|
7
|
-
|
8
|
-
AST:
|
9
|
-
* Use whitequarcks parser and the to be writte unparser. This fixes all RBX AST issues.
|
10
5
|
|
11
6
|
Mutations:
|
12
7
|
* Add binary operator specific mutations (YAY, finally reached this point)
|
@@ -34,9 +29,6 @@ Killers:
|
|
34
29
|
* Add a general master <=> killer IPC interface. So different strategies of isolation
|
35
30
|
(fork, vs jruby runtime creation) will work without big impact.
|
36
31
|
|
37
|
-
Kill:
|
38
|
-
* Introduce mutant Kill that acts as a DTO between killer and runner. (Simplifies reporting)
|
39
|
-
|
40
32
|
Strategy:
|
41
33
|
* Aggregate warnings on missing spec files
|
42
34
|
* Provide "expicit files to kill with" strategy
|
@@ -46,5 +38,4 @@ Matcher:
|
|
46
38
|
* Allow matches on define_method with literal name argument?
|
47
39
|
|
48
40
|
jruby-support:
|
49
|
-
* Use whitequarck parser
|
50
41
|
* Create a runtime per mutation to kill mutations in isolation
|
data/bin/mutant
CHANGED
@@ -9,8 +9,8 @@ require 'mutant'
|
|
9
9
|
namespace =
|
10
10
|
if File.basename($0) == 'zombie'
|
11
11
|
$stderr.puts('Detected zombie environment...')
|
12
|
-
|
13
|
-
Zombie
|
12
|
+
Mutant::Zombifier.zombify
|
13
|
+
Zombie::Mutant
|
14
14
|
else
|
15
15
|
Mutant
|
16
16
|
end
|
data/bin/zombie
CHANGED
@@ -9,8 +9,8 @@ require 'mutant'
|
|
9
9
|
namespace =
|
10
10
|
if File.basename($0) == 'zombie'
|
11
11
|
$stderr.puts('Detected zombie environment...')
|
12
|
-
|
13
|
-
Zombie
|
12
|
+
Mutant::Zombifier.zombify
|
13
|
+
Zombie::Mutant
|
14
14
|
else
|
15
15
|
Mutant
|
16
16
|
end
|
data/config/flay.yml
CHANGED
data/config/reek.yml
CHANGED
@@ -58,14 +58,13 @@ RepeatedConditional:
|
|
58
58
|
exclude:
|
59
59
|
- Mutant::Mutator
|
60
60
|
- Mutant::Reporter::CLI
|
61
|
-
- Mutant::Mutator::Node::Send
|
62
|
-
- Mutant::Mutator::Node::If
|
63
61
|
max_ifs: 1
|
64
62
|
TooManyInstanceVariables:
|
65
63
|
enabled: true
|
66
64
|
exclude:
|
67
65
|
- Mutant::CLI # 4 vars
|
68
66
|
- Mutant::Killer # 4 vars
|
67
|
+
- Mutant::Mutator # 4 vars
|
69
68
|
max_instance_variables: 3
|
70
69
|
TooManyMethods:
|
71
70
|
enabled: true
|
data/lib/mutant.rb
CHANGED
@@ -22,6 +22,7 @@ require 'concord'
|
|
22
22
|
module Mutant
|
23
23
|
end
|
24
24
|
|
25
|
+
require 'mutant/cache'
|
25
26
|
require 'mutant/node_helpers'
|
26
27
|
require 'mutant/singleton_methods'
|
27
28
|
require 'mutant/constants'
|
@@ -77,6 +78,7 @@ require 'mutant/subject/method'
|
|
77
78
|
require 'mutant/matcher'
|
78
79
|
require 'mutant/matcher/chain'
|
79
80
|
require 'mutant/matcher/method'
|
81
|
+
require 'mutant/matcher/method/finder'
|
80
82
|
require 'mutant/matcher/method/singleton'
|
81
83
|
require 'mutant/matcher/method/instance'
|
82
84
|
require 'mutant/matcher/methods'
|
@@ -112,3 +114,4 @@ require 'mutant/reporter/cli/printer/config'
|
|
112
114
|
require 'mutant/reporter/cli/printer/subject'
|
113
115
|
require 'mutant/reporter/cli/printer/killer'
|
114
116
|
require 'mutant/reporter/cli/printer/mutation'
|
117
|
+
require 'mutant/zombifier'
|
data/lib/mutant/cache.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Mutant
|
2
|
+
# An AST cache
|
3
|
+
class Cache
|
4
|
+
# This is explicitly empty! Ask me if you are interested in reasons :D
|
5
|
+
include Equalizer.new
|
6
|
+
|
7
|
+
# Initialize object
|
8
|
+
#
|
9
|
+
# @return [undefined]
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
#
|
13
|
+
def initialize
|
14
|
+
@cache = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
# Return node for file
|
18
|
+
#
|
19
|
+
# @return [AST::Node]
|
20
|
+
#
|
21
|
+
# @api private
|
22
|
+
#
|
23
|
+
def parse(path)
|
24
|
+
@cache.fetch(path) do
|
25
|
+
@cache[path] = Parser::CurrentRuby.parse(File.read(path))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end # Cache
|
30
|
+
end # Mutant
|
data/lib/mutant/cli.rb
CHANGED
@@ -41,6 +41,8 @@ module Mutant
|
|
41
41
|
def initialize(arguments=[])
|
42
42
|
@filters, @matchers = [], []
|
43
43
|
|
44
|
+
@cache = Mutant::Cache.new
|
45
|
+
|
44
46
|
parse(arguments)
|
45
47
|
strategy
|
46
48
|
matcher
|
@@ -54,6 +56,7 @@ module Mutant
|
|
54
56
|
#
|
55
57
|
def config
|
56
58
|
Config.new(
|
59
|
+
:cache => @cache,
|
57
60
|
:debug => debug?,
|
58
61
|
:matcher => matcher,
|
59
62
|
:filter => filter,
|
@@ -212,7 +215,7 @@ module Mutant
|
|
212
215
|
#
|
213
216
|
def parse_matchers(patterns)
|
214
217
|
patterns.each do |pattern|
|
215
|
-
matcher = Classifier.build(pattern)
|
218
|
+
matcher = Classifier.build(@cache, pattern)
|
216
219
|
@matchers << matcher if matcher
|
217
220
|
end
|
218
221
|
end
|
@@ -2,10 +2,13 @@ module Mutant
|
|
2
2
|
class CLI
|
3
3
|
# A classifier for input strings
|
4
4
|
class Classifier < Matcher
|
5
|
-
include AbstractType, Adamantium::Flat,
|
5
|
+
include AbstractType, Adamantium::Flat, Concord.new(:cache, :match)
|
6
|
+
|
7
|
+
include Equalizer.new(:identifier)
|
6
8
|
|
7
9
|
SCOPE_NAME_PATTERN = /[A-Za-z][A-Za-z_0-9]*/.freeze
|
8
|
-
|
10
|
+
OPERATOR_PATTERN = Regexp.union(*OPERATOR_METHODS.map(&:to_s)).freeze
|
11
|
+
METHOD_NAME_PATTERN = /([_A-Za-z][A-Za-z0-9_]*[!?=]?|#{OPERATOR_PATTERN})/.freeze
|
9
12
|
SCOPE_PATTERN = /(?:::)?#{SCOPE_NAME_PATTERN}(?:::#{SCOPE_NAME_PATTERN})*/.freeze
|
10
13
|
|
11
14
|
SINGLETON_PATTERN = %r(\A(#{SCOPE_PATTERN})\z).freeze
|
@@ -32,15 +35,13 @@ module Mutant
|
|
32
35
|
# @api private
|
33
36
|
#
|
34
37
|
def self.constant_lookup(location)
|
35
|
-
location.gsub(%r(\A::), '').split('::').inject(
|
38
|
+
location.gsub(%r(\A::), '').split('::').inject(Object) do |parent, name|
|
36
39
|
parent.const_get(name)
|
37
40
|
end
|
38
41
|
end
|
39
42
|
|
40
43
|
# Return matchers for input
|
41
44
|
#
|
42
|
-
# @param [String] input
|
43
|
-
#
|
44
45
|
# @return [Classifier]
|
45
46
|
# if a classifier handles the input
|
46
47
|
#
|
@@ -49,9 +50,9 @@ module Mutant
|
|
49
50
|
#
|
50
51
|
# @api private
|
51
52
|
#
|
52
|
-
def self.build(
|
53
|
+
def self.build(*arguments)
|
53
54
|
classifiers = REGISTRY.map do |descendant|
|
54
|
-
descendant.run(
|
55
|
+
descendant.run(*arguments)
|
55
56
|
end.compact
|
56
57
|
|
57
58
|
raise if classifiers.length > 1
|
@@ -61,6 +62,10 @@ module Mutant
|
|
61
62
|
|
62
63
|
# Run classifier
|
63
64
|
#
|
65
|
+
# @param [Cache] cache
|
66
|
+
#
|
67
|
+
# @param [String] input
|
68
|
+
#
|
64
69
|
# @return [Classifier]
|
65
70
|
# if input is handled by classifier
|
66
71
|
#
|
@@ -69,11 +74,11 @@ module Mutant
|
|
69
74
|
#
|
70
75
|
# @api private
|
71
76
|
#
|
72
|
-
def self.run(input)
|
77
|
+
def self.run(cache, input)
|
73
78
|
match = self::REGEXP.match(input)
|
74
79
|
return unless match
|
75
80
|
|
76
|
-
new(match)
|
81
|
+
new(cache, match)
|
77
82
|
end
|
78
83
|
|
79
84
|
# No protected_class_method in ruby :(
|
@@ -95,38 +100,16 @@ module Mutant
|
|
95
100
|
self
|
96
101
|
end
|
97
102
|
|
98
|
-
# Return
|
103
|
+
# Return identifier
|
99
104
|
#
|
100
105
|
# @return [String]
|
101
106
|
#
|
102
107
|
# @api private
|
103
108
|
#
|
104
|
-
def
|
109
|
+
def identifier
|
105
110
|
match.to_s
|
106
111
|
end
|
107
|
-
memoize :
|
108
|
-
|
109
|
-
private
|
110
|
-
|
111
|
-
# Initialize object
|
112
|
-
#
|
113
|
-
# @param [MatchData] match
|
114
|
-
#
|
115
|
-
# @return [undefined]
|
116
|
-
#
|
117
|
-
# @api private
|
118
|
-
#
|
119
|
-
def initialize(match)
|
120
|
-
@match = match
|
121
|
-
end
|
122
|
-
|
123
|
-
# Return match
|
124
|
-
#
|
125
|
-
# @return [MatchData]
|
126
|
-
#
|
127
|
-
# @api private
|
128
|
-
#
|
129
|
-
attr_reader :match
|
112
|
+
memoize :identifier
|
130
113
|
|
131
114
|
# Return matcher
|
132
115
|
#
|
@@ -24,7 +24,7 @@ module Mutant
|
|
24
24
|
# @api private
|
25
25
|
#
|
26
26
|
def matcher
|
27
|
-
methods_matcher.matcher.new(scope, method)
|
27
|
+
methods_matcher.matcher.new(cache, scope, method)
|
28
28
|
end
|
29
29
|
memoize :matcher
|
30
30
|
|
@@ -90,7 +90,7 @@ module Mutant
|
|
90
90
|
# @api private
|
91
91
|
#
|
92
92
|
def methods_matcher
|
93
|
-
TABLE.fetch(scope_symbol).new(scope)
|
93
|
+
TABLE.fetch(scope_symbol).new(cache, scope)
|
94
94
|
end
|
95
95
|
memoize :methods_matcher
|
96
96
|
|
data/lib/mutant/config.rb
CHANGED
data/lib/mutant/constants.rb
CHANGED
@@ -44,6 +44,8 @@ module Mutant
|
|
44
44
|
|
45
45
|
BINARY_METHOD_OPERATORS = (OPERATOR_EXPANSIONS.keys - (INDEX_OPERATORS + UNARY_METHOD_OPERATORS)).to_set.freeze
|
46
46
|
|
47
|
+
OPERATOR_METHODS = OPERATOR_EXPANSIONS.keys + INDEX_OPERATORS + UNARY_METHOD_OPERATORS
|
48
|
+
|
47
49
|
# Hopefully all types parser does generate
|
48
50
|
NODE_TYPES = [
|
49
51
|
:lvasgn, :ivasgn, :cvasgn, :gvasgn,
|
data/lib/mutant/differ.rb
CHANGED
@@ -3,6 +3,9 @@ module Mutant
|
|
3
3
|
class Differ
|
4
4
|
include Adamantium::Flat
|
5
5
|
|
6
|
+
FORMAT = :unified
|
7
|
+
CONTEXT_LINES = 3
|
8
|
+
|
6
9
|
# Return source diff
|
7
10
|
#
|
8
11
|
# @return [String]
|
@@ -35,9 +38,6 @@ module Mutant
|
|
35
38
|
|
36
39
|
private
|
37
40
|
|
38
|
-
FORMAT = :unified
|
39
|
-
CONTEXT_LINES = 3
|
40
|
-
|
41
41
|
# Initialize differ object
|
42
42
|
#
|
43
43
|
# @param [String] old
|
data/lib/mutant/loader.rb
CHANGED
data/lib/mutant/matcher.rb
CHANGED
@@ -15,10 +15,10 @@ module Mutant
|
|
15
15
|
#
|
16
16
|
# @api private
|
17
17
|
#
|
18
|
-
def self.each(input, &block)
|
19
|
-
return to_enum(__method__, input) unless block_given?
|
18
|
+
def self.each(cache, input, &block)
|
19
|
+
return to_enum(__method__, cache, input) unless block_given?
|
20
20
|
|
21
|
-
new(input).each(&block)
|
21
|
+
new(cache, input).each(&block)
|
22
22
|
|
23
23
|
self
|
24
24
|
end
|
@@ -2,7 +2,7 @@ module Mutant
|
|
2
2
|
class Matcher
|
3
3
|
# Matcher for subjects that are a specific method
|
4
4
|
class Method < self
|
5
|
-
include Adamantium::Flat, Concord::Public.new(:scope, :method)
|
5
|
+
include Adamantium::Flat, Concord::Public.new(:cache, :scope, :method)
|
6
6
|
|
7
7
|
# Methods within rbx kernel directory are precompiled and their source
|
8
8
|
# cannot be accessed via reading source location
|
@@ -18,13 +18,12 @@ module Mutant
|
|
18
18
|
#
|
19
19
|
# @api private
|
20
20
|
#
|
21
|
-
def each
|
21
|
+
def each
|
22
22
|
return to_enum unless block_given?
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
yield util if util
|
24
|
+
unless skip?
|
25
|
+
yield subject if subject
|
26
|
+
end
|
28
27
|
|
29
28
|
self
|
30
29
|
end
|
@@ -78,7 +77,7 @@ module Mutant
|
|
78
77
|
# @api private
|
79
78
|
#
|
80
79
|
def ast
|
81
|
-
|
80
|
+
cache.parse(source_path)
|
82
81
|
end
|
83
82
|
|
84
83
|
# Return path to source
|
@@ -128,72 +127,6 @@ module Mutant
|
|
128
127
|
end
|
129
128
|
memoize :subject
|
130
129
|
|
131
|
-
# Visitor to find last match inside AST
|
132
|
-
class Finder
|
133
|
-
|
134
|
-
# Run finder
|
135
|
-
#
|
136
|
-
# @param [Parser::AST::Node]
|
137
|
-
#
|
138
|
-
# @return [Parser::AST::Node]
|
139
|
-
# if found
|
140
|
-
#
|
141
|
-
# @return [nil]
|
142
|
-
# otherwise
|
143
|
-
#
|
144
|
-
# @api private
|
145
|
-
#
|
146
|
-
#
|
147
|
-
def self.run(root, &predicate)
|
148
|
-
new(root, predicate).match
|
149
|
-
end
|
150
|
-
|
151
|
-
private_class_method :new
|
152
|
-
|
153
|
-
# Return match
|
154
|
-
#
|
155
|
-
# @return [Parser::AST::Node]
|
156
|
-
#
|
157
|
-
# @api private
|
158
|
-
#
|
159
|
-
attr_reader :match
|
160
|
-
|
161
|
-
private
|
162
|
-
|
163
|
-
# Initialize object
|
164
|
-
#
|
165
|
-
# @param [Parer::AST::Node]
|
166
|
-
#
|
167
|
-
# @return [undefined]
|
168
|
-
#
|
169
|
-
# @api private
|
170
|
-
#
|
171
|
-
#
|
172
|
-
def initialize(root, predicate)
|
173
|
-
@root, @predicate = root, predicate
|
174
|
-
visit(root)
|
175
|
-
end
|
176
|
-
|
177
|
-
# Visit node
|
178
|
-
#
|
179
|
-
# @param [Parser::AST::Node] node
|
180
|
-
#
|
181
|
-
# @return [undefined]
|
182
|
-
#
|
183
|
-
# @api private
|
184
|
-
#
|
185
|
-
def visit(node)
|
186
|
-
if @predicate.call(node)
|
187
|
-
@match = node
|
188
|
-
end
|
189
|
-
|
190
|
-
node.children.each do |child|
|
191
|
-
visit(child) if child.kind_of?(Parser::AST::Node)
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
end # Finder
|
196
|
-
|
197
130
|
# Return matched node
|
198
131
|
#
|
199
132
|
# @return [Parser::AST::Node]
|