Skip to content

Commit f0ff105

Browse files
authored
Merge pull request #4 from vimeo/offset_separation
offset: move offset clock into its own subpackage
2 parents 2fede4b + cb30dfb commit f0ff105

File tree

3 files changed

+105
-14
lines changed

3 files changed

+105
-14
lines changed

clock.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111
// until some number of other goroutines are sleeping.
1212
// See the documentation on the individual methods of fake.Clock for more
1313
// specific documentation.
14+
//
15+
// The offset subpackage contains an offset.Clock type which simply adds a
16+
// constant offset to any interactions with an absolute time. This type is most
17+
// useful for simulating clock-skew/unsynchronization in combination with the
18+
// fake-clock.
1419
package clocks
1520

1621
import (

offset_clock.go renamed to offset/offset_clock.go

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,51 @@
1-
package clocks
1+
package offset
22

33
import (
44
"context"
55
"time"
6+
7+
clocks "github.com/vimeo/go-clocks"
68
)
79

8-
// OffsetClock wraps another clock, adjusting time intervals by a constant
10+
// Clock wraps another clock, adjusting time intervals by adding a constant
911
// offset. (useful for simulating clock-skew)
10-
type OffsetClock struct {
11-
inner Clock
12+
// Note that relative durations are unaffected.
13+
type Clock struct {
14+
inner clocks.Clock
1215
offset time.Duration
1316
}
1417

1518
// Now implements Clock, returning the current time (according to the captive
1619
// clock adjusted by offset)
17-
func (o *OffsetClock) Now() time.Time {
20+
func (o *Clock) Now() time.Time {
1821
return o.inner.Now().Add(o.offset)
1922
}
2023

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

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

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

42-
// NewOffsetClock creates an OffsetClock and returns it
43-
func NewOffsetClock(inner Clock, offset time.Duration) *OffsetClock {
44-
return &OffsetClock{
45+
// NewOffsetClock creates an OffsetClock.
46+
// offset is added to all absolute times
47+
func NewOffsetClock(inner clocks.Clock, offset time.Duration) *Clock {
48+
return &Clock{
4549
inner: inner,
4650
offset: offset,
4751
}

offset/offset_clock_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package offset
2+
3+
import (
4+
"context"
5+
"testing"
6+
"time"
7+
8+
"github.com/vimeo/go-clocks/fake"
9+
)
10+
11+
func TestOffsetClock(t *testing.T) {
12+
base := time.Now()
13+
inner := fake.NewClock(base)
14+
const o = time.Minute + 3*time.Second
15+
c := NewOffsetClock(inner, o)
16+
17+
ctx, cancel := context.WithCancel(context.Background())
18+
defer cancel()
19+
20+
if n := c.Now(); !base.Add(o).Equal(n) {
21+
t.Errorf("unexpected time from Now(): %s; expected %s",
22+
n, base.Add(o))
23+
}
24+
25+
if d := c.Until(base.Add(time.Hour)); d != (time.Hour + o) {
26+
t.Errorf("unexpected value for c.Until(%s); got %s; expected %s",
27+
base.Add(time.Hour), d, (time.Hour + o))
28+
}
29+
30+
{
31+
expectedWakeInner := base.Add(time.Hour)
32+
ch := make(chan bool)
33+
go func() {
34+
ch <- c.SleepFor(ctx, time.Hour)
35+
}()
36+
inner.AwaitSleepers(1)
37+
38+
if sl := inner.Sleepers(); len(sl) != 1 || !sl[0].Equal(expectedWakeInner) {
39+
t.Errorf("unexpected sleepers: %v; expected 1 with %s", sl, expectedWakeInner)
40+
}
41+
42+
select {
43+
case v := <-ch:
44+
t.Fatalf("SleepFor exited prematurely with value %t", v)
45+
default:
46+
}
47+
48+
if awoken := inner.Advance(time.Hour); awoken != 1 {
49+
t.Errorf("unexpected number of awoken waiters: %d; expected 1", awoken)
50+
}
51+
52+
if v := <-ch; !v {
53+
t.Errorf("unexpected return value from SleepFor; %t; expected true", v)
54+
}
55+
}
56+
{
57+
expectedWakeInner := base.Add(2 * time.Hour)
58+
ch := make(chan bool)
59+
go func() {
60+
ch <- c.SleepUntil(ctx, expectedWakeInner.Add(-o))
61+
}()
62+
inner.AwaitSleepers(1)
63+
64+
if sl := inner.Sleepers(); len(sl) != 1 || !sl[0].Equal(expectedWakeInner) {
65+
t.Errorf("unexpected sleepers: %v; expected 1 with %s", sl, expectedWakeInner)
66+
}
67+
68+
select {
69+
case v := <-ch:
70+
t.Fatalf("SleepFor exited prematurely with value %t", v)
71+
default:
72+
}
73+
74+
if awoken := inner.Advance(time.Hour); awoken != 1 {
75+
t.Errorf("unexpected number of awoken waiters: %d; expected 1", awoken)
76+
}
77+
78+
if v := <-ch; !v {
79+
t.Errorf("unexpected return value from SleepUntil; %t; expected true", v)
80+
}
81+
}
82+
}

0 commit comments

Comments
 (0)