Source file src/internal/synctest/synctest_test.go

     1  // Copyright 2024 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     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  		// Time starts at 2000-1-1 00:00:00.
    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  			// New goroutines see the same fake clock.
    34  			if got, want := time.Now(), start; !got.Equal(want) {
    35  				t.Errorf("time.Now = %v, want %v", got, want)
    36  			}
    37  		}()
    38  		// Time advances after a sleep.
    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  // TestMonotonicClock exercises comparing times from within a bubble
    47  // with ones from outside the bubble.
    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  // TestWait starts a collection of goroutines.
    86  // It checks that synctest.Wait waits for all goroutines to exit before returning.
    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  			// Ensure synctest group membership propagates through the AfterFunc.
   186  			i++ // 1
   187  			go func() {
   188  				time.Sleep(1 * time.Second)
   189  				i++ // 2
   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  // TestTimerNondeterminism verifies that timers firing at the same instant
   223  // don't always fire in exactly the same order.
   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  // TestSleepNondeterminism verifies that goroutines sleeping to the same instant
   247  // don't always schedule in exactly the same order.
   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  // TestTimerRunsImmediately verifies that a 0-duration timer sends on its channel
   282  // without waiting for the bubble to block.
   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  // TestTimerRunsLater verifies that reading from a timer's channel receives the
   299  // timer fired, even when that time is in reading from a timer's channel receives the
   300  // time the timer fired, even when that time is in the past.
   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  // TestAfterFuncRunsImmediately verifies that a 0-duration AfterFunc is scheduled
   319  // without waiting for the bubble to block.
   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  			// Bubbled channel accessed from outside any bubble.
   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  			// Bubbled channel accessed from a different bubble.
   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  			// Signal the cond.
   520  			time.Sleep(waitTime)
   521  			mu.Lock()
   522  			cond.Signal()
   523  			mu.Unlock()
   524  
   525  			// Broadcast to the cond.
   526  			time.Sleep(waitTime)
   527  			mu.Lock()
   528  			cond.Broadcast()
   529  			mu.Unlock()
   530  		}()
   531  
   532  		// Wait for cond.Signal.
   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  		// Wait for cond.Broadcast in two goroutines.
   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  		// Since wg.Add was called outside the bubble, Wait is not durably blocking
   663  		// and this waits until wg.Done is called below.
   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  		// Reusing the WaitGroup is safe, because its count is zero.
   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  		// Reusing the WaitGroup is safe, because its count is zero.
   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  		// This WaitGroup is probably linker-allocated and has no span,
   739  		// so we won't be able to add a special to it associating it with
   740  		// this bubble.
   741  		//
   742  		// Operations on it may not be durably blocking,
   743  		// but they shouldn't fail.
   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  		// This package-scoped WaitGroup var should have been heap-allocated,
   754  		// so we can associate it with a bubble.
   755  		testWaitGroupHeapAllocatedWG.Add(1)
   756  		go testWaitGroupHeapAllocatedWG.Wait()
   757  		synctest.Wait()
   758  		testWaitGroupHeapAllocatedWG.Done()
   759  	})
   760  }
   761  
   762  func TestHappensBefore(t *testing.T) {
   763  	// Use two parallel goroutines accessing different vars to ensure that
   764  	// we correctly account for multiple goroutines in the bubble.
   765  	var v1 int
   766  	var v2 int
   767  	synctest.Run(func() {
   768  		v1++ // 1
   769  		v2++ // 1
   770  
   771  		// Wait returns after these goroutines exit.
   772  		go func() {
   773  			v1++ // 2
   774  		}()
   775  		go func() {
   776  			v2++ // 2
   777  		}()
   778  		synctest.Wait()
   779  
   780  		v1++ // 3
   781  		v2++ // 3
   782  
   783  		// Wait returns after these goroutines block.
   784  		ch1 := make(chan struct{})
   785  		go func() {
   786  			v1++ // 4
   787  			<-ch1
   788  		}()
   789  		go func() {
   790  			v2++ // 4
   791  			<-ch1
   792  		}()
   793  		synctest.Wait()
   794  
   795  		v1++ // 5
   796  		v2++ // 5
   797  		close(ch1)
   798  
   799  		// Wait returns after these timers run.
   800  		time.AfterFunc(0, func() {
   801  			v1++ // 6
   802  		})
   803  		time.AfterFunc(0, func() {
   804  			v2++ // 6
   805  		})
   806  		synctest.Wait()
   807  
   808  		v1++ // 7
   809  		v2++ // 7
   810  
   811  		// Wait returns after these timer goroutines block.
   812  		ch2 := make(chan struct{})
   813  		time.AfterFunc(0, func() {
   814  			v1++ // 8
   815  			<-ch2
   816  		})
   817  		time.AfterFunc(0, func() {
   818  			v2++ // 8
   819  			<-ch2
   820  		})
   821  		synctest.Wait()
   822  
   823  		v1++ // 9
   824  		v2++ // 9
   825  		close(ch2)
   826  	})
   827  	// This Run happens after the previous Run returns.
   828  	synctest.Run(func() {
   829  		go func() {
   830  			go func() {
   831  				v1++ // 10
   832  			}()
   833  		}()
   834  		go func() {
   835  			go func() {
   836  				v2++ // 10
   837  			}()
   838  		}()
   839  	})
   840  	// These tests happen after Run returns.
   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  // https://go.dev/issue/73817
   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