mutant 0.3.6 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/Changelog.md +13 -0
- data/Gemfile +1 -1
- data/Guardfile +7 -25
- data/LICENSE +1 -1
- data/README.md +16 -10
- data/config/flay.yml +1 -1
- data/config/reek.yml +11 -11
- data/config/rubocop.yml +1 -1
- data/lib/mutant.rb +3 -10
- data/lib/mutant/cli.rb +199 -80
- data/lib/mutant/config.rb +3 -3
- data/lib/mutant/killer.rb +20 -0
- data/lib/mutant/matcher/filter.rb +3 -8
- data/lib/mutant/matcher/method/instance.rb +1 -1
- data/lib/mutant/matcher/namespace.rb +31 -2
- data/lib/mutant/matcher/null.rb +26 -0
- data/lib/mutant/mutation.rb +0 -1
- data/lib/mutant/reporter/cli/printer.rb +29 -0
- data/lib/mutant/reporter/cli/printer/config.rb +16 -116
- data/lib/mutant/runner/config.rb +47 -1
- data/lib/mutant/strategy.rb +39 -1
- data/lib/mutant/version.rb +1 -1
- data/lib/mutant/walker.rb +51 -0
- data/mutant-rspec.gemspec +24 -0
- data/mutant.gemspec +7 -3
- data/spec/integration/mutant/rspec_spec.rb +11 -6
- data/spec/integration/mutant/zombie_spec.rb +2 -2
- data/spec/shared/method_matcher_behavior.rb +6 -6
- data/spec/spec_helper.rb +2 -2
- data/spec/unit/mutant/cli_new_spec.rb +49 -34
- data/spec/unit/mutant/context/scope/root_spec.rb +1 -1
- data/spec/unit/mutant/loader/eval_spec.rb +2 -2
- data/spec/unit/mutant/matcher/chain_spec.rb +1 -1
- data/spec/unit/mutant/matcher/methods/instance_spec.rb +1 -1
- data/spec/unit/mutant/matcher/methods/singleton_spec.rb +1 -1
- data/spec/unit/mutant/matcher/namespace_spec.rb +1 -1
- data/spec/unit/mutant/mutation_spec.rb +1 -1
- data/spec/unit/mutant/{killer/rspec_spec.rb → rspec/killer_spec.rb} +2 -1
- data/spec/unit/mutant/runner/config_spec.rb +36 -21
- data/spec/unit/mutant_spec.rb +7 -9
- metadata +25 -22
- data/lib/mutant/cli/builder.rb +0 -167
- data/lib/mutant/killer/rspec.rb +0 -95
- data/lib/mutant/predicate.rb +0 -70
- data/lib/mutant/predicate/attribute.rb +0 -68
- data/lib/mutant/predicate/blacklist.rb +0 -27
- data/lib/mutant/predicate/matcher.rb +0 -38
- data/lib/mutant/predicate/whitelist.rb +0 -28
- data/lib/mutant/strategy/rspec.rb +0 -76
- data/spec/unit/mutant/cli/builder/rspec_spec.rb +0 -38
- data/spec/unit/mutant/matcher/filter_spec.rb +0 -19
- data/spec/unit/mutant/predicate_spec.rb +0 -135
data/lib/mutant/strategy.rb
CHANGED
@@ -4,7 +4,35 @@ module Mutant
|
|
4
4
|
|
5
5
|
# Abstract base class for killing strategies
|
6
6
|
class Strategy
|
7
|
-
include AbstractType, Adamantium::Flat
|
7
|
+
include AbstractType, Adamantium::Flat, Equalizer.new
|
8
|
+
|
9
|
+
REGISTRY = {}
|
10
|
+
|
11
|
+
# Lookup strategy for name
|
12
|
+
#
|
13
|
+
# @param [String] name
|
14
|
+
#
|
15
|
+
# @return [Strategy]
|
16
|
+
# if found
|
17
|
+
#
|
18
|
+
# @api private
|
19
|
+
#
|
20
|
+
def self.lookup(name)
|
21
|
+
REGISTRY.fetch(name)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Register strategy
|
25
|
+
#
|
26
|
+
# @param [String] name
|
27
|
+
#
|
28
|
+
# @return [undefined]
|
29
|
+
#
|
30
|
+
# @api private
|
31
|
+
#
|
32
|
+
def self.register(name)
|
33
|
+
REGISTRY[name] = self
|
34
|
+
end
|
35
|
+
private_class_method :register
|
8
36
|
|
9
37
|
# Perform strategy setup
|
10
38
|
#
|
@@ -50,5 +78,15 @@ module Mutant
|
|
50
78
|
self.class::KILLER
|
51
79
|
end
|
52
80
|
|
81
|
+
# Null strategy that never kills a mutation
|
82
|
+
class Null < self
|
83
|
+
|
84
|
+
register 'null'
|
85
|
+
|
86
|
+
KILLER = Killer::Null
|
87
|
+
|
88
|
+
end # Null
|
89
|
+
|
53
90
|
end # Strategy
|
91
|
+
|
54
92
|
end # Mutant
|
data/lib/mutant/version.rb
CHANGED
@@ -0,0 +1,51 @@
|
|
1
|
+
module Mutant
|
2
|
+
|
3
|
+
# Walker for all ast nodes
|
4
|
+
class Walker
|
5
|
+
|
6
|
+
# Run walkter
|
7
|
+
#
|
8
|
+
# @param [Parser::AST::Node] root
|
9
|
+
#
|
10
|
+
# @return [self]
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
#
|
14
|
+
def self.run(root, &block)
|
15
|
+
new(root, block)
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
private_class_method :new
|
20
|
+
|
21
|
+
# Initialize and run walker
|
22
|
+
#
|
23
|
+
# @param [Parser::AST::Node] root
|
24
|
+
# @param [#call(node)] block
|
25
|
+
#
|
26
|
+
# @return [undefined]
|
27
|
+
#
|
28
|
+
# @api private
|
29
|
+
#
|
30
|
+
def initialize(root, block)
|
31
|
+
@root, @block = root, block
|
32
|
+
dispatch(root)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# Perform dispatch
|
38
|
+
#
|
39
|
+
# @param [Parser::AST::Node] node
|
40
|
+
#
|
41
|
+
# @return [undefined]
|
42
|
+
#
|
43
|
+
# @api private
|
44
|
+
#
|
45
|
+
def dispatch(node)
|
46
|
+
@block.call(node)
|
47
|
+
node.children.grep(Parser::AST::Node).each(&method(:dispatch))
|
48
|
+
end
|
49
|
+
end # Walker
|
50
|
+
|
51
|
+
end # Mutant
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
require File.expand_path('../lib/mutant/version', __FILE__)
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.name = 'mutant-rspec'
|
7
|
+
gem.version = Mutant::VERSION.dup
|
8
|
+
gem.authors = ['Markus Schirp']
|
9
|
+
gem.email = ['mbj@schirp-dso.com']
|
10
|
+
gem.description = 'Rspec integration for mutant'
|
11
|
+
gem.summary = gem.description
|
12
|
+
gem.homepage = 'https://github.com/mbj/mutant'
|
13
|
+
gem.license = 'MIT'
|
14
|
+
|
15
|
+
gem.require_paths = %w[lib]
|
16
|
+
gem.files = `git ls-files -- lib/mutant{-,/}rspec.rb lib/mutant/rspec`.split("\n")
|
17
|
+
gem.test_files = `git ls-files -- spec/{unit/mutant/rspec,integration/rspec}`.split("\n")
|
18
|
+
gem.extra_rdoc_files = %w[TODO LICENSE]
|
19
|
+
|
20
|
+
gem.add_runtime_dependency('mutant', "~> #{gem.version}")
|
21
|
+
gem.add_runtime_dependency('rspec-core', '~> 2.14.1')
|
22
|
+
|
23
|
+
gem.add_development_dependency('bundler', '~> 1.3', '>= 1.3.5')
|
24
|
+
end
|
data/mutant.gemspec
CHANGED
@@ -13,7 +13,10 @@ Gem::Specification.new do |gem|
|
|
13
13
|
gem.license = 'MIT'
|
14
14
|
|
15
15
|
gem.require_paths = %w[lib]
|
16
|
-
|
16
|
+
|
17
|
+
mutant_rspec_files = `git ls-files -- lib/mutant{-,/}rspec.rb lib/mutant/rspec`.split("\n")
|
18
|
+
|
19
|
+
gem.files = `git ls-files`.split("\n") - mutant_rspec_files
|
17
20
|
gem.test_files = `git ls-files -- spec/{unit,integration}`.split("\n")
|
18
21
|
gem.extra_rdoc_files = %w[TODO LICENSE]
|
19
22
|
gem.executables = %w[mutant]
|
@@ -21,12 +24,13 @@ Gem::Specification.new do |gem|
|
|
21
24
|
gem.required_ruby_version = '>= 1.9.3'
|
22
25
|
|
23
26
|
gem.add_runtime_dependency('parser', '~> 2.1.4')
|
27
|
+
gem.add_runtime_dependency('morpher', '~> 0.0.1')
|
24
28
|
gem.add_runtime_dependency('procto', '~> 0.0.2')
|
25
29
|
gem.add_runtime_dependency('abstract_type', '~> 0.0.7')
|
26
30
|
gem.add_runtime_dependency('unparser', '~> 0.1.8')
|
27
|
-
gem.add_runtime_dependency('ice_nine', '~> 0.
|
31
|
+
gem.add_runtime_dependency('ice_nine', '~> 0.11.0')
|
28
32
|
gem.add_runtime_dependency('descendants_tracker', '~> 0.0.1')
|
29
|
-
gem.add_runtime_dependency('adamantium', '~> 0.
|
33
|
+
gem.add_runtime_dependency('adamantium', '~> 0.2.0')
|
30
34
|
gem.add_runtime_dependency('equalizer', '~> 0.0.7')
|
31
35
|
gem.add_runtime_dependency('inflecto', '~> 0.0.2')
|
32
36
|
gem.add_runtime_dependency('anima', '~> 0.2.0')
|
@@ -10,24 +10,29 @@ describe Mutant, 'rspec integration' do
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
let(:base_cmd) { 'bundle exec mutant -I lib --require test_app --rspec' }
|
13
|
+
let(:base_cmd) { 'bundle exec mutant -I lib --require test_app --use rspec' }
|
14
14
|
|
15
15
|
specify 'it allows to kill mutations' do
|
16
|
-
Kernel.system("#{base_cmd} ::TestApp::Literal#string").
|
16
|
+
expect(Kernel.system("#{base_cmd} ::TestApp::Literal#string")).to be(true)
|
17
17
|
end
|
18
18
|
|
19
19
|
specify 'it allows to exclude mutations' do
|
20
|
-
cli = "
|
21
|
-
|
20
|
+
cli = <<-CMD.split("\n").join(' ')
|
21
|
+
#{base_cmd}
|
22
|
+
::TestApp::Literal#string
|
23
|
+
::TestApp::Literal#uncovered_string
|
24
|
+
--ignore-subject ::TestApp::Literal#uncovered_string
|
25
|
+
CMD
|
26
|
+
expect(Kernel.system(cli)).to be(true)
|
22
27
|
end
|
23
28
|
|
24
29
|
specify 'fails to kill mutations when they are not covered' do
|
25
30
|
cli = "#{base_cmd} ::TestApp::Literal#uncovered_string"
|
26
|
-
Kernel.system(cli).
|
31
|
+
expect(Kernel.system(cli)).to be(false)
|
27
32
|
end
|
28
33
|
|
29
34
|
specify 'fails when some mutations are not covered' do
|
30
35
|
cli = "#{base_cmd} ::TestApp::Literal"
|
31
|
-
Kernel.system(cli).
|
36
|
+
expect(Kernel.system(cli)).to be(false)
|
32
37
|
end
|
33
38
|
end
|
@@ -3,8 +3,8 @@
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
5
|
describe Mutant, 'as a zombie' do
|
6
|
-
|
6
|
+
pending 'it allows to create zombie from mutant' do
|
7
7
|
Mutant::Zombifier.run('mutant')
|
8
|
-
Zombie.constants.
|
8
|
+
expect(Zombie.constants).to include(:Mutant)
|
9
9
|
end
|
10
10
|
end
|
@@ -9,28 +9,28 @@ shared_examples_for 'a method matcher' do
|
|
9
9
|
let(:mutation_subject) { yields.first }
|
10
10
|
|
11
11
|
it 'should return one subject' do
|
12
|
-
yields.size.
|
12
|
+
expect(yields.size).to be(1)
|
13
13
|
end
|
14
14
|
|
15
15
|
it_should_behave_like 'an #each method'
|
16
16
|
|
17
17
|
it 'should have correct method name' do
|
18
|
-
name.
|
18
|
+
expect(name).to eql(method_name)
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'should have correct line number' do
|
22
|
-
(node.location.expression.line - base).
|
22
|
+
expect(node.location.expression.line - base).to eql(method_line)
|
23
23
|
end
|
24
24
|
|
25
25
|
it 'should have correct arity' do
|
26
|
-
arguments.children.length.
|
26
|
+
expect(arguments.children.length).to eql(method_arity)
|
27
27
|
end
|
28
28
|
|
29
29
|
it 'should have correct scope in context' do
|
30
|
-
context.
|
30
|
+
expect(context.scope).to eql(scope)
|
31
31
|
end
|
32
32
|
|
33
33
|
it 'should have the correct node type' do
|
34
|
-
node.type.
|
34
|
+
expect(node.type).to be(type)
|
35
35
|
end
|
36
36
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -17,7 +17,7 @@ if ENV['COVERAGE'] == 'true'
|
|
17
17
|
add_filter 'vendor'
|
18
18
|
add_filter 'test_app'
|
19
19
|
|
20
|
-
minimum_coverage
|
20
|
+
minimum_coverage 89.79 # TODO: raise this to 100, then mutation test
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
@@ -48,6 +48,6 @@ RSpec.configure do |config|
|
|
48
48
|
config.include(ParserHelper)
|
49
49
|
config.include(Mutant::NodeHelpers)
|
50
50
|
config.expect_with :rspec do |rspec|
|
51
|
-
rspec.syntax =
|
51
|
+
rspec.syntax = :expect
|
52
52
|
end
|
53
53
|
end
|
@@ -27,8 +27,8 @@ describe Mutant::CLI, '.new' do
|
|
27
27
|
end
|
28
28
|
|
29
29
|
# Defaults
|
30
|
-
let(:expected_filter) {
|
31
|
-
let(:expected_strategy) { Mutant::Strategy::
|
30
|
+
let(:expected_filter) { Morpher.evaluator(s(:true)) }
|
31
|
+
let(:expected_strategy) { Mutant::Strategy::Null.new }
|
32
32
|
let(:expected_reporter) { Mutant::Reporter::CLI.new($stdout) }
|
33
33
|
|
34
34
|
let(:ns) { Mutant::Matcher }
|
@@ -54,80 +54,95 @@ describe Mutant::CLI, '.new' do
|
|
54
54
|
it_should_behave_like 'an invalid cli run'
|
55
55
|
end
|
56
56
|
|
57
|
-
context 'with many strategy flags' do
|
58
|
-
let(:arguments) { %w(--rspec --rspec TestApp) }
|
59
|
-
let(:expected_matcher) { Mutant::Matcher::Scope.new(cache, TestApp) }
|
60
|
-
|
61
|
-
it_should_behave_like 'a cli parser'
|
62
|
-
end
|
63
|
-
|
64
57
|
context 'without arguments' do
|
65
58
|
let(:arguments) { [] }
|
66
59
|
|
67
|
-
let(:expected_message) { 'No
|
60
|
+
let(:expected_message) { 'No patterns given' }
|
68
61
|
|
69
62
|
it_should_behave_like 'an invalid cli run'
|
70
63
|
end
|
71
64
|
|
72
65
|
context 'with code filter and missing argument' do
|
73
|
-
let(:arguments) { %w(--
|
66
|
+
let(:arguments) { %w(--code) }
|
74
67
|
let(:expected_message) { 'missing argument: --code' }
|
75
68
|
|
76
69
|
it_should_behave_like 'an invalid cli run'
|
77
70
|
end
|
78
71
|
|
79
|
-
context 'with explicit method
|
80
|
-
let(:arguments) { %w(
|
81
|
-
|
72
|
+
context 'with explicit method pattern' do
|
73
|
+
let(:arguments) { %w(TestApp::Literal#float) }
|
74
|
+
|
75
|
+
let(:expected_matcher) do
|
76
|
+
ns::Method::Instance.new(cache, TestApp::Literal, TestApp::Literal.instance_method(:float))
|
77
|
+
end
|
82
78
|
|
83
79
|
it_should_behave_like 'a cli parser'
|
84
80
|
end
|
85
81
|
|
86
82
|
context 'with debug flag' do
|
87
|
-
let(:
|
88
|
-
let(:arguments) { %W(--debug
|
83
|
+
let(:pattern) { '::TestApp*' }
|
84
|
+
let(:arguments) { %W(--debug #{pattern}) }
|
89
85
|
let(:expected_matcher) { ns::Namespace.new(cache, TestApp) }
|
90
86
|
|
91
87
|
it_should_behave_like 'a cli parser'
|
92
88
|
|
93
89
|
it 'should set the debug option' do
|
94
|
-
subject.config.debug.
|
90
|
+
expect(subject.config.debug).to be(true)
|
95
91
|
end
|
96
92
|
end
|
97
93
|
|
98
94
|
context 'with zombie flag' do
|
99
|
-
let(:
|
100
|
-
let(:arguments) { %W(--zombie
|
95
|
+
let(:pattern) { '::TestApp*' }
|
96
|
+
let(:arguments) { %W(--zombie #{pattern}) }
|
101
97
|
let(:expected_matcher) { ns::Namespace.new(cache, TestApp) }
|
102
98
|
|
103
99
|
it_should_behave_like 'a cli parser'
|
104
100
|
|
105
101
|
it 'should set the zombie option' do
|
106
|
-
subject.config.zombie.
|
102
|
+
expect(subject.config.zombie).to be(true)
|
107
103
|
end
|
108
104
|
end
|
109
105
|
|
110
|
-
context 'with namespace
|
111
|
-
let(:
|
112
|
-
let(:arguments) { %W(
|
106
|
+
context 'with namespace pattern' do
|
107
|
+
let(:pattern) { '::TestApp*' }
|
108
|
+
let(:arguments) { %W(#{pattern}) }
|
113
109
|
let(:expected_matcher) { ns::Namespace.new(cache, TestApp) }
|
114
110
|
|
115
111
|
it_should_behave_like 'a cli parser'
|
116
112
|
end
|
117
113
|
|
118
|
-
context 'with code filter' do
|
119
|
-
let(:
|
120
|
-
let(:arguments) { %W(--
|
121
|
-
|
122
|
-
let(:
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
114
|
+
context 'with subject code filter' do
|
115
|
+
let(:pattern) { 'TestApp::Literal#float' }
|
116
|
+
let(:arguments) { %W(--code faa --code bbb #{pattern}) }
|
117
|
+
|
118
|
+
let(:expected_filter) do
|
119
|
+
Morpher.evaluator(
|
120
|
+
s(:mxor,
|
121
|
+
s(:eql, s(:attribute, :code), s(:value, 'faa')),
|
122
|
+
s(:eql, s(:attribute, :code), s(:value, 'bbb'))
|
123
|
+
)
|
124
|
+
)
|
127
125
|
end
|
128
126
|
|
129
|
-
let(:expected_matcher)
|
130
|
-
|
127
|
+
let(:expected_matcher) do
|
128
|
+
matcher = ns::Method::Instance.new(
|
129
|
+
cache,
|
130
|
+
TestApp::Literal, TestApp::Literal.instance_method(:float)
|
131
|
+
)
|
132
|
+
predicate = Morpher.evaluator(
|
133
|
+
s(:or,
|
134
|
+
s(:eql,
|
135
|
+
s(:attribute, :code),
|
136
|
+
s(:static, 'faa')
|
137
|
+
),
|
138
|
+
s(:eql,
|
139
|
+
s(:attribute, :code),
|
140
|
+
s(:static, 'bbb')
|
141
|
+
)
|
142
|
+
)
|
143
|
+
)
|
144
|
+
ns::Filter.new(matcher, predicate)
|
145
|
+
end
|
131
146
|
|
132
147
|
it_should_behave_like 'a cli parser'
|
133
148
|
end
|
@@ -41,8 +41,8 @@ describe Mutant::Loader::Eval, '.call' do
|
|
41
41
|
|
42
42
|
it 'should set file and line correctly' do
|
43
43
|
subject
|
44
|
-
::SomeNamespace::Bar
|
44
|
+
expect(::SomeNamespace::Bar
|
45
45
|
.instance_method(:some_method)
|
46
|
-
.source_location.
|
46
|
+
.source_location).to eql([__FILE__, 3])
|
47
47
|
end
|
48
48
|
end
|