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
|
module Mutant
|
||||||
# Abstract reporter
|
# Abstract base class for reporters
|
||||||
class Reporter
|
class Reporter
|
||||||
include Adamantium::Flat, AbstractType
|
include Adamantium::Flat, AbstractType
|
||||||
|
|
||||||
ACTIONS = {
|
|
||||||
Subject => :subject,
|
|
||||||
}.freeze
|
|
||||||
|
|
||||||
# Report object
|
# Report object
|
||||||
#
|
#
|
||||||
# @param [Object] object
|
# @param [Object] object
|
||||||
|
@ -15,14 +11,7 @@ module Mutant
|
||||||
#
|
#
|
||||||
# @api private
|
# @api private
|
||||||
#
|
#
|
||||||
def report(object)
|
abstract_method :report
|
||||||
klass = object.class
|
|
||||||
method = self.class::ACTIONS.fetch(klass) do
|
|
||||||
raise "No reporter for: #{klass}"
|
|
||||||
end
|
|
||||||
send(method, object)
|
|
||||||
self
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,17 +4,85 @@ module Mutant
|
||||||
class CLI < self
|
class CLI < self
|
||||||
include Concord.new(:io)
|
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
|
private
|
||||||
|
|
||||||
# Report subject
|
# Report subject
|
||||||
#
|
#
|
||||||
# @param [Subject] _subject
|
# @param [Subject] subject
|
||||||
#
|
#
|
||||||
# @return [undefined]
|
# @return [undefined]
|
||||||
#
|
#
|
||||||
# @api private
|
# @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
|
end
|
||||||
|
|
||||||
# Report mutation
|
# Report mutation
|
||||||
|
@ -28,7 +96,7 @@ module Mutant
|
||||||
def mutation(_mutation)
|
def mutation(_mutation)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Report start
|
# Report configuration
|
||||||
#
|
#
|
||||||
# @param [Mutant::Config] config
|
# @param [Mutant::Config] config
|
||||||
#
|
#
|
||||||
|
@ -45,6 +113,41 @@ module Mutant
|
||||||
puts message.join("\n")
|
puts message.join("\n")
|
||||||
end
|
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
|
# Report killer
|
||||||
#
|
#
|
||||||
# @param [Killer] killer
|
# @param [Killer] killer
|
||||||
|
@ -54,19 +157,27 @@ module Mutant
|
||||||
# @api private
|
# @api private
|
||||||
#
|
#
|
||||||
def killer(killer)
|
def killer(killer)
|
||||||
status = killer.killed? ? 'Killed' : 'Alive'
|
if killer.success?
|
||||||
color = killer.success? ? Color::GREEN : Color::RED
|
char('.', Color::GREEN)
|
||||||
|
return
|
||||||
puts(colorize(color, "%s: %s (%02.2fs)" % [status, killer.identification, killer.runtime]))
|
|
||||||
|
|
||||||
unless killer.success?
|
|
||||||
colorized_diff(killer.mutation)
|
|
||||||
end
|
end
|
||||||
|
char('F', Color::RED)
|
||||||
self
|
self
|
||||||
end
|
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
|
# Test for colored output
|
||||||
#
|
#
|
||||||
|
|
|
@ -22,9 +22,22 @@ module Mutant
|
||||||
#
|
#
|
||||||
def initialize(config)
|
def initialize(config)
|
||||||
@config = config
|
@config = config
|
||||||
|
@start = Time.now
|
||||||
run
|
run
|
||||||
|
@end = Time.now
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Return runtime
|
||||||
|
#
|
||||||
|
# @return [Float]
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
def runtime
|
||||||
|
@end - @start
|
||||||
|
end
|
||||||
|
memoize :runtime
|
||||||
|
|
||||||
# Test if runner failed
|
# Test if runner failed
|
||||||
#
|
#
|
||||||
# @return [true]
|
# @return [true]
|
||||||
|
|
|
@ -56,12 +56,15 @@ module Mutant
|
||||||
# @api private
|
# @api private
|
||||||
#
|
#
|
||||||
def run
|
def run
|
||||||
|
report(config)
|
||||||
strategy = self.strategy
|
strategy = self.strategy
|
||||||
strategy.setup
|
strategy.setup
|
||||||
@subjects = config.subjects.map do |subject|
|
@subjects = config.subjects.map do |subject|
|
||||||
Subject.run(self, subject)
|
Subject.run(self, subject)
|
||||||
end
|
end
|
||||||
strategy.teardown
|
strategy.teardown
|
||||||
|
@end = Time.now
|
||||||
|
report(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -68,9 +68,11 @@ module Mutant
|
||||||
# @api private
|
# @api private
|
||||||
#
|
#
|
||||||
def run
|
def run
|
||||||
|
report(subject)
|
||||||
@mutations = subject.map do |mutation|
|
@mutations = subject.map do |mutation|
|
||||||
Mutation.run(config, mutation)
|
Mutation.run(config, mutation)
|
||||||
end
|
end
|
||||||
|
report(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,10 +5,19 @@ describe Mutant::Runner::Config, '#subjects' do
|
||||||
|
|
||||||
subject { object.subjects }
|
subject { object.subjects }
|
||||||
|
|
||||||
let(:config) { mock('Config', :subjects => [mutation_subject], :strategy => strategy) }
|
let(:config) do
|
||||||
let(:strategy) { mock('Strategy') }
|
mock(
|
||||||
let(:mutation_subject) { mock('Mutation subject') }
|
'Config',
|
||||||
let(:subject_runner) { mock('Subject runner') }
|
: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
|
class DummySubjectRunner
|
||||||
include Concord.new(:config, :mutation)
|
include Concord.new(:config, :mutation)
|
||||||
|
@ -19,6 +28,7 @@ describe Mutant::Runner::Config, '#subjects' do
|
||||||
before do
|
before do
|
||||||
strategy.stub(:setup)
|
strategy.stub(:setup)
|
||||||
strategy.stub(:teardown)
|
strategy.stub(:teardown)
|
||||||
|
reporter.stub(:report => reporter)
|
||||||
stub_const('Mutant::Runner::Subject', DummySubjectRunner)
|
stub_const('Mutant::Runner::Subject', DummySubjectRunner)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,20 @@ describe Mutant::Runner::Config, '#success?' do
|
||||||
|
|
||||||
let(:object) { described_class.run(config) }
|
let(:object) { described_class.run(config) }
|
||||||
|
|
||||||
let(:config) { mock('Config', :strategy => strategy, :subjects => subjects) }
|
let(:config) do
|
||||||
let(:strategy) { mock('Strategy') }
|
mock(
|
||||||
let(:subjects) { [subject_a, subject_b] }
|
'Config',
|
||||||
let(:subject_a) { mock('Subject A', :fails? => false) }
|
:reporter => reporter,
|
||||||
let(:subject_b) { mock('Subject B', :fails? => false) }
|
: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
|
class DummySubjectRunner
|
||||||
include Concord.new(:config, :subject)
|
include Concord.new(:config, :subject)
|
||||||
|
@ -25,6 +34,7 @@ describe Mutant::Runner::Config, '#success?' do
|
||||||
|
|
||||||
before do
|
before do
|
||||||
stub_const('Mutant::Runner::Subject', DummySubjectRunner)
|
stub_const('Mutant::Runner::Subject', DummySubjectRunner)
|
||||||
|
reporter.stub(:report => reporter)
|
||||||
strategy.stub(:setup)
|
strategy.stub(:setup)
|
||||||
strategy.stub(:teardown)
|
strategy.stub(:teardown)
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,12 +5,17 @@ describe Mutant::Runner::Subject, '#success?' do
|
||||||
|
|
||||||
let(:object) { described_class.run(config, mutation_subject) }
|
let(:object) { described_class.run(config, mutation_subject) }
|
||||||
|
|
||||||
|
let(:reporter) { mock('Reporter') }
|
||||||
let(:mutation_subject) { mock('Subject', :map => mutations) }
|
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_a) { mock('Mutation A', :failed? => false) }
|
||||||
let(:mutation_b) { mock('Mutation B', :failed? => false) }
|
let(:mutation_b) { mock('Mutation B', :failed? => false) }
|
||||||
let(:mutations) { [mutation_a, mutation_b] }
|
let(:mutations) { [mutation_a, mutation_b] }
|
||||||
|
|
||||||
|
before do
|
||||||
|
reporter.stub(:report => reporter)
|
||||||
|
end
|
||||||
|
|
||||||
class DummyMutationRunner
|
class DummyMutationRunner
|
||||||
include Concord.new(:config, :mutation)
|
include Concord.new(:config, :mutation)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue