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

Code import. #1

Merged
merged 1 commit into from
May 24, 2018
Merged
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
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ check: check-licence check-go
go test $(PROJECT)/...

check-licence:
@(fgrep -rl "Licensed under the LGPLv3" .;\
fgrep -rl "MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT" .;\
@(fgrep -rl "Licensed under the LGPLv3" --exclude *.s .;\
fgrep -rl "MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT" --exclude *.s .;\
find . -name "*.go") | sed -e 's,\./,,' | sort | uniq -u | \
xargs -I {} echo FAIL: licence missed: {}

Expand Down
53 changes: 53 additions & 0 deletions clock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright 2015 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.

package clock

import "time"

// Clock provides an interface for dealing with clocks.
type Clock interface {
// Now returns the current clock time.
Now() time.Time

// After waits for the duration to elapse and then sends the
// current time on the returned channel.
After(time.Duration) <-chan time.Time

// AfterFunc waits for the duration to elapse and then calls f in its own goroutine.
// It returns a Timer that can be used to cancel the call using its Stop method.
AfterFunc(d time.Duration, f func()) Timer

// NewTimer creates a new Timer that will send the current time
// on its channel after at least duration d.
NewTimer(d time.Duration) Timer
}

// Alarm returns a channel that will have the time sent on it at some point
// after the supplied time occurs.
//
// This is short for c.After(t.Sub(c.Now())).
func Alarm(c Clock, t time.Time) <-chan time.Time {
return c.After(t.Sub(c.Now()))
}

// The Timer type represents a single event.
// A Timer must be created with AfterFunc.
// This interface follows time.Timer's methods but provides easier mocking.
type Timer interface {
// When the Timer expires, the current time will be sent on the
// channel returned from Chan, unless the Timer was created by
// AfterFunc.
Chan() <-chan time.Time

// Reset changes the timer to expire after duration d.
// It returns true if the timer had been active, false if
// the timer had expired or been stopped.
Reset(time.Duration) bool

// Stop prevents the Timer from firing. It returns true if
// the call stops the timer, false if the timer has already expired or been stopped.
// Stop does not close the channel, to prevent a read
// from the channel succeeding incorrectly.
Stop() bool
}
11 changes: 11 additions & 0 deletions dependencies.tsv
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
github.com/juju/errors git 1b5e39b83d1835fa480e0c2ddefb040ee82d58b3 2015-09-16T12:56:42Z
github.com/juju/loggo git 8232ab8918d91c72af1a9fb94d3edbe31d88b790 2017-06-05T01:46:07Z
github.com/juju/retry git 62c62032529169c7ec02fa48f93349604c345e1f 2015-10-29T02:48:21Z
github.com/juju/testing git 72703b1e95eb8ce4737fd8a3d8496c6b0be280a6 2018-05-17T13:41:05Z
github.com/juju/utils git 9b65c33e54c793d74a4ed99c15111c44faddb8e4 2017-10-25T16:38:56Z
github.com/juju/version git 1f41e27e54f21acccf9b2dddae063a782a8a7ceb 2016-10-31T05:19:06Z
golang.org/x/crypto git 96846453c37f0876340a66a47f3f75b1f3a6cd2d 2017-04-21T04:31:20Z
golang.org/x/net git ea47fc708ee3e20177f3ca3716217c4ab75942cb 2015-08-29T23:03:18Z
gopkg.in/check.v1 git 4f90aeace3a26ad7021961c297b22c42160c7b25 2016-01-05T16:49:36Z
gopkg.in/mgo.v2 git f2b6f6c918c452ad107eec89615f074e3bd80e33 2016-08-18T01:52:18Z
gopkg.in/yaml.v2 git 1be3d31502d6eabc0dd7ce5b0daab022e14a5538 2017-07-12T05:45:46Z
22 changes: 22 additions & 0 deletions monotonic/monotonic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2017 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.

package monotonic

import (
"time"
_ "unsafe"
)

//go:noescape
//go:linkname nanotime runtime.nanotime
func nanotime() int64

// Now returns the current time in nanoseconds from a monotonic clock.
//
// The result is guaranteed to not jump due to NTP or other changes to
// system time, which may jump forward or backwards. Instead, in response to
// such changes, the clock frequency is adjusted slowly.
func Now() time.Duration {
return time.Duration(nanotime())
}
4 changes: 4 additions & 0 deletions monotonic/monotonic.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright 2017 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.

// Deliberately empty - see https://github.com/golang/go/issues/15006
33 changes: 33 additions & 0 deletions monotonic/monotonic_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2017 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.

package monotonic_test

import (
"testing"
"time"

gc "gopkg.in/check.v1"

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

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

type MonotonicSuite struct {
}

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

func (s *MonotonicSuite) TestNow(c *gc.C) {
var prev time.Duration
for i := 0; i < 1000; i++ {
val := monotonic.Now()
if val < prev {
c.Fatal("now is less than previous value")
}
prev = val
}
}
248 changes: 248 additions & 0 deletions testclock/clock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
// Copyright 2015-2018 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.

package testclock

import (
"sort"
"sync"
"time"

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

// timer implements a mock clock.Timer for testing purposes.
type timer struct {
deadline time.Time
clock *Clock
c chan time.Time
// trigger is called when the timer expires. It is
// called with the clock mutex held and will not block.
trigger func()
}

// Reset is part of the clock.Timer interface.
func (t *timer) Reset(d time.Duration) bool {
return t.clock.reset(t, d)
}

// Stop is part of the clock.Timer interface.
func (t *timer) Stop() bool {
return t.clock.stop(t)
}

// Chan is part of the clock.Timer interface.
func (t *timer) Chan() <-chan time.Time {
return t.c
}

// Clock implements a mock clock.Clock for testing purposes.
type Clock struct {
mu sync.Mutex
now time.Time
waiting []*timer // timers waiting to fire, sorted by deadline.
notifyAlarms chan struct{}
}

// NewClock returns a new clock set to the supplied time. If your SUT needs to
// call After, AfterFunc, NewTimer or Timer.Reset more than 10000 times: (1)
// you have probably written a bad test; and (2) you'll need to read from the
// Alarms chan to keep the buffer clear.
func NewClock(now time.Time) *Clock {
return &Clock{
now: now,
notifyAlarms: make(chan struct{}, 10000),
}
}

// Now is part of the clock.Clock interface.
func (clock *Clock) Now() time.Time {
clock.mu.Lock()
defer clock.mu.Unlock()
return clock.now
}

// After is part of the clock.Clock interface.
func (clock *Clock) After(d time.Duration) <-chan time.Time {
return clock.NewTimer(d).Chan()
}

func (clock *Clock) NewTimer(d time.Duration) clock.Timer {
c := make(chan time.Time, 1)
return clock.addAlarm(d, c, func() {
c <- clock.now
})
}

// AfterFunc is part of the clock.Clock interface.
func (clock *Clock) AfterFunc(d time.Duration, f func()) clock.Timer {
return clock.addAlarm(d, nil, func() {
go f()
})
}

func (clock *Clock) addAlarm(d time.Duration, c chan time.Time, trigger func()) *timer {
defer clock.notifyAlarm()
clock.mu.Lock()
defer clock.mu.Unlock()
t := &timer{
c: c,
deadline: clock.now.Add(d),
clock: clock,
trigger: trigger,
}
clock.addTimer(t)
clock.triggerAll()
return t
}

// Advance advances the result of Now by the supplied duration, and sends
// the "current" time on all alarms which are no longer "in the future".
func (clock *Clock) Advance(d time.Duration) {
clock.mu.Lock()
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.triggerAll()
}

// WaitAdvance functions the same as Advance, but only if there is n timers in
// clock.waiting. This came about while fixing lp:1607044 intermittent
// failures. It turns out that testing.Clock.Advance might advance the time
// and trigger notifications before triggers are set. So we wait a limited time
// 'w' for 'n' timers to show up in clock.waiting, and if they do we advance
// 'd'.
func (clock *Clock) WaitAdvance(d, w time.Duration, n int) error {
if w == 0 {
w = time.Second
}
pause := w / 10
for i := 0; i < 10; i++ {
if clock.hasNWaiters(n) {
clock.Advance(d)
return nil
}
time.Sleep(pause)
}
clock.mu.Lock()
got := len(clock.waiting)
clock.mu.Unlock()
return errors.Errorf(
"got %d timers added after waiting %s: wanted %d", got, w.String(), n)
}

// hasNWaiters checks if the clock currently has 'n' timers waiting to fire.
func (clock *Clock) hasNWaiters(n int) bool {
clock.mu.Lock()
hasWaiters := len(clock.waiting) == n
clock.mu.Unlock()
return hasWaiters
}

// Alarms returns a channel on which you can read one value for every call to
// After and AfterFunc; and for every successful Timer.Reset backed by this
// Clock. It might not be elegant but it's necessary when testing time logic
// that runs on a goroutine other than that of the test.
func (clock *Clock) Alarms() <-chan struct{} {
return clock.notifyAlarms
}

// triggerAll triggers any alarms that are currently due and removes them
// from clock.waiting.
func (clock *Clock) triggerAll() {
triggered := 0
for _, t := range clock.waiting {
if clock.now.Before(t.deadline) {
break
}
t.trigger()
triggered++
}
clock.waiting = clock.waiting[triggered:]
}

// reset is the underlying implementation of clock.Timer.Reset, which may be
// called by any Timer backed by this Clock.
func (clock *Clock) reset(t *timer, d time.Duration) bool {
defer clock.notifyAlarm()
clock.mu.Lock()
defer clock.mu.Unlock()

found := false
for _, wt := range clock.waiting {
if wt == t {
found = true
}
}
if !found {
clock.waiting = append(clock.waiting, t)
}
t.deadline = clock.now.Add(d)
sort.Sort(byDeadline(clock.waiting))
return found
}

// stop is the underlying implementation of clock.Timer.Reset, which may be
// called by any Timer backed by this Clock.
func (clock *Clock) stop(t *timer) bool {
clock.mu.Lock()
defer clock.mu.Unlock()

for i, wt := range clock.waiting {
if wt == t {
clock.waiting = removeFromSlice(clock.waiting, i)
return true
}
}
return false
}

// addTimer adds an alarm at time t.
func (clock *Clock) addTimer(t *timer) {
clock.waiting = append(clock.waiting, t)
sort.Sort(byDeadline(clock.waiting))
}

// notifyAlarm sends a value on the channel exposed by Alarms().
func (clock *Clock) notifyAlarm() {
select {
case clock.notifyAlarms <- struct{}{}:
default:
panic("alarm notification buffer full")
}
}

// byDeadline is used to sort alarms by time.
type byDeadline []*timer

func (a byDeadline) Len() int { return len(a) }
func (a byDeadline) Less(i, j int) bool { return a[i].deadline.Before(a[j].deadline) }
func (a byDeadline) Swap(i, j int) { a[i], a[j] = a[j], a[i] }

// removeFromSlice removes item at the specified index from the slice.
func removeFromSlice(sl []*timer, index int) []*timer {
return append(sl[:index], sl[index+1:]...)
}

// AutoAdvancingClock wraps a clock.Clock, calling the Advance
// function whenever After or AfterFunc are called.
type AutoAdvancingClock struct {
clock.Clock
Advance func(time.Duration)
}

func (c *AutoAdvancingClock) After(d time.Duration) <-chan time.Time {
ch := c.Clock.After(d)
c.Advance(d)
return ch
}

func (c *AutoAdvancingClock) AfterFunc(d time.Duration, f func()) clock.Timer {
t := c.Clock.AfterFunc(d, f)
c.Advance(d)
return t
}
Loading