also looking at this
fix: enforce bounded parallel test execution
#21
3 files
+478
-176
| @@ -79,57 +79,35 @@ pub const TestRunner = struct { | ||
| 79 | 79 | return RunnerError.NoTestsFound; |
| 80 | 80 | } |
| 81 | 81 | |
| 82 | // Notify reporter of run start | |
| 83 | try current_reporter.onRunStart(total_tests); | |
| 84 | ||
| 85 | 82 | // Run tests in parallel or sequential based on options |
| 86 | 83 | if (self.options.parallel) { |
| 87 | 84 | const parallel_opts = parallel.ParallelOptions{ |
| 88 | 85 | .enabled = true, |
| 89 | 86 | .n_jobs = self.options.n_jobs, |
| 87 | .filter = self.options.filter, | |
| 90 | 88 | }; |
| 91 | 89 | |
| 92 | _ = parallel.runTestsParallel( | |
| 90 | const all_passed = try parallel.runTestsParallel( | |
| 93 | 91 | self.allocator, |
| 94 | 92 | self.registry, |
| 95 | 93 | current_reporter, |
| 96 | 94 | parallel_opts, |
| 97 | ) catch |err| { | |
| 98 | std.debug.print("Parallel execution failed: {any}, falling back to sequential\n", .{err}); | |
| 99 | // Fall back to sequential execution | |
| 100 | for (self.registry.root_suites.items) |test_suite| { | |
| 101 | try self.runSuite(test_suite, current_reporter); | |
| 102 | if (self.options.bail and self.results.failed > 0) { | |
| 103 | break; | |
| 104 | } | |
| 105 | } | |
| 106 | return false; | |
| 107 | }; | |
| 108 | ||
| 109 | // Update results from parallel execution | |
| 110 | self.results.total = 0; | |
| 111 | self.results.passed = 0; | |
| 112 | self.results.failed = 0; | |
| 113 | self.results.skipped = 0; | |
| 95 | ); | |
| 114 | 96 | |
| 115 | 97 | for (self.registry.root_suites.items) |test_suite| { |
| 116 | for (test_suite.tests.items) |test_case| { | |
| 117 | self.results.total += 1; | |
| 118 | switch (test_case.status) { | |
| 119 | .passed => self.results.passed += 1, | |
| 120 | .failed => self.results.failed += 1, | |
| 121 | .skipped => self.results.skipped += 1, | |
| 122 | else => {}, | |
| 123 | } | |
| 124 | } | |
| 98 | try self.addSuiteResults(test_suite); | |
| 125 | 99 | } |
| 126 | } else { | |
| 127 | // Sequential execution (original behavior) | |
| 128 | for (self.registry.root_suites.items) |test_suite| { | |
| 129 | try self.runSuite(test_suite, current_reporter); | |
| 130 | if (self.options.bail and self.results.failed > 0) { | |
| 131 | break; | |
| 132 | } | |
| 100 | ||
| 101 | try stdout_writer.interface.flush(); | |
| 102 | return all_passed and self.results.failed == 0; | |
| 103 | } | |
| 104 | ||
| 105 | // Sequential execution (original behavior) | |
| 106 | try current_reporter.onRunStart(total_tests); | |
| 107 | for (self.registry.root_suites.items) |test_suite| { | |
| 108 | try self.runSuite(test_suite, current_reporter); | |
| 109 | if (self.options.bail and self.results.failed > 0) { | |
| 110 | break; | |
| 133 | 111 | } |
| 134 | 112 | } |
| 135 | 113 | |
| @@ -142,6 +120,11 @@ pub const TestRunner = struct { | ||
| 142 | 120 | return self.results.failed == 0; |
| 143 | 121 | } |
| 144 | 122 | |
| 123 | fn addSuiteResults(self: *Self, test_suite: *suite.TestSuite) !void { | |
| 124 | for (test_suite.tests.items) |*test_case| try self.results.addTest(test_case); | |
| 125 | for (test_suite.suites.items) |nested| try self.addSuiteResults(nested); | |
| 126 | } | |
| 127 | ||
| 145 | 128 | /// Run a single test suite |
| 146 | 129 | fn runSuite(self: *Self, test_suite: *suite.TestSuite, rep: *reporter_mod.Reporter) !void { |
| 147 | 130 | // Skip if marked as skip or if has_only and this isn't marked as only |