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 | |
| 5 | package context |
| 6 | |
| 7 | import ( |
| 8 | "fmt" |
Sameer Ajmani | a1f3609 | 2014-07-29 14:21:02 -0400 | [diff] [blame] | 9 | "math/rand" |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 10 | "runtime" |
Sameer Ajmani | 9c40a72 | 2014-08-01 09:28:31 -0400 | [diff] [blame] | 11 | "strings" |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 12 | "sync" |
| 13 | "testing" |
| 14 | "time" |
| 15 | ) |
| 16 | |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 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. |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 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 | } |
Sameer Ajmani | 9c40a72 | 2014-08-01 09:28:31 -0400 | [diff] [blame] | 34 | if got, want := fmt.Sprint(c), "context.Background"; got != want { |
| 35 | t.Errorf("Background().String() = %q want %q", got, want) |
Sameer Ajmani | a1f3609 | 2014-07-29 14:21:02 -0400 | [diff] [blame] | 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 | } |
Sameer Ajmani | 9c40a72 | 2014-08-01 09:28:31 -0400 | [diff] [blame] | 49 | if got, want := fmt.Sprint(c), "context.TODO"; got != want { |
| 50 | t.Errorf("TODO().String() = %q want %q", got, want) |
Sameer Ajmani | a1f3609 | 2014-07-29 14:21:02 -0400 | [diff] [blame] | 51 | } |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 52 | } |
| 53 | |
| 54 | func TestWithCancel(t *testing.T) { |
| 55 | c1, cancel := WithCancel(Background()) |
Sameer Ajmani | 9c40a72 | 2014-08-01 09:28:31 -0400 | [diff] [blame] | 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 | |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 61 | o := otherContext{c1} |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 62 | c2, _ := WithCancel(o) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 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() |
Sameer Ajmani | 9c40a72 | 2014-08-01 09:28:31 -0400 | [diff] [blame] | 81 | time.Sleep(100 * time.Millisecond) // let cancelation propagate |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 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) { |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 96 | // Context tree: |
| 97 | // parent -> cancelChild |
| 98 | // parent -> valueChild -> timerChild |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 99 | parent, cancel := WithCancel(Background()) |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 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() |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 105 | |
| 106 | select { |
| 107 | case x := <-parent.Done(): |
| 108 | t.Errorf("<-parent.Done() == %v want nothing (it should block)", x) |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 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) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 115 | default: |
| 116 | } |
| 117 | |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 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) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 126 | } |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 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 | } |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 135 | |
| 136 | cancel() |
| 137 | |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 138 | pc.mu.Lock() |
| 139 | if len(pc.children) != 0 { |
| 140 | t.Errorf("pc.cancel didn't clear pc.children = %v", pc.children) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 141 | } |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 142 | pc.mu.Unlock() |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 143 | |
| 144 | // parent and children should all be finished. |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 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 | } |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 154 | } |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 155 | check(parent, "parent") |
| 156 | check(cancelChild, "cancelChild") |
| 157 | check(valueChild, "valueChild") |
| 158 | check(timerChild, "timerChild") |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 159 | |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 160 | // WithCancel should return a canceled context on a canceled parent. |
| 161 | precanceledChild := WithValue(parent, "key", "value") |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 162 | select { |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 163 | case <-precanceledChild.Done(): |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 164 | default: |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 165 | t.Errorf("<-precanceledChild.Done() blocked, but shouldn't have") |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 166 | } |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 167 | if e := precanceledChild.Err(); e != Canceled { |
| 168 | t.Errorf("precanceledChild.Err() == %v want %v", e, Canceled) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 169 | } |
| 170 | } |
| 171 | |
| 172 | func TestChildFinishesFirst(t *testing.T) { |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 173 | cancelable, stop := WithCancel(Background()) |
| 174 | defer stop() |
| 175 | for _, parent := range []Context{Background(), cancelable} { |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 176 | child, cancel := WithCancel(parent) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 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 | |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 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) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 190 | } |
| 191 | |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 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) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 196 | } |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 197 | pc.mu.Unlock() |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 198 | } |
| 199 | |
| 200 | cancel() |
| 201 | |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 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() |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 208 | } |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 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, 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 | |
| 243 | func TestDeadline(t *testing.T) { |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 244 | c, _ := WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) |
Sameer Ajmani | 9c40a72 | 2014-08-01 09:28:31 -0400 | [diff] [blame] | 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 | } |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 248 | testDeadline(c, 200*time.Millisecond, t) |
| 249 | |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 250 | c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 251 | o := otherContext{c} |
| 252 | testDeadline(o, 200*time.Millisecond, t) |
| 253 | |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 254 | c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 255 | o = otherContext{c} |
| 256 | c, _ = WithDeadline(o, time.Now().Add(300*time.Millisecond)) |
| 257 | testDeadline(c, 200*time.Millisecond, t) |
| 258 | } |
| 259 | |
| 260 | func TestTimeout(t *testing.T) { |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 261 | c, _ := WithTimeout(Background(), 100*time.Millisecond) |
Sameer Ajmani | 9c40a72 | 2014-08-01 09:28:31 -0400 | [diff] [blame] | 262 | 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 Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 265 | testDeadline(c, 200*time.Millisecond, t) |
| 266 | |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 267 | c, _ = WithTimeout(Background(), 100*time.Millisecond) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 268 | o := otherContext{c} |
| 269 | testDeadline(o, 200*time.Millisecond, t) |
| 270 | |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 271 | c, _ = WithTimeout(Background(), 100*time.Millisecond) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 272 | o = otherContext{c} |
| 273 | c, _ = WithTimeout(o, 300*time.Millisecond) |
| 274 | testDeadline(c, 200*time.Millisecond, t) |
| 275 | } |
| 276 | |
Sameer Ajmani | 9c40a72 | 2014-08-01 09:28:31 -0400 | [diff] [blame] | 277 | func TestCanceledTimeout(t *testing.T) { |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 278 | c, _ := WithTimeout(Background(), 200*time.Millisecond) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 279 | o := otherContext{c} |
| 280 | c, cancel := WithTimeout(o, 400*time.Millisecond) |
| 281 | cancel() |
Sameer Ajmani | 9c40a72 | 2014-08-01 09:28:31 -0400 | [diff] [blame] | 282 | time.Sleep(100 * time.Millisecond) // let cancelation propagate |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 283 | 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 Ajmani | bf13cf4 | 2014-07-10 14:57:25 -0400 | [diff] [blame] | 293 | type key1 int |
| 294 | type key2 int |
| 295 | |
| 296 | var k1 = key1(1) |
| 297 | var k2 = key2(1) // same int as k1, different type |
| 298 | var k3 = key2(3) // same type as k2, different int |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 299 | |
| 300 | func TestValues(t *testing.T) { |
Sameer Ajmani | bf13cf4 | 2014-07-10 14:57:25 -0400 | [diff] [blame] | 301 | check := func(c Context, nm, v1, v2, v3 string) { |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 302 | 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 Ajmani | bf13cf4 | 2014-07-10 14:57:25 -0400 | [diff] [blame] | 308 | 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 Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 311 | } |
| 312 | |
| 313 | c0 := Background() |
Sameer Ajmani | bf13cf4 | 2014-07-10 14:57:25 -0400 | [diff] [blame] | 314 | check(c0, "c0", "", "", "") |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 315 | |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 316 | c1 := WithValue(Background(), k1, "c1k1") |
Sameer Ajmani | bf13cf4 | 2014-07-10 14:57:25 -0400 | [diff] [blame] | 317 | check(c1, "c1", "c1k1", "", "") |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 318 | |
Sameer Ajmani | 9c40a72 | 2014-08-01 09:28:31 -0400 | [diff] [blame] | 319 | 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 Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 323 | c2 := WithValue(c1, k2, "c2k2") |
Sameer Ajmani | bf13cf4 | 2014-07-10 14:57:25 -0400 | [diff] [blame] | 324 | check(c2, "c2", "c1k1", "c2k2", "") |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 325 | |
Sameer Ajmani | bf13cf4 | 2014-07-10 14:57:25 -0400 | [diff] [blame] | 326 | 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 Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 331 | |
| 332 | o0 := otherContext{Background()} |
Sameer Ajmani | bf13cf4 | 2014-07-10 14:57:25 -0400 | [diff] [blame] | 333 | check(o0, "o0", "", "", "") |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 334 | |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 335 | o1 := otherContext{WithValue(Background(), k1, "c1k1")} |
Sameer Ajmani | bf13cf4 | 2014-07-10 14:57:25 -0400 | [diff] [blame] | 336 | check(o1, "o1", "c1k1", "", "") |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 337 | |
| 338 | o2 := WithValue(o1, k2, "o2k2") |
Sameer Ajmani | bf13cf4 | 2014-07-10 14:57:25 -0400 | [diff] [blame] | 339 | check(o2, "o2", "c1k1", "o2k2", "") |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 340 | |
Sameer Ajmani | bf13cf4 | 2014-07-10 14:57:25 -0400 | [diff] [blame] | 341 | o3 := otherContext{c4} |
| 342 | check(o3, "o3", "", "c2k2", "c3k3") |
| 343 | |
| 344 | o4 := WithValue(o3, k3, nil) |
| 345 | check(o4, "o4", "", "c2k2", "") |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 346 | } |
| 347 | |
| 348 | func 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 Ajmani | 3ffb8fd | 2014-10-09 13:51:22 -0400 | [diff] [blame] | 368 | limit: 3, |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 369 | gccgoLimit: 3, |
| 370 | }, |
| 371 | { |
Alex Brainman | 6b9623c | 2014-06-18 10:02:55 +1000 | [diff] [blame] | 372 | desc: "WithTimeout(bg, 15*time.Millisecond)", |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 373 | f: func() { |
Alex Brainman | 6b9623c | 2014-06-18 10:02:55 +1000 | [diff] [blame] | 374 | c, _ := WithTimeout(bg, 15*time.Millisecond) |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 375 | <-c.Done() |
| 376 | }, |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 377 | limit: 8, |
David Symonds | 76c2dd9 | 2015-08-04 10:14:46 +1000 | [diff] [blame] | 378 | gccgoLimit: 15, |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 379 | }, |
| 380 | { |
| 381 | desc: "WithCancel(bg)", |
| 382 | f: func() { |
| 383 | c, cancel := WithCancel(bg) |
| 384 | cancel() |
| 385 | <-c.Done() |
| 386 | }, |
Sameer Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 387 | limit: 5, |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 388 | 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 Ajmani | a67aa99 | 2014-07-30 12:14:59 -0400 | [diff] [blame] | 397 | limit: 8, |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 398 | 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 Brainman | 6b9623c | 2014-06-18 10:02:55 +1000 | [diff] [blame] | 407 | if n := testing.AllocsPerRun(100, test.f); n > limit { |
Sameer Ajmani | f572974 | 2014-05-16 16:11:29 -0400 | [diff] [blame] | 408 | t.Errorf("%s allocs = %f want %d", test.desc, n, int(limit)) |
| 409 | } |
| 410 | } |
| 411 | } |
| 412 | |
| 413 | func 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 | |
| 461 | func 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 Ajmani | a1f3609 | 2014-07-29 14:21:02 -0400 | [diff] [blame] | 477 | |
| 478 | func TestLayersCancel(t *testing.T) { |
| 479 | testLayers(t, time.Now().UnixNano(), false) |
| 480 | } |
| 481 | |
| 482 | func TestLayersTimeout(t *testing.T) { |
| 483 | testLayers(t, time.Now().UnixNano(), true) |
| 484 | } |
| 485 | |
| 486 | func 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 Ajmani | a1f3609 | 2014-07-29 14:21:02 -0400 | [diff] [blame] | 506 | ctx = WithValue(ctx, v, v) |
| 507 | vals = append(vals, v) |
| 508 | case 1: |
| 509 | var cancel CancelFunc |
Sameer Ajmani | a1f3609 | 2014-07-29 14:21:02 -0400 | [diff] [blame] | 510 | ctx, cancel = WithCancel(ctx) |
| 511 | cancels = append(cancels, cancel) |
| 512 | case 2: |
| 513 | var cancel CancelFunc |
Sameer Ajmani | a1f3609 | 2014-07-29 14:21:02 -0400 | [diff] [blame] | 514 | 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 Ajmani | 9c40a72 | 2014-08-01 09:28:31 -0400 | [diff] [blame] | 531 | 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 Ajmani | a1f3609 | 2014-07-29 14:21:02 -0400 | [diff] [blame] | 535 | checkValues("before cancel") |
| 536 | if testTimeout { |
| 537 | select { |
| 538 | case <-ctx.Done(): |
Sameer Ajmani | 96feaeb | 2015-08-14 18:05:50 +0100 | [diff] [blame] | 539 | case <-time.After(timeout + 100*time.Millisecond): |
Sameer Ajmani | a1f3609 | 2014-07-29 14:21:02 -0400 | [diff] [blame] | 540 | 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 Neil | f090b05 | 2015-02-04 12:47:08 -0800 | [diff] [blame] | 554 | |
| 555 | func 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 | } |