How I made Autotest RedGreen and Growl party together
Posted by Giovanni Intini | Filed under Programming, Ruby, Ruby on Rails, Testing
If you’re like me and like autotest as a sort of private continous integration system, and if you like pretty output, you will probably have used some kind of autotest + redgreen and/or growl.
Unfortunately I had strange quirks in my growl notifications. Sometimes they worked, sometimes they didn’t (I was using the standard :ran_command autotest hook at the time), so I switched to the newer :red and :green hooks, and only worsened the problem. No matter what the test output was, I always got a green notification.
I was able to trace the problem to the redgreen gem. Its colorized output wasn’t being recognized correctly by autotest and it kept thinking everything was ok.
A little ruby fiddling and this is my new improved (and working!) .autotest file:
# -*- ruby -*- module Autotest::RedGreen Autotest.send(:alias_method, :real_ruby, :ruby) Autotest.send(:define_method, :ruby) do |*args| real_ruby + %[ -rrubygems -e "require 'redgreen'" ] end # Clean the output so other modules can work correctly Autotest.add_hook :ran_command do |at| at.results.each do |r| r.gsub!("\033[31m", "") r.gsub!("\033[32m", "") r.gsub!("\033[33m", "") r.gsub!("\033[0m", "") end end end module Autotest::Growl AUTOTEST_IMAGE_ROOT = "~/.autotest_images" def self.growl(title, msg, img, pri=0, sticky="") system "growlnotify -n autotest --image #{img} -p #{pri} -m '#{msg.inspect} #{title}' #{sticky}" end Autotest.add_hook :red do |at| growl("FAIL", "#{get_results(at)}", "#{AUTOTEST_IMAGE_ROOT}/fail.png", 2) end Autotest.add_hook :green do |at| growl("Pass", "#{get_results(at)}", "#{AUTOTEST_IMAGE_ROOT}/pass.png") end private def self.get_results(at) results = [at.results].flatten.join("\n") if results.include? 'tests' output = results.slice(/(\d+)\s+tests?,\s*(\d+)\s+assertions?,\s*(\d+)\s+failures?(,\s*(\d+)\s+errors)?/) else output = results.slice(/(\d+)\s+examples?,\s*(\d+)\s+failures?(,\s*(\d+)\s+not implemented)?/) end output end end # Esclusioni Autotest.add_hook :initialize do |at| %w{.hg .git .svn stories tmtags Rakefile Capfile README spec/spec.opts spec/rcov.opts vendor/gems autotest svn-commit .DS_Store }.each do |exception| at.add_exception(exception) end at.add_mapping(/spec\/defaults.rb/) do |f, _| at.files_matching %r%^spec/(controllers|helpers|lib|models|views)/.*\.rb$% end end
Our setup had apache as webserver so I don’t need any special code to restart the application. In addition to this recipe, made to deploy to production I also wrote a couple of tasks that shine during development.
Drupal has the problem of storing most of the important stuff in the database, and if you do local development, showing the status of the work to your clients can be a chore. The following tasks allow you to easily dump the development db and import the dump to the staging server to show your progresses to your customers and allow testing the site:
# Callbacks before 'deploy:start', 'drupal:db:import:production' before 'deploy:restart', 'mikamai:permissions:fix', 'mikamai:production:symlink', 'drupal:configure:production' before 'deploy:start', 'mikamai:permissions:fix', 'mikamai:production:symlink', 'drupal:configure:production' before 'deploy:cold', 'drupal:db:dump:development' # DB Stuff set :mysqldump, "/opt/local/bin/mysqldump5" # your path to mysqldump # local db credentials set :local_db_user, "root" set :local_db_password, "" set :local_db_name, "database" # remote db credentials set :db_user, "user" set :db_password, "secret" set :db_name, "database" namespace :drupal do namespace :configure do task :production do sudo "cp #{latest_release}/sites/default/settings.production.php #{latest_release}/sites/default/settings.php" end task :development do sudo "cp #{latest_release}/sites/default/settings.development.php #{latest_release}/sites/default/settings.php" end end namespace :db do namespace :dump do task :development do raise RuntimeError.new("failed dump") unless system "#{mysqldump} -u #{local_db_user} --password=#{local_db_password} #{local_db_name} > dump.sql" end end namespace :import do task :production do ENV["FILES"] = "dump.sql" deploy::upload run "mysql -u #{db_user} --password=#{db_password} #{db_name} < #{latest_release}/dump.sql" end end end end
May 31st, 2008 at 1:25 pm
Great Giovanni! I had the same problem, I’ll apply your patch ASAP!
July 2nd, 2008 at 11:48 am
I tried to use your script and had a problem with the real_ruby hack. It seems you do it just to require the redgreen gem right?
I got rid of that part and added:
require ‘rubygems’
gem ‘redgreen’
Now it works like a charm!
July 4th, 2008 at 12:39 pm
The real_ruby hack was taken from the redgreen rubygem itself, do you know why it wasn’t working?
July 4th, 2008 at 5:12 pm
the exact error is:
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -rrubygems -e “require ‘redgreen’” -S script/spec -O spec/spec.opts spec/models/incoming/sms_spec.rb spec/models/shortcode_spec.rb
invalid option: -O
Paolo
July 7th, 2008 at 12:25 am
Looks like the problem is with autotest spec runner. I didn’t test with specs yet, I’ll let you know.