OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package clock | 5 package clock |
6 | 6 |
7 import ( | 7 import ( |
8 "testing" | 8 "testing" |
9 "time" | 9 "time" |
10 | 10 |
11 . "github.com/smartystreets/goconvey/convey" | 11 . "github.com/smartystreets/goconvey/convey" |
12 "golang.org/x/net/context" | 12 "golang.org/x/net/context" |
13 ) | 13 ) |
14 | 14 |
15 // A test blocking operation. | 15 // manualClock is a partial Clock implementation that allows us to release |
16 func blockingOperation(ctx context.Context, releaseC chan struct{}) (bool, error
) { | 16 // blocking calls. |
17 » select { | 17 type manualClock struct { |
18 » case <-ctx.Done(): | 18 » Clock |
19 » » return false, ctx.Err() | |
20 | 19 |
21 » case <-releaseC: | 20 » now time.Time |
22 » » return true, nil | 21 » timeoutCallback func(time.Duration) bool |
23 » } | 22 » testFinishedC chan struct{} |
| 23 } |
| 24 |
| 25 func (mc *manualClock) Now() time.Time { |
| 26 » return mc.now |
| 27 } |
| 28 |
| 29 func (mc *manualClock) After(ctx context.Context, d time.Duration) <-chan TimerR
esult { |
| 30 » resultC := make(chan TimerResult) |
| 31 » go func() { |
| 32 » » ar := TimerResult{} |
| 33 » » defer func() { |
| 34 » » » resultC <- ar |
| 35 » » }() |
| 36 |
| 37 » » // If we are instructed to immediately timeout, do so. |
| 38 » » if cb := mc.timeoutCallback; cb != nil && cb(d) { |
| 39 » » » return |
| 40 » » } |
| 41 |
| 42 » » select { |
| 43 » » case <-ctx.Done(): |
| 44 » » » ar.Err = ctx.Err() |
| 45 » » case <-mc.testFinishedC: |
| 46 » » » break |
| 47 » » } |
| 48 » }() |
| 49 |
| 50 » return resultC |
| 51 } |
| 52 |
| 53 func wait(c context.Context) error { |
| 54 » <-c.Done() |
| 55 » return c.Err() |
24 } | 56 } |
25 | 57 |
26 func TestClockContext(t *testing.T) { | 58 func TestClockContext(t *testing.T) { |
27 t.Parallel() | 59 t.Parallel() |
28 | 60 |
29 » Convey(`A context with a deadline wrapping a cancellable parent`, t, fun
c() { | 61 » Convey(`A manual testing clock`, t, func() { |
30 » » now := time.Now() | 62 » » mc := manualClock{ |
31 » » cctx, pcf := context.WithCancel(context.Background()) | 63 » » » now: time.Date(2016, 1, 1, 0, 0, 0, 0, time.Lo
cal), |
32 » » ctx, cf := WithTimeout(cctx, 10*time.Millisecond) | 64 » » » testFinishedC: make(chan struct{}), |
33 » » releaseC := make(chan struct{}) | 65 » » } |
| 66 » » defer close(mc.testFinishedC) |
34 | 67 |
35 » » Convey(`Successfully reports its deadline.`, func() { | 68 » » Convey(`A context with a deadline wrapping a cancellable parent`
, func() { |
36 » » » deadline, ok := ctx.Deadline() | 69 » » » cctx, pcf := context.WithCancel(Set(context.Background()
, &mc)) |
37 » » » So(ok, ShouldBeTrue) | 70 » » » ctx, cf := WithTimeout(cctx, 10*time.Millisecond) |
38 » » » So(deadline.After(now), ShouldBeTrue) | 71 |
| 72 » » » Convey(`Successfully reports its deadline.`, func() { |
| 73 » » » » deadline, ok := ctx.Deadline() |
| 74 » » » » So(ok, ShouldBeTrue) |
| 75 » » » » So(deadline.After(mc.now), ShouldBeTrue) |
| 76 » » » }) |
| 77 |
| 78 » » » Convey(`Will successfully time out.`, func() { |
| 79 » » » » mc.timeoutCallback = func(time.Duration) bool { |
| 80 » » » » » return true |
| 81 » » » » } |
| 82 » » » » So(wait(ctx), ShouldEqual, context.DeadlineExcee
ded) |
| 83 » » » }) |
| 84 |
| 85 » » » Convey(`Will successfully cancel with its cancel func.`,
func() { |
| 86 » » » » go func() { |
| 87 » » » » » cf() |
| 88 » » » » }() |
| 89 » » » » So(wait(ctx), ShouldEqual, context.Canceled) |
| 90 » » » }) |
| 91 |
| 92 » » » Convey(`Will successfully cancel if the parent is cancel
ed.`, func() { |
| 93 » » » » go func() { |
| 94 » » » » » pcf() |
| 95 » » » » }() |
| 96 » » » » So(wait(ctx), ShouldEqual, context.Canceled) |
| 97 » » » }) |
39 }) | 98 }) |
40 | 99 |
41 » » Convey(`Will successfully time out.`, func() { | 100 » » Convey(`A context with a deadline wrapping a parent with a short
er deadline`, func() { |
42 » » » released, err := blockingOperation(ctx, releaseC) | 101 » » » cctx, _ := context.WithTimeout(context.Background(), 10*
time.Millisecond) |
43 » » » So(released, ShouldBeFalse) | 102 » » » ctx, cf := WithTimeout(cctx, 1*time.Hour) |
44 » » » So(err, ShouldEqual, context.DeadlineExceeded) | 103 |
| 104 » » » Convey(`Will successfully time out.`, func() { |
| 105 » » » » mc.timeoutCallback = func(d time.Duration) bool
{ |
| 106 » » » » » return d == 10*time.Millisecond |
| 107 » » » » } |
| 108 |
| 109 » » » » So(wait(ctx), ShouldEqual, context.DeadlineExcee
ded) |
| 110 » » » }) |
| 111 |
| 112 » » » Convey(`Will successfully cancel with its cancel func.`,
func() { |
| 113 » » » » go func() { |
| 114 » » » » » cf() |
| 115 » » » » }() |
| 116 » » » » So(wait(ctx), ShouldEqual, context.Canceled) |
| 117 » » » }) |
45 }) | 118 }) |
46 | 119 |
47 » » Convey(`Will successfully cancel with its cancel func.`, func()
{ | 120 » » Convey(`A context with a deadline in the past`, func() { |
48 » » » go func() { | 121 » » » ctx, _ := WithDeadline(context.Background(), mc.now.Add(
-time.Second)) |
49 » » » » cf() | |
50 » » » }() | |
51 » » » released, err := blockingOperation(ctx, releaseC) | |
52 » » » So(released, ShouldBeFalse) | |
53 » » » So(err, ShouldEqual, context.Canceled) | |
54 » » }) | |
55 | 122 |
56 » » Convey(`Will successfully cancel if the parent is cancelled.`, f
unc() { | 123 » » » Convey(`Will time out immediately.`, func() { |
57 » » » go func() { | 124 » » » » So(wait(ctx), ShouldEqual, context.DeadlineExcee
ded) |
58 » » » » pcf() | 125 » » » }) |
59 » » » }() | |
60 » » » released, err := blockingOperation(ctx, releaseC) | |
61 » » » So(released, ShouldBeFalse) | |
62 » » » So(err, ShouldEqual, context.Canceled) | |
63 » » }) | |
64 | |
65 » » Convey(`Will release before the deadline.`, func() { | |
66 » » » go func() { | |
67 » » » » close(releaseC) | |
68 » » » }() | |
69 » » » released, err := blockingOperation(ctx, releaseC) | |
70 » » » So(released, ShouldBeTrue) | |
71 » » » So(err, ShouldBeNil) | |
72 » » }) | |
73 » }) | |
74 | |
75 » Convey(`A context with a deadline wrapping a parent with a shorter deadl
ine`, t, func() { | |
76 » » cctx, _ := context.WithTimeout(context.Background(), 10*time.Mil
lisecond) | |
77 » » ctx, cf := WithTimeout(cctx, 1*time.Hour) | |
78 » » releaseC := make(chan struct{}) | |
79 | |
80 » » Convey(`Will successfully time out.`, func() { | |
81 » » » released, err := blockingOperation(ctx, releaseC) | |
82 » » » So(released, ShouldBeFalse) | |
83 » » » So(err, ShouldEqual, context.DeadlineExceeded) | |
84 » » }) | |
85 | |
86 » » Convey(`Will successfully cancel with its cancel func.`, func()
{ | |
87 » » » go func() { | |
88 » » » » cf() | |
89 » » » }() | |
90 » » » released, err := blockingOperation(ctx, releaseC) | |
91 » » » So(released, ShouldBeFalse) | |
92 » » » So(err, ShouldEqual, context.Canceled) | |
93 » » }) | |
94 » }) | |
95 | |
96 » Convey(`A context with a deadline in the past`, t, func() { | |
97 » » ctx, _ := WithDeadline(context.Background(), time.Unix(0, 0)) | |
98 » » releaseC := make(chan struct{}) | |
99 | |
100 » » Convey(`Will time out immediately.`, func() { | |
101 » » » go func() { | |
102 » » » » defer close(releaseC) | |
103 » » » » time.Sleep(50 * time.Millisecond) | |
104 » » » }() | |
105 » » » released, err := blockingOperation(ctx, releaseC) | |
106 » » » So(released, ShouldBeFalse) | |
107 » » » So(err, ShouldEqual, context.DeadlineExceeded) | |
108 }) | 126 }) |
109 }) | 127 }) |
110 } | 128 } |
OLD | NEW |