mutant 0.6.6 → 0.6.7

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 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: