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
5package context
6
7import (
8	"fmt"
9	"math/rand"
10	"runtime"
11	"strings"
12	"sync"
13	"sync/atomic"
14	"time"
15)
16
17type 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.
38type otherContext struct {
39	Context
40}
41
42func 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
57func 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
72func 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
113func contains(m map[canceler]struct{}, key canceler) bool {
114	_, ret := m[key]
115	return ret
116}
117
118func 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
195func 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
255func 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
267func 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
290func 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
307func 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
323type key1 int
324type key2 int
325
326var k1 = key1(1)
327var k2 = key2(1) // same int as k1, different type
328var k3 = key2(3) // same type as k2, different int
329
330func 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
378func 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
447func 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
495func 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
512func XTestLayersCancel(t testingT) {
513	testLayers(t, time.Now().UnixNano(), false)
514}
515
516func XTestLayersTimeout(t testingT) {
517	testLayers(t, time.Now().UnixNano(), true)
518}
519
520func 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
589func 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
611func 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
626func 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
637func recoveredValue(fn func()) (v interface{}) {
638	defer func() { v = recover() }()
639	fn()
640	return
641}
642
643func 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
655type myCtx struct {
656	Context
657}
658
659type myDoneCtx struct {
660	Context
661}
662
663func (d *myDoneCtx) Done() <-chan struct{} {
664	c := make(chan struct{})
665	return c
666}
667
668func 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