| 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 |