diff --git a/lib/mutant/constants.rb b/lib/mutant/constants.rb index 9be1e21b..ef4cf6c4 100644 --- a/lib/mutant/constants.rb +++ b/lib/mutant/constants.rb @@ -6,7 +6,7 @@ module Mutant '!' => '_bang' }.freeze - BINARY_METHOD_OPERATOR_EXPANSIONS = { + OPERATOR_EXPANSIONS = { :<=> => :spaceship_operator, :=== => :case_equality_operator, :[]= => :element_writer, @@ -29,19 +29,20 @@ module Mutant :< => :less_than_operator, :> => :greater_than_operator, :+ => :addition_operator, - :- => :substraction_operator - }.freeze - - UNARY_METHOD_OPERATOR_EXPANSIONS = { + :- => :substraction_operator, :~@ => :unary_match_operator, :+@ => :unary_addition_operator, :-@ => :unary_substraction_operator, :'!' => :negation_operator }.freeze - BINARY_METHOD_OPERATORS = BINARY_METHOD_OPERATOR_EXPANSIONS.keys.to_set.freeze + INDEX_OPERATORS = [ :[], :[]= ].freeze - OPERATOR_EXPANSIONS = BINARY_METHOD_OPERATOR_EXPANSIONS.merge(UNARY_METHOD_OPERATOR_EXPANSIONS).freeze + UNARY_METHOD_OPERATORS = [ + :~@, :+@, :-@, :'!' + ].freeze + + BINARY_METHOD_OPERATORS = (OPERATOR_EXPANSIONS.keys - (INDEX_OPERATORS + UNARY_METHOD_OPERATORS)).to_set.freeze # Hopefully all types parser does generate NODE_TYPES = [ diff --git a/lib/mutant/mutator.rb b/lib/mutant/mutator.rb index 2864fb5e..af651343 100644 --- a/lib/mutant/mutator.rb +++ b/lib/mutant/mutator.rb @@ -12,9 +12,9 @@ module Mutant # # @api private # - def self.each(node, &block) - return to_enum(__method__, node) unless block_given? - Registry.lookup(node).new(node, block) + def self.each(node, parent=nil, &block) + return to_enum(__method__, node, parent) unless block_given? + Registry.lookup(node).new(node, parent, block) self end @@ -52,19 +52,28 @@ module Mutant # attr_reader :input + # Return input + # + # @return [Object] + # + # @api private + # + attr_reader :parent + private # Initialize object # # @param [Object] input + # @param [Object] parent # @param [#call(node)] block # # @return [undefined] # # @api private # - def initialize(input, block) - @input, @block = IceNine.deep_freeze(input), block + def initialize(input, parent, block) + @input, @parent, @block = input, parent, block @seen = Set.new guard(input) dispatch @@ -173,7 +182,7 @@ module Mutant # @api private # def run(mutator) - mutator.new(input, method(:emit)) + mutator.new(input, self, method(:emit)) end # Shortcut to create a new unfrozen duplicate of input diff --git a/lib/mutant/mutator/node.rb b/lib/mutant/mutator/node.rb index 47f3143d..ba229999 100644 --- a/lib/mutant/mutator/node.rb +++ b/lib/mutant/mutator/node.rb @@ -99,7 +99,7 @@ module Mutant # @api private # def emit_children_mutations - Mutator::Util::Array.each(children) do |children| + Mutator::Util::Array.each(children, self) do |children| emit_self(*children) end end @@ -125,7 +125,7 @@ module Mutant def mutate_child(index, mutator = Mutator) children = node.children child = children[index] - mutator.each(child) do |mutation| + mutator.each(child, self) do |mutation| emit_child_update(index, mutation) end end diff --git a/lib/mutant/mutator/node/argument.rb b/lib/mutant/mutator/node/argument.rb index d8f25b18..f0c1f439 100644 --- a/lib/mutant/mutator/node/argument.rb +++ b/lib/mutant/mutator/node/argument.rb @@ -30,7 +30,7 @@ module Mutant # @api private # def emit_name_mutation - Mutator::Util::Symbol.each(name) do |name| + Mutator::Util::Symbol.each(name, self) do |name| emit_name(name) end end diff --git a/lib/mutant/mutator/node/assignment.rb b/lib/mutant/mutator/node/assignment.rb index fc7ade97..14365a4a 100644 --- a/lib/mutant/mutator/node/assignment.rb +++ b/lib/mutant/mutator/node/assignment.rb @@ -39,7 +39,7 @@ module Mutant # def mutate_name prefix = MAP.fetch(node.type) - Mutator::Util::Symbol.each(name) do |name| + Mutator::Util::Symbol.each(name, self) do |name| emit_name("#{prefix}#{name}") end end diff --git a/lib/mutant/mutator/node/begin.rb b/lib/mutant/mutator/node/begin.rb index 4d54b891..42817fef 100644 --- a/lib/mutant/mutator/node/begin.rb +++ b/lib/mutant/mutator/node/begin.rb @@ -16,7 +16,7 @@ module Mutant # @api private # def dispatch - Util::Array.each(children) do |children| + Util::Array.each(children, self) do |children| if children.length > 1 emit_self(*children) end @@ -24,7 +24,21 @@ module Mutant children.each do |child| emit(child) end - emit(nil) + emit(nil) unless parent_send? + end + + # Test if parent input is a send + # + # @return [true] + # if parent input is a send node + # + # @return [false] + # otherwise + # + # @api private + # + def parent_send? + parent && parent.input.type == :send end end # Block diff --git a/lib/mutant/mutator/util.rb b/lib/mutant/mutator/util.rb index 14fae2a1..738663f8 100644 --- a/lib/mutant/mutator/util.rb +++ b/lib/mutant/mutator/util.rb @@ -6,6 +6,7 @@ module Mutant # Run ulitity mutator # # @param [Object] object + # @param [Object] parent # # @return [Enumerator] # if no block given @@ -15,10 +16,10 @@ module Mutant # # @api private # - def self.each(object, &block) - return to_enum(__method__, object) unless block_given? + def self.each(object, parent, &block) + return to_enum(__method__, object, parent) unless block_given? - new(object, block) + new(object, parent, block) self end diff --git a/spec/unit/mutant/mutator/emit_new_spec.rb b/spec/unit/mutant/mutator/emit_new_spec.rb index 6f6b1394..09e23b34 100644 --- a/spec/unit/mutant/mutator/emit_new_spec.rb +++ b/spec/unit/mutant/mutator/emit_new_spec.rb @@ -15,9 +15,10 @@ describe Mutant::Mutator, '#emit_new' do end end - let(:object) { class_under_test.new(input, block) } - let(:block) { Block.new } - let(:input) { :input } + let(:object) { class_under_test.new(input, parent, block) } + let(:block) { Block.new } + let(:input) { :input } + let(:parent) { :parent } let(:class_under_test) do Class.new(described_class) do diff --git a/spec/unit/mutant/mutator/emit_spec.rb b/spec/unit/mutant/mutator/emit_spec.rb index 15932904..5d0b7713 100644 --- a/spec/unit/mutant/mutator/emit_spec.rb +++ b/spec/unit/mutant/mutator/emit_spec.rb @@ -15,9 +15,10 @@ describe Mutant::Mutator, '#emit' do end end - let(:object) { class_under_test.new(input, block) } - let(:block) { Block.new } - let(:input) { :nput } + let(:object) { class_under_test.new(input, parent, block) } + let(:block) { Block.new } + let(:input) { :input } + let(:parent) { :parent } let(:class_under_test) do Class.new(described_class) do diff --git a/spec/unit/mutant/mutator/node/send/mutation_spec.rb b/spec/unit/mutant/mutator/node/send/mutation_spec.rb index 4734f670..ed760b8a 100644 --- a/spec/unit/mutant/mutator/node/send/mutation_spec.rb +++ b/spec/unit/mutant/mutator/node/send/mutation_spec.rb @@ -168,21 +168,46 @@ describe Mutant::Mutator, 'send' do end context 'binary operator methods' do - Mutant::BINARY_METHOD_OPERATORS.each do |operator| - let(:source) { "true #{operator} false" } + context 'nested' do + let(:source) { '(left - right) / foo' } let(:mutations) do mutations = [] - mutations << "false #{operator} false" - mutations << "nil #{operator} false" - mutations << "true #{operator} true" - mutations << "true #{operator} nil" - mutations << 'true' - mutations << 'false' + mutations << 'foo' + mutations << 'left - right' end it_should_behave_like 'a mutator' end + + Mutant::BINARY_METHOD_OPERATORS.each do |operator| + context 'on literal scalar arguments' do + let(:source) { "true #{operator} false" } + + let(:mutations) do + mutations = [] + mutations << "false #{operator} false" + mutations << "nil #{operator} false" + mutations << "true #{operator} true" + mutations << "true #{operator} nil" + mutations << 'true' + mutations << 'false' + end + + it_should_behave_like 'a mutator' + end + + context 'on non literal scalar arguments' do + let(:source) { "left #{operator} right" } + let(:mutations) do + mutations = [] + mutations << 'left' + mutations << 'right' + end + + it_should_behave_like 'a mutator' + end + end end end end