Refactor reporter infrastructure
This commit is contained in:
parent
bbc91b9457
commit
d41d7eddb0
21 changed files with 519 additions and 490 deletions
|
@ -38,6 +38,8 @@ require 'mutant/helper'
|
|||
require 'mutant/random'
|
||||
require 'mutant/mutator'
|
||||
require 'mutant/mutation'
|
||||
require 'mutant/mutation/evil'
|
||||
require 'mutant/mutation/neutral'
|
||||
require 'mutant/mutation/filter'
|
||||
require 'mutant/mutation/filter/code'
|
||||
require 'mutant/mutation/filter/whitelist'
|
||||
|
@ -97,7 +99,9 @@ require 'mutant/killer'
|
|||
require 'mutant/killer/static'
|
||||
require 'mutant/killer/rspec'
|
||||
require 'mutant/killer/forking'
|
||||
require 'mutant/killer/forked'
|
||||
require 'mutant/strategy'
|
||||
require 'mutant/strategy/static'
|
||||
require 'mutant/strategy/method_expansion'
|
||||
require 'mutant/strategy/rspec'
|
||||
require 'mutant/strategy/rspec/dm2'
|
||||
|
|
|
@ -1,20 +1,35 @@
|
|||
module Mutant
|
||||
# Abstract base class for mutant killers
|
||||
class Killer
|
||||
include Adamantium::Flat, AbstractType
|
||||
include Adamantium::Flat, AbstractType, Equalizer.new(:strategy, :mutation, :killed?)
|
||||
|
||||
# Test for kill failure
|
||||
#
|
||||
# @return [true]
|
||||
# returns true when mutant was killed
|
||||
# when mutant was killed
|
||||
#
|
||||
# @return [false]
|
||||
# returns false otherwise
|
||||
# otherwise
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def fail?
|
||||
!@killed
|
||||
def success?
|
||||
mutation.success?(self)
|
||||
end
|
||||
memoize :success?
|
||||
|
||||
# Test if mutant was killed
|
||||
#
|
||||
# @return [true]
|
||||
# if mutant was killed
|
||||
#
|
||||
# @return [false]
|
||||
# otherwise
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def killed?
|
||||
@killed
|
||||
end
|
||||
|
||||
# Return runtime of killer
|
||||
|
@ -25,14 +40,14 @@ module Mutant
|
|||
#
|
||||
attr_reader :runtime
|
||||
|
||||
# Return original source
|
||||
# Return configuration
|
||||
#
|
||||
# @return [String]
|
||||
# @return [Configuration]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def original_source
|
||||
mutation.original_source
|
||||
def configuration
|
||||
strategy.configuration
|
||||
end
|
||||
|
||||
# Return mutated source
|
||||
|
@ -45,14 +60,6 @@ module Mutant
|
|||
mutation.source
|
||||
end
|
||||
|
||||
# Return strategy
|
||||
#
|
||||
# @return [Strategy]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
attr_reader :strategy
|
||||
|
||||
# Return name of killer
|
||||
#
|
||||
# @return [String]
|
||||
|
@ -63,6 +70,14 @@ module Mutant
|
|||
self::TYPE
|
||||
end
|
||||
|
||||
# Return strategy
|
||||
#
|
||||
# @return [Strategy]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
attr_reader :strategy
|
||||
|
||||
# Return identification
|
||||
#
|
||||
# @return [String]
|
||||
|
|
58
lib/mutant/killer/forked.rb
Normal file
58
lib/mutant/killer/forked.rb
Normal file
|
@ -0,0 +1,58 @@
|
|||
module Mutant
|
||||
class Killer
|
||||
|
||||
# Killer that executes other killer in forked environment
|
||||
class Forked < self
|
||||
|
||||
# Initialize object
|
||||
#
|
||||
# @param [Killer] killer
|
||||
# @param [Strategy] strategy
|
||||
# @param [Mutation] mutation
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def initialize(killer, strategy, mutation)
|
||||
@killer = killer
|
||||
super(strategy, mutation)
|
||||
end
|
||||
|
||||
# Return killer type
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def type
|
||||
@killer.type
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Run killer
|
||||
#
|
||||
# @return [true]
|
||||
# if mutant was killed
|
||||
#
|
||||
# @return [false]
|
||||
# otherwise
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def run
|
||||
fork do
|
||||
begin
|
||||
killer = @killer.new(strategy, mutation)
|
||||
Kernel.exit(killer.fail? ? 1 : 0)
|
||||
rescue
|
||||
Kernel.exit(1)
|
||||
end
|
||||
end
|
||||
|
||||
status = Process.wait2.last
|
||||
status.exitstatus.zero?
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,59 +1,6 @@
|
|||
module Mutant
|
||||
class Killer
|
||||
|
||||
# Killer that executes other killer in forked environment
|
||||
class Forked < self
|
||||
|
||||
# Initialize object
|
||||
#
|
||||
# @param [Killer] killer
|
||||
# @param [Strategy] strategy
|
||||
# @param [Mutation] mutation
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def initialize(killer, strategy, mutation)
|
||||
@killer = killer
|
||||
super(strategy, mutation)
|
||||
end
|
||||
|
||||
# Return killer type
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def type
|
||||
@killer.type
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Run killer
|
||||
#
|
||||
# @return [true]
|
||||
# if mutant was killed
|
||||
#
|
||||
# @return [false]
|
||||
# otherwise
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def run
|
||||
fork do
|
||||
begin
|
||||
killer = @killer.new(strategy, mutation)
|
||||
Kernel.exit(killer.fail? ? 1 : 0)
|
||||
rescue
|
||||
Kernel.exit(1)
|
||||
end
|
||||
end
|
||||
|
||||
status = Process.wait2.last
|
||||
status.exitstatus.zero?
|
||||
end
|
||||
end
|
||||
|
||||
# A killer that executes other killer in forked environemnts
|
||||
class Forking < self
|
||||
include Equalizer.new(:killer)
|
||||
|
@ -93,6 +40,5 @@ module Mutant
|
|||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,21 +2,11 @@ module Mutant
|
|||
class Killer
|
||||
# Runner for rspec tests
|
||||
class Rspec < self
|
||||
|
||||
TYPE = 'rspec'.freeze
|
||||
|
||||
private
|
||||
|
||||
# Initialize rspec runner
|
||||
#
|
||||
# @return [undefined]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def initialize(*)
|
||||
@error_stream, @output_stream = StringIO.new, StringIO.new
|
||||
super
|
||||
end
|
||||
|
||||
# Run rspec test
|
||||
#
|
||||
# @return [true]
|
||||
|
|
|
@ -1,7 +1,20 @@
|
|||
module Mutant
|
||||
# Represent a mutated node with its subject
|
||||
class Mutation
|
||||
include Adamantium::Flat, Equalizer.new(:sha1)
|
||||
include AbstractType, Adamantium::Flat, Equalizer.new(:sha1)
|
||||
|
||||
# Initialize mutation object
|
||||
#
|
||||
# @param [Subject] subject
|
||||
# @param [Rubinius::Node::AST] node
|
||||
#
|
||||
# @return [undefined]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def initialize(subject, node)
|
||||
@subject, @node = subject, node
|
||||
end
|
||||
|
||||
# Return mutation subject
|
||||
#
|
||||
|
@ -30,6 +43,20 @@ module Mutant
|
|||
end
|
||||
memoize :root
|
||||
|
||||
# Test if killer is successful
|
||||
#
|
||||
# @param [Killer] killer
|
||||
#
|
||||
# @return [true]
|
||||
# if killer is successful
|
||||
#
|
||||
# @return [false]
|
||||
# otherwise
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
abstract_method :success?
|
||||
|
||||
# Insert mutated node
|
||||
#
|
||||
# @return [self]
|
||||
|
@ -95,46 +122,5 @@ module Mutant
|
|||
subject.source
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Initialize mutation object
|
||||
#
|
||||
# @param [Subject] subject
|
||||
# @param [Rubinius::Node::AST] node
|
||||
#
|
||||
# @return [undefined]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def initialize(subject, node)
|
||||
@subject, @node = subject, node
|
||||
end
|
||||
|
||||
# Noop mutation
|
||||
class Noop < self
|
||||
|
||||
# Initialize object
|
||||
#
|
||||
# @param [Subject] subject
|
||||
#
|
||||
# @return [undefined]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def initialize(subject)
|
||||
super(subject, subject.node)
|
||||
end
|
||||
|
||||
# Return identification
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def identification
|
||||
"noop:#{super}"
|
||||
end
|
||||
memoize :identification
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
35
lib/mutant/mutation/evil.rb
Normal file
35
lib/mutant/mutation/evil.rb
Normal file
|
@ -0,0 +1,35 @@
|
|||
module Mutant
|
||||
class Mutation
|
||||
# Evul mutation
|
||||
class Evil < self
|
||||
|
||||
# Return identification
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def identification
|
||||
"evil:#{super}"
|
||||
end
|
||||
memoize :identification
|
||||
|
||||
# Test if killer is successful
|
||||
#
|
||||
# @param [Killer] killer
|
||||
#
|
||||
# @return [true]
|
||||
# if killer killed mutation
|
||||
#
|
||||
# @return [false]
|
||||
# otherwise
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def success?(killer)
|
||||
killer.killed?
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
35
lib/mutant/mutation/neutral.rb
Normal file
35
lib/mutant/mutation/neutral.rb
Normal file
|
@ -0,0 +1,35 @@
|
|||
module Mutant
|
||||
class Mutation
|
||||
# Neutral mutation
|
||||
class Neutral < self
|
||||
|
||||
# Return identification
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def identification
|
||||
"noop:#{super}"
|
||||
end
|
||||
memoize :identification
|
||||
|
||||
# Test if killer is successful
|
||||
#
|
||||
# @param [Killer] killer
|
||||
#
|
||||
# @return [true]
|
||||
# if killer did NOT killed mutation
|
||||
#
|
||||
# @return [false]
|
||||
# otherwise
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def success?(killer)
|
||||
!killer.killed?
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,7 +1,18 @@
|
|||
module Mutant
|
||||
# Abstract reporter
|
||||
class Reporter
|
||||
include Adamantium::Flat, AbstractType
|
||||
include Adamantium::Flat, AbstractType, Equalizer.new(:stats)
|
||||
|
||||
# Initialize reporter
|
||||
#
|
||||
# @param [Config] config
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def initialize(config)
|
||||
@stats = Stats.new
|
||||
@config = config
|
||||
end
|
||||
|
||||
# Report subject
|
||||
#
|
||||
|
@ -11,7 +22,10 @@ module Mutant
|
|||
#
|
||||
# @api private
|
||||
#
|
||||
abstract_method :subject
|
||||
def subject(subject)
|
||||
stats.count_subject
|
||||
self
|
||||
end
|
||||
|
||||
# Report mutation
|
||||
#
|
||||
|
@ -21,17 +35,9 @@ module Mutant
|
|||
#
|
||||
# @api private
|
||||
#
|
||||
abstract_method :mutation
|
||||
|
||||
# Report notice
|
||||
#
|
||||
# @param [String] notice
|
||||
#
|
||||
# @return [self]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
abstract_method :notice
|
||||
def mutation(mutation)
|
||||
self
|
||||
end
|
||||
|
||||
# Report killer
|
||||
#
|
||||
|
@ -41,44 +47,55 @@ module Mutant
|
|||
#
|
||||
# @api private
|
||||
#
|
||||
abstract_method :killer
|
||||
def report_killer(killer)
|
||||
stats.count_killer(killer)
|
||||
|
||||
# Report config
|
||||
#
|
||||
# @param [Mutant::Config] config
|
||||
#
|
||||
# @return [self]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
abstract_method :config
|
||||
|
||||
# Return output stream
|
||||
#
|
||||
# @return [IO]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
abstract_method :output_stream
|
||||
|
||||
# Return error stream
|
||||
#
|
||||
# @return [IO]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
abstract_method :error_stream
|
||||
|
||||
private
|
||||
|
||||
# Initialize reporter
|
||||
#
|
||||
# @param [Config] config
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def initialize(config)
|
||||
@config = config
|
||||
self
|
||||
end
|
||||
|
||||
# Test for running in debug mode
|
||||
#
|
||||
# @return [true]
|
||||
# if running in debug mode
|
||||
#
|
||||
# @return [false]
|
||||
# otherwise
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def debug?
|
||||
config.debug?
|
||||
end
|
||||
|
||||
# Return stats
|
||||
#
|
||||
# @return [Reporter::Stats]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
attr_reader :stats
|
||||
|
||||
# Return config
|
||||
#
|
||||
# @return [Config]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
attr_reader :config
|
||||
|
||||
# Test if errors are present
|
||||
#
|
||||
# @return [true]
|
||||
# if errors are present
|
||||
#
|
||||
# @return [false]
|
||||
# otherwise
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def errors?
|
||||
stats.errors?
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,9 +2,21 @@ module Mutant
|
|||
class Reporter
|
||||
# Reporter that reports in human readable format
|
||||
class CLI < self
|
||||
include Equalizer.new(:io)
|
||||
|
||||
# Reporter subject
|
||||
# Initialize reporter
|
||||
#
|
||||
# @param [Config] config
|
||||
#
|
||||
# @return [undefined]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def initialize(config)
|
||||
super
|
||||
@io = $stdout
|
||||
end
|
||||
|
||||
# Reporte subject
|
||||
#
|
||||
# @param [Subject] subject
|
||||
#
|
||||
|
@ -13,7 +25,7 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def subject(subject)
|
||||
stats.subject
|
||||
super
|
||||
puts("Subject: #{subject.identification}")
|
||||
end
|
||||
|
||||
|
@ -24,7 +36,7 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def error_stream
|
||||
@config.debug? ? io : StringIO.new
|
||||
debug? ? io : StringIO.new
|
||||
end
|
||||
|
||||
# Return output stream
|
||||
|
@ -34,7 +46,7 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def output_stream
|
||||
@config.debug? ? io : StringIO.new
|
||||
debug? ? io : StringIO.new
|
||||
end
|
||||
|
||||
# Report mutation
|
||||
|
@ -46,7 +58,9 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def mutation(mutation)
|
||||
if @config.debug?
|
||||
super
|
||||
|
||||
if debug?
|
||||
colorized_diff(mutation)
|
||||
end
|
||||
|
||||
|
@ -61,96 +75,37 @@ module Mutant
|
|||
#
|
||||
# @api private
|
||||
#
|
||||
def config(config)
|
||||
puts 'Mutant configuration:'
|
||||
puts "Matcher: #{config.matcher.inspect}"
|
||||
puts "Filter: #{config.filter.inspect}"
|
||||
puts "Strategy: #{config.strategy.inspect}"
|
||||
def print_config
|
||||
message = []
|
||||
message << 'Mutant configuration:'
|
||||
message << "Matcher: #{config.matcher.inspect}"
|
||||
message << "Filter: #{config.filter.inspect}"
|
||||
message << "Strategy: #{config.strategy.inspect}"
|
||||
puts message.join("\n")
|
||||
end
|
||||
|
||||
# Report noop
|
||||
#
|
||||
# @param [Killer] killer
|
||||
#
|
||||
# @return [self]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def noop(killer)
|
||||
color, word =
|
||||
if killer.fail?
|
||||
[Color::GREEN, 'Alive']
|
||||
else
|
||||
[Color::RED, 'Killed']
|
||||
end
|
||||
## Reporter killer
|
||||
##
|
||||
## @param [Killer] killer
|
||||
##
|
||||
## @return [self]
|
||||
##
|
||||
## @api private
|
||||
##
|
||||
#def killer(killer)
|
||||
# super
|
||||
|
||||
print_killer(color, word, killer)
|
||||
# status = killer.killed? ? 'Killed' : 'Alive'
|
||||
# color = killer.success? ? Color::GREEN : Color::RED
|
||||
|
||||
unless killer.fail?
|
||||
puts(killer.mutation.source)
|
||||
stats.noop_fail(killer)
|
||||
end
|
||||
# puts(colorize(color, "#{status}: #{killer.identification} (%02.2fs)" % killer.runtime))
|
||||
|
||||
self
|
||||
end
|
||||
# unless killer.success?
|
||||
# colorized_diff(killer.mutation)
|
||||
# end
|
||||
|
||||
# Reporter killer
|
||||
#
|
||||
# @param [Killer] killer
|
||||
#
|
||||
# @return [self]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def killer(killer)
|
||||
stats.killer(killer)
|
||||
|
||||
color, word =
|
||||
if killer.fail?
|
||||
[Color::RED, 'Alive']
|
||||
else
|
||||
[Color::GREEN, 'Killed']
|
||||
end
|
||||
|
||||
print_killer(color, word, killer)
|
||||
|
||||
if killer.fail?
|
||||
colorized_diff(killer.mutation)
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
# Report errors
|
||||
#
|
||||
# @param [Enumerable<Killer>] errors
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
# @return [self]
|
||||
#
|
||||
def errors(errors)
|
||||
errors.each do |error|
|
||||
failure(error)
|
||||
end
|
||||
|
||||
puts
|
||||
puts "subjects: #{stats.subjects}"
|
||||
puts "mutations: #{stats.mutations}"
|
||||
puts "noop_fails: #{stats.noop_fails}"
|
||||
puts "kills: #{stats.kills}"
|
||||
puts "alive: #{stats.alive}"
|
||||
puts "mtime: %02.2fs" % stats.time
|
||||
puts "rtime: %02.2fs" % stats.runtime
|
||||
end
|
||||
|
||||
# Return IO stream
|
||||
#
|
||||
# @return [IO]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
attr_reader :io
|
||||
# self
|
||||
#end
|
||||
|
||||
# Return stats
|
||||
#
|
||||
|
@ -162,33 +117,13 @@ module Mutant
|
|||
|
||||
private
|
||||
|
||||
# Initialize reporter
|
||||
# Return IO stream
|
||||
#
|
||||
# @param [Config] config
|
||||
#
|
||||
# @return [undefined]
|
||||
# @return [IO]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def initialize(config)
|
||||
super
|
||||
@io = $stdout
|
||||
@stats = Stats.new
|
||||
end
|
||||
|
||||
# Report failure on killer
|
||||
#
|
||||
# @param [Killer] killer
|
||||
#
|
||||
# @return [undefined]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def failure(killer)
|
||||
puts(colorize(Color::RED, "!!! Mutant alive: #{killer.identification} !!!"))
|
||||
colorized_diff(killer.mutation)
|
||||
puts("Took: (%02.2fs)" % killer.runtime)
|
||||
end
|
||||
attr_reader :io
|
||||
|
||||
# Test for colored output
|
||||
#
|
||||
|
@ -241,7 +176,7 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def colorized_diff(mutation)
|
||||
if mutation.kind_of?(Mutation::Noop)
|
||||
if mutation.kind_of?(Mutation::Neutral)
|
||||
puts mutation.original_source
|
||||
return
|
||||
end
|
||||
|
@ -252,27 +187,13 @@ module Mutant
|
|||
|
||||
# FIXME remove this branch before release
|
||||
if diff.empty?
|
||||
raise "Unable to create a diff, so ast mutation or to_source has an error!"
|
||||
raise 'Unable to create a diff, so ast mutation or to_source has an error!'
|
||||
end
|
||||
|
||||
puts(diff)
|
||||
self
|
||||
end
|
||||
|
||||
# Print killer
|
||||
#
|
||||
# @param [Color] color
|
||||
# @param [String] word
|
||||
# @param [Killer] killer
|
||||
#
|
||||
# @return [undefined]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def print_killer(color, word, killer)
|
||||
puts(colorize(color, "#{word}: #{killer.identification} (%02.2fs)" % killer.runtime))
|
||||
end
|
||||
|
||||
# Test for output to tty
|
||||
#
|
||||
# @return [true]
|
||||
|
@ -287,6 +208,7 @@ module Mutant
|
|||
@io.respond_to?(:tty?) && @io.tty?
|
||||
end
|
||||
memoize :tty?
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,45 +4,48 @@ module Mutant
|
|||
# Stats gathered while reporter is running
|
||||
class Stats
|
||||
|
||||
# Return subject count
|
||||
#
|
||||
# @return [Fixnum]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
attr_reader :subjects
|
||||
# A counter with fail counts
|
||||
class Counter
|
||||
include Equalizer.new(:count, :fails)
|
||||
|
||||
# Return mutation count
|
||||
#
|
||||
# @return [Fixnum]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
attr_reader :mutations
|
||||
attr_reader :count
|
||||
|
||||
# Return skip count
|
||||
#
|
||||
# @return [Fixnum]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
attr_reader :noop_fails
|
||||
# Return fail count
|
||||
#
|
||||
# @return [Fixnum]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
attr_reader :fails
|
||||
|
||||
# Return kill count
|
||||
#
|
||||
# @return [Fixnum]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
attr_reader :kills
|
||||
# Initialize object
|
||||
#
|
||||
# @return [undefined]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def initialize
|
||||
@count = @fails = 0
|
||||
end
|
||||
|
||||
# Return mutation runtime
|
||||
#
|
||||
# @return [Float]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
attr_reader :time
|
||||
# Count killer
|
||||
#
|
||||
# @param [Killer] killer
|
||||
#
|
||||
# @return [self]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def handle(killer)
|
||||
@count += 1
|
||||
unless killer.success?
|
||||
@fails += 1
|
||||
end
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
include Equalizer.new(:start, :counts, :killers)
|
||||
|
||||
# Initialize object
|
||||
#
|
||||
|
@ -51,8 +54,76 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def initialize
|
||||
@start = Time.now
|
||||
@noop_fails = @subjects = @mutations = @kills = @time = 0
|
||||
@start = start
|
||||
@counts = Hash.new(0)
|
||||
@killers = {}
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Return counts
|
||||
#
|
||||
# @return [Hash]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
attr_reader :counts
|
||||
|
||||
# Return start time
|
||||
#
|
||||
# @return [Time]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
attr_reader :start
|
||||
|
||||
# Return killers
|
||||
#
|
||||
# @return [Hash]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
attr_reader :killers
|
||||
|
||||
public
|
||||
|
||||
# Count subject
|
||||
#
|
||||
# @return [self]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def count_subject
|
||||
@counts[:subject] += 1
|
||||
end
|
||||
|
||||
# Count killer
|
||||
#
|
||||
# @param [Killer] killer
|
||||
#
|
||||
# @return [self]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def count_killer(killer)
|
||||
counter = @killers[killer.mutation.class] ||= Counter.new
|
||||
counter.handle(killer)
|
||||
self
|
||||
end
|
||||
|
||||
# Test for errors
|
||||
#
|
||||
# @return [true]
|
||||
# if there are errors
|
||||
#
|
||||
# @return [false]
|
||||
# otherwise
|
||||
#
|
||||
def errors?
|
||||
@killers.values.inject(0) do |count, counter|
|
||||
p counter, count
|
||||
count += counter.fails
|
||||
end.nonzero?
|
||||
end
|
||||
|
||||
# Return runtime in seconds
|
||||
|
@ -65,56 +136,6 @@ module Mutant
|
|||
Time.now - @start
|
||||
end
|
||||
|
||||
# Count subject
|
||||
#
|
||||
# @return [self]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def subject
|
||||
@subjects +=1
|
||||
self
|
||||
end
|
||||
|
||||
# Return number of mutants alive
|
||||
#
|
||||
# @return [Fixnum]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def alive
|
||||
@mutations - @kills
|
||||
end
|
||||
|
||||
# Count noop mutation fail
|
||||
#
|
||||
# @param [Killer] killer
|
||||
#
|
||||
# @return [self]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def noop_fail(killer)
|
||||
@noop_fails += 1
|
||||
@time += killer.runtime
|
||||
self
|
||||
end
|
||||
|
||||
# Count killer
|
||||
#
|
||||
# @param [Killer] killer
|
||||
#
|
||||
# @return [self]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def killer(killer)
|
||||
@mutations +=1
|
||||
@kills +=1 unless killer.fail?
|
||||
@time += killer.runtime
|
||||
self
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,14 +4,6 @@ module Mutant
|
|||
include Adamantium::Flat
|
||||
extend MethodObject
|
||||
|
||||
# Return killers with errors
|
||||
#
|
||||
# @return [Enumerable<Killer>]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
attr_reader :errors
|
||||
|
||||
# Test for failure
|
||||
#
|
||||
# @return [true]
|
||||
|
@ -23,7 +15,7 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def fail?
|
||||
!errors.empty?
|
||||
reporter.errors?
|
||||
end
|
||||
|
||||
# Return config
|
||||
|
@ -45,12 +37,18 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def initialize(config)
|
||||
@config, @errors = config, []
|
||||
|
||||
util_reporter = reporter
|
||||
util_reporter.config(config)
|
||||
@config = config
|
||||
run
|
||||
util_reporter.errors(@errors)
|
||||
end
|
||||
|
||||
# Return strategy
|
||||
#
|
||||
# @return [Strategy]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def strategy
|
||||
config.strategy
|
||||
end
|
||||
|
||||
# Return reporter
|
||||
|
@ -70,10 +68,14 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def run
|
||||
reporter.print_config
|
||||
util = strategy
|
||||
util.setup
|
||||
config.matcher.each do |subject|
|
||||
reporter.subject(subject)
|
||||
run_subject(subject)
|
||||
end
|
||||
util.teardown
|
||||
end
|
||||
|
||||
# Run mutation killers on subject
|
||||
|
@ -85,7 +87,7 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def run_subject(subject)
|
||||
return unless noop(subject)
|
||||
return unless test_noop(subject)
|
||||
subject.each do |mutation|
|
||||
next unless config.filter.match?(mutation)
|
||||
reporter.mutation(mutation)
|
||||
|
@ -93,26 +95,22 @@ module Mutant
|
|||
end
|
||||
end
|
||||
|
||||
# Test for noop mutation
|
||||
#
|
||||
# @param [Subject] subject
|
||||
# Test noop mutation
|
||||
#
|
||||
# @return [true]
|
||||
# if noop mutation is okay
|
||||
# if noop mutation is alive
|
||||
#
|
||||
# @return [false]
|
||||
# otherwise
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def noop(subject)
|
||||
killer = killer(subject.noop)
|
||||
reporter.noop(killer)
|
||||
unless killer.fail?
|
||||
@errors << killer
|
||||
def test_noop(subject)
|
||||
noop = subject.noop
|
||||
unless kill(noop)
|
||||
reporter.noop(noop)
|
||||
return false
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
|
@ -121,7 +119,7 @@ module Mutant
|
|||
# @param [Mutation] mutation
|
||||
#
|
||||
# @return [true]
|
||||
# if killer was unsuccessful
|
||||
# if killer was successful
|
||||
#
|
||||
# @return [false]
|
||||
# otherwise
|
||||
|
@ -130,11 +128,8 @@ module Mutant
|
|||
#
|
||||
def kill(mutation)
|
||||
killer = killer(mutation)
|
||||
reporter.killer(killer)
|
||||
|
||||
if killer.fail?
|
||||
@errors << killer
|
||||
end
|
||||
reporter.report_killer(killer)
|
||||
killer.success?
|
||||
end
|
||||
|
||||
# Return killer for mutation
|
||||
|
@ -144,7 +139,7 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def killer(mutation)
|
||||
config.strategy.kill(mutation)
|
||||
strategy.kill(mutation)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -24,24 +24,24 @@ module Mutant
|
|||
@config = config
|
||||
end
|
||||
|
||||
# Return output stream
|
||||
# Perform setup
|
||||
#
|
||||
# @return [IO]
|
||||
# @return [self]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def output_stream
|
||||
config.reporter.output_stream
|
||||
def setup
|
||||
self
|
||||
end
|
||||
|
||||
# Return error stream
|
||||
# Perform teardown
|
||||
#
|
||||
# @return [IO]
|
||||
# @return [self]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def error_stream
|
||||
config.reporter.error_stream
|
||||
def teardown
|
||||
self
|
||||
end
|
||||
|
||||
# Kill mutation
|
||||
|
@ -65,21 +65,5 @@ module Mutant
|
|||
def killer
|
||||
self.class::KILLER
|
||||
end
|
||||
|
||||
# Static strategies
|
||||
class Static < self
|
||||
include Equalizer.new
|
||||
|
||||
# Always fail to kill strategy
|
||||
class Fail < self
|
||||
KILLER = Killer::Static::Fail
|
||||
end
|
||||
|
||||
# Always succeed to kill strategy
|
||||
class Success < self
|
||||
KILLER = Killer::Static::Success
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
18
lib/mutant/strategy/static.rb
Normal file
18
lib/mutant/strategy/static.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
module Mutant
|
||||
class Strategy
|
||||
# Static strategies
|
||||
class Static < self
|
||||
|
||||
# Always fail to kill strategy
|
||||
class Fail < self
|
||||
KILLER = Killer::Static::Fail
|
||||
end
|
||||
|
||||
# Always succeed to kill strategy
|
||||
class Success < self
|
||||
KILLER = Killer::Static::Success
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -32,7 +32,7 @@ module Mutant
|
|||
def each
|
||||
return to_enum unless block_given?
|
||||
Mutator.each(node) do |mutant|
|
||||
yield Mutation.new(self, mutant)
|
||||
yield Mutation::Evil.new(self, mutant)
|
||||
end
|
||||
|
||||
self
|
||||
|
@ -45,7 +45,7 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def noop
|
||||
Mutation::Noop.new(self)
|
||||
Mutation::Neutral.new(self, node)
|
||||
end
|
||||
memoize :noop
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def subtype
|
||||
"#{context.name}.#{node.name}"
|
||||
"#{context.identification}.#{node.body.name}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
9
spec/support/ice_nine_config.rb
Normal file
9
spec/support/ice_nine_config.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
require 'ice_nine'
|
||||
|
||||
module IceNine
|
||||
class Freezer
|
||||
class RSpec < NoFreeze
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -16,6 +16,11 @@ end
|
|||
describe Mutant::CLI, '.new' do
|
||||
|
||||
let(:object) { described_class }
|
||||
let(:time) { Time.now }
|
||||
|
||||
before do
|
||||
Time.stub(:now => time)
|
||||
end
|
||||
|
||||
# Defaults
|
||||
let(:expected_filter) { Mutant::Mutation::Filter::ALL }
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Mutant::Killer,'#fail?' do
|
||||
subject { object.fail? }
|
||||
|
||||
let(:object) { class_under_test.new(strategy, mutation) }
|
||||
let(:strategy) { mock('Strategy') }
|
||||
let(:mutation) { mock('Mutation') }
|
||||
|
||||
before do
|
||||
mutation.stub(:insert)
|
||||
mutation.stub(:reset)
|
||||
end
|
||||
|
||||
let(:class_under_test) do
|
||||
kill_state = self.kill_state
|
||||
Class.new(described_class) do
|
||||
define_method :run do
|
||||
kill_state
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when mutant was killed' do
|
||||
let(:kill_state) { true }
|
||||
|
||||
it_should_behave_like 'an idempotent method'
|
||||
|
||||
it { should be(false) }
|
||||
end
|
||||
|
||||
context 'when mutant was NOT killed' do
|
||||
let(:kill_state) { false }
|
||||
|
||||
it_should_behave_like 'an idempotent method'
|
||||
|
||||
it { should be(true) }
|
||||
end
|
||||
end
|
|
@ -20,14 +20,14 @@ describe Mutant::Killer::Rspec, '.new' do
|
|||
context 'when run exits zero' do
|
||||
let(:exit_status) { 0 }
|
||||
|
||||
its(:fail?) { should be(true) }
|
||||
its(:killed?) { should be(false) }
|
||||
it { should be_a(described_class) }
|
||||
end
|
||||
|
||||
context 'when run exits nonzero' do
|
||||
let(:exit_status) { 1 }
|
||||
|
||||
its(:fail?) { should be(false) }
|
||||
its(:killed?) { should be(true) }
|
||||
it { should be_a(described_class) }
|
||||
end
|
||||
end
|
||||
|
|
28
spec/unit/mutant/killer/success_predicate_spec.rb
Normal file
28
spec/unit/mutant/killer/success_predicate_spec.rb
Normal file
|
@ -0,0 +1,28 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Mutant::Killer, '#success?' do
|
||||
subject { object.success? }
|
||||
|
||||
let(:object) { class_under_test.new(strategy, mutation) }
|
||||
let(:strategy) { mock('Strategy') }
|
||||
let(:mutation) { mock('Mutation', :success? => kill_state) }
|
||||
let(:kill_state) { mock('Kill State') }
|
||||
|
||||
before do
|
||||
kill_state.stub(:freeze => kill_state, :dup => kill_state)
|
||||
end
|
||||
|
||||
let(:class_under_test) do
|
||||
Class.new(described_class) do
|
||||
def run
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it_should_behave_like 'an idempotent method'
|
||||
|
||||
it 'should use kill state to gather success' do
|
||||
mutation.should_receive(:success?).with(object).and_return(kill_state)
|
||||
should be(kill_state)
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue