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

Cannot retrieve contributors at this time

file 153 lines (126 sloc) 3.972 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
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"] && RUBY_PLATFORM !~ /mswin|mingw|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

      module TestUnit
        def run(result)
          unless defined?(@@ran_class_setup)
            self.class.setup if self.class.respond_to?(:setup)
            @@ran_class_setup = true
          end

          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)
          unless defined?(@@ran_class_setup)
            self.class.setup if self.class.respond_to?(:setup)
            @@ran_class_setup = true
          end

          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
        # 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.