class UnitDiff
UnitDiff
makes reading Test::Unit output easy and fun. Instead of a confusing jumble of text with nearly unnoticable changes like this:
1) Failure: test_to_gpoints(RouteTest) [test/unit/route_test.rb:29]: <"new GPolyline([\n new GPoint( 47.00000, -122.00000),\n new GPoint( 46.5000 0, -122.50000),\n new GPoint( 46.75000, -122.75000),\n new GPoint( 46.00000, -123.00000)])"> expected but was <"new Gpolyline([\n new GPoint( 47.00000, -122.00000),\n new GPoint( 46.5000 0, -122.50000),\n new GPoint( 46.75000, -122.75000),\n new GPoint( 46.00000, -123.00000)])">.
You get an easy-to-read diff output like this:
1) Failure: test_to_gpoints(RouteTest) [test/unit/route_test.rb:29]: 1c1 < new GPolyline([ --- > new Gpolyline([
Usage¶ ↑
test.rb | unit_diff [options] options: -b ignore whitespace differences -c contextual diff -h show usage -k keep temp diff files around -l prefix line numbers on the diffs -u unified diff [default] -p plain diff -v display version
Constants
- DIFF
- WINDOZE
Public Class Methods
unit_diff()
click to toggle source
Handy wrapper for UnitDiff#unit_diff
.
# File lib/unit_diff.rb, line 57 def self.unit_diff trap 'INT' do exit 1 end puts UnitDiff.new.unit_diff end
Public Instance Methods
diff(expect, butwas)
click to toggle source
# File lib/unit_diff.rb, line 233 def diff expect, butwas output = nil Tempfile.open("expect") do |a| a.write(massage(expect)) a.rewind Tempfile.open("butwas") do |b| b.write(massage(butwas)) b.rewind diff_flags = $p ? "" : $c ? "-c" : "-u" diff_flags += " -b" if $b result = `#{DIFF} #{diff_flags} #{a.path} #{b.path}` result.sub!(/^\-\-\- .+/, "--- expected") result.sub!(/^\+\+\+ .+/, "+++ actual") output = if result.empty? then "[no difference--suspect ==]" else result.split(/\n/) end if $k then warn "moving #{a.path} to #{a.path}.keep" File.rename a.path, a.path + ".keep" warn "moving #{b.path} to #{b.path}.keep" File.rename b.path, b.path + ".keep" end end end output end
massage(data)
click to toggle source
# File lib/unit_diff.rb, line 268 def massage(data) # unescape newlines, strip <> from entire string data = data.join data = data.gsub(/\\n/, "\n").gsub(/0x[a-f0-9]+/m, '0xXXXXXX') + "\n" data += "\n" unless data[-1] == ?\n data end
parse_diff(result)
click to toggle source
Parses a single diff recording the header and what was expected, and what was actually obtained.
# File lib/unit_diff.rb, line 121 def parse_diff(result) header = [] expect = [] butwas = [] footer = [] state = :header until result.empty? do case state when :header then header << result.shift state = :expect if result.first =~ /^<|^Expected/ when :expect then case result.first when /^Expected (.*?) to equal (.*?):$/ then expect << $1 butwas << $2 state = :footer result.shift when /^Expected (.*?), not (.*)$/m then expect << $1 butwas << $2 state = :footer result.shift when /^Expected (.*?)$/ then expect << "#{$1}\n".dup result.shift when /^to equal / then state = :spec_butwas bw = result.shift.sub(/^to equal (.*):?$/, '\1') butwas << bw else ex = result.shift.dup state = :butwas if ex.sub!(/ expected( but was|, not)/, '') expect << ex end when :butwas then butwas = result[0..-1].map(&:dup) result.clear when :spec_butwas then if result.first =~ /^\s+\S+ at |^:\s*$/ state = :footer else butwas << result.shift.dup end when :footer then butwas.last.sub!(/:$/, '') footer = result.map {|l| l.chomp } result.clear else raise "unknown state #{state}" end end return header, expect, nil, footer if butwas.empty? expect.last.chomp! expect.first.sub!(/^<\"/, '') expect.last.sub!(/\">$/, '') butwas.last.chomp! butwas.last.chop! if butwas.last =~ /\.$/ butwas.first.sub!( /^<\"/, '') butwas.last.sub!(/\">$/, '') return header, expect, butwas, footer end
parse_input(input, output)
click to toggle source
# File lib/unit_diff.rb, line 62 def parse_input(input, output) current = [] data = [] data << current print_lines = true term = "\nFinished".split(//).map { |c| c[0] } term_length = term.size old_sync = output.sync output.sync = true while line = input.gets case line when /^(Loaded suite|Started|# Running tests:)/ then print_lines = true output.puts line chars = [] while c = input.getc do output.putc c chars << c tail = chars[-term_length..-1] break if chars.size >= term_length and tail == term end output.puts input.gets # the rest of "Finished in..." output.puts next when /^\s*$/, /^\(?\s*\d+\) (Failure|Error):/, /^\d+\)/ then print_lines = false current = [] data << current when /^Finished in \d/ then print_lines = false end output.puts line if print_lines current << line end output.sync = old_sync data = data.reject { |o| o == ["\n"] or o.empty? } footer = data.pop data.map do |result| break if result.any? { |l| l =~ / expected( but was|, not)/ } header = result.find do |l| l =~ /^\(?\s*\d+\) (Failure|Error):/ end break unless header message_index = result.index(header) + 2 result[message_index..-1] = result[message_index..-1].join end return data, footer end
unit_diff(input=ARGF, output=$stdout)
click to toggle source
Scans Test::Unit output input
looking for comparison failures and makes them easily readable by passing them through diff.
# File lib/unit_diff.rb, line 193 def unit_diff(input=ARGF, output=$stdout) $b = false unless defined? $b $c = false unless defined? $c $k = false unless defined? $k $u = true unless defined? $u $p = false unless defined? $p data, footer = self.parse_input(input, output) output = [] # Output data.each do |result| if result.first =~ /Error/ then output.push result.join('') next end prefix, expect, butwas, result_footer = parse_diff(result) output.push prefix.compact.map {|line| line.strip}.join("\n") if butwas then output.push self.diff(expect, butwas) output.push result_footer output.push '' else output.push expect.join('') end end if footer then footer.shift if footer.first.strip.empty? output.push footer.compact.map {|line| line.strip}.join("\n") end return output.flatten.join("\n") end