mutant 0.6.6 → 0.6.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f3fbd46aa447dc402152bb4199a0ecfb1f1e9c8e
4
- data.tar.gz: f01ecbd6a0dc0fac2c5676ab51d957b7a64744b8
3
+ metadata.gz: 11e3b674e08512b58bd258c3ab1434bdf684d71a
4
+ data.tar.gz: f1c0559335e982f5348d6fb71c4ed2f7ae01b5b4
5
5
  SHA512:
6
- metadata.gz: 04d88ed0b9914df3e8709711bd47a88390196a6316b385b07ea4a311b79b547936a5c3660b1ff85740fc8162dbe9c12ed463861695e9733ffcd87fe84eaebe56
7
- data.tar.gz: 9702946488ff1c82f7a8ba2de538903acdc200ac161d33f016c37548d93594134243dd590c15f0647e5fed36b54b1cef7c3980a4af688b53e90801c9250ad5af
6
+ metadata.gz: d7decc3ff8888ea325265bfaf8ea098ee4656a9254978c72b0561997944cdf287d003a724ff9511ec2d15355e708b070f10c7d5561a107bfdfb5eec21bcd3263
7
+ data.tar.gz: 9e229f14f8b9b808ab72293ac15855627cd821cdf151413aa38dfd5fd3815c64c11eb37665cca847652c98db90c51f50ec53c2afa1c6e27ae2f9bf3185fd3094
data/Changelog.md CHANGED
@@ -1,3 +1,8 @@
1
+ # v0.6.7 2014-11-17
2
+
3
+ * Fix duplicate neutral emit for memoized instance method subjects
4
+ * Fix neutral error detection edge cases
5
+
1
6
  # v0.6.6 2014-11-11
2
7
 
3
8
  * Fix emitter to recurse into left and right of binary nodes.
data/Gemfile.devtools CHANGED
@@ -8,6 +8,7 @@ group :development do
8
8
 
9
9
  platform :rbx do
10
10
  gem 'rubysl-singleton', '~> 2.0.0'
11
+ gem 'rubinius-coverage', '~> 2.0.1'
11
12
  end
12
13
  end
13
14
 
@@ -38,7 +39,7 @@ group :metrics do
38
39
  gem 'flay', '~> 2.5.0'
39
40
  gem 'flog', '~> 4.2.1'
40
41
  gem 'reek', '~> 1.3.7'
41
- gem 'rubocop', '~> 0.26.1'
42
+ gem 'rubocop', '~> 0.26.0'
42
43
  gem 'simplecov', '~> 0.7.1'
43
44
  gem 'yardstick', '~> 0.9.9'
44
45
 
data/config/flay.yml CHANGED
@@ -1,3 +1,3 @@
1
1
  ---
2
2
  threshold: 18
3
- total_score: 1126
3
+ total_score: 1132
data/lib/mutant/ast.rb CHANGED
@@ -44,7 +44,7 @@ module Mutant
44
44
  # @api private
45
45
  #
46
46
  def self.find_last_path(node, &predicate)
47
- raise ArgumentError, 'block expected' unless block_given?
47
+ fail ArgumentError, 'block expected' unless block_given?
48
48
  path = []
49
49
  walk(node, [node]) do |candidate, stack|
50
50
  if predicate.call(candidate, &predicate)
data/lib/mutant/cli.rb CHANGED
@@ -22,7 +22,7 @@ module Mutant
22
22
  # @api private
23
23
  #
24
24
  def self.run(arguments)
25
- Env.call(call(arguments)).success? ? EXIT_SUCCESS : EXIT_FAILURE
25
+ Runner.call(Env.new(call(arguments))).success? ? EXIT_SUCCESS : EXIT_FAILURE
26
26
  rescue Error => exception
27
27
  $stderr.puts(exception.message)
28
28
  EXIT_FAILURE
@@ -45,12 +45,12 @@ module Mutant
45
45
  def self.wrap(scope, node)
46
46
  name = s(:const, nil, scope.name.split(NAMESPACE_DELIMITER).last.to_sym)
47
47
  case scope
48
- when ::Class
48
+ when Class
49
49
  s(:class, name, nil, node)
50
- when ::Module
50
+ when Module
51
51
  s(:module, name, node)
52
52
  else
53
- raise "Cannot wrap scope: #{scope.inspect}"
53
+ fail "Cannot wrap scope: #{scope.inspect}"
54
54
  end
55
55
  end
56
56
 
@@ -79,16 +79,6 @@ module Mutant
79
79
  name_nesting.last
80
80
  end
81
81
 
82
- # Return name
83
- #
84
- # @return [String]
85
- #
86
- # @api private
87
- #
88
- def name
89
- scope.name
90
- end
91
-
92
82
  # Return match expressions
93
83
  #
94
84
  # @return [Enumerable<Expression>]
data/lib/mutant/env.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  module Mutant
2
2
  # Abstract base class for mutant environments
3
3
  class Env
4
- include Adamantium::Flat, Concord::Public.new(:config, :cache), Procto.call(:run)
4
+ include Adamantium::Flat, Concord::Public.new(:config, :cache)
5
5
 
6
6
  # Return new env
7
7
  #
@@ -30,16 +30,6 @@ module Mutant
30
30
  initialize_mutations
31
31
  end
32
32
 
33
- # Run mutant producing a report on configured env
34
- #
35
- # @return [Report]
36
- #
37
- # @api private
38
- #
39
- def run
40
- Runner.call(self)
41
- end
42
-
43
33
  # Print warning message
44
34
  #
45
35
  # @param [String]
@@ -96,7 +86,7 @@ module Mutant
96
86
  def scope_name(scope)
97
87
  scope.name
98
88
  rescue => exception
99
- warn("While obtaining #{scope.class}#name from: #{scope.inspect} It raised an error: #{exception.inspect} fix your lib!")
89
+ warn("#{scope.class}#name from: #{scope.inspect} raised an error: #{exception.inspect} fix your lib to follow normal ruby semantics!")
100
90
  nil
101
91
  end
102
92
 
@@ -116,7 +106,7 @@ module Mutant
116
106
  name = scope_name(scope) or return
117
107
 
118
108
  unless name.is_a?(String)
119
- warn("#{scope.class}#name from: #{scope.inspect} did not return a String or nil. Fix your lib to support normal ruby semantics!")
109
+ warn("#{scope.class}#name from: #{scope.inspect} returned #{name.inspect} instead String or nil. Fix your lib to follow normal ruby semantics!")
120
110
  return
121
111
  end
122
112
 
@@ -25,7 +25,7 @@ module Mutant
25
25
  methods_matcher = MATCHERS.fetch(scope_symbol).new(env, scope)
26
26
  method = methods_matcher.methods.detect do |meth|
27
27
  meth.name.equal?(method_name)
28
- end or raise NameError, "Cannot find method #{method_name}"
28
+ end or fail NameError, "Cannot find method #{method_name}"
29
29
  methods_matcher.matcher.build(env, scope, method)
30
30
  end
31
31
 
@@ -40,7 +40,7 @@ module Mutant
40
40
  def self.call(&block)
41
41
  reader, writer = IO.pipe
42
42
 
43
- pid = fork do
43
+ pid = Process.fork do
44
44
  File.open('/dev/null', 'w') do |file|
45
45
  $stderr.reopen(file)
46
46
  reader.close
data/lib/mutant/loader.rb CHANGED
@@ -13,14 +13,13 @@ module Mutant
13
13
  # @api private
14
14
  #
15
15
  def call
16
- subject.prepare
17
16
  eval(
18
17
  source,
19
18
  TOPLEVEL_BINDING,
20
19
  subject.source_path.to_s,
21
20
  subject.source_line
22
21
  )
23
- nil
22
+ self
24
23
  end
25
24
 
26
25
  private
@@ -40,7 +40,7 @@ module Mutant
40
40
  # @api private
41
41
  #
42
42
  def example
43
- raise 'source not defined' unless @source
43
+ fail 'source not defined' unless @source
44
44
  Example.new(@file, @source, @expected)
45
45
  end
46
46
 
@@ -55,7 +55,7 @@ module Mutant
55
55
  # @api private
56
56
  #
57
57
  def source(input)
58
- raise 'source already defined' if @source
58
+ fail 'source already defined' if @source
59
59
  @source = node(input)
60
60
 
61
61
  self
@@ -72,7 +72,7 @@ module Mutant
72
72
  def mutation(input)
73
73
  node = node(input)
74
74
  if @expected.include?(node)
75
- raise "Node for input: #{input.inspect} is already expected"
75
+ fail "Node for input: #{input.inspect} is already expected"
76
76
  end
77
77
  @expected << node
78
78
 
@@ -108,7 +108,7 @@ module Mutant
108
108
  when Parser::AST::Node
109
109
  input
110
110
  else
111
- raise "Cannot coerce to node: #{source.inspect}"
111
+ fail "Cannot coerce to node: #{source.inspect}"
112
112
  end
113
113
  end
114
114
 
@@ -7,17 +7,6 @@ module Mutant
7
7
  CODE_DELIMITER = "\0".freeze
8
8
  CODE_RANGE = (0..4).freeze
9
9
 
10
- # Return mutated root node
11
- #
12
- # @return [Parser::AST::Node]
13
- #
14
- # @api private
15
- #
16
- def root
17
- subject.root(node)
18
- end
19
- memoize :root
20
-
21
10
  # Insert mutated node
22
11
  #
23
12
  # FIXME: Cache subject visibility in a better way! Ideally dont mutate it
@@ -30,6 +19,7 @@ module Mutant
30
19
  #
31
20
  def insert
32
21
  subject.public?
22
+ subject.prepare
33
23
  Loader::Eval.call(root, subject)
34
24
  self
35
25
  end
@@ -102,6 +92,16 @@ module Mutant
102
92
  end
103
93
  memoize :sha1
104
94
 
95
+ # Return mutated root node
96
+ #
97
+ # @return [Parser::AST::Node]
98
+ #
99
+ # @api private
100
+ #
101
+ def root
102
+ subject.context.root(node)
103
+ end
104
+
105
105
  # Evil mutation that should case mutations to fail tests
106
106
  class Evil < self
107
107
 
@@ -16,7 +16,7 @@ module Mutant
16
16
  #
17
17
  def self.each(input, parent = nil, &block)
18
18
  return to_enum(__method__, input, parent) unless block_given?
19
- Registry.lookup(input).new(input, parent, block)
19
+ REGISTRY.lookup(input).new(input, parent, block)
20
20
 
21
21
  self
22
22
  end
@@ -29,7 +29,7 @@ module Mutant
29
29
  #
30
30
  def self.handle(*types)
31
31
  types.each do |type|
32
- Registry.register(type, self)
32
+ REGISTRY.register(type, self)
33
33
  end
34
34
  end
35
35
  private_class_method :handle
@@ -1,27 +1,34 @@
1
1
  module Mutant
2
2
  class Mutator
3
3
  # Registry for mutators
4
- module Registry
4
+ class Registry
5
5
 
6
- # Raised when the type is an invalid type
7
- InvalidTypeError = Class.new(TypeError)
6
+ # Initialize object
7
+ #
8
+ # @return [undefined]
9
+ #
10
+ # @api private
11
+ #
12
+ def initialize
13
+ @registry = {}
14
+ end
8
15
 
9
- # Raised when the type is a duplicate
10
- DuplicateTypeError = Class.new(ArgumentError)
16
+ # Raised when the type is an invalid type
17
+ RegistryError = Class.new(TypeError)
11
18
 
12
19
  # Register mutator class for AST node class
13
20
  #
14
21
  # @param [Symbol] type
15
- # @param [Class] mutator_class
22
+ # @param [Class:Mutator] mutator
16
23
  #
17
24
  # @api private
18
25
  #
19
26
  # @return [self]
20
27
  #
21
- def self.register(type, mutator_class)
22
- assert_valid_type(type)
23
- assert_unique_type(type)
24
- registry[type] = mutator_class
28
+ def register(type, mutator)
29
+ fail RegistryError, "Invalid type registration: #{type}" unless AST::Types::ALL.include?(type)
30
+ fail RegistryError, "Duplicate type registration: #{type}" if @registry.key?(type)
31
+ @registry[type] = mutator
25
32
  self
26
33
  end
27
34
 
@@ -36,58 +43,16 @@ module Mutant
36
43
  #
37
44
  # @api private
38
45
  #
39
- def self.lookup(node)
46
+ def lookup(node)
40
47
  type = node.type
41
- registry.fetch(type) do
42
- raise ArgumentError, "No mutator to handle: #{type.inspect}"
48
+ @registry.fetch(type) do
49
+ fail RegistryError, "No mutator to handle: #{type.inspect}"
43
50
  end
44
51
  end
45
52
 
46
- # Return registry state
47
- #
48
- # @return [Hash]
49
- #
50
- # @api private
51
- #
52
- def self.registry
53
- @registry ||= {}
54
- end
55
- private_class_method :registry
56
-
57
- # Assert the node type is valid
58
- #
59
- # @param [Symbol] type
60
- #
61
- # @return [undefined]
62
- #
63
- # @raise [InvalidTypeError]
64
- # raised when the node type is invalid
65
- #
66
- # @api private
67
- #
68
- def self.assert_valid_type(type)
69
- unless AST::Types::ALL.include?(type) || type.is_a?(Class)
70
- raise InvalidTypeError, "invalid type registration: #{type}"
71
- end
72
- end
73
- private_class_method :assert_valid_type
53
+ end # Registry
74
54
 
75
- # Assert the node type is unique and not already registered
76
- #
77
- # @return [undefined]
78
- #
79
- # @raise [DuplicateTypeError]
80
- # raised when the node type is a duplicate
81
- #
82
- # @api private
83
- #
84
- def self.assert_unique_type(type)
85
- if registry.key?(type)
86
- raise DuplicateTypeError, "duplicate type registration: #{type}"
87
- end
88
- end
89
- private_class_method :assert_unique_type
55
+ REGISTRY = Registry.new
90
56
 
91
- end # Registry
92
57
  end # Mutator
93
58
  end # Mutant
@@ -5,8 +5,6 @@ module Mutant
5
5
  # Mutators that mutates an array of inputs
6
6
  class Array < self
7
7
 
8
- handle(::Array)
9
-
10
8
  # Element presence mutator
11
9
  class Presence < Util
12
10
 
@@ -5,8 +5,6 @@ module Mutant
5
5
  # Utility symbol mutator
6
6
  class Symbol < self
7
7
 
8
- handle(::Symbol)
9
-
10
8
  POSTFIX = '__mutant__'.freeze
11
9
 
12
10
  private
@@ -88,18 +88,6 @@ module Mutant
88
88
  end
89
89
  memoize :source
90
90
 
91
- # Return root AST for node
92
- #
93
- # @param [Parser::AST::Node] node
94
- #
95
- # @return [Parser::AST::Node]
96
- #
97
- # @api private
98
- #
99
- def root(node)
100
- context.root(node)
101
- end
102
-
103
91
  # Return match expression
104
92
  #
105
93
  # @return [Expression]
@@ -9,11 +9,7 @@ module Mutant
9
9
 
10
10
  # A list of methods that will warn when they are undefined
11
11
  WARN_METHODS_UNDEFINED =
12
- if RUBY_ENGINE.eql?('ruby')
13
- [:initialize, :__send__, :object_id].freeze
14
- else
15
- EMPTY_ARRAY
16
- end
12
+ RUBY_ENGINE.eql?('ruby') ? [:initialize, :__send__, :object_id].freeze : EMPTY_ARRAY
17
13
 
18
14
  # Test if method is public
19
15
  #
@@ -85,7 +81,6 @@ module Mutant
85
81
  # @api private
86
82
  #
87
83
  def generate_mutations(emitter)
88
- emitter << neutral_mutation
89
84
  Mutator.each(node) do |mutant|
90
85
  emitter << Mutation::Evil.new(self, memoizer_node(mutant))
91
86
  end
@@ -1,4 +1,4 @@
1
1
  module Mutant
2
2
  # The current mutant version
3
- VERSION = '0.6.6'.freeze
3
+ VERSION = '0.6.7'.freeze
4
4
  end # Mutant
@@ -1,7 +1,7 @@
1
1
  RSpec.describe 'Mutant on ruby corpus' do
2
2
 
3
3
  before do
4
- skip 'Corpus test is deactivated on 1.9.3' if RUBY_VERSION.eql?('1.9.3')
4
+ skip 'Corpus test is deactivated on < 2.1' if RUBY_VERSION < '2.1'
5
5
  skip 'Corpus test is deactivated on RBX' if RUBY_ENGINE.eql?('rbx')
6
6
  end
7
7
 
@@ -2,7 +2,7 @@ RSpec.describe do
2
2
 
3
3
  specify 'mutant should not crash for any node parser can generate' do
4
4
  Mutant::AST::Types::ALL.each do |type|
5
- Mutant::Mutator::Registry.lookup(s(type))
5
+ Mutant::Mutator::REGISTRY.lookup(s(type))
6
6
  end
7
7
  end
8
8
  end
@@ -18,14 +18,15 @@ RSpec.describe Mutant::CLI do
18
18
  describe '.run' do
19
19
  subject { object.run(arguments) }
20
20
 
21
- let(:arguments) { double('arguments') }
22
-
23
- let(:report) { double('Report', success?: report_success) }
24
- let(:config) { double('Config') }
21
+ let(:arguments) { double('arguments') }
22
+ let(:report) { double('Report', success?: report_success) }
23
+ let(:config) { double('Config') }
24
+ let(:env) { double('env') }
25
25
 
26
26
  before do
27
27
  expect(Mutant::CLI).to receive(:call).with(arguments).and_return(config)
28
- expect(Mutant::Env).to receive(:call).with(config).and_return(report)
28
+ expect(Mutant::Env).to receive(:new).with(config).and_return(env)
29
+ expect(Mutant::Runner).to receive(:call).with(env).and_return(report)
29
30
  end
30
31
 
31
32
  context 'when report signalls success' do
@@ -0,0 +1,56 @@
1
+ RSpec.describe Mutant::Context::Scope do
2
+ describe '.wrap' do
3
+ subject { described_class.wrap(scope, node) }
4
+
5
+ let(:node) { s(:str, 'test') }
6
+
7
+ context 'with Module as scope' do
8
+ let(:scope) { Mutant }
9
+
10
+ let(:expected) do
11
+ s(:module,
12
+ s(:const, nil, :Mutant),
13
+ s(:str, 'test')
14
+ )
15
+ end
16
+
17
+ it { should eql(expected) }
18
+ end
19
+
20
+ context 'with Class as scope' do
21
+ let(:scope) { Mutant::Context }
22
+
23
+ let(:expected) do
24
+ s(:class,
25
+ s(:const, nil, :Context),
26
+ nil,
27
+ s(:str, 'test')
28
+ )
29
+ end
30
+
31
+ it { should eql(expected) }
32
+ end
33
+
34
+ context 'with Class as scope' do
35
+ let(:scope) { Mutant::Context }
36
+
37
+ let(:expected) do
38
+ s(:class,
39
+ s(:const, nil, :Context),
40
+ nil,
41
+ s(:str, 'test')
42
+ )
43
+ end
44
+
45
+ it { should eql(expected) }
46
+ end
47
+
48
+ context 'with anything else as scope' do
49
+ let(:scope) { double(name: 'Foo') }
50
+
51
+ it 'raises exception' do
52
+ expect { subject }.to raise_error(RuntimeError, "Cannot wrap scope: #{scope.inspect}")
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,49 @@
1
+ RSpec.describe Mutant::Env do
2
+ let(:config) { Mutant::Config::DEFAULT.update(jobs: 1, reporter: Mutant::Reporter::Trace.new) }
3
+
4
+ context '.new' do
5
+ subject { described_class.new(config) }
6
+
7
+ context 'when Module#name calls result in exceptions' do
8
+ it 'warns via reporter' do
9
+ klass = Class.new do
10
+ def self.name
11
+ raise
12
+ end
13
+ end
14
+
15
+ expected_warnings = ["Class#name from: #{klass} raised an error: RuntimeError fix your lib to follow normal ruby semantics!"]
16
+
17
+ expect { subject }.to change { config.reporter.warn_calls }.from([]).to(expected_warnings)
18
+
19
+ # Fix Class#name so other specs do not see this one
20
+ class << klass
21
+ undef :name
22
+ def name
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ context 'when Module#name does not return a String or nil' do
29
+ it 'warns via reporter' do
30
+ klass = Class.new do
31
+ def self.name
32
+ Object
33
+ end
34
+ end
35
+
36
+ expected_warnings = ["Class#name from: #{klass.inspect} returned #{Object.inspect} instead String or nil. Fix your lib to follow normal ruby semantics!"]
37
+
38
+ expect { subject }.to change { config.reporter.warn_calls }.from([]).to(expected_warnings)
39
+
40
+ # Fix Class#name so other specs do not see this one
41
+ class << klass
42
+ undef :name
43
+ def name
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -63,5 +63,25 @@ RSpec.describe Mutant::Isolation::Fork do
63
63
  $stderr = STDERR
64
64
  end
65
65
  end
66
+
67
+ # Spec stubbing out the fork to ensure all lines are covered
68
+ # with expectations
69
+ it 'covers all lines' do
70
+ reader, writer = double('reader'), double('writer')
71
+ expect(IO).to receive(:pipe).ordered.and_return([reader, writer])
72
+ pid = double('PID')
73
+ expect(Process).to receive(:fork).ordered.and_yield.and_return(pid)
74
+ file = double('file')
75
+ expect(File).to receive(:open).ordered.with('/dev/null', 'w').and_yield(file)
76
+ expect($stderr).to receive(:reopen).ordered.with(file)
77
+ expect(reader).to receive(:close).ordered
78
+ expect(writer).to receive(:write).ordered.with(Marshal.dump(:foo))
79
+ expect(writer).to receive(:close).ordered
80
+ expect(writer).to receive(:close).ordered
81
+ expect(reader).to receive(:read).ordered.and_return(Marshal.dump(:foo))
82
+ expect(Process).to receive(:waitpid).with(pid)
83
+
84
+ expect(object.call { :foo }).to be(:foo)
85
+ end
66
86
  end
67
87
  end
@@ -10,10 +10,6 @@ RSpec.describe Mutant::Loader::Eval, '.call' do
10
10
  double('Subject', source_path: path, source_line: line)
11
11
  end
12
12
 
13
- before do
14
- expect(mutation_subject).to receive(:prepare).and_return(mutation_subject)
15
- end
16
-
17
13
  let(:source) do
18
14
  <<-RUBY
19
15
  class SomeNamespace
@@ -4,9 +4,9 @@ RSpec.describe Mutant::Mutation do
4
4
  SYMBOL = 'test'.freeze
5
5
  end
6
6
 
7
- let(:object) { TestMutation.new(mutation_subject, Mutant::AST::Nodes::N_NIL) }
8
- let(:mutation_subject) { double('Subject', identification: 'subject', source: 'original') }
9
- let(:node) { double('Node') }
7
+ let(:object) { TestMutation.new(mutation_subject, Mutant::AST::Nodes::N_NIL) }
8
+ let(:mutation_subject) { double('Subject', identification: 'subject', context: context, source: 'original') }
9
+ let(:context) { double('Context') }
10
10
 
11
11
  describe '#code' do
12
12
  subject { object.code }
@@ -24,6 +24,21 @@ RSpec.describe Mutant::Mutation do
24
24
  it_should_behave_like 'an idempotent method'
25
25
  end
26
26
 
27
+ describe '#insert' do
28
+ subject { object.insert }
29
+
30
+ let(:wrapped_node) { double('Wrapped Node') }
31
+
32
+ before do
33
+ expect(mutation_subject).to receive(:public?).ordered.and_return(true)
34
+ expect(mutation_subject).to receive(:prepare).ordered
35
+ expect(context).to receive(:root).ordered.with(s(:nil)).and_return(wrapped_node)
36
+ expect(Mutant::Loader::Eval).to receive(:call).ordered.with(wrapped_node, mutation_subject).and_return(nil)
37
+ end
38
+
39
+ it_should_behave_like 'a command method'
40
+ end
41
+
27
42
  describe '#source' do
28
43
  subject { object.source }
29
44
 
@@ -0,0 +1,57 @@
1
+ RSpec.describe Mutant::Mutator::Registry do
2
+ describe '#lookup' do
3
+ subject { Mutant::Mutator::REGISTRY.lookup(node) }
4
+
5
+ context 'on registred node' do
6
+ let(:node) { s(:true) }
7
+
8
+ it { should eql(Mutant::Mutator::Node::Literal::Boolean) }
9
+ end
10
+
11
+ context 'on unknown node' do
12
+ let(:node) { s(:unknown) }
13
+
14
+ it 'raises error' do
15
+ expect { subject }.to raise_error(described_class::RegistryError, "No mutator to handle: :unknown")
16
+ end
17
+ end
18
+ end
19
+
20
+ describe '#register' do
21
+ let(:object) { described_class.new }
22
+
23
+ let(:mutator) { double('Mutator') }
24
+
25
+ subject { object.register(type, mutator) }
26
+
27
+ context 'when registring an invalid node type' do
28
+ let(:type) { :invalid }
29
+
30
+ it 'raises error' do
31
+ expect { subject }.to raise_error(described_class::RegistryError, 'Invalid type registration: invalid')
32
+ end
33
+ end
34
+
35
+ context 'when registring a valid node type' do
36
+ let(:type) { :true }
37
+
38
+ it 'allows to lookup mutator' do
39
+ subject
40
+ expect(object.lookup(s(type))).to be(mutator)
41
+ end
42
+
43
+ it_behaves_like 'a command method'
44
+ end
45
+
46
+ context 'when duplicate the registration of a valid node type' do
47
+ let(:type) { :true }
48
+
49
+ it 'allows to lookup mutator' do
50
+ object.register(type, mutator)
51
+ expect { subject }.to raise_error(described_class::RegistryError, 'Duplicate type registration: true')
52
+ end
53
+
54
+ it_behaves_like 'a command method'
55
+ end
56
+ end
57
+ end
@@ -85,6 +85,34 @@ RSpec.describe Mutant::Subject::Method::Instance do
85
85
 
86
86
  it { should eql("def foo\nend") }
87
87
  end
88
+
89
+ describe '#public?' do
90
+ subject { object.public? }
91
+
92
+ context 'when method is public' do
93
+ it { should be(true) }
94
+ end
95
+
96
+ context 'when method is private' do
97
+ before do
98
+ scope.class_eval do
99
+ private :foo
100
+ end
101
+ end
102
+
103
+ it { should be(false) }
104
+ end
105
+
106
+ context 'when method is protected' do
107
+ before do
108
+ scope.class_eval do
109
+ protected :foo
110
+ end
111
+ end
112
+
113
+ it { should be(false) }
114
+ end
115
+ end
88
116
  end
89
117
 
90
118
  RSpec.describe Mutant::Subject::Method::Instance::Memoized do
@@ -124,6 +152,20 @@ RSpec.describe Mutant::Subject::Method::Instance::Memoized do
124
152
  it_should_behave_like 'a command method'
125
153
  end
126
154
 
155
+ describe '#mutations' do
156
+ subject { object.mutations }
157
+
158
+ let(:expected) do
159
+ [
160
+ Mutant::Mutation::Neutral.new(object, s(:begin, s(:def, :foo, s(:args)), s(:send, nil, :memoize, s(:args, s(:sym, :foo))))),
161
+ Mutant::Mutation::Evil.new(object, s(:begin, s(:def, :foo, s(:args), s(:send, nil, :raise)), s(:send, nil, :memoize, s(:args, s(:sym, :foo))))),
162
+ Mutant::Mutation::Evil.new(object, s(:begin, s(:def, :foo, s(:args), nil), s(:send, nil, :memoize, s(:args, s(:sym, :foo))))),
163
+ ]
164
+ end
165
+
166
+ it { should eql(expected) }
167
+ end
168
+
127
169
  describe '#source' do
128
170
  subject { object.source }
129
171
 
@@ -2,7 +2,11 @@ RSpec.describe Mutant::Subject do
2
2
  let(:class_under_test) do
3
3
  Class.new(described_class) do
4
4
  def expression
5
- Mutant::Expression.parse('Test')
5
+ Mutant::Expression.parse('SubjectA')
6
+ end
7
+
8
+ def match_expressions
9
+ [expression] << Mutant::Expression.parse('SubjectB')
6
10
  end
7
11
  end
8
12
  end
@@ -34,7 +38,7 @@ RSpec.describe Mutant::Subject do
34
38
  describe '#identification' do
35
39
  subject { object.identification }
36
40
 
37
- it { should eql('Test:source_path:source_line') }
41
+ it { should eql('SubjectA:source_path:source_line') }
38
42
  end
39
43
 
40
44
  describe '#prepare' do
@@ -43,6 +47,56 @@ RSpec.describe Mutant::Subject do
43
47
  it_should_behave_like 'a command method'
44
48
  end
45
49
 
50
+ describe '#tests' do
51
+ let(:config) { Mutant::Config::DEFAULT.update(integration: integration) }
52
+ let(:integration) { double('Integration', all_tests: all_tests) }
53
+ let(:test_a) { double('test', expression: Mutant::Expression.parse('SubjectA')) }
54
+ let(:test_b) { double('test', expression: Mutant::Expression.parse('SubjectB')) }
55
+ let(:test_c) { double('test', expression: Mutant::Expression.parse('SubjectC')) }
56
+
57
+ subject { object.tests }
58
+
59
+ context 'without available tests' do
60
+ let(:all_tests) { [] }
61
+
62
+ it { should eql([]) }
63
+
64
+ it_should_behave_like 'an idempotent method'
65
+ end
66
+
67
+ context 'without qualifying tests' do
68
+ let(:all_tests) { [test_c] }
69
+
70
+ it { should eql([]) }
71
+
72
+ it_should_behave_like 'an idempotent method'
73
+ end
74
+
75
+ context 'with qualifying tests for first match expression' do
76
+ let(:all_tests) { [test_a] }
77
+
78
+ it { should eql([test_a]) }
79
+
80
+ it_should_behave_like 'an idempotent method'
81
+ end
82
+
83
+ context 'with qualifying tests for second match expression' do
84
+ let(:all_tests) { [test_b] }
85
+
86
+ it { should eql([test_b]) }
87
+
88
+ it_should_behave_like 'an idempotent method'
89
+ end
90
+
91
+ context 'with qualifying tests for the first and second match expression' do
92
+ let(:all_tests) { [test_a, test_b] }
93
+
94
+ it { should eql([test_a]) }
95
+
96
+ it_should_behave_like 'an idempotent method'
97
+ end
98
+ end
99
+
46
100
  describe '#node' do
47
101
  subject { object.node }
48
102
 
@@ -49,7 +49,7 @@ RSpec.describe Mutant::WarningExpectation do
49
49
  let(:expected_warnings) { [warning_a] }
50
50
 
51
51
  it 'raises an expectation error' do
52
- expect { subject }.to raise_error(Mutant::WarningExpectation::ExpectationError.new(expected_warnings))
52
+ expect { subject }.to raise_error(Mutant::WarningExpectation::ExpectationError.new([warning_b], expected_warnings))
53
53
  end
54
54
  end
55
55
 
@@ -64,9 +64,17 @@ RSpec.describe Mutant::WarningExpectation do
64
64
  let(:actual_warnings) { [warning_b] }
65
65
 
66
66
  it 'raises an expectation error' do
67
- expect { subject }.to raise_error(Mutant::WarningExpectation::ExpectationError.new([warning_b]))
67
+ expect { subject }.to raise_error(Mutant::WarningExpectation::ExpectationError.new([warning_b], expected_warnings))
68
68
  end
69
69
  end
70
70
  end
71
71
  end
72
72
  end
73
+
74
+ RSpec.describe Mutant::WarningExpectation::ExpectationError do
75
+ describe '#message' do
76
+ subject { described_class.new(['unexpected-a'], ['expected-b']).message }
77
+
78
+ it { should eql('Unexpected warnings: ["unexpected-a"], expected: ["expected-b"]') }
79
+ end
80
+ end
@@ -8,6 +8,7 @@ group :development do
8
8
 
9
9
  platform :rbx do
10
10
  gem 'rubysl-singleton', '~> 2.0.0'
11
+ gem 'rubinius-coverage', '~> 2.0.1'
11
12
  end
12
13
  end
13
14
 
@@ -38,7 +39,7 @@ group :metrics do
38
39
  gem 'flay', '~> 2.5.0'
39
40
  gem 'flog', '~> 4.2.1'
40
41
  gem 'reek', '~> 1.3.7'
41
- gem 'rubocop', '~> 0.26.1'
42
+ gem 'rubocop', '~> 0.26.0'
42
43
  gem 'simplecov', '~> 0.7.1'
43
44
  gem 'yardstick', '~> 0.9.9'
44
45
 
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.6.6
4
+ version: 0.6.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Markus Schirp
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-11 00:00:00.000000000 Z
11
+ date: 2014-11-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser
@@ -470,7 +470,9 @@ files:
470
470
  - spec/unit/mutant/context/root_spec.rb
471
471
  - spec/unit/mutant/context/scope/root_spec.rb
472
472
  - spec/unit/mutant/context/scope/unqualified_name_spec.rb
473
+ - spec/unit/mutant/context_spec.rb
473
474
  - spec/unit/mutant/diff_spec.rb
475
+ - spec/unit/mutant/env_spec.rb
474
476
  - spec/unit/mutant/expression/method_spec.rb
475
477
  - spec/unit/mutant/expression/methods_spec.rb
476
478
  - spec/unit/mutant/expression/namespace/flat_spec.rb
@@ -491,6 +493,7 @@ files:
491
493
  - spec/unit/mutant/matcher/null_spec.rb
492
494
  - spec/unit/mutant/mutation_spec.rb
493
495
  - spec/unit/mutant/mutator/node_spec.rb
496
+ - spec/unit/mutant/mutator/registry_spec.rb
494
497
  - spec/unit/mutant/reporter/cli_spec.rb
495
498
  - spec/unit/mutant/reporter/null_spec.rb
496
499
  - spec/unit/mutant/require_highjack_spec.rb
@@ -501,7 +504,7 @@ files:
501
504
  - spec/unit/mutant/subject/method/singleton_spec.rb
502
505
  - spec/unit/mutant/subject_spec.rb
503
506
  - spec/unit/mutant/test_spec.rb
504
- - spec/unit/mutant/warning_expectation.rb
507
+ - spec/unit/mutant/warning_expectation_spec.rb
505
508
  - spec/unit/mutant/warning_filter_spec.rb
506
509
  - spec/unit/mutant_spec.rb
507
510
  - test_app/.rspec
@@ -549,7 +552,9 @@ test_files:
549
552
  - spec/unit/mutant/context/root_spec.rb
550
553
  - spec/unit/mutant/context/scope/root_spec.rb
551
554
  - spec/unit/mutant/context/scope/unqualified_name_spec.rb
555
+ - spec/unit/mutant/context_spec.rb
552
556
  - spec/unit/mutant/diff_spec.rb
557
+ - spec/unit/mutant/env_spec.rb
553
558
  - spec/unit/mutant/expression/method_spec.rb
554
559
  - spec/unit/mutant/expression/methods_spec.rb
555
560
  - spec/unit/mutant/expression/namespace/flat_spec.rb
@@ -570,6 +575,7 @@ test_files:
570
575
  - spec/unit/mutant/matcher/null_spec.rb
571
576
  - spec/unit/mutant/mutation_spec.rb
572
577
  - spec/unit/mutant/mutator/node_spec.rb
578
+ - spec/unit/mutant/mutator/registry_spec.rb
573
579
  - spec/unit/mutant/reporter/cli_spec.rb
574
580
  - spec/unit/mutant/reporter/null_spec.rb
575
581
  - spec/unit/mutant/require_highjack_spec.rb
@@ -580,7 +586,7 @@ test_files:
580
586
  - spec/unit/mutant/subject/method/singleton_spec.rb
581
587
  - spec/unit/mutant/subject_spec.rb
582
588
  - spec/unit/mutant/test_spec.rb
583
- - spec/unit/mutant/warning_expectation.rb
589
+ - spec/unit/mutant/warning_expectation_spec.rb
584
590
  - spec/unit/mutant/warning_filter_spec.rb
585
591
  - spec/unit/mutant_spec.rb
586
592
  has_rdoc: