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  // Tests in package context cannot depend directly on package testing due to an import cycle.
     8  // If your test does requires access to unexported members of the context package,
     9  // add your test below as `func XTestFoo(t testingT)` and add a `TestFoo` to x_test.go
    10  // that calls it. Otherwise, write a regular test in a test.go file in package context_test.
    11  
    12  import (
    13  	"time"
    14  )
    15  
    16  type testingT interface {
    17  	Deadline() (time.Time, bool)
    18  	Error(args ...any)
    19  	Errorf(format string, args ...any)
    20  	Fail()
    21  	FailNow()
    22  	Failed() bool
    23  	Fatal(args ...any)
    24  	Fatalf(format string, args ...any)
    25  	Helper()
    26  	Log(args ...any)
    27  	Logf(format string, args ...any)
    28  	Name() string
    29  	Parallel()
    30  	Skip(args ...any)
    31  	SkipNow()
    32  	Skipf(format string, args ...any)
    33  	Skipped() bool
    34  }
    35  
    36  const veryLongDuration = 1000 * time.Hour // an arbitrary upper bound on the test's running time
    37  
    38  func contains(m map[canceler]struct{}, key canceler) bool {
    39  	_, ret := m[key]
    40  	return ret
    41  }
    42  
    43  func XTestParentFinishesChild(t testingT) {
    44  	// Context tree:
    45  	// parent -> cancelChild
    46  	// parent -> valueChild -> timerChild
    47  	// parent -> afterChild
    48  	parent, cancel := WithCancel(Background())
    49  	cancelChild, stop := WithCancel(parent)
    50  	defer stop()
    51  	valueChild := WithValue(parent, "key", "value")
    52  	timerChild, stop := WithTimeout(valueChild, veryLongDuration)
    53  	defer stop()
    54  	afterStop := AfterFunc(parent, func() {})
    55  	defer afterStop()
    56  
    57  	select {
    58  	case x := <-parent.Done():
    59  		t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
    60  	case x := <-cancelChild.Done():
    61  		t.Errorf("<-cancelChild.Done() == %v want nothing (it should block)", x)
    62  	case x := <-timerChild.Done():
    63  		t.Errorf("<-timerChild.Done() == %v want nothing (it should block)", x)
    64  	case x := <-valueChild.Done():
    65  		t.Errorf("<-valueChild.Done() == %v want nothing (it should block)", x)
    66  	default:
    67  	}
    68  
    69  	// The parent's children should contain the three cancelable children.
    70  	pc := parent.(*cancelCtx)
    71  	cc := cancelChild.(*cancelCtx)
    72  	tc := timerChild.(*timerCtx)
    73  	pc.mu.Lock()
    74  	var ac *afterFuncCtx
    75  	for c := range pc.children {
    76  		if a, ok := c.(*afterFuncCtx); ok {
    77  			ac = a
    78  			break
    79  		}
    80  	}
    81  	if len(pc.children) != 3 || !contains(pc.children, cc) || !contains(pc.children, tc) || ac == nil {
    82  		t.Errorf("bad linkage: pc.children = %v, want %v, %v, and an afterFunc",
    83  			pc.children, cc, tc)
    84  	}
    85  	pc.mu.Unlock()
    86  
    87  	if p, ok := parentCancelCtx(cc.Context); !ok || p != pc {
    88  		t.Errorf("bad linkage: parentCancelCtx(cancelChild.Context) = %v, %v want %v, true", p, ok, pc)
    89  	}
    90  	if p, ok := parentCancelCtx(tc.Context); !ok || p != pc {
    91  		t.Errorf("bad linkage: parentCancelCtx(timerChild.Context) = %v, %v want %v, true", p, ok, pc)
    92  	}
    93  	if p, ok := parentCancelCtx(ac.Context); !ok || p != pc {
    94  		t.Errorf("bad linkage: parentCancelCtx(afterChild.Context) = %v, %v want %v, true", p, ok, pc)
    95  	}
    96  
    97  	cancel()
    98  
    99  	pc.mu.Lock()
   100  	if len(pc.children) != 0 {
   101  		t.Errorf("pc.cancel didn't clear pc.children = %v", pc.children)
   102  	}
   103  	pc.mu.Unlock()
   104  
   105  	// parent and children should all be finished.
   106  	check := func(ctx Context, name string) {
   107  		select {
   108  		case <-ctx.Done():
   109  		default:
   110  			t.Errorf("<-%s.Done() blocked, but shouldn't have", name)
   111  		}
   112  		if e := ctx.Err(); e != Canceled {
   113  			t.Errorf("%s.Err() == %v want %v", name, e, Canceled)
   114  		}
   115  	}
   116  	check(parent, "parent")
   117  	check(cancelChild, "cancelChild")
   118  	check(valueChild, "valueChild")
   119  	check(timerChild, "timerChild")
   120  
   121  	// WithCancel should return a canceled context on a canceled parent.
   122  	precanceledChild := WithValue(parent, "key", "value")
   123  	select {
   124  	case <-precanceledChild.Done():
   125  	default:
   126  		t.Errorf("<-precanceledChild.Done() blocked, but shouldn't have")
   127  	}
   128  	if e := precanceledChild.Err(); e != Canceled {
   129  		t.Errorf("precanceledChild.Err() == %v want %v", e, Canceled)
   130  	}
   131  }
   132  
   133  func XTestChildFinishesFirst(t testingT) {
   134  	cancelable, stop := WithCancel(Background())
   135  	defer stop()
   136  	for _, parent := range []Context{Background(), cancelable} {
   137  		child, cancel := WithCancel(parent)
   138  
   139  		select {
   140  		case x := <-parent.Done():
   141  			t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
   142  		case x := <-child.Done():
   143  			t.Errorf("<-child.Done() == %v want nothing (it should block)", x)
   144  		default:
   145  		}
   146  
   147  		cc := child.(*cancelCtx)
   148  		pc, pcok := parent.(*cancelCtx) // pcok == false when parent == Background()
   149  		if p, ok := parentCancelCtx(cc.Context); ok != pcok || (ok && pc != p) {
   150  			t.Errorf("bad linkage: parentCancelCtx(cc.Context) = %v, %v want %v, %v", p, ok, pc, pcok)
   151  		}
   152  
   153  		if pcok {
   154  			pc.mu.Lock()
   155  			if len(pc.children) != 1 || !contains(pc.children, cc) {
   156  				t.Errorf("bad linkage: pc.children = %v, cc = %v", pc.children, cc)
   157  			}
   158  			pc.mu.Unlock()
   159  		}
   160  
   161  		cancel()
   162  
   163  		if pcok {
   164  			pc.mu.Lock()
   165  			if len(pc.children) != 0 {
   166  				t.Errorf("child's cancel didn't remove self from pc.children = %v", pc.children)
   167  			}
   168  			pc.mu.Unlock()
   169  		}
   170  
   171  		// child should be finished.
   172  		select {
   173  		case <-child.Done():
   174  		default:
   175  			t.Errorf("<-child.Done() blocked, but shouldn't have")
   176  		}
   177  		if e := child.Err(); e != Canceled {
   178  			t.Errorf("child.Err() == %v want %v", e, Canceled)
   179  		}
   180  
   181  		// parent should not be finished.
   182  		select {
   183  		case x := <-parent.Done():
   184  			t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
   185  		default:
   186  		}
   187  		if e := parent.Err(); e != nil {
   188  			t.Errorf("parent.Err() == %v want nil", e)
   189  		}
   190  	}
   191  }
   192  
   193  func XTestCancelRemoves(t testingT) {
   194  	checkChildren := func(when string, ctx Context, want int) {
   195  		if got := len(ctx.(*cancelCtx).children); got != want {
   196  			t.Errorf("%s: context has %d children, want %d", when, got, want)
   197  		}
   198  	}
   199  
   200  	ctx, _ := WithCancel(Background())
   201  	checkChildren("after creation", ctx, 0)
   202  	_, cancel := WithCancel(ctx)
   203  	checkChildren("with WithCancel child ", ctx, 1)
   204  	cancel()
   205  	checkChildren("after canceling WithCancel child", ctx, 0)
   206  
   207  	ctx, _ = WithCancel(Background())
   208  	checkChildren("after creation", ctx, 0)
   209  	_, cancel = WithTimeout(ctx, 60*time.Minute)
   210  	checkChildren("with WithTimeout child ", ctx, 1)
   211  	cancel()
   212  	checkChildren("after canceling WithTimeout child", ctx, 0)
   213  
   214  	ctx, _ = WithCancel(Background())
   215  	checkChildren("after creation", ctx, 0)
   216  	stop := AfterFunc(ctx, func() {})
   217  	checkChildren("with AfterFunc child ", ctx, 1)
   218  	stop()
   219  	checkChildren("after stopping AfterFunc child ", ctx, 0)
   220  }
   221  
   222  type myCtx struct {
   223  	Context
   224  }
   225  
   226  type myDoneCtx struct {
   227  	Context
   228  }
   229  
   230  func (d *myDoneCtx) Done() <-chan struct{} {
   231  	c := make(chan struct{})
   232  	return c
   233  }
   234  func XTestCustomContextGoroutines(t testingT) {
   235  	g := goroutines.Load()
   236  	checkNoGoroutine := func() {
   237  		t.Helper()
   238  		now := goroutines.Load()
   239  		if now != g {
   240  			t.Fatalf("%d goroutines created", now-g)
   241  		}
   242  	}
   243  	checkCreatedGoroutine := func() {
   244  		t.Helper()
   245  		now := goroutines.Load()
   246  		if now != g+1 {
   247  			t.Fatalf("%d goroutines created, want 1", now-g)
   248  		}
   249  		g = now
   250  	}
   251  
   252  	_, cancel0 := WithCancel(&myDoneCtx{Background()})
   253  	cancel0()
   254  	checkCreatedGoroutine()
   255  
   256  	_, cancel0 = WithTimeout(&myDoneCtx{Background()}, veryLongDuration)
   257  	cancel0()
   258  	checkCreatedGoroutine()
   259  
   260  	checkNoGoroutine()
   261  	defer checkNoGoroutine()
   262  
   263  	ctx1, cancel1 := WithCancel(Background())
   264  	defer cancel1()
   265  	checkNoGoroutine()
   266  
   267  	ctx2 := &myCtx{ctx1}
   268  	ctx3, cancel3 := WithCancel(ctx2)
   269  	defer cancel3()
   270  	checkNoGoroutine()
   271  
   272  	_, cancel3b := WithCancel(&myDoneCtx{ctx2})
   273  	defer cancel3b()
   274  	checkCreatedGoroutine() // ctx1 is not providing Done, must not be used
   275  
   276  	ctx4, cancel4 := WithTimeout(ctx3, veryLongDuration)
   277  	defer cancel4()
   278  	checkNoGoroutine()
   279  
   280  	ctx5, cancel5 := WithCancel(ctx4)
   281  	defer cancel5()
   282  	checkNoGoroutine()
   283  
   284  	cancel5()
   285  	checkNoGoroutine()
   286  
   287  	_, cancel6 := WithTimeout(ctx5, veryLongDuration)
   288  	defer cancel6()
   289  	checkNoGoroutine()
   290  
   291  	// Check applied to canceled context.
   292  	cancel6()
   293  	cancel1()
   294  	_, cancel7 := WithCancel(ctx5)
   295  	defer cancel7()
   296  	checkNoGoroutine()
   297  }
   298  

View as plain text