Class: Ace::TestRunner::Molecules::InProcessRunner
- Inherits:
-
Object
- Object
- Ace::TestRunner::Molecules::InProcessRunner
- Defined in:
- lib/ace/test_runner/molecules/in_process_runner.rb
Overview
Runs tests directly in the current Ruby process without spawning subprocesses This provides significantly faster execution for unit tests that don’t need isolation
Instance Method Summary collapse
- #execute_single_file(file, options = {}) ⇒ Object
- #execute_tests(files, options = {}) ⇒ Object
- #execute_with_progress(files, options = {}, &block) ⇒ Object
-
#initialize(timeout: nil) ⇒ InProcessRunner
constructor
A new instance of InProcessRunner.
Constructor Details
#initialize(timeout: nil) ⇒ InProcessRunner
Returns a new instance of InProcessRunner.
13 14 15 |
# File 'lib/ace/test_runner/molecules/in_process_runner.rb', line 13 def initialize(timeout: nil) @timeout = timeout end |
Instance Method Details
#execute_single_file(file, options = {}) ⇒ Object
174 175 176 |
# File 'lib/ace/test_runner/molecules/in_process_runner.rb', line 174 def execute_single_file(file, = {}) execute_tests([file], ) end |
#execute_tests(files, options = {}) ⇒ Object
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 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/ace/test_runner/molecules/in_process_runner.rb', line 17 def execute_tests(files, = {}) return empty_result if files.empty? start_time = Time.now # Capture stdout/stderr original_stdout = $stdout original_stderr = $stderr stdout_io = StringIO.new stderr_io = StringIO.new # Store original verbose setting original_verbose = $VERBOSE original_mt_no_autorun = ENV["MT_NO_AUTORUN"] begin $stdout = stdout_io $stderr = stderr_io $VERBOSE = nil if [:suppress_warnings] # Prevent Minitest from auto-running ENV["MT_NO_AUTORUN"] = "1" # Add test directory to load path if not already there test_dir = File.("test") lib_dir = File.("lib") $LOAD_PATH.unshift(test_dir) unless $LOAD_PATH.include?(test_dir) $LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir) # Only require minitest/autorun if not already loaded # This prevents double runs when ace/test_support is loaded unless defined?(Minitest.autorun) require "minitest/autorun" end # First pass: check for line numbers and resolve test names # This must be done before loading files test_names_to_run = [] files_to_load = [] files.each do |file| # Check if file has line number (file:line format) if file =~ /^(.+):(\d+)$/ actual_file = $1 line_number = $2.to_i # Resolve line number to test name require_relative "../atoms/line_number_resolver" test_name = Ace::TestRunner::Atoms::LineNumberResolver.resolve_test_at_line(actual_file, line_number) test_names_to_run << test_name if test_name files_to_load << actual_file else files_to_load << file end end # Store test names in options to pass to run_minitest_with_args = .merge(test_names_filter: test_names_to_run) if test_names_to_run.any? # Clear previously loaded test classes to avoid accumulation between groups # This is crucial for in-process execution where tests from previous groups # would otherwise be re-run in subsequent groups Minitest::Runnable.runnables.clear # Setup Minitest::Reporters BEFORE loading test files # For in-process mode, we need to handle reporter state carefully # because Minitest::Reporters.use! only works properly on first call require "minitest/reporters" # Create a fresh reporter for this group reporter = Minitest::Reporters::DefaultReporter.new(io: $stdout) # If this isn't the first group, we need to replace the existing reporter if Minitest.reporter && Minitest.reporter.reporters $stdout.flush Minitest.reporter.reporters.clear Minitest.reporter.reporters << reporter # Reset reporter state for the new group reporter.start_time = nil # NOTE: Known limitation - progress dots don't show for subsequent groups # in in-process mode. This appears to be a Minitest::Reporters limitation # where some internal state prevents proper output after the first run. # Test counts and results are still accurate. else # First group - use the standard setup Minitest::Reporters.use! reporter end # Load the test files files_to_load.uniq.each do |file| file_path = File.(file) begin load file_path rescue LoadError => e stderr_io.puts "Failed to load #{file}: #{e.}" # Re-raise to fail the entire test run raise end end # Run Minitest with captured output # Suppress Minitest's own output by using null reporter exit_code = if @timeout Timeout.timeout(@timeout) do run_minitest_silent() end else run_minitest_silent() end success = exit_code == true || exit_code == 0 rescue Timeout::Error stderr_io.puts "Test execution timed out after #{@timeout} seconds" success = false exit_code = 124 rescue LoadError # LoadError already logged in the loop above stderr_io.puts "Test run aborted due to load error" success = false exit_code = 1 rescue => e stderr_io.puts "Error running tests: #{e.}" stderr_io.puts e.backtrace.join("\n") if [:verbose] success = false exit_code = 1 ensure $stdout = original_stdout $stderr = original_stderr $VERBOSE = original_verbose # Restore original MT_NO_AUTORUN value if original_mt_no_autorun ENV["MT_NO_AUTORUN"] = original_mt_no_autorun else ENV.delete("MT_NO_AUTORUN") end end end_time = Time.now { stdout: stdout_io.string, stderr: stderr_io.string, status: OpenStruct.new(success?: success, exitstatus: if exit_code.is_a?(Integer) exit_code else (success ? 0 : 1) end), command: "in-process:#{files.join(",")}", start_time: start_time, end_time: end_time, duration: end_time - start_time, success: success } end |
#execute_with_progress(files, options = {}, &block) ⇒ Object
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'lib/ace/test_runner/molecules/in_process_runner.rb', line 178 def execute_with_progress(files, = {}, &block) # For in-process execution, we run all tests together for best performance result = execute_tests(files, ) # Send stdout event for per-test progress parsing if block_given? && result[:stdout] yield({type: :stdout, content: result[:stdout]}) end # Simulate progress callbacks for compatibility if block_given? files.each { |file| yield({type: :start, file: file}) } files.each { |file| yield({type: :complete, file: file, success: result[:success], duration: result[:duration] / files.size}) } end result end |