mutant 0.3.0.beta8 → 0.3.0.beta9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +10 -4
- data/Rakefile +2 -1
- data/config/flay.yml +1 -1
- data/config/reek.yml +2 -1
- data/lib/mutant.rb +3 -1
- data/lib/mutant/cli/classifier/method.rb +11 -1
- data/lib/mutant/differ.rb +11 -2
- data/lib/mutant/killer/forked.rb +1 -1
- data/lib/mutant/killer/rspec.rb +2 -2
- data/lib/mutant/matcher.rb +15 -2
- data/lib/mutant/matcher/method.rb +6 -2
- data/lib/mutant/matcher/method/instance.rb +34 -0
- data/lib/mutant/matcher/methods.rb +1 -1
- data/lib/mutant/mutation/neutral.rb +10 -1
- data/lib/mutant/mutator/node/{noop.rb → generic.rb} +6 -4
- data/lib/mutant/reporter/cli/printer.rb +23 -2
- data/lib/mutant/reporter/cli/printer/mutation.rb +60 -17
- data/lib/mutant/reporter/cli/printer/subject.rb +1 -1
- data/lib/mutant/subject.rb +15 -2
- data/lib/mutant/subject/method.rb +12 -48
- data/lib/mutant/subject/method/instance.rb +60 -0
- data/lib/mutant/subject/method/singleton.rb +28 -0
- data/mutant.gemspec +4 -4
- data/spec/unit/mutant/matcher/method/instance/class_methods/build_spec.rb +40 -0
- data/spec/unit/mutant/subject/each_spec.rb +16 -23
- metadata +14 -12
- data/spec/unit/mutant/mutator/node/noop/mutation_spec.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2680d48343b2c5b7122252e999dceca9fb51ff4a
|
4
|
+
data.tar.gz: 3fe806d025ebb900b98f863aff8e11b0dffa7fed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e8a6cd662eb49939f08df67a418edd884c8b7318cc4b2b6ab1d630118ce7ec3f0448c508e65678b866379e5ba4b37f8ea46e2bc355e164affbd880b3aa33fcbb
|
7
|
+
data.tar.gz: d3bab712b12881bc4cf816dbedac61568f4d928e09f2c1cf9d2618ec2e908674fa5b8f5cdb453ec1d6da6abb8f1a77c7da58f5c96720808631b295c9a8c197c2
|
data/README.md
CHANGED
@@ -42,6 +42,12 @@ Installation
|
|
42
42
|
|
43
43
|
Install the gem `mutant` via your preferred method.
|
44
44
|
|
45
|
+
The 0.2 series is stable but has outdated dependencies. The 0.3 series is in beta phase currently.
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
gem install mutant -v0.3.0.beta8
|
49
|
+
```
|
50
|
+
|
45
51
|
Examples
|
46
52
|
--------
|
47
53
|
|
@@ -50,13 +56,13 @@ CLI will be simplified in the next releases, but currently stick with this:
|
|
50
56
|
```
|
51
57
|
cd virtus
|
52
58
|
# Run mutant on virtus namespace (that uses the dm-2 style spec layout)
|
53
|
-
mutant
|
59
|
+
mutant --rspec-dm2 '::Virtus*'
|
54
60
|
# Run mutant on specific virtus class
|
55
|
-
mutant
|
61
|
+
mutant --rspec-dm2 ::Virtus::Attribute
|
56
62
|
# Run mutant on specific virtus class method
|
57
|
-
mutant
|
63
|
+
mutant --rspec-dm2 ::Virtus::Attribute.build
|
58
64
|
# Run mutant on specific virtus instance method
|
59
|
-
mutant
|
65
|
+
mutant --rspec-dm2 ::Virtus::Attribute#name
|
60
66
|
```
|
61
67
|
|
62
68
|
Strategies
|
data/Rakefile
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
require 'devtools'
|
2
2
|
Devtools.init_rake_tasks
|
3
3
|
|
4
|
+
Rake.application.load_imports
|
4
5
|
task('metrics:mutant').clear
|
5
6
|
|
6
7
|
namespace :metrics do
|
7
|
-
task :mutant do
|
8
|
+
task :mutant => :coverage do
|
8
9
|
$stderr.puts 'Mutant self mutation is disable till mutant is fast enough for travis'
|
9
10
|
end
|
10
11
|
end
|
data/config/flay.yml
CHANGED
data/config/reek.yml
CHANGED
data/lib/mutant.rb
CHANGED
@@ -40,7 +40,7 @@ require 'mutant/mutator/util'
|
|
40
40
|
require 'mutant/mutator/util/array'
|
41
41
|
require 'mutant/mutator/util/symbol'
|
42
42
|
require 'mutant/mutator/node'
|
43
|
-
require 'mutant/mutator/node/
|
43
|
+
require 'mutant/mutator/node/generic'
|
44
44
|
require 'mutant/mutator/node/literal'
|
45
45
|
require 'mutant/mutator/node/literal/boolean'
|
46
46
|
require 'mutant/mutator/node/literal/range'
|
@@ -74,6 +74,8 @@ require 'mutant/context'
|
|
74
74
|
require 'mutant/context/scope'
|
75
75
|
require 'mutant/subject'
|
76
76
|
require 'mutant/subject/method'
|
77
|
+
require 'mutant/subject/method/instance'
|
78
|
+
require 'mutant/subject/method/singleton'
|
77
79
|
require 'mutant/matcher'
|
78
80
|
require 'mutant/matcher/chain'
|
79
81
|
require 'mutant/matcher/method'
|
@@ -24,10 +24,20 @@ module Mutant
|
|
24
24
|
# @api private
|
25
25
|
#
|
26
26
|
def matcher
|
27
|
-
methods_matcher.matcher.
|
27
|
+
methods_matcher.matcher.build(cache, scope, method)
|
28
28
|
end
|
29
29
|
memoize :matcher
|
30
30
|
|
31
|
+
# Return identification
|
32
|
+
#
|
33
|
+
# @return [String]
|
34
|
+
#
|
35
|
+
# @api private
|
36
|
+
#
|
37
|
+
def identification
|
38
|
+
match.to_s
|
39
|
+
end
|
40
|
+
|
31
41
|
private
|
32
42
|
|
33
43
|
# Return method
|
data/lib/mutant/differ.rb
CHANGED
@@ -6,6 +6,10 @@ module Mutant
|
|
6
6
|
# Return source diff
|
7
7
|
#
|
8
8
|
# @return [String]
|
9
|
+
# if there is a diff
|
10
|
+
#
|
11
|
+
# @return [nil]
|
12
|
+
# otherwise
|
9
13
|
#
|
10
14
|
# @api private
|
11
15
|
#
|
@@ -18,7 +22,7 @@ module Mutant
|
|
18
22
|
output = Diff::LCS::Hunk.new(old, new, diffs.first, max_length, 0).diff(:unified)
|
19
23
|
output << "\n"
|
20
24
|
else
|
21
|
-
|
25
|
+
abort 'Mutation resulted in more than one diff, should not happen! PLS report a bug!'
|
22
26
|
end
|
23
27
|
end
|
24
28
|
memoize :diff
|
@@ -26,10 +30,15 @@ module Mutant
|
|
26
30
|
# Return colorized source diff
|
27
31
|
#
|
28
32
|
# @return [String]
|
33
|
+
# if there is a diff
|
34
|
+
#
|
35
|
+
# @return [nil]
|
36
|
+
# otherwise
|
29
37
|
#
|
30
38
|
# @api private
|
31
39
|
#
|
32
40
|
def colorized_diff
|
41
|
+
return unless diff
|
33
42
|
diff.lines.map do |line|
|
34
43
|
self.class.colorize_line(line)
|
35
44
|
end.join
|
@@ -82,7 +91,7 @@ module Mutant
|
|
82
91
|
# @api private
|
83
92
|
#
|
84
93
|
def max_length
|
85
|
-
old
|
94
|
+
[old, new].map(&:length).max
|
86
95
|
end
|
87
96
|
|
88
97
|
# Return colorized diff line
|
data/lib/mutant/killer/forked.rb
CHANGED
@@ -32,7 +32,7 @@ module Mutant
|
|
32
32
|
def run
|
33
33
|
pid = fork do
|
34
34
|
killer = @killer.new(strategy, mutation)
|
35
|
-
exit(killer.
|
35
|
+
exit(killer.killed? ? CLI::EXIT_SUCCESS : CLI::EXIT_FAILURE)
|
36
36
|
end
|
37
37
|
|
38
38
|
status = Process.wait2(pid).last
|
data/lib/mutant/killer/rspec.rb
CHANGED
@@ -20,9 +20,9 @@ module Mutant
|
|
20
20
|
# TODO: replace with real streams from configuration
|
21
21
|
require 'stringio'
|
22
22
|
null = StringIO.new
|
23
|
-
|
23
|
+
argv = command_line_arguments
|
24
24
|
begin
|
25
|
-
!::RSpec::Core::Runner.run(
|
25
|
+
!::RSpec::Core::Runner.run(argv, null, null).zero?
|
26
26
|
rescue StandardError
|
27
27
|
true
|
28
28
|
end
|
data/lib/mutant/matcher.rb
CHANGED
@@ -11,18 +11,31 @@ module Mutant
|
|
11
11
|
# @return [self]
|
12
12
|
# if block given
|
13
13
|
#
|
14
|
-
# @return [
|
14
|
+
# @return [Enumerable<Subject>]
|
15
15
|
#
|
16
16
|
# @api private
|
17
17
|
#
|
18
18
|
def self.each(cache, input, &block)
|
19
19
|
return to_enum(__method__, cache, input) unless block_given?
|
20
20
|
|
21
|
-
|
21
|
+
build(cache, input).each(&block)
|
22
22
|
|
23
23
|
self
|
24
24
|
end
|
25
25
|
|
26
|
+
# Default matcher build implementation
|
27
|
+
#
|
28
|
+
# @param [Cache] cache
|
29
|
+
# @param [Object] input
|
30
|
+
#
|
31
|
+
# @return [undefined]
|
32
|
+
#
|
33
|
+
# @api private
|
34
|
+
#
|
35
|
+
def self.build(*arguments)
|
36
|
+
new(*arguments)
|
37
|
+
end
|
38
|
+
|
26
39
|
# Enumerate subjects
|
27
40
|
#
|
28
41
|
# @api private
|
@@ -10,7 +10,7 @@ module Mutant
|
|
10
10
|
|
11
11
|
# Enumerate matches
|
12
12
|
#
|
13
|
-
# @return [Enumerable]
|
13
|
+
# @return [Enumerable<Subject>]
|
14
14
|
# returns enumerable when no block given
|
15
15
|
#
|
16
16
|
# @return [self]
|
@@ -22,7 +22,11 @@ module Mutant
|
|
22
22
|
return to_enum unless block_given?
|
23
23
|
|
24
24
|
unless skip?
|
25
|
-
|
25
|
+
if subject
|
26
|
+
yield subject
|
27
|
+
else
|
28
|
+
$stderr.puts "Cannot find definition of: #{identification} in #{source_location.join(':')}"
|
29
|
+
end
|
26
30
|
end
|
27
31
|
|
28
32
|
self
|
@@ -5,6 +5,23 @@ module Mutant
|
|
5
5
|
class Instance < self
|
6
6
|
SUBJECT_CLASS = Subject::Method::Instance
|
7
7
|
|
8
|
+
# Dispatching builder, detects adamantium case
|
9
|
+
#
|
10
|
+
# @param [Cache] cache
|
11
|
+
# @param [Class, Module] scope
|
12
|
+
# @param [UnboundMethod] method
|
13
|
+
#
|
14
|
+
# @return [Matcher::Method::Instance]
|
15
|
+
#
|
16
|
+
# @api private
|
17
|
+
#
|
18
|
+
def self.build(cache, scope, method)
|
19
|
+
if scope.ancestors.include?(::Adamantium) and scope.memoized?(method.name)
|
20
|
+
return Memoized.new(cache, scope, method)
|
21
|
+
end
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
8
25
|
# Return identification
|
9
26
|
#
|
10
27
|
# @return [String]
|
@@ -40,6 +57,23 @@ module Mutant
|
|
40
57
|
node.children[NAME_INDEX] == method_name
|
41
58
|
end
|
42
59
|
|
60
|
+
# Matcher for memoized instance methods
|
61
|
+
class Memoized < self
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
# Return source location
|
66
|
+
#
|
67
|
+
# @return [Array]
|
68
|
+
#
|
69
|
+
# @api private
|
70
|
+
#
|
71
|
+
def source_location
|
72
|
+
scope.original_instance_method(method.name).source_location
|
73
|
+
end
|
74
|
+
|
75
|
+
end # Memoized
|
76
|
+
|
43
77
|
end # Instance
|
44
78
|
end # Method
|
45
79
|
end # Matcher
|
@@ -3,6 +3,15 @@ module Mutant
|
|
3
3
|
# Neutral mutation
|
4
4
|
class Neutral < self
|
5
5
|
|
6
|
+
SYMBOL = 'neutral'
|
7
|
+
|
8
|
+
# Noop mutation, special case of neutral
|
9
|
+
class Noop < self
|
10
|
+
|
11
|
+
SYMBOL = 'noop'
|
12
|
+
|
13
|
+
end
|
14
|
+
|
6
15
|
# Return identification
|
7
16
|
#
|
8
17
|
# @return [String]
|
@@ -10,7 +19,7 @@ module Mutant
|
|
10
19
|
# @api private
|
11
20
|
#
|
12
21
|
def identification
|
13
|
-
"
|
22
|
+
"#{self.class::SYMBOL}:#{super}"
|
14
23
|
end
|
15
24
|
memoize :identification
|
16
25
|
|
@@ -1,13 +1,12 @@
|
|
1
1
|
module Mutant
|
2
2
|
class Mutator
|
3
3
|
class Node
|
4
|
-
#
|
5
|
-
class
|
4
|
+
# Generic mutator
|
5
|
+
class Generic < self
|
6
6
|
|
7
|
-
# Literal references to self do not need to be mutated?
|
8
7
|
handle(:self)
|
9
8
|
|
10
|
-
# These nodes still need a mutator, your contribution is that close!
|
9
|
+
# These nodes still need a dedicated mutator, your contribution is that close!
|
11
10
|
handle(
|
12
11
|
:zsuper, :not, :or, :and, :defined,
|
13
12
|
:next, :break, :match, :gvar, :cvar, :ensure,
|
@@ -28,6 +27,9 @@ module Mutant
|
|
28
27
|
# @api private
|
29
28
|
#
|
30
29
|
def dispatch
|
30
|
+
children.each_index do |index|
|
31
|
+
mutate_child(index) if children.at(index)
|
32
|
+
end
|
31
33
|
end
|
32
34
|
|
33
35
|
end # Noop
|
@@ -30,6 +30,16 @@ module Mutant
|
|
30
30
|
REGISTRY.freeze
|
31
31
|
end
|
32
32
|
|
33
|
+
# Build printer
|
34
|
+
#
|
35
|
+
# @return [Printer]
|
36
|
+
#
|
37
|
+
# @api private
|
38
|
+
#
|
39
|
+
def self.build(*args)
|
40
|
+
new(*args)
|
41
|
+
end
|
42
|
+
|
33
43
|
# Run printer
|
34
44
|
#
|
35
45
|
# @return [self]
|
@@ -37,7 +47,7 @@ module Mutant
|
|
37
47
|
# @api private
|
38
48
|
#
|
39
49
|
def self.run(*args)
|
40
|
-
|
50
|
+
build(*args).run
|
41
51
|
self
|
42
52
|
end
|
43
53
|
|
@@ -59,7 +69,6 @@ module Mutant
|
|
59
69
|
|
60
70
|
private
|
61
71
|
|
62
|
-
|
63
72
|
# Return status color
|
64
73
|
#
|
65
74
|
# @return [Color]
|
@@ -70,6 +79,18 @@ module Mutant
|
|
70
79
|
success? ? Color::GREEN : Color::RED
|
71
80
|
end
|
72
81
|
|
82
|
+
# Visit object
|
83
|
+
#
|
84
|
+
# @param [Object] object
|
85
|
+
#
|
86
|
+
# @return [undefined]
|
87
|
+
#
|
88
|
+
# @api private
|
89
|
+
#
|
90
|
+
def visit(object)
|
91
|
+
self.class.visit(object, output)
|
92
|
+
end
|
93
|
+
|
73
94
|
# Print an info line to output
|
74
95
|
#
|
75
96
|
# @return [undefined]
|
@@ -5,6 +5,29 @@ module Mutant
|
|
5
5
|
# Mutation printer
|
6
6
|
class Mutation < self
|
7
7
|
|
8
|
+
handle(Runner::Mutation)
|
9
|
+
|
10
|
+
# Build printer
|
11
|
+
#
|
12
|
+
# @param [Runner::Mutation] runner
|
13
|
+
# @param [IO] output
|
14
|
+
#
|
15
|
+
# @return [Printer::Mutation]
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
#
|
19
|
+
def self.build(runner, output)
|
20
|
+
mutation = runner.mutation
|
21
|
+
case mutation
|
22
|
+
when Mutant::Mutation::Neutral::Noop
|
23
|
+
Noop
|
24
|
+
when Mutant::Mutation::Evil, Mutant::Mutation::Neutral
|
25
|
+
Diff
|
26
|
+
else
|
27
|
+
raise "Unknown mutation: #{mutation}"
|
28
|
+
end.new(runner, output)
|
29
|
+
end
|
30
|
+
|
8
31
|
# Run mutation printer
|
9
32
|
#
|
10
33
|
# @return [undefined]
|
@@ -13,7 +36,7 @@ module Mutant
|
|
13
36
|
#
|
14
37
|
def run
|
15
38
|
status(mutation.identification)
|
16
|
-
puts(
|
39
|
+
puts(details)
|
17
40
|
end
|
18
41
|
|
19
42
|
private
|
@@ -28,25 +51,45 @@ module Mutant
|
|
28
51
|
object.mutation
|
29
52
|
end
|
30
53
|
|
31
|
-
#
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
differ = Differ.build(original, current)
|
42
|
-
diff = color? ? differ.colorized_diff : differ.diff
|
54
|
+
# Reporter for noop mutations
|
55
|
+
class Noop < self
|
56
|
+
|
57
|
+
MESSAGE =
|
58
|
+
"Parsed subject AST:\n" \
|
59
|
+
"%s\n" \
|
60
|
+
"Unparsed source:\n" \
|
61
|
+
"%s\n"
|
62
|
+
|
63
|
+
private
|
43
64
|
|
44
|
-
|
45
|
-
|
65
|
+
# Return details
|
66
|
+
#
|
67
|
+
# @return [String]
|
68
|
+
#
|
69
|
+
# @api private
|
70
|
+
#
|
71
|
+
def details
|
72
|
+
sprintf(MESSAGE, mutation.subject.node.inspect, mutation.original_source)
|
46
73
|
end
|
47
74
|
|
48
|
-
|
49
|
-
|
75
|
+
end # Noop
|
76
|
+
|
77
|
+
# Reporter for neutral and evil mutations
|
78
|
+
class Diff < self
|
79
|
+
|
80
|
+
# Return diff
|
81
|
+
#
|
82
|
+
# @return [String]
|
83
|
+
#
|
84
|
+
# @api private
|
85
|
+
#
|
86
|
+
def details
|
87
|
+
original, current = mutation.original_source, mutation.source
|
88
|
+
differ = Differ.build(original, current)
|
89
|
+
color? ? differ.colorized_diff : differ.diff
|
90
|
+
end
|
91
|
+
|
92
|
+
end # Evil
|
50
93
|
|
51
94
|
end # Mutantion
|
52
95
|
end # Printer
|
data/lib/mutant/subject.rb
CHANGED
@@ -15,8 +15,11 @@ module Mutant
|
|
15
15
|
#
|
16
16
|
def each
|
17
17
|
return to_enum unless block_given?
|
18
|
-
|
19
|
-
|
18
|
+
|
19
|
+
yield noop_mutation
|
20
|
+
|
21
|
+
mutations.each do |mutation|
|
22
|
+
yield mutation
|
20
23
|
end
|
21
24
|
|
22
25
|
self
|
@@ -109,5 +112,15 @@ module Mutant
|
|
109
112
|
abstract_method :subtype
|
110
113
|
private :subtype
|
111
114
|
|
115
|
+
# Return neutral mutation
|
116
|
+
#
|
117
|
+
# @return [Mutation::Neutral]
|
118
|
+
#
|
119
|
+
# @api private
|
120
|
+
#
|
121
|
+
def noop_mutation
|
122
|
+
Mutation::Neutral::Noop.new(self, node)
|
123
|
+
end
|
124
|
+
|
112
125
|
end # Subject
|
113
126
|
end # Mutant
|
@@ -27,6 +27,18 @@ module Mutant
|
|
27
27
|
|
28
28
|
private
|
29
29
|
|
30
|
+
# Return mutations
|
31
|
+
#
|
32
|
+
# @return [Enumerable<Mutation>]
|
33
|
+
#
|
34
|
+
# @api private
|
35
|
+
#
|
36
|
+
def mutations
|
37
|
+
Mutator.each(node).map do |mutant|
|
38
|
+
Mutation::Evil.new(self, mutant)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
30
42
|
# Return scope
|
31
43
|
#
|
32
44
|
# @return [Class, Module]
|
@@ -47,54 +59,6 @@ module Mutant
|
|
47
59
|
"#{context.identification}#{self.class::SYMBOL}#{name}"
|
48
60
|
end
|
49
61
|
|
50
|
-
# Instance method subjects
|
51
|
-
class Instance < self
|
52
|
-
|
53
|
-
NAME_INDEX = 0
|
54
|
-
SYMBOL = '#'.freeze
|
55
|
-
|
56
|
-
# Test if method is public
|
57
|
-
#
|
58
|
-
# @return [true]
|
59
|
-
# if method is public
|
60
|
-
#
|
61
|
-
# @return [false]
|
62
|
-
# otherwise
|
63
|
-
#
|
64
|
-
# @api private
|
65
|
-
#
|
66
|
-
def public?
|
67
|
-
scope.public_method_defined?(name)
|
68
|
-
end
|
69
|
-
memoize :public?
|
70
|
-
|
71
|
-
private
|
72
|
-
|
73
|
-
end # Instance
|
74
|
-
|
75
|
-
# Singleton method subjects
|
76
|
-
class Singleton < self
|
77
|
-
|
78
|
-
NAME_INDEX = 1
|
79
|
-
SYMBOL = '.'.freeze
|
80
|
-
|
81
|
-
# Test if method is public
|
82
|
-
#
|
83
|
-
# @return [true]
|
84
|
-
# if method is public
|
85
|
-
#
|
86
|
-
# @return [false]
|
87
|
-
# otherwise
|
88
|
-
#
|
89
|
-
# @api private
|
90
|
-
#
|
91
|
-
def public?
|
92
|
-
scope.singleton_class.public_method_defined?(name)
|
93
|
-
end
|
94
|
-
memoize :public?
|
95
|
-
|
96
|
-
end # Singleton
|
97
|
-
|
98
62
|
end # Method
|
99
63
|
end # Subject
|
100
64
|
end # Mutant
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Mutant
|
2
|
+
class Subject
|
3
|
+
class Method
|
4
|
+
# Instance method subjects
|
5
|
+
class Instance < self
|
6
|
+
|
7
|
+
NAME_INDEX = 0
|
8
|
+
SYMBOL = '#'.freeze
|
9
|
+
|
10
|
+
# Test if method is public
|
11
|
+
#
|
12
|
+
# @return [true]
|
13
|
+
# if method is public
|
14
|
+
#
|
15
|
+
# @return [false]
|
16
|
+
# otherwise
|
17
|
+
#
|
18
|
+
# @api private
|
19
|
+
#
|
20
|
+
def public?
|
21
|
+
scope.public_method_defined?(name)
|
22
|
+
end
|
23
|
+
memoize :public?
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# Mutator for memoized instance methods
|
28
|
+
class Memoized < self
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
# Return mutations
|
33
|
+
#
|
34
|
+
# @return [Enumerable<Mutation>]
|
35
|
+
#
|
36
|
+
# @api private
|
37
|
+
#
|
38
|
+
def mutations
|
39
|
+
Mutator.each(node).map do |mutant|
|
40
|
+
Mutation::Evil.new(self, memoizer_node(mutant))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Return memoizer node for mutant
|
45
|
+
#
|
46
|
+
# @param [Parser::AST::Node] mutant
|
47
|
+
#
|
48
|
+
# @return [Parser::AST::Node]
|
49
|
+
#
|
50
|
+
# @api private
|
51
|
+
#
|
52
|
+
def memoizer_node(mutant)
|
53
|
+
s(:begin, mutant, s(:send, nil, :memoize, s(:args, s(:sym, name))))
|
54
|
+
end
|
55
|
+
|
56
|
+
end # Memoized
|
57
|
+
end # Instance
|
58
|
+
end # Method
|
59
|
+
end # Subject
|
60
|
+
end # Mutant
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Mutant
|
2
|
+
class Subject
|
3
|
+
class Method
|
4
|
+
# Singleton method subjects
|
5
|
+
class Singleton < self
|
6
|
+
|
7
|
+
NAME_INDEX = 1
|
8
|
+
SYMBOL = '.'.freeze
|
9
|
+
|
10
|
+
# Test if method is public
|
11
|
+
#
|
12
|
+
# @return [true]
|
13
|
+
# if method is public
|
14
|
+
#
|
15
|
+
# @return [false]
|
16
|
+
# otherwise
|
17
|
+
#
|
18
|
+
# @api private
|
19
|
+
#
|
20
|
+
def public?
|
21
|
+
scope.singleton_class.public_method_defined?(name)
|
22
|
+
end
|
23
|
+
memoize :public?
|
24
|
+
|
25
|
+
end # Singleton
|
26
|
+
end # Method
|
27
|
+
end # Subject
|
28
|
+
end # Mutant
|
data/mutant.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |gem|
|
4
4
|
gem.name = 'mutant'
|
5
|
-
gem.version = '0.3.0.
|
5
|
+
gem.version = '0.3.0.beta9'
|
6
6
|
gem.authors = [ 'Markus Schirp' ]
|
7
7
|
gem.email = [ 'mbj@schirp-dso.com' ]
|
8
8
|
|
@@ -16,11 +16,11 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.extra_rdoc_files = %w[TODO LICENSE]
|
17
17
|
gem.executables = [ 'mutant', 'zombie' ]
|
18
18
|
|
19
|
-
gem.add_runtime_dependency('parser', '~> 2.0.
|
20
|
-
gem.add_runtime_dependency('unparser', '~> 0.0.
|
19
|
+
gem.add_runtime_dependency('parser', '~> 2.0.pre1')
|
20
|
+
gem.add_runtime_dependency('unparser', '~> 0.0.8')
|
21
21
|
gem.add_runtime_dependency('ice_nine', '~> 0.8.0')
|
22
22
|
gem.add_runtime_dependency('descendants_tracker', '~> 0.0.1')
|
23
|
-
gem.add_runtime_dependency('adamantium', '~> 0.0.
|
23
|
+
gem.add_runtime_dependency('adamantium', '~> 0.0.10')
|
24
24
|
gem.add_runtime_dependency('equalizer', '~> 0.0.5')
|
25
25
|
gem.add_runtime_dependency('inflecto', '~> 0.0.2')
|
26
26
|
gem.add_runtime_dependency('anima', '~> 0.0.6')
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Mutant::Matcher::Method::Instance, '.build' do
|
4
|
+
let(:object) { described_class }
|
5
|
+
|
6
|
+
subject { object.build(cache, scope, method) }
|
7
|
+
|
8
|
+
let(:cache) { mock }
|
9
|
+
|
10
|
+
let(:scope) do
|
11
|
+
Class.new do
|
12
|
+
include Adamantium
|
13
|
+
|
14
|
+
def foo
|
15
|
+
end
|
16
|
+
memoize :foo
|
17
|
+
|
18
|
+
def bar
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
let(:method) do
|
24
|
+
scope.instance_method(method_name)
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'with adamantium infected scope' do
|
28
|
+
context 'with unmemoized method' do
|
29
|
+
let(:method_name) { :bar }
|
30
|
+
|
31
|
+
it { should eql(Mutant::Matcher::Method::Instance.new(cache, scope, method)) }
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'with memoized method' do
|
35
|
+
let(:method_name) { :foo }
|
36
|
+
|
37
|
+
it { should eql(Mutant::Matcher::Method::Instance::Memoized.new(cache, scope, method)) }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -4,35 +4,28 @@ describe Mutant::Subject, '#each' do
|
|
4
4
|
subject { object.each { |item| yields << item } }
|
5
5
|
|
6
6
|
let(:class_under_test) do
|
7
|
-
|
7
|
+
mutations = [mutation_a, mutation_b]
|
8
|
+
Class.new(described_class) do
|
9
|
+
define_method(:mutations) { mutations }
|
10
|
+
end
|
8
11
|
end
|
9
12
|
|
10
|
-
let(:object)
|
11
|
-
let(:
|
12
|
-
let(:
|
13
|
-
let(:context)
|
14
|
-
let(:mutant)
|
15
|
-
let(:
|
16
|
-
let(:
|
17
|
-
|
18
|
-
before do
|
19
|
-
Mutant::Mutator.stub(:each).with(ast).and_yield(mutant).and_return(Mutant::Mutator)
|
20
|
-
Mutant::Mutation.stub(:new => mutation)
|
21
|
-
end
|
13
|
+
let(:object) { class_under_test.new(context, node) }
|
14
|
+
let(:yields) { [] }
|
15
|
+
let(:node) { mock('Node') }
|
16
|
+
let(:context) { mock('Context') }
|
17
|
+
let(:mutant) { mock('Mutant') }
|
18
|
+
let(:mutation_a) { mock('Mutation A') }
|
19
|
+
let(:mutation_b) { mock('Mutation B') }
|
22
20
|
|
23
21
|
it_should_behave_like 'an #each method'
|
24
22
|
|
25
|
-
|
26
|
-
Mutant::
|
27
|
-
subject
|
28
|
-
end
|
29
|
-
|
30
|
-
it 'should yield mutations' do
|
31
|
-
expect { subject }.to change { yields.dup }.from([]).to([mutation])
|
23
|
+
let(:neutral_mutation) do
|
24
|
+
Mutant::Mutation::Neutral.new(object, node)
|
32
25
|
end
|
33
26
|
|
34
|
-
it '
|
35
|
-
|
36
|
-
|
27
|
+
it 'yields mutations' do
|
28
|
+
expect { subject }.to change { yields.dup }.from([])
|
29
|
+
.to([neutral_mutation, mutation_a, mutation_b])
|
37
30
|
end
|
38
31
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mutant
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.0.
|
4
|
+
version: 0.3.0.beta9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Markus Schirp
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-07-
|
11
|
+
date: 2013-07-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: parser
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ~>
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 2.0.
|
19
|
+
version: 2.0.pre1
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ~>
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 2.0.
|
26
|
+
version: 2.0.pre1
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: unparser
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ~>
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.0.
|
33
|
+
version: 0.0.8
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ~>
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.0.
|
40
|
+
version: 0.0.8
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: ice_nine
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -72,14 +72,14 @@ dependencies:
|
|
72
72
|
requirements:
|
73
73
|
- - ~>
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: 0.0.
|
75
|
+
version: 0.0.10
|
76
76
|
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - ~>
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: 0.0.
|
82
|
+
version: 0.0.10
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: equalizer
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -224,6 +224,7 @@ files:
|
|
224
224
|
- lib/mutant/mutator/node/block.rb
|
225
225
|
- lib/mutant/mutator/node/case.rb
|
226
226
|
- lib/mutant/mutator/node/define.rb
|
227
|
+
- lib/mutant/mutator/node/generic.rb
|
227
228
|
- lib/mutant/mutator/node/if.rb
|
228
229
|
- lib/mutant/mutator/node/literal.rb
|
229
230
|
- lib/mutant/mutator/node/literal/array.rb
|
@@ -238,7 +239,6 @@ files:
|
|
238
239
|
- lib/mutant/mutator/node/literal/string.rb
|
239
240
|
- lib/mutant/mutator/node/literal/symbol.rb
|
240
241
|
- lib/mutant/mutator/node/mlhs.rb
|
241
|
-
- lib/mutant/mutator/node/noop.rb
|
242
242
|
- lib/mutant/mutator/node/return.rb
|
243
243
|
- lib/mutant/mutator/node/send.rb
|
244
244
|
- lib/mutant/mutator/node/send/binary.rb
|
@@ -273,6 +273,8 @@ files:
|
|
273
273
|
- lib/mutant/strategy/static.rb
|
274
274
|
- lib/mutant/subject.rb
|
275
275
|
- lib/mutant/subject/method.rb
|
276
|
+
- lib/mutant/subject/method/instance.rb
|
277
|
+
- lib/mutant/subject/method/singleton.rb
|
276
278
|
- lib/mutant/support/method_object.rb
|
277
279
|
- lib/mutant/zombifier.rb
|
278
280
|
- mutant.gemspec
|
@@ -303,6 +305,7 @@ files:
|
|
303
305
|
- spec/unit/mutant/matcher/chain/each_spec.rb
|
304
306
|
- spec/unit/mutant/matcher/chain/matchers_spec.rb
|
305
307
|
- spec/unit/mutant/matcher/each_spec.rb
|
308
|
+
- spec/unit/mutant/matcher/method/instance/class_methods/build_spec.rb
|
306
309
|
- spec/unit/mutant/matcher/method/instance/each_spec.rb
|
307
310
|
- spec/unit/mutant/matcher/method/singleton/each_spec.rb
|
308
311
|
- spec/unit/mutant/matcher/methods/instance/each_spec.rb
|
@@ -328,7 +331,6 @@ files:
|
|
328
331
|
- spec/unit/mutant/mutator/node/literal/regex_spec.rb
|
329
332
|
- spec/unit/mutant/mutator/node/literal/string_spec.rb
|
330
333
|
- spec/unit/mutant/mutator/node/literal/symbol_spec.rb
|
331
|
-
- spec/unit/mutant/mutator/node/noop/mutation_spec.rb
|
332
334
|
- spec/unit/mutant/mutator/node/return/mutation_spec.rb
|
333
335
|
- spec/unit/mutant/mutator/node/send/mutation_spec.rb
|
334
336
|
- spec/unit/mutant/mutator/node/super/mutation_spec.rb
|
@@ -378,7 +380,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
378
380
|
version: 1.3.1
|
379
381
|
requirements: []
|
380
382
|
rubyforge_project:
|
381
|
-
rubygems_version: 2.0.
|
383
|
+
rubygems_version: 2.0.2
|
382
384
|
signing_key:
|
383
385
|
specification_version: 4
|
384
386
|
summary: Mutation testing tool for ruby under MRI and Rubinius
|
@@ -410,6 +412,7 @@ test_files:
|
|
410
412
|
- spec/unit/mutant/matcher/chain/each_spec.rb
|
411
413
|
- spec/unit/mutant/matcher/chain/matchers_spec.rb
|
412
414
|
- spec/unit/mutant/matcher/each_spec.rb
|
415
|
+
- spec/unit/mutant/matcher/method/instance/class_methods/build_spec.rb
|
413
416
|
- spec/unit/mutant/matcher/method/instance/each_spec.rb
|
414
417
|
- spec/unit/mutant/matcher/method/singleton/each_spec.rb
|
415
418
|
- spec/unit/mutant/matcher/methods/instance/each_spec.rb
|
@@ -435,7 +438,6 @@ test_files:
|
|
435
438
|
- spec/unit/mutant/mutator/node/literal/regex_spec.rb
|
436
439
|
- spec/unit/mutant/mutator/node/literal/string_spec.rb
|
437
440
|
- spec/unit/mutant/mutator/node/literal/symbol_spec.rb
|
438
|
-
- spec/unit/mutant/mutator/node/noop/mutation_spec.rb
|
439
441
|
- spec/unit/mutant/mutator/node/return/mutation_spec.rb
|
440
442
|
- spec/unit/mutant/mutator/node/send/mutation_spec.rb
|
441
443
|
- spec/unit/mutant/mutator/node/super/mutation_spec.rb
|
@@ -1,13 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
# FIXME: This spec needs to be structured better!
|
4
|
-
describe Mutant::Mutator::Node::Noop, 'send' do
|
5
|
-
|
6
|
-
let(:source) { 'alias foo bar' }
|
7
|
-
|
8
|
-
let(:mutations) do
|
9
|
-
mutations = []
|
10
|
-
end
|
11
|
-
|
12
|
-
it_should_behave_like 'a mutator'
|
13
|
-
end
|