Autotest notifications on Ubuntu using lib-notify

words by Brian Racer

At Jetpack we are big fans of Test Driven Development(TDD), and specifically Behavior Driven Development(BDD) using excellent testing frameworks like RSpec and Cucumber. When you are using ZenTest’s autotest or autospec, it’s nice to have unobtrusive notifications of passing/failing tests while you work. Mac users can integrate autotest with the excellent Growl, but what about us Linux users? Well be can use a library called lib-notify.

First lets play with the lib-notify binary. Install it:

sudo apt-get install libnotify-bin

To make sure it is working, try this:

notify-send "Hello World!"

You should see the notification appear! Now lets edit our ~/.autotest file to use that notification:

vi ~/.autotest
module Autotest::GnomeNotify
  # Time notification will be displayed before disappearing automatically
  ERROR_STOCK_ICON = "gtk-dialog-error"
  SUCCESS_STOCK_ICON = "gtk-dialog-info"
  # Convenience method to send an error notification message
  # [stock_icon]   Stock icon name of icon to display
  # [title]        Notification message title
  # [message]      Core message for the notification
  def self.notify stock_icon, title, message
    options = "-t #{EXPIRATION_IN_SECONDS * 1000} -i #{stock_icon}"
    system "notify-send #{options} '#{title}' '#{message}'"
  Autotest.add_hook :red do |at|
    notify ERROR_STOCK_ICON, "Tests failed", "#{at.files_to_test.size} tests failed"
  Autotest.add_hook :green do |at|
    notify SUCCESS_STOCK_ICON, "All tests passed, good job!", ""

Although not quite as pretty as Growl yet, it works! If you are happy with that you can stop there. If you want something that looks a little bit nicer, read on!

The next few steps are going to require a few development libraries and gems, so install the following packages to make sure we are on the same page:

apt-get install  libinotify-ruby libgtk2-ruby  libnotify-dev
sudo gem install zentest autotest autotest-rails red-green launchy

Unfortunately the libnotify ruby library isn’t included in Ubuntu 9.04 Jaunty, so we will have to build that ourselves:

tar -xvjpf ruby-libnotify-0.3.3.tar.bz2
cd  ruby-libnotify-0.3.3
ruby extconf.rb
sudo make install

Next put the following pretty images in your ~./autotest_images directory:

Now we can use a modified version of Linden LAN’s .autotest:

require 'rnotify'
require 'launchy'
require 'gtk2'
module Autotest::RNotify
  class Notification
    attr_accessor :verbose, :image_root, :tray_icon, :notification,
                  :image_pass, :image_pending, :image_fail,
                  :image_file_pass, :image_file_pending, :image_file_fail,
                  :status_image_pass, :status_image_pending, :status_image_fail
    def initialize(timeout = 5000,
                   image_root = "#{ENV['HOME']}/.autotest_images" ,
                   report_url = "doc/spec/report.html",
                   verbose = false)
      self.verbose = verbose
      self.image_root = image_root
      self.image_file_pass = "#{image_root}/pass.png"
      self.image_file_pending = "#{image_root}/pending.png"
      self.image_file_fail = "#{image_root}/fail.png"
      raise("#{image_file_pass} not found") unless File.exists?(image_file_pass)
      raise("#{image_file_pending} not found") unless File.exists?(image_file_pending)
      raise("#{image_file_fail} not found") unless File.exists?(image_file_fail)
      puts 'Autotest Hook: loading Notify' if verbose
      Notify.init('Autotest') || raise('Failed to initialize Notify')
      puts 'Autotest Hook: initializing tray icon' if verbose
      self.tray_icon =
      tray_icon.pixbuf =,22,22)
      tray_icon.tooltip = 'RSpec Autotest'
      puts 'Autotest Hook: Creating Notifier' if verbose
      self.notification ='X', nil, nil, tray_icon)
      notification.timeout = timeout
      puts 'Autotest Hook: Connecting mouse click event' if verbose
      tray_icon.signal_connect("activate") do
      end { Gtk.main }
      sleep 1
      tray_icon.embedded? || raise('Failed to set up tray icon')
    def notify(icon, tray, title, message)
      notification.update(title, message, nil)
      notification.pixbuf_icon = icon
      tray_icon.tooltip = "Last Result: #{message}"
      tray_icon.pixbuf = tray
    def passed(title, message)
      self.image_pass ||=, 48, 48)
      self.status_image_pass ||=, 22, 22)
      notify(image_pass, status_image_pass, title, message)
    def pending(title, message)
      self.image_pending ||=, 48, 48)
      self.status_image_pending ||=, 22, 22)
      notify(image_pending, status_image_pending, title, message)
    def failed(title, message)
      self.image_fail ||=, 48, 48)
      self.status_image_fail ||=, 22, 22)
      notify(image_fail, status_image_fail, title, message)
    def quit
      puts 'Autotest Hook: Shutting Down...' if verbose
  Autotest.add_hook :initialize do |at|
    @notify =
  Autotest.add_hook :ran_command do |at|
    results = at.results.last
    unless results.nil?
      output = results[/(\d+)\s+examples?,\s*(\d+)\s+failures?(,\s*(\d+)\s+pending)?/]
      if output
        failures = $~[2].to_i
        pending = $~[4].to_i
      if failures > 0
        @notify.failed("Tests Failed", output)
      elsif pending > 0
        @notify.pending("Tests Pending", output)
        unless at.tainted
          @notify.passed("All Tests Passed", output)
          @notify.passed("Tests Passed", output)
  Autotest.add_hook :quit do |at|

This should leave you with a pretty good base if you want to further customize the script!