Object
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([
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 -v display version
Handy wrapper for UnitDiff#unit_diff.
# File lib/unit_diff.rb, line 54 54: def self.unit_diff 55: trap 'INT' do exit 1 end 56: puts UnitDiff.new.unit_diff 57: end
# File lib/unit_diff.rb, line 234 234: def diff expect, butwas 235: output = nil 236: 237: Tempfile.open("expect") do |a| 238: a.write(massage(expect)) 239: a.rewind 240: Tempfile.open("butwas") do |b| 241: b.write(massage(butwas)) 242: b.rewind 243: 244: diff_flags = $u ? "-u" : $c ? "-c" : "" 245: diff_flags += " -b" if $b 246: 247: result = `#{DIFF} #{diff_flags} #{a.path} #{b.path}` 248: output = if result.empty? then 249: "[no difference--suspect ==]" 250: else 251: result.split(/\n/) 252: end 253: 254: if $k then 255: warn "moving #{a.path} to #{a.path}.keep" 256: File.rename a.path, a.path + ".keep" 257: warn "moving #{b.path} to #{b.path}.keep" 258: File.rename b.path, b.path + ".keep" 259: end 260: end 261: end 262: 263: output 264: end
# File lib/unit_diff.rb, line 266 266: def massage(data) 267: count = 0 268: # unescape newlines, strip <> from entire string 269: data = data.join 270: data = data.gsub(/\\n/, "\n").gsub(/0x[a-f0-9]+/, '0xXXXXXX') + "\n" 271: data += "\n" unless data[1] == \n\ 272: data 273: end
Parses a single diff recording the header and what was expected, and what was actually obtained.
# File lib/unit_diff.rb, line 120 120: def parse_diff(result) 121: header = [] 122: expect = [] 123: butwas = [] 124: footer = [] 125: found = false 126: state = :header 127: 128: until result.empty? do 129: case state 130: when :header then 131: header << result.shift 132: state = :expect if result.first =~ /^<|^Expected/ 133: when :expect then 134: case result.first 135: when /^Expected (.*?) to equal (.*?):$/ then 136: expect << $1 137: butwas << $2 138: state = :footer 139: result.shift 140: when /^Expected (.*?), not (.*)$/ then 141: expect << $1 142: butwas << $2 143: state = :footer 144: result.shift 145: when /^Expected (.*?)$/ then 146: expect << "#{$1}\n" 147: result.shift 148: when /^to equal / then 149: state = :spec_butwas 150: bw = result.shift.sub(/^to equal (.*):?$/, '\1') 151: butwas << bw 152: else 153: state = :butwas if result.first.sub!(/ expected( but was|, not)/, '') 154: expect << result.shift 155: end 156: when :butwas then 157: butwas = result[0..1] 158: result.clear 159: when :spec_butwas then 160: if result.first =~ /^\s+\S+ at |^:\s*$/ 161: state = :footer 162: else 163: butwas << result.shift 164: end 165: when :footer then 166: butwas.last.sub!(/:$/, '') 167: footer = result.map {|l| l.chomp } 168: result.clear 169: else 170: raise "unknown state #{state}" 171: end 172: end 173: 174: return header, expect, nil, footer if butwas.empty? 175: 176: expect.last.chomp! 177: expect.first.sub!(/^<\"/, '') 178: expect.last.sub!(/\">$/, '') 179: 180: butwas.last.chomp! 181: butwas.last.chop! if butwas.last =~ /\.$/ 182: butwas.first.sub!( /^<\"/, '') 183: butwas.last.sub!(/\">$/, '') 184: 185: return header, expect, butwas, footer 186: end
# File lib/unit_diff.rb, line 59 59: def parse_input(input, output) 60: current = [] 61: data = [] 62: data << current 63: print_lines = true 64: 65: term = "\nFinished".split(//).map { |c| c[0] } 66: term_length = term.size 67: 68: old_sync = output.sync 69: output.sync = true 70: while line = input.gets 71: case line 72: when /^(Loaded suite|Started)/ then 73: print_lines = true 74: output.puts line 75: chars = [] 76: while c = input.getc do 77: output.putc c 78: chars << c 79: tail = chars[-term_length..1] 80: break if chars.size >= term_length and tail == term 81: end 82: output.puts input.gets # the rest of "Finished in..." 83: output.puts 84: next 85: when /^\s*$/, /^\(?\s*\d+\) (Failure|Error):/, /^\d+\)/ then 86: print_lines = false 87: current = [] 88: data << current 89: when /^Finished in \d/ then 90: print_lines = false 91: end 92: output.puts line if print_lines 93: current << line 94: end 95: output.sync = old_sync 96: data = data.reject { |o| o == ["\n"] or o.empty? } 97: footer = data.pop 98: 99: data.map do |result| 100: break if result.find do |line| 101: line =~ / expected( but was|, not)/ 102: end 103: 104: header = result.find do |line| 105: line =~ /^\(?\s*\d+\) (Failure|Error):/ 106: end 107: 108: break unless header 109: 110: message_index = result.index(header) + 2 111: 112: result[message_index..1] = result[message_index..1].join 113: end 114: 115: return data, footer 116: end
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 192 192: def unit_diff(input=ARGF, output=$stdout) 193: $b = false unless defined? $b 194: $c = false unless defined? $c 195: $k = false unless defined? $k 196: $u = false unless defined? $u 197: 198: data, footer = self.parse_input(input, output) 199: 200: output = [] 201: 202: # Output 203: data.each do |result| 204: first = [] 205: second = [] 206: 207: if result.first =~ /Error/ then 208: output.push result.join('') 209: next 210: end 211: 212: prefix, expect, butwas, result_footer = parse_diff(result) 213: 214: output.push prefix.compact.map {|line| line.strip}.join("\n") 215: 216: if butwas then 217: output.push self.diff(expect, butwas) 218: 219: output.push result_footer 220: output.push '' 221: else 222: output.push expect.join('') 223: end 224: end 225: 226: if footer then 227: footer.shift if footer.first.strip.empty?# unless footer.first.nil? 228: output.push footer.compact.map {|line| line.strip}.join("\n") 229: end 230: 231: return output.flatten.join("\n") 232: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.