From 85beab56ffaa1969d307f9fd71491cd6670f4140 Mon Sep 17 00:00:00 2001 From: Andreas Heiberg Date: Thu, 23 Apr 2026 12:57:21 +0200 Subject: [PATCH] Fix CI_QUEUE_LAZY_LOAD silently breaking queues without stream_populate When lazy_load=true but the queue does not implement stream_populate (e.g. CI::Queue::Bisect), load_tests skipped requiring test files, leaving Minitest.loaded_tests empty. populate_queue then called queue.populate([]), so Bisect#failing_test_present? always returned nil, causing the bisect runner to exit with "The failing test does not exist." despite the test being a valid, runnable test. Fix: only skip eager file loading when the queue actually supports stream_populate. When it does not, fall through to the eager path so Minitest.loaded_tests is populated before populate is called. --- .../queue/queue_population_strategy.rb | 2 +- .../queue/queue_population_strategy_test.rb | 36 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/ruby/lib/minitest/queue/queue_population_strategy.rb b/ruby/lib/minitest/queue/queue_population_strategy.rb index 9a461ab0..94a93ce4 100644 --- a/ruby/lib/minitest/queue/queue_population_strategy.rb +++ b/ruby/lib/minitest/queue/queue_population_strategy.rb @@ -42,7 +42,7 @@ def populate_queue def load_tests start = Process.clock_gettime(Process::CLOCK_MONOTONIC) - if preresolved_test_list || queue_config.lazy_load + if preresolved_test_list || (queue_config.lazy_load && queue.respond_to?(:stream_populate)) # In preresolved or lazy-load mode, test files are loaded on-demand by the entry resolver. # Load test helpers (e.g., test/test_helper.rb via CI_QUEUE_LAZY_LOAD_TEST_HELPERS) # to boot the app for all workers. diff --git a/ruby/test/minitest/queue/queue_population_strategy_test.rb b/ruby/test/minitest/queue/queue_population_strategy_test.rb index 6f7ec7dc..90a1fbed 100644 --- a/ruby/test/minitest/queue/queue_population_strategy_test.rb +++ b/ruby/test/minitest/queue/queue_population_strategy_test.rb @@ -25,6 +25,16 @@ def stream_populate(tests, random:, batch_size:) end end + # Simulates CI::Queue::Bisect — no stream_populate + class FakeQueueWithoutStreamPopulate + attr_accessor :entry_resolver + attr_reader :populated_with + + def populate(tests, random:) + @populated_with = { tests: tests, random: random } + end + end + def test_eager_mode_populates_loaded_tests queue = FakeQueue.new config = CI::Queue::Configuration.new(lazy_load: false) @@ -205,5 +215,31 @@ def test_lazy_mode_sets_resolver_and_streams assert_equal 7, queue.streamed_with[:batch_size] assert_nil queue.populated_with end + + def test_lazy_mode_falls_back_to_eager_loading_when_queue_lacks_stream_populate + queue = FakeQueueWithoutStreamPopulate.new + config = CI::Queue::Configuration.new(lazy_load: true) + class_name = "StrategyLazyFallback#{Process.pid}#{rand(1000)}" + + Dir.mktmpdir do |dir| + file = File.join(dir, "strategy_lazy_fallback_test.rb") + File.write(file, "class #{class_name} < Minitest::Test\n def test_fallback\n assert true\n end\nend\n") + strategy = QueuePopulationStrategy.new( + queue: queue, + queue_config: config, + argv: [file], + test_files_file: nil, + ordering_seed: Random.new(123), + ) + strategy.load_and_populate! + end + + assert queue.populated_with, "expected populate to be called" + ids = queue.populated_with[:tests].map(&:id) + assert_includes ids, "#{class_name}#test_fallback", + "lazy_load=true should not prevent test files from being loaded when stream_populate is unavailable" + ensure + Object.send(:remove_const, class_name) if class_name && Object.const_defined?(class_name) + end end end