Chromium Code Reviews| 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..8203b1ebbe6d61a1180eab64f1edc1821f97d9e2 100644 |
| --- a/common/clock/testclock/testtimer_test.go |
| +++ b/common/clock/testclock/testtimer_test.go |
| @@ -5,10 +5,15 @@ |
| package testclock |
| import ( |
| + "fmt" |
| + "strings" |
| + "sync" |
| "testing" |
| "time" |
| "github.com/luci/luci-go/common/clock" |
| + "golang.org/x/net/context" |
| + |
| . "github.com/smartystreets/goconvey/convey" |
| ) |
| @@ -16,14 +21,17 @@ 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 +44,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 +69,96 @@ 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(`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) |
|
iannucci
2016/02/10 22:30:28
add test for block on channel and concurrent Stop(
dnj (Google)
2016/02/11 01:26:55
Done.
|
| }) |
| }) |
| - 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 +175,59 @@ 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 flags {"A", "B"}`, t, func() { |
|
iannucci
2016/02/10 22:30:28
s/flags/tags
dnj (Google)
2016/02/11 01:26:55
Done.
|
| + 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), ShouldBeFalse) |
| + 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", "B"} and positive for {}.`, func() { |
| + clk := clock.GetSystemClock() |
| + t := clk.NewTimer(c) |
| + |
| + So(HasTags(t), ShouldBeTrue) |
| + So(HasTags(t, "A", "B"), ShouldBeFalse) |
| + }) |
| + }) |
| +} |