Skip method subjects defined in blocks

* No easy way to insert mutants on these via current monkey patching
  loader
* The blocks are typically part of a method building library when those
  libraries are mutated the def/defs nodes will be part of an mutated
  block that is included in a subject.

[fix #254]
This commit is contained in:
Markus Schirp 2014-09-16 19:30:45 +00:00
parent bfac03f29d
commit a2de0442f6
6 changed files with 154 additions and 129 deletions

View file

@ -1,3 +1,3 @@
--- ---
threshold: 18 threshold: 18
total_score: 1094 total_score: 1098

View file

@ -3,7 +3,7 @@ module Mutant
# Matcher for subjects that are a specific method # Matcher for subjects that are a specific method
class Method < self class Method < self
include Adamantium::Flat, Concord::Public.new(:env, :scope, :target_method) include Adamantium::Flat, Concord::Public.new(:env, :scope, :target_method)
include Equalizer.new(:identification) include AST::NodePredicates, Equalizer.new(:identification)
# Methods within rbx kernel directory are precompiled and their source # Methods within rbx kernel directory are precompiled and their source
# cannot be accessed via reading source location. Same for methods created by eval. # cannot be accessed via reading source location. Same for methods created by eval.
@ -40,7 +40,10 @@ module Mutant
def skip? def skip?
location = source_location location = source_location
if location.nil? || BLACKLIST.match(location.first) if location.nil? || BLACKLIST.match(location.first)
env.warn(format('%s does not have valid source location unable to emit matcher', target_method.inspect)) env.warn(format('%s does not have valid source location unable to emit subject', target_method.inspect))
true
elsif matched_node_path.any?(&method(:n_block?))
env.warn(format('%s is defined from a 3rd party lib unable to emit subject', target_method.inspect))
true true
else else
false false

View file

@ -17,7 +17,7 @@ RSpec.shared_examples_for 'a method matcher' do
end end
it 'should have correct line number' do it 'should have correct line number' do
expect(node.location.expression.line - base).to eql(method_line) expect(node.location.expression.line).to eql(method_line)
end end
it 'should have correct arity' do it 'should have correct arity' do

View file

@ -11,10 +11,10 @@ RSpec.describe Mutant::Matcher::Method::Instance do
let(:method) { scope.instance_method(method_name) } let(:method) { scope.instance_method(method_name) }
let(:yields) { [] } let(:yields) { [] }
let(:namespace) { self.class } let(:namespace) { self.class }
let(:scope) { self.class::Foo }
let(:type) { :def } let(:type) { :def }
let(:method_name) { :bar } let(:method_name) { :foo }
let(:method_arity) { 0 } let(:method_arity) { 0 }
let(:base) { TestApp::InstanceMethodTests }
def name def name
node.children[0] node.children[0]
@ -36,109 +36,79 @@ RSpec.describe Mutant::Matcher::Method::Instance do
it 'does warn' do it 'does warn' do
subject subject
expect(reporter.warn_calls.last).to( expect(reporter.warn_calls.last).to(
eql("#{method.inspect} does not have valid source location unable to emit matcher") eql("#{method.inspect} does not have valid source location unable to emit subject")
) )
end end
end end
context 'when method is defined once' do context 'when method is defined once' do
let(:base) { __LINE__ } let(:scope) { base::DefinedOnce }
class self::Foo let(:method_line) { 7 }
def bar; end
end
let(:method_line) { 2 }
it_should_behave_like 'a method matcher' it_should_behave_like 'a method matcher'
end end
context 'when method is defined once with a memoizer' do context 'when method is defined once with a memoizer' do
let(:base) { __LINE__ } let(:scope) { base::WithMemoizer }
class self::Foo let(:method_line) { 12 }
def bar; end
include Adamantium
memoize :bar
end
let(:method_line) { 2 }
it_should_behave_like 'a method matcher' it_should_behave_like 'a method matcher'
end end
context 'when method is defined multiple times' do context 'when method is defined multiple times' do
context 'on different lines' do context 'on different lines' do
let(:base) { __LINE__ } let(:scope) { base::DefinedMultipleTimes::DifferentLines }
class self::Foo let(:method_line) { 21 }
def bar let(:method_arity) { 1 }
end
def bar(_arg)
end
end
let(:method_line) { 5 }
let(:method_arity) { 1 }
it_should_behave_like 'a method matcher' it_should_behave_like 'a method matcher'
end end
context 'on the same line' do context 'on the same line' do
let(:base) { __LINE__ } let(:scope) { base::DefinedMultipleTimes::SameLineSameScope }
class self::Foo let(:method_line) { 26 }
def bar; end; def bar(_arg); end let(:method_arity) { 1 }
end
let(:method_line) { 2 }
let(:method_arity) { 1 }
it_should_behave_like 'a method matcher' it_should_behave_like 'a method matcher'
end end
context 'on the same line with different scope' do context 'on the same line with different scope' do
let(:base) { __LINE__ } let(:scope) { base::DefinedMultipleTimes::SameLineDifferentScope }
class self::Foo let(:method_line) { 30 }
def self.bar; end; def bar(_arg); end let(:method_arity) { 1 }
end
let(:method_line) { 2 }
let(:method_arity) { 1 }
it_should_behave_like 'a method matcher' it_should_behave_like 'a method matcher'
end end
context 'when nested' do context 'in module eval' do
let(:pattern) { 'Foo::Bar#baz' } let(:scope) { base::InModuleEval }
context 'in class' do it 'does not emit matcher' do
let(:base) { __LINE__ } subject
class self::Foo expect(yields.length).to be(0)
class Bar
def baz
end
end
end
let(:method_line) { 3 }
let(:method_name) { :baz }
let(:scope) { self.class::Foo::Bar }
it_should_behave_like 'a method matcher'
end end
context 'in module' do it 'does warn' do
let(:base) { __LINE__ } subject
module self::Foo expect(reporter.warn_calls.last).to(
class Bar eql("#{method.inspect} is defined from a 3rd party lib unable to emit subject")
def baz )
end end
end end
end
let(:method_line) { 3 } context 'in class eval' do
let(:method_name) { :baz } let(:scope) { base::InClassEval }
let(:scope) { self.class::Foo::Bar }
it_should_behave_like 'a method matcher' it 'does not emit matcher' do
subject
expect(yields.length).to be(0)
end
it 'does warn' do
subject
expect(reporter.warn_calls.last).to(
eql("#{method.inspect} is defined from a 3rd party lib unable to emit subject")
)
end end
end end
end end

View file

@ -6,10 +6,10 @@ RSpec.describe Mutant::Matcher::Method::Singleton, '#each' do
let(:method) { scope.method(method_name) } let(:method) { scope.method(method_name) }
let(:env) { Fixtures::TEST_ENV } let(:env) { Fixtures::TEST_ENV }
let(:yields) { [] } let(:yields) { [] }
let(:namespace) { self.class }
let(:scope) { self.class::Foo }
let(:type) { :defs } let(:type) { :defs }
let(:method_name) { :foo }
let(:method_arity) { 0 } let(:method_arity) { 0 }
let(:base) { TestApp::SingletonMethodTests }
def name def name
node.children[1] node.children[1]
@ -22,14 +22,8 @@ RSpec.describe Mutant::Matcher::Method::Singleton, '#each' do
context 'on singleton methods' do context 'on singleton methods' do
context 'when also defined on lvar' do context 'when also defined on lvar' do
let(:base) { __LINE__ } let(:scope) { base::AlsoDefinedOnLvar }
class self::Foo let(:method_line) { 63 }
a = Object.new
def a.bar; end; def self.bar; end
end
let(:method_name) { :bar }
let(:method_line) { 3 }
it_should_behave_like 'a method matcher' it_should_behave_like 'a method matcher'
@ -42,13 +36,8 @@ RSpec.describe Mutant::Matcher::Method::Singleton, '#each' do
end end
context 'when defined on self' do context 'when defined on self' do
let(:base) { __LINE__ } let(:scope) { base::DefinedOnSelf }
class self::Foo let(:method_line) { 58 }
def self.bar; end
end
let(:method_name) { :bar }
let(:method_line) { 2 }
it_should_behave_like 'a method matcher' it_should_behave_like 'a method matcher'
end end
@ -56,34 +45,15 @@ RSpec.describe Mutant::Matcher::Method::Singleton, '#each' do
context 'when defined on constant' do context 'when defined on constant' do
context 'inside namespace' do context 'inside namespace' do
let(:base) { __LINE__ } let(:scope) { base::DefinedOnConstant::InsideNamespace }
module self::Namespace let(:method_line) { 68 }
class Foo
def Foo.bar
end
end
end
let(:scope) { self.class::Namespace::Foo }
let(:method_name) { :bar }
let(:method_line) { 3 }
it_should_behave_like 'a method matcher' it_should_behave_like 'a method matcher'
end end
context 'outside namespace' do context 'outside namespace' do
let(:base) { __LINE__ } let(:method_line) { 75 }
module self::Namespace let(:scope) { base::DefinedOnConstant::OutsideNamespace }
class Foo
end
def Foo.bar
end
end
let(:method_name) { :bar }
let(:method_line) { 5 }
let(:scope) { self.class::Namespace::Foo }
it_should_behave_like 'a method matcher' it_should_behave_like 'a method matcher'
end end
@ -91,21 +61,9 @@ RSpec.describe Mutant::Matcher::Method::Singleton, '#each' do
context 'when defined multiple times in the same line' do context 'when defined multiple times in the same line' do
context 'with method on different scope' do context 'with method on different scope' do
let(:base) { __LINE__ } let(:scope) { base::DefinedMultipleTimes::SameLine::DifferentScope }
module self::Namespace let(:method_line) { 94 }
module Foo; end let(:method_arity) { 1 }
module Bar
def self.baz
end
def Foo.baz(_arg)
end
end
end
let(:scope) { self.class::Namespace::Bar }
let(:method_name) { :baz }
let(:method_line) { 4 }
let(:method_arity) { 0 }
it_should_behave_like 'a method matcher' it_should_behave_like 'a method matcher'
end end

View file

@ -2,6 +2,100 @@
# Namespace for test application # Namespace for test application
module TestApp module TestApp
module InstanceMethodTests
module DefinedOnce
def foo; end
end
class WithMemoizer
include Adamantium
def foo; end
memoize :foo
end
module DefinedMultipleTimes
class DifferentLines
def foo
end
def foo(_arg)
end
end
class SameLineSameScope
def foo; end; def foo(_arg); end
end
class SameLineDifferentScope
def self.foo; end; def foo(_arg); end
end
end
class InClassEval
class_eval do
def foo
end
end
end
class InModuleEval
module_eval do
def foo
end
end
end
class InInstanceEval
module_eval do
def foo
end
end
end
end
module SingletonMethodTests
module DefinedOnSelf
def self.foo; end
end
module AlsoDefinedOnLvar
a = Object.new
def a.foo; end; def self.foo; end
end
module DefinedOnConstant
module InsideNamespace
def InsideNamespace.foo
end
end
module OutsideNamespace
end
def OutsideNamespace.foo
end
end
module DefinedMultipleTimes
module DifferentLines
def self.foo
end
def self.foo(_arg)
end
end
module SameLine
module SameScope
def self.foo; end; def self.foo(_arg); end;
end
module DifferentScope
def self.foo; end; def DifferentScope.foo(_arg); end
end
end
end
end
end end
require 'test_app/literal' require 'test_app/literal'