Source file src/context/context_test.go

     1  // Copyright 2014 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 context
     6  
     7  import (
     8  	"fmt"
     9  	"math/rand"
    10  	"runtime"
    11  	"strings"
    12  	"sync"
    13  	"sync/atomic"
    14  	"time"
    15  )
    16  
    17  type testingT interface {
    18  	Deadline() (time.Time, bool)
    19  	Error(args ...any)
    20  	Errorf(format string, args ...any)
    21  	Fail()
    22  	FailNow()
    23  	Failed() bool
    24  	Fatal(args ...any)
    25  	Fatalf(format string, args ...any)
    26  	Helper()
    27  	Log(args ...any)
    28  	Logf(format string, args ...any)
    29  	Name() string
    30  	Parallel()
    31  	Skip(args ...any)
    32  	SkipNow()
    33  	Skipf(format string, args ...any)
    34  	Skipped() bool
    35  }
    36  
    37  // otherContext is a Context that's not one of the types defined in context.go.
    38  // This lets us test code paths that differ based on the underlying type of the
    39  // Context.
    40  type otherContext struct {
    41  	Context
    42  }
    43  
    44  const (
    45  	shortDuration    = 1 * time.Millisecond // a reasonable duration to block in a test
    46  	veryLongDuration = 1000 * time.Hour     // an arbitrary upper bound on the test's running time
    47  )
    48  
    49  // quiescent returns an arbitrary duration by which the program should have
    50  // completed any remaining work and reached a steady (idle) state.
    51  func quiescent(t testingT) time.Duration {
    52  	deadline, ok := t.Deadline()
    53  	if !ok {
    54  		return 5 * time.Second
    55  	}
    56  
    57  	const arbitraryCleanupMargin = 1 * time.Second
    58  	return time.Until(deadline) - arbitraryCleanupMargin
    59  }
    60  
    61  func XTestBackground(t testingT) {
    62  	c := Background()
    63  	if c == nil {
    64  		t.Fatalf("Background returned nil")
    65  	}
    66  	select {
    67  	case x := <-c.Done():
    68  		t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
    69  	default:
    70  	}
    71  	if got, want := fmt.Sprint(c), "context.Background"; got != want {
    72  		t.Errorf("Background().String() = %q want %q", got, want)
    73  	}
    74  }
    75  
    76  func XTestTODO(t testingT) {
    77  	c := TODO()
    78  	if c == nil {
    79  		t.Fatalf("TODO returned nil")
    80  	}
    81  	select {
    82  	case x := <-c.Done():
    83  		t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
    84  	default:
    85  	}
    86  	if got, want := fmt.Sprint(c), "context.TODO"; got != want {
    87  		t.Errorf("TODO().String() = %q want %q", got, want)
    88  	}
    89  }
    90  
    91  func XTestWithCancel(t testingT) {
    92  	c1, cancel := WithCancel(Background())
    93  
    94  	if got, want := fmt.Sprint(c1), "context.Background.WithCancel"; got != want {
    95  		t.Errorf("c1.String() = %q want %q", got, want)
    96  	}
    97  
    98  	o := otherContext{c1}
    99  	c2, _ := WithCancel(o)
   100  	contexts := []Context{c1, o, c2}
   101  
   102  	for i, c := range contexts {
   103  		if d := c.Done(); d == nil {
   104  			t.Errorf("c[%d].Done() == %v want non-nil", i, d)
   105  		}
   106  		if e := c.Err(); e != nil {
   107  			t.Errorf("c[%d].Err() == %v want nil", i, e)
   108  		}
   109  
   110  		select {
   111  		case x := <-c.Done():
   112  			t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
   113  		default:
   114  		}
   115  	}
   116  
   117  	cancel() // Should propagate synchronously.
   118  	for i, c := range contexts {
   119  		select {
   120  		case <-c.Done():
   121  		default:
   122  			t.Errorf("<-c[%d].Done() blocked, but shouldn't have", i)
   123  		}
   124  		if e := c.Err(); e != Canceled {
   125  			t.Errorf("c[%d].Err() == %v want %v", i, e, Canceled)
   126  		}
   127  	}
   128  }
   129  
   130  func contains(m map[canceler]struct{}, key canceler) bool {
   131  	_, ret := m[key]
   132  	return ret
   133  }
   134  
   135  func XTestParentFinishesChild(t testingT) {
   136  	// Context tree:
   137  	// parent -> cancelChild
   138  	// parent -> valueChild -> timerChild
   139  	parent, cancel := WithCancel(Background())
   140  	cancelChild, stop := WithCancel(parent)
   141  	defer stop()
   142  	valueChild := WithValue(parent, "key", "value")
   143  	timerChild, stop := WithTimeout(valueChild, veryLongDuration)
   144  	defer stop()
   145  
   146  	select {
   147  	case x := <-parent.Done():
   148  		t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
   149  	case x := <-cancelChild.Done():
   150  		t.Errorf("<-cancelChild.Done() == %v want nothing (it should block)", x)
   151  	case x := <-timerChild.Done():
   152  		t.Errorf("<-timerChild.Done() == %v want nothing (it should block)", x)
   153  	case x := <-valueChild.Done():
   154  		t.Errorf("<-valueChild.Done() == %v want nothing (it should block)", x)
   155  	default:
   156  	}
   157  
   158  	// The parent's children should contain the two cancelable children.
   159  	pc := parent.(*cancelCtx)
   160  	cc := cancelChild.(*cancelCtx)
   161  	tc := timerChild.(*timerCtx)
   162  	pc.mu.Lock()
   163  	if len(pc.children) != 2 || !contains(pc.children, cc) || !contains(pc.children, tc) {
   164  		t.Errorf("bad linkage: pc.children = %v, want %v and %v",
   165  			pc.children, cc, tc)
   166  	}
   167  	pc.mu.Unlock()
   168  
   169  	if p, ok := parentCancelCtx(cc.Context); !ok || p != pc {
   170  		t.Errorf("bad linkage: parentCancelCtx(cancelChild.Context) = %v, %v want %v, true", p, ok, pc)
   171  	}
   172  	if p, ok := parentCancelCtx(tc.Context); !ok || p != pc {
   173  		t.Errorf("bad linkage: parentCancelCtx(timerChild.Context) = %v, %v want %v, true", p, ok, pc)
   174  	}
   175  
   176  	cancel()
   177  
   178  	pc.mu.Lock()
   179  	if len(pc.children) != 0 {
   180  		t.Errorf("pc.cancel didn't clear pc.children = %v", pc.children)
   181  	}
   182  	pc.mu.Unlock()
   183  
   184  	// parent and children should all be finished.
   185  	check := func(ctx Context, name string) {
   186  		select {
   187  		case <-ctx.Done():
   188  		default:
   189  			t.Errorf("<-%s.Done() blocked, but shouldn't have", name)
   190  		}
   191  		if e := ctx.Err(); e != Canceled {
   192  			t.Errorf("%s.Err() == %v want %v", name, e, Canceled)
   193  		}
   194  	}
   195  	check(parent, "parent")
   196  	check(cancelChild, "cancelChild")
   197  	check(valueChild, "valueChild")
   198  	check(timerChild, "timerChild")
   199  
   200  	// WithCancel should return a canceled context on a canceled parent.
   201  	precanceledChild := WithValue(parent, "key", "value")
   202  	select {
   203  	case <-precanceledChild.Done():
   204  	default:
   205  		t.Errorf("<-precanceledChild.Done() blocked, but shouldn't have")
   206  	}
   207  	if e := precanceledChild.Err(); e != Canceled {
   208  		t.Errorf("precanceledChild.Err() == %v want %v", e, Canceled)
   209  	}
   210  }
   211  
   212  func XTestChildFinishesFirst(t testingT) {
   213  	cancelable, stop := WithCancel(Background())
   214  	defer stop()
   215  	for _, parent := range []Context{Background(), cancelable} {
   216  		child, cancel := WithCancel(parent)
   217  
   218  		select {
   219  		case x := <-parent.Done():
   220  			t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
   221  		case x := <-child.Done():
   222  			t.Errorf("<-child.Done() == %v want nothing (it should block)", x)
   223  		default:
   224  		}
   225  
   226  		cc := child.(*cancelCtx)
   227  		pc, pcok := parent.(*cancelCtx) // pcok == false when parent == Background()
   228  		if p, ok := parentCancelCtx(cc.Context); ok != pcok || (ok && pc != p) {
   229  			t.Errorf("bad linkage: parentCancelCtx(cc.Context) = %v, %v want %v, %v", p, ok, pc, pcok)
   230  		}
   231  
   232  		if pcok {
   233  			pc.mu.Lock()
   234  			if len(pc.children) != 1 || !contains(pc.children, cc) {
   235  				t.Errorf("bad linkage: pc.children = %v, cc = %v", pc.children, cc)
   236  			}
   237  			pc.mu.Unlock()
   238  		}
   239  
   240  		cancel()
   241  
   242  		if pcok {
   243  			pc.mu.Lock()
   244  			if len(pc.children) != 0 {
   245  				t.Errorf("child's cancel didn't remove self from pc.children = %v", pc.children)
   246  			}
   247  			pc.mu.Unlock()
   248  		}
   249  
   250  		// child should be finished.
   251  		select {
   252  		case <-child.Done():
   253  		default:
   254  			t.Errorf("<-child.Done() blocked, but shouldn't have")
   255  		}
   256  		if e := child.Err(); e != Canceled {
   257  			t.Errorf("child.Err() == %v want %v", e, Canceled)
   258  		}
   259  
   260  		// parent should not be finished.
   261  		select {
   262  		case x := <-parent.Done():
   263  			t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
   264  		default:
   265  		}
   266  		if e := parent.Err(); e != nil {
   267  			t.Errorf("parent.Err() == %v want nil", e)
   268  		}
   269  	}
   270  }
   271  
   272  func testDeadline(c Context, name string, t testingT) {
   273  	t.Helper()
   274  	d := quiescent(t)
   275  	timer := time.NewTimer(d)
   276  	defer timer.Stop()
   277  	select {
   278  	case <-timer.C:
   279  		t.Fatalf("%s: context not timed out after %v", name, d)
   280  	case <-c.Done():
   281  	}
   282  	if e := c.Err(); e != DeadlineExceeded {
   283  		t.Errorf("%s: c.Err() == %v; want %v", name, e, DeadlineExceeded)
   284  	}
   285  }
   286  
   287  func XTestDeadline(t testingT) {
   288  	t.Parallel()
   289  
   290  	c, _ := WithDeadline(Background(), time.Now().Add(shortDuration))
   291  	if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
   292  		t.Errorf("c.String() = %q want prefix %q", got, prefix)
   293  	}
   294  	testDeadline(c, "WithDeadline", t)
   295  
   296  	c, _ = WithDeadline(Background(), time.Now().Add(shortDuration))
   297  	o := otherContext{c}
   298  	testDeadline(o, "WithDeadline+otherContext", t)
   299  
   300  	c, _ = WithDeadline(Background(), time.Now().Add(shortDuration))
   301  	o = otherContext{c}
   302  	c, _ = WithDeadline(o, time.Now().Add(veryLongDuration))
   303  	testDeadline(c, "WithDeadline+otherContext+WithDeadline", t)
   304  
   305  	c, _ = WithDeadline(Background(), time.Now().Add(-shortDuration))
   306  	testDeadline(c, "WithDeadline+inthepast", t)
   307  
   308  	c, _ = WithDeadline(Background(), time.Now())
   309  	testDeadline(c, "WithDeadline+now", t)
   310  }
   311  
   312  func XTestTimeout(t testingT) {
   313  	t.Parallel()
   314  
   315  	c, _ := WithTimeout(Background(), shortDuration)
   316  	if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
   317  		t.Errorf("c.String() = %q want prefix %q", got, prefix)
   318  	}
   319  	testDeadline(c, "WithTimeout", t)
   320  
   321  	c, _ = WithTimeout(Background(), shortDuration)
   322  	o := otherContext{c}
   323  	testDeadline(o, "WithTimeout+otherContext", t)
   324  
   325  	c, _ = WithTimeout(Background(), shortDuration)
   326  	o = otherContext{c}
   327  	c, _ = WithTimeout(o, veryLongDuration)
   328  	testDeadline(c, "WithTimeout+otherContext+WithTimeout", t)
   329  }
   330  
   331  func XTestCanceledTimeout(t testingT) {
   332  	c, _ := WithTimeout(Background(), time.Second)
   333  	o := otherContext{c}
   334  	c, cancel := WithTimeout(o, veryLongDuration)
   335  	cancel() // Should propagate synchronously.
   336  	select {
   337  	case <-c.Done():
   338  	default:
   339  		t.Errorf("<-c.Done() blocked, but shouldn't have")
   340  	}
   341  	if e := c.Err(); e != Canceled {
   342  		t.Errorf("c.Err() == %v want %v", e, Canceled)
   343  	}
   344  }
   345  
   346  type key1 int
   347  type key2 int
   348  
   349  var k1 = key1(1)
   350  var k2 = key2(1) // same int as k1, different type
   351  var k3 = key2(3) // same type as k2, different int
   352  
   353  func XTestValues(t testingT) {
   354  	check := func(c Context, nm, v1, v2, v3 string) {
   355  		if v, ok := c.Value(k1).(string); ok == (len(v1) == 0) || v != v1 {
   356  			t.Errorf(`%s.Value(k1).(string) = %q, %t want %q, %t`, nm, v, ok, v1, len(v1) != 0)
   357  		}
   358  		if v, ok := c.Value(k2).(string); ok == (len(v2) == 0) || v != v2 {
   359  			t.Errorf(`%s.Value(k2).(string) = %q, %t want %q, %t`, nm, v, ok, v2, len(v2) != 0)
   360  		}
   361  		if v, ok := c.Value(k3).(string); ok == (len(v3) == 0) || v != v3 {
   362  			t.Errorf(`%s.Value(k3).(string) = %q, %t want %q, %t`, nm, v, ok, v3, len(v3) != 0)
   363  		}
   364  	}
   365  
   366  	c0 := Background()
   367  	check(c0, "c0", "", "", "")
   368  
   369  	c1 := WithValue(Background(), k1, "c1k1")
   370  	check(c1, "c1", "c1k1", "", "")
   371  
   372  	if got, want := fmt.Sprint(c1), `context.Background.WithValue(type context.key1, val c1k1)`; got != want {
   373  		t.Errorf("c.String() = %q want %q", got, want)
   374  	}
   375  
   376  	c2 := WithValue(c1, k2, "c2k2")
   377  	check(c2, "c2", "c1k1", "c2k2", "")
   378  
   379  	c3 := WithValue(c2, k3, "c3k3")
   380  	check(c3, "c2", "c1k1", "c2k2", "c3k3")
   381  
   382  	c4 := WithValue(c3, k1, nil)
   383  	check(c4, "c4", "", "c2k2", "c3k3")
   384  
   385  	o0 := otherContext{Background()}
   386  	check(o0, "o0", "", "", "")
   387  
   388  	o1 := otherContext{WithValue(Background(), k1, "c1k1")}
   389  	check(o1, "o1", "c1k1", "", "")
   390  
   391  	o2 := WithValue(o1, k2, "o2k2")
   392  	check(o2, "o2", "c1k1", "o2k2", "")
   393  
   394  	o3 := otherContext{c4}
   395  	check(o3, "o3", "", "c2k2", "c3k3")
   396  
   397  	o4 := WithValue(o3, k3, nil)
   398  	check(o4, "o4", "", "c2k2", "")
   399  }
   400  
   401  func XTestAllocs(t testingT, testingShort func() bool, testingAllocsPerRun func(int, func()) float64) {
   402  	bg := Background()
   403  	for _, test := range []struct {
   404  		desc       string
   405  		f          func()
   406  		limit      float64
   407  		gccgoLimit float64
   408  	}{
   409  		{
   410  			desc:       "Background()",
   411  			f:          func() { Background() },
   412  			limit:      0,
   413  			gccgoLimit: 0,
   414  		},
   415  		{
   416  			desc: fmt.Sprintf("WithValue(bg, %v, nil)", k1),
   417  			f: func() {
   418  				c := WithValue(bg, k1, nil)
   419  				c.Value(k1)
   420  			},
   421  			limit:      3,
   422  			gccgoLimit: 3,
   423  		},
   424  		{
   425  			desc: "WithTimeout(bg, 1*time.Nanosecond)",
   426  			f: func() {
   427  				c, _ := WithTimeout(bg, 1*time.Nanosecond)
   428  				<-c.Done()
   429  			},
   430  			limit:      12,
   431  			gccgoLimit: 15,
   432  		},
   433  		{
   434  			desc: "WithCancel(bg)",
   435  			f: func() {
   436  				c, cancel := WithCancel(bg)
   437  				cancel()
   438  				<-c.Done()
   439  			},
   440  			limit:      5,
   441  			gccgoLimit: 8,
   442  		},
   443  		{
   444  			desc: "WithTimeout(bg, 5*time.Millisecond)",
   445  			f: func() {
   446  				c, cancel := WithTimeout(bg, 5*time.Millisecond)
   447  				cancel()
   448  				<-c.Done()
   449  			},
   450  			limit:      8,
   451  			gccgoLimit: 25,
   452  		},
   453  	} {
   454  		limit := test.limit
   455  		if runtime.Compiler == "gccgo" {
   456  			// gccgo does not yet do escape analysis.
   457  			// TODO(iant): Remove this when gccgo does do escape analysis.
   458  			limit = test.gccgoLimit
   459  		}
   460  		numRuns := 100
   461  		if testingShort() {
   462  			numRuns = 10
   463  		}
   464  		if n := testingAllocsPerRun(numRuns, test.f); n > limit {
   465  			t.Errorf("%s allocs = %f want %d", test.desc, n, int(limit))
   466  		}
   467  	}
   468  }
   469  
   470  func XTestSimultaneousCancels(t testingT) {
   471  	root, cancel := WithCancel(Background())
   472  	m := map[Context]CancelFunc{root: cancel}
   473  	q := []Context{root}
   474  	// Create a tree of contexts.
   475  	for len(q) != 0 && len(m) < 100 {
   476  		parent := q[0]
   477  		q = q[1:]
   478  		for i := 0; i < 4; i++ {
   479  			ctx, cancel := WithCancel(parent)
   480  			m[ctx] = cancel
   481  			q = append(q, ctx)
   482  		}
   483  	}
   484  	// Start all the cancels in a random order.
   485  	var wg sync.WaitGroup
   486  	wg.Add(len(m))
   487  	for _, cancel := range m {
   488  		go func(cancel CancelFunc) {
   489  			cancel()
   490  			wg.Done()
   491  		}(cancel)
   492  	}
   493  
   494  	d := quiescent(t)
   495  	stuck := make(chan struct{})
   496  	timer := time.AfterFunc(d, func() { close(stuck) })
   497  	defer timer.Stop()
   498  
   499  	// Wait on all the contexts in a random order.
   500  	for ctx := range m {
   501  		select {
   502  		case <-ctx.Done():
   503  		case <-stuck:
   504  			buf := make([]byte, 10<<10)
   505  			n := runtime.Stack(buf, true)
   506  			t.Fatalf("timed out after %v waiting for <-ctx.Done(); stacks:\n%s", d, buf[:n])
   507  		}
   508  	}
   509  	// Wait for all the cancel functions to return.
   510  	done := make(chan struct{})
   511  	go func() {
   512  		wg.Wait()
   513  		close(done)
   514  	}()
   515  	select {
   516  	case <-done:
   517  	case <-stuck:
   518  		buf := make([]byte, 10<<10)
   519  		n := runtime.Stack(buf, true)
   520  		t.Fatalf("timed out after %v waiting for cancel functions; stacks:\n%s", d, buf[:n])
   521  	}
   522  }
   523  
   524  func XTestInterlockedCancels(t testingT) {
   525  	parent, cancelParent := WithCancel(Background())
   526  	child, cancelChild := WithCancel(parent)
   527  	go func() {
   528  		<-parent.Done()
   529  		cancelChild()
   530  	}()
   531  	cancelParent()
   532  	d := quiescent(t)
   533  	timer := time.NewTimer(d)
   534  	defer timer.Stop()
   535  	select {
   536  	case <-child.Done():
   537  	case <-timer.C:
   538  		buf := make([]byte, 10<<10)
   539  		n := runtime.Stack(buf, true)
   540  		t.Fatalf("timed out after %v waiting for child.Done(); stacks:\n%s", d, buf[:n])
   541  	}
   542  }
   543  
   544  func XTestLayersCancel(t testingT) {
   545  	testLayers(t, time.Now().UnixNano(), false)
   546  }
   547  
   548  func XTestLayersTimeout(t testingT) {
   549  	testLayers(t, time.Now().UnixNano(), true)
   550  }
   551  
   552  func testLayers(t testingT, seed int64, testTimeout bool) {
   553  	t.Parallel()
   554  
   555  	r := rand.New(rand.NewSource(seed))
   556  	errorf := func(format string, a ...any) {
   557  		t.Errorf(fmt.Sprintf("seed=%d: %s", seed, format), a...)
   558  	}
   559  	const (
   560  		minLayers = 30
   561  	)
   562  	type value int
   563  	var (
   564  		vals      []*value
   565  		cancels   []CancelFunc
   566  		numTimers int
   567  		ctx       = Background()
   568  	)
   569  	for i := 0; i < minLayers || numTimers == 0 || len(cancels) == 0 || len(vals) == 0; i++ {
   570  		switch r.Intn(3) {
   571  		case 0:
   572  			v := new(value)
   573  			ctx = WithValue(ctx, v, v)
   574  			vals = append(vals, v)
   575  		case 1:
   576  			var cancel CancelFunc
   577  			ctx, cancel = WithCancel(ctx)
   578  			cancels = append(cancels, cancel)
   579  		case 2:
   580  			var cancel CancelFunc
   581  			d := veryLongDuration
   582  			if testTimeout {
   583  				d = shortDuration
   584  			}
   585  			ctx, cancel = WithTimeout(ctx, d)
   586  			cancels = append(cancels, cancel)
   587  			numTimers++
   588  		}
   589  	}
   590  	checkValues := func(when string) {
   591  		for _, key := range vals {
   592  			if val := ctx.Value(key).(*value); key != val {
   593  				errorf("%s: ctx.Value(%p) = %p want %p", when, key, val, key)
   594  			}
   595  		}
   596  	}
   597  	if !testTimeout {
   598  		select {
   599  		case <-ctx.Done():
   600  			errorf("ctx should not be canceled yet")
   601  		default:
   602  		}
   603  	}
   604  	if s, prefix := fmt.Sprint(ctx), "context.Background."; !strings.HasPrefix(s, prefix) {
   605  		t.Errorf("ctx.String() = %q want prefix %q", s, prefix)
   606  	}
   607  	t.Log(ctx)
   608  	checkValues("before cancel")
   609  	if testTimeout {
   610  		d := quiescent(t)
   611  		timer := time.NewTimer(d)
   612  		defer timer.Stop()
   613  		select {
   614  		case <-ctx.Done():
   615  		case <-timer.C:
   616  			errorf("ctx should have timed out after %v", d)
   617  		}
   618  		checkValues("after timeout")
   619  	} else {
   620  		cancel := cancels[r.Intn(len(cancels))]
   621  		cancel()
   622  		select {
   623  		case <-ctx.Done():
   624  		default:
   625  			errorf("ctx should be canceled")
   626  		}
   627  		checkValues("after cancel")
   628  	}
   629  }
   630  
   631  func XTestCancelRemoves(t testingT) {
   632  	checkChildren := func(when string, ctx Context, want int) {
   633  		if got := len(ctx.(*cancelCtx).children); got != want {
   634  			t.Errorf("%s: context has %d children, want %d", when, got, want)
   635  		}
   636  	}
   637  
   638  	ctx, _ := WithCancel(Background())
   639  	checkChildren("after creation", ctx, 0)
   640  	_, cancel := WithCancel(ctx)
   641  	checkChildren("with WithCancel child ", ctx, 1)
   642  	cancel()
   643  	checkChildren("after canceling WithCancel child", ctx, 0)
   644  
   645  	ctx, _ = WithCancel(Background())
   646  	checkChildren("after creation", ctx, 0)
   647  	_, cancel = WithTimeout(ctx, 60*time.Minute)
   648  	checkChildren("with WithTimeout child ", ctx, 1)
   649  	cancel()
   650  	checkChildren("after canceling WithTimeout child", ctx, 0)
   651  }
   652  
   653  func XTestWithCancelCanceledParent(t testingT) {
   654  	parent, pcancel := WithCancel(Background())
   655  	pcancel()
   656  
   657  	c, _ := WithCancel(parent)
   658  	select {
   659  	case <-c.Done():
   660  	default:
   661  		t.Errorf("child not done immediately upon construction")
   662  	}
   663  	if got, want := c.Err(), Canceled; got != want {
   664  		t.Errorf("child not canceled; got = %v, want = %v", got, want)
   665  	}
   666  }
   667  
   668  func XTestWithValueChecksKey(t testingT) {
   669  	panicVal := recoveredValue(func() { WithValue(Background(), []byte("foo"), "bar") })
   670  	if panicVal == nil {
   671  		t.Error("expected panic")
   672  	}
   673  	panicVal = recoveredValue(func() { WithValue(Background(), nil, "bar") })
   674  	if got, want := fmt.Sprint(panicVal), "nil key"; got != want {
   675  		t.Errorf("panic = %q; want %q", got, want)
   676  	}
   677  }
   678  
   679  func XTestInvalidDerivedFail(t testingT) {
   680  	panicVal := recoveredValue(func() { WithCancel(nil) })
   681  	if panicVal == nil {
   682  		t.Error("expected panic")
   683  	}
   684  	panicVal = recoveredValue(func() { WithDeadline(nil, time.Now().Add(shortDuration)) })
   685  	if panicVal == nil {
   686  		t.Error("expected panic")
   687  	}
   688  	panicVal = recoveredValue(func() { WithValue(nil, "foo", "bar") })
   689  	if panicVal == nil {
   690  		t.Error("expected panic")
   691  	}
   692  }
   693  
   694  func recoveredValue(fn func()) (v any) {
   695  	defer func() { v = recover() }()
   696  	fn()
   697  	return
   698  }
   699  
   700  func XTestDeadlineExceededSupportsTimeout(t testingT) {
   701  	i, ok := DeadlineExceeded.(interface {
   702  		Timeout() bool
   703  	})
   704  	if !ok {
   705  		t.Fatal("DeadlineExceeded does not support Timeout interface")
   706  	}
   707  	if !i.Timeout() {
   708  		t.Fatal("wrong value for timeout")
   709  	}
   710  }
   711  
   712  type myCtx struct {
   713  	Context
   714  }
   715  
   716  type myDoneCtx struct {
   717  	Context
   718  }
   719  
   720  func (d *myDoneCtx) Done() <-chan struct{} {
   721  	c := make(chan struct{})
   722  	return c
   723  }
   724  
   725  func XTestCustomContextGoroutines(t testingT) {
   726  	g := atomic.LoadInt32(&goroutines)
   727  	checkNoGoroutine := func() {
   728  		t.Helper()
   729  		now := atomic.LoadInt32(&goroutines)
   730  		if now != g {
   731  			t.Fatalf("%d goroutines created", now-g)
   732  		}
   733  	}
   734  	checkCreatedGoroutine := func() {
   735  		t.Helper()
   736  		now := atomic.LoadInt32(&goroutines)
   737  		if now != g+1 {
   738  			t.Fatalf("%d goroutines created, want 1", now-g)
   739  		}
   740  		g = now
   741  	}
   742  
   743  	_, cancel0 := WithCancel(&myDoneCtx{Background()})
   744  	cancel0()
   745  	checkCreatedGoroutine()
   746  
   747  	_, cancel0 = WithTimeout(&myDoneCtx{Background()}, veryLongDuration)
   748  	cancel0()
   749  	checkCreatedGoroutine()
   750  
   751  	checkNoGoroutine()
   752  	defer checkNoGoroutine()
   753  
   754  	ctx1, cancel1 := WithCancel(Background())
   755  	defer cancel1()
   756  	checkNoGoroutine()
   757  
   758  	ctx2 := &myCtx{ctx1}
   759  	ctx3, cancel3 := WithCancel(ctx2)
   760  	defer cancel3()
   761  	checkNoGoroutine()
   762  
   763  	_, cancel3b := WithCancel(&myDoneCtx{ctx2})
   764  	defer cancel3b()
   765  	checkCreatedGoroutine() // ctx1 is not providing Done, must not be used
   766  
   767  	ctx4, cancel4 := WithTimeout(ctx3, veryLongDuration)
   768  	defer cancel4()
   769  	checkNoGoroutine()
   770  
   771  	ctx5, cancel5 := WithCancel(ctx4)
   772  	defer cancel5()
   773  	checkNoGoroutine()
   774  
   775  	cancel5()
   776  	checkNoGoroutine()
   777  
   778  	_, cancel6 := WithTimeout(ctx5, veryLongDuration)
   779  	defer cancel6()
   780  	checkNoGoroutine()
   781  
   782  	// Check applied to canceled context.
   783  	cancel6()
   784  	cancel1()
   785  	_, cancel7 := WithCancel(ctx5)
   786  	defer cancel7()
   787  	checkNoGoroutine()
   788  }
   789  

View as plain text