github.com/mattn/go@v0.0.0-20171011075504-07f7db3ea99f/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 "time" 14 ) 15 16 type 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. 36 type otherContext struct { 37 Context 38 } 39 40 func 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 55 func 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 70 func 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 111 func contains(m map[canceler]struct{}, key canceler) bool { 112 _, ret := m[key] 113 return ret 114 } 115 116 func 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 193 func 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 253 func 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 264 func 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 287 func 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 304 func 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 320 type key1 int 321 type key2 int 322 323 var k1 = key1(1) 324 var k2 = key2(1) // same int as k1, different type 325 var k3 = key2(3) // same type as k2, different int 326 327 func 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 375 func 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: 15, 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 444 func 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 492 func 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 509 func XTestLayersCancel(t testingT) { 510 testLayers(t, time.Now().UnixNano(), false) 511 } 512 513 func XTestLayersTimeout(t testingT) { 514 testLayers(t, time.Now().UnixNano(), true) 515 } 516 517 func 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 586 func 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 608 func 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 623 func 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 634 func recoveredValue(fn func()) (v interface{}) { 635 defer func() { v = recover() }() 636 fn() 637 return 638 } 639 640 func 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 }