From 583668138e1709946a9a3f2d6370059e8ad113eb Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Thu, 16 Aug 2012 19:10:24 +0200 Subject: [PATCH] Add mutations on singleton methods * Fixing singleton matcher to return Rubiniuy::AST::DefineSingleton and not Rubinius::AST::DefineSingletonScope * Adjust counts of runner spec --- lib/mutant/matcher/method.rb | 43 +++++++------------ lib/mutant/matcher/method/instance.rb | 35 ++++++++------- lib/mutant/matcher/method/singleton.rb | 43 +++++-------------- lib/mutant/mutator/define.rb | 2 + lib/mutant/reporter/cli.rb | 2 +- .../mutant/method_matching_spec.rb | 18 +++++++- spec/integration/mutant/runner_spec.rb | 6 ++- spec/shared/method_match_behavior.rb | 4 +- .../mutant/mutator/define/mutation_spec.rb | 19 ++++++++ 9 files changed, 91 insertions(+), 81 deletions(-) diff --git a/lib/mutant/matcher/method.rb b/lib/mutant/matcher/method.rb index 2f47e3b8..5e448a3c 100644 --- a/lib/mutant/matcher/method.rb +++ b/lib/mutant/matcher/method.rb @@ -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 diff --git a/lib/mutant/matcher/method/instance.rb b/lib/mutant/matcher/method/instance.rb index 62c1e25b..13a96980 100644 --- a/lib/mutant/matcher/method/instance.rb +++ b/lib/mutant/matcher/method/instance.rb @@ -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 diff --git a/lib/mutant/matcher/method/singleton.rb b/lib/mutant/matcher/method/singleton.rb index 5b353229..c19fbe9c 100644 --- a/lib/mutant/matcher/method/singleton.rb +++ b/lib/mutant/matcher/method/singleton.rb @@ -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 diff --git a/lib/mutant/mutator/define.rb b/lib/mutant/mutator/define.rb index 5ccedeea..8b77684e 100644 --- a/lib/mutant/mutator/define.rb +++ b/lib/mutant/mutator/define.rb @@ -3,6 +3,8 @@ module Mutant class Define < self handle(Rubinius::AST::Define) + handle(Rubinius::AST::DefineSingleton) + handle(Rubinius::AST::DefineSingletonScope) private diff --git a/lib/mutant/reporter/cli.rb b/lib/mutant/reporter/cli.rb index c2327711..c4e9a274 100644 --- a/lib/mutant/reporter/cli.rb +++ b/lib/mutant/reporter/cli.rb @@ -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 diff --git a/spec/integration/mutant/method_matching_spec.rb b/spec/integration/mutant/method_matching_spec.rb index d3ac94dd..90d28332 100644 --- a/spec/integration/mutant/method_matching_spec.rb +++ b/spec/integration/mutant/method_matching_spec.rb @@ -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 diff --git a/spec/integration/mutant/runner_spec.rb b/spec/integration/mutant/runner_spec.rb index 0764988f..42404027 100644 --- a/spec/integration/mutant/runner_spec.rb +++ b/spec/integration/mutant/runner_spec.rb @@ -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 diff --git a/spec/shared/method_match_behavior.rb b/spec/shared/method_match_behavior.rb index dd899220..7257e7ac 100644 --- a/spec/shared/method_match_behavior.rb +++ b/spec/shared/method_match_behavior.rb @@ -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 diff --git a/spec/unit/mutant/mutator/define/mutation_spec.rb b/spec/unit/mutant/mutator/define/mutation_spec.rb index f2edeb4d..b1b819bb 100644 --- a/spec/unit/mutant/mutator/define/mutation_spec.rb +++ b/spec/unit/mutant/mutator/define/mutation_spec.rb @@ -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