From cee1d3943cf60d7d95528464ae6abd8d51e27122 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Thu, 16 Aug 2012 18:02:03 +0200 Subject: [PATCH] Add colorized diffs to cli reporter --- lib/mutant.rb | 5 +++ lib/mutant/color.rb | 27 +++++++++++++ lib/mutant/differ.rb | 54 ++++++++++++++++++++++++++ lib/mutant/reporter/cli.rb | 54 +++++++++++++++++++++++--- mutant.gemspec | 1 + spec/integration/mutant/differ_spec.rb | 15 +++++++ spec/spec_helper.rb | 1 + spec/support/compress_helper.rb | 10 +++++ 8 files changed, 161 insertions(+), 6 deletions(-) create mode 100644 lib/mutant/color.rb create mode 100644 lib/mutant/differ.rb create mode 100644 spec/integration/mutant/differ_spec.rb create mode 100644 spec/support/compress_helper.rb diff --git a/lib/mutant.rb b/lib/mutant.rb index 06a0e698..41450e46 100644 --- a/lib/mutant.rb +++ b/lib/mutant.rb @@ -4,6 +4,9 @@ require 'securerandom' require 'to_source' require 'ice_nine' require 'backports' +require 'diff/lcs' +require 'diff/lcs/hunk' +require 'pp' # Library namespace module Mutant @@ -72,6 +75,8 @@ require 'mutant/matcher/method/classifier' require 'mutant/killer' require 'mutant/killer/rspec' require 'mutant/runner' +require 'mutant/color' +require 'mutant/differ' require 'mutant/reporter' require 'mutant/reporter/null' require 'mutant/reporter/cli' diff --git a/lib/mutant/color.rb b/lib/mutant/color.rb new file mode 100644 index 00000000..61b89923 --- /dev/null +++ b/lib/mutant/color.rb @@ -0,0 +1,27 @@ +module Mutant + # Class to colorize strings + class Color + include Immutable + + def initialize(code) + @code = code + end + + def format(text) + "\e[#{@code}m#{text}\e[0m" + end + + NONE = Class.new(self) do + def initialize(*) + end + + def format(text) + text + end + end.new.freeze + + RED = Color.new(31) + GREEN = Color.new(32) + BLUE = Color.new(34) + end +end diff --git a/lib/mutant/differ.rb b/lib/mutant/differ.rb new file mode 100644 index 00000000..1a1cb211 --- /dev/null +++ b/lib/mutant/differ.rb @@ -0,0 +1,54 @@ +module Mutant + class Differ + include Immutable + + def initialize(old, new) + @new, @old = new.lines.map(&:chomp), old.lines.map(&:chomp) + @diffs = Diff::LCS.diff(@old, @new) + end + + def format + :unified + end + + def context_lines + 3 + end + + def length_difference + @new.size - @old.size + end + + def diff + output = '' + @diffs.each do |piece| + hunk = Diff::LCS::Hunk.new(@old, @new, piece, context_lines, length_difference) + output << hunk.diff(format) + output << "\n" + end + output + end + memoize :diff + + def colorized_diff + diff.lines.map do |line| + self.class.colorize_line(line) + end.join + end + memoize :colorized_diff + + def self.colorize_line(line) + case line[0].chr + when '+' + Color::GREEN + when '-' + Color::RED + when '@' + line[1].chr == '@' ? Color::BLUE : Color::NONE + else + Color::NONE + end.format(line) + end + end +end + diff --git a/lib/mutant/reporter/cli.rb b/lib/mutant/reporter/cli.rb index a51fb390..c2327711 100644 --- a/lib/mutant/reporter/cli.rb +++ b/lib/mutant/reporter/cli.rb @@ -11,7 +11,7 @@ module Mutant # @api private # def subject(subject) - @io.puts("Found subject: #{subject.identification}") + @io.puts("Subject: #{subject.identification}") end # Report mutations @@ -35,14 +35,15 @@ module Mutant # @api private # def killer(killer) - @io.puts('Killer: %s / %02.2fs' % [killer.identification,killer.runtime]) - if killer.fail? - @io.puts "Uncovered mutation" - @io.puts "=== Original ===\n#{killer.original_source}" + @io.puts(colorize(Color::RED, "!!! Uncovered Mutation !!!")) + differ = Differ.new(killer.original_source,killer.mutation_source) + diff = color? ? differ.colorized_diff : color + @io.puts(diff) @io.puts - @io.puts "=== Mutation ===\n#{killer.mutation_source}" end + + self end private @@ -58,6 +59,47 @@ module Mutant def initialize(io) @io = io 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 + # + def colorize(color, message) + color = Color::NONE unless color? + color.format(message) + 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 end end diff --git a/mutant.gemspec b/mutant.gemspec index 4513d2b7..92b3459d 100644 --- a/mutant.gemspec +++ b/mutant.gemspec @@ -21,4 +21,5 @@ Gem::Specification.new do |gem| gem.add_runtime_dependency('backports', '~> 2.6') gem.add_runtime_dependency('immutable', '~> 0.0.1') gem.add_runtime_dependency('abstract', '~> 0.0.1') + gem.add_runtime_dependency('diff-lcs', '~> 1.1.3') end diff --git a/spec/integration/mutant/differ_spec.rb b/spec/integration/mutant/differ_spec.rb new file mode 100644 index 00000000..4262a8ea --- /dev/null +++ b/spec/integration/mutant/differ_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe Mutant,'differ' do + specify 'allows to create diffs from text' do + a = "Foo\nBar\n" + b = "Foo\nBaz\n" + differ = Mutant::Differ.new(a,b) + differ.diff.should == strip_indent(<<-RUBY) + @@ -1,3 +1,3 @@ + Foo + -Bar + +Baz + RUBY + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 08986893..a761eb74 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -11,4 +11,5 @@ require 'test_app' require 'mutant' RSpec.configure do |config| + config.include(CompressHelper) end diff --git a/spec/support/compress_helper.rb b/spec/support/compress_helper.rb new file mode 100644 index 00000000..7bc827af --- /dev/null +++ b/spec/support/compress_helper.rb @@ -0,0 +1,10 @@ +module CompressHelper + def strip_indent(string) + lines = string.lines + match = /\A( *)/.match(lines.first) + whitespaces = match[1].to_s.length + stripped = lines.map do |line| + line[whitespaces..-1] + end.join + end +end