Skip to content
This repository
tag: v3.0.0_RC2
Fetching contributors…

Cannot retrieve contributors at this time

file 157 lines (128 sloc) 4.095 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
require 'rbconfig'
module ActiveSupport
  module Testing
    class RemoteError < StandardError

      attr_reader :message, :backtrace

      def initialize(exception)
        @message = "caught #{exception.class.name}: #{exception.message}"
        @backtrace = exception.backtrace
      end
    end

    class ProxyTestResult
      def initialize
        @calls = []
      end

      def add_error(e)
        e = Test::Unit::Error.new(e.test_name, RemoteError.new(e.exception))
        @calls << [:add_error, e]
      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 self.forking_env?
        !ENV["NO_FORK"] && ((RbConfig::CONFIG['host_os'] !~ /mswin|mingw/) && (RUBY_PLATFORM !~ /java/))
      end

      def self.included(base)
        if defined?(::MiniTest) && base < ::MiniTest::Unit::TestCase
          base.send :include, MiniTest
        elsif defined?(Test::Unit)
          base.send :include, TestUnit
        end
      end

      def _run_class_setup # class setup method should only happen in parent
        unless defined?(@@ran_class_setup) || ENV['ISOLATION_TEST']
          self.class.setup if self.class.respond_to?(:setup)
          @@ran_class_setup = true
        end
      end

      module TestUnit
        def run(result)
          _run_class_setup

          yield(Test::Unit::TestCase::STARTED, name)

          @_result = result

          serialized = run_in_isolation do |proxy|
            begin
              super(proxy) { }
            rescue Exception => e
              proxy.add_error(Test::Unit::Error.new(name, e))
            end
          end

          retval, proxy = Marshal.load(serialized)
          proxy.__replay__(@_result)

          yield(Test::Unit::TestCase::FINISHED, name)
          retval
        end
      end

      module MiniTest
        def run(runner)
          _run_class_setup

          serialized = run_in_isolation do |isolated_runner|
            super(isolated_runner)
          end

          retval, proxy = Marshal.load(serialized)
          proxy.__replay__(runner)
          retval
        end
      end

      module Forking
        def run_in_isolation(&blk)
          read, write = IO.pipe

          pid = fork do
            read.close
            proxy = ProxyTestResult.new
            retval = yield proxy
            write.puts [Marshal.dump([retval, proxy])].pack("m")
            exit!
          end

          write.close
          result = read.read
          Process.wait2(pid)
          return result.unpack("m")[0]
        end
      end

      module Subprocess
        ORIG_ARGV = ARGV.dup unless defined?(ORIG_ARGV)

        # Crazy H4X to get this working in windows / jruby with
        # no forking.
        def run_in_isolation(&blk)
          require "tempfile"

          if ENV["ISOLATION_TEST"]
            proxy = ProxyTestResult.new
            retval = yield proxy
            File.open(ENV["ISOLATION_OUTPUT"], "w") do |file|
              file.puts [Marshal.dump([retval, proxy])].pack("m")
            end
            exit!
          else
            Tempfile.open("isolation") do |tmpfile|
              ENV["ISOLATION_TEST"] = @method_name
              ENV["ISOLATION_OUTPUT"] = tmpfile.path

              load_paths = $-I.map {|p| "-I\"#{File.expand_path(p)}\"" }.join(" ")
              `#{Gem.ruby} #{load_paths} #{$0} #{ORIG_ARGV.join(" ")} -t\"#{self.class}\"`

              ENV.delete("ISOLATION_TEST")
              ENV.delete("ISOLATION_OUTPUT")

              return tmpfile.read.unpack("m")[0]
            end
          end
        end
      end

      include forking_env? ? Forking : Subprocess
    end
  end
end

# Only in subprocess for windows / jruby.
if ENV['ISOLATION_TEST']
  require "test/unit/collector/objectspace"
  class Test::Unit::Collector::ObjectSpace
    def include?(test)
      super && test.method_name == ENV['ISOLATION_TEST']
    end
  end
end
Something went wrong with that request. Please try again.