mutant 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Changelog.md +12 -1
- data/Gemfile +1 -1
- data/lib/mutant.rb +3 -1
- data/lib/mutant/matcher/method.rb +23 -14
- data/lib/mutant/matcher/method/classifier.rb +21 -6
- data/lib/mutant/matcher/method/instance.rb +0 -11
- data/lib/mutant/matcher/method/singleton.rb +0 -11
- data/lib/mutant/matcher/scope_methods.rb +56 -13
- data/lib/mutant/mutator/node/block.rb +20 -0
- data/lib/mutant/mutator/node/if_statement.rb +2 -2
- data/lib/mutant/mutator/node/literal/range.rb +1 -1
- data/lib/mutant/strategy/rspec.rb +4 -4
- data/lib/mutant/strategy/rspec/example_lookup.rb +16 -2
- data/mutant.gemspec +1 -1
- data/spec/shared/mutator_behavior.rb +17 -11
- data/spec/unit/mutant/matcher/method/classifier/class_methods/run_spec.rb +27 -9
- data/spec/unit/mutant/mutator/node/block/mutation_spec.rb +9 -8
- data/spec/unit/mutant/mutator/node/define/mutation_spec.rb +2 -2
- data/spec/unit/mutant/mutator/node/if_statement/mutation_spec.rb +47 -17
- data/spec/unit/mutant/mutator/node/literal/array_spec.rb +1 -1
- data/spec/unit/mutant/mutator/node/literal/empty_array_spec.rb +1 -1
- data/spec/unit/mutant/mutator/node/literal/fixnum_spec.rb +1 -1
- data/spec/unit/mutant/mutator/node/literal/float_spec.rb +2 -2
- data/spec/unit/mutant/mutator/node/literal/hash_spec.rb +13 -13
- data/spec/unit/mutant/mutator/node/literal/range_spec.rb +2 -2
- data/spec/unit/mutant/strategy/rspec/example_lookup/spec_file_spec.rb +61 -0
- metadata +3 -5
- data/spec/shared/method_filter_parse_behavior.rb +0 -16
- data/spec/unit/mutant/matcher/method/method_spec.rb +0 -11
data/Changelog.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
-
# v0.
|
1
|
+
# v0.2.1 xxx
|
2
|
+
|
3
|
+
* [fixed] Crash on unavailable source location
|
4
|
+
* [fixed] Incorrect handling of if and unless statements
|
5
|
+
* [fixed] Expand Foo#initialize to spec/unit/foo in rspec dm2 strategy
|
6
|
+
* [fixed] Correctly expand [] to element_reader_spec.rb in rspec dm2 strategy
|
7
|
+
* [fixed] Correctly expand []= to element_writer_spec.rb in rspec dm2 strategy
|
8
|
+
* [fixed] Correctly expand foo= to foo_writer_spec.rb in rspec dm2 strategy
|
9
|
+
|
10
|
+
[Compare v0.2.0..v0.2.1](https://github.com/mbj/mutant/compare/v0.2.0...v0.2.1)
|
11
|
+
|
12
|
+
# v0.2.0 2012-12-07
|
2
13
|
|
3
14
|
First public release!
|
data/Gemfile
CHANGED
@@ -2,5 +2,5 @@ source 'https://rubygems.org'
|
|
2
2
|
|
3
3
|
gemspec
|
4
4
|
|
5
|
-
gem 'devtools', :git => 'https://github.com/mbj/devtools.git'
|
5
|
+
gem 'devtools', :git => 'https://github.com/mbj/devtools.git'
|
6
6
|
eval(File.read(File.join(File.dirname(__FILE__),'Gemfile.devtools')))
|
data/lib/mutant.rb
CHANGED
@@ -41,6 +41,8 @@ module Mutant
|
|
41
41
|
self
|
42
42
|
end
|
43
43
|
|
44
|
+
PID = Process.pid
|
45
|
+
|
44
46
|
end
|
45
47
|
|
46
48
|
require 'mutant/support/method_object'
|
@@ -84,8 +86,8 @@ require 'mutant/matcher/object_space'
|
|
84
86
|
require 'mutant/matcher/method'
|
85
87
|
require 'mutant/matcher/method/singleton'
|
86
88
|
require 'mutant/matcher/method/instance'
|
87
|
-
require 'mutant/matcher/method/classifier'
|
88
89
|
require 'mutant/matcher/scope_methods'
|
90
|
+
require 'mutant/matcher/method/classifier'
|
89
91
|
require 'mutant/killer'
|
90
92
|
require 'mutant/killer/static'
|
91
93
|
require 'mutant/killer/rspec'
|
@@ -28,6 +28,12 @@ module Mutant
|
|
28
28
|
#
|
29
29
|
def each(&block)
|
30
30
|
return to_enum unless block_given?
|
31
|
+
|
32
|
+
unless source_location
|
33
|
+
$stderr.puts "#{method.inspect} does not have source location unable to emit matcher"
|
34
|
+
return self
|
35
|
+
end
|
36
|
+
|
31
37
|
subject.tap do |subject|
|
32
38
|
yield subject if subject
|
33
39
|
end
|
@@ -35,21 +41,21 @@ module Mutant
|
|
35
41
|
self
|
36
42
|
end
|
37
43
|
|
38
|
-
# Return
|
44
|
+
# Return method
|
39
45
|
#
|
40
|
-
# @return [
|
46
|
+
# @return [UnboundMethod, Method]
|
41
47
|
#
|
42
48
|
# @api private
|
43
49
|
#
|
44
|
-
attr_reader :
|
50
|
+
attr_reader :method
|
45
51
|
|
46
|
-
# Return
|
52
|
+
# Return scope
|
47
53
|
#
|
48
|
-
# @return [
|
54
|
+
# @return [Class|Module]
|
49
55
|
#
|
50
56
|
# @api private
|
51
57
|
#
|
52
|
-
attr_reader :
|
58
|
+
attr_reader :scope
|
53
59
|
|
54
60
|
# Return method name
|
55
61
|
#
|
@@ -57,7 +63,9 @@ module Mutant
|
|
57
63
|
#
|
58
64
|
# @api private
|
59
65
|
#
|
60
|
-
|
66
|
+
def method_name
|
67
|
+
method.name
|
68
|
+
end
|
61
69
|
|
62
70
|
# Test if method is public
|
63
71
|
#
|
@@ -76,26 +84,27 @@ module Mutant
|
|
76
84
|
# Initialize method filter
|
77
85
|
#
|
78
86
|
# @param [Class|Module] scope
|
79
|
-
# @param [
|
87
|
+
# @param [Method, UnboundMethod] method
|
80
88
|
#
|
81
89
|
# @return [undefined]
|
82
90
|
#
|
83
91
|
# @api private
|
84
92
|
#
|
85
|
-
def initialize(scope,
|
86
|
-
@scope, @
|
87
|
-
@context = Context::Scope.build(scope, source_path)
|
93
|
+
def initialize(scope, method)
|
94
|
+
@scope, @method = scope, method
|
88
95
|
# FIXME: cache public private should not be needed, loader should not override visibility! (But does currently) :(
|
89
96
|
public?
|
90
97
|
end
|
91
98
|
|
92
|
-
# Return
|
99
|
+
# Return context
|
93
100
|
#
|
94
|
-
# @return [
|
101
|
+
# @return [Context::Scope]
|
95
102
|
#
|
96
103
|
# @api private
|
97
104
|
#
|
98
|
-
|
105
|
+
def context
|
106
|
+
Context::Scope.build(scope, source_path)
|
107
|
+
end
|
99
108
|
|
100
109
|
# Return full ast
|
101
110
|
#
|
@@ -6,15 +6,15 @@ module Mutant
|
|
6
6
|
include Adamantium::Flat
|
7
7
|
|
8
8
|
TABLE = {
|
9
|
-
'.' => Matcher::
|
10
|
-
'#' => Matcher::
|
9
|
+
'.' => Matcher::ScopeMethods::Singleton,
|
10
|
+
'#' => Matcher::ScopeMethods::Instance
|
11
11
|
}.freeze
|
12
12
|
|
13
13
|
SCOPE_FORMAT = /\A([^#.]+)(\.|#)(.+)\z/.freeze
|
14
14
|
|
15
15
|
# Positions of captured regexp groups
|
16
16
|
# Freezing fixnums to avoid their singleton classes are patched.
|
17
|
-
SCOPE_NAME_POSITION
|
17
|
+
SCOPE_NAME_POSITION = 1.freeze
|
18
18
|
SCOPE_SYMBOL_POSITION = 2.freeze
|
19
19
|
METHOD_NAME_POSITION = 3.freeze
|
20
20
|
|
@@ -45,8 +45,22 @@ module Mutant
|
|
45
45
|
# @api private
|
46
46
|
#
|
47
47
|
def matcher
|
48
|
-
|
48
|
+
scope_matcher.matcher.new(scope, method)
|
49
49
|
end
|
50
|
+
memoize :matcher
|
51
|
+
|
52
|
+
# Return method
|
53
|
+
#
|
54
|
+
# @return [Method, UnboundMethod]
|
55
|
+
#
|
56
|
+
# @api private
|
57
|
+
#
|
58
|
+
def method
|
59
|
+
scope_matcher.methods.detect do |method|
|
60
|
+
method.name == method_name
|
61
|
+
end || raise("Cannot find #{method_name} for #{scope}")
|
62
|
+
end
|
63
|
+
memoize :method, :freezer => :noop
|
50
64
|
|
51
65
|
# Return match
|
52
66
|
#
|
@@ -117,9 +131,10 @@ module Mutant
|
|
117
131
|
#
|
118
132
|
# @api private
|
119
133
|
#
|
120
|
-
def
|
121
|
-
TABLE.fetch(scope_symbol)
|
134
|
+
def scope_matcher
|
135
|
+
TABLE.fetch(scope_symbol).new(scope)
|
122
136
|
end
|
137
|
+
memoize :scope_matcher
|
123
138
|
end
|
124
139
|
end
|
125
140
|
end
|
@@ -50,17 +50,6 @@ module Mutant
|
|
50
50
|
node.name == method_name
|
51
51
|
end
|
52
52
|
|
53
|
-
# Return method instance
|
54
|
-
#
|
55
|
-
# @return [UnboundMethod]
|
56
|
-
#
|
57
|
-
# @api private
|
58
|
-
#
|
59
|
-
def method
|
60
|
-
scope.instance_method(method_name)
|
61
|
-
end
|
62
|
-
memoize :method, :freezer => :noop
|
63
|
-
|
64
53
|
end
|
65
54
|
end
|
66
55
|
end
|
@@ -33,17 +33,6 @@ module Mutant
|
|
33
33
|
|
34
34
|
private
|
35
35
|
|
36
|
-
# Return method instance
|
37
|
-
#
|
38
|
-
# @return [UnboundMethod]
|
39
|
-
#
|
40
|
-
# @api private
|
41
|
-
#
|
42
|
-
def method
|
43
|
-
scope.method(method_name)
|
44
|
-
end
|
45
|
-
memoize :method, :freezer => :noop
|
46
|
-
|
47
36
|
# Test for node match
|
48
37
|
#
|
49
38
|
# @param [Rubinius::AST::Node] node
|
@@ -24,6 +24,7 @@ module Mutant
|
|
24
24
|
#
|
25
25
|
def each(&block)
|
26
26
|
return to_enum unless block_given?
|
27
|
+
|
27
28
|
methods.each do |method|
|
28
29
|
emit_matches(method, &block)
|
29
30
|
end
|
@@ -31,6 +32,29 @@ module Mutant
|
|
31
32
|
self
|
32
33
|
end
|
33
34
|
|
35
|
+
# Return methods
|
36
|
+
#
|
37
|
+
# @return [Enumerable<Method, UnboundMethod>]
|
38
|
+
#
|
39
|
+
# @api private
|
40
|
+
#
|
41
|
+
def methods
|
42
|
+
method_names.map do |name|
|
43
|
+
access(name)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
memoize :methods
|
47
|
+
|
48
|
+
# Return method matcher class
|
49
|
+
#
|
50
|
+
# @return [Class:Matcher::Method]
|
51
|
+
#
|
52
|
+
# @api private
|
53
|
+
#
|
54
|
+
def matcher
|
55
|
+
self.class::MATCHER
|
56
|
+
end
|
57
|
+
|
34
58
|
private
|
35
59
|
|
36
60
|
# Initialize object
|
@@ -47,7 +71,7 @@ module Mutant
|
|
47
71
|
|
48
72
|
# Emit matches for method
|
49
73
|
#
|
50
|
-
# @param [UnboundMethod] method
|
74
|
+
# @param [UnboundMethod, Method] method
|
51
75
|
#
|
52
76
|
# @return [undefined]
|
53
77
|
#
|
@@ -59,22 +83,29 @@ module Mutant
|
|
59
83
|
end
|
60
84
|
end
|
61
85
|
|
62
|
-
|
63
|
-
abstract_method :methods
|
64
|
-
|
65
|
-
# Return method matcher class
|
86
|
+
# Return method names
|
66
87
|
#
|
67
|
-
# @return [
|
88
|
+
# @return [Enumerable<Symbol>]
|
68
89
|
#
|
69
90
|
# @api private
|
70
91
|
#
|
71
|
-
|
72
|
-
self.class::MATCHER
|
73
|
-
end
|
92
|
+
abstract_method :method_names
|
74
93
|
|
75
94
|
class Singleton < self
|
76
95
|
MATCHER = Mutant::Matcher::Method::Singleton
|
77
96
|
|
97
|
+
# Return method for name
|
98
|
+
#
|
99
|
+
# @param [Symbol] method_name
|
100
|
+
#
|
101
|
+
# @return [Method]
|
102
|
+
#
|
103
|
+
# @api private
|
104
|
+
#
|
105
|
+
def access(method_name)
|
106
|
+
scope.method(method_name)
|
107
|
+
end
|
108
|
+
|
78
109
|
private
|
79
110
|
|
80
111
|
# Return singleton methods defined on scope
|
@@ -85,7 +116,7 @@ module Mutant
|
|
85
116
|
#
|
86
117
|
# @api private
|
87
118
|
#
|
88
|
-
def
|
119
|
+
def method_names
|
89
120
|
singleton_class = scope.singleton_class
|
90
121
|
|
91
122
|
names =
|
@@ -93,7 +124,7 @@ module Mutant
|
|
93
124
|
singleton_class.private_instance_methods(false) +
|
94
125
|
singleton_class.protected_instance_methods(false)
|
95
126
|
|
96
|
-
names.
|
127
|
+
names.sort.reject do |name|
|
97
128
|
name.to_sym == :__class_init__
|
98
129
|
end
|
99
130
|
end
|
@@ -102,6 +133,18 @@ module Mutant
|
|
102
133
|
class Instance < self
|
103
134
|
MATCHER = Mutant::Matcher::Method::Instance
|
104
135
|
|
136
|
+
# Return method for name
|
137
|
+
#
|
138
|
+
# @param [Symbol] method_name
|
139
|
+
#
|
140
|
+
# @return [UnboundMethod]
|
141
|
+
#
|
142
|
+
# @api private
|
143
|
+
#
|
144
|
+
def access(method_name)
|
145
|
+
scope.instance_method(method_name)
|
146
|
+
end
|
147
|
+
|
105
148
|
private
|
106
149
|
|
107
150
|
# Return instance methods names of scope
|
@@ -110,7 +153,7 @@ module Mutant
|
|
110
153
|
#
|
111
154
|
# @return [Enumerable<Symbol>]
|
112
155
|
#
|
113
|
-
def
|
156
|
+
def method_names
|
114
157
|
scope = self.scope
|
115
158
|
return [] unless scope.kind_of?(Module)
|
116
159
|
|
@@ -119,7 +162,7 @@ module Mutant
|
|
119
162
|
scope.private_instance_methods(false) +
|
120
163
|
scope.protected_instance_methods(false)
|
121
164
|
|
122
|
-
names.uniq.
|
165
|
+
names.uniq.sort
|
123
166
|
end
|
124
167
|
end
|
125
168
|
end
|
@@ -18,6 +18,26 @@ module Mutant
|
|
18
18
|
array = input.array
|
19
19
|
emit_attribute_mutations(:array)
|
20
20
|
end
|
21
|
+
|
22
|
+
# Test if node is new
|
23
|
+
#
|
24
|
+
# FIXME: Remove this hack and make sure empty bodies are not generated
|
25
|
+
#
|
26
|
+
# @param [Rubinius::AST::Node]
|
27
|
+
#
|
28
|
+
# @return [true]
|
29
|
+
# if node is new
|
30
|
+
#
|
31
|
+
# @return [false]
|
32
|
+
# otherwise
|
33
|
+
#
|
34
|
+
def new?(node)
|
35
|
+
if node.array.empty?
|
36
|
+
node.array << new_nil
|
37
|
+
end
|
38
|
+
|
39
|
+
super
|
40
|
+
end
|
21
41
|
end
|
22
42
|
end
|
23
43
|
end
|
@@ -16,8 +16,8 @@ module Mutant
|
|
16
16
|
#
|
17
17
|
def dispatch
|
18
18
|
emit_attribute_mutations(:condition)
|
19
|
-
emit_attribute_mutations(:body)
|
20
|
-
emit_attribute_mutations(:else) if node.else
|
19
|
+
emit_attribute_mutations(:body) if node.body.class != Rubinius::AST::NilLiteral
|
20
|
+
emit_attribute_mutations(:else) if node.else.class != Rubinius::AST::NilLiteral
|
21
21
|
emit_inverted_condition
|
22
22
|
emit_deleted_if_branch
|
23
23
|
emit_deleted_else_branch
|
@@ -11,7 +11,7 @@ module Mutant
|
|
11
11
|
|
12
12
|
# Return filename pattern
|
13
13
|
#
|
14
|
-
# @return [String]
|
14
|
+
# @return [Enumerable<String>]
|
15
15
|
#
|
16
16
|
# @api private
|
17
17
|
#
|
@@ -25,17 +25,17 @@ module Mutant
|
|
25
25
|
|
26
26
|
# Return file name pattern for mutation
|
27
27
|
#
|
28
|
-
# @return [
|
28
|
+
# @return [Enumerable<String>]
|
29
29
|
#
|
30
30
|
# @api private
|
31
31
|
#
|
32
32
|
def self.spec_files(mutation)
|
33
|
-
|
33
|
+
['spec/unit']
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
37
|
# Run all integration specs per mutation
|
38
|
-
class
|
38
|
+
class Integration < self
|
39
39
|
|
40
40
|
# Return file name pattern for mutation
|
41
41
|
#
|
@@ -63,13 +63,25 @@ module Mutant
|
|
63
63
|
# @api private
|
64
64
|
#
|
65
65
|
def spec_file
|
66
|
-
|
66
|
+
method_name.to_s.
|
67
|
+
gsub(/\A\[\]\z/, 'element_reader').
|
68
|
+
gsub(/\A\[\]=\z/, 'element_writer').
|
67
69
|
gsub(/\?\z/, '_predicate').
|
68
|
-
gsub(
|
70
|
+
gsub(/=\z/, '_writer').
|
69
71
|
gsub(/!\z/, '_bang') + '_spec.rb'
|
70
72
|
end
|
71
73
|
memoize :spec_file
|
72
74
|
|
75
|
+
# Return method name
|
76
|
+
#
|
77
|
+
# @return [Symbol]
|
78
|
+
#
|
79
|
+
# @api private
|
80
|
+
#
|
81
|
+
def method_name
|
82
|
+
matcher.method_name
|
83
|
+
end
|
84
|
+
|
73
85
|
# Return glob expression
|
74
86
|
#
|
75
87
|
# @return [String]
|
@@ -77,6 +89,8 @@ module Mutant
|
|
77
89
|
# @api private
|
78
90
|
#
|
79
91
|
def glob_expression
|
92
|
+
return base_path if method_name == :initialize
|
93
|
+
|
80
94
|
if mutation.subject.matcher.public?
|
81
95
|
"#{base_path}/#{spec_file}"
|
82
96
|
else
|
data/mutant.gemspec
CHANGED
@@ -15,25 +15,31 @@ shared_examples_for 'a mutator' do
|
|
15
15
|
|
16
16
|
it { should be_instance_of(to_enum.class) }
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
mutation
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
18
|
+
unless instance_methods.include?(:expected_mutations)
|
19
|
+
let(:expected_mutations) do
|
20
|
+
mutations.map do |mutation|
|
21
|
+
case mutation
|
22
|
+
when String
|
23
|
+
mutation.to_ast
|
24
|
+
when Rubinius::AST::Node
|
25
|
+
mutation
|
26
|
+
else
|
27
|
+
raise
|
28
|
+
end
|
29
|
+
end.map do |node|
|
30
|
+
ToSource.to_source(node)
|
31
|
+
end.to_set
|
32
|
+
end
|
26
33
|
end
|
27
34
|
|
28
35
|
it 'generates the expected mutations' do
|
29
|
-
generated = self.subject.map(
|
36
|
+
generated = self.subject.map { |mutation| ToSource.to_source(mutation) }.to_set
|
30
37
|
|
31
38
|
missing = (expected_mutations - generated).to_a
|
32
39
|
unexpected = (generated - expected_mutations).to_a
|
33
40
|
|
34
41
|
unless generated == expected_mutations
|
35
|
-
|
36
|
-
fail message
|
42
|
+
fail "Missing mutations:\n%s\nUnexpected mutations:\n%s" % [missing.join("\n----\n"), unexpected.join("\n----\n")]
|
37
43
|
end
|
38
44
|
end
|
39
45
|
end
|
@@ -3,25 +3,43 @@ require 'spec_helper'
|
|
3
3
|
describe Mutant::Matcher::Method::Classifier, '.run' do
|
4
4
|
subject { described_class.run(input) }
|
5
5
|
|
6
|
+
shared_examples_for 'Mutant::Matcher::Method::Classifier.run' do
|
7
|
+
before do
|
8
|
+
expected_class.stub(:new => response)
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:response) { :Response }
|
12
|
+
|
13
|
+
it { should be(response) }
|
14
|
+
|
15
|
+
it 'should initialize method filter with correct arguments' do
|
16
|
+
expected_class.should_receive(:new).with(TestApp::Literal, expected_method).and_return(response)
|
17
|
+
subject
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
6
21
|
context 'with explicit toplevel scope' do
|
7
|
-
let(:input)
|
8
|
-
let(:expected_class)
|
22
|
+
let(:input) { '::TestApp::Literal#string' }
|
23
|
+
let(:expected_class) { Mutant::Matcher::Method::Instance }
|
24
|
+
let(:expected_method) { TestApp::Literal.instance_method(:string) }
|
9
25
|
|
10
|
-
it_should_behave_like '
|
26
|
+
it_should_behave_like 'Mutant::Matcher::Method::Classifier.run'
|
11
27
|
end
|
12
28
|
|
13
29
|
context 'with instance method notation' do
|
14
|
-
let(:input)
|
15
|
-
let(:
|
30
|
+
let(:input) { 'TestApp::Literal#string' }
|
31
|
+
let(:expected_method) { TestApp::Literal.instance_method(:string) }
|
32
|
+
let(:expected_class) { Mutant::Matcher::Method::Instance }
|
16
33
|
|
17
|
-
it_should_behave_like '
|
34
|
+
it_should_behave_like 'Mutant::Matcher::Method::Classifier.run'
|
18
35
|
end
|
19
36
|
|
20
37
|
context 'with singleton method notation' do
|
21
|
-
let(:input)
|
22
|
-
let(:
|
38
|
+
let(:input) { 'TestApp::Literal.string' }
|
39
|
+
let(:expected_method) { TestApp::Literal.method(:string) }
|
40
|
+
let(:expected_class) { Mutant::Matcher::Method::Singleton }
|
23
41
|
|
24
|
-
it_should_behave_like '
|
42
|
+
it_should_behave_like 'Mutant::Matcher::Method::Classifier.run'
|
25
43
|
end
|
26
44
|
|
27
45
|
context 'with invalid notation' do
|
@@ -9,26 +9,27 @@ describe Mutant::Mutator, 'block' do
|
|
9
9
|
mutations = []
|
10
10
|
|
11
11
|
# Mutation of each statement in block
|
12
|
-
mutations << "foo\nself.bar"
|
13
|
-
mutations << "self.foo\nbar"
|
12
|
+
mutations << "foo\nself.bar".to_ast
|
13
|
+
mutations << "self.foo\nbar".to_ast
|
14
14
|
|
15
|
-
|
16
|
-
mutations <<
|
17
|
-
mutations <<
|
18
|
-
mutations << [
|
15
|
+
# Remove statement in block
|
16
|
+
mutations << Rubinius::AST::Block.new(1, ['self.foo'.to_ast])
|
17
|
+
mutations << Rubinius::AST::Block.new(1, ['self.bar'.to_ast])
|
18
|
+
mutations << Rubinius::AST::Block.new(1, ['nil'.to_ast])
|
19
19
|
end
|
20
20
|
|
21
21
|
it_should_behave_like 'a mutator'
|
22
22
|
end
|
23
23
|
|
24
24
|
|
25
|
+
|
25
26
|
context 'with one statement' do
|
26
27
|
let(:node) { Rubinius::AST::Block.new(1, ['self.foo'.to_ast]) }
|
27
28
|
|
28
29
|
let(:mutations) do
|
29
30
|
mutations = []
|
30
|
-
mutations <<
|
31
|
-
mutations << [
|
31
|
+
mutations << Rubinius::AST::Block.new(1, ['foo'.to_ast])
|
32
|
+
mutations << Rubinius::AST::Block.new(1, ['nil'.to_ast])
|
32
33
|
end
|
33
34
|
|
34
35
|
it_should_behave_like 'a mutator'
|
@@ -17,7 +17,7 @@ describe Mutant::Mutator, 'define' do
|
|
17
17
|
mutations << 'def foo; self.bar; end'
|
18
18
|
|
19
19
|
# Remove all statements
|
20
|
-
mutations <<
|
20
|
+
mutations << 'def foo; end'
|
21
21
|
end
|
22
22
|
|
23
23
|
it_should_behave_like 'a mutator'
|
@@ -38,7 +38,7 @@ describe Mutant::Mutator, 'define' do
|
|
38
38
|
mutations << 'def self.foo; self.baz; end'
|
39
39
|
|
40
40
|
# Remove all statements
|
41
|
-
mutations <<
|
41
|
+
mutations << 'def self.foo; end'
|
42
42
|
end
|
43
43
|
|
44
44
|
it_should_behave_like 'a mutator'
|
@@ -1,30 +1,60 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Mutant::Mutator, 'if statement' do
|
4
|
-
let(:source) { 'if self.condition; true; else false; end' }
|
5
4
|
|
6
|
-
|
7
|
-
|
5
|
+
context 'if with two branches' do
|
6
|
+
let(:source) { 'if self.condition; true; else false; end' }
|
8
7
|
|
9
|
-
|
10
|
-
|
8
|
+
let(:mutations) do
|
9
|
+
mutants = []
|
11
10
|
|
12
|
-
|
11
|
+
# mutations of condition
|
12
|
+
mutants << 'if condition; true; else false; end'
|
13
13
|
|
14
|
-
|
15
|
-
mutants << 'if self.condition; true end'
|
14
|
+
mutants << 'if !self.condition; true; else false; end'
|
16
15
|
|
17
|
-
|
18
|
-
|
16
|
+
# Deleted else branch
|
17
|
+
mutants << 'if self.condition; true end'
|
19
18
|
|
20
|
-
|
21
|
-
|
22
|
-
mutants << 'if self.condition; nil; else false; end'
|
19
|
+
# Deleted if branch with promoting else branch to if branch
|
20
|
+
mutants << 'if self.condition; false end'
|
23
21
|
|
24
|
-
|
25
|
-
|
26
|
-
|
22
|
+
# mutations of body
|
23
|
+
mutants << 'if self.condition; false; else false; end'
|
24
|
+
mutants << 'if self.condition; nil; else false; end'
|
25
|
+
|
26
|
+
# mutations of else body
|
27
|
+
mutants << 'if self.condition; true; else true; end'
|
28
|
+
mutants << 'if self.condition; true; else nil; end'
|
29
|
+
end
|
30
|
+
|
31
|
+
it_should_behave_like 'a mutator'
|
27
32
|
end
|
28
33
|
|
29
|
-
|
34
|
+
context 'unless with one branch' do
|
35
|
+
let(:source) { 'unless condition; true; end' }
|
36
|
+
|
37
|
+
let(:mutations) do
|
38
|
+
mutants = []
|
39
|
+
mutants << 'unless !condition; true; end'
|
40
|
+
mutants << 'if condition; true; end'
|
41
|
+
mutants << 'unless condition; false; end'
|
42
|
+
mutants << 'unless condition; nil; end'
|
43
|
+
end
|
44
|
+
|
45
|
+
it_should_behave_like 'a mutator'
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'if with one branch' do
|
49
|
+
let(:source) { 'if condition; true; end' }
|
50
|
+
|
51
|
+
let(:mutations) do
|
52
|
+
mutants = []
|
53
|
+
mutants << 'if !condition; true; end'
|
54
|
+
mutants << 'if condition; false; end'
|
55
|
+
mutants << 'if condition; nil; end'
|
56
|
+
end
|
57
|
+
|
58
|
+
it_should_behave_like 'a mutator'
|
59
|
+
end
|
30
60
|
end
|
@@ -11,8 +11,8 @@ describe Mutant::Mutator::Node::Literal, 'float' do
|
|
11
11
|
mutations << random_float.to_s
|
12
12
|
mutations << '0.0/0.0'
|
13
13
|
mutations << '1.0/0.0'
|
14
|
-
mutations <<
|
15
|
-
mutations <<
|
14
|
+
mutations << '-1.0 / 0.0'
|
15
|
+
mutations << '-10.0'
|
16
16
|
end
|
17
17
|
|
18
18
|
let(:random_float) { 7.123 }
|
@@ -7,27 +7,27 @@ describe Mutant::Mutator::Node::Literal, 'hash' do
|
|
7
7
|
mutations = []
|
8
8
|
|
9
9
|
# Literal replaced with nil
|
10
|
-
mutations <<
|
10
|
+
mutations << 'nil'
|
11
11
|
|
12
12
|
# Mutation of each key and value in hash
|
13
|
-
mutations <<
|
14
|
-
mutations <<
|
15
|
-
mutations <<
|
16
|
-
mutations <<
|
17
|
-
mutations <<
|
18
|
-
mutations <<
|
19
|
-
mutations <<
|
20
|
-
mutations <<
|
13
|
+
mutations << '{ false => true , false => false }'
|
14
|
+
mutations << '{ nil => true , false => false }'
|
15
|
+
mutations << '{ true => false , false => false }'
|
16
|
+
mutations << '{ true => nil , false => false }'
|
17
|
+
mutations << '{ true => true , true => false }'
|
18
|
+
mutations << '{ true => true , nil => false }'
|
19
|
+
mutations << '{ true => true , false => true }'
|
20
|
+
mutations << '{ true => true , false => nil }'
|
21
21
|
|
22
22
|
# Remove each key once
|
23
|
-
mutations <<
|
24
|
-
mutations <<
|
23
|
+
mutations << '{ true => true }'
|
24
|
+
mutations << '{ false => false }'
|
25
25
|
|
26
26
|
# Empty hash
|
27
|
-
mutations <<
|
27
|
+
mutations << '{}'
|
28
28
|
|
29
29
|
# Extra element
|
30
|
-
mutations <<
|
30
|
+
mutations << '{true => true, false => false, nil => nil}'
|
31
31
|
end
|
32
32
|
|
33
33
|
it_should_behave_like 'a mutator'
|
@@ -9,7 +9,7 @@ describe Mutant::Mutator::Node::Literal, 'range' do
|
|
9
9
|
mutations << 'nil'
|
10
10
|
mutations << '1...100'
|
11
11
|
mutations << '(0.0/0.0)..100'
|
12
|
-
mutations << [:dot2, [:negate, [:call, [:lit, 1.0], :/, [:arglist, [:lit, 0.0]]]], [:lit, 100]]
|
12
|
+
#mutations << [:dot2, [:negate, [:call, [:lit, 1.0], :/, [:arglist, [:lit, 0.0]]]], [:lit, 100]]
|
13
13
|
mutations << '1..(1.0/0.0)'
|
14
14
|
mutations << '1..(0.0/0.0)'
|
15
15
|
end
|
@@ -25,7 +25,7 @@ describe Mutant::Mutator::Node::Literal, 'range' do
|
|
25
25
|
mutations << 'nil'
|
26
26
|
mutations << '1..100'
|
27
27
|
mutations << '(0.0/0.0)...100'
|
28
|
-
mutations << [:dot3, [:negate, [:call, [:lit, 1.0], :/, [:arglist, [:lit, 0.0]]]], [:lit, 100]]
|
28
|
+
#mutations << [:dot3, [:negate, [:call, [:lit, 1.0], :/, [:arglist, [:lit, 0.0]]]], [:lit, 100]]
|
29
29
|
mutations << '1...(1.0/0.0)'
|
30
30
|
mutations << '1...(0.0/0.0)'
|
31
31
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Mutant::Strategy::Rspec::ExampleLookup, '#spec_file' do
|
4
|
+
|
5
|
+
let(:object) { described_class.new(mutation) }
|
6
|
+
let(:mutation) { mock('Mutation', :subject => mutation_subject) }
|
7
|
+
let(:mutation_subject) { mock('Subject', :matcher => matcher) }
|
8
|
+
let(:matcher) { mock('Matcher', :method_name => method_name) }
|
9
|
+
|
10
|
+
subject { object.send(:spec_file) }
|
11
|
+
|
12
|
+
shared_examples_for 'Mutant::Strategy::Rspec::ExampleLookup#spec_file' do
|
13
|
+
it_should_behave_like 'an idempotent method'
|
14
|
+
|
15
|
+
it { should eql(expected_spec_file) }
|
16
|
+
it { should be_frozen }
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'with element reader method' do
|
20
|
+
let(:method_name) { '[]' }
|
21
|
+
let(:expected_spec_file) { 'element_reader_spec.rb' }
|
22
|
+
|
23
|
+
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'with element writer method' do
|
27
|
+
let(:method_name) { '[]=' }
|
28
|
+
|
29
|
+
let(:expected_spec_file) { 'element_writer_spec.rb' }
|
30
|
+
|
31
|
+
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'with writer method' do
|
35
|
+
let(:method_name) { 'foo=' }
|
36
|
+
let(:expected_spec_file) { 'foo_writer_spec.rb' }
|
37
|
+
|
38
|
+
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'with bang method' do
|
42
|
+
let(:method_name) { 'foo!' }
|
43
|
+
let(:expected_spec_file) { 'foo_bang_spec.rb' }
|
44
|
+
|
45
|
+
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'with predicate method' do
|
49
|
+
let(:method_name) { 'foo?' }
|
50
|
+
let(:expected_spec_file) { 'foo_predicate_spec.rb' }
|
51
|
+
|
52
|
+
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'with regular method' do
|
56
|
+
let(:method_name) { 'foo' }
|
57
|
+
let(:expected_spec_file) { 'foo_spec.rb' }
|
58
|
+
|
59
|
+
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
|
60
|
+
end
|
61
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mutant
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -245,7 +245,6 @@ files:
|
|
245
245
|
- spec/shared/hash_method_behavior.rb
|
246
246
|
- spec/shared/idempotent_method_behavior.rb
|
247
247
|
- spec/shared/invertible_method_behaviour.rb
|
248
|
-
- spec/shared/method_filter_parse_behavior.rb
|
249
248
|
- spec/shared/method_match_behavior.rb
|
250
249
|
- spec/shared/mutator_behavior.rb
|
251
250
|
- spec/spec_helper.rb
|
@@ -270,7 +269,6 @@ files:
|
|
270
269
|
- spec/unit/mutant/matcher/each_spec.rb
|
271
270
|
- spec/unit/mutant/matcher/method/class_methods/parse_spec.rb
|
272
271
|
- spec/unit/mutant/matcher/method/classifier/class_methods/run_spec.rb
|
273
|
-
- spec/unit/mutant/matcher/method/method_spec.rb
|
274
272
|
- spec/unit/mutant/matcher/object_space/class_methods/parse_spec.rb
|
275
273
|
- spec/unit/mutant/matcher/object_space/each_spec.rb
|
276
274
|
- spec/unit/mutant/mutator/each_spec.rb
|
@@ -294,6 +292,7 @@ files:
|
|
294
292
|
- spec/unit/mutant/mutator/node/return/mutation_spec.rb
|
295
293
|
- spec/unit/mutant/mutator/node/send/mutation_spec.rb
|
296
294
|
- spec/unit/mutant/mutator/self_spec.rb
|
295
|
+
- spec/unit/mutant/strategy/rspec/example_lookup/spec_file_spec.rb
|
297
296
|
- spec/unit/mutant/subject/class_methods/new_spec.rb
|
298
297
|
- spec/unit/mutant/subject/context_spec.rb
|
299
298
|
- spec/unit/mutant/subject/each_spec.rb
|
@@ -359,7 +358,6 @@ test_files:
|
|
359
358
|
- spec/shared/hash_method_behavior.rb
|
360
359
|
- spec/shared/idempotent_method_behavior.rb
|
361
360
|
- spec/shared/invertible_method_behaviour.rb
|
362
|
-
- spec/shared/method_filter_parse_behavior.rb
|
363
361
|
- spec/shared/method_match_behavior.rb
|
364
362
|
- spec/shared/mutator_behavior.rb
|
365
363
|
- spec/spec_helper.rb
|
@@ -384,7 +382,6 @@ test_files:
|
|
384
382
|
- spec/unit/mutant/matcher/each_spec.rb
|
385
383
|
- spec/unit/mutant/matcher/method/class_methods/parse_spec.rb
|
386
384
|
- spec/unit/mutant/matcher/method/classifier/class_methods/run_spec.rb
|
387
|
-
- spec/unit/mutant/matcher/method/method_spec.rb
|
388
385
|
- spec/unit/mutant/matcher/object_space/class_methods/parse_spec.rb
|
389
386
|
- spec/unit/mutant/matcher/object_space/each_spec.rb
|
390
387
|
- spec/unit/mutant/mutator/each_spec.rb
|
@@ -408,6 +405,7 @@ test_files:
|
|
408
405
|
- spec/unit/mutant/mutator/node/return/mutation_spec.rb
|
409
406
|
- spec/unit/mutant/mutator/node/send/mutation_spec.rb
|
410
407
|
- spec/unit/mutant/mutator/self_spec.rb
|
408
|
+
- spec/unit/mutant/strategy/rspec/example_lookup/spec_file_spec.rb
|
411
409
|
- spec/unit/mutant/subject/class_methods/new_spec.rb
|
412
410
|
- spec/unit/mutant/subject/context_spec.rb
|
413
411
|
- spec/unit/mutant/subject/each_spec.rb
|
@@ -1,16 +0,0 @@
|
|
1
|
-
shared_examples_for 'a method filter parse result' do
|
2
|
-
before do
|
3
|
-
expected_class.stub(:new => response)
|
4
|
-
end
|
5
|
-
|
6
|
-
let(:response) { mock('Response') }
|
7
|
-
|
8
|
-
it { should be(response) }
|
9
|
-
|
10
|
-
it 'should initialize method filter with correct arguments' do
|
11
|
-
expected_class.should_receive(:new).with(TestApp::Literal, :string).and_return(response)
|
12
|
-
subject
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
|
@@ -1,11 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Mutant::Matcher::Method, '#method' do
|
4
|
-
subject { object.send(:method) }
|
5
|
-
|
6
|
-
let(:object) { described_class.allocate }
|
7
|
-
|
8
|
-
it 'should raise error' do
|
9
|
-
expect { subject }.to raise_error(NotImplementedError, 'Mutant::Matcher::Method#method is not implemented')
|
10
|
-
end
|
11
|
-
end
|