mutant 0.3.0.beta4 → 0.3.0.beta5
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.
- 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]
|