TestRunLoopExitsPromptlyOnCancel_DuringPoll pins that RunLoop returns quickly when its context is cancelled while it is blocked on the fallback poll ticker. Regression guard for the fallback interval: if a future change introduces a non-cancellable wait here, this test will hang and fail.
(t *testing.T)
| 1482 | // interval: if a future change introduces a non-cancellable wait |
| 1483 | // here, this test will hang and fail. |
| 1484 | func TestRunLoopExitsPromptlyOnCancel_DuringPoll(t *testing.T) { |
| 1485 | t.Parallel() |
| 1486 | |
| 1487 | logger := slogtest.Make(t, nil) |
| 1488 | mClock := quartz.NewMock(t) |
| 1489 | h := agentgit.NewHandler(logger, agentgit.WithClock(mClock)) |
| 1490 | |
| 1491 | ctx, cancel := context.WithCancel(context.Background()) |
| 1492 | t.Cleanup(cancel) |
| 1493 | |
| 1494 | // Trap NewTicker so the test can synchronize on RunLoop's |
| 1495 | // ticker creation rather than racing against it with a |
| 1496 | // best-effort Advance. |
| 1497 | tickerTrap := mClock.Trap().NewTicker() |
| 1498 | defer tickerTrap.Close() |
| 1499 | |
| 1500 | done := make(chan struct{}) |
| 1501 | go func() { |
| 1502 | defer close(done) |
| 1503 | h.RunLoop(ctx, func() {}) |
| 1504 | }() |
| 1505 | |
| 1506 | // Wait until RunLoop has actually called clock.NewTicker, then |
| 1507 | // release the trap so the ticker is installed. At this point |
| 1508 | // RunLoop is deterministically inside its select, blocked on |
| 1509 | // <-ticker.C / <-scanTrigger / <-ctx.Done(). |
| 1510 | tickerTrap.MustWait(ctx).MustRelease(ctx) |
| 1511 | |
| 1512 | cancel() |
| 1513 | |
| 1514 | select { |
| 1515 | case <-done: |
| 1516 | case <-time.After(testutil.WaitShort): |
| 1517 | t.Fatal("RunLoop did not return within WaitShort after ctx cancel") |
| 1518 | } |
| 1519 | } |
| 1520 | |
| 1521 | // TestRunLoopExitsPromptlyOnCancel_DuringCooldown pins that RunLoop |
| 1522 | // returns quickly when its context is cancelled while a |