Skip to content

Commit 3fdaa3f

Browse files
test: use deterministic signaling and fix EOF comment
Replace time.Sleep with readEntered channel for deterministic assertion that the first reader is blocked. Fix comment to accurately describe EOF error path (not panic recovery).
1 parent 0d7debe commit 3fdaa3f

1 file changed

Lines changed: 24 additions & 7 deletions

File tree

token_test.go

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -471,11 +471,18 @@ func TestNextToken_CancelDrainClosedChannelStartsSecondResponse(t *testing.T) {
471471
}
472472

473473
// blockingTransport blocks Read until unblock is closed, then returns EOF.
474+
// It signals readEntered when a Read call begins, allowing deterministic
475+
// synchronization without time.Sleep.
474476
type blockingTransport struct {
475-
unblock chan struct{}
477+
unblock chan struct{}
478+
readEntered chan struct{}
476479
}
477480

478481
func (b *blockingTransport) Read([]byte) (int, error) {
482+
select {
483+
case b.readEntered <- struct{}{}:
484+
default:
485+
}
479486
<-b.unblock
480487
return 0, io.EOF
481488
}
@@ -487,14 +494,24 @@ func (b *blockingTransport) Close() error { return nil }
487494
// the previous goroutine to finish before launching a new one.
488495
func TestStartResponseReaderSerializes(t *testing.T) {
489496
// First reader: transport blocks until we say so.
497+
readEntered := make(chan struct{}, 1)
490498
unblock := make(chan struct{})
491499
sess := &tdsSession{
492-
buf: newTdsBuffer(defaultPacketSize, &blockingTransport{unblock: unblock}),
500+
buf: newTdsBuffer(defaultPacketSize, &blockingTransport{
501+
unblock: unblock,
502+
readEntered: readEntered,
503+
}),
493504
}
494505

495506
ch1 := make(chan tokenStruct, 10)
496507
sess.startResponseReader(context.Background(), ch1, outputs{})
497-
// First goroutine is now blocked inside processSingleResponse on Read.
508+
509+
// Wait for the first goroutine to actually enter Read, proving it's blocked.
510+
select {
511+
case <-readEntered:
512+
case <-time.After(5 * time.Second):
513+
t.Fatal("first reader never entered Read")
514+
}
498515

499516
// Launch second startResponseReader in a goroutine; it should block on
500517
// <-sess.readDone until the first reader finishes.
@@ -505,14 +522,14 @@ func TestStartResponseReaderSerializes(t *testing.T) {
505522
secondStarted.Store(1)
506523
}()
507524

508-
// Give the goroutine time to reach the <-sess.readDone wait.
509-
time.Sleep(50 * time.Millisecond)
525+
// The second goroutine cannot proceed while the first reader is blocked,
526+
// so secondStarted must still be 0.
510527
if secondStarted.Load() != 0 {
511528
t.Fatal("second startResponseReader returned before first completed")
512529
}
513530

514-
// Unblock the first reader. processSingleResponse will hit EOF, recover
515-
// from the panic, and close ch1 + readDone.
531+
// Unblock the first reader. processSingleResponse will receive EOF from
532+
// BeginRead as an error, send it to ch1, and return, closing readDone.
516533
close(unblock)
517534

518535
// Second call should now proceed.

0 commit comments

Comments
 (0)