Source File
fakeclock.go
Belonging Package
go.pact.im/x/clock/fakeclock
// Package fakeclock provides support for testing users of a clock.
package fakeclock
import (
)
var _ interface {
clock.Scheduler
clock.NowScheduler
clock.TimerScheduler
clock.TickerScheduler
} = (*Clock)(nil)
// moment represents a scheduled event.
type moment interface {
// next returns the duration until the next occurrence of this event, or
// false if it is the last event.
next(now time.Time) (time.Duration, bool)
}
// Clock is a fake clock.Scheduler interface implementation that is safe for
// concurrent use by multiple goroutines.
//
// Use Next, Set, Add and AddDate methods to change clock time. Advancing the
// time triggers scheduled events, timers and tickers.
//
// Note that the order in which events scheduled for the same time are triggered
// is undefined, but it is guaranteed that all events that are not after the new
// current time are triggered on clock time change (even if old time is equal to
// the next time value).
//
// The zero Clock defaults to zero time and is ready for use.
type Clock struct {
mu sync.Mutex
now time.Time
sched map[moment]time.Time
}
// Unix returns a clock set to the Unix epoch time. That is, it is set to
// 1970-01-01 00:00:00 UTC.
func () *Clock {
return Time(time.Unix(0, 0))
}
// Go returns a clock set to the Go initial release date. That is, it is set to
// 2009-11-10 23:00:00 UTC.
func () *Clock {
return Time(time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC))
}
// Y2038 returns a clock set to d duration after the Year 2038 problem time.
// That is, it is set to the given duration after 2038-01-19 03:14:07 UTC, the
// latest time that can be properly encoded as a 32-bit integer that is a number
// of seconds after the Unix epoch.
func ( time.Duration) *Clock {
:= time.Unix(math.MaxInt32, 0)
return Time(.Add())
}
// Time returns a clock set to the given now time.
func ( time.Time) *Clock {
return &Clock{
now: ,
sched: map[moment]time.Time{},
}
}
// Now returns the current clock time.
func ( *Clock) () time.Time {
.mu.Lock()
defer .mu.Unlock()
return .now
}
// Next advances the time to the next timer or ticker event and returns the new
// current time. If there are events in the past, the time is not changed. It
// returns false if there are no scheduled events. Otherwise it returns true and
// runs at least one scheduled event.
func ( *Clock) () (time.Time, bool) {
.mu.Lock()
defer .mu.Unlock()
:= len(.sched) > 0
.now = .next(.now)
.advance(.now)
return .now,
}
// Set sets the given time to be the current clock time. It is possible to set
// t that is before the current clock time.
func ( *Clock) ( time.Time) {
.mu.Lock()
defer .mu.Unlock()
.now =
.advance(.now)
}
// Add adds the given duration to the current time and returns the resulting
// clock time. It is possible to add a negative duration.
//
// It is safe for concurrent use and is a shorthand for
// now := c.Now().Add(d)
// c.Set(now)
func ( *Clock) ( time.Duration) time.Time {
.mu.Lock()
defer .mu.Unlock()
.now = .now.Add()
.advance(.now)
return .now
}
// AddDate adds the duration corresponding to the given number of years, months
// and days relative to the current time and returns the resulting clock time.
// It is possible to add negative values.
//
// It is safe for concurrent use and is a shorthand for
// now := c.Now().AddDate(years, months, days)
// c.Set(now)
func ( *Clock) (, , int) time.Time {
.mu.Lock()
defer .mu.Unlock()
.now = .now.AddDate(, , )
.advance(.now)
return .now
}
// Schedule implements the clock.Scheduler interface.
func ( *Clock) ( time.Duration, func( time.Time)) clock.Event {
:= &event{
c: ,
f: ,
}
.reset(, , nil)
return
}
// Timer implements the clock.TimerScheduler interface.
func ( *Clock) ( time.Duration) clock.Timer {
:= &timer{
c: ,
ch: make(chan time.Time, 1),
}
.reset(, , nil)
return
}
// Ticker implements the clock.TickerScheduler interface. Note that the returned
// ticker does not adjust the time interval or drop ticks to make up for slow
// receivers.
func ( *Clock) ( time.Duration) clock.Ticker {
if <= 0 {
panic("non-positive interval for Ticker")
}
:= &ticker{
c: ,
ch: make(chan time.Time, 1),
}
.reset(, , &.d)
return
}
// stop removes the given moment from the set of scheduled events. It returns
// true if stop prevented the event from firing. Note that stop acquires the
// underlying lock.
func ( *Clock) ( moment) bool {
.mu.Lock()
defer .mu.Unlock()
, := .sched[]
delete(.sched, )
return
}
// reset resets the given moment to run d duration after the current time.
// Note that reset acquires the underlying lock. Reset returns true if it
// rescheduled an event.
func ( *Clock) ( moment, time.Duration, *time.Duration) bool {
.mu.Lock()
defer .mu.Unlock()
if != nil {
* =
}
return .schedule(, .now.Add())
}
// schedule schedules the given moment to run on next clock advance. It returns
// true if an event was rescheduled.
func ( *Clock) ( moment, time.Time) bool {
if .sched == nil {
.sched = map[moment]time.Time{}
}
, := .sched[]
.sched[] =
return
}
// next returns the time of the next scheduled event. It returns the given now
// time if there are no events or some of them are in the past.
func ( *Clock) ( time.Time) time.Time {
if len(.sched) == 0 {
return
}
var time.Time
for , := range .sched {
=
break
}
for , := range .sched {
if !.After() {
return
}
if .After() {
continue
}
=
}
return
}
// advance runs the scheduled events for the current clock time.
func ( *Clock) ( time.Time) {
for , := range .sched {
if .After() {
continue
}
, := .next()
if ! {
delete(.sched, )
continue
}
_ = .schedule(, .Add())
}
}
// event implements the moment and clock.Event interfaces.
type event struct {
c *Clock
f func(time.Time)
}
// Stop implements the clock.Event interface.
func ( *event) () bool {
return .c.stop()
}
// Reset implements the clock.Event interface.
func ( *event) ( time.Duration) bool {
return .c.reset(, , nil)
}
// next implements the moment interface.
func ( *event) ( time.Time) (time.Duration, bool) {
go .f()
return 0, false
}
// timer implements the moment and clock.Timer interfaces.
type timer struct {
c *Clock
ch chan time.Time
}
// C implements the clock.Timer interface.
func ( *timer) () <-chan time.Time {
return .ch
}
// Stop implements the clock.Timer interface.
func ( *timer) () bool {
return .c.stop()
}
// Reset implements the clock.Timer interface.
func ( *timer) ( time.Duration) {
_ = .c.reset(, , nil)
}
// next implements the moment interface.
func ( *timer) ( time.Time) (time.Duration, bool) {
select {
case .ch <- :
default:
}
return 0, false
}
// ticker implements the moment and clock.Ticker interfaces.
type ticker struct {
c *Clock
ch chan time.Time
d time.Duration
}
// C implements the clock.Ticker interface.
func ( *ticker) () <-chan time.Time {
return .ch
}
// Stop implements the clock.Ticker interface.
func ( *ticker) () {
_ = .c.stop()
}
// Reset implements the clock.Ticker interface.
func ( *ticker) ( time.Duration) {
if <= 0 {
panic("non-positive interval for Ticker.Reset")
}
_ = .c.reset(, , &.d)
}
// next implements the moment interface.
func ( *ticker) ( time.Time) (time.Duration, bool) {
select {
case .ch <- :
default:
}
return .d, true
}
The pages are generated with Golds v0.4.9. (GOOS=linux GOARCH=amd64)