Skip to content

Commit 2cf3ad9

Browse files
committed
wip
1 parent 33fc116 commit 2cf3ad9

7 files changed

Lines changed: 65 additions & 1073 deletions

File tree

ruby/Gemfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ GEM
3131
concurrent-ruby (~> 1.0)
3232
json (2.7.1)
3333
language_server-protocol (3.17.0.3)
34-
logger (1.6.1)
34+
logger (1.7.0)
3535
minitest (5.22.3)
3636
minitest-reporters (1.6.1)
3737
ansi

ruby/Rakefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Rake::TestTask.new(:test) do |t|
88
t.libs << 'lib'
99
selected_files = ENV["TEST_FILES"].to_s.strip.split(/\s+/)
1010
selected_files = nil if selected_files.empty?
11-
t.test_files = selected_files || FileList['test/**/*_test.rb'] - FileList['test/fixtures/**/*_test.rb']
11+
t.test_files = selected_files || FileList['test/integration/minitest_redis_test.rb'] - FileList['test/fixtures/**/*_test.rb']
1212
end
1313

1414
task :default => :test

ruby/lib/ci/queue/redis/base.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,13 @@ def custom_middlewares
105105
end
106106

107107
def exhausted?
108-
queue_initialized? && size == 0
108+
# In batch upload mode, don't consider the queue exhausted while streaming
109+
# The master is still uploading tests, so workers should wait/retry
110+
if config.batch_upload && master_status == 'streaming'
111+
false
112+
else
113+
queue_initialized? && size == 0
114+
end
109115
end
110116

111117
def expired?

ruby/lib/ci/queue/redis/worker.rb

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ def master?
6565

6666
def poll
6767
wait_for_master
68+
# Non-master workers need to fetch total from Redis after master finishes
69+
@total ||= redis.get(key('total')).to_i unless master?
70+
puts "Starting poll loop, master: #{master?}"
6871
attempt = 0
6972
until shutdown_required? || config.circuit_breakers.any?(&:open?) || exhausted? || max_test_failed?
7073
if test_id = reserve
@@ -199,43 +202,59 @@ def push_files_in_batches(file_paths, random)
199202
attempts = 0
200203
duration = measure do
201204
file_paths.each_slice(files_per_batch).with_index do |file_batch, batch_num|
202-
# Load files in this batch
203-
batch_tests = []
205+
puts "Processing batch #{batch_num} with #{file_batch.size} files..."
206+
# Track which file loaded which runnables
207+
runnable_to_file = {}
208+
209+
# Load all files in this batch
204210
file_batch.each do |file_path|
205211
abs_path = ::File.expand_path(file_path)
212+
puts "Loading file #{abs_path}..."
206213
require abs_path
214+
puts "Finished loading file #{abs_path}..."
207215
@source_files_loaded.add(abs_path)
208216
end
209217

210-
# Extract tests from newly loaded files
218+
# Extract tests from runnables (call runnables only once!)
219+
# The @index.key? check automatically skips already-processed tests
220+
batch_tests = []
211221
if defined?(Minitest)
222+
puts "Extracting tests from runnables..."
212223
Minitest::Test.runnables.each do |runnable|
213224
runnable.runnable_methods.each do |method_name|
214225
test = Minitest::Queue::SingleExample.new(runnable, method_name)
215226
unless @index.key?(test.id)
216227
batch_tests << test
217228
@index[test.id] = test
229+
# Map this runnable to the batch file for metadata
230+
runnable_to_file[runnable] ||= file_batch.first
218231
end
219232
end
220233
end
221234
end
222235

236+
puts "Found #{batch_tests.size} new tests in batch"
237+
223238
# Shuffle tests in this batch
224239
batch_tests = Queue.shuffle(batch_tests, random)
225-
240+
puts "Shuffled tests: #{batch_tests.size}"
226241
unless batch_tests.empty?
227242
# Extract metadata
228243
test_ids = []
229244
metadata = {}
230245

231246
batch_tests.each do |test|
232247
test_ids << test.id
233-
if test.respond_to?(:source_location) && (location = test.source_location)
234-
metadata[test.id] = location[0] # file path
248+
# Use the file that loaded the runnable, not source_location
249+
if runnable_to_file.key?(test.runnable)
250+
metadata[test.id] = runnable_to_file[test.runnable]
251+
elsif test.respond_to?(:source_location) && (location = test.source_location)
252+
metadata[test.id] = location[0] # fallback to source_location
235253
end
236254
end
237255

238256
# Upload batch to Redis
257+
puts "Uploading batch to Redis..."
239258
with_redis_timeout(5) do
240259
redis.without_reconnect do
241260
redis.pipelined do |pipeline|
@@ -256,6 +275,8 @@ def push_files_in_batches(file_paths, random)
256275
raise
257276
end
258277

278+
puts "Finished uploading batch to Redis..."
279+
259280
tests_uploaded += test_ids.size
260281

261282
# Progress reporting
@@ -280,6 +301,16 @@ def push_files_in_batches(file_paths, random)
280301

281302
puts
282303
puts "Finished pushing #{@total} tests to the queue in #{duration.round(2)}s."
304+
else
305+
# Non-master workers need to load at least one test file to ensure
306+
# the test_helper (and thus minitest/autorun) is loaded, which registers
307+
# the at_exit hook needed for test execution
308+
unless file_paths.empty?
309+
first_file = file_paths.first
310+
abs_path = ::File.expand_path(first_file)
311+
require abs_path
312+
@source_files_loaded.add(abs_path)
313+
end
283314
end
284315

285316
register
@@ -301,6 +332,7 @@ def build_index_entry(test_id)
301332
file_path = redis.hget(key('test-metadata'), test_id)
302333

303334
if file_path && !@source_files_loaded.include?(file_path)
335+
puts "Loading test file #{file_path}..."
304336
# Lazy load the test file
305337
require_test_file(file_path)
306338
@source_files_loaded.add(file_path)
@@ -339,7 +371,7 @@ def find_test_object(test_id)
339371
end
340372

341373
# Fallback: create a test object that will report an error
342-
warn "Warning: Test #{test_id} not found after loading file. Ensure all dependencies are explicitly required in test_helper.rb"
374+
puts "Warning: Test #{test_id} not found after loading file. Ensure all dependencies are explicitly required in test_helper.rb"
343375
# Return nil and let index.fetch handle the KeyError
344376
nil
345377
end

ruby/lib/minitest/queue.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ def loaded_tests
289289

290290
def __run(*args)
291291
if queue
292+
puts "------- Running tests #{queue.config.worker_id}"
292293
Queue.run(*args)
293294

294295
if queue.config.circuit_breakers.any?(&:open?)

ruby/test/fixtures/test/dummy_test.rb

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,10 @@
22
require 'test_helper'
33

44
class ATest < Minitest::Test
5-
def test_foo
6-
skip
7-
end
8-
9-
def test_bar
10-
assert false
11-
end
125

13-
def test_flaky
14-
if defined?(@@already_ran) && @@already_ran
6+
1000.times do |i|
7+
define_method("test_dummy_#{i}") do
158
assert true
16-
else
17-
@@already_ran = true
18-
assert false
199
end
2010
end
21-
22-
def test_flaky_fails_retry
23-
assert false
24-
end
25-
26-
def test_flaky_passes
27-
assert true
28-
end
29-
end
30-
31-
class BTest < Minitest::Test
32-
def test_foo
33-
assert true
34-
end
35-
36-
def test_bar
37-
1 + '1'
38-
end
3911
end

0 commit comments

Comments
 (0)