Add mutations on singleton methods
* Fixing singleton matcher to return Rubiniuy::AST::DefineSingleton and not Rubinius::AST::DefineSingletonScope * Adjust counts of runner spec
This commit is contained in:
parent
23199b3c94
commit
583668138e
9 changed files with 91 additions and 81 deletions
|
@ -86,34 +86,6 @@ module Mutant
|
|||
#
|
||||
abstract_method :method
|
||||
|
||||
# Return node classes this matcher matches
|
||||
#
|
||||
# @return [Rubinius::AST::Node]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def node_class
|
||||
self.class::NODE_CLASS
|
||||
end
|
||||
|
||||
# Check if node is matched
|
||||
#
|
||||
# @param [Rubinius::AST::Node] node
|
||||
#
|
||||
# @return [true]
|
||||
# returns true if node matches method
|
||||
#
|
||||
# @return [false]
|
||||
# returns false if node NOT matches method
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def match?(node)
|
||||
node.line == source_line &&
|
||||
node.class == node_class &&
|
||||
node.name == method_name
|
||||
end
|
||||
|
||||
# Return full ast
|
||||
#
|
||||
# @return [Rubinius::AST::Node]
|
||||
|
@ -178,6 +150,21 @@ module Mutant
|
|||
Subject.new(context, node)
|
||||
end
|
||||
memoize :subject
|
||||
|
||||
# Return matched node
|
||||
#
|
||||
# @return [Rubinus::AST::Node]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def matched_node
|
||||
last_match = nil
|
||||
ast.walk do |predicate, node|
|
||||
last_match = node if match?(node)
|
||||
predicate
|
||||
end
|
||||
last_match
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,8 +4,6 @@ module Mutant
|
|||
# Matcher for instance methods
|
||||
class Instance < self
|
||||
|
||||
NODE_CLASS = Rubinius::AST::Define
|
||||
|
||||
# Extract instance method matchers from scope
|
||||
#
|
||||
# @param [Class|Module] scope
|
||||
|
@ -31,8 +29,27 @@ module Mutant
|
|||
def identification
|
||||
"#{scope.name}##{method_name}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Check if node is matched
|
||||
#
|
||||
# @param [Rubinius::AST::Node] node
|
||||
#
|
||||
# @return [true]
|
||||
# returns true if node matches method
|
||||
#
|
||||
# @return [false]
|
||||
# returns false if node NOT matches method
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def match?(node)
|
||||
node.line == source_line &&
|
||||
node.class == Rubinius::AST::Define &&
|
||||
node.name == method_name
|
||||
end
|
||||
|
||||
# Return method instance
|
||||
#
|
||||
# @return [UnboundMethod]
|
||||
|
@ -43,20 +60,6 @@ module Mutant
|
|||
scope.instance_method(method_name)
|
||||
end
|
||||
|
||||
# Return matched node
|
||||
#
|
||||
# @return [Rubinus::AST::Define]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def matched_node
|
||||
last_match = nil
|
||||
ast.walk do |predicate, node|
|
||||
last_match = node if match?(node)
|
||||
predicate
|
||||
end
|
||||
last_match
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,8 +4,6 @@ module Mutant
|
|||
# Matcher for singleton methods
|
||||
class Singleton < self
|
||||
|
||||
NODE_CLASS = Rubinius::AST::DefineSingletonScope
|
||||
|
||||
# Return matcher enumerable
|
||||
#
|
||||
# @param [Class|Module] scope
|
||||
|
@ -16,11 +14,10 @@ module Mutant
|
|||
#
|
||||
def self.each(scope)
|
||||
return to_enum unless block_given?
|
||||
return unless scope.kind_of?(Module)
|
||||
scope.singleton_class.public_instance_methods(false).reject do |method|
|
||||
method.to_sym == :__class_init__
|
||||
end.map do |name|
|
||||
new(scope, name)
|
||||
end.each do |name|
|
||||
yield new(scope, name)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -46,24 +43,22 @@ module Mutant
|
|||
scope.method(method_name)
|
||||
end
|
||||
|
||||
# Check for stopping AST walk on branch
|
||||
# Check if node is matched
|
||||
#
|
||||
# This method exist to protect against the
|
||||
# artifical edge case where DefineSingleton nodes
|
||||
# with differend receivers exist on the same line.
|
||||
#
|
||||
# @param [Rubnius::AST::Node] node
|
||||
# @param [Rubinius::AST::Node] node
|
||||
#
|
||||
# @return [true]
|
||||
# returns true when node should NOT be followed
|
||||
# returns true if node matches method
|
||||
#
|
||||
# @return [false]
|
||||
# returns false when node can be followed
|
||||
# returns false if node NOT matches method
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def stop?(node)
|
||||
node.is_a?(Rubinius::AST::DefineSingleton) && !match_receiver?(node)
|
||||
#
|
||||
def match?(node)
|
||||
node.line == source_line &&
|
||||
node.class == Rubinius::AST::DefineSingleton &&
|
||||
node.body.name == method_name && match_receiver?(node)
|
||||
end
|
||||
|
||||
# Check if receiver matches
|
||||
|
@ -106,22 +101,6 @@ module Mutant
|
|||
node.name.to_s == context.unqualified_name
|
||||
end
|
||||
|
||||
# Return matched node
|
||||
#
|
||||
# @return [Rubinus::AST::DefineSingletonScope]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def matched_node
|
||||
last_match = nil
|
||||
ast.walk do |predicate, node|
|
||||
if match?(node)
|
||||
last_match = node
|
||||
end
|
||||
!stop?(node)
|
||||
end
|
||||
last_match
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,6 +3,8 @@ module Mutant
|
|||
class Define < self
|
||||
|
||||
handle(Rubinius::AST::Define)
|
||||
handle(Rubinius::AST::DefineSingleton)
|
||||
handle(Rubinius::AST::DefineSingletonScope)
|
||||
|
||||
private
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ module Mutant
|
|||
if killer.fail?
|
||||
@io.puts(colorize(Color::RED, "!!! Uncovered Mutation !!!"))
|
||||
differ = Differ.new(killer.original_source,killer.mutation_source)
|
||||
diff = color? ? differ.colorized_diff : color
|
||||
diff = color? ? differ.colorized_diff : differ.diff
|
||||
@io.puts(diff)
|
||||
@io.puts
|
||||
end
|
||||
|
|
|
@ -15,6 +15,14 @@ describe Mutant, 'method matching' do
|
|||
let(:defaults) { {} }
|
||||
|
||||
context 'on instance methods' do
|
||||
def name(node)
|
||||
node.name
|
||||
end
|
||||
|
||||
def arguments(node)
|
||||
node.arguments
|
||||
end
|
||||
|
||||
let(:pattern) { 'Foo#bar' }
|
||||
let(:defaults) do
|
||||
{
|
||||
|
@ -155,11 +163,19 @@ describe Mutant, 'method matching' do
|
|||
let(:defaults) do
|
||||
{
|
||||
:scope => Foo,
|
||||
:node_class => Rubinius::AST::DefineSingletonScope,
|
||||
:node_class => Rubinius::AST::DefineSingleton,
|
||||
:method_arity => 0
|
||||
}
|
||||
end
|
||||
|
||||
def name(node)
|
||||
node.body.name
|
||||
end
|
||||
|
||||
def arguments(node)
|
||||
node.body.arguments
|
||||
end
|
||||
|
||||
context 'when defined on self' do
|
||||
let(:body) do
|
||||
<<-RUBY
|
||||
|
|
|
@ -8,11 +8,15 @@ describe Mutant, 'runner' do
|
|||
end
|
||||
|
||||
it 'allows to run mutant over a project' do
|
||||
output = StringIO.new
|
||||
runner = Mutant::Runner.run(
|
||||
:pattern => /\ATestApp::/,
|
||||
:killer => Mutant::Killer::Rspec,
|
||||
:reporter => Mutant::Reporter::CLI.new($stdout)
|
||||
:reporter => Mutant::Reporter::CLI.new(output)
|
||||
)
|
||||
runner.fail?.should be(true)
|
||||
runner.errors.size.should be(22)
|
||||
output.rewind
|
||||
output.lines.grep(/Mutation/).size.should be(47)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,7 +18,7 @@ shared_examples_for 'a method match' do
|
|||
end
|
||||
|
||||
it 'should have correct method name' do
|
||||
node.name.should eql(method_name)
|
||||
name(node).should eql(method_name)
|
||||
end
|
||||
|
||||
it 'should have correct line number' do
|
||||
|
@ -26,7 +26,7 @@ shared_examples_for 'a method match' do
|
|||
end
|
||||
|
||||
it 'should have correct arity' do
|
||||
node.arguments.required.length.should eql(method_arity)
|
||||
arguments(node).required.length.should eql(method_arity)
|
||||
end
|
||||
|
||||
it 'should have correct scope in context' do
|
||||
|
|
|
@ -19,4 +19,23 @@ describe Mutant::Mutator, 'define' do
|
|||
|
||||
it_should_behave_like 'a mutator'
|
||||
end
|
||||
|
||||
context 'define on singleton' do
|
||||
let(:source) { 'def self.foo; self.bar; self.baz; end' }
|
||||
|
||||
let(:mutations) do
|
||||
mutations = []
|
||||
|
||||
# Body presence mutations
|
||||
mutations << 'def self.foo; bar; self.baz; end'
|
||||
mutations << 'def self.foo; self.bar; baz; end'
|
||||
|
||||
# Body presence mutations
|
||||
mutations << 'def self.foo; self.bar; end'
|
||||
mutations << 'def self.foo; self.baz; end'
|
||||
end
|
||||
|
||||
it_should_behave_like 'a mutator'
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue