269 lines
6.1 KiB
Ruby
269 lines
6.1 KiB
Ruby
module Mutant
|
|
class Mutator
|
|
class Node
|
|
# Class for mutations where messages are send to objects
|
|
class Send < self
|
|
|
|
handle(Rubinius::AST::Send)
|
|
|
|
# Test if node corresponds to "self.class"
|
|
#
|
|
# @param [Rubinius::AST::Node] node
|
|
#
|
|
# @return [true]
|
|
# if node equals to self.class
|
|
#
|
|
# @return [false]
|
|
# otherwise
|
|
#
|
|
# @api private
|
|
#
|
|
def self.keyword_name?(node)
|
|
node.kind_of?(Rubinius::AST::Send) &&
|
|
Mutant::KEYWORDS.include?(node.name) &&
|
|
node.receiver.kind_of?(Rubinius::AST::Self)
|
|
end
|
|
|
|
private
|
|
|
|
# Emit mutations
|
|
#
|
|
# @return [undefined]
|
|
#
|
|
# @api private
|
|
#
|
|
def dispatch
|
|
emit_receiver
|
|
emit_implicit_self_receiver
|
|
emit_receiver_mutations
|
|
emit_block_mutations
|
|
emit_block_absence_mutation
|
|
end
|
|
|
|
# Emit receiver
|
|
#
|
|
# @return [undefined]
|
|
#
|
|
# @api private
|
|
#
|
|
def emit_receiver
|
|
unless to_self?
|
|
emit(receiver)
|
|
end
|
|
end
|
|
|
|
# Emit block mutations
|
|
#
|
|
# @return [undefined]
|
|
#
|
|
# @api private
|
|
#
|
|
def emit_block_mutations
|
|
if node.block
|
|
emit_attribute_mutations(:block)
|
|
end
|
|
end
|
|
|
|
# Emit receiver mutations
|
|
#
|
|
# @return [undefined]
|
|
#
|
|
# @api private
|
|
#
|
|
def emit_receiver_mutations
|
|
util = self.class
|
|
|
|
unless to_self? or util.keyword_name?(receiver)
|
|
emit_attribute_mutations(:receiver)
|
|
end
|
|
end
|
|
|
|
# Emit block absence mutation
|
|
#
|
|
# @return [undefined]
|
|
#
|
|
# @api private
|
|
#
|
|
def emit_block_absence_mutation
|
|
dup = dup_node
|
|
dup.block = nil
|
|
emit(dup)
|
|
end
|
|
|
|
# Return receiver AST node
|
|
#
|
|
# @return [Rubinius::AST::Node]
|
|
#
|
|
# @api private
|
|
#
|
|
def receiver
|
|
node.receiver
|
|
end
|
|
|
|
# Return name of call
|
|
#
|
|
# @return [Symbol]
|
|
#
|
|
# @api private
|
|
#
|
|
def name
|
|
node.name
|
|
end
|
|
|
|
# Check if receiver is self
|
|
#
|
|
# @return [true]
|
|
# if receiver is a Rubinius::AST::Self node
|
|
#
|
|
# @return [false]
|
|
# return false otherwise
|
|
#
|
|
# @api private
|
|
#
|
|
def to_self?
|
|
receiver.kind_of?(Rubinius::AST::Self)
|
|
end
|
|
|
|
# Emit mutation that replaces explicit send to self with implicit send to self
|
|
#
|
|
# @example:
|
|
#
|
|
# # This class does use Foo#a with explicitly specifing the receiver self.
|
|
# # But an implicit (privately) call should be used as there is no need for
|
|
# # specifing en explicit receiver.
|
|
#
|
|
# class Foo # Mutation
|
|
# def print_a # def print_a
|
|
# puts self.a # puts a
|
|
# end # end
|
|
#
|
|
# def a
|
|
# :bar
|
|
# end
|
|
# end
|
|
#
|
|
# There will not be any exception so the mutant is not killed and such calls where
|
|
# implicit receiver should be used will be spotted.
|
|
#
|
|
# @return [undefined]
|
|
#
|
|
# @api private
|
|
#
|
|
def emit_implicit_self_receiver
|
|
return unless to_self?
|
|
return if self.class.keyword_name?(node)
|
|
mutant = dup_node
|
|
mutant.privately = true
|
|
# TODO: Fix rubinius to allow this as an attr_accessor
|
|
mutant.instance_variable_set(:@vcall_style, true)
|
|
emit(mutant)
|
|
end
|
|
|
|
class SendWithArguments < self
|
|
|
|
handle(Rubinius::AST::SendWithArguments)
|
|
|
|
class BinaryOperatorMethod < Node
|
|
|
|
private
|
|
|
|
# Emit mutations
|
|
#
|
|
# @return [undefined]
|
|
#
|
|
# @api private
|
|
#
|
|
def dispatch
|
|
emit_left_mutations
|
|
emit_right_mutations
|
|
end
|
|
|
|
# Emit left mutations
|
|
#
|
|
# @return [undefined]
|
|
#
|
|
# @api private
|
|
#
|
|
def emit_left_mutations
|
|
emit_attribute_mutations(:receiver)
|
|
end
|
|
|
|
# Emit right mutations
|
|
#
|
|
# @return [undefined]
|
|
#
|
|
# @api private
|
|
#
|
|
def emit_right_mutations
|
|
right = node.arguments.array.first
|
|
Mutator.each(right).each do |mutated|
|
|
dup = dup_node
|
|
dup.arguments.array[0] = mutated
|
|
emit(dup)
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
private
|
|
|
|
# Emit mutations
|
|
#
|
|
# @return [undefined]
|
|
#
|
|
# @api private
|
|
#
|
|
def dispatch
|
|
super
|
|
emit_call_remove_mutation
|
|
emit_argument_mutations
|
|
end
|
|
|
|
# Test if message is a binary operator
|
|
#
|
|
# @return [true]
|
|
# if message is a binary operator
|
|
#
|
|
# @return [false]
|
|
# otherwise
|
|
#
|
|
# @api private
|
|
#
|
|
def binary_operator?
|
|
Mutant::BINARY_METHOD_OPERATORS.include?(node.name)
|
|
end
|
|
|
|
# Emit argument mutations
|
|
#
|
|
# @api private
|
|
#
|
|
# @return [undefined]
|
|
#
|
|
# @api private
|
|
#
|
|
def emit_argument_mutations
|
|
if binary_operator?
|
|
run(BinaryOperatorMethod)
|
|
return
|
|
end
|
|
|
|
emit_attribute_mutations(:arguments)
|
|
end
|
|
|
|
# Emit transfomr call mutation
|
|
#
|
|
# @return [undefined]
|
|
#
|
|
# @api private
|
|
#
|
|
def emit_call_remove_mutation
|
|
array = node.arguments.array
|
|
return unless array.length == 1
|
|
emit(array.first)
|
|
end
|
|
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|