264 lines
5.8 KiB
Ruby
264 lines
5.8 KiB
Ruby
module Mutant
|
|
class Reporter
|
|
# Reporter that reports in human readable format
|
|
class CLI < self
|
|
include Concord::Public.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
|
|
#
|
|
# @return [undefined]
|
|
#
|
|
# @api private
|
|
#
|
|
def subject(subject)
|
|
puts
|
|
puts(subject.identification)
|
|
end
|
|
|
|
# Report subject results
|
|
#
|
|
# @param [Subject]
|
|
#
|
|
# @return [undefined]
|
|
#
|
|
# @api private
|
|
#
|
|
def subject_results(runner)
|
|
mutations = runner.mutations
|
|
puts if mutations.any?
|
|
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
|
|
#
|
|
# @param [Mutation] _mutation
|
|
#
|
|
# @return [undefined]
|
|
#
|
|
# @api private
|
|
#
|
|
def mutation(_mutation)
|
|
end
|
|
|
|
# Report configuration
|
|
#
|
|
# @param [Mutant::Config] config
|
|
#
|
|
# @return [self]
|
|
#
|
|
# @api private
|
|
#
|
|
def config(config)
|
|
puts 'Mutant configuration:'
|
|
puts "Matcher: #{config.matcher.inspect }"
|
|
puts "Filter: #{config.filter.inspect }"
|
|
puts "Strategy: #{config.strategy.inspect}"
|
|
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' % runtime
|
|
puts 'Killtime: %0.2fs' % killtime
|
|
puts 'Overhead: %0.2f%%' % overhead
|
|
puts 'Coverage: %0.2f%%' % coverage
|
|
end
|
|
|
|
|
|
# Report killer
|
|
#
|
|
# @param [Killer] killer
|
|
#
|
|
# @return [self]
|
|
#
|
|
# @api private
|
|
#
|
|
def killer(killer)
|
|
if killer.success?
|
|
char('.', Color::GREEN)
|
|
return
|
|
end
|
|
char('F', Color::RED)
|
|
self
|
|
end
|
|
|
|
# Write colorized char
|
|
#
|
|
# @param [String] char
|
|
# @param [Color]
|
|
#
|
|
# @return [undefined]
|
|
#
|
|
# @api private
|
|
#
|
|
def char(char, color)
|
|
io = self.io
|
|
io.write(colorize(color, char))
|
|
io.flush
|
|
end
|
|
|
|
# Test for colored output
|
|
#
|
|
# @return [true]
|
|
# returns true if output is colored
|
|
#
|
|
# @return [false]
|
|
# returns false otherwise
|
|
#
|
|
# @api private
|
|
#
|
|
def color?
|
|
tty?
|
|
end
|
|
|
|
# Colorize message
|
|
#
|
|
# @param [Color] color
|
|
# @param [String] message
|
|
#
|
|
# @api private
|
|
#
|
|
# @return [String]
|
|
# returns colorized string if color is enabled
|
|
# returns unmodified message otherwise
|
|
#
|
|
def colorize(color, message)
|
|
color = Color::NONE unless color?
|
|
color.format(message)
|
|
end
|
|
|
|
# Write string to io
|
|
#
|
|
# @param [String] string
|
|
#
|
|
# @return [undefined]
|
|
#
|
|
# @api private
|
|
#
|
|
def puts(string="\n")
|
|
io.puts(string)
|
|
end
|
|
|
|
# Write colorized diff
|
|
#
|
|
# @param [Mutation] mutation
|
|
#
|
|
# @return [undefined]
|
|
#
|
|
# @api private
|
|
#
|
|
def colorized_diff(mutation)
|
|
if mutation.kind_of?(Mutation::Neutral)
|
|
puts mutation.original_source
|
|
return
|
|
end
|
|
|
|
original, current = mutation.original_source, mutation.source
|
|
differ = Differ.new(original, current)
|
|
diff = color? ? differ.colorized_diff : differ.diff
|
|
|
|
if diff.empty?
|
|
raise 'Unable to create a diff, so ast mutant or to_source does something strange!!'
|
|
end
|
|
|
|
puts(diff)
|
|
self
|
|
end
|
|
|
|
# Test for output to tty
|
|
#
|
|
# @return [true]
|
|
# returns true if output is a tty
|
|
#
|
|
# @return [false]
|
|
# returns false otherwise
|
|
#
|
|
# @api private
|
|
#
|
|
def tty?
|
|
@io.respond_to?(:tty?) && @io.tty?
|
|
end
|
|
memoize :tty?
|
|
|
|
end # CLI
|
|
end # Reporter
|
|
end # Mutant
|