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:
parent
cf4a0295ec
commit
c5f5d595ca
5 changed files with 73 additions and 31 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue