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 }