Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 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 | |
Brad Fitzpatrick | afc22ee | 2016-03-08 00:20:11 +0000 | [diff] [blame] | 5 | // +build !go1.7 |
| 6 | |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 7 | package context |
| 8 | |
| 9 | import ( |
| 10 | "fmt" |
Sameer Ajmani | a1f3609 | 2014-07-29 14:21:02 -0400 | [diff] [blame] | 11 | "math/rand" |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 12 | "runtime" |
Sameer Ajmani | 9c40a72 | 2014-08-01 09:28:31 -0400 | [diff] [blame] | 13 | "strings" |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 14 | "sync" |
| 15 | "testing" |
| 16 | "time" |
| 17 | ) |
| 18 | |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 19 | // otherContext is a Context that's not one of the types defined in context.go. |
| 20 | // This lets us test code paths that differ based on the underlying type of the |
| 21 | // Context. |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 22 | type otherContext struct { |
| 23 | Context |
| 24 | } |
| 25 | |
| 26 | func TestBackground(t *testing.T) { |
| 27 | c := Background() |
| 28 | if c == nil { |
| 29 | t.Fatalf("Background returned nil") |
| 30 | } |
| 31 | select { |
| 32 | case x := <-c.Done(): |
| 33 | t.Errorf("<-c.Done() == %v want nothing (it should block)", x) |
| 34 | default: |
| 35 | } |
Sameer Ajmani | 9c40a72 | 2014-08-01 09:28:31 -0400 | [diff] [blame] | 36 | if got, want := fmt.Sprint(c), "context.Background"; got != want { |
| 37 | t.Errorf("Background().String() = %q want %q", got, want) |
Sameer Ajmani | a1f3609 | 2014-07-29 14:21:02 -0400 | [diff] [blame] | 38 | } |
| 39 | } |
| 40 | |
| 41 | func TestTODO(t *testing.T) { |
| 42 | c := TODO() |
| 43 | if c == nil { |
| 44 | t.Fatalf("TODO returned nil") |
| 45 | } |
| 46 | select { |
| 47 | case x := <-c.Done(): |
| 48 | t.Errorf("<-c.Done() == %v want nothing (it should block)", x) |
| 49 | default: |
| 50 | } |
Sameer Ajmani | 9c40a72 | 2014-08-01 09:28:31 -0400 | [diff] [blame] | 51 | if got, want := fmt.Sprint(c), "context.TODO"; got != want { |
| 52 | t.Errorf("TODO().String() = %q want %q", got, want) |
Sameer Ajmani | a1f3609 | 2014-07-29 14:21:02 -0400 | [diff] [blame] | 53 | } |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 54 | } |
| 55 | |
| 56 | func TestWithCancel(t *testing.T) { |
| 57 | c1, cancel := WithCancel(Background()) |
Sameer Ajmani | 9c40a72 | 2014-08-01 09:28:31 -0400 | [diff] [blame] | 58 | |
| 59 | if got, want := fmt.Sprint(c1), "context.Background.WithCancel"; got != want { |
| 60 | t.Errorf("c1.String() = %q want %q", got, want) |
| 61 | } |
| 62 | |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 63 | o := otherContext{c1} |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 64 | c2, _ := WithCancel(o) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 65 | contexts := []Context{c1, o, c2} |
| 66 | |
| 67 | for i, c := range contexts { |
| 68 | if d := c.Done(); d == nil { |
| 69 | t.Errorf("c[%d].Done() == %v want non-nil", i, d) |
| 70 | } |
| 71 | if e := c.Err(); e != nil { |
| 72 | t.Errorf("c[%d].Err() == %v want nil", i, e) |
| 73 | } |
| 74 | |
| 75 | select { |
| 76 | case x := <-c.Done(): |
| 77 | t.Errorf("<-c.Done() == %v want nothing (it should block)", x) |
| 78 | default: |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | cancel() |
Sameer Ajmani | 9c40a72 | 2014-08-01 09:28:31 -0400 | [diff] [blame] | 83 | time.Sleep(100 * time.Millisecond) // let cancelation propagate |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 84 | |
| 85 | for i, c := range contexts { |
| 86 | select { |
| 87 | case <-c.Done(): |
| 88 | default: |
| 89 | t.Errorf("<-c[%d].Done() blocked, but shouldn't have", i) |
| 90 | } |
| 91 | if e := c.Err(); e != Canceled { |
| 92 | t.Errorf("c[%d].Err() == %v want %v", i, e, Canceled) |
| 93 | } |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | func TestParentFinishesChild(t *testing.T) { |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 98 | // Context tree: |
| 99 | // parent -> cancelChild |
| 100 | // parent -> valueChild -> timerChild |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 101 | parent, cancel := WithCancel(Background()) |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 102 | cancelChild, stop := WithCancel(parent) |
| 103 | defer stop() |
| 104 | valueChild := WithValue(parent, "key", "value") |
| 105 | timerChild, stop := WithTimeout(valueChild, 10000*time.Hour) |
| 106 | defer stop() |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 107 | |
| 108 | select { |
| 109 | case x := <-parent.Done(): |
| 110 | t.Errorf("<-parent.Done() == %v want nothing (it should block)", x) |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 111 | case x := <-cancelChild.Done(): |
| 112 | t.Errorf("<-cancelChild.Done() == %v want nothing (it should block)", x) |
| 113 | case x := <-timerChild.Done(): |
| 114 | t.Errorf("<-timerChild.Done() == %v want nothing (it should block)", x) |
| 115 | case x := <-valueChild.Done(): |
| 116 | t.Errorf("<-valueChild.Done() == %v want nothing (it should block)", x) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 117 | default: |
| 118 | } |
| 119 | |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 120 | // The parent's children should contain the two cancelable children. |
| 121 | pc := parent.(*cancelCtx) |
| 122 | cc := cancelChild.(*cancelCtx) |
| 123 | tc := timerChild.(*timerCtx) |
| 124 | pc.mu.Lock() |
| 125 | if len(pc.children) != 2 || !pc.children[cc] || !pc.children[tc] { |
| 126 | t.Errorf("bad linkage: pc.children = %v, want %v and %v", |
| 127 | pc.children, cc, tc) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 128 | } |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 129 | pc.mu.Unlock() |
| 130 | |
| 131 | if p, ok := parentCancelCtx(cc.Context); !ok || p != pc { |
| 132 | t.Errorf("bad linkage: parentCancelCtx(cancelChild.Context) = %v, %v want %v, true", p, ok, pc) |
| 133 | } |
| 134 | if p, ok := parentCancelCtx(tc.Context); !ok || p != pc { |
| 135 | t.Errorf("bad linkage: parentCancelCtx(timerChild.Context) = %v, %v want %v, true", p, ok, pc) |
| 136 | } |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 137 | |
| 138 | cancel() |
| 139 | |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 140 | pc.mu.Lock() |
| 141 | if len(pc.children) != 0 { |
| 142 | t.Errorf("pc.cancel didn't clear pc.children = %v", pc.children) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 143 | } |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 144 | pc.mu.Unlock() |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 145 | |
| 146 | // parent and children should all be finished. |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 147 | check := func(ctx Context, name string) { |
| 148 | select { |
| 149 | case <-ctx.Done(): |
| 150 | default: |
| 151 | t.Errorf("<-%s.Done() blocked, but shouldn't have", name) |
| 152 | } |
| 153 | if e := ctx.Err(); e != Canceled { |
| 154 | t.Errorf("%s.Err() == %v want %v", name, e, Canceled) |
| 155 | } |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 156 | } |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 157 | check(parent, "parent") |
| 158 | check(cancelChild, "cancelChild") |
| 159 | check(valueChild, "valueChild") |
| 160 | check(timerChild, "timerChild") |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 161 | |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 162 | // WithCancel should return a canceled context on a canceled parent. |
| 163 | precanceledChild := WithValue(parent, "key", "value") |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 164 | select { |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 165 | case <-precanceledChild.Done(): |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 166 | default: |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 167 | t.Errorf("<-precanceledChild.Done() blocked, but shouldn't have") |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 168 | } |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 169 | if e := precanceledChild.Err(); e != Canceled { |
| 170 | t.Errorf("precanceledChild.Err() == %v want %v", e, Canceled) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 171 | } |
| 172 | } |
| 173 | |
| 174 | func TestChildFinishesFirst(t *testing.T) { |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 175 | cancelable, stop := WithCancel(Background()) |
| 176 | defer stop() |
| 177 | for _, parent := range []Context{Background(), cancelable} { |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 178 | child, cancel := WithCancel(parent) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 179 | |
| 180 | select { |
| 181 | case x := <-parent.Done(): |
| 182 | t.Errorf("<-parent.Done() == %v want nothing (it should block)", x) |
| 183 | case x := <-child.Done(): |
| 184 | t.Errorf("<-child.Done() == %v want nothing (it should block)", x) |
| 185 | default: |
| 186 | } |
| 187 | |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 188 | cc := child.(*cancelCtx) |
| 189 | pc, pcok := parent.(*cancelCtx) // pcok == false when parent == Background() |
| 190 | if p, ok := parentCancelCtx(cc.Context); ok != pcok || (ok && pc != p) { |
| 191 | t.Errorf("bad linkage: parentCancelCtx(cc.Context) = %v, %v want %v, %v", p, ok, pc, pcok) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 192 | } |
| 193 | |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 194 | if pcok { |
| 195 | pc.mu.Lock() |
| 196 | if len(pc.children) != 1 || !pc.children[cc] { |
| 197 | t.Errorf("bad linkage: pc.children = %v, cc = %v", pc.children, cc) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 198 | } |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 199 | pc.mu.Unlock() |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 200 | } |
| 201 | |
| 202 | cancel() |
| 203 | |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 204 | if pcok { |
| 205 | pc.mu.Lock() |
| 206 | if len(pc.children) != 0 { |
| 207 | t.Errorf("child's cancel didn't remove self from pc.children = %v", pc.children) |
| 208 | } |
| 209 | pc.mu.Unlock() |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 210 | } |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 211 | |
| 212 | // child should be finished. |
| 213 | select { |
| 214 | case <-child.Done(): |
| 215 | default: |
| 216 | t.Errorf("<-child.Done() blocked, but shouldn't have") |
| 217 | } |
| 218 | if e := child.Err(); e != Canceled { |
| 219 | t.Errorf("child.Err() == %v want %v", e, Canceled) |
| 220 | } |
| 221 | |
| 222 | // parent should not be finished. |
| 223 | select { |
| 224 | case x := <-parent.Done(): |
| 225 | t.Errorf("<-parent.Done() == %v want nothing (it should block)", x) |
| 226 | default: |
| 227 | } |
| 228 | if e := parent.Err(); e != nil { |
| 229 | t.Errorf("parent.Err() == %v want nil", e) |
| 230 | } |
| 231 | } |
| 232 | } |
| 233 | |
| 234 | func testDeadline(c Context, wait time.Duration, t *testing.T) { |
| 235 | select { |
| 236 | case <-time.After(wait): |
| 237 | t.Fatalf("context should have timed out") |
| 238 | case <-c.Done(): |
| 239 | } |
| 240 | if e := c.Err(); e != DeadlineExceeded { |
| 241 | t.Errorf("c.Err() == %v want %v", e, DeadlineExceeded) |
| 242 | } |
| 243 | } |
| 244 | |
| 245 | func TestDeadline(t *testing.T) { |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 246 | c, _ := WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) |
Sameer Ajmani | 9c40a72 | 2014-08-01 09:28:31 -0400 | [diff] [blame] | 247 | if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) { |
| 248 | t.Errorf("c.String() = %q want prefix %q", got, prefix) |
| 249 | } |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 250 | testDeadline(c, 200*time.Millisecond, t) |
| 251 | |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 252 | c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 253 | o := otherContext{c} |
| 254 | testDeadline(o, 200*time.Millisecond, t) |
| 255 | |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 256 | c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 257 | o = otherContext{c} |
| 258 | c, _ = WithDeadline(o, time.Now().Add(300*time.Millisecond)) |
| 259 | testDeadline(c, 200*time.Millisecond, t) |
| 260 | } |
| 261 | |
| 262 | func TestTimeout(t *testing.T) { |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 263 | c, _ := WithTimeout(Background(), 100*time.Millisecond) |
Sameer Ajmani | 9c40a72 | 2014-08-01 09:28:31 -0400 | [diff] [blame] | 264 | if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) { |
| 265 | t.Errorf("c.String() = %q want prefix %q", got, prefix) |
| 266 | } |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 267 | testDeadline(c, 200*time.Millisecond, t) |
| 268 | |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 269 | c, _ = WithTimeout(Background(), 100*time.Millisecond) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 270 | o := otherContext{c} |
| 271 | testDeadline(o, 200*time.Millisecond, t) |
| 272 | |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 273 | c, _ = WithTimeout(Background(), 100*time.Millisecond) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 274 | o = otherContext{c} |
| 275 | c, _ = WithTimeout(o, 300*time.Millisecond) |
| 276 | testDeadline(c, 200*time.Millisecond, t) |
| 277 | } |
| 278 | |
Sameer Ajmani | 9c40a72 | 2014-08-01 09:28:31 -0400 | [diff] [blame] | 279 | func TestCanceledTimeout(t *testing.T) { |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 280 | c, _ := WithTimeout(Background(), 200*time.Millisecond) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 281 | o := otherContext{c} |
| 282 | c, cancel := WithTimeout(o, 400*time.Millisecond) |
| 283 | cancel() |
Sameer Ajmani | 9c40a72 | 2014-08-01 09:28:31 -0400 | [diff] [blame] | 284 | time.Sleep(100 * time.Millisecond) // let cancelation propagate |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 285 | select { |
| 286 | case <-c.Done(): |
| 287 | default: |
| 288 | t.Errorf("<-c.Done() blocked, but shouldn't have") |
| 289 | } |
| 290 | if e := c.Err(); e != Canceled { |
| 291 | t.Errorf("c.Err() == %v want %v", e, Canceled) |
| 292 | } |
| 293 | } |
| 294 | |
Sameer Ajmani | bf13cf4 | 2014-07-10 14:57:25 -0400 | [diff] [blame] | 295 | type key1 int |
| 296 | type key2 int |
| 297 | |
| 298 | var k1 = key1(1) |
| 299 | var k2 = key2(1) // same int as k1, different type |
| 300 | var k3 = key2(3) // same type as k2, different int |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 301 | |
| 302 | func TestValues(t *testing.T) { |
Sameer Ajmani | bf13cf4 | 2014-07-10 14:57:25 -0400 | [diff] [blame] | 303 | check := func(c Context, nm, v1, v2, v3 string) { |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 304 | if v, ok := c.Value(k1).(string); ok == (len(v1) == 0) || v != v1 { |
| 305 | t.Errorf(`%s.Value(k1).(string) = %q, %t want %q, %t`, nm, v, ok, v1, len(v1) != 0) |
| 306 | } |
| 307 | if v, ok := c.Value(k2).(string); ok == (len(v2) == 0) || v != v2 { |
| 308 | t.Errorf(`%s.Value(k2).(string) = %q, %t want %q, %t`, nm, v, ok, v2, len(v2) != 0) |
| 309 | } |
Sameer Ajmani | bf13cf4 | 2014-07-10 14:57:25 -0400 | [diff] [blame] | 310 | if v, ok := c.Value(k3).(string); ok == (len(v3) == 0) || v != v3 { |
| 311 | t.Errorf(`%s.Value(k3).(string) = %q, %t want %q, %t`, nm, v, ok, v3, len(v3) != 0) |
| 312 | } |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 313 | } |
| 314 | |
| 315 | c0 := Background() |
Sameer Ajmani | bf13cf4 | 2014-07-10 14:57:25 -0400 | [diff] [blame] | 316 | check(c0, "c0", "", "", "") |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 317 | |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 318 | c1 := WithValue(Background(), k1, "c1k1") |
Sameer Ajmani | bf13cf4 | 2014-07-10 14:57:25 -0400 | [diff] [blame] | 319 | check(c1, "c1", "c1k1", "", "") |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 320 | |
Sameer Ajmani | 9c40a72 | 2014-08-01 09:28:31 -0400 | [diff] [blame] | 321 | if got, want := fmt.Sprint(c1), `context.Background.WithValue(1, "c1k1")`; got != want { |
| 322 | t.Errorf("c.String() = %q want %q", got, want) |
| 323 | } |
| 324 | |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 325 | c2 := WithValue(c1, k2, "c2k2") |
Sameer Ajmani | bf13cf4 | 2014-07-10 14:57:25 -0400 | [diff] [blame] | 326 | check(c2, "c2", "c1k1", "c2k2", "") |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 327 | |
Sameer Ajmani | bf13cf4 | 2014-07-10 14:57:25 -0400 | [diff] [blame] | 328 | c3 := WithValue(c2, k3, "c3k3") |
| 329 | check(c3, "c2", "c1k1", "c2k2", "c3k3") |
| 330 | |
| 331 | c4 := WithValue(c3, k1, nil) |
| 332 | check(c4, "c4", "", "c2k2", "c3k3") |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 333 | |
| 334 | o0 := otherContext{Background()} |
Sameer Ajmani | bf13cf4 | 2014-07-10 14:57:25 -0400 | [diff] [blame] | 335 | check(o0, "o0", "", "", "") |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 336 | |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 337 | o1 := otherContext{WithValue(Background(), k1, "c1k1")} |
Sameer Ajmani | bf13cf4 | 2014-07-10 14:57:25 -0400 | [diff] [blame] | 338 | check(o1, "o1", "c1k1", "", "") |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 339 | |
| 340 | o2 := WithValue(o1, k2, "o2k2") |
Sameer Ajmani | bf13cf4 | 2014-07-10 14:57:25 -0400 | [diff] [blame] | 341 | check(o2, "o2", "c1k1", "o2k2", "") |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 342 | |
Sameer Ajmani | bf13cf4 | 2014-07-10 14:57:25 -0400 | [diff] [blame] | 343 | o3 := otherContext{c4} |
| 344 | check(o3, "o3", "", "c2k2", "c3k3") |
| 345 | |
| 346 | o4 := WithValue(o3, k3, nil) |
| 347 | check(o4, "o4", "", "c2k2", "") |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 348 | } |
| 349 | |
| 350 | func TestAllocs(t *testing.T) { |
| 351 | bg := Background() |
| 352 | for _, test := range []struct { |
| 353 | desc string |
| 354 | f func() |
| 355 | limit float64 |
| 356 | gccgoLimit float64 |
| 357 | }{ |
| 358 | { |
| 359 | desc: "Background()", |
| 360 | f: func() { Background() }, |
| 361 | limit: 0, |
| 362 | gccgoLimit: 0, |
| 363 | }, |
| 364 | { |
| 365 | desc: fmt.Sprintf("WithValue(bg, %v, nil)", k1), |
| 366 | f: func() { |
| 367 | c := WithValue(bg, k1, nil) |
| 368 | c.Value(k1) |
| 369 | }, |
Sameer Ajmani | 3ffb8fd | 2014-10-09 13:51:22 -0400 | [diff] [blame] | 370 | limit: 3, |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 371 | gccgoLimit: 3, |
| 372 | }, |
| 373 | { |
Alex Brainman | 6b9623c | 2014-06-18 10:02:55 +1000 | [diff] [blame] | 374 | desc: "WithTimeout(bg, 15*time.Millisecond)", |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 375 | f: func() { |
Alex Brainman | 6b9623c | 2014-06-18 10:02:55 +1000 | [diff] [blame] | 376 | c, _ := WithTimeout(bg, 15*time.Millisecond) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 377 | <-c.Done() |
| 378 | }, |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 379 | limit: 8, |
David Symonds | fb93926 | 2016-04-13 08:48:50 +1000 | [diff] [blame] | 380 | gccgoLimit: 16, |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 381 | }, |
| 382 | { |
| 383 | desc: "WithCancel(bg)", |
| 384 | f: func() { |
| 385 | c, cancel := WithCancel(bg) |
| 386 | cancel() |
| 387 | <-c.Done() |
| 388 | }, |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 389 | limit: 5, |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 390 | gccgoLimit: 8, |
| 391 | }, |
| 392 | { |
| 393 | desc: "WithTimeout(bg, 100*time.Millisecond)", |
| 394 | f: func() { |
| 395 | c, cancel := WithTimeout(bg, 100*time.Millisecond) |
| 396 | cancel() |
| 397 | <-c.Done() |
| 398 | }, |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 399 | limit: 8, |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 400 | gccgoLimit: 25, |
| 401 | }, |
| 402 | } { |
| 403 | limit := test.limit |
| 404 | if runtime.Compiler == "gccgo" { |
| 405 | // gccgo does not yet do escape analysis. |
Ian Lance Taylor | 2a35e68 | 2016-05-06 12:47:26 -0700 | [diff] [blame] | 406 | // TODO(iant): Remove this when gccgo does do escape analysis. |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 407 | limit = test.gccgoLimit |
| 408 | } |
Alex Brainman | 6b9623c | 2014-06-18 10:02:55 +1000 | [diff] [blame] | 409 | if n := testing.AllocsPerRun(100, test.f); n > limit { |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 410 | t.Errorf("%s allocs = %f want %d", test.desc, n, int(limit)) |
| 411 | } |
| 412 | } |
| 413 | } |
| 414 | |
| 415 | func TestSimultaneousCancels(t *testing.T) { |
| 416 | root, cancel := WithCancel(Background()) |
| 417 | m := map[Context]CancelFunc{root: cancel} |
| 418 | q := []Context{root} |
| 419 | // Create a tree of contexts. |
| 420 | for len(q) != 0 && len(m) < 100 { |
| 421 | parent := q[0] |
| 422 | q = q[1:] |
| 423 | for i := 0; i < 4; i++ { |
| 424 | ctx, cancel := WithCancel(parent) |
| 425 | m[ctx] = cancel |
| 426 | q = append(q, ctx) |
| 427 | } |
| 428 | } |
| 429 | // Start all the cancels in a random order. |
| 430 | var wg sync.WaitGroup |
| 431 | wg.Add(len(m)) |
| 432 | for _, cancel := range m { |
| 433 | go func(cancel CancelFunc) { |
| 434 | cancel() |
| 435 | wg.Done() |
| 436 | }(cancel) |
| 437 | } |
| 438 | // Wait on all the contexts in a random order. |
| 439 | for ctx := range m { |
| 440 | select { |
| 441 | case <-ctx.Done(): |
| 442 | case <-time.After(1 * time.Second): |
| 443 | buf := make([]byte, 10<<10) |
| 444 | n := runtime.Stack(buf, true) |
| 445 | t.Fatalf("timed out waiting for <-ctx.Done(); stacks:\n%s", buf[:n]) |
| 446 | } |
| 447 | } |
| 448 | // Wait for all the cancel functions to return. |
| 449 | done := make(chan struct{}) |
| 450 | go func() { |
| 451 | wg.Wait() |
| 452 | close(done) |
| 453 | }() |
| 454 | select { |
| 455 | case <-done: |
| 456 | case <-time.After(1 * time.Second): |
| 457 | buf := make([]byte, 10<<10) |
| 458 | n := runtime.Stack(buf, true) |
| 459 | t.Fatalf("timed out waiting for cancel functions; stacks:\n%s", buf[:n]) |
| 460 | } |
| 461 | } |
| 462 | |
| 463 | func TestInterlockedCancels(t *testing.T) { |
| 464 | parent, cancelParent := WithCancel(Background()) |
| 465 | child, cancelChild := WithCancel(parent) |
| 466 | go func() { |
| 467 | parent.Done() |
| 468 | cancelChild() |
| 469 | }() |
| 470 | cancelParent() |
| 471 | select { |
| 472 | case <-child.Done(): |
| 473 | case <-time.After(1 * time.Second): |
| 474 | buf := make([]byte, 10<<10) |
| 475 | n := runtime.Stack(buf, true) |
| 476 | t.Fatalf("timed out waiting for child.Done(); stacks:\n%s", buf[:n]) |
| 477 | } |
| 478 | } |
Sameer Ajmani | a1f3609 | 2014-07-29 14:21:02 -0400 | [diff] [blame] | 479 | |
| 480 | func TestLayersCancel(t *testing.T) { |
| 481 | testLayers(t, time.Now().UnixNano(), false) |
| 482 | } |
| 483 | |
| 484 | func TestLayersTimeout(t *testing.T) { |
| 485 | testLayers(t, time.Now().UnixNano(), true) |
| 486 | } |
| 487 | |
| 488 | func testLayers(t *testing.T, seed int64, testTimeout bool) { |
| 489 | rand.Seed(seed) |
| 490 | errorf := func(format string, a ...interface{}) { |
| 491 | t.Errorf(fmt.Sprintf("seed=%d: %s", seed, format), a...) |
| 492 | } |
| 493 | const ( |
| 494 | timeout = 200 * time.Millisecond |
| 495 | minLayers = 30 |
| 496 | ) |
| 497 | type value int |
| 498 | var ( |
| 499 | vals []*value |
| 500 | cancels []CancelFunc |
| 501 | numTimers int |
| 502 | ctx = Background() |
| 503 | ) |
| 504 | for i := 0; i < minLayers || numTimers == 0 || len(cancels) == 0 || len(vals) == 0; i++ { |
| 505 | switch rand.Intn(3) { |
| 506 | case 0: |
| 507 | v := new(value) |
Sameer Ajmani | a1f3609 | 2014-07-29 14:21:02 -0400 | [diff] [blame] | 508 | ctx = WithValue(ctx, v, v) |
| 509 | vals = append(vals, v) |
| 510 | case 1: |
| 511 | var cancel CancelFunc |
Sameer Ajmani | a1f3609 | 2014-07-29 14:21:02 -0400 | [diff] [blame] | 512 | ctx, cancel = WithCancel(ctx) |
| 513 | cancels = append(cancels, cancel) |
| 514 | case 2: |
| 515 | var cancel CancelFunc |
Sameer Ajmani | a1f3609 | 2014-07-29 14:21:02 -0400 | [diff] [blame] | 516 | ctx, cancel = WithTimeout(ctx, timeout) |
| 517 | cancels = append(cancels, cancel) |
| 518 | numTimers++ |
| 519 | } |
| 520 | } |
| 521 | checkValues := func(when string) { |
| 522 | for _, key := range vals { |
| 523 | if val := ctx.Value(key).(*value); key != val { |
| 524 | errorf("%s: ctx.Value(%p) = %p want %p", when, key, val, key) |
| 525 | } |
| 526 | } |
| 527 | } |
| 528 | select { |
| 529 | case <-ctx.Done(): |
| 530 | errorf("ctx should not be canceled yet") |
| 531 | default: |
| 532 | } |
Sameer Ajmani | 9c40a72 | 2014-08-01 09:28:31 -0400 | [diff] [blame] | 533 | if s, prefix := fmt.Sprint(ctx), "context.Background."; !strings.HasPrefix(s, prefix) { |
| 534 | t.Errorf("ctx.String() = %q want prefix %q", s, prefix) |
| 535 | } |
| 536 | t.Log(ctx) |
Sameer Ajmani | a1f3609 | 2014-07-29 14:21:02 -0400 | [diff] [blame] | 537 | checkValues("before cancel") |
| 538 | if testTimeout { |
| 539 | select { |
| 540 | case <-ctx.Done(): |
Sameer Ajmani | 96feaeb | 2015-08-14 18:05:50 +0100 | [diff] [blame] | 541 | case <-time.After(timeout + 100*time.Millisecond): |
Sameer Ajmani | a1f3609 | 2014-07-29 14:21:02 -0400 | [diff] [blame] | 542 | errorf("ctx should have timed out") |
| 543 | } |
| 544 | checkValues("after timeout") |
| 545 | } else { |
| 546 | cancel := cancels[rand.Intn(len(cancels))] |
| 547 | cancel() |
| 548 | select { |
| 549 | case <-ctx.Done(): |
| 550 | default: |
| 551 | errorf("ctx should be canceled") |
| 552 | } |
| 553 | checkValues("after cancel") |
| 554 | } |
| 555 | } |
Damien Neil | f090b05 | 2015-02-04 12:47:08 -0800 | [diff] [blame] | 556 | |
| 557 | func TestCancelRemoves(t *testing.T) { |
| 558 | checkChildren := func(when string, ctx Context, want int) { |
| 559 | if got := len(ctx.(*cancelCtx).children); got != want { |
| 560 | t.Errorf("%s: context has %d children, want %d", when, got, want) |
| 561 | } |
| 562 | } |
| 563 | |
| 564 | ctx, _ := WithCancel(Background()) |
| 565 | checkChildren("after creation", ctx, 0) |
| 566 | _, cancel := WithCancel(ctx) |
| 567 | checkChildren("with WithCancel child ", ctx, 1) |
| 568 | cancel() |
| 569 | checkChildren("after cancelling WithCancel child", ctx, 0) |
| 570 | |
| 571 | ctx, _ = WithCancel(Background()) |
| 572 | checkChildren("after creation", ctx, 0) |
| 573 | _, cancel = WithTimeout(ctx, 60*time.Minute) |
| 574 | checkChildren("with WithTimeout child ", ctx, 1) |
| 575 | cancel() |
| 576 | checkChildren("after cancelling WithTimeout child", ctx, 0) |
| 577 | } |