lnd/clock/test_clock.go
Andras Banki-Horvath 7024f36a76 general: adding the Clock interface to aid testing
This commit adds Clock and DefaultClock and moves the private
invoices.testClock under the clock package while adding basic
unit tests for it.
Clock is an interface currently encapsulating Now() and TickAfter().
It can be added as an external dependency to any class. This way
tests can stub out time.Now() or time.After().

The DefaultClock class simply returns the real time.Now() and
time.After().
2019-12-13 16:52:22 +01:00

76 lines
1.7 KiB
Go

package clock
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 after the specified
// duration has passed passed by the user set test time.
func (c *TestClock) TickAfter(duration time.Duration) <-chan time.Time {
c.timeLock.Lock()
defer c.timeLock.Unlock()
triggerTime := c.currentTime.Add(duration)
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
}