Source file src/context/context_test.go

Documentation: context

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

View as plain text