blob: 05345fc5e5fb856499057fef5d1b9ea59bdb69ef [file] [log] [blame]
Sameer Ajmanif5729742014-05-16 16:11:29 -04001// 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"
Sameer Ajmania1f36092014-07-29 14:21:02 -04009 "math/rand"
Sameer Ajmanif5729742014-05-16 16:11:29 -040010 "runtime"
Sameer Ajmani9c40a722014-08-01 09:28:31 -040011 "strings"
Sameer Ajmanif5729742014-05-16 16:11:29 -040012 "sync"
13 "testing"
14 "time"
15)
16
Sameer Ajmania67aa992014-07-30 12:14:59 -040017// 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.
Sameer Ajmanif5729742014-05-16 16:11:29 -040020type otherContext struct {
21 Context
22}
23
24func 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 }
Sameer Ajmani9c40a722014-08-01 09:28:31 -040034 if got, want := fmt.Sprint(c), "context.Background"; got != want {
35 t.Errorf("Background().String() = %q want %q", got, want)
Sameer Ajmania1f36092014-07-29 14:21:02 -040036 }
37}
38
39func 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 }
Sameer Ajmani9c40a722014-08-01 09:28:31 -040049 if got, want := fmt.Sprint(c), "context.TODO"; got != want {
50 t.Errorf("TODO().String() = %q want %q", got, want)
Sameer Ajmania1f36092014-07-29 14:21:02 -040051 }
Sameer Ajmanif5729742014-05-16 16:11:29 -040052}
53
54func TestWithCancel(t *testing.T) {
55 c1, cancel := WithCancel(Background())
Sameer Ajmani9c40a722014-08-01 09:28:31 -040056
57 if got, want := fmt.Sprint(c1), "context.Background.WithCancel"; got != want {
58 t.Errorf("c1.String() = %q want %q", got, want)
59 }
60
Sameer Ajmanif5729742014-05-16 16:11:29 -040061 o := otherContext{c1}
Sameer Ajmania67aa992014-07-30 12:14:59 -040062 c2, _ := WithCancel(o)
Sameer Ajmanif5729742014-05-16 16:11:29 -040063 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()
Sameer Ajmani9c40a722014-08-01 09:28:31 -040081 time.Sleep(100 * time.Millisecond) // let cancelation propagate
Sameer Ajmanif5729742014-05-16 16:11:29 -040082
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
95func TestParentFinishesChild(t *testing.T) {
Sameer Ajmania67aa992014-07-30 12:14:59 -040096 // Context tree:
97 // parent -> cancelChild
98 // parent -> valueChild -> timerChild
Sameer Ajmanif5729742014-05-16 16:11:29 -040099 parent, cancel := WithCancel(Background())
Sameer Ajmania67aa992014-07-30 12:14:59 -0400100 cancelChild, stop := WithCancel(parent)
101 defer stop()
102 valueChild := WithValue(parent, "key", "value")
103 timerChild, stop := WithTimeout(valueChild, 10000*time.Hour)
104 defer stop()
Sameer Ajmanif5729742014-05-16 16:11:29 -0400105
106 select {
107 case x := <-parent.Done():
108 t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
Sameer Ajmania67aa992014-07-30 12:14:59 -0400109 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)
Sameer Ajmanif5729742014-05-16 16:11:29 -0400115 default:
116 }
117
Sameer Ajmania67aa992014-07-30 12:14:59 -0400118 // 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)
Sameer Ajmanif5729742014-05-16 16:11:29 -0400126 }
Sameer Ajmania67aa992014-07-30 12:14:59 -0400127 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 }
Sameer Ajmanif5729742014-05-16 16:11:29 -0400135
136 cancel()
137
Sameer Ajmania67aa992014-07-30 12:14:59 -0400138 pc.mu.Lock()
139 if len(pc.children) != 0 {
140 t.Errorf("pc.cancel didn't clear pc.children = %v", pc.children)
Sameer Ajmanif5729742014-05-16 16:11:29 -0400141 }
Sameer Ajmania67aa992014-07-30 12:14:59 -0400142 pc.mu.Unlock()
Sameer Ajmanif5729742014-05-16 16:11:29 -0400143
144 // parent and children should all be finished.
Sameer Ajmania67aa992014-07-30 12:14:59 -0400145 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 }
Sameer Ajmanif5729742014-05-16 16:11:29 -0400154 }
Sameer Ajmania67aa992014-07-30 12:14:59 -0400155 check(parent, "parent")
156 check(cancelChild, "cancelChild")
157 check(valueChild, "valueChild")
158 check(timerChild, "timerChild")
Sameer Ajmanif5729742014-05-16 16:11:29 -0400159
Sameer Ajmania67aa992014-07-30 12:14:59 -0400160 // WithCancel should return a canceled context on a canceled parent.
161 precanceledChild := WithValue(parent, "key", "value")
Sameer Ajmanif5729742014-05-16 16:11:29 -0400162 select {
Sameer Ajmania67aa992014-07-30 12:14:59 -0400163 case <-precanceledChild.Done():
Sameer Ajmanif5729742014-05-16 16:11:29 -0400164 default:
Sameer Ajmania67aa992014-07-30 12:14:59 -0400165 t.Errorf("<-precanceledChild.Done() blocked, but shouldn't have")
Sameer Ajmanif5729742014-05-16 16:11:29 -0400166 }
Sameer Ajmania67aa992014-07-30 12:14:59 -0400167 if e := precanceledChild.Err(); e != Canceled {
168 t.Errorf("precanceledChild.Err() == %v want %v", e, Canceled)
Sameer Ajmanif5729742014-05-16 16:11:29 -0400169 }
170}
171
172func TestChildFinishesFirst(t *testing.T) {
Sameer Ajmania67aa992014-07-30 12:14:59 -0400173 cancelable, stop := WithCancel(Background())
174 defer stop()
175 for _, parent := range []Context{Background(), cancelable} {
Sameer Ajmanif5729742014-05-16 16:11:29 -0400176 child, cancel := WithCancel(parent)
Sameer Ajmanif5729742014-05-16 16:11:29 -0400177
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
Sameer Ajmania67aa992014-07-30 12:14:59 -0400186 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)
Sameer Ajmanif5729742014-05-16 16:11:29 -0400190 }
191
Sameer Ajmania67aa992014-07-30 12:14:59 -0400192 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)
Sameer Ajmanif5729742014-05-16 16:11:29 -0400196 }
Sameer Ajmania67aa992014-07-30 12:14:59 -0400197 pc.mu.Unlock()
Sameer Ajmanif5729742014-05-16 16:11:29 -0400198 }
199
200 cancel()
201
Sameer Ajmania67aa992014-07-30 12:14:59 -0400202 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()
Sameer Ajmanif5729742014-05-16 16:11:29 -0400208 }
Sameer Ajmanif5729742014-05-16 16:11:29 -0400209
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
232func testDeadline(c Context, wait time.Duration, t *testing.T) {
233 select {
234 case <-time.After(wait):
235 t.Fatalf("context should have timed out")
236 case <-c.Done():
237 }
238 if e := c.Err(); e != DeadlineExceeded {
239 t.Errorf("c.Err() == %v want %v", e, DeadlineExceeded)
240 }
241}
242
243func TestDeadline(t *testing.T) {
Sameer Ajmania67aa992014-07-30 12:14:59 -0400244 c, _ := WithDeadline(Background(), time.Now().Add(100*time.Millisecond))
Sameer Ajmani9c40a722014-08-01 09:28:31 -0400245 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 }
Sameer Ajmanif5729742014-05-16 16:11:29 -0400248 testDeadline(c, 200*time.Millisecond, t)
249
Sameer Ajmania67aa992014-07-30 12:14:59 -0400250 c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond))
Sameer Ajmanif5729742014-05-16 16:11:29 -0400251 o := otherContext{c}
252 testDeadline(o, 200*time.Millisecond, t)
253
Sameer Ajmania67aa992014-07-30 12:14:59 -0400254 c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond))
Sameer Ajmanif5729742014-05-16 16:11:29 -0400255 o = otherContext{c}
256 c, _ = WithDeadline(o, time.Now().Add(300*time.Millisecond))
257 testDeadline(c, 200*time.Millisecond, t)
258}
259
260func TestTimeout(t *testing.T) {
Sameer Ajmania67aa992014-07-30 12:14:59 -0400261 c, _ := WithTimeout(Background(), 100*time.Millisecond)
Sameer Ajmani9c40a722014-08-01 09:28:31 -0400262 if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
263 t.Errorf("c.String() = %q want prefix %q", got, prefix)
264 }
Sameer Ajmanif5729742014-05-16 16:11:29 -0400265 testDeadline(c, 200*time.Millisecond, t)
266
Sameer Ajmania67aa992014-07-30 12:14:59 -0400267 c, _ = WithTimeout(Background(), 100*time.Millisecond)
Sameer Ajmanif5729742014-05-16 16:11:29 -0400268 o := otherContext{c}
269 testDeadline(o, 200*time.Millisecond, t)
270
Sameer Ajmania67aa992014-07-30 12:14:59 -0400271 c, _ = WithTimeout(Background(), 100*time.Millisecond)
Sameer Ajmanif5729742014-05-16 16:11:29 -0400272 o = otherContext{c}
273 c, _ = WithTimeout(o, 300*time.Millisecond)
274 testDeadline(c, 200*time.Millisecond, t)
275}
276
Sameer Ajmani9c40a722014-08-01 09:28:31 -0400277func TestCanceledTimeout(t *testing.T) {
Sameer Ajmania67aa992014-07-30 12:14:59 -0400278 c, _ := WithTimeout(Background(), 200*time.Millisecond)
Sameer Ajmanif5729742014-05-16 16:11:29 -0400279 o := otherContext{c}
280 c, cancel := WithTimeout(o, 400*time.Millisecond)
281 cancel()
Sameer Ajmani9c40a722014-08-01 09:28:31 -0400282 time.Sleep(100 * time.Millisecond) // let cancelation propagate
Sameer Ajmanif5729742014-05-16 16:11:29 -0400283 select {
284 case <-c.Done():
285 default:
286 t.Errorf("<-c.Done() blocked, but shouldn't have")
287 }
288 if e := c.Err(); e != Canceled {
289 t.Errorf("c.Err() == %v want %v", e, Canceled)
290 }
291}
292
Sameer Ajmanibf13cf42014-07-10 14:57:25 -0400293type key1 int
294type key2 int
295
296var k1 = key1(1)
297var k2 = key2(1) // same int as k1, different type
298var k3 = key2(3) // same type as k2, different int
Sameer Ajmanif5729742014-05-16 16:11:29 -0400299
300func TestValues(t *testing.T) {
Sameer Ajmanibf13cf42014-07-10 14:57:25 -0400301 check := func(c Context, nm, v1, v2, v3 string) {
Sameer Ajmanif5729742014-05-16 16:11:29 -0400302 if v, ok := c.Value(k1).(string); ok == (len(v1) == 0) || v != v1 {
303 t.Errorf(`%s.Value(k1).(string) = %q, %t want %q, %t`, nm, v, ok, v1, len(v1) != 0)
304 }
305 if v, ok := c.Value(k2).(string); ok == (len(v2) == 0) || v != v2 {
306 t.Errorf(`%s.Value(k2).(string) = %q, %t want %q, %t`, nm, v, ok, v2, len(v2) != 0)
307 }
Sameer Ajmanibf13cf42014-07-10 14:57:25 -0400308 if v, ok := c.Value(k3).(string); ok == (len(v3) == 0) || v != v3 {
309 t.Errorf(`%s.Value(k3).(string) = %q, %t want %q, %t`, nm, v, ok, v3, len(v3) != 0)
310 }
Sameer Ajmanif5729742014-05-16 16:11:29 -0400311 }
312
313 c0 := Background()
Sameer Ajmanibf13cf42014-07-10 14:57:25 -0400314 check(c0, "c0", "", "", "")
Sameer Ajmanif5729742014-05-16 16:11:29 -0400315
Sameer Ajmania67aa992014-07-30 12:14:59 -0400316 c1 := WithValue(Background(), k1, "c1k1")
Sameer Ajmanibf13cf42014-07-10 14:57:25 -0400317 check(c1, "c1", "c1k1", "", "")
Sameer Ajmanif5729742014-05-16 16:11:29 -0400318
Sameer Ajmani9c40a722014-08-01 09:28:31 -0400319 if got, want := fmt.Sprint(c1), `context.Background.WithValue(1, "c1k1")`; got != want {
320 t.Errorf("c.String() = %q want %q", got, want)
321 }
322
Sameer Ajmanif5729742014-05-16 16:11:29 -0400323 c2 := WithValue(c1, k2, "c2k2")
Sameer Ajmanibf13cf42014-07-10 14:57:25 -0400324 check(c2, "c2", "c1k1", "c2k2", "")
Sameer Ajmanif5729742014-05-16 16:11:29 -0400325
Sameer Ajmanibf13cf42014-07-10 14:57:25 -0400326 c3 := WithValue(c2, k3, "c3k3")
327 check(c3, "c2", "c1k1", "c2k2", "c3k3")
328
329 c4 := WithValue(c3, k1, nil)
330 check(c4, "c4", "", "c2k2", "c3k3")
Sameer Ajmanif5729742014-05-16 16:11:29 -0400331
332 o0 := otherContext{Background()}
Sameer Ajmanibf13cf42014-07-10 14:57:25 -0400333 check(o0, "o0", "", "", "")
Sameer Ajmanif5729742014-05-16 16:11:29 -0400334
Sameer Ajmania67aa992014-07-30 12:14:59 -0400335 o1 := otherContext{WithValue(Background(), k1, "c1k1")}
Sameer Ajmanibf13cf42014-07-10 14:57:25 -0400336 check(o1, "o1", "c1k1", "", "")
Sameer Ajmanif5729742014-05-16 16:11:29 -0400337
338 o2 := WithValue(o1, k2, "o2k2")
Sameer Ajmanibf13cf42014-07-10 14:57:25 -0400339 check(o2, "o2", "c1k1", "o2k2", "")
Sameer Ajmanif5729742014-05-16 16:11:29 -0400340
Sameer Ajmanibf13cf42014-07-10 14:57:25 -0400341 o3 := otherContext{c4}
342 check(o3, "o3", "", "c2k2", "c3k3")
343
344 o4 := WithValue(o3, k3, nil)
345 check(o4, "o4", "", "c2k2", "")
Sameer Ajmanif5729742014-05-16 16:11:29 -0400346}
347
348func TestAllocs(t *testing.T) {
349 bg := Background()
350 for _, test := range []struct {
351 desc string
352 f func()
353 limit float64
354 gccgoLimit float64
355 }{
356 {
357 desc: "Background()",
358 f: func() { Background() },
359 limit: 0,
360 gccgoLimit: 0,
361 },
362 {
363 desc: fmt.Sprintf("WithValue(bg, %v, nil)", k1),
364 f: func() {
365 c := WithValue(bg, k1, nil)
366 c.Value(k1)
367 },
Sameer Ajmani3ffb8fd2014-10-09 13:51:22 -0400368 limit: 3,
Sameer Ajmanif5729742014-05-16 16:11:29 -0400369 gccgoLimit: 3,
370 },
371 {
Alex Brainman6b9623c2014-06-18 10:02:55 +1000372 desc: "WithTimeout(bg, 15*time.Millisecond)",
Sameer Ajmanif5729742014-05-16 16:11:29 -0400373 f: func() {
Alex Brainman6b9623c2014-06-18 10:02:55 +1000374 c, _ := WithTimeout(bg, 15*time.Millisecond)
Sameer Ajmanif5729742014-05-16 16:11:29 -0400375 <-c.Done()
376 },
Sameer Ajmania67aa992014-07-30 12:14:59 -0400377 limit: 8,
David Symonds76c2dd92015-08-04 10:14:46 +1000378 gccgoLimit: 15,
Sameer Ajmanif5729742014-05-16 16:11:29 -0400379 },
380 {
381 desc: "WithCancel(bg)",
382 f: func() {
383 c, cancel := WithCancel(bg)
384 cancel()
385 <-c.Done()
386 },
Sameer Ajmania67aa992014-07-30 12:14:59 -0400387 limit: 5,
Sameer Ajmanif5729742014-05-16 16:11:29 -0400388 gccgoLimit: 8,
389 },
390 {
391 desc: "WithTimeout(bg, 100*time.Millisecond)",
392 f: func() {
393 c, cancel := WithTimeout(bg, 100*time.Millisecond)
394 cancel()
395 <-c.Done()
396 },
Sameer Ajmania67aa992014-07-30 12:14:59 -0400397 limit: 8,
Sameer Ajmanif5729742014-05-16 16:11:29 -0400398 gccgoLimit: 25,
399 },
400 } {
401 limit := test.limit
402 if runtime.Compiler == "gccgo" {
403 // gccgo does not yet do escape analysis.
404 // TOOD(iant): Remove this when gccgo does do escape analysis.
405 limit = test.gccgoLimit
406 }
Alex Brainman6b9623c2014-06-18 10:02:55 +1000407 if n := testing.AllocsPerRun(100, test.f); n > limit {
Sameer Ajmanif5729742014-05-16 16:11:29 -0400408 t.Errorf("%s allocs = %f want %d", test.desc, n, int(limit))
409 }
410 }
411}
412
413func TestSimultaneousCancels(t *testing.T) {
414 root, cancel := WithCancel(Background())
415 m := map[Context]CancelFunc{root: cancel}
416 q := []Context{root}
417 // Create a tree of contexts.
418 for len(q) != 0 && len(m) < 100 {
419 parent := q[0]
420 q = q[1:]
421 for i := 0; i < 4; i++ {
422 ctx, cancel := WithCancel(parent)
423 m[ctx] = cancel
424 q = append(q, ctx)
425 }
426 }
427 // Start all the cancels in a random order.
428 var wg sync.WaitGroup
429 wg.Add(len(m))
430 for _, cancel := range m {
431 go func(cancel CancelFunc) {
432 cancel()
433 wg.Done()
434 }(cancel)
435 }
436 // Wait on all the contexts in a random order.
437 for ctx := range m {
438 select {
439 case <-ctx.Done():
440 case <-time.After(1 * time.Second):
441 buf := make([]byte, 10<<10)
442 n := runtime.Stack(buf, true)
443 t.Fatalf("timed out waiting for <-ctx.Done(); stacks:\n%s", buf[:n])
444 }
445 }
446 // Wait for all the cancel functions to return.
447 done := make(chan struct{})
448 go func() {
449 wg.Wait()
450 close(done)
451 }()
452 select {
453 case <-done:
454 case <-time.After(1 * time.Second):
455 buf := make([]byte, 10<<10)
456 n := runtime.Stack(buf, true)
457 t.Fatalf("timed out waiting for cancel functions; stacks:\n%s", buf[:n])
458 }
459}
460
461func TestInterlockedCancels(t *testing.T) {
462 parent, cancelParent := WithCancel(Background())
463 child, cancelChild := WithCancel(parent)
464 go func() {
465 parent.Done()
466 cancelChild()
467 }()
468 cancelParent()
469 select {
470 case <-child.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 child.Done(); stacks:\n%s", buf[:n])
475 }
476}
Sameer Ajmania1f36092014-07-29 14:21:02 -0400477
478func TestLayersCancel(t *testing.T) {
479 testLayers(t, time.Now().UnixNano(), false)
480}
481
482func TestLayersTimeout(t *testing.T) {
483 testLayers(t, time.Now().UnixNano(), true)
484}
485
486func testLayers(t *testing.T, seed int64, testTimeout bool) {
487 rand.Seed(seed)
488 errorf := func(format string, a ...interface{}) {
489 t.Errorf(fmt.Sprintf("seed=%d: %s", seed, format), a...)
490 }
491 const (
492 timeout = 200 * time.Millisecond
493 minLayers = 30
494 )
495 type value int
496 var (
497 vals []*value
498 cancels []CancelFunc
499 numTimers int
500 ctx = Background()
501 )
502 for i := 0; i < minLayers || numTimers == 0 || len(cancels) == 0 || len(vals) == 0; i++ {
503 switch rand.Intn(3) {
504 case 0:
505 v := new(value)
Sameer Ajmania1f36092014-07-29 14:21:02 -0400506 ctx = WithValue(ctx, v, v)
507 vals = append(vals, v)
508 case 1:
509 var cancel CancelFunc
Sameer Ajmania1f36092014-07-29 14:21:02 -0400510 ctx, cancel = WithCancel(ctx)
511 cancels = append(cancels, cancel)
512 case 2:
513 var cancel CancelFunc
Sameer Ajmania1f36092014-07-29 14:21:02 -0400514 ctx, cancel = WithTimeout(ctx, timeout)
515 cancels = append(cancels, cancel)
516 numTimers++
517 }
518 }
519 checkValues := func(when string) {
520 for _, key := range vals {
521 if val := ctx.Value(key).(*value); key != val {
522 errorf("%s: ctx.Value(%p) = %p want %p", when, key, val, key)
523 }
524 }
525 }
526 select {
527 case <-ctx.Done():
528 errorf("ctx should not be canceled yet")
529 default:
530 }
Sameer Ajmani9c40a722014-08-01 09:28:31 -0400531 if s, prefix := fmt.Sprint(ctx), "context.Background."; !strings.HasPrefix(s, prefix) {
532 t.Errorf("ctx.String() = %q want prefix %q", s, prefix)
533 }
534 t.Log(ctx)
Sameer Ajmania1f36092014-07-29 14:21:02 -0400535 checkValues("before cancel")
536 if testTimeout {
537 select {
538 case <-ctx.Done():
Sameer Ajmani96feaeb2015-08-14 18:05:50 +0100539 case <-time.After(timeout + 100*time.Millisecond):
Sameer Ajmania1f36092014-07-29 14:21:02 -0400540 errorf("ctx should have timed out")
541 }
542 checkValues("after timeout")
543 } else {
544 cancel := cancels[rand.Intn(len(cancels))]
545 cancel()
546 select {
547 case <-ctx.Done():
548 default:
549 errorf("ctx should be canceled")
550 }
551 checkValues("after cancel")
552 }
553}
Damien Neilf090b052015-02-04 12:47:08 -0800554
555func TestCancelRemoves(t *testing.T) {
556 checkChildren := func(when string, ctx Context, want int) {
557 if got := len(ctx.(*cancelCtx).children); got != want {
558 t.Errorf("%s: context has %d children, want %d", when, got, want)
559 }
560 }
561
562 ctx, _ := WithCancel(Background())
563 checkChildren("after creation", ctx, 0)
564 _, cancel := WithCancel(ctx)
565 checkChildren("with WithCancel child ", ctx, 1)
566 cancel()
567 checkChildren("after cancelling WithCancel child", ctx, 0)
568
569 ctx, _ = WithCancel(Background())
570 checkChildren("after creation", ctx, 0)
571 _, cancel = WithTimeout(ctx, 60*time.Minute)
572 checkChildren("with WithTimeout child ", ctx, 1)
573 cancel()
574 checkChildren("after cancelling WithTimeout child", ctx, 0)
575}