Autotest with Growl. And iChat?
By Phil Matarese on April 18th, 2008
Autotest is Great
It adds another layer of satisfaction onto test-driven development. The ideal setup for TDD is to have two monitors – one for developing and another for your autotest terminal (along with server and log-tailing terminals.) Without a second monitor, though, your autotest results need a better way of notifying you. Many people have solved this on their Macs by using Growl's growlnotify in a .autotest file (goes in your project root or home directory.) It could be as simple as requiring ZenTest's own growl interface:
1 require 'autotest/growl'But, for more control, it's sometimes better to expose the guts and operate a little bit like this:
1 module Autotest::Growl
2 PICTURE_ROOT = '/Users/philip/Pictures/Autotest/'
3
4 def self.growl msg, status
5 pri = {:fail=>2,:pending=>-1,:ok=>-2}[status]
6 title =
{:fail=>'Failed',:pending=>'Pending',:ok=>'Passed'}[
status]
7
8 msg.gsub!("\n","\\\n\n")
9 system "growlnotify -n autotest --image
\"#{PICTURE_ROOT}rails_#{status.to_s}.png\" -p
#{pri} -m \"#{msg}\" \"Tests #{title}\""
10 end
11
12 def self.failure_details results
13 details = ''
14 results.each do |line|
15 line = line[/[^\/]*\.rb\:[0-9]*\:/]
16 details += "#{line}\n" if line
17 end
18 return details
19 end
20
21 def self.pending_details results
22 results.slice!(0,3)
23 results.slice!(-4,4)
24 details = ''
25 results.each do |line|
26 details += "#{line[/[^\(]*/]}\n"
27 end
28 return details
29 end
30
31 Autotest.add_hook :ran_command do |at|
32 output =
at.results.last[/[0-9]*\sexample(s)*,\s[0-9]*\sfailu
re(s)*(,\s[0-9]*\spending)*/]
33 if output =~ /\s0\sfailure(s)*/
34 if output =~ /\spending*/
35 growl "#{pending_details(at.results)}\n#{output}",
:pending
36 else
37 growl "#{output}", :ok
38 end
39 else
40 growl "#{failure_details(at.results)}\n#{output}",
:fail
41 end
42 end
43 end
That script is based on internautdesign.com's, and uses their images (
,
). I added a few updates, like better formatting in the Growl notification and implementation of 'pending' status (
). It took me a few tries to get the line-breaks right in the notification, until I realized that I needed double backslashes – one for the shell, and one for the \n. I also decided to grab a little more than the last line of results for pending and fail statuses, which took a few ugly lines of text hacking. Also, you should set your Growl colors to something like the picture on the right.
That Was Nice
That was nice, and it solves the single-screen-notification problem pretty well, but now it's time for something new.

And, since I was looking for a reason to try out RubyOSA, I wrote a .autotest script that notifies my team (and my friends (and my mom)) whenever a test passes (or fails (or is pending)) via iChat's status and buddy icon. Too fun! Maybe this isn't the most practical way to do TDD, but it's fun code to play with. You could even use RubyOSA to lower the volume on iTunes whenever there are too many errors – maybe.
1 require 'rbosa'
2
3 module Autotest::Chat
4 PICTURE_ROOT = '/Users/philip/Pictures/Autotest/'
5
6 def self.chat test_stats, status
7 @@chat ||= OSA.app('iChat')
8 begin
9 @@chat.status_message = test_stats
10 @@chat.status = status==:fail ? 'away' : 'aval'
11 @@chat.image =
File.read("#{PICTURE_ROOT}rails_#{status.to_s}.png
")
12 rescue
13 #ignore it, iChat is prolly just turned off
14 end
15 end
16
17 Autotest.add_hook :ran_command do |at|
18 output =
at.results.last[/[0-9]*\sexample(s)*,\s[0-9]*\sfailu
re(s)*(,\s[0-9]*\spending)*/]
19 if output =~ /\s0\sfailure(s)*/
20 if output =~ /\spending*/
21 chat output, :pending
22 else
23 chat output, :ok
24 end
25 else
26 chat output, :fail
27 end
28 end
29
30 Autotest.add_hook :quit do |at|
31 chat "Chillin'", :chillin
32 end
33 end Autotest has some usefel hooks like 'quit', so I can let people now that I'm chillin' again (
) after a successful round of testing. Oh, and you can get the chat code from RubyForge with the following command.
curl 'http://rubyforge.org/snippet/download.php?type=snippet&id=329'Today
As I wrote this post, I started looking deep into the bowels of ZenTest to make sure I was setting up my autotest scripts correctly. What I found was heaps of code already designed to use Growl, and iChat, and KDE Notify, Snarl, Heckle, and some other stuff. What I didn't find was much documentation about the features – you pretty much have to dig into the source. And even with the source, some of the code's usage is mysterious – like utilizing generic autotest hooks by adding require 'autotest/growl' or require 'autotest/shame' to an empty .autotest file.
Now Back To the RUBY-CENTRIC Music

