Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

slim dependencies; add go.mod #5

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
language: go
go_import_path: github.com/juju/clock
go:
- "1.11.x"
- "1.12.x"
env:
global:
- GO111MODULE=on
install: "echo no install step required"
script: go test ./...
11 changes: 0 additions & 11 deletions dependencies.tsv

This file was deleted.

5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/juju/clock

go 1.13

require github.com/frankban/quicktest v1.2.2
9 changes: 9 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
github.com/frankban/quicktest v1.2.2 h1:xfmOhhoH5fGPgbEAlhLpJH9p0z/0Qizio9osmvn9IUY=
github.com/frankban/quicktest v1.2.2/go.mod h1:Qh/WofXFeiAFII1aEBu529AtJo6Zg2VHscnEsbBnJ20=
github.com/google/go-cmp v0.2.1-0.20190312032427-6f77996f0c42 h1:q3pnF5JFBNRz8sRD+IRj7Y6DMyYGTNqnZ9axTbSfoNI=
github.com/google/go-cmp v0.2.1-0.20190312032427-6f77996f0c42/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
15 changes: 2 additions & 13 deletions monotonic/monotonic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,15 @@ import (
"testing"
"time"

gc "gopkg.in/check.v1"

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

func TestPackage(t *testing.T) {
gc.TestingT(t)
}

type MonotonicSuite struct {
}

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

func (s *MonotonicSuite) TestNow(c *gc.C) {
func TestNow(t *testing.T) {
var prev time.Duration
for i := 0; i < 1000; i++ {
val := monotonic.Now()
if val < prev {
c.Fatal("now is less than previous value")
t.Fatal("now is less than previous value")
}
prev = val
}
Expand Down
14 changes: 10 additions & 4 deletions testclock/clock.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import (
"time"

"github.com/juju/clock"
"github.com/juju/errors"
"github.com/juju/loggo"
)

// timer implements a mock clock.Timer for testing purposes.
Expand Down Expand Up @@ -44,6 +42,7 @@ func (t *timer) Chan() <-chan time.Time {
// Clock implements a mock clock.Clock for testing purposes.
type Clock struct {
mu sync.Mutex
log func(string)
now time.Time
waiting []*timer // timers waiting to fire, sorted by deadline.
notifyAlarms chan struct{}
Expand All @@ -55,11 +54,18 @@ type Clock struct {
// Alarms chan to keep the buffer clear.
func NewClock(now time.Time) *Clock {
return &Clock{
log: func(string) {},
now: now,
notifyAlarms: make(chan struct{}, 10000),
}
}

// SetLog sets the log function that's called if something that probably
// shouldn't happen occurs.
func (clock *Clock) SetLog(log func(msg string)) {
clock.log = log
}

// Now is part of the clock.Clock interface.
func (clock *Clock) Now() time.Time {
clock.mu.Lock()
Expand Down Expand Up @@ -109,7 +115,7 @@ func (clock *Clock) Advance(d time.Duration) {
defer clock.mu.Unlock()
clock.now = clock.now.Add(d)
if len(clock.waiting) == 0 {
loggo.GetLogger("juju.clock").Debugf("advancing a clock that has nothing waiting: cf. https://github.com/juju/juju/wiki/Intermittent-failures")
clock.log("advancing a clock that has nothing waiting: cf. https://github.com/juju/juju/wiki/Intermittent-failures")
}
clock.triggerAll()
}
Expand Down Expand Up @@ -141,7 +147,7 @@ func (clock *Clock) WaitAdvance(d, w time.Duration, n int) error {
stacks += fmt.Sprintf("timer deadline: %v\n%s", t.deadline, string(t.stack))
}
clock.mu.Unlock()
return errors.Errorf(
return fmt.Errorf(
"got %d timers added after waiting %s: wanted %d, stacks:\n%s",
got, w.String(), n, stacks)
case <-next:
Expand Down
80 changes: 41 additions & 39 deletions testclock/clock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,74 +4,70 @@
package testclock_test

import (
"bytes"
"fmt"
"sync"
gotesting "testing"
"testing"
"time"

"github.com/juju/loggo"
"github.com/juju/testing"
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"
qt "github.com/frankban/quicktest"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unsure about adding another testing library into the Juju ecosystem..

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW this library is very much general purpose, rather than Juju-specific, and quicktest was developed by a Canonicaler and is already in use by quite a few Canonical modules.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for quicktest, it's simpler than gocheck and actively maintained.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it too, but we already have multiple testing approaches that are partially implemented. Would rather an addition like this was added after consensus has been reached in a thread to the Juju discource.


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

type clockSuite struct {
testing.LoggingSuite
}

func TestAll(t *gotesting.T) {
gc.TestingT(t)
}

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

func (*clockSuite) TestNow(c *gc.C) {
func TestNow(t *testing.T) {
c := qt.New(t)
t0 := time.Now()
cl := testclock.NewClock(t0)
c.Assert(cl.Now(), gc.Equals, t0)
c.Assert(cl.Now(), qt.Equals, t0)
}

var (
shortWait = 50 * time.Millisecond
longWait = time.Second
)

func (*clockSuite) TestAdvanceLogs(c *gc.C) {
loggo.GetLogger("juju.clock").SetLogLevel(loggo.DEBUG)
func TestAdvanceLogs(t *testing.T) {
c := qt.New(t)
t0 := time.Now()
cl := testclock.NewClock(t0)
var logBuf bytes.Buffer
cl.SetLog(func(msg string) {
fmt.Fprintln(&logBuf, msg)
})

// Shouldn't log anything.
t := cl.After(time.Second)
tc := cl.After(time.Second)
cl.Advance(time.Minute)
<-t
c.Check(c.GetTestLog(), jc.DeepEquals, "")
<-tc
c.Check(logBuf.Bytes(), qt.HasLen, 0)

// Should log since nothing's waiting.
cl.Advance(time.Hour)
c.Check(c.GetTestLog(), jc.Contains, "advancing a clock that has nothing waiting: cf. https://github.com/juju/juju/wiki/Intermittent-failures")
c.Check(logBuf.String(), qt.Equals, "advancing a clock that has nothing waiting: cf. https://github.com/juju/juju/wiki/Intermittent-failures\n")
}

func (*clockSuite) TestWaitAdvance(c *gc.C) {
func TestWaitAdvance(t *testing.T) {
c := qt.New(t)
t0 := time.Now()
cl := testclock.NewClock(t0)

// It is legal to just say 'nothing is waiting'
err := cl.WaitAdvance(0, 0, 0)
c.Check(err, jc.ErrorIsNil)
c.Check(err, qt.Equals, nil)

// Test that no timers errors out.
err = cl.WaitAdvance(time.Millisecond, 10*time.Millisecond, 1)
c.Check(err, gc.ErrorMatches, "got 0 timers added after waiting 10ms: wanted 1, stacks:\n")
c.Check(err, qt.ErrorMatches, "got 0 timers added after waiting 10ms: wanted 1, stacks:\n")

// Test that a timer doesn't error.
_ = cl.After(time.Nanosecond)
err = cl.WaitAdvance(time.Millisecond, 10*time.Millisecond, 1)
c.Check(err, jc.ErrorIsNil)
c.Check(err, qt.Equals, nil)
}

func (*clockSuite) TestAdvanceWithAfter(c *gc.C) {
func TestAdvanceWithAfter(t *testing.T) {
c := qt.New(t)
t0 := time.Now()
cl := testclock.NewClock(t0)
ch := cl.After(time.Second)
Expand Down Expand Up @@ -112,10 +108,11 @@ func (*clockSuite) TestAdvanceWithAfter(c *gc.C) {
case <-time.After(longWait):
c.Fatalf("expected event to be triggered")
}
c.Assert(cl.Now().UTC(), gc.Equals, t0.Add(4*time.Second).UTC())
c.Assert(cl.Now().UTC(), qt.Equals, t0.Add(4*time.Second).UTC())
}

func (*clockSuite) TestAdvanceWithAfterFunc(c *gc.C) {
func TestAdvanceWithAfterFunc(t *testing.T) {
c := qt.New(t)
// Most of the details have been checked in TestAdvanceWithAfter,
// so just check that AfterFunc is wired up correctly.
t0 := time.Now()
Expand All @@ -132,7 +129,8 @@ func (*clockSuite) TestAdvanceWithAfterFunc(c *gc.C) {
}
}

func (*clockSuite) TestAfterFuncStop(c *gc.C) {
func TestAfterFuncStop(t *testing.T) {
c := qt.New(t)
t0 := time.Now()
cl := testclock.NewClock(t0)
fired := make(chan struct{})
Expand All @@ -148,14 +146,15 @@ func (*clockSuite) TestAfterFuncStop(c *gc.C) {
}
}

func (*clockSuite) TestNewTimerReset(c *gc.C) {
func TestNewTimerReset(t *testing.T) {
c := qt.New(t)
t0 := time.Now()
cl := testclock.NewClock(t0)
timer := cl.NewTimer(time.Second)
cl.Advance(time.Second)
select {
case t := <-timer.Chan():
c.Assert(t.UTC(), gc.Equals, t0.Add(time.Second).UTC())
c.Assert(t.UTC(), qt.Equals, t0.Add(time.Second).UTC())
case <-time.After(longWait):
c.Fatalf("expected event to be triggered")
}
Expand All @@ -164,13 +163,14 @@ func (*clockSuite) TestNewTimerReset(c *gc.C) {
cl.Advance(100 * time.Millisecond)
select {
case t := <-timer.Chan():
c.Assert(t.UTC(), gc.Equals, t0.Add(time.Second+100*time.Millisecond).UTC())
c.Assert(t.UTC(), qt.Equals, t0.Add(time.Second+100*time.Millisecond).UTC())
case <-time.After(longWait):
c.Fatalf("expected event to be triggered")
}
}

func (*clockSuite) TestNewTimerAsyncReset(c *gc.C) {
func TestNewTimerAsyncReset(t *testing.T) {
c := qt.New(t)
t0 := time.Now()
clock := testclock.NewClock(t0)
timer := clock.NewTimer(time.Hour)
Expand Down Expand Up @@ -219,7 +219,8 @@ func (*clockSuite) TestNewTimerAsyncReset(c *gc.C) {
wg.Wait()
}

func (*clockSuite) TestNewTimerResetCausesWakeup(c *gc.C) {
func TestNewTimerResetCausesWakeup(t *testing.T) {
c := qt.New(t)
t0 := time.Now()
clock := testclock.NewClock(t0)
timer1 := clock.NewTimer(time.Hour)
Expand All @@ -231,7 +232,7 @@ func (*clockSuite) TestNewTimerResetCausesWakeup(c *gc.C) {
defer wg.Done()
select {
case t := <-timer1.Chan():
c.Check(t0, gc.Equals, t)
c.Check(t0, qt.Equals, t)
case <-time.After(longWait):
c.Errorf("timer1 took too long to wake up")
}
Expand All @@ -252,7 +253,7 @@ func (*clockSuite) TestNewTimerResetCausesWakeup(c *gc.C) {
select {
case t := <-timer3.Chan():
// Even though the reset was negative, it triggers at 'now'
c.Check(t0, gc.Equals, t)
c.Check(t0, qt.Equals, t)
case <-time.After(longWait):
c.Errorf("timer3 took too long to wake up")
}
Expand All @@ -264,7 +265,8 @@ func (*clockSuite) TestNewTimerResetCausesWakeup(c *gc.C) {
wg.Wait()
}

func (*clockSuite) TestMultipleWaiters(c *gc.C) {
func TestMultipleWaiters(t *testing.T) {
c := qt.New(t)
var wg sync.WaitGroup
t0 := time.Date(2000, 01, 01, 01, 0, 0, 0, time.UTC)
cl := testclock.NewClock(t0)
Expand Down