Port case mutations to parser
This commit is contained in:
parent
d4eb7743ba
commit
56fb2c7285
11 changed files with 353 additions and 223 deletions
|
@ -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)
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
47
lib/mutant/mutator/node/case.rb
Normal file
47
lib/mutant/mutator/node/case.rb
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
226
spec/unit/mutant/mutator/node/case/mutation_spec.rb
Normal file
226
spec/unit/mutant/mutator/node/case/mutation_spec.rb
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
Loading…
Reference in a new issue