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