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