@@ -43,6 +43,46 @@ def test_requeue # redefine the shared one
4343 CI ::Queue ::Redis . requeue_offset = previous_offset
4444 end
4545
46+ def test_requeue_runs_the_test_again_after_the_next_offset_plus_one_tests
47+ with_requeue_offset ( 2 ) do
48+ tests = numbered_tests ( 8 )
49+ pop_order = CI ::Queue . shuffle ( tests , Random . new ( 0 ) )
50+ queue = worker ( 1 , tests : tests , build_id : 'requeue-offset' , max_requeues : 1 , requeue_tolerance : 1.0 )
51+
52+ test_order = poll_failing_once ( queue , pop_order . first )
53+
54+ assert_equal [ pop_order [ 0 ] , *pop_order [ 1 ..3 ] , pop_order [ 0 ] , *pop_order [ 4 ..] ] , test_order
55+ end
56+ end
57+
58+ def test_requeue_runs_the_test_last_and_keeps_the_queue_ttl_when_only_offset_plus_one_tests_remain
59+ with_requeue_offset ( 2 ) do
60+ tests = numbered_tests ( 4 )
61+ pop_order = CI ::Queue . shuffle ( tests , Random . new ( 0 ) )
62+ queue = worker ( 1 , tests : tests , build_id : 'requeue-short-queue' , max_requeues : 1 , requeue_tolerance : 1.0 )
63+ queue_ttl_after_requeue = nil
64+
65+ test_order = poll_failing_once ( queue , pop_order . first ) do |test |
66+ queue_ttl_after_requeue = @redis . ttl ( 'build:requeue-short-queue:queue' ) if test == pop_order [ 1 ]
67+ end
68+
69+ assert_equal [ *pop_order , pop_order [ 0 ] ] , test_order
70+ assert_operator queue_ttl_after_requeue , :> , 0
71+ end
72+ end
73+
74+ def test_requeue_with_a_zero_offset_runs_the_test_last
75+ with_requeue_offset ( 0 ) do
76+ tests = numbered_tests ( 4 )
77+ pop_order = CI ::Queue . shuffle ( tests , Random . new ( 0 ) )
78+ queue = worker ( 1 , tests : tests , build_id : 'requeue-zero-offset' , max_requeues : 1 , requeue_tolerance : 1.0 )
79+
80+ test_order = poll_failing_once ( queue , pop_order . first )
81+
82+ assert_equal [ *pop_order , pop_order [ 0 ] ] , test_order
83+ end
84+ end
85+
4686 def test_retry_queue_with_all_tests_passing
4787 poll ( @queue )
4888 retry_queue = @queue . retry_queue
@@ -467,6 +507,24 @@ def test_reserve_defers_own_requeued_test_once
467507 assert_equal entry , second_try [ 0 ]
468508 end
469509
510+ def test_reserve_defers_own_requeued_test_behind_the_next_offset_plus_one_tests
511+ with_requeue_offset ( 2 ) do
512+ tests = numbered_tests ( 10 )
513+ pop_order = CI ::Queue . shuffle ( tests , Random . new ( 0 ) )
514+ queue = worker ( 1 , tests : tests , build_id : 'defer-offset' , max_requeues : 1 , requeue_tolerance : 1.0 )
515+ worker ( 2 , tests : tests , build_id : 'defer-offset' )
516+ assert_equal 2 , queue . workers_count
517+ queue_after_deferral = nil
518+
519+ test_order = poll_failing_once ( queue , pop_order . first ) do |test |
520+ queue_after_deferral = queue . to_a if test == pop_order [ 4 ]
521+ end
522+
523+ assert_equal [ *pop_order [ 4 ..6 ] , pop_order [ 0 ] , *pop_order [ 7 ..] ] , queue_after_deferral
524+ assert_equal [ *pop_order , pop_order [ 0 ] ] , test_order
525+ end
526+ end
527+
470528 def test_heartbeat_only_checks_lease
471529 queue = worker ( 1 , populate : false )
472530 entry = CI ::Queue ::QueueEntry . format ( 'ATest#test_foo' , '/tmp/a_test.rb' )
@@ -1111,6 +1169,29 @@ def worker(id, **args)
11111169 end
11121170 end
11131171
1172+ def numbered_tests ( count )
1173+ Array . new ( count ) { |index | SharedTestCases ::TestCase . new ( "ATest#test_#{ index } " ) }
1174+ end
1175+
1176+ def poll_failing_once ( queue , failing_test , &block )
1177+ failed = false
1178+ passes = -> ( test ) do
1179+ next true if failed || test != failing_test
1180+
1181+ failed = true
1182+ false
1183+ end
1184+ poll ( queue , passes , &block )
1185+ end
1186+
1187+ def with_requeue_offset ( offset )
1188+ previous_offset = CI ::Queue ::Redis . requeue_offset
1189+ CI ::Queue ::Redis . requeue_offset = offset
1190+ yield
1191+ ensure
1192+ CI ::Queue ::Redis . requeue_offset = previous_offset
1193+ end
1194+
11141195 # A failed TLS handshake is otherwise retried for ~10 seconds.
11151196 def without_reconnect_attempts
11161197 original = ENV [ 'CI_QUEUE_DISABLE_RECONNECT_ATTEMPTS' ]
0 commit comments