In Files

Parent

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
    -v display version

Constants

WINDOZE
DIFF

Public Class Methods

unit_diff() click to toggle source

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

Public Instance Methods

diff(expect, butwas) click to toggle source
     # 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
massage(data) click to toggle source
     # 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
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 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
parse_input(input, output) click to toggle source
     # 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
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 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.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.