Skip to content

Commit 1cbe351

Browse files
authored
Use synctest to test mutexMap (#183)
Makes tests deterministic and no longer take wall clock time.
1 parent 33722ab commit 1cbe351

File tree

1 file changed

+46
-53
lines changed

1 file changed

+46
-53
lines changed

internal/backend/mutex_map_test.go

Lines changed: 46 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -6,61 +6,54 @@ package backend
66
import (
77
"context"
88
"testing"
9+
"testing/synctest"
910
"time"
1011
)
1112

1213
func TestMutexMap(t *testing.T) {
13-
// Prevent this test from blocking past the test deadline.
14-
ctx := context.Background()
15-
if deadline, ok := t.Deadline(); ok {
16-
var cancel context.CancelFunc
17-
ctx, cancel = context.WithDeadline(ctx, deadline)
18-
defer cancel()
19-
}
20-
21-
mm := new(mutexMap[int])
22-
unlock1, err := mm.lock(ctx, 1)
23-
if err != nil {
24-
t.Fatal("lock(ctx, 1) on new map failed:", err)
25-
}
26-
27-
// Verify that we can acquire a lock on an independent key.
28-
unlock2, err := mm.lock(ctx, 2)
29-
if err != nil {
30-
t.Fatal("lock(ctx, 2) after lock(ctx, 1) failed:", err)
31-
}
32-
33-
// Verify that attempting a lock on the same key blocks until Done.
34-
failFastCtx, cancelFailFast := context.WithTimeout(ctx, 100*time.Millisecond)
35-
unlock1b, err := mm.lock(failFastCtx, 1)
36-
cancelFailFast()
37-
if err == nil {
38-
t.Error("lock(ctx, 1) acquired without releasing unlock1")
39-
unlock1b()
40-
}
41-
42-
// Verify that unlocking a key allows a subsequent lock to succeed.
43-
unlock1()
44-
unlock1, err = mm.lock(ctx, 1)
45-
if err != nil {
46-
t.Fatal("lock(ctx, 1) after unlock1 failed:", err)
47-
}
48-
49-
// Verify that unlocking a key allows a concurrent lock to succeed.
50-
lock2Done := make(chan error)
51-
go func() {
52-
_, err := mm.lock(ctx, 2)
53-
lock2Done <- err
54-
}()
55-
// Wait for a little bit to make it more likely that the other goroutine hit lock(2).
56-
timer := time.NewTimer(10 * time.Millisecond)
57-
select {
58-
case <-timer.C:
59-
case <-ctx.Done():
60-
timer.Stop()
61-
}
62-
unlock2()
63-
if err := <-lock2Done; err != nil {
64-
t.Error("lock(ctx, 2) with concurrent unlock2 failed:", err)
65-
}
14+
synctest.Test(t, func(t *testing.T) {
15+
ctx := context.Background()
16+
17+
mm := new(mutexMap[int])
18+
unlock1, err := mm.lock(ctx, 1)
19+
if err != nil {
20+
t.Fatal("lock(ctx, 1) on new map failed:", err)
21+
}
22+
23+
// Verify that we can acquire a lock on an independent key.
24+
unlock2, err := mm.lock(ctx, 2)
25+
if err != nil {
26+
t.Fatal("lock(ctx, 2) after lock(ctx, 1) failed:", err)
27+
}
28+
29+
// Verify that attempting a lock on the same key blocks until Done.
30+
failFastCtx, cancelFailFast := context.WithTimeout(ctx, 100*time.Millisecond)
31+
unlock1b, err := mm.lock(failFastCtx, 1)
32+
cancelFailFast()
33+
if err == nil {
34+
t.Error("lock(ctx, 1) acquired without releasing unlock1")
35+
unlock1b()
36+
}
37+
38+
// Verify that unlocking a key allows a subsequent lock to succeed.
39+
unlock1()
40+
unlock1, err = mm.lock(ctx, 1)
41+
if err != nil {
42+
t.Fatal("lock(ctx, 1) after unlock1 failed:", err)
43+
}
44+
unlock1()
45+
46+
// Verify that unlocking a key allows a concurrent lock to succeed.
47+
lock2Done := make(chan error)
48+
go func() {
49+
_, err := mm.lock(ctx, 2)
50+
lock2Done <- err
51+
}()
52+
// Sleep to make other goroutine hit lock(2). (Yay synctest!)
53+
time.Sleep(10 * time.Millisecond)
54+
unlock2()
55+
if err := <-lock2Done; err != nil {
56+
t.Error("lock(ctx, 2) with concurrent unlock2 failed:", err)
57+
}
58+
})
6659
}

0 commit comments

Comments
 (0)