Created AS::Testing::Isolation which runs each test case in a separate process.

This allows for testing rails bootup (files are required, correct constants are
	set, etc...). Currently, this is implemented via forking only, but we will add 
	support for jruby and windows shortly.
This commit is contained in:
Yehuda Katz + Carl Lerche 2009-06-30 12:00:50 -07:00
parent 9101941c12
commit 575b95ea0b
3 changed files with 181 additions and 0 deletions

@ -13,6 +13,7 @@
require 'active_support/testing/deprecation'
require 'active_support/testing/declarative'
require 'active_support/testing/pending'
require 'active_support/testing/isolation'
module ActiveSupport
class TestCase < ::Test::Unit::TestCase

@ -0,0 +1,39 @@
module ActiveSupport::Testing
class ProxyTestResult
def initialize
@calls = []
end
def __replay__(result)
@calls.each do |name, args|
result.send(name, *args)
end
end
def method_missing(name, *args)
@calls << [name, args]
end
end
module Isolation
def run(result)
yield(Test::Unit::TestCase::STARTED, name)
read, write = IO.pipe
pid = fork do
# child
read.close
proxy = ProxyTestResult.new
super(proxy) { }
write.puts [Marshal.dump(proxy)].pack("m")
exit!
end
write.close
Marshal.load(read.read.unpack("m")[0]).__replay__(result)
Process.wait2(pid)
yield(Test::Unit::TestCase::FINISHED, name)
end
end
end

@ -0,0 +1,141 @@
require 'abstract_unit'
# Does awesome
if ENV['CHILD']
class ChildIsolationTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
def setup
@instance = "HELLO"
end
def teardown
raise if @boom
end
test "runs the test" do
assert true
end
test "captures errors" do
raise
end
test "captures failures" do
assert false
end
test "first runs in isolation" do
assert_nil $x
$x = 1
end
test "second runs in isolation" do
assert_nil $x
$x = 2
end
test "runs with slow tests" do
sleep 0.3
assert true
sleep 0.2
end
test "runs setup" do
assert "HELLO", @instance
end
test "runs teardown" do
@boom = true
end
test "resets requires one" do
assert !defined?(Racc)
require 'racc/parser'
end
test "resets requires two" do
assert !defined?(Racc)
require 'racc/parser'
end
end
else
class ParentIsolationTest < ActiveSupport::TestCase
ENV["CHILD"] = "1"
OUTPUT = `#{Gem.ruby} -I#{File.dirname(__FILE__)} #{File.expand_path(__FILE__)} -v`
ENV.delete("CHILD")
def setup
# Extract the results
@results = {}
OUTPUT[/Started\n\s*(.*)\s*\nFinished/mi, 1].split(/\s*\n\s*/).each do |result|
result =~ %r'^(\w+)\(\w+\):\s*(\.|E|F)$'
@results[$1] = { 'E' => :error, '.' => :success, 'F' => :failure }[$2]
end
# Extract the backtraces
@backtraces = {}
OUTPUT.scan(/^\s*\d+\).*?\n\n/m).each do |backtrace|
# \n 1) Error:\ntest_captures_errors(ChildIsolationTest):
backtrace =~ %r'\s*\d+\)\s*(Error|Failure):\n(\w+)'i
@backtraces[$2] = { :type => $1, :output => backtrace }
end
end
def assert_failing(name)
assert_equal :failure, @results[name.to_s], "Test #{name} did not fail"
end
def assert_passing(name)
assert_equal :success, @results[name.to_s], "Test #{name} did not pass"
end
def assert_erroring(name)
assert_equal :error, @results[name.to_s], "Test #{name} did not error"
end
test "has all tests" do
assert_equal 10, @results.length
end
test "passing tests are still reported" do
assert_passing :test_runs_the_test
assert_passing :test_runs_with_slow_tests
end
test "resets global variables" do
assert_passing :test_first_runs_in_isolation
assert_passing :test_second_runs_in_isolation
end
test "resets requires" do
assert_passing :test_resets_requires_one
assert_passing :test_resets_requires_two
end
test "erroring tests are still reported" do
assert_erroring :test_captures_errors
end
test "runs setup and teardown methods" do
assert_passing :test_runs_setup
assert_erroring :test_runs_teardown
end
test "correct tests fail" do
assert_failing :test_captures_failures
end
test "backtrace is printed for errors" do
assert_equal 'Error', @backtraces["test_captures_errors"][:type]
assert_match %{isolation_test.rb:21:in `test_captures_errors'}, @backtraces["test_captures_errors"][:output]
end
test "backtrace is printed for failures" do
assert_equal 'Failure', @backtraces["test_captures_failures"][:type]
assert_match %{isolation_test.rb:25:in `test_captures_failures'}, @backtraces["test_captures_failures"][:output]
end
end
end