github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/net/context/context_test.go (about)

     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  //go:build !go1.7
     6  // +build !go1.7
     7  
     8  package context
     9  
    10  import (
    11  	"fmt"
    12  	"math/rand"
    13  	"runtime"
    14  	"strings"
    15  	"sync"
    16  	"testing"
    17  	"time"
    18  )
    19  
    20  // otherContext is a Context that's not one of the types defined in context.go.
    21  // This lets us test code paths that differ based on the underlying type of the
    22  // Context.
    23  type otherContext struct {
    24  	Context
    25  }
    26  
    27  func TestBackground(t *testing.T) {
    28  	c := Background()
    29  	if c == nil {
    30  		t.Fatalf("Background returned nil")
    31  	}
    32  	select {
    33  	case x := <-c.Done():
    34  		t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
    35  	default:
    36  	}
    37  	if got, want := fmt.Sprint(c), "context.Background"; got != want {
    38  		t.Errorf("Background().String() = %q want %q", got, want)
    39  	}
    40  }
    41  
    42  func TestTODO(t *testing.T) {
    43  	c := TODO()
    44  	if c == nil {
    45  		t.Fatalf("TODO 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.TODO"; got != want {
    53  		t.Errorf("TODO().String() = %q want %q", got, want)
    54  	}
    55  }
    56  
    57  func TestWithCancel(t *testing.T) {
    58  	c1, cancel := WithCancel(Background())
    59  
    60  	if got, want := fmt.Sprint(c1), "context.Background.WithCancel"; got != want {
    61  		t.Errorf("c1.String() = %q want %q", got, want)
    62  	}
    63  
    64  	o := otherContext{c1}
    65  	c2, _ := WithCancel(o)
    66  	contexts := []Context{c1, o, c2}
    67  
    68  	for i, c := range contexts {
    69  		if d := c.Done(); d == nil {
    70  			t.Errorf("c[%d].Done() == %v want non-nil", i, d)
    71  		}
    72  		if e := c.Err(); e != nil {
    73  			t.Errorf("c[%d].Err() == %v want nil", i, e)
    74  		}
    75  
    76  		select {
    77  		case x := <-c.Done():
    78  			t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
    79  		default:
    80  		}
    81  	}
    82  
    83  	cancel()
    84  	time.Sleep(100 * time.Millisecond) // let cancelation propagate
    85  
    86  	for i, c := range contexts {
    87  		select {
    88  		case <-c.Done():
    89  		default:
    90  			t.Errorf("<-c[%d].Done() blocked, but shouldn't have", i)
    91  		}
    92  		if e := c.Err(); e != Canceled {
    93  			t.Errorf("c[%d].Err() == %v want %v", i, e, Canceled)
    94  		}
    95  	}
    96  }
    97  
    98  func TestParentFinishesChild(t *testing.T) {
    99  	// Context tree:
   100  	// parent -> cancelChild
   101  	// parent -> valueChild -> timerChild
   102  	parent, cancel := WithCancel(Background())
   103  	cancelChild, stop := WithCancel(parent)
   104  	defer stop()
   105  	valueChild := WithValue(parent, "key", "value")
   106  	timerChild, stop := WithTimeout(valueChild, 10000*time.Hour)
   107  	defer stop()
   108  
   109  	select {
   110  	case x := <-parent.Done():
   111  		t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
   112  	case x := <-cancelChild.Done():
   113  		t.Errorf("<-cancelChild.Done() == %v want nothing (it should block)", x)
   114  	case x := <-timerChild.Done():
   115  		t.Errorf("<-timerChild.Done() == %v want nothing (it should block)", x)
   116  	case x := <-valueChild.Done():
   117  		t.Errorf("<-valueChild.Done() == %v want nothing (it should block)", x)
   118  	default:
   119  	}
   120  
   121  	// The parent's children should contain the two cancelable children.
   122  	pc := parent.(*cancelCtx)
   123  	cc := cancelChild.(*cancelCtx)
   124  	tc := timerChild.(*timerCtx)
   125  	pc.mu.Lock()
   126  	if len(pc.children) != 2 || !pc.children[cc] || !pc.children[tc] {
   127  		t.Errorf("bad linkage: pc.children = %v, want %v and %v",
   128  			pc.children, cc, tc)
   129  	}
   130  	pc.mu.Unlock()
   131  
   132  	if p, ok := parentCancelCtx(cc.Context); !ok || p != pc {
   133  		t.Errorf("bad linkage: parentCancelCtx(cancelChild.Context) = %v, %v want %v, true", p, ok, pc)
   134  	}
   135  	if p, ok := parentCancelCtx(tc.Context); !ok || p != pc {
   136  		t.Errorf("bad linkage: parentCancelCtx(timerChild.Context) = %v, %v want %v, true", p, ok, pc)
   137  	}
   138  
   139  	cancel()
   140  
   141  	pc.mu.Lock()
   142  	if len(pc.children) != 0 {
   143  		t.Errorf("pc.cancel didn't clear pc.children = %v", pc.children)
   144  	}
   145  	pc.mu.Unlock()
   146  
   147  	// parent and children should all be finished.
   148  	check := func(ctx Context, name string) {
   149  		select {
   150  		case <-ctx.Done():
   151  		default:
   152  			t.Errorf("<-%s.Done() blocked, but shouldn't have", name)
   153  		}
   154  		if e := ctx.Err(); e != Canceled {
   155  			t.Errorf("%s.Err() == %v want %v", name, e, Canceled)
   156  		}
   157  	}
   158  	check(parent, "parent")
   159  	check(cancelChild, "cancelChild")
   160  	check(valueChild, "valueChild")
   161  	check(timerChild, "timerChild")
   162  
   163  	// WithCancel should return a canceled context on a canceled parent.
   164  	precanceledChild := WithValue(parent, "key", "value")
   165  	select {
   166  	case <-precanceledChild.Done():
   167  	default:
   168  		t.Errorf("<-precanceledChild.Done() blocked, but shouldn't have")
   169  	}
   170  	if e := precanceledChild.Err(); e != Canceled {
   171  		t.Errorf("precanceledChild.Err() == %v want %v", e, Canceled)
   172  	}
   173  }
   174  
   175  func TestChildFinishesFirst(t *testing.T) {
   176  	cancelable, stop := WithCancel(Background())
   177  	defer stop()
   178  	for _, parent := range []Context{Background(), cancelable} {
   179  		child, cancel := WithCancel(parent)
   180  
   181  		select {
   182  		case x := <-parent.Done():
   183  			t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
   184  		case x := <-child.Done():
   185  			t.Errorf("<-child.Done() == %v want nothing (it should block)", x)
   186  		default:
   187  		}
   188  
   189  		cc := child.(*cancelCtx)
   190  		pc, pcok := parent.(*cancelCtx) // pcok == false when parent == Background()
   191  		if p, ok := parentCancelCtx(cc.Context); ok != pcok || (ok && pc != p) {
   192  			t.Errorf("bad linkage: parentCancelCtx(cc.Context) = %v, %v want %v, %v", p, ok, pc, pcok)
   193  		}
   194  
   195  		if pcok {
   196  			pc.mu.Lock()
   197  			if len(pc.children) != 1 || !pc.children[cc] {
   198  				t.Errorf("bad linkage: pc.children = %v, cc = %v", pc.children, cc)
   199  			}
   200  			pc.mu.Unlock()
   201  		}
   202  
   203  		cancel()
   204  
   205  		if pcok {
   206  			pc.mu.Lock()
   207  			if len(pc.children) != 0 {
   208  				t.Errorf("child's cancel didn't remove self from pc.children = %v", pc.children)
   209  			}
   210  			pc.mu.Unlock()
   211  		}
   212  
   213  		// child should be finished.
   214  		select {
   215  		case <-child.Done():
   216  		default:
   217  			t.Errorf("<-child.Done() blocked, but shouldn't have")
   218  		}
   219  		if e := child.Err(); e != Canceled {
   220  			t.Errorf("child.Err() == %v want %v", e, Canceled)
   221  		}
   222  
   223  		// parent should not be finished.
   224  		select {
   225  		case x := <-parent.Done():
   226  			t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
   227  		default:
   228  		}
   229  		if e := parent.Err(); e != nil {
   230  			t.Errorf("parent.Err() == %v want nil", e)
   231  		}
   232  	}
   233  }
   234  
   235  func testDeadline(c Context, wait time.Duration, t *testing.T) {
   236  	select {
   237  	case <-time.After(wait):
   238  		t.Fatalf("context should have timed out")
   239  	case <-c.Done():
   240  	}
   241  	if e := c.Err(); e != DeadlineExceeded {
   242  		t.Errorf("c.Err() == %v want %v", e, DeadlineExceeded)
   243  	}
   244  }
   245  
   246  func TestDeadline(t *testing.T) {
   247  	t.Parallel()
   248  	const timeUnit = 500 * time.Millisecond
   249  	c, _ := WithDeadline(Background(), time.Now().Add(1*timeUnit))
   250  	if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
   251  		t.Errorf("c.String() = %q want prefix %q", got, prefix)
   252  	}
   253  	testDeadline(c, 2*timeUnit, t)
   254  
   255  	c, _ = WithDeadline(Background(), time.Now().Add(1*timeUnit))
   256  	o := otherContext{c}
   257  	testDeadline(o, 2*timeUnit, t)
   258  
   259  	c, _ = WithDeadline(Background(), time.Now().Add(1*timeUnit))
   260  	o = otherContext{c}
   261  	c, _ = WithDeadline(o, time.Now().Add(3*timeUnit))
   262  	testDeadline(c, 2*timeUnit, t)
   263  }
   264  
   265  func TestTimeout(t *testing.T) {
   266  	t.Parallel()
   267  	const timeUnit = 500 * time.Millisecond
   268  	c, _ := WithTimeout(Background(), 1*timeUnit)
   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, 2*timeUnit, t)
   273  
   274  	c, _ = WithTimeout(Background(), 1*timeUnit)
   275  	o := otherContext{c}
   276  	testDeadline(o, 2*timeUnit, t)
   277  
   278  	c, _ = WithTimeout(Background(), 1*timeUnit)
   279  	o = otherContext{c}
   280  	c, _ = WithTimeout(o, 3*timeUnit)
   281  	testDeadline(c, 2*timeUnit, t)
   282  }
   283  
   284  func TestCanceledTimeout(t *testing.T) {
   285  	t.Parallel()
   286  	const timeUnit = 500 * time.Millisecond
   287  	c, _ := WithTimeout(Background(), 2*timeUnit)
   288  	o := otherContext{c}
   289  	c, cancel := WithTimeout(o, 4*timeUnit)
   290  	cancel()
   291  	time.Sleep(1 * timeUnit) // let cancelation propagate
   292  	select {
   293  	case <-c.Done():
   294  	default:
   295  		t.Errorf("<-c.Done() blocked, but shouldn't have")
   296  	}
   297  	if e := c.Err(); e != Canceled {
   298  		t.Errorf("c.Err() == %v want %v", e, Canceled)
   299  	}
   300  }
   301  
   302  type key1 int
   303  type key2 int
   304  
   305  var k1 = key1(1)
   306  var k2 = key2(1) // same int as k1, different type
   307  var k3 = key2(3) // same type as k2, different int
   308  
   309  func TestValues(t *testing.T) {
   310  	check := func(c Context, nm, v1, v2, v3 string) {
   311  		if v, ok := c.Value(k1).(string); ok == (len(v1) == 0) || v != v1 {
   312  			t.Errorf(`%s.Value(k1).(string) = %q, %t want %q, %t`, nm, v, ok, v1, len(v1) != 0)
   313  		}
   314  		if v, ok := c.Value(k2).(string); ok == (len(v2) == 0) || v != v2 {
   315  			t.Errorf(`%s.Value(k2).(string) = %q, %t want %q, %t`, nm, v, ok, v2, len(v2) != 0)
   316  		}
   317  		if v, ok := c.Value(k3).(string); ok == (len(v3) == 0) || v != v3 {
   318  			t.Errorf(`%s.Value(k3).(string) = %q, %t want %q, %t`, nm, v, ok, v3, len(v3) != 0)
   319  		}
   320  	}
   321  
   322  	c0 := Background()
   323  	check(c0, "c0", "", "", "")
   324  
   325  	c1 := WithValue(Background(), k1, "c1k1")
   326  	check(c1, "c1", "c1k1", "", "")
   327  
   328  	if got, want := fmt.Sprint(c1), `context.Background.WithValue(1, "c1k1")`; got != want {
   329  		t.Errorf("c.String() = %q want %q", got, want)
   330  	}
   331  
   332  	c2 := WithValue(c1, k2, "c2k2")
   333  	check(c2, "c2", "c1k1", "c2k2", "")
   334  
   335  	c3 := WithValue(c2, k3, "c3k3")
   336  	check(c3, "c2", "c1k1", "c2k2", "c3k3")
   337  
   338  	c4 := WithValue(c3, k1, nil)
   339  	check(c4, "c4", "", "c2k2", "c3k3")
   340  
   341  	o0 := otherContext{Background()}
   342  	check(o0, "o0", "", "", "")
   343  
   344  	o1 := otherContext{WithValue(Background(), k1, "c1k1")}
   345  	check(o1, "o1", "c1k1", "", "")
   346  
   347  	o2 := WithValue(o1, k2, "o2k2")
   348  	check(o2, "o2", "c1k1", "o2k2", "")
   349  
   350  	o3 := otherContext{c4}
   351  	check(o3, "o3", "", "c2k2", "c3k3")
   352  
   353  	o4 := WithValue(o3, k3, nil)
   354  	check(o4, "o4", "", "c2k2", "")
   355  }
   356  
   357  func TestAllocs(t *testing.T) {
   358  	bg := Background()
   359  	for _, test := range []struct {
   360  		desc       string
   361  		f          func()
   362  		limit      float64
   363  		gccgoLimit float64
   364  	}{
   365  		{
   366  			desc:       "Background()",
   367  			f:          func() { Background() },
   368  			limit:      0,
   369  			gccgoLimit: 0,
   370  		},
   371  		{
   372  			desc: fmt.Sprintf("WithValue(bg, %v, nil)", k1),
   373  			f: func() {
   374  				c := WithValue(bg, k1, nil)
   375  				c.Value(k1)
   376  			},
   377  			limit:      3,
   378  			gccgoLimit: 3,
   379  		},
   380  		{
   381  			desc: "WithTimeout(bg, 15*time.Millisecond)",
   382  			f: func() {
   383  				c, _ := WithTimeout(bg, 15*time.Millisecond)
   384  				<-c.Done()
   385  			},
   386  			limit:      8,
   387  			gccgoLimit: 16,
   388  		},
   389  		{
   390  			desc: "WithCancel(bg)",
   391  			f: func() {
   392  				c, cancel := WithCancel(bg)
   393  				cancel()
   394  				<-c.Done()
   395  			},
   396  			limit:      5,
   397  			gccgoLimit: 8,
   398  		},
   399  		{
   400  			desc: "WithTimeout(bg, 100*time.Millisecond)",
   401  			f: func() {
   402  				c, cancel := WithTimeout(bg, 100*time.Millisecond)
   403  				cancel()
   404  				<-c.Done()
   405  			},
   406  			limit:      8,
   407  			gccgoLimit: 25,
   408  		},
   409  	} {
   410  		limit := test.limit
   411  		if runtime.Compiler == "gccgo" {
   412  			// gccgo does not yet do escape analysis.
   413  			// TODO(iant): Remove this when gccgo does do escape analysis.
   414  			limit = test.gccgoLimit
   415  		}
   416  		if n := testing.AllocsPerRun(100, test.f); n > limit {
   417  			t.Errorf("%s allocs = %f want %d", test.desc, n, int(limit))
   418  		}
   419  	}
   420  }
   421  
   422  func TestSimultaneousCancels(t *testing.T) {
   423  	root, cancel := WithCancel(Background())
   424  	m := map[Context]CancelFunc{root: cancel}
   425  	q := []Context{root}
   426  	// Create a tree of contexts.
   427  	for len(q) != 0 && len(m) < 100 {
   428  		parent := q[0]
   429  		q = q[1:]
   430  		for i := 0; i < 4; i++ {
   431  			ctx, cancel := WithCancel(parent)
   432  			m[ctx] = cancel
   433  			q = append(q, ctx)
   434  		}
   435  	}
   436  	// Start all the cancels in a random order.
   437  	var wg sync.WaitGroup
   438  	wg.Add(len(m))
   439  	for _, cancel := range m {
   440  		go func(cancel CancelFunc) {
   441  			cancel()
   442  			wg.Done()
   443  		}(cancel)
   444  	}
   445  	// Wait on all the contexts in a random order.
   446  	for ctx := range m {
   447  		select {
   448  		case <-ctx.Done():
   449  		case <-time.After(1 * time.Second):
   450  			buf := make([]byte, 10<<10)
   451  			n := runtime.Stack(buf, true)
   452  			t.Fatalf("timed out waiting for <-ctx.Done(); stacks:\n%s", buf[:n])
   453  		}
   454  	}
   455  	// Wait for all the cancel functions to return.
   456  	done := make(chan struct{})
   457  	go func() {
   458  		wg.Wait()
   459  		close(done)
   460  	}()
   461  	select {
   462  	case <-done:
   463  	case <-time.After(1 * time.Second):
   464  		buf := make([]byte, 10<<10)
   465  		n := runtime.Stack(buf, true)
   466  		t.Fatalf("timed out waiting for cancel functions; stacks:\n%s", buf[:n])
   467  	}
   468  }
   469  
   470  func TestInterlockedCancels(t *testing.T) {
   471  	parent, cancelParent := WithCancel(Background())
   472  	child, cancelChild := WithCancel(parent)
   473  	go func() {
   474  		parent.Done()
   475  		cancelChild()
   476  	}()
   477  	cancelParent()
   478  	select {
   479  	case <-child.Done():
   480  	case <-time.After(1 * time.Second):
   481  		buf := make([]byte, 10<<10)
   482  		n := runtime.Stack(buf, true)
   483  		t.Fatalf("timed out waiting for child.Done(); stacks:\n%s", buf[:n])
   484  	}
   485  }
   486  
   487  func TestLayersCancel(t *testing.T) {
   488  	testLayers(t, time.Now().UnixNano(), false)
   489  }
   490  
   491  func TestLayersTimeout(t *testing.T) {
   492  	testLayers(t, time.Now().UnixNano(), true)
   493  }
   494  
   495  func testLayers(t *testing.T, seed int64, testTimeout bool) {
   496  	rand.Seed(seed)
   497  	errorf := func(format string, a ...interface{}) {
   498  		t.Errorf(fmt.Sprintf("seed=%d: %s", seed, format), a...)
   499  	}
   500  	const (
   501  		timeout   = 200 * time.Millisecond
   502  		minLayers = 30
   503  	)
   504  	type value int
   505  	var (
   506  		vals      []*value
   507  		cancels   []CancelFunc
   508  		numTimers int
   509  		ctx       = Background()
   510  	)
   511  	for i := 0; i < minLayers || numTimers == 0 || len(cancels) == 0 || len(vals) == 0; i++ {
   512  		switch rand.Intn(3) {
   513  		case 0:
   514  			v := new(value)
   515  			ctx = WithValue(ctx, v, v)
   516  			vals = append(vals, v)
   517  		case 1:
   518  			var cancel CancelFunc
   519  			ctx, cancel = WithCancel(ctx)
   520  			cancels = append(cancels, cancel)
   521  		case 2:
   522  			var cancel CancelFunc
   523  			ctx, cancel = WithTimeout(ctx, timeout)
   524  			cancels = append(cancels, cancel)
   525  			numTimers++
   526  		}
   527  	}
   528  	checkValues := func(when string) {
   529  		for _, key := range vals {
   530  			if val := ctx.Value(key).(*value); key != val {
   531  				errorf("%s: ctx.Value(%p) = %p want %p", when, key, val, key)
   532  			}
   533  		}
   534  	}
   535  	select {
   536  	case <-ctx.Done():
   537  		errorf("ctx should not be canceled yet")
   538  	default:
   539  	}
   540  	if s, prefix := fmt.Sprint(ctx), "context.Background."; !strings.HasPrefix(s, prefix) {
   541  		t.Errorf("ctx.String() = %q want prefix %q", s, prefix)
   542  	}
   543  	t.Log(ctx)
   544  	checkValues("before cancel")
   545  	if testTimeout {
   546  		select {
   547  		case <-ctx.Done():
   548  		case <-time.After(timeout + 100*time.Millisecond):
   549  			errorf("ctx should have timed out")
   550  		}
   551  		checkValues("after timeout")
   552  	} else {
   553  		cancel := cancels[rand.Intn(len(cancels))]
   554  		cancel()
   555  		select {
   556  		case <-ctx.Done():
   557  		default:
   558  			errorf("ctx should be canceled")
   559  		}
   560  		checkValues("after cancel")
   561  	}
   562  }
   563  
   564  func TestCancelRemoves(t *testing.T) {
   565  	checkChildren := func(when string, ctx Context, want int) {
   566  		if got := len(ctx.(*cancelCtx).children); got != want {
   567  			t.Errorf("%s: context has %d children, want %d", when, got, want)
   568  		}
   569  	}
   570  
   571  	ctx, _ := WithCancel(Background())
   572  	checkChildren("after creation", ctx, 0)
   573  	_, cancel := WithCancel(ctx)
   574  	checkChildren("with WithCancel child ", ctx, 1)
   575  	cancel()
   576  	checkChildren("after cancelling WithCancel child", ctx, 0)
   577  
   578  	ctx, _ = WithCancel(Background())
   579  	checkChildren("after creation", ctx, 0)
   580  	_, cancel = WithTimeout(ctx, 60*time.Minute)
   581  	checkChildren("with WithTimeout child ", ctx, 1)
   582  	cancel()
   583  	checkChildren("after cancelling WithTimeout child", ctx, 0)
   584  }