-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
implement abstraction over clock source & add tests
- Loading branch information
Showing
5 changed files
with
290 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package main | ||
|
||
import "time" | ||
|
||
// Time is a custom interface to ensure we can test timestamp() | ||
type Time interface { | ||
Now() time.Time | ||
Sub(time.Time) time.Duration | ||
} | ||
|
||
type realClock struct{} | ||
|
||
func (realClock) Now() time.Time { return time.Now() } | ||
func (c realClock) Sub(t time.Time) time.Duration { return c.Sub(t) } | ||
|
||
type stuckClock struct { | ||
sec int64 | ||
nsec int64 | ||
} | ||
|
||
func (c stuckClock) Now() time.Time { | ||
return time.Unix(c.sec, c.nsec) | ||
} | ||
|
||
func (c stuckClock) Sub(t time.Time) time.Duration { | ||
when := time.Unix(c.sec, c.nsec) | ||
return when.Sub(time.Unix(c.sec, c.nsec)) | ||
} | ||
|
||
// StuckClock creates a new "stuck" clock, which starts at the given sec, nsec | ||
// and always returns itself for any Sub() call | ||
func StuckClock(sec, nsec int64) Time { | ||
return stuckClock{sec: sec, nsec: nsec} | ||
} | ||
|
||
type monotonicClock struct { | ||
sec int64 | ||
nsec int64 | ||
secIncrease int64 | ||
nsecIncrease int64 | ||
} | ||
|
||
func (c *monotonicClock) Now() time.Time { | ||
then := time.Unix(c.sec, c.nsec) | ||
c.sec += c.secIncrease | ||
c.nsec += c.nsecIncrease | ||
for c.nsec > 1_000_000_000 { | ||
c.sec++ | ||
c.nsec -= 1_000_000_000 | ||
} | ||
return then | ||
} | ||
|
||
func (c monotonicClock) Sub(t time.Time) time.Duration { | ||
when := time.Unix(c.sec, c.nsec) | ||
return when.Sub(t) | ||
} | ||
|
||
// To ensure MonotonicClock can do its thing, create an instance of it which | ||
// gets changed by all calls to Now() and friends. | ||
var theMonotonicClock monotonicClock | ||
|
||
// MonotonicClock creates a new "stuck" clock, which starts at the given sec, | ||
// nsec and whenever Now() is called, it returns the last time incremented by | ||
// the given delta seconds and nsec. | ||
func MonotonicClock(sec, nsec, secIncrease, nsecIncrease int64) Time { | ||
theMonotonicClock = monotonicClock{sec: sec, nsec: nsec, secIncrease: secIncrease, nsecIncrease: nsecIncrease} | ||
return &theMonotonicClock | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package main | ||
|
||
import ( | ||
"testing" | ||
) | ||
|
||
func TestMonotonicClock(t *testing.T) { | ||
c := MonotonicClock(0, 0, 0, 5_000_000) | ||
now1 := c.Now() | ||
now2 := c.Now() | ||
if now1.Unix() == now2.Unix() && now1.UnixNano() == now2.UnixNano() { | ||
t.Fail() | ||
t.Logf("now1==now2 = %v & %v & %v.%v", now1, now2, now1.Unix(), now1.UnixNano()) | ||
return | ||
} | ||
if now2.Unix() != now2.Unix() { | ||
t.Fail() | ||
t.Logf("now1.Unix()==now2.Unix() = %v & %v", now1, now2) | ||
return | ||
} | ||
if now2.UnixNano() != (now1.UnixNano() + 5_000_000) { | ||
t.Fail() | ||
t.Logf("now2.UnixNano()!=now1.UnixNano()+5_000_000 = now1:%v (%d.%d) & now2:%v (%d.%d) => %d!=%d", | ||
now1, now1.Unix(), now1.UnixNano(), | ||
now2, now2.Unix(), now2.UnixNano(), | ||
now2.UnixNano(), now1.UnixNano()+5_000_000, | ||
) | ||
return | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"os" | ||
"testing" | ||
) | ||
|
||
var fakeEpoch int64 = 909090909 | ||
|
||
// perl -lE'say scalar gmtime 909090909' | ||
// Thu Oct 22 21:15:09 1998 | ||
|
||
func TestTimestampSingleLine(t *testing.T) { | ||
if os.Getenv("TZ") != "UTC" { | ||
t.Fail() | ||
t.Logf("Run tests with TZ=UTC!") | ||
return | ||
} | ||
c := StuckClock(fakeEpoch, 0) | ||
in := bytes.NewBufferString("foo") | ||
var outB bytes.Buffer | ||
timestamp(c, in, &outB, false) | ||
wantS := "1998-10-22 21:15:09.000000 00:00:00.000000 00:00:00.000000 foo\n" | ||
if outB.String() != wantS { | ||
t.Fail() | ||
t.Logf("GOT bytes: %v", outB) | ||
t.Logf("WANT string:\n%v", wantS) | ||
t.Logf("GOT string:\n%v", outB.String()) | ||
} | ||
} | ||
|
||
func TestTimestampTwoLines(t *testing.T) { | ||
if os.Getenv("TZ") != "UTC" { | ||
t.Fail() | ||
t.Logf("Run tests with TZ=UTC!") | ||
return | ||
} | ||
c := StuckClock(fakeEpoch, 0) | ||
in := bytes.NewBufferString("foo\nbar") | ||
var outB bytes.Buffer | ||
timestamp(c, in, &outB, false) | ||
wantS := "1998-10-22 21:15:09.000000 00:00:00.000000 00:00:00.000000 foo\n1998-10-22 21:15:09.000000 00:00:00.000000 00:00:00.000000 bar\n" | ||
if outB.String() != wantS { | ||
t.Fail() | ||
t.Logf("GOT bytes: %v", outB) | ||
t.Logf("WANT string:\n%v", wantS) | ||
t.Logf("GOT string:\n%v", outB.String()) | ||
} | ||
} | ||
|
||
func TestTimestampTwoLinesColored(t *testing.T) { | ||
if os.Getenv("TZ") != "UTC" { | ||
t.Fail() | ||
t.Logf("Run tests with TZ=UTC!") | ||
return | ||
} | ||
c := StuckClock(fakeEpoch, 0) | ||
in := bytes.NewBufferString("foo\nbar") | ||
var outB bytes.Buffer | ||
timestamp(c, in, &outB, true) | ||
wantS := "1998-10-22 21:15:09.000000 00:00:00.000000 00:00:00.000000 foo\n1998-10-22 21:15:09.000000 00:00:00.000000 00:00:00.000000 bar\n" | ||
if outB.String() != wantS { | ||
t.Fail() | ||
t.Logf("GOT bytes: %v", outB) | ||
t.Logf("WANT string:\n%v", wantS) | ||
t.Logf("GOT string:\n%v", outB.String()) | ||
} | ||
} | ||
|
||
func TestTimestampTwoLinesHalfSecond(t *testing.T) { | ||
if os.Getenv("TZ") != "UTC" { | ||
t.Fail() | ||
t.Logf("Run tests with TZ=UTC!") | ||
return | ||
} | ||
c := MonotonicClock(fakeEpoch, 0, 0, 500_000_000) | ||
in := bytes.NewBufferString("foo\nbar") | ||
var outB bytes.Buffer | ||
timestamp(c, in, &outB, false) | ||
wantS := "1998-10-22 21:15:09.500000 00:00:00.500000 00:00:00.500000 foo\n1998-10-22 21:15:10.000000 00:00:01.000000 00:00:00.500000 bar\n" | ||
if outB.String() != wantS { | ||
t.Fail() | ||
t.Logf("GOT bytes: %v", outB) | ||
t.Logf("WANT string:\n%v", wantS) | ||
t.Logf("GOT string:\n%v", outB.String()) | ||
} | ||
} | ||
|
||
func TestTimestampTwoLinesHalfSecondColored(t *testing.T) { | ||
if os.Getenv("TZ") != "UTC" { | ||
t.Fail() | ||
t.Logf("Run tests with TZ=UTC!") | ||
return | ||
} | ||
c := MonotonicClock(fakeEpoch, 0, 0, 500_000_000) | ||
in := bytes.NewBufferString("foo\nbar") | ||
var outB bytes.Buffer | ||
timestamp(c, in, &outB, true) | ||
wantS := "1998-10-22 21:15:09.500000 00:00:00.500000 00:00:00.500000 foo\n1998-10-22 21:15:10.000000 00:00:01.000000 00:00:00.500000 bar\n" | ||
if outB.String() != wantS { | ||
t.Fail() | ||
t.Logf("GOT bytes: %v", outB) | ||
t.Logf("WANT string:\n%v", wantS) | ||
t.Logf("GOT string:\n%v", outB.String()) | ||
} | ||
} | ||
|
||
func TestTimestampTwoLinesOneHalfSecond(t *testing.T) { | ||
if os.Getenv("TZ") != "UTC" { | ||
t.Fail() | ||
t.Logf("Run tests with TZ=UTC!") | ||
return | ||
} | ||
c := MonotonicClock(fakeEpoch, 0, 1, 500_000_000) | ||
in := bytes.NewBufferString("foo\nbar") | ||
var outB bytes.Buffer | ||
timestamp(c, in, &outB, false) | ||
wantS := "1998-10-22 21:15:10.500000 00:00:01.500000 00:00:01.500000 foo\n1998-10-22 21:15:12.000000 00:00:03.000000 00:00:01.500000 bar\n" | ||
if outB.String() != wantS { | ||
t.Fail() | ||
t.Logf("GOT bytes: %v", outB) | ||
t.Logf("WANT string:\n%v", wantS) | ||
t.Logf("GOT string:\n%v", outB.String()) | ||
} | ||
} | ||
|
||
func TestTimestampTwoLinesOneHalfSecondColored(t *testing.T) { | ||
if os.Getenv("TZ") != "UTC" { | ||
t.Fail() | ||
t.Logf("Run tests with TZ=UTC!") | ||
return | ||
} | ||
c := MonotonicClock(fakeEpoch, 0, 1, 500_000_000) | ||
in := bytes.NewBufferString("foo\nbar") | ||
var outB bytes.Buffer | ||
timestamp(c, in, &outB, true) | ||
wantS := "1998-10-22 21:15:10.500000 00:00:01.500000 \033[33m00:00:01.500000\033[0m foo\n1998-10-22 21:15:12.000000 00:00:03.000000 \033[33m00:00:01.500000\033[0m bar\n" | ||
if outB.String() != wantS { | ||
t.Fail() | ||
t.Logf("WANT bytes: %v", []byte(wantS)) | ||
t.Logf("GOT bytes: %v", outB) | ||
t.Logf("WANT string:\n%v", wantS) | ||
t.Logf("GOT string:\n%v", outB.String()) | ||
} | ||
} | ||
|
||
func TestTimestampTwoLinesNinetySeconds(t *testing.T) { | ||
if os.Getenv("TZ") != "UTC" { | ||
t.Fail() | ||
t.Logf("Run tests with TZ=UTC!") | ||
return | ||
} | ||
c := MonotonicClock(fakeEpoch, 0, 90, 0) | ||
in := bytes.NewBufferString("foo\nbar") | ||
var outB bytes.Buffer | ||
timestamp(c, in, &outB, false) | ||
wantS := "1998-10-22 21:16:39.000000 00:01:30.000000 00:01:30.000000 foo\n1998-10-22 21:18:09.000000 00:03:00.000000 00:01:30.000000 bar\n" | ||
if outB.String() != wantS { | ||
t.Fail() | ||
t.Logf("GOT bytes: %v", outB) | ||
t.Logf("WANT string:\n%v", wantS) | ||
t.Logf("GOT string:\n%v", outB.String()) | ||
} | ||
} | ||
|
||
func TestTimestampTwoLinesNinetySecondsColored(t *testing.T) { | ||
if os.Getenv("TZ") != "UTC" { | ||
t.Fail() | ||
t.Logf("Run tests with TZ=UTC!") | ||
return | ||
} | ||
c := MonotonicClock(fakeEpoch, 0, 90, 0) | ||
in := bytes.NewBufferString("foo\nbar") | ||
var outB bytes.Buffer | ||
timestamp(c, in, &outB, true) | ||
wantS := "1998-10-22 21:16:39.000000 00:01:30.000000 \033[31m00:01:30.000000\033[0m foo\n1998-10-22 21:18:09.000000 00:03:00.000000 \033[31m00:01:30.000000\033[0m bar\n" | ||
if outB.String() != wantS { | ||
t.Fail() | ||
t.Logf("GOT bytes: %v", outB) | ||
t.Logf("WANT string:\n%v", wantS) | ||
t.Logf("GOT string:\n%v", outB.String()) | ||
} | ||
} |