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