Skip to content

Commit

Permalink
Merge pull request #4 from vimeo/offset_separation
Browse files Browse the repository at this point in the history
offset: move offset clock into its own subpackage
  • Loading branch information
dfinkel authored Sep 10, 2020
2 parents 2fede4b + cb30dfb commit f0ff105
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 14 deletions.
5 changes: 5 additions & 0 deletions clock.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
// until some number of other goroutines are sleeping.
// See the documentation on the individual methods of fake.Clock for more
// specific documentation.
//
// The offset subpackage contains an offset.Clock type which simply adds a
// constant offset to any interactions with an absolute time. This type is most
// useful for simulating clock-skew/unsynchronization in combination with the
// fake-clock.
package clocks

import (
Expand Down
32 changes: 18 additions & 14 deletions offset_clock.go → offset/offset_clock.go
Original file line number Diff line number Diff line change
@@ -1,47 +1,51 @@
package clocks
package offset

import (
"context"
"time"

clocks "github.com/vimeo/go-clocks"
)

// OffsetClock wraps another clock, adjusting time intervals by a constant
// Clock wraps another clock, adjusting time intervals by adding a constant
// offset. (useful for simulating clock-skew)
type OffsetClock struct {
inner Clock
// Note that relative durations are unaffected.
type Clock struct {
inner clocks.Clock
offset time.Duration
}

// Now implements Clock, returning the current time (according to the captive
// clock adjusted by offset)
func (o *OffsetClock) Now() time.Time {
func (o *Clock) Now() time.Time {
return o.inner.Now().Add(o.offset)
}

// Until implements Clock, returning the difference between the current time
// and its argument (according to the captive clock adjusted by offset)
func (o *OffsetClock) Until(t time.Time) time.Duration {
func (o *Clock) Until(t time.Time) time.Duration {
return o.inner.Until(t) + o.offset
}

// SleepUntil blocks until either ctx expires or until arrives.
// Return value is false if context-cancellation/expiry prompted an
// early return
func (o *OffsetClock) SleepUntil(ctx context.Context, until time.Time) bool {
func (o *Clock) SleepUntil(ctx context.Context, until time.Time) bool {
return o.inner.SleepUntil(ctx, until.Add(o.offset))
}

// SleepFor is the relative-time equivalent of SleepUntil. In the
// default implementation, this is the lower-level method, but other
// implementations may disagree.
func (o *OffsetClock) SleepFor(ctx context.Context, dur time.Duration) bool {
// SleepFor is the relative-time equivalent of SleepUntil.
// Return value is false if context-cancellation/expiry prompted an
// early return
func (o *Clock) SleepFor(ctx context.Context, dur time.Duration) bool {
// SleepFor is relative, so it doesn't need any adjustment
return o.inner.SleepFor(ctx, dur)
}

// NewOffsetClock creates an OffsetClock and returns it
func NewOffsetClock(inner Clock, offset time.Duration) *OffsetClock {
return &OffsetClock{
// NewOffsetClock creates an OffsetClock.
// offset is added to all absolute times
func NewOffsetClock(inner clocks.Clock, offset time.Duration) *Clock {
return &Clock{
inner: inner,
offset: offset,
}
Expand Down
82 changes: 82 additions & 0 deletions offset/offset_clock_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package offset

import (
"context"
"testing"
"time"

"github.com/vimeo/go-clocks/fake"
)

func TestOffsetClock(t *testing.T) {
base := time.Now()
inner := fake.NewClock(base)
const o = time.Minute + 3*time.Second
c := NewOffsetClock(inner, o)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

if n := c.Now(); !base.Add(o).Equal(n) {
t.Errorf("unexpected time from Now(): %s; expected %s",
n, base.Add(o))
}

if d := c.Until(base.Add(time.Hour)); d != (time.Hour + o) {
t.Errorf("unexpected value for c.Until(%s); got %s; expected %s",
base.Add(time.Hour), d, (time.Hour + o))
}

{
expectedWakeInner := base.Add(time.Hour)
ch := make(chan bool)
go func() {
ch <- c.SleepFor(ctx, time.Hour)
}()
inner.AwaitSleepers(1)

if sl := inner.Sleepers(); len(sl) != 1 || !sl[0].Equal(expectedWakeInner) {
t.Errorf("unexpected sleepers: %v; expected 1 with %s", sl, expectedWakeInner)
}

select {
case v := <-ch:
t.Fatalf("SleepFor exited prematurely with value %t", v)
default:
}

if awoken := inner.Advance(time.Hour); awoken != 1 {
t.Errorf("unexpected number of awoken waiters: %d; expected 1", awoken)
}

if v := <-ch; !v {
t.Errorf("unexpected return value from SleepFor; %t; expected true", v)
}
}
{
expectedWakeInner := base.Add(2 * time.Hour)
ch := make(chan bool)
go func() {
ch <- c.SleepUntil(ctx, expectedWakeInner.Add(-o))
}()
inner.AwaitSleepers(1)

if sl := inner.Sleepers(); len(sl) != 1 || !sl[0].Equal(expectedWakeInner) {
t.Errorf("unexpected sleepers: %v; expected 1 with %s", sl, expectedWakeInner)
}

select {
case v := <-ch:
t.Fatalf("SleepFor exited prematurely with value %t", v)
default:
}

if awoken := inner.Advance(time.Hour); awoken != 1 {
t.Errorf("unexpected number of awoken waiters: %d; expected 1", awoken)
}

if v := <-ch; !v {
t.Errorf("unexpected return value from SleepUntil; %t; expected true", v)
}
}
}

0 comments on commit f0ff105

Please sign in to comment.