Class: Minitest::ForkExecutor

Inherits:
Object
  • Object
show all
Defined in:
lib/minitest/fork_executor.rb

Overview

Minitest runs individual test cases via Minitest.run_one_method. When ForkExecutor is started, we need to override it and implement the fork algorithm. See the detailed comments below to understand how it's done.

Please keep in mind we support Ruby 1.9 and hence can't use conveniences offered by more modern Rubies.

Defined Under Namespace

Classes: FailureTransport, UnmarshallableError

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.runObject

Run a fork-based test case. The block passed to the method should actually run the test using the original code from Minitest.



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
# File 'lib/minitest/fork_executor.rb', line 12

def self.run
  # Set up a binary pipe for transporting test results from the child
  # to the parent process.
  read_io, write_io = IO.pipe
  read_io.binmode
  write_io.binmode

  if Process.fork
    # The parent process responsible for collecting results.

    # The parent process doesn't write anything.
    write_io.close

    # Load the result object passed by the child process.
    result = Marshal.load(read_io)

    # Unwrap all failures from FailureTransport so that they can be
    # safely presented to the user.
    result.failures.map!(&:failure)

    # We're done reading results from the child so it's safe to close the
    # IO object now.
    read_io.close

    # Wait for the child process to finish before returning the result.
    Process.wait
  else
    # The child process responsible for running the test case.

    # Run the test case method via the original .run_one_method.
    result = yield

    # Wrap failures in FailureTransport to avoid issue when marshalling.
    # Some failures correspond to exceptions referencing unmarshallable
    # objects. For example, a PostgreSQL exception may reference
    # PG::Connection that cannot be marshalled. In those case, we replace
    # the original error with UnmarshallableError retaining as much
    # detail as possible.
    result.failures.map! { |failure| FailureTransport.new(failure) }

    # The child process doesn't read anything.
    read_io.close

    # Dump the result object to the write IO object so that it can be
    # read by the parent process.
    Marshal.dump(result, write_io)

    # We're done sending results to the parent so it's safe to close the
    # IO object now.
    write_io.close

    # Exit the child process as its job is now done.
    exit
  end

  # This value is returned ONLY in the parent process, not in the child
  # process.
  result
end

Instance Method Details

#shutdownObject



113
114
115
116
# File 'lib/minitest/fork_executor.rb', line 113

def shutdown
  # Nothing to do here but required by Minitest. In a future version, we may
  # reinstate the original Minitest.run_one_method here.
end

#startObject

#start is called by Minitest when initializing the executor. This is where we override some Minitest internals to implement fork-based execution.



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
# File 'lib/minitest/fork_executor.rb', line 74

def start
  if Minitest.respond_to?(:run_one_method)
    # Minitest 5

    # Store the reference to the original run_one_method method in order to
    # use it to actually run the test case.
    original_run_one_method = Minitest.method(:run_one_method)

    # Remove the original singleton method from Minitest in order to avoid
    # method redefinition warnings when patching it in the next step.
    class << Minitest
      remove_method(:run_one_method)
    end

    # Define a new version of run_one_method that forks, calls the original
    # run_one_method in the child process, and sends results back to the
    # parent. klass and method_name are the two parameters accepted by the
    # original run_one_method - they're the test class (e.g. UserTest) and
    # the test method name (e.g. :test_email_must_be_unique).
    Minitest.define_singleton_method(:run_one_method) do |klass, method_name|
      ForkExecutor.run do
        original_run_one_method.call(klass, method_name)
      end
    end
  else
    # Minitest 6

    Runnable.runnables.each do |runnable_class|
      original_run_method = runnable_class.instance_method(:run)
      runnable_class.define_method(:run) do
        bound_original_run_method = original_run_method.bind(self)
        ForkExecutor.run do
          bound_original_run_method.call
        end
      end
    end
  end
end