Port if node mutator to parser

This commit is contained in:
Markus Schirp 2013-06-10 10:15:59 +02:00
parent 8e2f244838
commit c5cdc05e5e
6 changed files with 126 additions and 276 deletions

View file

@ -43,7 +43,7 @@ module Mutant
#
def emit_children_mutations
Mutator::Util::Array.each(children) do |children|
emit_self(children)
emit_self(*children)
end
end
@ -85,7 +85,7 @@ module Mutant
def emit_child_update(index, update)
new_children = children.dup
new_children[index]=update
emit_self(new_children)
emit_self(*new_children)
end
# Emit a new AST node with same class as wrapped node
@ -94,8 +94,8 @@ module Mutant
#
# @api private
#
def emit_self(children)
emit(Parser::AST::Node.new(node.type, children))
def emit_self(*children)
emit(new_self(*children))
end
# Emit a new AST node with NilLiteral class
@ -108,6 +108,16 @@ module Mutant
emit(s(:nil))
end
# Return new self typed child
#
# @return [Parser::AST::Node]
#
# @api private
#
def new_self(*children)
Parser::AST::Node.new(node.type, children)
end
end # Node
end # Mutator
end # Mutant

View file

@ -6,123 +6,88 @@ module Mutant
handle(:if)
CONDITION_INDEX = 0
IF_BRANCH_INDEX = 1
ELSE_BRANCH_INDEX = 2
private
# Emit mutations
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
emit_attribute_mutations(:condition)
emit_attribute_mutations(:body) unless nil_literal?(:body)
emit_attribute_mutations(:else) unless nil_literal?(:else)
emit_inverted_condition
emit_deleted_if_branch
emit_deleted_else_branch
emit_true_if_branch
emit_false_if_branch
mutate_condition
mutate_if_branch
mutate_else_branch
end
# Test if attribute is non nil literal
#
# @param [Symbol] name
#
# @return [true]
# if attribute value a nil literal
#
# @return [false]
# otherwise
#
# @api private
#
def nil_literal?(name)
node.public_send(name).kind_of?(Rubinius::AST::NilLiteral)
end
# Emit inverted condition
#
# Especially the same like swap branches but more universal as it also
# covers the case there is no else branch
# Emit conditon mutations
#
# @return [undefined]
#
# @api private
#
def emit_inverted_condition
emit_self(new_send(condition, :'!'), if_branch, else_branch)
def mutate_condition
mutate_child(CONDITION_INDEX)
emit_self(s(:send, condition, :!), if_branch, else_branch)
emit_self(s(:true), if_branch, else_branch)
emit_self(s(:false), if_branch, else_branch)
end
# Emit deleted else branch
# Emit if branch mutations
#
# @return [undefined]
#
# @api private
#
def emit_deleted_else_branch
emit_self(condition, if_branch, nil)
def mutate_if_branch
mutate_child(IF_BRANCH_INDEX) if if_branch
emit_self(condition, else_branch, nil)
emit_self(condition, if_branch, nil)
end
# Emit deleted if branch
# Emit else branch mutations
#
# @return [undefined]
#
# @api private
#
def emit_deleted_if_branch
body = else_branch || return
emit_self(condition, body, nil)
def mutate_else_branch
mutate_child(ELSE_BRANCH_INDEX)
emit_self(condition, s(:nil), else_branch)
end
# Emit true if branch
#
# @return [undefined]
#
# @api private
#
def emit_true_if_branch
emit_self(new(Rubinius::AST::TrueLiteral), if_branch, else_branch)
end
# Emit false if branch
#
# @return [undefined]
#
# @api private
#
def emit_false_if_branch
emit_self(new(Rubinius::AST::FalseLiteral), if_branch, else_branch)
end
# Return if_branch of node
#
# @return [Parser::AST::Node]
#
# @api private
#
def if_branch
node.body
end
# Return condition of node
# Return condition node
#
# @return [Parser::AST::Node]
#
# @api private
#
def condition
node.condition
children[CONDITION_INDEX]
end
# Return else body of node
# Return if branch
#
# @return [Parser::AST::Node]
#
# @api private
#
def if_branch
children[IF_BRANCH_INDEX]
end
# Return else branch
#
# @return [Parser::AST::Node]
#
# @api private
#
def else_branch
node.else
children[ELSE_BRANCH_INDEX]
end
end # If

View file

@ -7,18 +7,6 @@ module Mutant
private
# Return new float literal
#
# @param [#to_f] value
#
# @return [Parser::Node::FloatLiteral]
#
# @api private
#
def new_float(value)
new(Parser::AST::FloatLiteral, value)
end
# Emit a new node with wrapping class for each entry in values
#
# @param [Array] values

View file

@ -16,124 +16,6 @@ module Mutant
# @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
emit_attribute_mutations(:block) if node.block
end
# Emit receiver mutations
#
# @return [undefined]
#
# @api private
#
def emit_receiver_mutations
emit_attribute_mutations(:receiver)
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 [Parser::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
unless to_self? and !Mutant::KEYWORDS.include?(node.name)
return
end
mutant = dup_node
mutant.privately = true
emit(mutant)
end
end # Send

View file

@ -0,0 +1,75 @@
require 'spec_helper'
describe Mutant::Mutator, 'if' do
before do
Mutant::Random.stub(:hex_string => 'random')
end
context 'with if and else branches' do
let(:source) { 'if :condition; true; else false; end' }
let(:mutations) do
mutants = []
# mutations of condition
mutants << 'if :srandom; true; else false; end'
mutants << 'if !:condition; true; else false; end'
mutants << 'if nil; true; else false; end'
mutants << 'if true; true; else false; end'
mutants << 'if false; true; else false; end'
# Deleted else branch
mutants << 'if :condition; true end'
# Deleted if branch resuting in unless
mutants << 'unless :condition; false; end'
# Deleted if branch with promoting else branch to if branch
mutants << 'if :condition; false end'
# mutations of if body
mutants << 'if :condition; false; else false; end'
mutants << 'if :condition; nil; else false; end'
# mutations of else body
mutants << 'if :condition; true; else true; end'
mutants << 'if :condition; true; else nil; end'
end
it_should_behave_like 'a mutator'
end
context 'unless with one branch' do
let(:source) { 'unless :condition; true; end' }
let(:mutations) do
mutants = []
mutants << 'unless !:condition; true; end'
mutants << 'unless :srandom; true; end'
mutants << 'unless nil; true; end'
mutants << 'if :condition; true; end'
mutants << 'unless :condition; false; end'
mutants << 'unless :condition; nil; end'
mutants << 'unless true; true; end'
mutants << 'unless false; true; end'
end
it_should_behave_like 'a mutator'
end
#context 'if with one branch' do
# let(:source) { 'if condition; true; end' }
# let(:mutations) do
# mutants = []
# mutants << 'if !condition; true; end'
# mutants << 'if condition; false; end'
# mutants << 'if condition; nil; end'
# mutants << 'if true; true; end'
# mutants << 'if false; true; end'
# end
# it_should_behave_like 'a mutator'
#end
end

View file

@ -1,70 +0,0 @@
require 'spec_helper'
describe Mutant::Mutator, 'if statement' do
context 'if with two branches' do
let(:source) { 'if self.condition; true; else false; end' }
let(:mutations) do
mutants = []
# mutations of condition
mutants << 'if condition; true; else false; end'
mutants << 'if !self.condition; true; else false; end'
# Deleted else branch
mutants << 'if self.condition; true end'
# Deleted if branch with promoting else branch to if branch
mutants << 'if self.condition; false end'
# mutations of body
mutants << 'if self.condition; false; else false; end'
mutants << 'if self.condition; nil; else false; end'
# mutations of else body
mutants << 'if self.condition; true; else true; end'
mutants << 'if self.condition; true; else nil; end'
# mutation of condition to always be true
mutants << 'if true; true; else false; end'
# mutation of condition to always be false
mutants << 'if false; true; else false; end'
end
it_should_behave_like 'a mutator'
end
context 'unless with one branch' do
let(:source) { 'unless condition; true; end' }
let(:mutations) do
mutants = []
mutants << 'unless !condition; true; end'
mutants << 'if condition; true; end'
mutants << 'unless condition; false; end'
mutants << 'unless condition; nil; end'
mutants << 'unless true; true; end'
mutants << 'unless false; true; end'
end
it_should_behave_like 'a mutator'
end
context 'if with one branch' do
let(:source) { 'if condition; true; end' }
let(:mutations) do
mutants = []
mutants << 'if !condition; true; end'
mutants << 'if condition; false; end'
mutants << 'if condition; nil; end'
mutants << 'if true; true; end'
mutants << 'if false; true; end'
end
it_should_behave_like 'a mutator'
end
end