Documentation ¶
Index ¶
- Variables
- func AssertEventCount(tb testing.TB, timeApi *FakeTimeApi, count int)
- func ContextName(c context.Context) string
- func DurationString(d time.Duration) string
- func FakeOptions() fakeOptions
- type FakeTicker
- type FakeTimeApi
- func (t *FakeTimeApi) AddEvent(event string)
- func (t *FakeTimeApi) After(d time.Duration) <-chan time.Time
- func (t *FakeTimeApi) AfterFunc(d time.Duration, f func()) *Timer
- func (t *FakeTimeApi) AppendEvents(events []string) []string
- func (t *FakeTimeApi) AppendTickProducerNames(names []string) []string
- func (t *FakeTimeApi) Gosched()
- func (t *FakeTimeApi) IncrementClock(d time.Duration) *FakeTimeApi
- func (t *FakeTimeApi) NewTicker(d time.Duration) *Ticker
- func (t *FakeTimeApi) NewTimer(d time.Duration) *Timer
- func (t *FakeTimeApi) Now() time.Time
- func (t *FakeTimeApi) SetOptions(opts fakeOptions) *FakeTimeApi
- func (t *FakeTimeApi) Since(tm time.Time) time.Duration
- func (t *FakeTimeApi) Sleep(d time.Duration)
- func (t *FakeTimeApi) Start(tm time.Time) *FakeTimeApi
- func (t *FakeTimeApi) Stop() *FakeTimeApi
- func (t *FakeTimeApi) Tick(d time.Duration) <-chan time.Time
- func (t *FakeTimeApi) TickProducerCount() int
- func (t *FakeTimeApi) Until(tm time.Time) time.Duration
- func (t *FakeTimeApi) WithDeadline(ctx context.Context, tm time.Time) (context.Context, context.CancelFunc)
- func (t *FakeTimeApi) WithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc)
- type FakeTimer
- type IntervalFuncTicker
- type RealTimeApi
- func (t *RealTimeApi) After(d time.Duration) <-chan time.Time
- func (t *RealTimeApi) AfterFunc(d time.Duration, f func()) *Timer
- func (t *RealTimeApi) Gosched()
- func (t *RealTimeApi) NewTicker(d time.Duration) *Ticker
- func (t *RealTimeApi) NewTimer(d time.Duration) *Timer
- func (t *RealTimeApi) Now() time.Time
- func (t *RealTimeApi) Since(tm time.Time) time.Duration
- func (t *RealTimeApi) Sleep(d time.Duration)
- func (t *RealTimeApi) Tick(d time.Duration) <-chan time.Time
- func (t *RealTimeApi) Until(tm time.Time) time.Duration
- func (t *RealTimeApi) WithDeadline(ctx context.Context, tm time.Time) (context.Context, context.CancelFunc)
- func (t *RealTimeApi) WithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc)
- type Ticker
- type TickerAdapter
- type TimeApi
- type Timer
- type TimerAdapter
- type TimerProvider
Constants ¶
This section is empty.
Variables ¶
var MaxDuration = 1<<63 - 1
UnixNano only works within ±292 years around 1970 (between 1678 and 2262). Also, since maximum duration is ~292 years, even those two will give a clamped result on Sub
var MaxTime = MinTime.Add(1<<63 - 1)
var MinTime = time.Unix(-2208988800, 0) // Jan 1, 1900
Functions ¶
func AssertEventCount ¶
func AssertEventCount(tb testing.TB, timeApi *FakeTimeApi, count int)
assert the time event log is the size you expect, else spam the test output with the event log.
func ContextName ¶
helper function, get the name of a context via the public String() method thats not in the interface
func DurationString ¶
convert time durations into something easier to read 2h, 2m, 2s, 2ms, 2us, or 2ns for example
func FakeOptions ¶
func FakeOptions() fakeOptions
Types ¶
type FakeTicker ¶
type FakeTicker struct { C <-chan time.Time // The channel on which the ticks are delivered. // contains filtered or unexported fields }
fake time api implementation of ticker
func (*FakeTicker) Reset ¶
func (t *FakeTicker) Reset(d time.Duration)
Reset stops a ticker and resets its period to the specified duration. The next tick will arrive after the new period elapses. The duration d must be greater than zero; if not, Reset will panic.
func (*FakeTicker) Stop ¶
func (t *FakeTicker) Stop()
Stop turns off a ticker. After Stop, no more ticks will be sent. Stop does not close the channel, to prevent a concurrent goroutine reading from the channel from seeing an erroneous "tick".
type FakeTimeApi ¶
type FakeTimeApi struct {
// contains filtered or unexported fields
}
the fake time api struct
func NewFake ¶
func NewFake(opts ...fakeOptions) *FakeTimeApi
NewFake returns test fake so your tests can control the when and how much time advances
func WithFakeTime ¶
func WithFakeTime(startTime time.Time, fn func(timeApi *FakeTimeApi)) *FakeTimeApi
convenience wrapper to generate a fake and start it
func (*FakeTimeApi) AddEvent ¶
func (t *FakeTimeApi) AddEvent(event string)
add a user event to the fakes time event log Use like:
fakeTimeApi, ok := timeapi.(*FakeTimeApi) if ok { fakeTimeApi.AddEvent(" My Thingy Ran!") }
func (*FakeTimeApi) AfterFunc ¶
func (t *FakeTimeApi) AfterFunc(d time.Duration, f func()) *Timer
The real AfterFunc waits for the duration to elapse and then calls f in its own goroutine. It returns a Timer that can be used to cancel the call using its Stop method. This version just calls it from whichever goroutine is running the AdvanceClock operation
func (*FakeTimeApi) AppendEvents ¶
func (t *FakeTimeApi) AppendEvents(events []string) []string
a copy of the timeapi's event log
func (*FakeTimeApi) AppendTickProducerNames ¶
func (t *FakeTimeApi) AppendTickProducerNames(names []string) []string
Get list of names for active tick producers Inactive tick producers are only reaped whenever you advance or stop the clock
func (*FakeTimeApi) Gosched ¶
func (t *FakeTimeApi) Gosched()
gosched typically takes about 45-60ns to run on my machine, but sometimes 0ns presumably depending on core task queue size as a guess, the fake advances the clock 60ns when you call its fake version. This does not cause a sleep, only a call to gosched
func (*FakeTimeApi) IncrementClock ¶
func (t *FakeTimeApi) IncrementClock(d time.Duration) *FakeTimeApi
Make time pass
func (*FakeTimeApi) Now ¶
func (t *FakeTimeApi) Now() time.Time
func (*FakeTimeApi) SetOptions ¶
func (t *FakeTimeApi) SetOptions(opts fakeOptions) *FakeTimeApi
if you reuse your fake across many tests, sometimses you need to change the options
func (*FakeTimeApi) Sleep ¶
func (t *FakeTimeApi) Sleep(d time.Duration)
WARNING! If you sleep concurrently the clock will advance non-deterministically, in the event you sleep less than 2ms, the clock will advance 2ms to match real world sleep behavior on windows (ie you sleep at least the requested amount, bounded by clock granularity)
func (*FakeTimeApi) Start ¶
func (t *FakeTimeApi) Start(tm time.Time) *FakeTimeApi
uses start/stop pattern so it can track resource leaks
func (*FakeTimeApi) TickProducerCount ¶
func (t *FakeTimeApi) TickProducerCount() int
Get the count of active tick producers. Inactive tick producers are only reaped whenever you advance or stop the clock
func (*FakeTimeApi) WithDeadline ¶
func (t *FakeTimeApi) WithDeadline(ctx context.Context, tm time.Time) (context.Context, context.CancelFunc)
func (*FakeTimeApi) WithTimeout ¶
func (t *FakeTimeApi) WithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc)
type FakeTimer ¶
type FakeTimer struct { C <-chan time.Time // The channel on which the ticks are delivered. Public readonly alias // contains filtered or unexported fields }
fake time api implementation of timer
func (*FakeTimer) Reset ¶
Reset changes the timer to expire after duration d. It returns true if the timer had been active, false if the timer had expired or been stopped.
For a Timer created with NewTimer, Reset should be invoked only on stopped or expired timers with drained channels.
If a program has already received a value from t.C, the timer is known to have expired and the channel drained, so t.Reset can be used directly. If a program has not yet received a value from t.C, however, the timer must be stopped and—if Stop reports that the timer expired before being stopped—the channel explicitly drained:
if !t.Stop() { <-t.C } t.Reset(d)
This should not be done concurrent to other receives from the Timer's channel.
Note that it is not possible to use Reset's return value correctly, as there is a race condition between draining the channel and the new timer expiring. Reset should always be invoked on stopped or expired channels, as described above. The return value exists to preserve compatibility with existing programs.
For a Timer created with AfterFunc(d, f), Reset either reschedules when f will run, in which case Reset returns true, or schedules f to run again, in which case it returns false. When Reset returns false, Reset neither waits for the prior f to complete before returning nor does it guarantee that the subsequent goroutine running f does not run concurrently with the prior one. If the caller needs to know whether the prior execution of f is completed, it must coordinate with f explicitly.
func (*FakeTimer) Stop ¶
Stop prevents the Timer from firing. It returns true if the call stops the timer, false if the timer has already expired or been stopped. Stop does not close the channel, to prevent a read from the channel succeeding incorrectly.
To ensure the channel is empty after a call to Stop, check the return value and drain the channel. For example, assuming the program has not received from t.C already:
if !t.Stop() { <-t.C }
This cannot be done concurrent to other receives from the Timer's channel or other calls to the Timer's Stop method.
For a timer created with AfterFunc(d, f), if t.Stop returns false, then the timer has already expired and the function f has been started in its own goroutine; Stop does not wait for f to complete before returning. If the caller needs to know whether f is completed, it must coordinate with f explicitly.
type IntervalFuncTicker ¶
type IntervalFuncTicker struct {
// contains filtered or unexported fields
}
IntervalFuncTicker, should probably be called a TickerFunc
func (*IntervalFuncTicker) ClearInterval ¶
func (t *IntervalFuncTicker) ClearInterval() *IntervalFuncTicker
Stop the ticker func from executing in a race safe way.
func (*IntervalFuncTicker) Count ¶
func (t *IntervalFuncTicker) Count() int
get the number of times the callback function has been invoked
func (*IntervalFuncTicker) WaitUntilCount ¶
func (t *IntervalFuncTicker) WaitUntilCount(count int) (sleepCount int)
spin with timeapi.Sleep until the invocation count is as specified returns the # of 1ms sleeps it waited.
type RealTimeApi ¶
type RealTimeApi struct {
// contains filtered or unexported fields
}
The real time api struct, wraps the system time/context/goshed functions
func New ¶
func New() *RealTimeApi
New returns an instance of a real-time clock. This is a simple wrapper around the time.* functions in the go standard library
func (*RealTimeApi) After ¶
func (t *RealTimeApi) After(d time.Duration) <-chan time.Time
After waits for the duration to elapse and then sends the current time on the returned channel. It is equivalent to NewTimer(d).C. The underlying Timer is not recovered by the garbage collector until the timer fires. If efficiency is a concern, use NewTimer instead and call Timer.Stop if the timer is no longer needed.
func (*RealTimeApi) AfterFunc ¶
func (t *RealTimeApi) AfterFunc(d time.Duration, f func()) *Timer
AfterFunc waits for the duration to elapse and then calls f in its own goroutine. It returns a Timer that can be used to cancel the call using its Stop method.
func (*RealTimeApi) Gosched ¶
func (t *RealTimeApi) Gosched()
Gosched yields the processor, allowing other goroutines to run. It does not suspend the current goroutine, so execution resumes automatically. Note gosched typically takes about 45-60ns to run on my machine
func (*RealTimeApi) NewTicker ¶
func (t *RealTimeApi) NewTicker(d time.Duration) *Ticker
NewTicker returns a new Ticker containing a channel that will send the current time on the channel after each tick. The period of the ticks is specified by the duration argument. The ticker will adjust the time interval or drop ticks to make up for slow receivers. The duration d must be greater than zero; if not, NewTicker will panic. Stop the ticker to release associated resources.
func (*RealTimeApi) NewTimer ¶
func (t *RealTimeApi) NewTimer(d time.Duration) *Timer
NewTimer creates a new Timer that will send the current time on its channel after at least duration d.
func (*RealTimeApi) Since ¶
func (t *RealTimeApi) Since(tm time.Time) time.Duration
Since returns the time elapsed since t. It is shorthand for time.Now().Sub(t).
func (*RealTimeApi) Sleep ¶
func (t *RealTimeApi) Sleep(d time.Duration)
Sleep pauses the current goroutine for at least the duration d. A negative or zero duration causes Sleep to return immediately. Note on windows golang maximum sleep resolution is roughly 2ms, any amount < than that will sleep for that long
func (*RealTimeApi) Tick ¶
func (t *RealTimeApi) Tick(d time.Duration) <-chan time.Time
Tick is a convenience wrapper for NewTicker providing access to the ticking channel only. While Tick is useful for clients that have no need to shut down the Ticker, be aware that without a way to shut it down the underlying Ticker cannot be recovered by the garbage collector; it "leaks". Unlike NewTicker, Tick will return nil if d <= 0. Wise programmers should avoid this abomination like the plague
func (*RealTimeApi) Until ¶
func (t *RealTimeApi) Until(tm time.Time) time.Duration
Until returns the duration until t. It is shorthand for t.Sub(time.Now()).
func (*RealTimeApi) WithDeadline ¶
func (t *RealTimeApi) WithDeadline(ctx context.Context, tm time.Time) (context.Context, context.CancelFunc)
WithDeadline returns a copy of the parent context with the deadline adjusted to be no later than d. If the parent's deadline is already earlier than d, WithDeadline(parent, d) is semantically equivalent to parent. The returned context's Done channel is closed when the deadline expires, when the returned cancel function is called, or when the parent context's Done channel is closed, whichever happens first.
Canceling this context releases resources associated with it, so code should call cancel as soon as the operations running in this Context complete.
func (*RealTimeApi) WithTimeout ¶
func (t *RealTimeApi) WithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc)
WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
Canceling this context releases resources associated with it, so code should call cancel as soon as the operations running in this Context complete:
func slowOperationWithTimeout(ctx context.Context) (Result, error) { ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) defer cancel() // releases resources if slowOperation completes before timeout elapses return slowOperation(ctx) }
type Ticker ¶
type Ticker struct { C <-chan time.Time // republished channel from the internal adapter // contains filtered or unexported fields }
Cant put a channel datamember in an interface, so we need an adapter to wrap and make the channel accessible has the comments from time.Ticker on its methods so your intellesence looks the same
type TickerAdapter ¶
the interface to a ticker, real or fake
type TimeApi ¶
type TimeApi interface { After(d time.Duration) <-chan time.Time AfterFunc(d time.Duration, f func()) *Timer Now() time.Time Since(t time.Time) time.Duration Until(t time.Time) time.Duration Sleep(d time.Duration) Gosched() Tick(d time.Duration) <-chan time.Time NewTicker(d time.Duration) *Ticker NewTimer(d time.Duration) *Timer WithDeadline(parent context.Context, d time.Time) (context.Context, context.CancelFunc) WithTimeout(parent context.Context, t time.Duration) (context.Context, context.CancelFunc) }
Inspired by github.com/benbjohnson/clock wrap all the global time methods, plus gosched, and the context timeouts
type Timer ¶
type Timer struct { C <-chan time.Time // republished channel from the internal adapter // contains filtered or unexported fields }
Cant put a channel datamember in an interface, so we need an adapter to wrap and make the channel accessible has the comments from time.Timer on its methods so your intellesence looks the same
func (*Timer) Reset ¶
Reset should be invoked only on stopped or expired timers with drained channels.
If a program has already received a value from t.C, the timer is known to have expired and the channel drained, so t.Reset can be used directly. If a program has not yet received a value from t.C, however, the timer must be stopped and—if Stop reports that the timer expired before being stopped—the channel explicitly drained:
if !t.Stop() { <-t.C } t.Reset(d)
func (*Timer) Stop ¶
Stop prevents the Timer from firing. It returns true if the call stops the timer, false if the timer has already expired or been stopped. Stop does not close the channel, to prevent a read from the channel succeeding incorrectly.
To ensure the channel is empty after a call to Stop, check the return value and drain the channel. For example, assuming the program has not received from t.C already:
if !t.Stop() { <-t.C }
This cannot be done concurrent to other receives from the Timer's channel or other calls to the Timer's Stop method.
For a timer created with AfterFunc(d, f), if t.Stop returns false, then the timer has already expired and the function f has been started in its own goroutine; Stop does not wait for f to complete before returning. If the caller needs to know whether f is completed, it must coordinate with f explicitly.
type TimerAdapter ¶
the interface to a timer, real or fake
type TimerProvider ¶
type TimerProvider struct {
// contains filtered or unexported fields
}
Convenience wrapper for making tickers. Hmm, should probably have called it a TickerFuncProvider
func NewTimerProvider ¶
func NewTimerProvider(timeApi TimeApi) (*TimerProvider, error)
func (*TimerProvider) SetInterval ¶
func (t *TimerProvider) SetInterval(fn func(tm time.Time), interval time.Duration) *IntervalFuncTicker
creates a ticker, and applies a mutex so that the func(tm time.Time) you pass will be invoked every heart beat it can only run one at time (it will serialize around its internal mutex) stopping it is likewise safe via mutex