diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-13 14:35:27 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-13 14:35:27 +0300 |
| commit | 306b9eb1c1054992506dd36fc9f1b64e11276598 (patch) | |
| tree | 51cb6690961e64035bf85e95c212fbe34a5ea7d5 /internal | |
| parent | ed7cf2505d92e05411d476b445bda45cab9aaf89 (diff) | |
test: replace time.Sleep with deterministic synchronization in unit tests
Replace three time.Sleep usages in tests with channel-based and
happens-before reasoning:
- internal/ior_mode_test.go: waitForStreamRows no longer polls with
time.Sleep(1ms); starter() only returns after the trace goroutine
closes the started channel, which happens after all printCb pushes,
forming a happens-before edge that guarantees the rows are visible.
- internal/probemanager/manager_test.go: the intermediate attach-count
assertions (enter==1, exit==0) are now checked immediately after
<-enterBlocked, which is itself a happens-before edge, rather than
after a 50ms sleep. The concurrent goroutine 2 is started afterwards;
the final count assertions after both goroutines complete verify the
serialization invariant.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/ior_mode_test.go | 15 | ||||
| -rw-r--r-- | internal/probemanager/manager_test.go | 24 |
2 files changed, 24 insertions, 15 deletions
diff --git a/internal/ior_mode_test.go b/internal/ior_mode_test.go index 81fd314..a7c3bee 100644 --- a/internal/ior_mode_test.go +++ b/internal/ior_mode_test.go @@ -943,16 +943,17 @@ func testTracePair(seq uint64, comm string) *event.Pair { return pair } +// waitForStreamRows asserts that buffer.Len() equals want immediately. +// starter() returns only after the trace goroutine closes the started channel, +// which happens after all printCb calls have completed and pushed their rows +// into the buffer. The channel close forms a happens-before edge that makes all +// prior writes visible to the test goroutine, so no polling or sleeping is +// needed. func waitForStreamRows(t *testing.T, buffer *streamrow.RingBuffer, want int) { t.Helper() - deadline := time.Now().Add(2 * time.Second) - for time.Now().Before(deadline) { - if buffer.Len() == want { - return - } - time.Sleep(time.Millisecond) + if got := buffer.Len(); got != want { + t.Fatalf("stream buffer len = %d, want %d", got, want) } - t.Fatalf("stream buffer len = %d, want %d", buffer.Len(), want) } func readRecordedParquet(t *testing.T, path string) []parquet.Record { diff --git a/internal/probemanager/manager_test.go b/internal/probemanager/manager_test.go index 2beb11e..b75a579 100644 --- a/internal/probemanager/manager_test.go +++ b/internal/probemanager/manager_test.go @@ -158,19 +158,27 @@ func TestManagerAttachSerializesConcurrentCalls(t *testing.T) { t.Fatal("first attach did not start") } - errCh2 := make(chan error, 1) - go func() { - errCh2 <- mgr.Attach("close") - }() - - time.Sleep(50 * time.Millisecond) + // Goroutine 1 is now blocked inside AttachTracepoint for the enter probe. + // Enter has been called exactly once; exit has not been called yet because + // attachPair calls enter then exit sequentially. These assertions are safe + // without any sleep: enterBlocked being closed is a happens-before edge that + // makes the attach-count writes visible here. if got := enter.attachCalls(); got != 1 { - t.Fatalf("expected only one enter attach while first call was in flight, got %d", got) + t.Fatalf("expected enter attach to be in flight (called once), got %d", got) } if got := exit.attachCalls(); got != 0 { - t.Fatalf("expected exit attach to wait for enter completion, got %d", got) + t.Fatalf("expected exit attach to not have started while enter is blocked, got %d", got) } + // Start a second concurrent Attach. It will acquire m.mu briefly then + // block on entry.attachMu (held by goroutine 1) before it can reach + // AttachTracepoint. The final count assertions below confirm it never ran + // a second attach. + errCh2 := make(chan error, 1) + go func() { + errCh2 <- mgr.Attach("close") + }() + close(releaseEnter) if err := <-errCh1; err != nil { |
