Class: UnitDiff

### Home Classes Methods

### In Files

- lib/unit_diff.rb

### Parent

Object

### Methods

- ::unit_diff
- #diff
- #massage
- #parse_diff
- #parse_input
- #unit_diff

### Files

- History.txt
- Manifest.txt
- README.txt
- how_to_use_zentest.txt
- example.txt

### Class/Module Index

Quicksearch

- Autotest
- Autotest::AutoUpdate
- Autotest::Bundler
- Autotest::Isolate
- Autotest::Once
- Autotest::Preload
- Autotest::RCov
- Autotest::Restart
- Autotest::Timestamp
- FunctionalTestMatrix
- Module
- Multiruby
- UnitDiff
- ZenTest
- ZenTestMapping

No matching classes.

# 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 232
    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 267
    def massage(data)
      # unescape newlines, strip <> from entire string
      data = data.join
      data = data.gsub(/\\n/, "\n").gsub(/0x[a-f0-9]+/, '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 (.*)$/ then
            expect << $1
            butwas << $2
            state = :footer
            result.shift
          when /^Expected (.*?)$/ then
            expect << "#{$1}\n"
            result.shift
          when /^to equal / then
            state = :spec_butwas
            bw = result.shift.sub(/^to equal (.*):?$/, '\1')
            butwas << bw
          else
            state = :butwas if result.first.sub!(/ expected( but was|, not)/, '')
            expect << result.shift
          end
        when :butwas then
          butwas = result[0..-1]
          result.clear
        when :spec_butwas then
          if result.first =~ /^\s+\S+ at |^:\s*$/
            state = :footer
          else
            butwas << result.shift
          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 192
    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

[Validate]

Generated with the Darkfish Rdoc Generator 2.