OLD | NEW |
---|---|
(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 f or | |
23 // sychronizing state when testing. | |
iannucci
2015/06/03 17:37:20
I think we're still trying to wrap 80 on comments
dnj
2015/06/03 18:21:27
I did a best-effort run. I wonder if this is enfor
| |
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 membe r | |
31 // variables. Time-based events are explicitly triggered by sending on a Timer | |
32 // 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 func (c *testClock) whenTimePasses(threshold time.Time, callback func(time.Time) ) cancelFunc { | |
129 stopC := make(chan struct{}) | |
130 finishedC := make(chan struct{}) | |
131 | |
132 // Our control goroutine will monitor both time and stop signals. It wil l only terminate | |
133 // when stopC has been closed. | |
134 // | |
135 // The lock that we take our here is owned by the following goroutine. | |
136 c.Lock() | |
137 go func() { | |
138 defer func() { | |
139 now := c.now | |
140 c.Unlock() | |
141 close(finishedC) | |
142 callback(now) | |
143 }() | |
144 | |
145 for { | |
146 // Determine if we are past our signalling threshold. We can safely access our | |
147 // clock's time member directly, since we hold its lock from the condition firing. | |
148 if !c.now.Before(threshold) { | |
149 return | |
150 } | |
151 | |
152 // Wait for a signal from our clock's condition. | |
153 c.timerCond.Wait() | |
154 | |
155 // Have we been stopped? | |
156 select { | |
157 case <-stopC: | |
158 return | |
159 | |
160 default: | |
161 // Nope. | |
162 } | |
163 } | |
164 }() | |
165 | |
166 return func() { | |
167 // Close our stop channel and block pending goroutine terminatio n. | |
168 close(stopC) | |
169 c.pokeTimers(false) | |
170 <-finishedC | |
171 } | |
172 } | |
OLD | NEW |