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

Side by Side Diff: go/src/infra/libs/clock/testclock/testclock.go

Issue 1154213012: Add context-aware "time" library wrapper. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Added coverage files. Created 5 years, 6 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package testclock
6
7 import (
8 "sync"
9 "time"
10
11 "infra/libs/clock"
12 )
13
14 // TestClock is a Clock interface with additional methods to help instrument it.
15 type TestClock interface {
16 clock.Clock
17 Set(time.Time)
18 Add(time.Duration)
19 SetTimerCallback(TimerCallback)
20 }
21
22 // TimerCallback that can be invoked when a timer has been set. This is useful
23 // for sychronizing state when testing.
24 type TimerCallback func(clock.Timer)
25
26 type cancelFunc func()
27
28 // testClock is a test-oriented implementation of the 'Clock' interface.
29 //
30 // This implementation's Clock responses are configurable by modifying its
31 // member variables. Time-based events are explicitly triggered by sending on a
32 // Timer instance's channel.
33 type testClock struct {
34 sync.RWMutex
35
36 now time.Time // The current clock time.
37 timerCond *sync.Cond // Condition used to manage timer blocking.
38
39 timerCallback TimerCallback // Optional callback when a timer has been s et.
40 }
41
42 var _ TestClock = (*testClock)(nil)
43
44 // New returns a TestClock instance set at the specified time.
45 func New(now time.Time) TestClock {
46 c := testClock{
47 now: now,
48 }
49 c.timerCond = sync.NewCond(&c)
50 return &c
51 }
52
53 func (c *testClock) Now() time.Time {
54 c.RLock()
55 defer c.RUnlock()
56
57 return c.now
58 }
59
60 func (c *testClock) Sleep(d time.Duration) {
61 <-c.After(d)
62 }
63
64 func (c *testClock) NewTimer() clock.Timer {
65 return newTimer(c)
66 }
67
68 func (c *testClock) After(d time.Duration) <-chan time.Time {
69 t := c.NewTimer()
70 t.Reset(d)
71 return t.GetC()
72 }
73
74 func (c *testClock) Set(t time.Time) {
75 c.Lock()
76 defer c.Unlock()
77
78 c.setTimeLocked(t)
79 }
80
81 func (c *testClock) Add(d time.Duration) {
82 c.Lock()
83 defer c.Unlock()
84
85 c.setTimeLocked(c.now.Add(d))
86 }
87
88 func (c *testClock) setTimeLocked(t time.Time) {
89 if t.Before(c.now) {
90 panic("Cannot go backwards in time. You're not Doc Brown.")
91 }
92 c.now = t
93
94 // Unblock any blocking timers that are waiting on our lock.
95 c.pokeTimers(true)
96 }
97
98 func (c *testClock) SetTimerCallback(callback TimerCallback) {
99 c.Lock()
100 defer c.Unlock()
101
102 c.timerCallback = callback
103 }
104
105 func (c *testClock) getTimerCallback() TimerCallback {
106 c.Lock()
107 defer c.Unlock()
108
109 return c.timerCallback
110 }
111
112 func (c *testClock) signalTimerSet(t clock.Timer) {
113 callback := c.getTimerCallback()
114 if callback != nil {
115 callback(t)
116 }
117 }
118
119 func (c *testClock) pokeTimers(locked bool) {
120 if !locked {
121 c.Lock()
122 defer c.Unlock()
123 }
124
125 c.timerCond.Broadcast()
126 }
127
128 // invokeAt invokes the specified callback when the Clock has advanced at
129 // or after the specified threshold.
130 //
131 // The returned cancelFunc can be used to cancel the blocking. If the cancel
132 // function is invoked before the callback, the callback will not be invoked.
133 func (c *testClock) invokeAt(threshold time.Time, callback func(time.Time)) canc elFunc {
134 stopC := make(chan struct{})
135 finishedC := make(chan struct{})
136
137 // Our control goroutine will monitor both time and stop signals. It wil l only
138 // terminate when stopC has been closed.
139 //
140 // The lock that we take our here is owned by the following goroutine.
141 c.Lock()
142 go func() {
143 defer func() {
144 now := c.now
145 c.Unlock()
146 close(finishedC)
147 callback(now)
148 }()
149
150 for {
151 // Determine if we are past our signalling threshold. We can safely access
152 // our clock's time member directly, since we hold its l ock from the
153 // condition firing.
154 if !c.now.Before(threshold) {
155 return
156 }
157
158 // Wait for a signal from our clock's condition.
159 c.timerCond.Wait()
160
161 // Have we been stopped?
162 select {
163 case <-stopC:
164 return
165
166 default:
167 // Nope.
168 }
169 }
170 }()
171
172 return func() {
173 // Close our stop channel and block pending goroutine terminatio n.
174 close(stopC)
175 c.pokeTimers(false)
176 <-finishedC
177 }
178 }
OLDNEW
« no previous file with comments | « go/src/infra/libs/clock/testclock/context.go ('k') | go/src/infra/libs/clock/testclock/testclock.infra_testing » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698