noizZze

Autotest and Growl for Both RSpec and Test::Unit

GrowlPreviously I wrote about BDD and zen testing in my post ”Ruby / Rails: Zen Testing with Autotest and XOSD”. I mentioned a couple of links where you could find an .autotest script for your Linux / Mac environment to throw the results of your tests to Growl or XOSD for a nice on-screen text display. This kept you on top of background testing at all times.

Over the course of the last few months I was taking part in different Rails projects. Some of them used RSpec and some Shoulda/Mocha for unit testing. The original .autotest script that I picked and adopted was intended for RSpec, so I had to modify it over and over to support the environment I was currently working in. Finally, as I realized there was not a slightest hope to end this jumping back and forth, I came up with a multi-environment script that works with both RSpec and Test::Unit.

It detects it all automatically, and the only thing you need to do is to give the paths to your pass / fail / pending icons.

Here’s the script for your copy-pasting convenience. Name it “.autotest” and place in your home directory. Enjoy!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
module Autotest::Growl

  STATUS_ICONS = {
    :pass     => "~/Library/autotest/pass.png",
    :fail     => "~/Library/autotest/fail.png",
    :pending  => "~/Library/autotest/pending.png"
  }

  def self.growl(title, msg, img, pri=0, stick="")
    system "growlnotify -n autotest --image #{img} -p #{pri} -m #{msg.inspect} #{title} #{stick}"
    sleep 1
  end

  Autotest.add_hook(:ran_command) do |autotest|
    results = [ autotest.results ].flatten.join("\n")

    rspec_regexp     = /(\d+)\s+examples?,\s*(\d+)\s+failures?(,\s*(\d+)\s+pending)?/
    test_unit_regexp = /(\d+)\s+tests?,\s*(\d+)\s+assertions?,\s*(\d+)\s+failures?,\s*(\d+)\s+errors?/

    if !(output = results.scan(rspec_regexp).flatten).empty?
      # RSPEC
      examples, failures, x, pending = output
      if failures.to_i > 0
        prefix, icon, priority, stick = "FAIL:", :fail, 2, "-s"
      elsif pending > 0
        prefix, icon, priority, stick = "PENDING:", :pending, 2, "-s"
      else
        prefix, icon, priority, stick = "PASS:", :pass, 0, ""
      end

      info  = "Examples: #{examples}"
      info += " Failures: #{failures}" if failures.to_i > 0
      info += " Pending: #{pending}" if pending.to_i > 0

    elsif !(output = results.scan(test_unit_regexp).flatten).empty?
      # Test::Unit
      tests, assertions, failures, errors = output
      failed = failures.to_i > 0 || errors.to_i > 0
      if failed
        prefix, icon, priority, stick = "FAIL: ", :fail, 2, "-s"
      else
        prefix, icon, priority, stick = "PASS: ", :pass, 0, ""
      end

      info  = "Tests: #{tests}/#{assertions}"
      info += " Errors: #{errors}" if errors.to_i > 0
      info += " Failures: #{failures}" if failures.to_i > 0

    else
      # Unknown format
      return
    end

    growl prefix, info, STATUS_ICONS[icon], priority, stick
  end
end