Correctly avoid mutations that produce differend AST but same source

* Add detection for this case in Example::Verification
* Revert source location killing Normalization after parse.
* Closes #198
This commit is contained in:
Markus Schirp 2014-06-09 14:56:13 +00:00
parent cf4a0295ec
commit c5f5d595ca
5 changed files with 73 additions and 31 deletions

View file

@ -24,7 +24,7 @@ module Mutant
#
def parse(path)
@cache.fetch(path) do
@cache[path] = Unparser::Preprocessor.run(Parser::CurrentRuby.parse(File.read(path)))
@cache[path] = Parser::CurrentRuby.parse(File.read(path))
end
end

View file

@ -15,20 +15,33 @@ module Mutant
Verification.new(self, generated)
end
# Return source
#
# @return [String]
#
# @api private
#
def source
Unparser.unparse(node)
end
memoize :source
# Return generated mutations
#
# @return [Emumerable<Parser::AST::Node>]
# @return [Emumerable<Mutant::Mutation>]
#
# @api private
#
def generated
Mutant::Mutator.each(node).to_a
Mutant::Mutator.each(node).map do |node|
Mutant::Mutation::Evil.new(self, node)
end
end
memoize :generated
# Example verification
class Verification
include Adamantium::Flat, Concord.new(:example, :generated)
include Adamantium::Flat, Concord.new(:example, :mutations)
# Test if mutation was verified successfully
#
@ -37,7 +50,7 @@ module Mutant
# @api private
#
def success?
unparser.success? && missing.empty? && unexpected.empty?
unparser.success? && missing.empty? && unexpected.empty? && no_diffs.empty?
end
# Return error report
@ -62,10 +75,21 @@ module Mutant
# @api private
#
def unexpected
generated - example.mutations
mutations.map(&:node) - example.mutations
end
memoize :unexpected
# Return mutations with no diff to original
#
# @return [Enumerable<Mutation>]
#
# @api private
#
def no_diffs
mutations.select { |mutation| mutation.source.eql?(example.source) }
end
memoize :no_diffs
# Return mutation report
#
# @return [String]
@ -78,9 +102,10 @@ module Mutant
'Original-AST:',
original_node.inspect,
'Original-Source:',
Unparser.unparse(original_node),
example.source,
*missing_report,
*unexpected_report
*unexpected_report,
*no_diff_report,
].join("\n======\n")
end
@ -97,6 +122,21 @@ module Mutant
] if missing.any?
end
# Return no diff report
#
# @return [Array, nil]
#
# @api private
#
def no_diff_report
[
'No source diffs to original:',
no_diffs.map do |mutation|
"#{mutation.node.inspect}\n#{mutation.source}"
end
] if no_diffs.any?
end
# Return unexpected report
#
# @return [Array, nil]
@ -123,14 +163,14 @@ module Mutant
].join("\n")
end
# Return missing mutationso
# Return missing mutations
#
# @return [Array<Parser::AST::Node>]
#
# @api private
#
def missing
example.mutations - generated
example.mutations - mutations.map(&:node)
end
memoize :missing

View file

@ -81,8 +81,8 @@ module Mutant
# @api private
#
def singleton_mutations
mutation 'nil'
mutation 'self'
mutation('nil')
mutation('self')
end
# Helper method to coerce input to node
@ -97,17 +97,14 @@ module Mutant
# @api private
#
def node(input)
node =
case input
when String
Parser::CurrentRuby.parse(input)
when Parser::AST::Node
input
else
raise "Cannot coerce to node: #{source.inspect}"
end
Unparser::Preprocessor.run(node)
case input
when String
Unparser::Preprocessor.run(Parser::CurrentRuby.parse(input))
when Parser::AST::Node
input
else
raise "Cannot coerce to node: #{source.inspect}"
end
end
end # DSL

View file

@ -23,7 +23,7 @@ module Mutant
end
children.each_with_index do |child, index|
mutate_child(index)
emit(child)
emit(child) unless children.eql?([child])
end
end
@ -36,13 +36,8 @@ module Mutant
# @api private
#
def emit_child_subset(children)
case children.length
when 0
when 1
emit(children.first)
else
emit_type(*children)
end
return if children.length < 2
emit_type(*children)
end
end # Block

View file

@ -13,3 +13,13 @@ Mutant::Meta::Example.add do
mutation 'true'
mutation 'false'
end
# encoding: utf-8
Mutant::Meta::Example.add do
source s(:begin, s(:true))
# Mutation of each statement in block
mutation s(:begin, s(:false))
mutation s(:begin, s(:nil))
end