Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(52)

Unified Diff: common/clock/testclock/testtimer_test.go

Issue 1679023005: Add Context cancellation to clock. (Closed) Base URL: https://github.com/luci/luci-go@master
Patch Set: Actually upload the patch. Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « common/clock/testclock/testtimer.go ('k') | common/clock/timer.go » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: common/clock/testclock/testtimer_test.go
diff --git a/common/clock/testclock/testtimer_test.go b/common/clock/testclock/testtimer_test.go
index 90234e43ada67a3c9208c30444d22721c9b8e2a6..7e652ab0c10ad6114b81a735e083c06d849e524c 100644
--- a/common/clock/testclock/testtimer_test.go
+++ b/common/clock/testclock/testtimer_test.go
@@ -5,25 +5,40 @@
package testclock
import (
+ "fmt"
+ "runtime"
+ "strings"
+ "sync"
"testing"
"time"
"github.com/luci/luci-go/common/clock"
+ "golang.org/x/net/context"
+
. "github.com/smartystreets/goconvey/convey"
)
+// trashTimer is a useless implementation of clock.Timer specifically designed
+// to exist and not be a test timer type.
+type trashTimer struct {
+ clock.Timer
+}
+
func TestTestTimer(t *testing.T) {
t.Parallel()
Convey(`A testing clock instance`, t, func() {
- now := time.Date(2015, 01, 01, 00, 00, 00, 00, time.UTC)
- c := New(now)
+ ctx, cancelFunc := context.WithCancel(context.Background())
+ defer cancelFunc()
+
+ now := TestTimeLocal
+ clk := New(now)
Convey(`A timer instance`, func() {
- t := c.NewTimer()
+ t := clk.NewTimer(ctx)
Convey(`Should have a non-nil C.`, func() {
- So(t.GetC(), ShouldBeNil)
+ So(t.GetC(), ShouldNotBeNil)
})
Convey(`When activated`, func() {
@@ -36,16 +51,16 @@ func TestTestTimer(t *testing.T) {
Convey(`When stopped, should return active.`, func() {
So(t.Stop(), ShouldBeTrue)
- So(t.GetC(), ShouldBeNil)
+ So(t.GetC(), ShouldNotBeNil)
Convey(`And when stopped again, should return inactive.`, func() {
So(t.Stop(), ShouldBeFalse)
- So(t.GetC(), ShouldBeNil)
+ So(t.GetC(), ShouldNotBeNil)
})
})
Convey(`When stopped after expiring, should not have a signal.`, func() {
- c.Add(1 * time.Second)
+ clk.Add(1 * time.Second)
So(t.Stop(), ShouldBeTrue)
var signalled bool
@@ -61,25 +76,120 @@ func TestTestTimer(t *testing.T) {
Convey(`Should successfully signal.`, func() {
So(t.Reset(1*time.Second), ShouldBeFalse)
- c.Add(1 * time.Second)
+ clk.Add(1 * time.Second)
- So(t.GetC(), ShouldNotBeNil)
- So(<-t.GetC(), ShouldResemble, now.Add(1*time.Second))
+ So(<-t.GetC(), ShouldResemble, clock.TimerResult{Time: now.Add(1 * time.Second)})
+ })
+
+ Convey(`Should signal immediately if the timer is in the past.`, func() {
+ So(t.Reset(-1*time.Second), ShouldBeFalse)
+ So(<-t.GetC(), ShouldResemble, clock.TimerResult{Time: now})
+ })
+
+ Convey(`Will trigger immediately if the Context is canceled when reset.`, func() {
+ cancelFunc()
+
+ // Works for the first timer?
+ So(t.Reset(time.Hour), ShouldBeFalse)
+ So((<-t.GetC()).Err, ShouldEqual, context.Canceled)
+
+ // Works for the second timer?
+ So(t.Reset(time.Hour), ShouldBeFalse)
+ So((<-t.GetC()).Err, ShouldEqual, context.Canceled)
+ })
+
+ Convey(`Will trigger when the Context is canceled.`, func() {
+ clk.SetTimerCallback(func(time.Duration, clock.Timer) {
+ cancelFunc()
+ })
+
+ // Works for the first timer?
+ So(t.Reset(time.Hour), ShouldBeFalse)
+ So((<-t.GetC()).Err, ShouldEqual, context.Canceled)
+
+ // Works for the second timer?
+ So(t.Reset(time.Hour), ShouldBeFalse)
+ So((<-t.GetC()).Err, ShouldEqual, context.Canceled)
+ })
+
+ Convey(`Will use the same channel when reset.`, func() {
+ timerC := t.GetC()
+ t.Reset(time.Second)
+ clk.Add(time.Second)
+ So(<-timerC, ShouldResemble, clock.TimerResult{Time: now.Add(1 * time.Second)})
+ })
+
+ Convey(`Will not signal the timer channel if stopped.`, func() {
+ t.Reset(time.Second)
+ t.Stop()
+ clk.Add(time.Second)
+
+ runtime.Gosched()
+
+ triggered := false
+ select {
+ case <-t.GetC():
+ triggered = true
+ default:
+ break
+ }
+ So(triggered, ShouldBeFalse)
+ })
+
+ Convey(`Can set and retrieve timer tags.`, func() {
+ var tagMu sync.Mutex
+ tagMap := map[string]struct{}{}
+
+ // On the last timer callback, advance time past the timer threshold.
+ timers := make([]clock.Timer, 10)
+ count := 0
+ clk.SetTimerCallback(func(_ time.Duration, t clock.Timer) {
+ tagMu.Lock()
+ defer tagMu.Unlock()
+
+ tagMap[strings.Join(GetTags(t), "")] = struct{}{}
+ count++
+ if count == len(timers) {
+ clk.Add(time.Second)
+ }
+ })
+
+ for i := range timers {
+ t := clk.NewTimer(clock.Tag(ctx, fmt.Sprintf("%d", i)))
+ t.Reset(time.Second)
+ timers[i] = t
+ }
+
+ // Wait until all timers have expired.
+ for _, t := range timers {
+ <-t.GetC()
+ }
+
+ // Did we see all of their tags?
+ for i := range timers {
+ _, ok := tagMap[fmt.Sprintf("%d", i)]
+ So(ok, ShouldBeTrue)
+ }
+ })
+
+ Convey(`A non-test timer has a nil tag.`, func() {
+ t := trashTimer{}
+ So(GetTags(t), ShouldBeNil)
})
})
- Convey(`Multiple goroutines using the timer...`, func() {
+ Convey(`Multiple goroutines using timers...`, func() {
// Mark when timers are started, so we can ensure that our signalling
// happens after the timers have been instantiated.
timerStartedC := make(chan bool)
- c.SetTimerCallback(func(_ time.Duration, _ clock.Timer) {
+ clk.SetTimerCallback(func(_ time.Duration, _ clock.Timer) {
timerStartedC <- true
})
- resultC := make(chan time.Time)
+ resultC := make(chan clock.TimerResult)
for i := time.Duration(0); i < 5; i++ {
go func(d time.Duration) {
- timer := c.NewTimer()
+ timer := clk.NewTimer(ctx)
timer.Reset(d)
resultC <- <-timer.GetC()
}(i * time.Second)
@@ -96,21 +206,57 @@ func TestTestTimer(t *testing.T) {
}
// Advance the clock to +2s. Three timers should signal.
- c.Set(now.Add(2 * time.Second))
+ clk.Set(now.Add(2 * time.Second))
<-resultC
<-resultC
<-resultC
So(moreResults(), ShouldBeFalse)
// Advance clock to +3s. One timer should signal.
- c.Set(now.Add(3 * time.Second))
+ clk.Set(now.Add(3 * time.Second))
<-resultC
So(moreResults(), ShouldBeFalse)
// Advance clock to +10s. One final timer should signal.
- c.Set(now.Add(10 * time.Second))
+ clk.Set(now.Add(10 * time.Second))
<-resultC
So(moreResults(), ShouldBeFalse)
})
})
}
+
+func TestTimerTags(t *testing.T) {
+ t.Parallel()
+
+ Convey(`A context with tags {"A", "B"}`, t, func() {
+ c := clock.Tag(clock.Tag(context.Background(), "A"), "B")
+
+ Convey(`Has tags, {"A", "B"}`, func() {
+ So(clock.Tags(c), ShouldResemble, []string{"A", "B"})
+ })
+
+ Convey(`Will be retained by a testclock.Timer.`, func() {
+ tc := New(TestTimeUTC)
+ t := tc.NewTimer(c)
+
+ So(GetTags(t), ShouldResemble, []string{"A", "B"})
+
+ Convey(`The timer tests positive for tags {"A", "B"}`, func() {
+ So(HasTags(t, "A", "B"), ShouldBeTrue)
+ })
+
+ Convey(`The timer tests negative for tags {"A"}, {"B"}, and {"A", "C"}`, func() {
+ So(HasTags(t, "A"), ShouldBeFalse)
+ So(HasTags(t, "B"), ShouldBeFalse)
+ So(HasTags(t, "A", "C"), ShouldBeFalse)
+ })
+ })
+
+ Convey(`A non-test timer tests negative for {"A"} and {"A", "B"}.`, func() {
+ t := &trashTimer{}
+
+ So(HasTags(t, "A"), ShouldBeFalse)
+ So(HasTags(t, "A", "B"), ShouldBeFalse)
+ })
+ })
+}
« no previous file with comments | « common/clock/testclock/testtimer.go ('k') | common/clock/timer.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698