| Index: go/src/infra/libs/clock/testclock/testclock.go
|
| diff --git a/go/src/infra/libs/clock/testclock/testclock.go b/go/src/infra/libs/clock/testclock/testclock.go
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..cfeacedfbe257f4b422ba65b1b58f7b7ad4560bf
|
| --- /dev/null
|
| +++ b/go/src/infra/libs/clock/testclock/testclock.go
|
| @@ -0,0 +1,113 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +package testclock
|
| +
|
| +import (
|
| + "sync"
|
| + "time"
|
| +
|
| + "golang.org/x/net/context"
|
| + "infra/libs/clock"
|
| +)
|
| +
|
| +// TestClock is a Clock interface with additional methods to help instrument it.
|
| +type TestClock interface {
|
| + clock.Clock
|
| + SetNow(time.Time)
|
| + SetTimerCallback(TimerCallback)
|
| +}
|
| +
|
| +// TimerCallback that can be invoked when a timer has been set. This is useful for
|
| +// sychronizing state when testing.
|
| +type TimerCallback func(clock.Timer)
|
| +
|
| +// testClock is a test-oriented implementation of the 'Clock' interface.
|
| +//
|
| +// This implementation's Clock responses are configurable by modifying its member
|
| +// variables. Time-based events are explicitly triggered by sending on a Timer
|
| +// instance's channel.
|
| +type testClock struct {
|
| + sync.RWMutex
|
| +
|
| + now time.Time // The current clock time.
|
| + timerCond *sync.Cond // Condition used to manage timer blocking.
|
| +
|
| + timerCallback TimerCallback // Optional callback when a timer has been set.
|
| +}
|
| +
|
| +var _ TestClock = (*testClock)(nil)
|
| +
|
| +// New returns a TestClock instance set at the specified time.
|
| +func New(now time.Time) TestClock {
|
| + c := testClock{
|
| + now: now,
|
| + }
|
| + c.timerCond = sync.NewCond(&c)
|
| + return &c
|
| +}
|
| +
|
| +func (c *testClock) Now() time.Time {
|
| + c.RLock()
|
| + defer c.RUnlock()
|
| +
|
| + return c.now
|
| +}
|
| +
|
| +func (c *testClock) Sleep(d time.Duration) {
|
| + <-c.After(d)
|
| +}
|
| +
|
| +func (c *testClock) NewTimer() clock.Timer {
|
| + return newTimer(c)
|
| +}
|
| +
|
| +func (c *testClock) After(d time.Duration) <-chan time.Time {
|
| + t := c.NewTimer()
|
| + t.Reset(d)
|
| + return t.GetC()
|
| +}
|
| +
|
| +func (c *testClock) SetNow(t time.Time) {
|
| + c.Lock()
|
| + defer c.Unlock()
|
| +
|
| + c.now = t
|
| +
|
| + // Unblock any blocking timers that are waiting on our lock.
|
| + go c.pokeTimers()
|
| +}
|
| +
|
| +func (c *testClock) SetTimerCallback(callback TimerCallback) {
|
| + c.Lock()
|
| + defer c.Unlock()
|
| +
|
| + c.timerCallback = callback
|
| +}
|
| +
|
| +func (c *testClock) getTimerCallback() TimerCallback {
|
| + c.Lock()
|
| + defer c.Unlock()
|
| +
|
| + return c.timerCallback
|
| +}
|
| +
|
| +func (c *testClock) signalTimerSet(t clock.Timer) {
|
| + callback := c.getTimerCallback()
|
| + if callback != nil {
|
| + callback(t)
|
| + }
|
| +}
|
| +
|
| +func (c *testClock) pokeTimers() {
|
| + c.Lock()
|
| + defer c.Unlock()
|
| +
|
| + c.timerCond.Broadcast()
|
| +}
|
| +
|
| +// Set creates a new Context using the supplied clock instance.
|
| +func Set(ctx context.Context, now time.Time) context.Context {
|
| + return clock.SetClock(ctx, New(now))
|
| +}
|
|
|