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:
Markus Schirp 2012-08-16 19:10:24 +02:00
parent 23199b3c94
commit 583668138e
9 changed files with 91 additions and 81 deletions

View file

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

View file

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

View file

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

View file

@ -3,6 +3,8 @@ module Mutant
class Define < self
handle(Rubinius::AST::Define)
handle(Rubinius::AST::DefineSingleton)
handle(Rubinius::AST::DefineSingletonScope)
private

View file

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

View file

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

View file

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

View file

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

View file

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