Guard against the generation of equal mutants

* Move Mutant::Mutator::Generator in a dedicated file
* Mutant::Mutator::Generator#append only forwards mutated node when it
  does not equal original nodes sexpession. This does not catch
  equivalent mutations, but mutations that exactly reproduce the same
  AST. This way a check for not generating the same asts can be
  centralized.
This commit is contained in:
Markus Schirp 2012-07-30 21:40:49 +02:00
parent 993f08d975
commit ef9172bca4
5 changed files with 76 additions and 13 deletions

3
TODO
View file

@ -17,3 +17,6 @@
It can be used to make sure each literal value is touched. It can be used to make sure each literal value is touched.
* Replace nil or add "do not touch me object" to literal mutations. * Replace nil or add "do not touch me object" to literal mutations.
* Add remaining literals * Add remaining literals
* Mutate options for Regexp literals
* Move Mutant.random_* into Mutant::Random namespace.
* Use inheritable alias once (virtus,veritas,mapper,session, ...) support gem is born.

View file

@ -82,6 +82,7 @@ module Mutant
end end
require 'mutant/mutator' require 'mutant/mutator'
require 'mutant/mutator/generator'
require 'mutant/mutator/true_literal' require 'mutant/mutator/true_literal'
require 'mutant/mutator/false_literal' require 'mutant/mutator/false_literal'
require 'mutant/mutator/symbol_literal' require 'mutant/mutator/symbol_literal'
@ -93,6 +94,8 @@ require 'mutant/mutator/empty_array'
require 'mutant/mutator/hash_literal' require 'mutant/mutator/hash_literal'
require 'mutant/mutator/range' require 'mutant/mutator/range'
require 'mutant/mutator/range_exclude' require 'mutant/mutator/range_exclude'
require 'mutant/mutator/regex_literal'
require 'mutant/mutator/dynamic_string'
require 'mutant/mutator/block' require 'mutant/mutator/block'
require 'mutant/loader' require 'mutant/loader'
require 'mutant/context' require 'mutant/context'

View file

@ -31,25 +31,13 @@ module Mutant
const_get(unqualified_name) const_get(unqualified_name)
end end
class Generator
def initialize(block)
@block = block
end
def append(node)
@block.call(node)
end
alias :<< :append
end
# Enumerate mutated asts # Enumerate mutated asts
# #
# @api private # @api private
# #
def each(&block) def each(&block)
return to_enum unless block_given? return to_enum unless block_given?
mutants(Generator.new(block)) mutants(Generator.new(@node,block))
self self
end end

View file

@ -0,0 +1,25 @@
module Mutant
class Mutator
class Generator
def initialize(node,block)
@sexp,@block = node.to_sexp,block
end
def append(node)
p same_node?(node)
unless same_node?(node)
@block.call(node)
end
end
# FIXME: Use interhitable alias once in support gem.
alias :<< :append
private
def same_node?(node)
@sexp == node.to_sexp
end
end
end
end

View file

@ -0,0 +1,44 @@
require 'spec_helper'
describe Mutant::Mutator::Generator,'#append' do
subject { object.append(node) }
class Block
attr_reader :arguments
def called?
defined?(@arguments)
end
def call(*arguments)
@arguments = arguments
end
end
let(:object) { described_class.new(wrapped_node,block) }
let(:block) { Block.new }
let(:wrapped_node) { '"foo"'.to_ast }
context 'with node that is not equal to wrapped node' do
let(:node) { '"bar"'.to_ast }
it 'should call block' do
subject
block.should be_called
end
it 'should call block with node' do
subject
block.arguments.should eql([node])
end
end
context 'with node that is equal to wrapped node' do
let(:node) { '"foo"'.to_ast }
it 'should call block' do
subject
block.should_not be_called
end
end
end