1
2
3
4
5 package synctest_test
6
7 import (
8 "fmt"
9 "internal/synctest"
10 "internal/testenv"
11 "iter"
12 "os"
13 "reflect"
14 "runtime"
15 "slices"
16 "strconv"
17 "strings"
18 "sync"
19 "sync/atomic"
20 "testing"
21 "time"
22 "weak"
23 )
24
25 func TestNow(t *testing.T) {
26 start := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC).In(time.Local)
27 synctest.Run(func() {
28
29 if got, want := time.Now(), start; !got.Equal(want) {
30 t.Errorf("at start: time.Now = %v, want %v", got, want)
31 }
32 go func() {
33
34 if got, want := time.Now(), start; !got.Equal(want) {
35 t.Errorf("time.Now = %v, want %v", got, want)
36 }
37 }()
38
39 time.Sleep(1 * time.Second)
40 if got, want := time.Now(), start.Add(1*time.Second); !got.Equal(want) {
41 t.Errorf("after sleep: time.Now = %v, want %v", got, want)
42 }
43 })
44 }
45
46
47
48 func TestMonotonicClock(t *testing.T) {
49 start := time.Now()
50 synctest.Run(func() {
51 time.Sleep(time.Until(start.Round(0)))
52 if got, want := time.Now().In(time.UTC), start.In(time.UTC); !got.Equal(want) {
53 t.Fatalf("time.Now() = %v, want %v", got, want)
54 }
55
56 wait := 1 * time.Second
57 time.Sleep(wait)
58 if got := time.Since(start); got != wait {
59 t.Fatalf("time.Since(start) = %v, want %v", got, wait)
60 }
61 if got := time.Now().Sub(start); got != wait {
62 t.Fatalf("time.Now().Sub(start) = %v, want %v", got, wait)
63 }
64 })
65 }
66
67 func TestRunEmpty(t *testing.T) {
68 synctest.Run(func() {
69 })
70 }
71
72 func TestSimpleWait(t *testing.T) {
73 synctest.Run(func() {
74 synctest.Wait()
75 })
76 }
77
78 func TestGoroutineWait(t *testing.T) {
79 synctest.Run(func() {
80 go func() {}()
81 synctest.Wait()
82 })
83 }
84
85
86
87 func TestWait(t *testing.T) {
88 synctest.Run(func() {
89 done := false
90 ch := make(chan int)
91 var f func()
92 f = func() {
93 count := <-ch
94 if count == 0 {
95 done = true
96 } else {
97 go f()
98 ch <- count - 1
99 }
100 }
101 go f()
102 ch <- 100
103 synctest.Wait()
104 if !done {
105 t.Fatalf("done = false, want true")
106 }
107 })
108 }
109
110 func TestMallocs(t *testing.T) {
111 for i := 0; i < 100; i++ {
112 synctest.Run(func() {
113 done := false
114 ch := make(chan []byte)
115 var f func()
116 f = func() {
117 b := <-ch
118 if len(b) == 0 {
119 done = true
120 } else {
121 go f()
122 ch <- make([]byte, len(b)-1)
123 }
124 }
125 go f()
126 ch <- make([]byte, 100)
127 synctest.Wait()
128 if !done {
129 t.Fatalf("done = false, want true")
130 }
131 })
132 }
133 }
134
135 func TestTimerReadBeforeDeadline(t *testing.T) {
136 synctest.Run(func() {
137 start := time.Now()
138 tm := time.NewTimer(5 * time.Second)
139 <-tm.C
140 if got, want := time.Since(start), 5*time.Second; got != want {
141 t.Errorf("after sleep: time.Since(start) = %v, want %v", got, want)
142 }
143 })
144 }
145
146 func TestTimerReadAfterDeadline(t *testing.T) {
147 synctest.Run(func() {
148 delay := 1 * time.Second
149 want := time.Now().Add(delay)
150 tm := time.NewTimer(delay)
151 time.Sleep(2 * delay)
152 got := <-tm.C
153 if got != want {
154 t.Errorf("<-tm.C = %v, want %v", got, want)
155 }
156 })
157 }
158
159 func TestTimerReset(t *testing.T) {
160 synctest.Run(func() {
161 start := time.Now()
162 tm := time.NewTimer(1 * time.Second)
163 if got, want := <-tm.C, start.Add(1*time.Second); got != want {
164 t.Errorf("first sleep: <-tm.C = %v, want %v", got, want)
165 }
166
167 tm.Reset(2 * time.Second)
168 if got, want := <-tm.C, start.Add((1+2)*time.Second); got != want {
169 t.Errorf("second sleep: <-tm.C = %v, want %v", got, want)
170 }
171
172 tm.Reset(3 * time.Second)
173 time.Sleep(1 * time.Second)
174 tm.Reset(3 * time.Second)
175 if got, want := <-tm.C, start.Add((1+2+4)*time.Second); got != want {
176 t.Errorf("third sleep: <-tm.C = %v, want %v", got, want)
177 }
178 })
179 }
180
181 func TestTimeAfter(t *testing.T) {
182 synctest.Run(func() {
183 i := 0
184 time.AfterFunc(1*time.Second, func() {
185
186 i++
187 go func() {
188 time.Sleep(1 * time.Second)
189 i++
190 }()
191 })
192 time.Sleep(3 * time.Second)
193 synctest.Wait()
194 if got, want := i, 2; got != want {
195 t.Errorf("after sleep and wait: i = %v, want %v", got, want)
196 }
197 })
198 }
199
200 func TestTimerAfterBubbleExit(t *testing.T) {
201 run := false
202 synctest.Run(func() {
203 time.AfterFunc(1*time.Second, func() {
204 run = true
205 })
206 })
207 if run {
208 t.Errorf("timer ran before bubble exit")
209 }
210 }
211
212 func TestTimerFromOutsideBubble(t *testing.T) {
213 tm := time.NewTimer(10 * time.Millisecond)
214 synctest.Run(func() {
215 <-tm.C
216 })
217 if tm.Stop() {
218 t.Errorf("synctest.Run unexpectedly returned before timer fired")
219 }
220 }
221
222
223
224 func TestTimerNondeterminism(t *testing.T) {
225 synctest.Run(func() {
226 const iterations = 1000
227 var seen1, seen2 bool
228 for range iterations {
229 tm1 := time.NewTimer(1)
230 tm2 := time.NewTimer(1)
231 select {
232 case <-tm1.C:
233 seen1 = true
234 case <-tm2.C:
235 seen2 = true
236 }
237 if seen1 && seen2 {
238 return
239 }
240 synctest.Wait()
241 }
242 t.Errorf("after %v iterations, seen timer1:%v, timer2:%v; want both", iterations, seen1, seen2)
243 })
244 }
245
246
247
248 func TestSleepNondeterminism(t *testing.T) {
249 synctest.Run(func() {
250 const iterations = 1000
251 var seen1, seen2 bool
252 for range iterations {
253 var first atomic.Int32
254 go func() {
255 time.Sleep(1)
256 first.CompareAndSwap(0, 1)
257 }()
258 go func() {
259 time.Sleep(1)
260 first.CompareAndSwap(0, 2)
261 }()
262 time.Sleep(1)
263 synctest.Wait()
264 switch v := first.Load(); v {
265 case 1:
266 seen1 = true
267 case 2:
268 seen2 = true
269 default:
270 t.Fatalf("first = %v, want 1 or 2", v)
271 }
272 if seen1 && seen2 {
273 return
274 }
275 synctest.Wait()
276 }
277 t.Errorf("after %v iterations, seen goroutine 1:%v, 2:%v; want both", iterations, seen1, seen2)
278 })
279 }
280
281
282
283 func TestTimerRunsImmediately(t *testing.T) {
284 synctest.Run(func() {
285 start := time.Now()
286 tm := time.NewTimer(0)
287 select {
288 case got := <-tm.C:
289 if !got.Equal(start) {
290 t.Errorf("<-tm.C = %v, want %v", got, start)
291 }
292 default:
293 t.Errorf("0-duration timer channel is not readable; want it to be")
294 }
295 })
296 }
297
298
299
300
301 func TestTimerRanInPast(t *testing.T) {
302 synctest.Run(func() {
303 delay := 1 * time.Second
304 want := time.Now().Add(delay)
305 tm := time.NewTimer(delay)
306 time.Sleep(2 * delay)
307 select {
308 case got := <-tm.C:
309 if !got.Equal(want) {
310 t.Errorf("<-tm.C = %v, want %v", got, want)
311 }
312 default:
313 t.Errorf("0-duration timer channel is not readable; want it to be")
314 }
315 })
316 }
317
318
319
320 func TestAfterFuncRunsImmediately(t *testing.T) {
321 synctest.Run(func() {
322 var b atomic.Bool
323 time.AfterFunc(0, func() {
324 b.Store(true)
325 })
326 for !b.Load() {
327 runtime.Gosched()
328 }
329 })
330 }
331
332 func TestChannelFromOutsideBubble(t *testing.T) {
333 choutside := make(chan struct{})
334 for _, test := range []struct {
335 desc string
336 outside func(ch chan int)
337 inside func(ch chan int)
338 }{{
339 desc: "read closed",
340 outside: func(ch chan int) { close(ch) },
341 inside: func(ch chan int) { <-ch },
342 }, {
343 desc: "read value",
344 outside: func(ch chan int) { ch <- 0 },
345 inside: func(ch chan int) { <-ch },
346 }, {
347 desc: "write value",
348 outside: func(ch chan int) { <-ch },
349 inside: func(ch chan int) { ch <- 0 },
350 }, {
351 desc: "select outside only",
352 outside: func(ch chan int) { close(ch) },
353 inside: func(ch chan int) {
354 select {
355 case <-ch:
356 case <-choutside:
357 }
358 },
359 }, {
360 desc: "select mixed",
361 outside: func(ch chan int) { close(ch) },
362 inside: func(ch chan int) {
363 ch2 := make(chan struct{})
364 select {
365 case <-ch:
366 case <-ch2:
367 }
368 },
369 }} {
370 t.Run(test.desc, func(t *testing.T) {
371 ch := make(chan int)
372 time.AfterFunc(1*time.Millisecond, func() {
373 test.outside(ch)
374 })
375 synctest.Run(func() {
376 test.inside(ch)
377 })
378 })
379 }
380 }
381
382 func TestChannelMovedOutOfBubble(t *testing.T) {
383 for _, test := range []struct {
384 desc string
385 f func(chan struct{})
386 wantPanic string
387 }{{
388 desc: "receive",
389 f: func(ch chan struct{}) {
390 <-ch
391 },
392 wantPanic: "receive on synctest channel from outside bubble",
393 }, {
394 desc: "send",
395 f: func(ch chan struct{}) {
396 ch <- struct{}{}
397 },
398 wantPanic: "send on synctest channel from outside bubble",
399 }, {
400 desc: "close",
401 f: func(ch chan struct{}) {
402 close(ch)
403 },
404 wantPanic: "close of synctest channel from outside bubble",
405 }} {
406 t.Run(test.desc, func(t *testing.T) {
407
408 t.Run("outside_bubble", func(t *testing.T) {
409 donec := make(chan struct{})
410 ch := make(chan chan struct{})
411 go func() {
412 defer close(donec)
413 defer wantPanic(t, test.wantPanic)
414 test.f(<-ch)
415 }()
416 synctest.Run(func() {
417 ch <- make(chan struct{})
418 })
419 <-donec
420 })
421
422 t.Run("different_bubble", func(t *testing.T) {
423 donec := make(chan struct{})
424 ch := make(chan chan struct{})
425 go func() {
426 defer close(donec)
427 c := <-ch
428 synctest.Run(func() {
429 defer wantPanic(t, test.wantPanic)
430 test.f(c)
431 })
432 }()
433 synctest.Run(func() {
434 ch <- make(chan struct{})
435 })
436 <-donec
437 })
438 })
439 }
440 }
441
442 func TestTimerFromInsideBubble(t *testing.T) {
443 for _, test := range []struct {
444 desc string
445 f func(tm *time.Timer)
446 wantPanic string
447 }{{
448 desc: "read channel",
449 f: func(tm *time.Timer) {
450 <-tm.C
451 },
452 wantPanic: "receive on synctest channel from outside bubble",
453 }, {
454 desc: "Reset",
455 f: func(tm *time.Timer) {
456 tm.Reset(1 * time.Second)
457 },
458 wantPanic: "reset of synctest timer from outside bubble",
459 }, {
460 desc: "Stop",
461 f: func(tm *time.Timer) {
462 tm.Stop()
463 },
464 wantPanic: "stop of synctest timer from outside bubble",
465 }} {
466 t.Run(test.desc, func(t *testing.T) {
467 donec := make(chan struct{})
468 ch := make(chan *time.Timer)
469 go func() {
470 defer close(donec)
471 defer wantPanic(t, test.wantPanic)
472 test.f(<-ch)
473 }()
474 synctest.Run(func() {
475 tm := time.NewTimer(1 * time.Second)
476 ch <- tm
477 })
478 <-donec
479 })
480 }
481 }
482
483 func TestDeadlockRoot(t *testing.T) {
484 defer wantPanic(t, "deadlock: all goroutines in bubble are blocked")
485 synctest.Run(func() {
486 select {}
487 })
488 }
489
490 func TestDeadlockChild(t *testing.T) {
491 defer wantPanic(t, "deadlock: main bubble goroutine has exited but blocked goroutines remain")
492 synctest.Run(func() {
493 go func() {
494 select {}
495 }()
496 })
497 }
498
499 func TestDeadlockTicker(t *testing.T) {
500 defer wantPanic(t, "deadlock: main bubble goroutine has exited but blocked goroutines remain")
501 synctest.Run(func() {
502 go func() {
503 for range time.Tick(1 * time.Second) {
504 t.Errorf("ticker unexpectedly ran")
505 return
506 }
507 }()
508 })
509 }
510
511 func TestCond(t *testing.T) {
512 synctest.Run(func() {
513 var mu sync.Mutex
514 cond := sync.NewCond(&mu)
515 start := time.Now()
516 const waitTime = 1 * time.Millisecond
517
518 go func() {
519
520 time.Sleep(waitTime)
521 mu.Lock()
522 cond.Signal()
523 mu.Unlock()
524
525
526 time.Sleep(waitTime)
527 mu.Lock()
528 cond.Broadcast()
529 mu.Unlock()
530 }()
531
532
533 mu.Lock()
534 cond.Wait()
535 mu.Unlock()
536 if got, want := time.Since(start), waitTime; got != want {
537 t.Errorf("after cond.Signal: time elapsed = %v, want %v", got, want)
538 }
539
540
541 waiterDone := false
542 go func() {
543 mu.Lock()
544 cond.Wait()
545 mu.Unlock()
546 waiterDone = true
547 }()
548 mu.Lock()
549 cond.Wait()
550 mu.Unlock()
551 synctest.Wait()
552 if !waiterDone {
553 t.Errorf("after cond.Broadcast: waiter not done")
554 }
555 if got, want := time.Since(start), 2*waitTime; got != want {
556 t.Errorf("after cond.Broadcast: time elapsed = %v, want %v", got, want)
557 }
558 })
559 }
560
561 func TestIteratorPush(t *testing.T) {
562 synctest.Run(func() {
563 seq := func(yield func(time.Time) bool) {
564 for yield(time.Now()) {
565 time.Sleep(1 * time.Second)
566 }
567 }
568 var got []time.Time
569 go func() {
570 for now := range seq {
571 got = append(got, now)
572 if len(got) >= 3 {
573 break
574 }
575 }
576 }()
577 want := []time.Time{
578 time.Now(),
579 time.Now().Add(1 * time.Second),
580 time.Now().Add(2 * time.Second),
581 }
582 time.Sleep(5 * time.Second)
583 synctest.Wait()
584 if !slices.Equal(got, want) {
585 t.Errorf("got: %v; want: %v", got, want)
586 }
587 })
588 }
589
590 func TestIteratorPull(t *testing.T) {
591 synctest.Run(func() {
592 seq := func(yield func(time.Time) bool) {
593 for yield(time.Now()) {
594 time.Sleep(1 * time.Second)
595 }
596 }
597 var got []time.Time
598 go func() {
599 next, stop := iter.Pull(seq)
600 defer stop()
601 for len(got) < 3 {
602 now, _ := next()
603 got = append(got, now)
604 }
605 }()
606 want := []time.Time{
607 time.Now(),
608 time.Now().Add(1 * time.Second),
609 time.Now().Add(2 * time.Second),
610 }
611 time.Sleep(5 * time.Second)
612 synctest.Wait()
613 if !slices.Equal(got, want) {
614 t.Errorf("got: %v; want: %v", got, want)
615 }
616 })
617 }
618
619 func TestReflectFuncOf(t *testing.T) {
620 mkfunc := func(name string, i int) {
621 reflect.FuncOf([]reflect.Type{
622 reflect.StructOf([]reflect.StructField{{
623 Name: name + strconv.Itoa(i),
624 Type: reflect.TypeOf(0),
625 }}),
626 }, nil, false)
627 }
628 go func() {
629 for i := 0; i < 100000; i++ {
630 mkfunc("A", i)
631 }
632 }()
633 synctest.Run(func() {
634 for i := 0; i < 100000; i++ {
635 mkfunc("A", i)
636 }
637 })
638 }
639
640 func TestWaitGroupInBubble(t *testing.T) {
641 synctest.Run(func() {
642 var wg sync.WaitGroup
643 wg.Add(1)
644 const delay = 1 * time.Second
645 go func() {
646 time.Sleep(delay)
647 wg.Done()
648 }()
649 start := time.Now()
650 wg.Wait()
651 if got := time.Since(start); got != delay {
652 t.Fatalf("WaitGroup.Wait() took %v, want %v", got, delay)
653 }
654 })
655 }
656
657 func TestWaitGroupOutOfBubble(t *testing.T) {
658 var wg sync.WaitGroup
659 wg.Add(1)
660 donec := make(chan struct{})
661 go synctest.Run(func() {
662
663
664 wg.Wait()
665 close(donec)
666 })
667 select {
668 case <-donec:
669 t.Fatalf("synctest.Run finished before WaitGroup.Done called")
670 case <-time.After(1 * time.Millisecond):
671 }
672 wg.Done()
673 <-donec
674 }
675
676 func TestWaitGroupMovedIntoBubble(t *testing.T) {
677 wantFatal(t, "fatal error: sync: WaitGroup.Add called from inside and outside synctest bubble", func() {
678 var wg sync.WaitGroup
679 wg.Add(1)
680 synctest.Run(func() {
681 wg.Add(1)
682 })
683 })
684 }
685
686 func TestWaitGroupMovedOutOfBubble(t *testing.T) {
687 wantFatal(t, "fatal error: sync: WaitGroup.Add called from inside and outside synctest bubble", func() {
688 var wg sync.WaitGroup
689 synctest.Run(func() {
690 wg.Add(1)
691 })
692 wg.Add(1)
693 })
694 }
695
696 func TestWaitGroupMovedBetweenBubblesWithNonZeroCount(t *testing.T) {
697 wantFatal(t, "fatal error: sync: WaitGroup.Add called from multiple synctest bubbles", func() {
698 var wg sync.WaitGroup
699 synctest.Run(func() {
700 wg.Add(1)
701 })
702 synctest.Run(func() {
703 wg.Add(1)
704 })
705 })
706 }
707
708 func TestWaitGroupMovedBetweenBubblesWithZeroCount(t *testing.T) {
709 var wg sync.WaitGroup
710 synctest.Run(func() {
711 wg.Add(1)
712 wg.Done()
713 })
714 synctest.Run(func() {
715
716 wg.Add(1)
717 wg.Done()
718 })
719 }
720
721 func TestWaitGroupMovedBetweenBubblesAfterWait(t *testing.T) {
722 var wg sync.WaitGroup
723 synctest.Run(func() {
724 wg.Go(func() {})
725 wg.Wait()
726 })
727 synctest.Run(func() {
728
729 wg.Go(func() {})
730 wg.Wait()
731 })
732 }
733
734 var testWaitGroupLinkerAllocatedWG sync.WaitGroup
735
736 func TestWaitGroupLinkerAllocated(t *testing.T) {
737 synctest.Run(func() {
738
739
740
741
742
743
744 testWaitGroupLinkerAllocatedWG.Go(func() {})
745 testWaitGroupLinkerAllocatedWG.Wait()
746 })
747 }
748
749 var testWaitGroupHeapAllocatedWG = new(sync.WaitGroup)
750
751 func TestWaitGroupHeapAllocated(t *testing.T) {
752 synctest.Run(func() {
753
754
755 testWaitGroupHeapAllocatedWG.Add(1)
756 go testWaitGroupHeapAllocatedWG.Wait()
757 synctest.Wait()
758 testWaitGroupHeapAllocatedWG.Done()
759 })
760 }
761
762 func TestHappensBefore(t *testing.T) {
763
764
765 var v1 int
766 var v2 int
767 synctest.Run(func() {
768 v1++
769 v2++
770
771
772 go func() {
773 v1++
774 }()
775 go func() {
776 v2++
777 }()
778 synctest.Wait()
779
780 v1++
781 v2++
782
783
784 ch1 := make(chan struct{})
785 go func() {
786 v1++
787 <-ch1
788 }()
789 go func() {
790 v2++
791 <-ch1
792 }()
793 synctest.Wait()
794
795 v1++
796 v2++
797 close(ch1)
798
799
800 time.AfterFunc(0, func() {
801 v1++
802 })
803 time.AfterFunc(0, func() {
804 v2++
805 })
806 synctest.Wait()
807
808 v1++
809 v2++
810
811
812 ch2 := make(chan struct{})
813 time.AfterFunc(0, func() {
814 v1++
815 <-ch2
816 })
817 time.AfterFunc(0, func() {
818 v2++
819 <-ch2
820 })
821 synctest.Wait()
822
823 v1++
824 v2++
825 close(ch2)
826 })
827
828 synctest.Run(func() {
829 go func() {
830 go func() {
831 v1++
832 }()
833 }()
834 go func() {
835 go func() {
836 v2++
837 }()
838 }()
839 })
840
841 if got, want := v1, 10; got != want {
842 t.Errorf("v1 = %v, want %v", got, want)
843 }
844 if got, want := v2, 10; got != want {
845 t.Errorf("v2 = %v, want %v", got, want)
846 }
847 }
848
849
850 func TestWeak(t *testing.T) {
851 synctest.Run(func() {
852 for range 5 {
853 runtime.GC()
854 b := make([]byte, 1024)
855 weak.Make(&b)
856 }
857 })
858 }
859
860 func wantPanic(t *testing.T, want string) {
861 if e := recover(); e != nil {
862 if got := fmt.Sprint(e); got != want {
863 t.Errorf("got panic message %q, want %q", got, want)
864 }
865 } else {
866 t.Errorf("got no panic, want one")
867 }
868 }
869
870 func wantFatal(t *testing.T, want string, f func()) {
871 t.Helper()
872
873 if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
874 f()
875 return
876 }
877
878 cmd := testenv.Command(t, testenv.Executable(t), "-test.run=^"+t.Name()+"$")
879 cmd = testenv.CleanCmdEnv(cmd)
880 cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1")
881 out, err := cmd.CombinedOutput()
882 if err == nil {
883 t.Errorf("expected test function to panic, but test returned successfully")
884 }
885 if !strings.Contains(string(out), want) {
886 t.Errorf("wanted test output contaiing %q; got %q", want, string(out))
887 }
888 }
889
View as plain text