From 56958493fe0bc8477fe8bdd9c44b30d421ea3315 Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Mon, 9 Dec 2019 13:28:05 +0100 Subject: [PATCH] invoices/test: add test clock --- invoices/clock_test.go | 77 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 invoices/clock_test.go diff --git a/invoices/clock_test.go b/invoices/clock_test.go new file mode 100644 index 000000000..41dd49912 --- /dev/null +++ b/invoices/clock_test.go @@ -0,0 +1,77 @@ +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 +}