Created
October 27, 2010 06:48
-
-
Save mmrwoods/648573 to your computer and use it in GitHub Desktop.
Use autotest to run rails tests/specs impacted by changes shown by git-diff
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
require 'rubygems' | |
require 'autotest' | |
require 'optparse' | |
# exit if we're not in a git repository | |
exit $?.exitstatus unless system('git diff --name-only > /dev/null') | |
# make sure we're in the root path for the repository | |
loop do | |
begin | |
Dir.entries('.git') | |
break | |
rescue SystemCallError | |
Dir.chdir('..') | |
next | |
end | |
end | |
options = {:fast => false, :diff => 'HEAD', :trace => false} | |
OptionParser.new do |opts| | |
opts.banner = "Usage: ./script/gittest [options]" | |
opts.on("-f", "--fast", "Fast mode - skips preparation of test db") do |o| | |
options[:fast] = o | |
end | |
opts.on("-d MANDATORY", "--diff MANDATORY", "Commit argument for git diff command used to check for new or modified files (defaults to HEAD)") do |o| | |
options[:diff] = o | |
end | |
opts.on("-t", "--trace", "Enable trace option when calling rake tasks to prepare test db") do |o| | |
options[:trace] = o | |
end | |
end.parse! | |
# prepare db if fast start not switched on | |
unless options[:fast] | |
puts "Preparing test database..." | |
puts "(You can use the -f switch to skip this in future)" | |
rake_options = options[:trace] ? '--trace' : '' | |
system "rake db:migrate RAILS_ENV=test #{$rake_options} > /dev/null" | |
system "rake db:test:prepare #{$rake_options} > /dev/null" | |
end | |
# autotest options | |
$f = true # never run the entire test/spec suite on startup | |
$v = false | |
$h = false | |
$q = false | |
$DEBUG = false | |
$help = false | |
# use ansi colors to highlight test/spec passes, failures and errors | |
COLORS = { :red => 31, :green => 32, :yellow => 33 } | |
# get a list of new or modified files according to git (using terminal commands is faster than using a ruby git library) | |
new_or_modified_files = `git diff --name-only #{options[:diff]}`.split("\n").uniq | |
if new_or_modified_files.size == 0 | |
puts "No modified files, exiting" | |
exit | |
end | |
msg = "#{new_or_modified_files.size} new or modified file" | |
msg << "s" unless new_or_modified_files.size == 1 | |
puts msg + ":" | |
new_or_modified_files.each {|f| puts "\t#{f}"} | |
at = Autotest.new | |
# Note: the initialize hook is normally called within Autotest#run | |
at.hook :initialize | |
at.reset | |
at.find_files # must populate the known files for autotest, otherwise Autotest#files_matching will always return nil | |
# this isn't pretty, but it will probably be reliable enough (can't see any good reason for renaming that particular instance variable, IMO it should be accessible anyway) | |
test_mappings = at.instance_eval { @test_mappings } | |
# find files to test | |
files_to_test = at.new_hash_of_arrays | |
new_or_modified_files.each do |f| | |
next if f =~ at.exceptions # skip exceptions | |
result = test_mappings.find { |file_re, ignored| f =~ file_re } | |
unless result.nil? | |
[result.last.call(f, $~)].flatten.each {|match| files_to_test[match] if File.exist?(match)} | |
end | |
end | |
# exit if no files to test | |
puts "No matching files to test, exiting" and exit if files_to_test.empty? | |
msg = "#{files_to_test.size} file" | |
msg << "s" unless files_to_test.size == 1 | |
msg << " to test" | |
puts msg + ":" | |
puts "\t" + files_to_test.map{|k,v| k}.sort.join("\n\t") | |
puts "Press ENTER to continue, or CTRL+C to quit" | |
begin | |
$stdin.gets # note: Kernel#gets assumes that ARGV contains a list of files from which to read next line | |
rescue Interrupt | |
exit 1 | |
end | |
puts "Running tests and specs, please wait..." | |
cmd = at.make_test_cmd(files_to_test) | |
at.hook :run_command | |
# copied from Autotest#run_tests and updated to use ansi colours in TURN enabled test output and specs run with the format option set to specdoc | |
old_sync = $stdout.sync | |
$stdout.sync = true | |
results = [] | |
line = [] | |
begin | |
open("| #{cmd}", "r") do |f| | |
until f.eof? do | |
c = f.getc or break | |
# putc c | |
line << c | |
if c == ?\n then | |
str = if RUBY_VERSION >= "1.9" then | |
line.join | |
else | |
line.pack "c*" | |
end | |
results << str | |
line.clear | |
if str.match(/(PASS|FAIL|ERROR)$/) | |
# test output | |
case $1 | |
when 'PASS' ; color = :green | |
when 'FAIL' ; color = :red | |
when 'ERROR' ; color = :yellow | |
end | |
print "\e[#{COLORS[color]}m" + str + "\e[0m" | |
elsif str.match(/^\- /) | |
# spec output | |
if str.match(/^\- .*(ERROR|FAILED) \- [0-9]+/) | |
color = $1 == 'FAILED' ? :red : :yellow | |
print "\e[#{COLORS[color]}m" + str + "\e[0m" | |
else | |
print "\e[#{COLORS[:green]}m" + str + "\e[0m" | |
end | |
else | |
print str | |
end | |
end | |
end | |
end | |
ensure | |
$stdout.sync = old_sync | |
end | |
at.handle_results(results.join) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment