mutant 0.3.6 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -0
  3. data/Changelog.md +13 -0
  4. data/Gemfile +1 -1
  5. data/Guardfile +7 -25
  6. data/LICENSE +1 -1
  7. data/README.md +16 -10
  8. data/config/flay.yml +1 -1
  9. data/config/reek.yml +11 -11
  10. data/config/rubocop.yml +1 -1
  11. data/lib/mutant.rb +3 -10
  12. data/lib/mutant/cli.rb +199 -80
  13. data/lib/mutant/config.rb +3 -3
  14. data/lib/mutant/killer.rb +20 -0
  15. data/lib/mutant/matcher/filter.rb +3 -8
  16. data/lib/mutant/matcher/method/instance.rb +1 -1
  17. data/lib/mutant/matcher/namespace.rb +31 -2
  18. data/lib/mutant/matcher/null.rb +26 -0
  19. data/lib/mutant/mutation.rb +0 -1
  20. data/lib/mutant/reporter/cli/printer.rb +29 -0
  21. data/lib/mutant/reporter/cli/printer/config.rb +16 -116
  22. data/lib/mutant/runner/config.rb +47 -1
  23. data/lib/mutant/strategy.rb +39 -1
  24. data/lib/mutant/version.rb +1 -1
  25. data/lib/mutant/walker.rb +51 -0
  26. data/mutant-rspec.gemspec +24 -0
  27. data/mutant.gemspec +7 -3
  28. data/spec/integration/mutant/rspec_spec.rb +11 -6
  29. data/spec/integration/mutant/zombie_spec.rb +2 -2
  30. data/spec/shared/method_matcher_behavior.rb +6 -6
  31. data/spec/spec_helper.rb +2 -2
  32. data/spec/unit/mutant/cli_new_spec.rb +49 -34
  33. data/spec/unit/mutant/context/scope/root_spec.rb +1 -1
  34. data/spec/unit/mutant/loader/eval_spec.rb +2 -2
  35. data/spec/unit/mutant/matcher/chain_spec.rb +1 -1
  36. data/spec/unit/mutant/matcher/methods/instance_spec.rb +1 -1
  37. data/spec/unit/mutant/matcher/methods/singleton_spec.rb +1 -1
  38. data/spec/unit/mutant/matcher/namespace_spec.rb +1 -1
  39. data/spec/unit/mutant/mutation_spec.rb +1 -1
  40. data/spec/unit/mutant/{killer/rspec_spec.rb → rspec/killer_spec.rb} +2 -1
  41. data/spec/unit/mutant/runner/config_spec.rb +36 -21
  42. data/spec/unit/mutant_spec.rb +7 -9
  43. metadata +25 -22
  44. data/lib/mutant/cli/builder.rb +0 -167
  45. data/lib/mutant/killer/rspec.rb +0 -95
  46. data/lib/mutant/predicate.rb +0 -70
  47. data/lib/mutant/predicate/attribute.rb +0 -68
  48. data/lib/mutant/predicate/blacklist.rb +0 -27
  49. data/lib/mutant/predicate/matcher.rb +0 -38
  50. data/lib/mutant/predicate/whitelist.rb +0 -28
  51. data/lib/mutant/strategy/rspec.rb +0 -76
  52. data/spec/unit/mutant/cli/builder/rspec_spec.rb +0 -38
  53. data/spec/unit/mutant/matcher/filter_spec.rb +0 -19
  54. data/spec/unit/mutant/predicate_spec.rb +0 -135
@@ -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
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Mutant
4
4
  # The current mutant version
5
- VERSION = '0.3.6'.freeze
5
+ VERSION = '0.5.0'.freeze
6
6
  end # Mutant
@@ -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
- gem.files = `git ls-files`.split("\n")
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.9.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.1.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").should be(true)
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 = "#{base_cmd} ::TestApp::Literal#string --ignore-subject ::TestApp::Literal#uncovered_string"
21
- Kernel.system(cli).should be(true)
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).should be(false)
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).should be(false)
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
- specify 'it allows to create zombie from mutant' do
6
+ pending 'it allows to create zombie from mutant' do
7
7
  Mutant::Zombifier.run('mutant')
8
- Zombie.constants.should include(:Mutant)
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.should be(1)
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.should eql(method_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).should eql(method_line)
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.should eql(method_arity)
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.send(:scope).should eql(scope)
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.should be(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 90.1 # TODO: raise this to 100, then mutation test
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 = [:expect, :should]
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) { Mutant::Predicate::TAUTOLOGY }
31
- let(:expected_strategy) { Mutant::Strategy::Rspec.new(0) }
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 matchers given' }
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(--rspec --code) }
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 matcher' do
80
- let(:arguments) { %w(--rspec TestApp::Literal#float) }
81
- let(:expected_matcher) { ns::Method::Instance.new(cache, TestApp::Literal, TestApp::Literal.instance_method(:float)) }
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(:matcher) { '::TestApp*' }
88
- let(:arguments) { %W(--debug --rspec #{matcher}) }
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.should be(true)
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(:matcher) { '::TestApp*' }
100
- let(:arguments) { %W(--zombie --rspec #{matcher}) }
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.should be(true)
102
+ expect(subject.config.zombie).to be(true)
107
103
  end
108
104
  end
109
105
 
110
- context 'with namespace matcher' do
111
- let(:matcher) { '::TestApp*' }
112
- let(:arguments) { %W(--rspec #{matcher}) }
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(:matcher) { 'TestApp::Literal#float' }
120
- let(:arguments) { %W(--rspec --code faa --code bbb #{matcher}) }
121
-
122
- let(:filters) do
123
- [
124
- Mutant::Predicate::Attribute.new(:code, 'faa'),
125
- Mutant::Predicate::Attribute.new(:code, 'bbb'),
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) { ns::Method::Instance.new(cache, TestApp::Literal, TestApp::Literal.instance_method(:float)) }
130
- let(:expected_filter) { Mutant::Predicate::Whitelist.new(filters) }
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
@@ -31,6 +31,6 @@ describe Mutant::Context::Scope, '#root' do
31
31
  end
32
32
 
33
33
  it 'should create correct source' do
34
- generated_source.should eql(expected_source)
34
+ expect(generated_source).to eql(expected_source)
35
35
  end
36
36
  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.should eql([__FILE__, 3])
46
+ .source_location).to eql([__FILE__, 3])
47
47
  end
48
48
  end
@@ -32,7 +32,7 @@ describe Mutant::Matcher::Chain do
32
32
  pending 'FIX RBX rspec? BUG HERE'
33
33
  else
34
34
  it 'yields the expected values' do
35
- subject.to_a.should eql(object.to_a)
35
+ expect(subject.to_a).to eql(object.to_a)
36
36
  end
37
37
  end
38
38
  end
@@ -58,7 +58,7 @@ describe Mutant::Matcher::Methods::Instance, '#each' do
58
58
 
59
59
  it 'should yield expected subjects' do
60
60
  subject
61
- yields.should eql(subjects)
61
+ expect(yields).to eql(subjects)
62
62
  end
63
63
 
64
64
  it_should_behave_like 'an #each method'