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