Index: common/clock/clockcontext_test.go |
diff --git a/common/clock/clockcontext_test.go b/common/clock/clockcontext_test.go |
index c550a45e94bf29b40da99d6bc2d00b45992dff1d..c360d04c49d7918903794d8c710040794eee01fe 100644 |
--- a/common/clock/clockcontext_test.go |
+++ b/common/clock/clockcontext_test.go |
@@ -12,99 +12,117 @@ import ( |
"golang.org/x/net/context" |
) |
-// A test blocking operation. |
-func blockingOperation(ctx context.Context, releaseC chan struct{}) (bool, error) { |
- select { |
- case <-ctx.Done(): |
- return false, ctx.Err() |
- |
- case <-releaseC: |
- return true, nil |
- } |
+// manualClock is a partial Clock implementation that allows us to release |
+// blocking calls. |
+type manualClock struct { |
+ Clock |
+ |
+ now time.Time |
+ timeoutCallback func(time.Duration) bool |
+ testFinishedC chan struct{} |
} |
-func TestClockContext(t *testing.T) { |
- t.Parallel() |
+func (mc *manualClock) Now() time.Time { |
+ return mc.now |
+} |
- Convey(`A context with a deadline wrapping a cancellable parent`, t, func() { |
- now := time.Now() |
- cctx, pcf := context.WithCancel(context.Background()) |
- ctx, cf := WithTimeout(cctx, 10*time.Millisecond) |
- releaseC := make(chan struct{}) |
+func (mc *manualClock) After(ctx context.Context, d time.Duration) <-chan TimerResult { |
+ resultC := make(chan TimerResult) |
+ go func() { |
+ ar := TimerResult{} |
+ defer func() { |
+ resultC <- ar |
+ }() |
+ |
+ // If we are instructed to immediately timeout, do so. |
+ if cb := mc.timeoutCallback; cb != nil && cb(d) { |
+ return |
+ } |
+ |
+ select { |
+ case <-ctx.Done(): |
+ ar.Err = ctx.Err() |
+ case <-mc.testFinishedC: |
+ break |
+ } |
+ }() |
+ |
+ return resultC |
+} |
- Convey(`Successfully reports its deadline.`, func() { |
- deadline, ok := ctx.Deadline() |
- So(ok, ShouldBeTrue) |
- So(deadline.After(now), ShouldBeTrue) |
- }) |
+func wait(c context.Context) error { |
+ <-c.Done() |
+ return c.Err() |
+} |
- Convey(`Will successfully time out.`, func() { |
- released, err := blockingOperation(ctx, releaseC) |
- So(released, ShouldBeFalse) |
- So(err, ShouldEqual, context.DeadlineExceeded) |
- }) |
+func TestClockContext(t *testing.T) { |
+ t.Parallel() |
- Convey(`Will successfully cancel with its cancel func.`, func() { |
- go func() { |
- cf() |
- }() |
- released, err := blockingOperation(ctx, releaseC) |
- So(released, ShouldBeFalse) |
- So(err, ShouldEqual, context.Canceled) |
+ Convey(`A manual testing clock`, t, func() { |
+ mc := manualClock{ |
+ now: time.Date(2016, 1, 1, 0, 0, 0, 0, time.Local), |
+ testFinishedC: make(chan struct{}), |
+ } |
+ defer close(mc.testFinishedC) |
+ |
+ Convey(`A context with a deadline wrapping a cancellable parent`, func() { |
+ cctx, pcf := context.WithCancel(Set(context.Background(), &mc)) |
+ ctx, cf := WithTimeout(cctx, 10*time.Millisecond) |
+ |
+ Convey(`Successfully reports its deadline.`, func() { |
+ deadline, ok := ctx.Deadline() |
+ So(ok, ShouldBeTrue) |
+ So(deadline.After(mc.now), ShouldBeTrue) |
+ }) |
+ |
+ Convey(`Will successfully time out.`, func() { |
+ mc.timeoutCallback = func(time.Duration) bool { |
+ return true |
+ } |
+ So(wait(ctx), ShouldEqual, context.DeadlineExceeded) |
+ }) |
+ |
+ Convey(`Will successfully cancel with its cancel func.`, func() { |
+ go func() { |
+ cf() |
+ }() |
+ So(wait(ctx), ShouldEqual, context.Canceled) |
+ }) |
+ |
+ Convey(`Will successfully cancel if the parent is canceled.`, func() { |
+ go func() { |
+ pcf() |
+ }() |
+ So(wait(ctx), ShouldEqual, context.Canceled) |
+ }) |
}) |
- Convey(`Will successfully cancel if the parent is cancelled.`, func() { |
- go func() { |
- pcf() |
- }() |
- released, err := blockingOperation(ctx, releaseC) |
- So(released, ShouldBeFalse) |
- So(err, ShouldEqual, context.Canceled) |
- }) |
+ Convey(`A context with a deadline wrapping a parent with a shorter deadline`, func() { |
+ cctx, _ := context.WithTimeout(context.Background(), 10*time.Millisecond) |
+ ctx, cf := WithTimeout(cctx, 1*time.Hour) |
- Convey(`Will release before the deadline.`, func() { |
- go func() { |
- close(releaseC) |
- }() |
- released, err := blockingOperation(ctx, releaseC) |
- So(released, ShouldBeTrue) |
- So(err, ShouldBeNil) |
- }) |
- }) |
+ Convey(`Will successfully time out.`, func() { |
+ mc.timeoutCallback = func(d time.Duration) bool { |
+ return d == 10*time.Millisecond |
+ } |
- Convey(`A context with a deadline wrapping a parent with a shorter deadline`, t, func() { |
- cctx, _ := context.WithTimeout(context.Background(), 10*time.Millisecond) |
- ctx, cf := WithTimeout(cctx, 1*time.Hour) |
- releaseC := make(chan struct{}) |
+ So(wait(ctx), ShouldEqual, context.DeadlineExceeded) |
+ }) |
- Convey(`Will successfully time out.`, func() { |
- released, err := blockingOperation(ctx, releaseC) |
- So(released, ShouldBeFalse) |
- So(err, ShouldEqual, context.DeadlineExceeded) |
+ Convey(`Will successfully cancel with its cancel func.`, func() { |
+ go func() { |
+ cf() |
+ }() |
+ So(wait(ctx), ShouldEqual, context.Canceled) |
+ }) |
}) |
- Convey(`Will successfully cancel with its cancel func.`, func() { |
- go func() { |
- cf() |
- }() |
- released, err := blockingOperation(ctx, releaseC) |
- So(released, ShouldBeFalse) |
- So(err, ShouldEqual, context.Canceled) |
- }) |
- }) |
+ Convey(`A context with a deadline in the past`, func() { |
+ ctx, _ := WithDeadline(context.Background(), mc.now.Add(-time.Second)) |
- Convey(`A context with a deadline in the past`, t, func() { |
- ctx, _ := WithDeadline(context.Background(), time.Unix(0, 0)) |
- releaseC := make(chan struct{}) |
- |
- Convey(`Will time out immediately.`, func() { |
- go func() { |
- defer close(releaseC) |
- time.Sleep(50 * time.Millisecond) |
- }() |
- released, err := blockingOperation(ctx, releaseC) |
- So(released, ShouldBeFalse) |
- So(err, ShouldEqual, context.DeadlineExceeded) |
+ Convey(`Will time out immediately.`, func() { |
+ So(wait(ctx), ShouldEqual, context.DeadlineExceeded) |
+ }) |
}) |
}) |
} |