Break up mutation spec in class specific files

* Also add mutation class registry to improve readability.
* Calling mutations are still broken.
This commit is contained in:
Markus Schirp 2012-08-01 13:27:35 +02:00
parent 55c61dfc28
commit b9b95ebe94
24 changed files with 545 additions and 417 deletions

1
.rspec
View file

@ -1,3 +1,2 @@
--color
--backtrace
#--fail-fast

View file

@ -5,6 +5,9 @@ gemspec
# For Veritas::Immutable, will be extracted soon
gem 'veritas', :git => 'https://github.com/dkubb/veritas'
# Until there is a release with explicit sends to self fix
gem 'to_source', :git => 'https://github.com/mbj/to_source'
group :development do
gem 'rake', '~> 0.9.2'
gem 'yard', '~> 0.8.1'

View file

@ -24,6 +24,7 @@ end
require 'mutant/support/abstract'
require 'mutant/random'
require 'mutant/mutator'
require 'mutant/mutator/registry'
require 'mutant/mutator/literal'
require 'mutant/mutator/literal/boolean'
require 'mutant/mutator/literal/range'
@ -38,9 +39,10 @@ require 'mutant/mutator/literal/regex'
#require 'mutant/mutator/literal/dynamic'
require 'mutant/mutator/block'
require 'mutant/mutator/self'
require 'mutant/mutator/send'
require 'mutant/mutator/send_with_arguments'
require 'mutant/loader'
require 'mutant/mutator/call'
require 'mutant/mutator/call/send'
require 'mutant/mutator/call/send_with_arguments'
require 'mutant/loader'
require 'mutant/context'
require 'mutant/context/constant'
require 'mutant/mutatee'

View file

@ -15,29 +15,11 @@ module Mutant
#
def self.each(node, &block)
return to_enum(__method__, node) unless block_given?
mutator(node).new(node, block)
Registry.lookup(node.class).new(node, block)
self
end
# Return mutator for node or raise
#
# @param [Rubinius::AST::Node] node
#
# @return [Mutator]
#
# @raise [NameError]
# raises NameError if mutator for node cannot be found
#
# @api private
#
def self.mutator(node)
Mutator.map.fetch(node.class) do
raise ArgumentError,"No mutator to handle: #{node.inspect}"
end
end
private_class_method :mutator
# Register node class handler
#
# @param [Class:Rubinius::AST::Node] node_class
@ -47,20 +29,10 @@ module Mutant
# @api private
#
def self.handle(node_class)
Mutator.map[node_class]=self
Registry.register(node_class,self)
end
private_class_method :handle
# Return map of rubinus AST nodes to mutators
#
# @return [Hash]
#
# @api private
#
def self.map
@map ||= {}
end
private
# Initialize generator
@ -291,7 +263,6 @@ module Mutant
emit_safe(new_nil)
end
# Return AST representing send
#
# @param [Rubinius::AST::Node] receiver
@ -306,6 +277,16 @@ module Mutant
new(Rubinius::AST::SendWithArguments, receiver, name, arguments)
end
# Return duplicated (unfrozen) node each call
#
# @return [Rubinius::AST::Node]
#
# @api private
#
def dup_node
node.dup
end
memoize :sexp
end
end

View file

@ -0,0 +1,42 @@
module Mutant
class Mutator
# Abstract class for mutatiosn where messages are send
class Call < Mutator
private
# 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]
# returns true when receiver is a Rubinius::AST::Self node
#
# @return [false]
# return false otherwise
#
def self?
receiver.kind_of?(Rubinius::AST::Self)
end
end
end
end

View file

@ -0,0 +1,34 @@
module Mutant
class Mutator
class Call < Mutator
# Mutator for Rubinius::AST::Send
class Send < Call
handle(Rubinius::AST::Send)
private
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
emit_explicit_receiver
end
# Emit a private vcall mutation
#
# Transforms a call on self with implict receiver into one with
# explcit receiver.
#
# foo(1) => self.foo(1)
#
def emit_explicit_receiver
emit_self(receiver,name,false,true)
end
end
end
end
end

View file

@ -0,0 +1,25 @@
module Mutant
class Mutator
# Mutator for Rubinius::AST::Send
class Call < Mutator
class SendWithArguments < Call
handle(Rubinius::AST::SendWithArguments)
private
# Emit mutations
#
# @return [undefined]
#
# @api private
#
# FIXME: There are MANY more mutations here :P
#
def dispatch
emit_explicit_receiver
end
end
end
end
end

View file

@ -0,0 +1,48 @@
module Mutant
class Mutator
# Registry for mutators
module Registry
# Return registry state
#
# @return [Hash]
#
# @api private
#
def self.registry
@registry ||= {}
end
private_class_method :registry
# Register mutator class for AST node class
#
# @param [Class] ast_class
# @param [Class] mutator_class
#
# @api private
#
# @return [self]
#
def self.register(ast_class,mutator_class)
registry[ast_class]=mutator_class
self
end
# Lookup mutator class for AST node class
#
# @param [Class] ast_class
#
# @return [Class]
#
# @raise [ArgumentError]
# raises argument error when mutator class cannot be found
#
# @api private
#
def self.lookup(ast_class)
registry.fetch(ast_class) do
raise ArgumentError,"No mutator to handle: #{ast_class.inspect}"
end
end
end
end
end

View file

@ -1,22 +0,0 @@
module Mutant
class Mutator
# Mutator for Rubinius::AST::Send
class Send < Mutator
handle(Rubinius::AST::Send)
private
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
# Mutate "self.foo" into "foo"
emit_self(node.receiver,node.name,true,true)
end
end
end
end

View file

@ -1,28 +0,0 @@
module Mutant
class Mutator
# Mutator for Rubinius::AST::Send
class SendWithArguments < Mutator
handle(Rubinius::AST::SendWithArguments)
private
# Emit mutations
#
# @return [undefined]
#
# @api private
#
# FIXME: # There are MANY more mutations here :P
#
def dispatch
# Mutate "foo(1)" into "self.foo(1)"
emit_self(node.receiver,node.name,arguments,true)
end
def arguments
new(Rubinius::AST::ArrayLiteral,node.arguments.array)
end
end
end
end

View file

@ -0,0 +1,37 @@
shared_examples_for 'a mutation enumerator method' do
subject { object.each(node) { |item| yields << item } }
let(:yields) { [] }
let(:object) { described_class }
let(:node) { source.to_ast }
it_should_behave_like 'a command method'
context 'with no block' do
subject { object.each(node) }
it { should be_instance_of(to_enum.class) }
let(:expected_mutations) do
mutations.map do |mutation|
if mutation.respond_to?(:to_ast)
mutation.to_ast.to_sexp
else
mutation
end
end.to_set
end
it 'generates the expected mutations' do
subject = self.subject.map(&:to_sexp).to_set
unless subject == expected_mutations
message = "Missing mutations: %s\nUnexpected mutations: %s" %
[expected_mutations - subject, subject - expected_mutations ].map(&:to_a).map(&:inspect)
fail message
end
end
end
end

View file

@ -0,0 +1,54 @@
require 'spec_helper'
describe Mutant::Mutator::Call,'.each' do
pending 'send' do
context 'to self' do
context 'implict' do
let(:source) { 'foo' }
let(:mutations) do
mutations = []
end
it_should_behave_like 'a mutation enumerator method'
end
context 'explict' do
let(:source) { 'self.foo' }
let(:mutations) do
mutations = []
mutations << 'foo' # without explict receiver (send privately)
end
it_should_behave_like 'a mutation enumerator method'
end
end
end
pending 'send with arguments' do
context 'to self' do
context 'implict' do
let(:source) { 'foo(1)' }
let(:mutations) do
mutations = []
end
it_should_behave_like 'a mutation enumerator method'
end
context 'explict' do
let(:source) { 'self.foo(1)' }
let(:mutations) do
mutations = []
mutations << 'foo(1)' # without explict receiver (send privately)
end
it_should_behave_like 'a mutation enumerator method'
end
end
end
end

View file

@ -1,74 +1,18 @@
# This file is big. Once mutation interface does not change
# anymore it will be split up in mutation specific stuff.
# This file is the sandbox for new mutations.
# Once finished mutation test will be moved to class specfic
# file.
require 'spec_helper'
shared_examples_for 'a mutation enumerator method' do
it_should_behave_like 'a command method'
context 'with no block' do
subject { object.each(node) }
it { should be_instance_of(to_enum.class) }
let(:expected_mutations) do
mutations.map do |mutation|
if mutation.respond_to?(:to_ast)
mutation.to_ast.to_sexp
else
mutation
end
end.to_set
end
it 'generates the expected mutations' do
subject = self.subject.map(&:to_sexp).to_set
unless subject == expected_mutations
message = "Missing mutations: %s\nUnexpected mutations: %s" %
[expected_mutations - subject, subject - expected_mutations ].map(&:to_a).map(&:inspect)
fail message
end
end
end
end
describe Mutant::Mutator, '.each' do
subject { object.each(node) { |item| yields << item } }
let(:random_string) { 'bar' }
let(:yields) { [] }
let(:object) { described_class }
let(:node) { source.to_ast }
let(:random_string) { 'bar' }
context 'true literal' do
let(:source) { 'true' }
pending 'interpolated string literal (DynamicString)' do
let(:source) { '"foo#{1}bar"' }
let(:mutations) do
%w(nil false)
end
it_should_behave_like 'a mutation enumerator method'
end
context 'false literal' do
let(:source) { 'false' }
let(:mutations) do
%w(nil true)
end
it_should_behave_like 'a mutation enumerator method'
end
context 'symbol literal' do
let(:source) { ':foo' }
let(:mutations) do
%w(nil) << ":#{random_string}"
mutations = []
mutations << 'nil'
end
before do
@ -78,212 +22,7 @@ describe Mutant::Mutator, '.each' do
it_should_behave_like 'a mutation enumerator method'
end
context 'string literal' do
let(:source) { '"foo"' }
let(:mutations) do
%W(nil "#{random_string}")
end
before do
Mutant::Random.stub(:hex_string => random_string)
end
it_should_behave_like 'a mutation enumerator method'
end
#context 'interpolated string literal (DynamicString)' do
# let(:source) { '"foo#{1}bar"' }
# let(:mutations) do
# mutations = []
# mutations << 'nil'
# end
# before do
# Mutant::Random.stub(:hex_string => random_string)
# end
# it_should_behave_like 'a mutation enumerator method'
#end
context 'fixnum literal' do
let(:source) { '10' }
let(:random_integer) { 5 }
let(:mutations) do
%W(nil 0 1 #{random_integer}) << [:lit, -10]
end
before do
Mutant::Random.stub(:fixnum => random_integer)
end
it_should_behave_like 'a mutation enumerator method'
end
context 'float literal' do
let(:source) { '10.0' }
let(:mutations) do
mutations = []
mutations << 'nil'
mutations << '0.0'
mutations << '1.0'
mutations << random_float.to_s
mutations << '0.0/0.0'
mutations << '1.0/0.0'
mutations << [:negate, [:call, [:lit, 1.0], :/, [:arglist, [:lit, 0.0]]]]
mutations << [:lit, -10.0]
end
let(:random_float) { 7.123 }
before do
Mutant::Random.stub(:float => random_float)
end
it_should_behave_like 'a mutation enumerator method'
end
context 'empty array literal' do
let(:source) { '[]' }
let(:mutations) do
mutations = []
# Literal replaced with nil
mutations << [:nil]
# Extra element
mutations << '[nil]'
end
it_should_behave_like 'a mutation enumerator method'
end
context 'array literal' do
let(:source) { '[true, false]' }
let(:mutations) do
mutations = []
# Literal replaced with nil
mutations << [:nil]
# Mutation of each element in array
mutations << '[nil, false]'
mutations << '[false, false]'
mutations << '[true, nil]'
mutations << '[true, true]'
# Remove each element of array once
mutations << '[true]'
mutations << '[false]'
# Empty array
mutations << '[]'
# Extra element
mutations << '[true, false, nil]'
end
it_should_behave_like 'a mutation enumerator method'
end
context 'hash literal' do
let(:source) { '{true => true, false => false}' }
let(:mutations) do
mutations = []
# Literal replaced with nil
mutations << [:nil]
# Mutation of each key and value in hash
mutations << [:hash, [:false ], [:true ], [:false], [:false]]
mutations << [:hash, [:nil ], [:true ], [:false], [:false]]
mutations << [:hash, [:true ], [:false], [:false], [:false]]
mutations << [:hash, [:true ], [:nil ], [:false], [:false]]
mutations << [:hash, [:true ], [:true ], [:true ], [:false]]
mutations << [:hash, [:true ], [:true ], [:nil ], [:false]]
mutations << [:hash, [:true ], [:true ], [:false], [:true ]]
mutations << [:hash, [:true ], [:true ], [:false], [:nil ]]
# Remove each key once
mutations << [:hash, [:true ], [:true ]]
mutations << [:hash, [:false ], [:false ]]
# Empty hash
mutations << [:hash]
# Extra element
mutations << [:hash, [:true ], [:true ], [:false], [:false ], [:nil], [:nil] ]
end
it_should_behave_like 'a mutation enumerator method'
end
context 'range literal' do
let(:source) { '1..100' }
let(:mutations) do
mutations = []
mutations << 'nil'
mutations << '1...100'
mutations << '(0.0/0.0)..100'
mutations << [:dot2, [:negate, [:call, [:lit, 1.0], :/, [:arglist, [:lit, 0.0]]]], [:lit, 100]]
mutations << '1..(1.0/0.0)'
mutations << '1..(0.0/0.0)'
end
it_should_behave_like 'a mutation enumerator method'
end
context 'exclusive range literal' do
let(:source) { '1...100' }
let(:mutations) do
mutations = []
mutations << 'nil'
mutations << '1..100'
mutations << '(0.0/0.0)...100'
mutations << [:dot3, [:negate, [:call, [:lit, 1.0], :/, [:arglist, [:lit, 0.0]]]], [:lit, 100]]
mutations << '1...(1.0/0.0)'
mutations << '1...(0.0/0.0)'
end
it_should_behave_like 'a mutation enumerator method'
end
context 'regexp literal' do
let(:source) { '/foo/' }
let(:base_mutations) do
mutations = []
mutations << 'nil'
mutations << "/#{random_string}/"
end
before do
Mutant::Random.stub(:hex_string => random_string)
end
let(:mutations) { base_mutations }
it_should_behave_like 'a mutation enumerator method'
#it 'when source is empty regexp' do
# let(:source) { '//' }
# let(:mutations) { base_mutations - [source.to_ast] }
# it_should_behave_like 'a mutation enumerator method'
#end
end
context 'block literal' do
pending 'block literal' do
# Two send operations
let(:source) { "self.foo\nself.bar" }
@ -301,66 +40,4 @@ describe Mutant::Mutator, '.each' do
it_should_behave_like 'a mutation enumerator method'
end
context 'self' do
let(:source) { 'self' }
let(:mutations) do
mutations = []
end
it_should_behave_like 'a mutation enumerator method'
end
context 'send' do
context 'to self' do
context 'implict' do
let(:source) { 'foo' }
let(:mutations) do
mutations = []
end
it_should_behave_like 'a mutation enumerator method'
end
context 'explict' do
let(:source) { 'self.foo' }
let(:mutations) do
mutations = []
mutations << 'foo' # without explict receiver (send privately)
end
it_should_behave_like 'a mutation enumerator method'
end
end
end
context 'send with arguments' do
context 'to self' do
context 'implict' do
let(:source) { 'foo(1)' }
let(:mutations) do
mutations = []
end
it_should_behave_like 'a mutation enumerator method'
end
context 'explict' do
let(:source) { 'self.foo(1)' }
let(:mutations) do
mutations = []
mutations << 'foo(1)' # without explict receiver (send privately)
end
it_should_behave_like 'a mutation enumerator method'
end
end
end
end

View file

@ -0,0 +1,32 @@
require 'spec_helper'
describe Mutant::Mutator::Literal::Array,'.each' do
context 'array literal' do
let(:source) { '[true, false]' }
let(:mutations) do
mutations = []
# Literal replaced with nil
mutations << [:nil]
# Mutation of each element in array
mutations << '[nil, false]'
mutations << '[false, false]'
mutations << '[true, nil]'
mutations << '[true, true]'
# Remove each element of array once
mutations << '[true]'
mutations << '[false]'
# Empty array
mutations << '[]'
# Extra element
mutations << '[true, false, nil]'
end
it_should_behave_like 'a mutation enumerator method'
end
end

View file

@ -0,0 +1,23 @@
require 'spec_helper'
describe Mutant::Mutator::Literal::Boolean,'.each' do
context 'true literal' do
let(:source) { 'true' }
let(:mutations) do
%w(nil false)
end
it_should_behave_like 'a mutation enumerator method'
end
context 'false literal' do
let(:source) { 'false' }
let(:mutations) do
%w(nil true)
end
it_should_behave_like 'a mutation enumerator method'
end
end

View file

@ -0,0 +1,19 @@
require 'spec_helper'
describe Mutant::Mutator::Literal::EmptyArray,'.each' do
context 'empty array literal' do
let(:source) { '[]' }
let(:mutations) do
mutations = []
# Literal replaced with nil
mutations << [:nil]
# Extra element
mutations << '[nil]'
end
it_should_behave_like 'a mutation enumerator method'
end
end

View file

@ -0,0 +1,19 @@
require 'spec_helper'
describe Mutant::Mutator::Literal::Fixnum,'.each' do
let(:random_fixnum) { 5 }
context 'fixnum literal' do
let(:source) { '10' }
let(:mutations) do
%W(nil 0 1 #{random_fixnum}) << [:lit, -10]
end
before do
Mutant::Random.stub(:fixnum => random_fixnum)
end
it_should_behave_like 'a mutation enumerator method'
end
end

View file

@ -0,0 +1,27 @@
require 'spec_helper'
describe Mutant::Mutator::Literal::Float, '.each' do
context 'float literal' do
let(:source) { '10.0' }
let(:mutations) do
mutations = []
mutations << 'nil'
mutations << '0.0'
mutations << '1.0'
mutations << random_float.to_s
mutations << '0.0/0.0'
mutations << '1.0/0.0'
mutations << [:negate, [:call, [:lit, 1.0], :/, [:arglist, [:lit, 0.0]]]]
mutations << [:lit, -10.0]
end
let(:random_float) { 7.123 }
before do
Mutant::Random.stub(:float => random_float)
end
it_should_behave_like 'a mutation enumerator method'
end
end

View file

@ -0,0 +1,36 @@
require 'spec_helper'
describe Mutant::Mutator::Literal::Hash, '.each' do
context 'hash literal' do
let(:source) { '{true => true, false => false}' }
let(:mutations) do
mutations = []
# Literal replaced with nil
mutations << [:nil]
# Mutation of each key and value in hash
mutations << [:hash, [:false ], [:true ], [:false], [:false]]
mutations << [:hash, [:nil ], [:true ], [:false], [:false]]
mutations << [:hash, [:true ], [:false], [:false], [:false]]
mutations << [:hash, [:true ], [:nil ], [:false], [:false]]
mutations << [:hash, [:true ], [:true ], [:true ], [:false]]
mutations << [:hash, [:true ], [:true ], [:nil ], [:false]]
mutations << [:hash, [:true ], [:true ], [:false], [:true ]]
mutations << [:hash, [:true ], [:true ], [:false], [:nil ]]
# Remove each key once
mutations << [:hash, [:true ], [:true ]]
mutations << [:hash, [:false ], [:false ]]
# Empty hash
mutations << [:hash]
# Extra element
mutations << [:hash, [:true ], [:true ], [:false], [:false ], [:nil], [:nil] ]
end
it_should_behave_like 'a mutation enumerator method'
end
end

View file

@ -0,0 +1,35 @@
require 'spec_helper'
describe Mutant::Mutator::Literal::Range,'.each' do
context 'inclusive range literal' do
let(:source) { '1..100' }
let(:mutations) do
mutations = []
mutations << 'nil'
mutations << '1...100'
mutations << '(0.0/0.0)..100'
mutations << [:dot2, [:negate, [:call, [:lit, 1.0], :/, [:arglist, [:lit, 0.0]]]], [:lit, 100]]
mutations << '1..(1.0/0.0)'
mutations << '1..(0.0/0.0)'
end
it_should_behave_like 'a mutation enumerator method'
end
context 'exclusive range literal' do
let(:source) { '1...100' }
let(:mutations) do
mutations = []
mutations << 'nil'
mutations << '1..100'
mutations << '(0.0/0.0)...100'
mutations << [:dot3, [:negate, [:call, [:lit, 1.0], :/, [:arglist, [:lit, 0.0]]]], [:lit, 100]]
mutations << '1...(1.0/0.0)'
mutations << '1...(0.0/0.0)'
end
it_should_behave_like 'a mutation enumerator method'
end
end

View file

@ -0,0 +1,35 @@
require 'spec_helper'
describe Mutant::Mutator::Literal::Regex,'.each' do
let(:random_string) { 'bar' }
context 'regexp literal' do
let(:source) { '/foo/' }
let(:base_mutations) do
mutations = []
mutations << 'nil'
mutations << "/#{random_string}/"
end
before do
Mutant::Random.stub(:hex_string => random_string)
end
let(:mutations) { base_mutations }
it_should_behave_like 'a mutation enumerator method'
context 'when source is empty regexp' do
before do
pending
end
let(:source) { '//' }
let(:mutations) { base_mutations - [source.to_ast] }
it_should_behave_like 'a mutation enumerator method'
end
end
end

View file

@ -0,0 +1,19 @@
require 'spec_helper'
describe Mutant::Mutator::Literal::String,'.each' do
let(:random_string) { 'bar' }
context 'string literal' do
let(:source) { '"foo"' }
let(:mutations) do
%W(nil "#{random_string}")
end
before do
Mutant::Random.stub(:hex_string => random_string)
end
it_should_behave_like 'a mutation enumerator method'
end
end

View file

@ -0,0 +1,20 @@
require 'spec_helper'
describe Mutant::Mutator::Literal::Symbol,'.each' do
let(:random_string) { 'bar' }
context 'symbol literal' do
let(:source) { ':foo' }
let(:mutations) do
%w(nil) << ":#{random_string}"
end
before do
Mutant::Random.stub(:hex_string => random_string)
end
it_should_behave_like 'a mutation enumerator method'
end
end

View file

@ -0,0 +1,11 @@
require 'spec_helper'
describe Mutant::Mutator::Self,'.each' do
let(:source) { 'self' }
let(:mutations) do
mutations = []
end
it_should_behave_like 'a mutation enumerator method'
end