Skip to content

Commit

Permalink
Merge pull request #8 from hpidcock/time-dilation
Browse files Browse the repository at this point in the history
Add DilatedWallClock for testclock.
  • Loading branch information
hpidcock authored Jul 4, 2022
2 parents d9deb86 + e7620ba commit a2b96c8
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 0 deletions.
70 changes: 70 additions & 0 deletions testclock/dilated.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright 2022 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.

package testclock

import (
"time"

"github.com/juju/clock"
)

// NewDilatedWallClock returns a clock that can be sped up or slowed down.
// realSecondDuration is the real duration of a second.
func NewDilatedWallClock(realSecondDuration time.Duration) clock.Clock {
return &dilationClock{
epoch: time.Now(),
realSecondDuration: realSecondDuration,
}
}

type dilationClock struct {
epoch time.Time
realSecondDuration time.Duration
}

// Now is part of the Clock interface.
func (dc *dilationClock) Now() time.Time {
now := time.Now()
return dc.epoch.Add(time.Duration(float64(now.Sub(dc.epoch)) / dc.realSecondDuration.Seconds()))
}

// After implements Clock.After.
func (dc *dilationClock) After(d time.Duration) <-chan time.Time {
return time.After(time.Duration(float64(d) * dc.realSecondDuration.Seconds()))
}

// AfterFunc implements Clock.AfterFunc.
func (dc *dilationClock) AfterFunc(d time.Duration, f func()) clock.Timer {
return dilatedWallTimer{
timer: time.AfterFunc(time.Duration(float64(d)*dc.realSecondDuration.Seconds()), f),
realSecondDuration: dc.realSecondDuration,
}
}

// NewTimer implements Clock.NewTimer.
func (dc *dilationClock) NewTimer(d time.Duration) clock.Timer {
return dilatedWallTimer{
timer: time.NewTimer(time.Duration(float64(d) * dc.realSecondDuration.Seconds())),
realSecondDuration: dc.realSecondDuration,
}
}

// dilatedWallTimer implements the Timer interface.
type dilatedWallTimer struct {
timer *time.Timer
realSecondDuration time.Duration
}

// Chan implements Timer.Chan.
func (t dilatedWallTimer) Chan() <-chan time.Time {
return t.timer.C
}

func (t dilatedWallTimer) Reset(d time.Duration) bool {
return t.timer.Reset(time.Duration(float64(d) * t.realSecondDuration.Seconds()))
}

func (t dilatedWallTimer) Stop() bool {
return t.timer.Stop()
}
85 changes: 85 additions & 0 deletions testclock/dilated_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright 2022 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.

package testclock_test

import (
"sync"
"time"

"github.com/juju/testing"
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"

"github.com/juju/clock/testclock"
)

type dilatedClockSuite struct {
testing.LoggingSuite
}

var _ = gc.Suite(&dilatedClockSuite{})

func (*dilatedClockSuite) TestSlowedAfter(c *gc.C) {
t0 := time.Now()
cl := testclock.NewDilatedWallClock(2 * time.Second)
t1 := <-cl.After(time.Second)
c.Assert(t1.Sub(t0).Seconds(), jc.GreaterThan, 1.9)
}

func (*dilatedClockSuite) TestFastAfter(c *gc.C) {
t0 := time.Now()
cl := testclock.NewDilatedWallClock(500 * time.Millisecond)
t1 := <-cl.After(time.Second)
c.Assert(t1.Sub(t0).Milliseconds(), jc.LessThan, 600)
}

func (*dilatedClockSuite) TestSlowedAfterFunc(c *gc.C) {
t0 := time.Now()
cl := testclock.NewDilatedWallClock(2 * time.Second)
mut := sync.Mutex{}
mut.Lock()
cl.AfterFunc(time.Second, func() {
defer mut.Unlock()
c.Check(time.Since(t0).Seconds(), jc.GreaterThan, 1.9)
})
mut.Lock()
}

func (*dilatedClockSuite) TestFastAfterFunc(c *gc.C) {
t0 := time.Now()
cl := testclock.NewDilatedWallClock(500 * time.Millisecond)
mut := sync.Mutex{}
mut.Lock()
cl.AfterFunc(time.Second, func() {
defer mut.Unlock()
c.Check(time.Since(t0).Milliseconds(), jc.LessThan, 600)
})
mut.Lock()
}

func (*dilatedClockSuite) TestSlowedNow(c *gc.C) {
t0 := time.Now()
cl := testclock.NewDilatedWallClock(2 * time.Second)
<-time.After(time.Second)
t2 := cl.Now()
c.Assert(t2.Sub(t0).Milliseconds(), jc.GreaterThan, 400)
c.Assert(t2.Sub(t0).Milliseconds(), jc.LessThan, 600)
<-time.After(time.Second)
t3 := cl.Now()
c.Assert(t3.Sub(t0).Milliseconds(), jc.GreaterThan, 900)
c.Assert(t3.Sub(t0).Milliseconds(), jc.LessThan, 1100)
}

func (*dilatedClockSuite) TestFastNow(c *gc.C) {
t0 := time.Now()
cl := testclock.NewDilatedWallClock(500 * time.Millisecond)
<-time.After(time.Second)
t2 := cl.Now()
c.Assert(t2.Sub(t0).Milliseconds(), jc.GreaterThan, 1900)
c.Assert(t2.Sub(t0).Milliseconds(), jc.LessThan, 2100)
<-time.After(time.Second)
t3 := cl.Now()
c.Assert(t3.Sub(t0).Milliseconds(), jc.GreaterThan, 3900)
c.Assert(t3.Sub(t0).Milliseconds(), jc.LessThan, 4100)
}

0 comments on commit a2b96c8

Please sign in to comment.