Class: Minitest::ForkExecutor
- Inherits:
-
Object
- Object
- Minitest::ForkExecutor
- 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
-
.run ⇒ Object
Run a fork-based test case.
Instance Method Summary collapse
- #shutdown ⇒ Object
-
#start ⇒ Object
#start is called by Minitest when initializing the executor.
Class Method Details
.run ⇒ Object
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
#shutdown ⇒ Object
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 |
#start ⇒ Object
#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 |