From 9dab28ce6c50bc9a2e201df1c67c1314ca46fb60 Mon Sep 17 00:00:00 2001 From: "Niklas Q. Nielsen" Date: Fri, 13 Nov 2015 17:37:13 -0800 Subject: [PATCH] Added new Chrono structure to keep track of virtual time --- core/chrono.go | 71 +++++++++++++++++++++++++++++++++++++ core/chrono_test.go | 86 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 core/chrono.go create mode 100644 core/chrono_test.go diff --git a/core/chrono.go b/core/chrono.go new file mode 100644 index 000000000..4d79f9dd3 --- /dev/null +++ b/core/chrono.go @@ -0,0 +1,71 @@ +/* +http://www.apache.org/licenses/LICENSE-2.0.txt + + +Copyright 2015 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package core + +import ( + "time" +) + +// The chrono structure provides an artificial notion of time that can be +// stopped, forwarded to make testing of timed code deterministic. +type chrono struct { + skew time.Duration + paused bool + pausedAt time.Time +} + +// Now() should replace usage of time.Now(). If chrono has been paused, Now() +// returns the time when it was paused. If there is any skew (due to forwarding +// or reversing), this is always added to the end time. +func (c *chrono) Now() time.Time { + var now time.Time + if c.paused { + now = c.pausedAt + } else { + now = time.Now() + } + return now.Add(c.skew) +} + +// Forwards time of chrono with skew time. This can be used in both running and +// paused mode. +func (c *chrono) Forward(skew time.Duration) { + c.skew = skew +} + +// Resets any previous set clock skew. +func (c *chrono) Reset() { + c.skew = 0 +} + +// "Stops" time by recording current time and shortcircuit Now() to return this +// time instead of the actual time (plus skew). +func (c *chrono) Pause() { + c.pausedAt = c.Now() + c.paused = true +} + +// Continues time after having been paused. This has no effect if clock is +// already running. +func (c *chrono) Continue() { + c.paused = false +} + +var Chrono chrono diff --git a/core/chrono_test.go b/core/chrono_test.go new file mode 100644 index 000000000..32ffc4613 --- /dev/null +++ b/core/chrono_test.go @@ -0,0 +1,86 @@ +/* +http://www.apache.org/licenses/LICENSE-2.0.txt + + +Copyright 2015 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package core + +import ( + "testing" + "time" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestChrono(t *testing.T) { + Convey("Given a new chrono", t, func() { + var Chrono chrono + Convey("Forward time should be 0", func() { + So(Chrono.skew, ShouldEqual, 0) + }) + + Convey("When forwarded for 30 seconds", func() { + Chrono.Forward(30 * time.Second) + Convey("Forward time should be 30 seconds", func() { + So(Chrono.skew, ShouldEqual, 30*time.Second) + }) + }) + + Convey("After pausing time", func() { + Chrono.Pause() + before := Chrono.Now() + Convey("And waiting 10 milliseconds", func() { + time.Sleep(10 * time.Millisecond) + + Convey("Time should stand still", func() { + So(Chrono.Now().Equal(before), ShouldBeTrue) + }) + }) + + Convey("When forwarding another hour", func() { + Chrono.Forward(1 * time.Hour) + + Convey("Time should be exactly when we stopped plus one hour", func() { + So(Chrono.Now().Equal(before.Add(1*time.Hour)), ShouldBeTrue) + }) + }) + + Convey("When resetting time", func() { + Chrono.Reset() + Convey("Forward time should be 0", func() { + So(Chrono.skew, ShouldEqual, 0) + }) + + Convey("And time should be the same as when we paused", func() { + So(Chrono.Now().Equal(before), ShouldBeTrue) + }) + }) + }) + + Convey("Continuing time", func() { + Chrono.Continue() + before := Chrono.Now() + Convey("And waiting 10 milliseconds", func() { + time.Sleep(10 * time.Millisecond) + + Convey("Time should progress", func() { + So(Chrono.Now().After(before), ShouldBeTrue) + }) + }) + }) + }) +}