• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
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
12import (
13	"time"
14)
15
16type 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
36const veryLongDuration = 1000 * time.Hour // an arbitrary upper bound on the test's running time
37
38func contains(m map[canceler]struct{}, key canceler) bool {
39	_, ret := m[key]
40	return ret
41}
42
43func 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
133func 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
193func 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
222type myCtx struct {
223	Context
224}
225
226type myDoneCtx struct {
227	Context
228}
229
230func (d *myDoneCtx) Done() <-chan struct{} {
231	c := make(chan struct{})
232	return c
233}
234func 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