78 lines
1.7 KiB
Go
78 lines
1.7 KiB
Go
|
package invoices
|
||
|
|
||
|
import (
|
||
|
"sync"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
// testClock can be used in tests to mock time.
|
||
|
type testClock struct {
|
||
|
currentTime time.Time
|
||
|
timeChanMap map[time.Time][]chan time.Time
|
||
|
timeLock sync.Mutex
|
||
|
}
|
||
|
|
||
|
// newTestClock returns a new test clock.
|
||
|
func newTestClock(startTime time.Time) *testClock {
|
||
|
return &testClock{
|
||
|
currentTime: startTime,
|
||
|
timeChanMap: make(map[time.Time][]chan time.Time),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// now returns the current (test) time.
|
||
|
func (c *testClock) now() time.Time {
|
||
|
c.timeLock.Lock()
|
||
|
defer c.timeLock.Unlock()
|
||
|
|
||
|
return c.currentTime
|
||
|
}
|
||
|
|
||
|
// tickAfter returns a channel that will receive a tick at the specified time.
|
||
|
func (c *testClock) tickAfter(duration time.Duration) <-chan time.Time {
|
||
|
c.timeLock.Lock()
|
||
|
defer c.timeLock.Unlock()
|
||
|
|
||
|
triggerTime := c.currentTime.Add(duration)
|
||
|
log.Debugf("tickAfter called: duration=%v, trigger_time=%v",
|
||
|
duration, triggerTime)
|
||
|
|
||
|
ch := make(chan time.Time, 1)
|
||
|
|
||
|
// If already expired, tick immediately.
|
||
|
if !triggerTime.After(c.currentTime) {
|
||
|
ch <- c.currentTime
|
||
|
return ch
|
||
|
}
|
||
|
|
||
|
// Otherwise store the channel until the trigger time is there.
|
||
|
chans := c.timeChanMap[triggerTime]
|
||
|
chans = append(chans, ch)
|
||
|
c.timeChanMap[triggerTime] = chans
|
||
|
|
||
|
return ch
|
||
|
}
|
||
|
|
||
|
// setTime sets the (test) time and triggers tick channels when they expire.
|
||
|
func (c *testClock) setTime(now time.Time) {
|
||
|
c.timeLock.Lock()
|
||
|
defer c.timeLock.Unlock()
|
||
|
|
||
|
c.currentTime = now
|
||
|
remainingChans := make(map[time.Time][]chan time.Time)
|
||
|
for triggerTime, chans := range c.timeChanMap {
|
||
|
// If the trigger time is still in the future, keep this channel
|
||
|
// in the channel map for later.
|
||
|
if triggerTime.After(now) {
|
||
|
remainingChans[triggerTime] = chans
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
for _, c := range chans {
|
||
|
c <- now
|
||
|
}
|
||
|
}
|
||
|
|
||
|
c.timeChanMap = remainingChans
|
||
|
}
|