mutant 0.3.0.beta8 → 0.3.0.beta9
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/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
|