mutant 0.3.6 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +1 -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
|