Skip to content

Instantly share code, notes, and snippets.

@gnufied
Created May 3, 2010 18:43
Show Gist options
  • Save gnufied/388435 to your computer and use it in GitHub Desktop.
Save gnufied/388435 to your computer and use it in GitHub Desktop.
module Tickle
extend self
def load_rails_environment(environment)
ENV["RAILS_ENV"] = environment
Object.const_set :RAILS_ENV, environment
require(File.join(RAILS_ROOT, 'config', 'environment'))
$: << "#{Rails.root}/test"
require "#{Rails.root}/test/test_helper"
end
def run_tests(dir, n = 2)
dir = "#{Rails.root}/test/#{dir}/**/*_test.rb" unless dir.index(Rails.root.to_s)
groups = Dir[dir].sort.in_groups(n, false)
pids = fork_tests(groups)
Signal.trap 'SIGINT', lambda { pids.each {|p| Process.kill("KILL", p)}; exit 1 }
errors = Process.waitall.map { |pid,status| status.exitstatus }
raise "Error running test" if(errors.any? { |x| x != 0 })
end
def run_cucumber(n = 2)
dir = Dir["#{Rails.root}/features/*.feature"]
groups = dir.sort.in_groups(n,false)
p groups
pids = fork_cucumber(groups)
Signal.trap 'SIGINT', lambda { pids.each {|p| Process.kill("KILL", p)}; exit 1 }
errors = Process.waitall.map { |pid,status| status.exitstatus }
raise "Error running cucumber" if(errors.any? { |x| x != 0 })
end
def fork_cucumber(groups)
pids = []
GC.start
groups.each_with_index do |group,db_counter|
if(!group.empty?)
pids << Process.fork do
files = group.find_all {|f| f !~ /^-/ }
prepare_analyzer()
ENV["RAILS_ENV"] = 'cucumber'
Object.const_set :RAILS_ENV, 'cucumber'
require(File.join(RAILS_ROOT, 'config', 'environment'))
prepare_databse(db_counter) unless try_migration_first(db_counter)
require "cucumber/cli/main"
cucumber_options = get_cucumber_options(files)
failure = Cucumber::Cli::Main.execute(cucumber_options)
raise "Cucumber failed" if failure
end
end
end
pids
end
def fork_tests(groups)
pids = []
GC.start
groups.each_with_index do |group, db_counter|
pids << Process.fork { child_run(group,db_counter) }
end
pids
end
def get_cucumber_options(files)
args = (
['-m'] + ['-q'] +
files
).flatten.compact
args
end
def child_run(group,db_counter)
prepare_analyzer()
load_rails_environment('test')
prepare_databse(db_counter) unless try_migration_first(db_counter)
files = group.find_all {|f| f !~ /^-/ }
files.each { |f| load(f) }
end
def prepare_analyzer()
saved_aggregate_data = rcov_load_aggregate_data($options.aggregate_file)
if(saved_aggregate_data[:coverage])
$rcov_code_coverage_analyzer = saved_aggregate_data[:coverage]
end
$rcov_code_coverage_analyzer.install_hook
end
def try_migration_first(db_counter)
begin
db_config = get_connection_config(db_counter)
ActiveRecord::Base.establish_connection(db_config)
ActiveRecord::Base.connection()
migrate_db()
return true
rescue Exception => e
puts "Error runnig miration #{e.inspect}"
puts e.backtrace
return false
end
end
def prepare_databse(db_counter)
create_db_using_raw_sql(db_counter)
try_migration_first(db_counter)
end
def create_db_using_raw_sql(db_counter)
test_config = get_connection_config(db_counter)
sql_connection = Mysql.connect(test_config["host"] || "localhost",
test_config["username"],
test_config["password"],
nil,
test_config["port"] || 3306)
sql_connection.real_query("DROP database IF EXISTS #{test_config["database"]}")
sql_connection.real_query("CREATE DATABASE #{test_config["database"]}")
sql_connection.close()
end
def migrate_db
ENV["VERBOSE"] = "false"
Rake::Task["db:migrate"].invoke
end
def get_connection_config(db_counter)
default_settings = config["test"].clone()
default_settings["database"] = "#{default_settings["database"]}_#{db_counter}"
default_settings
end
def config
ActiveRecord::Base.configurations
end
def rcov_load_aggregate_data(file)
begin
old_data = nil
Zlib::GzipReader.open(file){|gz| old_data = Marshal.load(gz) }
rescue
old_data = {}
end
old_data || {}
end
def rcov_save_aggregate_data(file)
Zlib::GzipWriter.open(file) do |f|
Marshal.dump({:callsites => nil, :coverage => $rcov_code_coverage_analyzer}, f)
end
end
def make_formatter(klass,options)
klass.new(:destdir => options.destdir, :color => options.color,
:fsr => options.range, :textmode => options.textmode,
:ignore => options.skip, :dont_ignore => options.include,
:sort => options.sort,
:sort_reverse => options.sort_reverse,
:output_threshold => options.output_threshold,
:callsite_analyzer => nil,
:coverage_diff_mode => options.coverage_diff_mode,
:coverage_diff_file => options.coverage_diff_file,
:callsites => options.callsites,
:cross_references => options.crossrefs,
:diff_cmd => options.diff_cmd,
:comments_run_by_default => options.comments_run_by_default,
:gcc_output => options.gcc_output,
:charset => options.charset,
:css => options.css,
:failure_threshold => options.failure_threshold
)
end
def fetch_rcov_options
{
:color => true, :range => 30.0, :profiling => false,
:destdir => "coverage", :loadpaths => ["test"], :textmode => false,
:skip => Rcov::BaseFormatter::DEFAULT_OPTS[:ignore], :include => [],
:html => true, :css => nil, :comments_run_by_default => false,
:test_unit_only => false, :spec_only => false, :sort => :name,
:sort_reverse => false, :output_threshold => 101, :failure_threshold => nil,
:replace_prog_name => false, :callsites => false, :crossrefs => false,
:coverage_diff_file => "coverage.info", :coverage_diff_mode => :compare,
:coverage_diff_save => false, :diff_cmd => "diff",
:report_rcov_bug_for => nil, :aggregate_file => "coverage.data",
:gcc_output => false, :charset => nil
}
end
end
require File.join(File.dirname(__FILE__), "../lib/tickle")
require "rcov"
require "ostruct"
require "zlib"
require "cucumber"
require "cucumber/platform"
# Yanked from Rails
desc 'Run all unit, functional and integration tests'
task :tickle, :count do |t, args|
$options = OpenStruct.new(Tickle.fetch_rcov_options)
p $options
$options.skip.concat [%r{\bvendor/},%r{\bconfig/},%r{\benvironment/},%r{\bfeatures/}]
saved_aggregate_data = Tickle.rcov_load_aggregate_data($options.aggregate_file)
if(saved_aggregate_data[:coverage])
$rcov_code_coverage_analyzer = saved_aggregate_data[:coverage]
else
$rcov_code_coverage_analyzer = Rcov::CodeCoverageAnalyzer.new
end
$rcov_callsite_analyzer = nil
formatters = []
formatters << Tickle.make_formatter(Rcov::HTMLCoverage,$options)
END {
puts "########## Running End block ##########"
$rcov_code_coverage_analyzer.remove_hook
$rcov_code_coverage_analyzer.dump_coverage_info(formatters)
Tickle.rcov_save_aggregate_data($options.aggregate_file)
}
errors = %w(tickle:units tickle:functionals tickle:cucumber).collect do |task|
begin
Rake::Task[task].invoke(args[:count])
nil
rescue => e
puts e
puts e.backtrace
task
end
end.compact
abort "Errors running #{errors.to_sentence}!" if errors.any?
end
namespace :tickle do
[:units, :functionals].each do |t|
type = t.to_s.sub(/s$/, '')
desc "Run #{type} tests"
task t, :count do |t, args|
size = args[:count] ? args[:count].to_i : 2
puts "Running #{type} tests using #{size} processes"
Tickle.run_tests type, size
end
end
desc "Run cucumeber:tickle test"
task :cucumber do |args|
size = 2
puts "Running cucumber tests using #{size} processes"
Tickle.run_cucumber(2)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment