diff --git a/testclock/clock_test.go b/testclock/clock_test.go index c3ff4c8..df453b8 100644 --- a/testclock/clock_test.go +++ b/testclock/clock_test.go @@ -5,7 +5,6 @@ package testclock_test import ( "sync" - gotesting "testing" "time" "github.com/juju/loggo" @@ -20,10 +19,6 @@ type clockSuite struct { testing.LoggingSuite } -func TestAll(t *gotesting.T) { - gc.TestingT(t) -} - var _ = gc.Suite(&clockSuite{}) func (*clockSuite) TestNow(c *gc.C) { @@ -32,11 +27,6 @@ func (*clockSuite) TestNow(c *gc.C) { c.Assert(cl.Now(), gc.Equals, t0) } -var ( - shortWait = 50 * time.Millisecond - longWait = time.Second -) - func (*clockSuite) TestAdvanceLogs(c *gc.C) { loggo.GetLogger("juju.clock").SetLogLevel(loggo.DEBUG) t0 := time.Now() diff --git a/testclock/dilated.go b/testclock/dilated.go index 81ce201..930829a 100644 --- a/testclock/dilated.go +++ b/testclock/dilated.go @@ -213,6 +213,9 @@ func (t *dilatedWallTimer) Stop() bool { func dilateTime(epoch, realNow time.Time, realSecondDuration, dilatedOffset time.Duration) time.Time { - return epoch.Add(dilatedOffset). - Add(time.Duration(float64(realNow.Sub(epoch)) / realSecondDuration.Seconds())) + delta := realNow.Sub(epoch) + if delta < 0 { + delta = time.Duration(0) + } + return epoch.Add(dilatedOffset).Add(time.Duration(float64(delta) / realSecondDuration.Seconds())) } diff --git a/testclock/dilated_test.go b/testclock/dilated_test.go index b498961..399991e 100644 --- a/testclock/dilated_test.go +++ b/testclock/dilated_test.go @@ -15,6 +15,11 @@ import ( "github.com/juju/clock/testclock" ) +const ( + halfSecond = 500 * time.Millisecond + doubleSecond = 2 * time.Second +) + type dilatedClockSuite struct { testing.LoggingSuite } @@ -23,21 +28,21 @@ var _ = gc.Suite(&dilatedClockSuite{}) func (*dilatedClockSuite) TestSlowedAfter(c *gc.C) { t0 := time.Now() - cl := testclock.NewDilatedWallClock(2 * time.Second) + cl := testclock.NewDilatedWallClock(doubleSecond) 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) + cl := testclock.NewDilatedWallClock(halfSecond) 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) + cl := testclock.NewDilatedWallClock(doubleSecond) mut := sync.Mutex{} mut.Lock() cl.AfterFunc(time.Second, func() { @@ -49,7 +54,7 @@ func (*dilatedClockSuite) TestSlowedAfterFunc(c *gc.C) { func (*dilatedClockSuite) TestFastAfterFunc(c *gc.C) { t0 := time.Now() - cl := testclock.NewDilatedWallClock(500 * time.Millisecond) + cl := testclock.NewDilatedWallClock(halfSecond) mut := sync.Mutex{} mut.Lock() cl.AfterFunc(time.Second, func() { @@ -61,7 +66,7 @@ func (*dilatedClockSuite) TestFastAfterFunc(c *gc.C) { func (*dilatedClockSuite) TestSlowedNow(c *gc.C) { t0 := time.Now() - cl := testclock.NewDilatedWallClock(2 * time.Second) + cl := testclock.NewDilatedWallClock(doubleSecond) <-time.After(time.Second) t2 := cl.Now() c.Assert(t2.Sub(t0).Milliseconds(), jc.GreaterThan, 400) @@ -74,7 +79,7 @@ func (*dilatedClockSuite) TestSlowedNow(c *gc.C) { func (*dilatedClockSuite) TestFastNow(c *gc.C) { t0 := time.Now() - cl := testclock.NewDilatedWallClock(500 * time.Millisecond) + cl := testclock.NewDilatedWallClock(halfSecond) <-time.After(time.Second) t2 := cl.Now() c.Assert(t2.Sub(t0).Milliseconds(), jc.GreaterThan, 1900) @@ -87,27 +92,30 @@ func (*dilatedClockSuite) TestFastNow(c *gc.C) { func (*dilatedClockSuite) TestAdvance(c *gc.C) { t0 := time.Now() - cl := testclock.NewDilatedWallClock(500 * time.Millisecond) - first := cl.After(1 * time.Second) - cl.Advance(500 * time.Millisecond) + cl := testclock.NewDilatedWallClock(halfSecond) + first := cl.After(time.Second) + cl.Advance(halfSecond) <-time.After(250 * time.Millisecond) select { case t := <-first: c.Assert(t.Sub(t0).Milliseconds(), jc.GreaterThan, 249) - case <-time.After(50 * time.Millisecond): + case <-time.After(shortWait): c.Fatal("timer failed to trigger early") } } func (*dilatedClockSuite) TestAdvanceMulti(c *gc.C) { - cl := testclock.NewDilatedWallClock(500 * time.Millisecond) - first := cl.After(1 * time.Second) + cl := testclock.NewDilatedWallClock(halfSecond) + first := cl.After(time.Second) second := cl.After(2 * time.Second) third := cl.After(1 * time.Hour) - fourth := cl.After(24 * time.Hour) + + done := time.After(longWait) + fourth := cl.After(12*time.Hour + longWait*2 + time.Second) + cl.Advance(12 * time.Hour) + n := 0 - done := time.After(10 * time.Second) out: for { select { @@ -128,9 +136,9 @@ out: func (*dilatedClockSuite) TestStop(c *gc.C) { numGo := runtime.NumGoroutine() - cl := testclock.NewDilatedWallClock(500 * time.Millisecond) - a := cl.NewTimer(1 * time.Second) - time.Sleep(100 * time.Millisecond) + cl := testclock.NewDilatedWallClock(halfSecond) + a := cl.NewTimer(time.Second) + time.Sleep(shortWait) ok := a.Stop() c.Assert(ok, jc.IsTrue) ok = a.Stop() @@ -138,47 +146,59 @@ func (*dilatedClockSuite) TestStop(c *gc.C) { select { case <-a.Chan(): c.Fatal("stopped clock fired") - case <-time.After(1 * time.Second): + case <-time.After(time.Second): } - time.Sleep(50 * time.Millisecond) - numGoAfter := runtime.NumGoroutine() - c.Assert(numGoAfter, gc.Equals, numGo, gc.Commentf("clock goroutine still running")) + for i := 0; i < 3; i++ { + if runtime.NumGoroutine() == numGo { + break + } + time.Sleep(shortWait) + } + c.Assert(runtime.NumGoroutine(), gc.Equals, numGo, gc.Commentf("clock goroutine still running")) } func (*dilatedClockSuite) TestReset(c *gc.C) { numGo := runtime.NumGoroutine() - cl := testclock.NewDilatedWallClock(500 * time.Millisecond) - a := cl.NewTimer(1 * time.Second) + cl := testclock.NewDilatedWallClock(halfSecond) + a := cl.NewTimer(time.Second) time.Sleep(250 * time.Millisecond) - ok := a.Reset(1 * time.Second) + ok := a.Reset(time.Second) c.Assert(ok, jc.IsTrue) - <-time.After(500 * time.Millisecond) + <-time.After(halfSecond) select { case <-a.Chan(): - case <-time.After(50 * time.Millisecond): + case <-time.After(shortWait): c.Fatal("clock did not fire") } - time.Sleep(50 * time.Millisecond) - numGoAfter := runtime.NumGoroutine() - c.Assert(numGoAfter, gc.Equals, numGo, gc.Commentf("clock goroutine still running")) + for i := 0; i < 3; i++ { + if runtime.NumGoroutine() == numGo { + break + } + time.Sleep(shortWait) + } + c.Assert(runtime.NumGoroutine(), gc.Equals, numGo, gc.Commentf("clock goroutine still running")) } func (*dilatedClockSuite) TestStopReset(c *gc.C) { numGo := runtime.NumGoroutine() - cl := testclock.NewDilatedWallClock(500 * time.Millisecond) - a := cl.NewTimer(1 * time.Second) + cl := testclock.NewDilatedWallClock(halfSecond) + a := cl.NewTimer(time.Second) time.Sleep(250 * time.Millisecond) ok := a.Stop() c.Assert(ok, jc.IsTrue) - ok = a.Reset(1 * time.Second) + ok = a.Reset(time.Second) c.Assert(ok, jc.IsTrue) - <-time.After(500 * time.Millisecond) + <-time.After(halfSecond) select { case <-a.Chan(): - case <-time.After(50 * time.Millisecond): + case <-time.After(shortWait): c.Fatal("clock did not fire") } - time.Sleep(50 * time.Millisecond) - numGoAfter := runtime.NumGoroutine() - c.Assert(numGoAfter, gc.Equals, numGo, gc.Commentf("clock goroutine still running")) + for i := 0; i < 3; i++ { + if runtime.NumGoroutine() == numGo { + break + } + time.Sleep(shortWait) + } + c.Assert(runtime.NumGoroutine(), gc.Equals, numGo, gc.Commentf("clock goroutine still running")) } diff --git a/testclock/interfaces.go b/testclock/interfaces.go index 82ab76c..df329b6 100644 --- a/testclock/interfaces.go +++ b/testclock/interfaces.go @@ -1,3 +1,6 @@ +// Copyright 2022 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + package testclock import ( diff --git a/testclock/package_test.go b/testclock/package_test.go new file mode 100644 index 0000000..1ca04af --- /dev/null +++ b/testclock/package_test.go @@ -0,0 +1,20 @@ +// Copyright 2022 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package testclock_test + +import ( + gotesting "testing" + "time" + + gc "gopkg.in/check.v1" +) + +func TestAll(t *gotesting.T) { + gc.TestingT(t) +} + +const ( + shortWait = 50 * time.Millisecond + longWait = time.Second +)