[fxtime] Add Stopwatch interface
This is used in infra/infra/tilo to measure invocation and test
run times.
IN-597 #comment
Change-Id: I52ef9636085f510a9707acf84808115202d3c5a3
diff --git a/fxtime/time.go b/fxtime/time.go
index 70c06ad..d4c14e4 100644
--- a/fxtime/time.go
+++ b/fxtime/time.go
@@ -23,6 +23,33 @@
return fixedClock{moment: moment}
}
+// Stopwatch measures time intervals.
+type Stopwatch interface {
+ Restart() time.Time
+ Elapsed() time.Duration
+}
+
+// NewStopwatch returns a new stopwatch. The watch's initial time is set to clock.Now()
+func NewStopwatch(clock Clock) Stopwatch {
+ sw := &stopwatch{clock: clock}
+ sw.Restart()
+ return sw
+}
+
+type stopwatch struct {
+ clock Clock
+ start time.Time
+}
+
+func (s *stopwatch) Restart() time.Time {
+ s.start = s.clock.Now()
+ return s.start
+}
+
+func (s stopwatch) Elapsed() time.Duration {
+ return s.clock.Now().Sub(s.start)
+}
+
type clock struct{}
func (c clock) Now() time.Time {
diff --git a/fxtime/time_test.go b/fxtime/time_test.go
new file mode 100644
index 0000000..cbdbfb1
--- /dev/null
+++ b/fxtime/time_test.go
@@ -0,0 +1,74 @@
+package fxtime_test
+
+import (
+ "testing"
+ "time"
+
+ "fuchsia.googlesource.com/infra/infra/fxtime"
+)
+
+func TestFixedClock(t *testing.T) {
+ t.Run("Now should return a fixed time", func(t *testing.T) {
+ now := time.Date(1, 2, 3, 4, 5, 6, 7, time.UTC)
+ clock := fxtime.NewFixedClock(now)
+ expectTime(t, now, clock.Now())
+ expectTime(t, now, clock.Now())
+ })
+}
+
+func TestStopwatch(t *testing.T) {
+ now := time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC)
+
+ t.Run("Restart should return the current time", func(t *testing.T) {
+ clock := &TestClock{
+ nowTimes: []time.Time{now, now.Add(time.Hour)},
+ }
+ stopwatch := fxtime.NewStopwatch(clock)
+ expectTime(t, clock.nowTimes[1], stopwatch.Restart())
+ })
+
+ t.Run("Elapsed should return the duration since Restart() was last called",
+ func(t *testing.T) {
+ clock := &TestClock{
+ nowTimes: []time.Time{
+ now,
+ now.Add(time.Hour),
+ now.Add(24 * time.Hour),
+ now.Add(72 * time.Hour),
+ },
+ }
+ stopwatch := fxtime.NewStopwatch(clock)
+ expectTime(t, clock.nowTimes[1], stopwatch.Restart())
+ duration := clock.nowTimes[2].Sub(clock.nowTimes[1])
+ expectDuration(t, duration, stopwatch.Elapsed())
+ duration = clock.nowTimes[3].Sub(clock.nowTimes[1])
+ expectDuration(t, duration, stopwatch.Elapsed())
+ })
+}
+
+func expectTime(t *testing.T, expected, actual time.Time) {
+ if expected != actual {
+ t.Fatalf("expected time %v but got %v", expected, actual)
+ }
+}
+
+func expectDuration(t *testing.T, expected, actual time.Duration) {
+ if expected != actual {
+ t.Fatalf("expected duration %v but got %v", expected, actual)
+ }
+}
+
+// TestClock is used to test Stopwatch.
+type TestClock struct {
+ // List of Times to return when Now is called.
+ nowTimes []time.Time
+ // Current index in nowTimes
+ i uint
+}
+
+// Returns the current time. This will panic if called more than len(nowTimes) times.
+func (c *TestClock) Now() time.Time {
+ t := c.nowTimes[c.i]
+ c.i += 1
+ return t
+}