Skip to content

Commit ace7482

Browse files
committed
Migrate go.mod to 1.25 and use synctest for real
This is a simple move of the existing synctest cases over top of the legacy ones, without touching the synctest.Run variants or removing the old assertions. I'll get to those in just a moment.
1 parent e8479bc commit ace7482

File tree

3 files changed

+61
-127
lines changed

3 files changed

+61
-127
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/featherbread/hypcast
22

3-
go 1.24.0
3+
go 1.25.0
44

55
require (
66
github.com/google/go-cmp v0.7.0
Lines changed: 0 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,3 @@
11
//go:build goexperiment.synctest && go1.25
22

33
package watch
4-
5-
import (
6-
"testing"
7-
"testing/synctest"
8-
9-
"github.com/stretchr/testify/assert"
10-
)
11-
12-
func TestCancelInactiveHandlerSynctest2(t *testing.T) {
13-
// The usual case of canceling a watch, where no handler is active at the time
14-
// of cancellation.
15-
synctest.Test(t, func(t *testing.T) {
16-
notify := make(chan string)
17-
v := NewValue("alice")
18-
w := v.Watch(func(x string) { notify <- x })
19-
20-
// Deal with the initial notification. Then, wait for the handler goroutine
21-
// to exit before canceling the watch.
22-
assert.Equal(t, "alice", <-notify)
23-
synctest.Wait()
24-
w.Cancel()
25-
26-
// Set another value, and ensure that we're not notified even after
27-
// background goroutines have settled.
28-
v.Set("bob")
29-
synctest.Wait()
30-
select {
31-
case <-notify:
32-
t.Error("watcher notified after being canceled")
33-
default:
34-
}
35-
})
36-
}
37-
38-
func TestDoubleCancelInactiveHandlerSynctest2(t *testing.T) {
39-
// A specific test for calling Cancel twice on an inactive handler.
40-
synctest.Test(t, func(t *testing.T) {
41-
v := NewValue("alice")
42-
w := v.Watch(func(x string) {})
43-
44-
// Wait for the initial handler to exit, then cancel the watch twice.
45-
// The goal is simply to not panic.
46-
synctest.Wait()
47-
w.Cancel()
48-
w.Cancel()
49-
w.Wait()
50-
})
51-
}
52-
53-
func TestWaitSynctest2(t *testing.T) {
54-
// A specific test to ensure that Wait properly blocks until the watch has
55-
// terminated.
56-
synctest.Test(t, func(t *testing.T) {
57-
notify := make(chan string)
58-
v := NewValue("alice")
59-
w := v.Watch(func(x string) { notify <- x })
60-
61-
// Ensure that we have a handler in flight from the initial notification.
62-
synctest.Wait()
63-
64-
// Start waiting in the background. We should remain blocked.
65-
done := make(chan struct{})
66-
go func() { defer close(done); w.Wait() }()
67-
synctest.Wait()
68-
select {
69-
case <-done:
70-
t.Fatal("watcher finished waiting before cancellation")
71-
default:
72-
}
73-
74-
// Cancel the watch, and ensure that Wait is still blocked.
75-
w.Cancel()
76-
synctest.Wait()
77-
select {
78-
case <-done:
79-
t.Fatal("watcher finished waiting before handler exit")
80-
default:
81-
}
82-
83-
// Allow the handler to finish. At this point, we should become unblocked.
84-
assert.Equal(t, "alice", <-notify)
85-
<-done
86-
})
87-
}

internal/watch/watch_test.go

Lines changed: 60 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import (
55
"runtime"
66
"sync"
77
"testing"
8+
"testing/synctest"
89
"time"
10+
11+
"github.com/stretchr/testify/assert"
912
)
1013

1114
const timeout = 2 * time.Second
@@ -201,36 +204,43 @@ func TestGoexitFromHandler(t *testing.T) {
201204

202205
func TestCancelInactiveHandler(t *testing.T) {
203206
// The usual case of canceling a watch, where no handler is active at the time
204-
// of cancellation. Once we cancel, no further handler calls should be made.
207+
// of cancellation.
208+
synctest.Test(t, func(t *testing.T) {
209+
notify := make(chan string)
210+
v := NewValue("alice")
211+
w := v.Watch(func(x string) { notify <- x })
212+
213+
// Deal with the initial notification. Then, wait for the handler goroutine
214+
// to exit before canceling the watch.
215+
assert.Equal(t, "alice", <-notify)
216+
synctest.Wait()
217+
w.Cancel()
205218

206-
v := NewValue("alice")
207-
notify := make(chan string, 1)
208-
w := v.Watch(func(x string) {
219+
// Set another value, and ensure that we're not notified even after
220+
// background goroutines have settled.
221+
v.Set("bob")
222+
synctest.Wait()
209223
select {
210-
case notify <- x:
224+
case <-notify:
225+
t.Error("watcher notified after being canceled")
211226
default:
212227
}
213228
})
214-
215-
assertNextReceive(t, notify, "alice")
216-
forceRuntimeProgress() // Try to ensure the handler has fully terminated.
217-
218-
w.Cancel()
219-
v.Set("bob")
220-
assertBlockedAfter(forceRuntimeProgress, t, notify)
221229
}
222230

223231
func TestDoubleCancelInactiveHandler(t *testing.T) {
224-
// A specific test for calling Cancel twice on an inactive handler, and
225-
// ensuring we don't panic.
226-
227-
v := NewValue("alice")
228-
w := v.Watch(func(x string) {})
229-
forceRuntimeProgress() // Try to ensure the initial handler has fully terminated.
230-
231-
w.Cancel()
232-
w.Cancel()
233-
assertWatchTerminates(t, w)
232+
// A specific test for calling Cancel twice on an inactive handler.
233+
synctest.Test(t, func(t *testing.T) {
234+
v := NewValue("alice")
235+
w := v.Watch(func(x string) {})
236+
237+
// Wait for the initial handler to exit, then cancel the watch twice.
238+
// The goal is simply to not panic.
239+
synctest.Wait()
240+
w.Cancel()
241+
w.Cancel()
242+
w.Wait()
243+
})
234244
}
235245

236246
func TestCancelBlockedWatcher(t *testing.T) {
@@ -299,29 +309,37 @@ func TestDoubleCancelFromHandler(t *testing.T) {
299309
func TestWait(t *testing.T) {
300310
// A specific test to ensure that Wait properly blocks until the watch has
301311
// terminated.
312+
synctest.Test(t, func(t *testing.T) {
313+
notify := make(chan string)
314+
v := NewValue("alice")
315+
w := v.Watch(func(x string) { notify <- x })
316+
317+
// Ensure that we have a handler in flight from the initial notification.
318+
synctest.Wait()
319+
320+
// Start waiting in the background. We should remain blocked.
321+
done := make(chan struct{})
322+
go func() { defer close(done); w.Wait() }()
323+
synctest.Wait()
324+
select {
325+
case <-done:
326+
t.Fatal("watcher finished waiting before cancellation")
327+
default:
328+
}
302329

303-
v := NewValue("alice")
330+
// Cancel the watch, and ensure that Wait is still blocked.
331+
w.Cancel()
332+
synctest.Wait()
333+
select {
334+
case <-done:
335+
t.Fatal("watcher finished waiting before handler exit")
336+
default:
337+
}
304338

305-
block, notify := make(chan struct{}), make(chan string)
306-
w := v.Watch(func(x string) {
307-
<-block
308-
notify <- x
339+
// Allow the handler to finish. At this point, we should become unblocked.
340+
assert.Equal(t, "alice", <-notify)
341+
<-done
309342
})
310-
311-
// Ensure that we have a handler in flight.
312-
block <- struct{}{}
313-
314-
// Start waiting in the background. We should remain blocked.
315-
done := makeWaitChannel(w)
316-
assertBlockedAfter(forceRuntimeProgress, t, done)
317-
318-
// Cancel the watch, and ensure that we are still blocked.
319-
w.Cancel()
320-
assertBlockedAfter(forceRuntimeProgress, t, done)
321-
322-
// Allow the handler to finish. At this point, we should become unblocked.
323-
assertNextReceive(t, notify, "alice")
324-
assertWatchTerminates(t, w)
325343
}
326344

327345
// makeWaitChannel returns a channel that will be closed after w.Wait returns.

0 commit comments

Comments
 (0)