Connect reporters with runners
This fixes all unit and integration specs!
This commit is contained in:
parent
c76b7c05c5
commit
f2a6243ac0
8 changed files with 178 additions and 35 deletions
|
@ -1,12 +1,8 @@
|
|||
module Mutant
|
||||
# Abstract reporter
|
||||
# Abstract base class for reporters
|
||||
class Reporter
|
||||
include Adamantium::Flat, AbstractType
|
||||
|
||||
ACTIONS = {
|
||||
Subject => :subject,
|
||||
}.freeze
|
||||
|
||||
# Report object
|
||||
#
|
||||
# @param [Object] object
|
||||
|
@ -15,14 +11,7 @@ module Mutant
|
|||
#
|
||||
# @api private
|
||||
#
|
||||
def report(object)
|
||||
klass = object.class
|
||||
method = self.class::ACTIONS.fetch(klass) do
|
||||
raise "No reporter for: #{klass}"
|
||||
end
|
||||
send(method, object)
|
||||
self
|
||||
end
|
||||
abstract_method :report
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,17 +4,85 @@ module Mutant
|
|||
class CLI < self
|
||||
include Concord.new(:io)
|
||||
|
||||
ACTIONS = {
|
||||
Config => :config,
|
||||
Subject => :subject,
|
||||
Killer => :killer,
|
||||
Runner::Subject => :subject_results,
|
||||
Runner::Config => :config_results
|
||||
}.freeze
|
||||
|
||||
# Perform lookup
|
||||
#
|
||||
# @param [Object] object
|
||||
#
|
||||
# @return [Symbol]
|
||||
# if found
|
||||
#
|
||||
# @raise [RuntimeError]
|
||||
# otherwise
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def self.lookup(object)
|
||||
current = klass = object.class
|
||||
begin
|
||||
symbol = ACTIONS[current]
|
||||
return symbol if symbol
|
||||
current = current.superclass
|
||||
end while current < ::Object
|
||||
raise "No reporter for #{klass}"
|
||||
end
|
||||
|
||||
# Report object
|
||||
#
|
||||
# @param [Object] object
|
||||
#
|
||||
# @return [self]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def report(object)
|
||||
method = self.class.lookup(object)
|
||||
send(method, object)
|
||||
self
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Report subject
|
||||
#
|
||||
# @param [Subject] _subject
|
||||
# @param [Subject] subject
|
||||
#
|
||||
# @return [undefined]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def subject(_subject)
|
||||
def subject(subject)
|
||||
puts
|
||||
puts(subject.identification)
|
||||
end
|
||||
|
||||
# Report subject results
|
||||
#
|
||||
# @param [Subject]
|
||||
#
|
||||
# @return [undefined]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def subject_results(runner)
|
||||
mutations = runner.mutations
|
||||
if mutations.any?
|
||||
puts # There is an open line
|
||||
end
|
||||
time = mutations.map(&:runtime).inject(0, :+)
|
||||
mutations = mutations.length
|
||||
fails = runner.failed_mutations
|
||||
fails = fails.length
|
||||
kills = mutations - fails
|
||||
coverage = kills.to_f / mutations * 100
|
||||
puts('(%02d/%02d) %3d%% - %0.02fs' % [kills, mutations, coverage, time])
|
||||
end
|
||||
|
||||
# Report mutation
|
||||
|
@ -28,7 +96,7 @@ module Mutant
|
|||
def mutation(_mutation)
|
||||
end
|
||||
|
||||
# Report start
|
||||
# Report configuration
|
||||
#
|
||||
# @param [Mutant::Config] config
|
||||
#
|
||||
|
@ -45,6 +113,41 @@ module Mutant
|
|||
puts message.join("\n")
|
||||
end
|
||||
|
||||
# Report configuration results
|
||||
#
|
||||
# TODO: Break this stuff into smaller methods or factor out in a subclass
|
||||
#
|
||||
# @param [Reporter::Config] runner
|
||||
#
|
||||
# @return [self]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def config_results(runner)
|
||||
message = []
|
||||
subjects = runner.subjects
|
||||
mutations = subjects.map(&:mutations).flatten
|
||||
killtime = mutations.map(&:runtime).inject(0, :+)
|
||||
kills = mutations.select(&:success?)
|
||||
|
||||
subjects = subjects.length
|
||||
mutations = mutations.length
|
||||
kills = kills.length
|
||||
coverage = kills.to_f / mutations * 100
|
||||
runtime = runner.runtime
|
||||
|
||||
overhead = (runtime - killtime) / runtime * 100
|
||||
|
||||
puts "Subjects: #{subjects}"
|
||||
puts "Mutations: #{mutations}"
|
||||
puts "Kills: #{kills}"
|
||||
puts 'Runtime: %0.2fs' % runner.runtime
|
||||
puts 'Killtime: %0.2fs' % killtime
|
||||
puts 'Overhead: %0.2f%%' % overhead
|
||||
puts 'Coverage: %0.2f%%' % coverage
|
||||
end
|
||||
|
||||
|
||||
# Report killer
|
||||
#
|
||||
# @param [Killer] killer
|
||||
|
@ -54,19 +157,27 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def killer(killer)
|
||||
status = killer.killed? ? 'Killed' : 'Alive'
|
||||
color = killer.success? ? Color::GREEN : Color::RED
|
||||
|
||||
puts(colorize(color, "%s: %s (%02.2fs)" % [status, killer.identification, killer.runtime]))
|
||||
|
||||
unless killer.success?
|
||||
colorized_diff(killer.mutation)
|
||||
if killer.success?
|
||||
char('.', Color::GREEN)
|
||||
return
|
||||
end
|
||||
|
||||
char('F', Color::RED)
|
||||
self
|
||||
end
|
||||
|
||||
private
|
||||
# Write colorized char
|
||||
#
|
||||
# @param [String] char
|
||||
# @param [Color]
|
||||
#
|
||||
# @return [undefined]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def char(char, color)
|
||||
io.write(colorize(color, char))
|
||||
io.flush
|
||||
end
|
||||
|
||||
# Test for colored output
|
||||
#
|
||||
|
|
|
@ -22,9 +22,22 @@ module Mutant
|
|||
#
|
||||
def initialize(config)
|
||||
@config = config
|
||||
@start = Time.now
|
||||
run
|
||||
@end = Time.now
|
||||
end
|
||||
|
||||
# Return runtime
|
||||
#
|
||||
# @return [Float]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def runtime
|
||||
@end - @start
|
||||
end
|
||||
memoize :runtime
|
||||
|
||||
# Test if runner failed
|
||||
#
|
||||
# @return [true]
|
||||
|
|
|
@ -56,12 +56,15 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def run
|
||||
report(config)
|
||||
strategy = self.strategy
|
||||
strategy.setup
|
||||
@subjects = config.subjects.map do |subject|
|
||||
Subject.run(self, subject)
|
||||
end
|
||||
strategy.teardown
|
||||
@end = Time.now
|
||||
report(self)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -68,9 +68,11 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def run
|
||||
report(subject)
|
||||
@mutations = subject.map do |mutation|
|
||||
Mutation.run(config, mutation)
|
||||
end
|
||||
report(self)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -5,10 +5,19 @@ describe Mutant::Runner::Config, '#subjects' do
|
|||
|
||||
subject { object.subjects }
|
||||
|
||||
let(:config) { mock('Config', :subjects => [mutation_subject], :strategy => strategy) }
|
||||
let(:strategy) { mock('Strategy') }
|
||||
let(:mutation_subject) { mock('Mutation subject') }
|
||||
let(:subject_runner) { mock('Subject runner') }
|
||||
let(:config) do
|
||||
mock(
|
||||
'Config',
|
||||
:subjects => [mutation_subject],
|
||||
:strategy => strategy,
|
||||
:reporter => reporter
|
||||
)
|
||||
end
|
||||
|
||||
let(:reporter) { mock('Reporter') }
|
||||
let(:strategy) { mock('Strategy') }
|
||||
let(:mutation_subject) { mock('Mutation subject') }
|
||||
let(:subject_runner) { mock('Subject runner') }
|
||||
|
||||
class DummySubjectRunner
|
||||
include Concord.new(:config, :mutation)
|
||||
|
@ -19,6 +28,7 @@ describe Mutant::Runner::Config, '#subjects' do
|
|||
before do
|
||||
strategy.stub(:setup)
|
||||
strategy.stub(:teardown)
|
||||
reporter.stub(:report => reporter)
|
||||
stub_const('Mutant::Runner::Subject', DummySubjectRunner)
|
||||
end
|
||||
|
||||
|
|
|
@ -5,11 +5,20 @@ describe Mutant::Runner::Config, '#success?' do
|
|||
|
||||
let(:object) { described_class.run(config) }
|
||||
|
||||
let(:config) { mock('Config', :strategy => strategy, :subjects => subjects) }
|
||||
let(:strategy) { mock('Strategy') }
|
||||
let(:subjects) { [subject_a, subject_b] }
|
||||
let(:subject_a) { mock('Subject A', :fails? => false) }
|
||||
let(:subject_b) { mock('Subject B', :fails? => false) }
|
||||
let(:config) do
|
||||
mock(
|
||||
'Config',
|
||||
:reporter => reporter,
|
||||
:strategy => strategy,
|
||||
:subjects => subjects
|
||||
)
|
||||
end
|
||||
|
||||
let(:reporter) { mock('Reporter') }
|
||||
let(:strategy) { mock('Strategy') }
|
||||
let(:subjects) { [subject_a, subject_b] }
|
||||
let(:subject_a) { mock('Subject A', :fails? => false) }
|
||||
let(:subject_b) { mock('Subject B', :fails? => false) }
|
||||
|
||||
class DummySubjectRunner
|
||||
include Concord.new(:config, :subject)
|
||||
|
@ -25,6 +34,7 @@ describe Mutant::Runner::Config, '#success?' do
|
|||
|
||||
before do
|
||||
stub_const('Mutant::Runner::Subject', DummySubjectRunner)
|
||||
reporter.stub(:report => reporter)
|
||||
strategy.stub(:setup)
|
||||
strategy.stub(:teardown)
|
||||
end
|
||||
|
|
|
@ -5,12 +5,17 @@ describe Mutant::Runner::Subject, '#success?' do
|
|||
|
||||
let(:object) { described_class.run(config, mutation_subject) }
|
||||
|
||||
let(:reporter) { mock('Reporter') }
|
||||
let(:mutation_subject) { mock('Subject', :map => mutations) }
|
||||
let(:config) { mock('Config') }
|
||||
let(:config) { mock('Config', :reporter => reporter) }
|
||||
let(:mutation_a) { mock('Mutation A', :failed? => false) }
|
||||
let(:mutation_b) { mock('Mutation B', :failed? => false) }
|
||||
let(:mutations) { [mutation_a, mutation_b] }
|
||||
|
||||
before do
|
||||
reporter.stub(:report => reporter)
|
||||
end
|
||||
|
||||
class DummyMutationRunner
|
||||
include Concord.new(:config, :mutation)
|
||||
|
||||
|
|
Loading…
Reference in a new issue