|
| 1 | +#!/usr/bin/env ruby |
| 2 | +# frozen_string_literal: true |
| 3 | + |
| 4 | +require_relative "../lib/sus/config" |
| 5 | +config = Sus::Config.load |
| 6 | + |
| 7 | +require_relative "../lib/sus" |
| 8 | + |
| 9 | +# Two-pass test execution: |
| 10 | +# Pass 1: Run all tests with null output to collect failures quickly |
| 11 | +# Pass 2: Re-run only failed tests with full output |
| 12 | + |
| 13 | +puts "🚀 Running fast two-pass test execution..." |
| 14 | + |
| 15 | +# Pass 1: Fast execution with null output |
| 16 | +print "Pass 1: Running all tests with null output... " |
| 17 | + |
| 18 | +registry = config.registry |
| 19 | +null_assertions = Sus::Assertions.default(output: Sus::Output::Null.new, verbose: false) |
| 20 | + |
| 21 | +config.before_tests(null_assertions) |
| 22 | +registry.call(null_assertions) |
| 23 | +config.after_tests(null_assertions) |
| 24 | + |
| 25 | +# Collect failure keys |
| 26 | +failure_keys = [] |
| 27 | +null_assertions.each_failure do |failure| |
| 28 | + |
| 29 | + if failure.respond_to?(:assertions) && failure.assertions.respond_to?(:identity) && failure.assertions.identity |
| 30 | + # This is for nested Assertions (test level) |
| 31 | + failure_keys << failure.assertions.identity.key |
| 32 | + elsif failure.respond_to?(:identity) && failure.identity |
| 33 | + # This is for individual Assert objects - we need the parent test identity |
| 34 | + # Try to get the test identity by using the parent or finding the containing test |
| 35 | + if failure.identity.parent |
| 36 | + failure_keys << failure.identity.parent.key |
| 37 | + else |
| 38 | + failure_keys << failure.identity.key |
| 39 | + end |
| 40 | + end |
| 41 | +end |
| 42 | + |
| 43 | +failure_keys.uniq! |
| 44 | + |
| 45 | +puts "done. Found #{failure_keys.size} failures out of #{null_assertions.total} tests." |
| 46 | + |
| 47 | +if failure_keys.empty? |
| 48 | + # All tests passed! Just use the after_tests callback with proper output for summary |
| 49 | + puts "🎉 All tests passed!" |
| 50 | + |
| 51 | + # Get the appropriate output for the summary |
| 52 | + if config.verbose? |
| 53 | + summary_output = config.output |
| 54 | + else |
| 55 | + summary_output = Sus::Output.default |
| 56 | + end |
| 57 | + |
| 58 | + # Print summary manually to avoid conflicts with hooks |
| 59 | + config.send(:print_summary, summary_output, null_assertions) |
| 60 | +else |
| 61 | + # Pass 2: Re-run failures with full output |
| 62 | + puts "Pass 2: Re-running #{failure_keys.size} failed tests with full output..." |
| 63 | + |
| 64 | + # Use Filter::Index directly to look up and execute failed tests |
| 65 | + index = Sus::Filter::Index.new |
| 66 | + index.add(registry.base) |
| 67 | + |
| 68 | + # Run with full output |
| 69 | + output = config.verbose? ? config.output : Sus::Output.default |
| 70 | + failure_assertions = Sus::Assertions.default(output: output, verbose: true) |
| 71 | + |
| 72 | + config.before_tests(failure_assertions) |
| 73 | + |
| 74 | + # Execute only the failed tests directly from the index |
| 75 | + failure_keys.each do |key| |
| 76 | + if test_context = index[key] |
| 77 | + test_context.call(failure_assertions) |
| 78 | + end |
| 79 | + end |
| 80 | + |
| 81 | + config.after_tests(failure_assertions) |
| 82 | + |
| 83 | + unless failure_assertions.passed? |
| 84 | + exit(1) |
| 85 | + end |
| 86 | +end |
0 commit comments