From 56fb2c7285653e2a20458e77d7976110242c2c59 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Fri, 14 Jun 2013 18:22:34 +0200 Subject: [PATCH] Port case mutations to parser --- Changelog.md | 4 +- lib/mutant.rb | 2 +- lib/mutant/mutator/node.rb | 14 ++ lib/mutant/mutator/node/case.rb | 47 ++++ lib/mutant/mutator/node/literal/range.rb | 65 ++--- lib/mutant/mutator/node/literal/regex.rb | 31 ++- lib/mutant/mutator/node/receiver_case.rb | 123 ---------- lib/mutant/mutator/node/when.rb | 27 ++- .../mutant/mutator/node/case/mutation_spec.rb | 226 ++++++++++++++++++ .../mutant/mutator/node/literal/regex_spec.rb | 10 +- .../node/receiver_case/mutation_spec.rb | 27 --- 11 files changed, 353 insertions(+), 223 deletions(-) create mode 100644 lib/mutant/mutator/node/case.rb delete mode 100644 lib/mutant/mutator/node/receiver_case.rb create mode 100644 spec/unit/mutant/mutator/node/case/mutation_spec.rb delete mode 100644 spec/unit/mutant/mutator/node/receiver_case/mutation_spec.rb diff --git a/Changelog.md b/Changelog.md index 927f3b4b..151525fc 100644 --- a/Changelog.md +++ b/Changelog.md @@ -116,7 +116,9 @@ Feature: # v0.2.6 2012-12-14 -* [fixed] Correctly set file and line of injected mutants +Bugfix: + +* Correctly set file and line of injected mutants [Compare v0.2.5..v0.2.6](https://github.com/mbj/mutant/compare/v0.2.5...v0.2.6) diff --git a/lib/mutant.rb b/lib/mutant.rb index 75f804a8..52a0c10c 100644 --- a/lib/mutant.rb +++ b/lib/mutant.rb @@ -65,7 +65,7 @@ require 'mutant/mutator/node/define' require 'mutant/mutator/node/return' require 'mutant/mutator/node/iter_19' require 'mutant/mutator/node/if' -require 'mutant/mutator/node/receiver_case' +require 'mutant/mutator/node/case' require 'mutant/config' require 'mutant/loader' require 'mutant/context' diff --git a/lib/mutant/mutator/node.rb b/lib/mutant/mutator/node.rb index bcd4cd29..dee7c788 100644 --- a/lib/mutant/mutator/node.rb +++ b/lib/mutant/mutator/node.rb @@ -73,6 +73,20 @@ module Mutant end end + # Emit delete child mutation + # + # @param [Fixnum] index + # + # @return [undefined] + # + # @api private + # + def delete_child(index) + dup_children = children.dup + dup_children.delete_at(index) + emit_self(*dup_children) + end + # Emit updated child # # @param [Fixnum] index diff --git a/lib/mutant/mutator/node/case.rb b/lib/mutant/mutator/node/case.rb new file mode 100644 index 00000000..755cc8bb --- /dev/null +++ b/lib/mutant/mutator/node/case.rb @@ -0,0 +1,47 @@ +module Mutant + class Mutator + class Node + + # Mutator for case nodes + class Case < self + + handle(:case) + + CONDITION_INDEX = 0 + + private + + # Emit mutations + # + # @return [undefined] + # + # @api private + # + def dispatch + mutate_child(CONDITION_INDEX) + emit_branch_mutations + end + + # Emit presence mutations + # + # @return [undefined] + # + # @api private + # + def emit_branch_mutations + children.each_with_index.drop(1).each do |child, index| + next unless child + mutate_child(index) + dup_children = children.dup + dup_children.delete_at(index) + if dup_children.last.type == :when + dup_children << nil + end + emit_self(*dup_children) + end + end + + end # Case + end # Node + end # Mutator +end # Mutant diff --git a/lib/mutant/mutator/node/literal/range.rb b/lib/mutant/mutator/node/literal/range.rb index 65a0a532..44cf770b 100644 --- a/lib/mutant/mutator/node/literal/range.rb +++ b/lib/mutant/mutator/node/literal/range.rb @@ -7,6 +7,15 @@ module Mutant class Range < self include AbstractType + MAP = { + :irange => :erange, + :erange => :irange + }.freeze + + START_INDEX, END_INDEX = 0, 1 + + handle(*MAP.keys) + private # Emit mutants @@ -17,8 +26,9 @@ module Mutant # def dispatch emit_nil - emit(inverse) - emit_range + emit_inverse + emit_start_mutations + emit_end_mutations end # Return inverse node @@ -27,20 +37,8 @@ module Mutant # # @api private # - def inverse - node = self.node - new(inverse_class, node.start, node.finish) - end - - # Emit range specific mutants - # - # @return [undefined] - # - # @api private - # - def emit_range - emit_finish_mutations - emit_start_mutations + def emit_inverse + emit(s(MAP.fetch(node.type), *children)) end # Emit range start mutations @@ -49,10 +47,10 @@ module Mutant # # @api private # - def emit_finish_mutations - finish = node.finish + def emit_end_mutations + end_ = children[END_INDEX] #emit_self(negative_infinity, finish) - emit_self(nan, finish) + emit_self(NAN, end_) end # Emit start mutations @@ -62,35 +60,12 @@ module Mutant # @api private # def emit_start_mutations - start = node.start - emit_self(start, infinity) - emit_self(start, nan) + start = children[START_INDEX] + emit_self(start, INFINITY) + emit_self(start, NAN) end - # Return inverse AST node class - # - # @return [Class:Parser::AST::Node] - # - # @api private - # - def inverse_class - self.class::INVERSE_CLASS - end - - # Mutator for range exclude literals - class Exclude < self - INVERSE_TYPE = :irange - handle(:erange) - end # Exclude - - # Mutator for range include literals - class Include < self - INVERSE_TYPE = :erange - handle(:irange) - end # Include - end # Range - end # Literal end # Node end # Mutator diff --git a/lib/mutant/mutator/node/literal/regex.rb b/lib/mutant/mutator/node/literal/regex.rb index b7aa8c50..a69a22ca 100644 --- a/lib/mutant/mutator/node/literal/regex.rb +++ b/lib/mutant/mutator/node/literal/regex.rb @@ -7,6 +7,13 @@ module Mutant handle(:regexp) + EMPTY_STRING = ''.freeze + + # No input can ever be matched with this + NULL_REGEXP_SOURCE = 'a\A'.freeze + + SOURCE_INDEX, OPTIONS_INDEX = 0, 1 + private # Emit mutants @@ -17,24 +24,16 @@ module Mutant # def dispatch emit_nil - emit_self('') # match all - emit_self('a\A') # match nothing - emit_new { new_self(Random.hex_string) } + emit_self(s(:str, EMPTY_STRING), options) + emit_self(s(:str, NULL_REGEXP_SOURCE), options) end - # Return new regexp node - # - # @param [String] source - # - # @param [Integer] options - # options of regexp, defaults to mutation subject node options - # - # @return [undefined] - # - # @api private - # - def new_self(source,options=nil) - super(source,options || node.options) + def options + children[OPTIONS_INDEX] + end + + def source + children[SOURCE_INDEX] end end # Regex diff --git a/lib/mutant/mutator/node/receiver_case.rb b/lib/mutant/mutator/node/receiver_case.rb deleted file mode 100644 index ff6a51e3..00000000 --- a/lib/mutant/mutator/node/receiver_case.rb +++ /dev/null @@ -1,123 +0,0 @@ -module Mutant - class Mutator - class Node - - # Mutator for case nodes - class ReceiverCase < self - - handle(:case) - - private - - # Emit mutations - # - # @return [undefined] - # - # @api private - # - def dispatch - emit_attribute_mutations(:receiver) - emit_when_branch_presence_mutations - emit_else_branch_presence_mutation - emit_when_branch_mutations - end - - # Emit else branch presence mutation - # - # @return [undefined] - # - # @api private - # - def emit_else_branch_presence_mutation - emit_self(receiver, when_branches, nil) - end - - # Emit when branch body mutations - # - # @return [undefined] - # - # @api private - # - def emit_when_branch_mutations - when_branches.each_with_index do |branch, index| - Mutator.each(branch) do |mutant| - branches = dup_when_branches - branches[index]=mutant - emit_self(receiver, branches, else_branch) - end - end - end - - # Emit when branch presence mutations - # - # @return [undefined] - # - # @api private - # - def emit_when_branch_presence_mutations - return if one? - when_branches.each_index do |index| - dup_branches = dup_when_branches - dup_branches.delete_at(index) - emit_self(receiver, dup_branches, else_branch) - end - end - - # Check for case there is only one when branch - # - # @return [true] - # returns true when there is only one when branch - # - # @return [false] - # returns false otherwise - # - # @api private - # - def one? - when_branches.one? - end - - # Return duplicate of when branches - # - # @return [Array] - # - # @api private - # - def dup_when_branches - when_branches.dup - end - - # Return when branches - # - # @return [Array] - # - # @api private - # - def when_branches - node.whens - end - - # Return receiver - # - # @return [Parser::AST::Node] - # - # @api private - # - def receiver - node.receiver - end - - # Return else branch - # - # @return [Parser::AST::Node] - # - # @api private - # - def else_branch - node.else - end - - end # ReceiverCase - end # Node - end # Mutator -end # Mutant diff --git a/lib/mutant/mutator/node/when.rb b/lib/mutant/mutator/node/when.rb index e53c702a..56ac8ccd 100644 --- a/lib/mutant/mutator/node/when.rb +++ b/lib/mutant/mutator/node/when.rb @@ -16,7 +16,32 @@ module Mutant # @api private # def dispatch - emit_attribute_mutations(:body) + mutate_body + mutate_conditions + end + + # Emit condition mutations + # + # @return [undefined] + # + # @api private + # + def mutate_conditions + conditions = children.length - 1 + children[0..-2].each_index do |index| + delete_child(index) if conditions > 1 + mutate_child(index) + end + end + + # Emit body mutations + # + # @return [undefined] + # + # @api private + # + def mutate_body + mutate_child(children.length-1) end end # When diff --git a/spec/unit/mutant/mutator/node/case/mutation_spec.rb b/spec/unit/mutant/mutator/node/case/mutation_spec.rb new file mode 100644 index 00000000..34acc5a0 --- /dev/null +++ b/spec/unit/mutant/mutator/node/case/mutation_spec.rb @@ -0,0 +1,226 @@ +require 'spec_helper' + +describe Mutant::Mutator::Node::Case do + let(:random_string) { 'random' } + + let(:source) { ':foo' } + + let(:mutations) do + %w(nil) << ":s#{random_string}" + end + + before do + Mutant::Random.stub(:hex_string => random_string) + end + + let(:source) do + <<-RUBY + case :condition + when :foo + :foo + when :bar, :baz + :barbaz + else + :else + end + RUBY + end + + let(:mutations) do + mutations = [] + + # Presence of branches + mutations << <<-RUBY + case :condition + when :bar, :baz + :barbaz + else + :else + end + RUBY + mutations << <<-RUBY + case :condition + when :foo + :foo + else + :else + end + RUBY + mutations << <<-RUBY + case :condition + when :foo + :foo + when :bar, :baz + :barbaz + end + RUBY + + # Mutations of condition + mutations << <<-RUBY + case nil + when :foo + :foo + when :bar, :baz + :barbaz + else + :else + end + RUBY + mutations << <<-RUBY + case :srandom + when :foo + :foo + when :bar, :baz + :barbaz + else + :else + end + RUBY + + # Mutations of branch bodies + mutations << <<-RUBY + case :condition + when :foo + nil + when :bar, :baz + :barbaz + else + :else + end + RUBY + mutations << <<-RUBY + case :condition + when :foo + :srandom + when :bar, :baz + :barbaz + else + :else + end + RUBY + mutations << <<-RUBY + case :condition + when :foo + :foo + when :bar, :baz + :srandom + else + :else + end + RUBY + mutations << <<-RUBY + case :condition + when :foo + :foo + when :bar, :baz + nil + else + :else + end + RUBY + mutations << <<-RUBY + case :condition + when :foo + :foo + when :bar, :baz + :barbaz + else + :srandom + end + RUBY + mutations << <<-RUBY + case :condition + when :foo + :foo + when :bar, :baz + :barbaz + else + nil + end + RUBY + + # Mutations of when conditions + mutations << <<-RUBY + case :condition + when :srandom + :foo + when :bar, :baz + :barbaz + else + :else + end + RUBY + mutations << <<-RUBY + case :condition + when nil + :foo + when :bar, :baz + :barbaz + else + :else + end + RUBY + mutations << <<-RUBY + case :condition + when :foo + :foo + when :srandom, :baz + :barbaz + else + :else + end + RUBY + mutations << <<-RUBY + case :condition + when :foo + :foo + when nil, :baz + :barbaz + else + :else + end + RUBY + mutations << <<-RUBY + case :condition + when :foo + :foo + when :bar, nil + :barbaz + else + :else + end + RUBY + mutations << <<-RUBY + case :condition + when :foo + :foo + when :bar, :srandom + :barbaz + else + :else + end + RUBY + mutations << <<-RUBY + case :condition + when :foo + :foo + when :baz + :barbaz + else + :else + end + RUBY + mutations << <<-RUBY + case :condition + when :foo + :foo + when :bar + :barbaz + else + :else + end + RUBY + end + + it_should_behave_like 'a mutator' +end diff --git a/spec/unit/mutant/mutator/node/literal/regex_spec.rb b/spec/unit/mutant/mutator/node/literal/regex_spec.rb index ab1d27db..36b08123 100644 --- a/spec/unit/mutant/mutator/node/literal/regex_spec.rb +++ b/spec/unit/mutant/mutator/node/literal/regex_spec.rb @@ -1,23 +1,15 @@ require 'spec_helper' describe Mutant::Mutator::Node::Literal, 'regex' do - let(:random_string) { 'bar' } let(:source) { '/foo/' } - let(:base_mutations) do + let(:mutations) do mutations = [] mutations << 'nil' - mutations << "/#{random_string}/" mutations << '//' # match all mutations << '/a\A/' # match nothing end - before do - Mutant::Random.stub(:hex_string => random_string) - end - - let(:mutations) { base_mutations } - it_should_behave_like 'a mutator' end diff --git a/spec/unit/mutant/mutator/node/receiver_case/mutation_spec.rb b/spec/unit/mutant/mutator/node/receiver_case/mutation_spec.rb deleted file mode 100644 index 5240f4ed..00000000 --- a/spec/unit/mutant/mutator/node/receiver_case/mutation_spec.rb +++ /dev/null @@ -1,27 +0,0 @@ -require 'spec_helper' - -describe Mutant::Mutator::Node::ReceiverCase do - let(:source) { 'case self.condition; when true; true; when false; false; else raise; end' } - - let(:mutations) do - mutations = [] - - # Delete each when once - mutations << 'case self.condition; when true; true; else raise; end' - mutations << 'case self.condition; when false; false; else raise; end' - - # Mutate receiver - mutations << 'case condition; when true; true; when false; false; else raise; end' - - # Remove else branch - mutations << 'case self.condition; when true; true; when false; false; end' - - # Mutate when branch bodies - mutations << 'case self.condition; when true; nil; when false; false; else raise; end' - mutations << 'case self.condition; when true; false; when false; false; else raise; end' - mutations << 'case self.condition; when true; true; when false; nil; else raise; end' - mutations << 'case self.condition; when true; true; when false; true; else raise; end' - end - - it_should_behave_like 'a mutator' -end