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.