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