├── .github └── workflows │ └── build.yml ├── LICENSE ├── README.md ├── clock.go ├── clock_test.go ├── context.go ├── context_test.go └── go.mod /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | 11 | build: 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | # Test on one "earliest" Go as well as the latest two major Go 17 | # versions. If some change requires bumping the "earliest" Go versiion, 18 | # that's fine - just include that in the commit description so that 19 | # users are aware. 20 | go: ["1.16.x", "1.19.x", "1.20.x"] 21 | 22 | steps: 23 | - uses: actions/checkout@v2 24 | 25 | - name: Set up Go 26 | uses: actions/setup-go@v2 27 | with: 28 | go-version: ${{ matrix.go }} 29 | 30 | - name: Load cached dependencies 31 | uses: actions/cache@v2 32 | with: 33 | path: ~/go/pkg/mod 34 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 35 | 36 | - name: Test 37 | run: go test -v -race ./... 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Ben Johnson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | clock 2 | ===== 3 | 4 | [![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/mod/github.com/benbjohnson/clock) 5 | 6 | Clock is a small library for mocking time in Go. It provides an interface 7 | around the standard library's [`time`][time] package so that the application 8 | can use the realtime clock while tests can use the mock clock. 9 | 10 | This module is no longer maintained. 11 | 12 | [time]: https://pkg.go.dev/github.com/benbjohnson/clock 13 | 14 | ## Usage 15 | 16 | ### Realtime Clock 17 | 18 | Your application can maintain a `Clock` variable that will allow realtime and 19 | mock clocks to be interchangeable. For example, if you had an `Application` type: 20 | 21 | ```go 22 | import "github.com/benbjohnson/clock" 23 | 24 | type Application struct { 25 | Clock clock.Clock 26 | } 27 | ``` 28 | 29 | You could initialize it to use the realtime clock like this: 30 | 31 | ```go 32 | var app Application 33 | app.Clock = clock.New() 34 | ... 35 | ``` 36 | 37 | Then all timers and time-related functionality should be performed from the 38 | `Clock` variable. 39 | 40 | 41 | ### Mocking time 42 | 43 | In your tests, you will want to use a `Mock` clock: 44 | 45 | ```go 46 | import ( 47 | "testing" 48 | 49 | "github.com/benbjohnson/clock" 50 | ) 51 | 52 | func TestApplication_DoSomething(t *testing.T) { 53 | mock := clock.NewMock() 54 | app := Application{Clock: mock} 55 | ... 56 | } 57 | ``` 58 | 59 | Now that you've initialized your application to use the mock clock, you can 60 | adjust the time programmatically. The mock clock always starts from the Unix 61 | epoch (midnight UTC on Jan 1, 1970). 62 | 63 | 64 | ### Controlling time 65 | 66 | The mock clock provides the same functions that the standard library's `time` 67 | package provides. For example, to find the current time, you use the `Now()` 68 | function: 69 | 70 | ```go 71 | mock := clock.NewMock() 72 | 73 | // Find the current time. 74 | mock.Now().UTC() // 1970-01-01 00:00:00 +0000 UTC 75 | 76 | // Move the clock forward. 77 | mock.Add(2 * time.Hour) 78 | 79 | // Check the time again. It's 2 hours later! 80 | mock.Now().UTC() // 1970-01-01 02:00:00 +0000 UTC 81 | ``` 82 | 83 | Timers and Tickers are also controlled by this same mock clock. They will only 84 | execute when the clock is moved forward: 85 | 86 | ```go 87 | mock := clock.NewMock() 88 | count := 0 89 | 90 | // Kick off a timer to increment every 1 mock second. 91 | go func() { 92 | ticker := mock.Ticker(1 * time.Second) 93 | for { 94 | <-ticker.C 95 | count++ 96 | } 97 | }() 98 | runtime.Gosched() 99 | 100 | // Move the clock forward 10 seconds. 101 | mock.Add(10 * time.Second) 102 | 103 | // This prints 10. 104 | fmt.Println(count) 105 | ``` 106 | -------------------------------------------------------------------------------- /clock.go: -------------------------------------------------------------------------------- 1 | package clock 2 | 3 | import ( 4 | "context" 5 | "sort" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | // Re-export of time.Duration 11 | type Duration = time.Duration 12 | 13 | // Clock represents an interface to the functions in the standard library time 14 | // package. Two implementations are available in the clock package. The first 15 | // is a real-time clock which simply wraps the time package's functions. The 16 | // second is a mock clock which will only change when 17 | // programmatically adjusted. 18 | type Clock interface { 19 | After(d time.Duration) <-chan time.Time 20 | AfterFunc(d time.Duration, f func()) *Timer 21 | Now() time.Time 22 | Since(t time.Time) time.Duration 23 | Until(t time.Time) time.Duration 24 | Sleep(d time.Duration) 25 | Tick(d time.Duration) <-chan time.Time 26 | Ticker(d time.Duration) *Ticker 27 | Timer(d time.Duration) *Timer 28 | WithDeadline(parent context.Context, d time.Time) (context.Context, context.CancelFunc) 29 | WithTimeout(parent context.Context, t time.Duration) (context.Context, context.CancelFunc) 30 | } 31 | 32 | // New returns an instance of a real-time clock. 33 | func New() Clock { 34 | return &clock{} 35 | } 36 | 37 | // clock implements a real-time clock by simply wrapping the time package functions. 38 | type clock struct{} 39 | 40 | func (c *clock) After(d time.Duration) <-chan time.Time { return time.After(d) } 41 | 42 | func (c *clock) AfterFunc(d time.Duration, f func()) *Timer { 43 | return &Timer{timer: time.AfterFunc(d, f)} 44 | } 45 | 46 | func (c *clock) Now() time.Time { return time.Now() } 47 | 48 | func (c *clock) Since(t time.Time) time.Duration { return time.Since(t) } 49 | 50 | func (c *clock) Until(t time.Time) time.Duration { return time.Until(t) } 51 | 52 | func (c *clock) Sleep(d time.Duration) { time.Sleep(d) } 53 | 54 | func (c *clock) Tick(d time.Duration) <-chan time.Time { return time.Tick(d) } 55 | 56 | func (c *clock) Ticker(d time.Duration) *Ticker { 57 | t := time.NewTicker(d) 58 | return &Ticker{C: t.C, ticker: t} 59 | } 60 | 61 | func (c *clock) Timer(d time.Duration) *Timer { 62 | t := time.NewTimer(d) 63 | return &Timer{C: t.C, timer: t} 64 | } 65 | 66 | func (c *clock) WithDeadline(parent context.Context, d time.Time) (context.Context, context.CancelFunc) { 67 | return context.WithDeadline(parent, d) 68 | } 69 | 70 | func (c *clock) WithTimeout(parent context.Context, t time.Duration) (context.Context, context.CancelFunc) { 71 | return context.WithTimeout(parent, t) 72 | } 73 | 74 | // Mock represents a mock clock that only moves forward programmically. 75 | // It can be preferable to a real-time clock when testing time-based functionality. 76 | type Mock struct { 77 | // mu protects all other fields in this struct, and the data that they 78 | // point to. 79 | mu sync.Mutex 80 | 81 | now time.Time // current time 82 | timers clockTimers // tickers & timers 83 | } 84 | 85 | // NewMock returns an instance of a mock clock. 86 | // The current time of the mock clock on initialization is the Unix epoch. 87 | func NewMock() *Mock { 88 | return &Mock{now: time.Unix(0, 0)} 89 | } 90 | 91 | // Add moves the current time of the mock clock forward by the specified duration. 92 | // This should only be called from a single goroutine at a time. 93 | func (m *Mock) Add(d time.Duration) { 94 | // Calculate the final current time. 95 | m.mu.Lock() 96 | t := m.now.Add(d) 97 | m.mu.Unlock() 98 | 99 | // Continue to execute timers until there are no more before the new time. 100 | for { 101 | if !m.runNextTimer(t) { 102 | break 103 | } 104 | } 105 | 106 | // Ensure that we end with the new time. 107 | m.mu.Lock() 108 | m.now = t 109 | m.mu.Unlock() 110 | 111 | // Give a small buffer to make sure that other goroutines get handled. 112 | gosched() 113 | } 114 | 115 | // Set sets the current time of the mock clock to a specific one. 116 | // This should only be called from a single goroutine at a time. 117 | func (m *Mock) Set(t time.Time) { 118 | // Continue to execute timers until there are no more before the new time. 119 | for { 120 | if !m.runNextTimer(t) { 121 | break 122 | } 123 | } 124 | 125 | // Ensure that we end with the new time. 126 | m.mu.Lock() 127 | m.now = t 128 | m.mu.Unlock() 129 | 130 | // Give a small buffer to make sure that other goroutines get handled. 131 | gosched() 132 | } 133 | 134 | // WaitForAllTimers sets the clock until all timers are expired 135 | func (m *Mock) WaitForAllTimers() time.Time { 136 | // Continue to execute timers until there are no more 137 | for { 138 | m.mu.Lock() 139 | if len(m.timers) == 0 { 140 | m.mu.Unlock() 141 | return m.Now() 142 | } 143 | 144 | sort.Sort(m.timers) 145 | next := m.timers[len(m.timers)-1].Next() 146 | m.mu.Unlock() 147 | m.Set(next) 148 | } 149 | } 150 | 151 | // runNextTimer executes the next timer in chronological order and moves the 152 | // current time to the timer's next tick time. The next time is not executed if 153 | // its next time is after the max time. Returns true if a timer was executed. 154 | func (m *Mock) runNextTimer(max time.Time) bool { 155 | m.mu.Lock() 156 | 157 | // Sort timers by time. 158 | sort.Sort(m.timers) 159 | 160 | // If we have no more timers then exit. 161 | if len(m.timers) == 0 { 162 | m.mu.Unlock() 163 | return false 164 | } 165 | 166 | // Retrieve next timer. Exit if next tick is after new time. 167 | t := m.timers[0] 168 | if t.Next().After(max) { 169 | m.mu.Unlock() 170 | return false 171 | } 172 | 173 | // Move "now" forward and unlock clock. 174 | m.now = t.Next() 175 | now := m.now 176 | m.mu.Unlock() 177 | 178 | // Execute timer. 179 | t.Tick(now) 180 | return true 181 | } 182 | 183 | // After waits for the duration to elapse and then sends the current time on the returned channel. 184 | func (m *Mock) After(d time.Duration) <-chan time.Time { 185 | return m.Timer(d).C 186 | } 187 | 188 | // AfterFunc waits for the duration to elapse and then executes a function in its own goroutine. 189 | // A Timer is returned that can be stopped. 190 | func (m *Mock) AfterFunc(d time.Duration, f func()) *Timer { 191 | m.mu.Lock() 192 | defer m.mu.Unlock() 193 | ch := make(chan time.Time, 1) 194 | t := &Timer{ 195 | c: ch, 196 | fn: f, 197 | mock: m, 198 | next: m.now.Add(d), 199 | stopped: false, 200 | } 201 | m.timers = append(m.timers, (*internalTimer)(t)) 202 | return t 203 | } 204 | 205 | // Now returns the current wall time on the mock clock. 206 | func (m *Mock) Now() time.Time { 207 | m.mu.Lock() 208 | defer m.mu.Unlock() 209 | return m.now 210 | } 211 | 212 | // Since returns time since `t` using the mock clock's wall time. 213 | func (m *Mock) Since(t time.Time) time.Duration { 214 | return m.Now().Sub(t) 215 | } 216 | 217 | // Until returns time until `t` using the mock clock's wall time. 218 | func (m *Mock) Until(t time.Time) time.Duration { 219 | return t.Sub(m.Now()) 220 | } 221 | 222 | // Sleep pauses the goroutine for the given duration on the mock clock. 223 | // The clock must be moved forward in a separate goroutine. 224 | func (m *Mock) Sleep(d time.Duration) { 225 | <-m.After(d) 226 | } 227 | 228 | // Tick is a convenience function for Ticker(). 229 | // It will return a ticker channel that cannot be stopped. 230 | func (m *Mock) Tick(d time.Duration) <-chan time.Time { 231 | return m.Ticker(d).C 232 | } 233 | 234 | // Ticker creates a new instance of Ticker. 235 | func (m *Mock) Ticker(d time.Duration) *Ticker { 236 | m.mu.Lock() 237 | defer m.mu.Unlock() 238 | ch := make(chan time.Time, 1) 239 | t := &Ticker{ 240 | C: ch, 241 | c: ch, 242 | mock: m, 243 | d: d, 244 | next: m.now.Add(d), 245 | } 246 | m.timers = append(m.timers, (*internalTicker)(t)) 247 | return t 248 | } 249 | 250 | // Timer creates a new instance of Timer. 251 | func (m *Mock) Timer(d time.Duration) *Timer { 252 | m.mu.Lock() 253 | ch := make(chan time.Time, 1) 254 | t := &Timer{ 255 | C: ch, 256 | c: ch, 257 | mock: m, 258 | next: m.now.Add(d), 259 | stopped: false, 260 | } 261 | m.timers = append(m.timers, (*internalTimer)(t)) 262 | now := m.now 263 | m.mu.Unlock() 264 | m.runNextTimer(now) 265 | return t 266 | } 267 | 268 | // removeClockTimer removes a timer from m.timers. m.mu MUST be held 269 | // when this method is called. 270 | func (m *Mock) removeClockTimer(t clockTimer) { 271 | for i, timer := range m.timers { 272 | if timer == t { 273 | copy(m.timers[i:], m.timers[i+1:]) 274 | m.timers[len(m.timers)-1] = nil 275 | m.timers = m.timers[:len(m.timers)-1] 276 | break 277 | } 278 | } 279 | sort.Sort(m.timers) 280 | } 281 | 282 | // clockTimer represents an object with an associated start time. 283 | type clockTimer interface { 284 | Next() time.Time 285 | Tick(time.Time) 286 | } 287 | 288 | // clockTimers represents a list of sortable timers. 289 | type clockTimers []clockTimer 290 | 291 | func (a clockTimers) Len() int { return len(a) } 292 | func (a clockTimers) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 293 | func (a clockTimers) Less(i, j int) bool { return a[i].Next().Before(a[j].Next()) } 294 | 295 | // Timer represents a single event. 296 | // The current time will be sent on C, unless the timer was created by AfterFunc. 297 | type Timer struct { 298 | C <-chan time.Time 299 | c chan time.Time 300 | timer *time.Timer // realtime impl, if set 301 | next time.Time // next tick time 302 | mock *Mock // mock clock, if set 303 | fn func() // AfterFunc function, if set 304 | stopped bool // True if stopped, false if running 305 | } 306 | 307 | // Stop turns off the ticker. 308 | func (t *Timer) Stop() bool { 309 | if t.timer != nil { 310 | return t.timer.Stop() 311 | } 312 | 313 | t.mock.mu.Lock() 314 | registered := !t.stopped 315 | t.mock.removeClockTimer((*internalTimer)(t)) 316 | t.stopped = true 317 | t.mock.mu.Unlock() 318 | return registered 319 | } 320 | 321 | // Reset changes the expiry time of the timer 322 | func (t *Timer) Reset(d time.Duration) bool { 323 | if t.timer != nil { 324 | return t.timer.Reset(d) 325 | } 326 | 327 | t.mock.mu.Lock() 328 | t.next = t.mock.now.Add(d) 329 | defer t.mock.mu.Unlock() 330 | 331 | registered := !t.stopped 332 | if t.stopped { 333 | t.mock.timers = append(t.mock.timers, (*internalTimer)(t)) 334 | } 335 | 336 | t.stopped = false 337 | return registered 338 | } 339 | 340 | type internalTimer Timer 341 | 342 | func (t *internalTimer) Next() time.Time { return t.next } 343 | func (t *internalTimer) Tick(now time.Time) { 344 | // a gosched() after ticking, to allow any consequences of the 345 | // tick to complete 346 | defer gosched() 347 | 348 | t.mock.mu.Lock() 349 | if t.fn != nil { 350 | // defer function execution until the lock is released, and 351 | defer func() { go t.fn() }() 352 | } else { 353 | t.c <- now 354 | } 355 | t.mock.removeClockTimer((*internalTimer)(t)) 356 | t.stopped = true 357 | t.mock.mu.Unlock() 358 | } 359 | 360 | // Ticker holds a channel that receives "ticks" at regular intervals. 361 | type Ticker struct { 362 | C <-chan time.Time 363 | c chan time.Time 364 | ticker *time.Ticker // realtime impl, if set 365 | next time.Time // next tick time 366 | mock *Mock // mock clock, if set 367 | d time.Duration // time between ticks 368 | stopped bool // True if stopped, false if running 369 | } 370 | 371 | // Stop turns off the ticker. 372 | func (t *Ticker) Stop() { 373 | if t.ticker != nil { 374 | t.ticker.Stop() 375 | } else { 376 | t.mock.mu.Lock() 377 | t.mock.removeClockTimer((*internalTicker)(t)) 378 | t.stopped = true 379 | t.mock.mu.Unlock() 380 | } 381 | } 382 | 383 | // Reset resets the ticker to a new duration. 384 | func (t *Ticker) Reset(dur time.Duration) { 385 | if t.ticker != nil { 386 | t.ticker.Reset(dur) 387 | return 388 | } 389 | 390 | t.mock.mu.Lock() 391 | defer t.mock.mu.Unlock() 392 | 393 | if t.stopped { 394 | t.mock.timers = append(t.mock.timers, (*internalTicker)(t)) 395 | t.stopped = false 396 | } 397 | 398 | t.d = dur 399 | t.next = t.mock.now.Add(dur) 400 | } 401 | 402 | type internalTicker Ticker 403 | 404 | func (t *internalTicker) Next() time.Time { return t.next } 405 | func (t *internalTicker) Tick(now time.Time) { 406 | select { 407 | case t.c <- now: 408 | default: 409 | } 410 | t.mock.mu.Lock() 411 | t.next = now.Add(t.d) 412 | t.mock.mu.Unlock() 413 | gosched() 414 | } 415 | 416 | // Sleep momentarily so that other goroutines can process. 417 | func gosched() { time.Sleep(1 * time.Millisecond) } 418 | 419 | var ( 420 | // type checking 421 | _ Clock = &Mock{} 422 | ) 423 | -------------------------------------------------------------------------------- /clock_test.go: -------------------------------------------------------------------------------- 1 | package clock 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sync" 7 | "sync/atomic" 8 | "testing" 9 | "time" 10 | ) 11 | 12 | // counter is an atomic uint32 that can be incremented easily. It's 13 | // useful for asserting things have happened in tests. 14 | type counter struct { 15 | count uint32 16 | } 17 | 18 | func (c *counter) incr() { 19 | atomic.AddUint32(&c.count, 1) 20 | } 21 | 22 | func (c *counter) get() uint32 { 23 | return atomic.LoadUint32(&c.count) 24 | } 25 | 26 | // Ensure that the clock's After channel sends at the correct time. 27 | func TestClock_After(t *testing.T) { 28 | start := time.Now() 29 | <-New().After(20 * time.Millisecond) 30 | dur := time.Since(start) 31 | 32 | if dur < 20*time.Millisecond || dur > 40*time.Millisecond { 33 | t.Fatalf("Bad duration: %s", dur) 34 | } 35 | } 36 | 37 | // Ensure that the clock's AfterFunc executes at the correct time. 38 | func TestClock_AfterFunc(t *testing.T) { 39 | var ok bool 40 | var wg sync.WaitGroup 41 | 42 | wg.Add(1) 43 | start := time.Now() 44 | New().AfterFunc(20*time.Millisecond, func() { 45 | ok = true 46 | wg.Done() 47 | }) 48 | wg.Wait() 49 | dur := time.Since(start) 50 | 51 | if dur < 20*time.Millisecond || dur > 40*time.Millisecond { 52 | t.Fatalf("Bad duration: %s", dur) 53 | } 54 | if !ok { 55 | t.Fatal("Function did not run") 56 | } 57 | } 58 | 59 | // Ensure that the clock's time matches the standary library. 60 | func TestClock_Now(t *testing.T) { 61 | a := time.Now().Round(time.Second) 62 | b := New().Now().Round(time.Second) 63 | if !a.Equal(b) { 64 | t.Errorf("not equal: %s != %s", a, b) 65 | } 66 | } 67 | 68 | // Ensure that the clock sleeps for the appropriate amount of time. 69 | func TestClock_Sleep(t *testing.T) { 70 | start := time.Now() 71 | New().Sleep(20 * time.Millisecond) 72 | dur := time.Since(start) 73 | 74 | if dur < 20*time.Millisecond || dur > 40*time.Millisecond { 75 | t.Fatalf("Bad duration: %s", dur) 76 | } 77 | } 78 | 79 | // Ensure that the clock ticks correctly. 80 | func TestClock_Tick(t *testing.T) { 81 | start := time.Now() 82 | c := New().Tick(20 * time.Millisecond) 83 | <-c 84 | <-c 85 | dur := time.Since(start) 86 | 87 | if dur < 20*time.Millisecond || dur > 50*time.Millisecond { 88 | t.Fatalf("Bad duration: %s", dur) 89 | } 90 | } 91 | 92 | // Ensure that the clock's ticker ticks correctly. 93 | func TestClock_Ticker(t *testing.T) { 94 | start := time.Now() 95 | ticker := New().Ticker(50 * time.Millisecond) 96 | <-ticker.C 97 | <-ticker.C 98 | dur := time.Since(start) 99 | 100 | if dur < 100*time.Millisecond || dur > 200*time.Millisecond { 101 | t.Fatalf("Bad duration: %s", dur) 102 | } 103 | } 104 | 105 | // Ensure that the clock's ticker can stop correctly. 106 | func TestClock_Ticker_Stp(t *testing.T) { 107 | ticker := New().Ticker(20 * time.Millisecond) 108 | <-ticker.C 109 | ticker.Stop() 110 | select { 111 | case <-ticker.C: 112 | t.Fatal("unexpected send") 113 | case <-time.After(30 * time.Millisecond): 114 | } 115 | } 116 | 117 | // Ensure that the clock's ticker can reset correctly. 118 | func TestClock_Ticker_Rst(t *testing.T) { 119 | start := time.Now() 120 | ticker := New().Ticker(20 * time.Millisecond) 121 | <-ticker.C 122 | ticker.Reset(5 * time.Millisecond) 123 | <-ticker.C 124 | dur := time.Since(start) 125 | if dur >= 30*time.Millisecond { 126 | t.Fatal("took more than 30ms") 127 | } 128 | ticker.Stop() 129 | } 130 | 131 | // Ensure that the clock's ticker can stop and then be reset correctly. 132 | func TestClock_Ticker_Stop_Rst(t *testing.T) { 133 | start := time.Now() 134 | ticker := New().Ticker(20 * time.Millisecond) 135 | <-ticker.C 136 | ticker.Stop() 137 | select { 138 | case <-ticker.C: 139 | t.Fatal("unexpected send") 140 | case <-time.After(30 * time.Millisecond): 141 | } 142 | ticker.Reset(5 * time.Millisecond) 143 | <-ticker.C 144 | dur := time.Since(start) 145 | if dur >= 60*time.Millisecond { 146 | t.Fatal("took more than 60ms") 147 | } 148 | ticker.Stop() 149 | } 150 | 151 | // Ensure that the clock's timer waits correctly. 152 | func TestClock_Timer(t *testing.T) { 153 | start := time.Now() 154 | timer := New().Timer(20 * time.Millisecond) 155 | <-timer.C 156 | dur := time.Since(start) 157 | 158 | if dur < 20*time.Millisecond || dur > 40*time.Millisecond { 159 | t.Fatalf("Bad duration: %s", dur) 160 | } 161 | 162 | if timer.Stop() { 163 | t.Fatal("timer still running") 164 | } 165 | } 166 | 167 | // Ensure that the clock's timer can be stopped. 168 | func TestClock_Timer_Stop(t *testing.T) { 169 | timer := New().Timer(20 * time.Millisecond) 170 | if !timer.Stop() { 171 | t.Fatal("timer not running") 172 | } 173 | if timer.Stop() { 174 | t.Fatal("timer wasn't cancelled") 175 | } 176 | select { 177 | case <-timer.C: 178 | t.Fatal("unexpected send") 179 | case <-time.After(30 * time.Millisecond): 180 | } 181 | } 182 | 183 | // Ensure that the clock's timer can be reset. 184 | func TestClock_Timer_Reset(t *testing.T) { 185 | start := time.Now() 186 | timer := New().Timer(10 * time.Millisecond) 187 | if !timer.Reset(20 * time.Millisecond) { 188 | t.Fatal("timer not running") 189 | } 190 | <-timer.C 191 | dur := time.Since(start) 192 | 193 | if dur < 20*time.Millisecond || dur > 40*time.Millisecond { 194 | t.Fatalf("Bad duration: %s", dur) 195 | } 196 | } 197 | 198 | func TestClock_NegativeDuration(t *testing.T) { 199 | clock := NewMock() 200 | timer := clock.Timer(-time.Second) 201 | select { 202 | case <-timer.C: 203 | default: 204 | t.Fatal("timer should have fired immediately") 205 | } 206 | } 207 | 208 | // Ensure reset can be called immediately after reading channel 209 | func TestClock_Timer_Reset_Unlock(t *testing.T) { 210 | clock := NewMock() 211 | timer := clock.Timer(1 * time.Second) 212 | 213 | var wg sync.WaitGroup 214 | wg.Add(1) 215 | go func() { 216 | defer wg.Done() 217 | 218 | select { 219 | case <-timer.C: 220 | timer.Reset(1 * time.Second) 221 | } 222 | 223 | select { 224 | case <-timer.C: 225 | } 226 | }() 227 | 228 | clock.Add(2 * time.Second) 229 | wg.Wait() 230 | } 231 | 232 | // Ensure that the mock's After channel sends at the correct time. 233 | func TestMock_After(t *testing.T) { 234 | var ok int32 235 | clock := NewMock() 236 | 237 | // Create a channel to execute after 10 mock seconds. 238 | ch := clock.After(10 * time.Second) 239 | go func(ch <-chan time.Time) { 240 | <-ch 241 | atomic.StoreInt32(&ok, 1) 242 | }(ch) 243 | 244 | // Move clock forward to just before the time. 245 | clock.Add(9 * time.Second) 246 | if atomic.LoadInt32(&ok) == 1 { 247 | t.Fatal("too early") 248 | } 249 | 250 | // Move clock forward to the after channel's time. 251 | clock.Add(1 * time.Second) 252 | if atomic.LoadInt32(&ok) == 0 { 253 | t.Fatal("too late") 254 | } 255 | } 256 | 257 | // Ensure that the mock's After channel doesn't block on write. 258 | func TestMock_UnusedAfter(t *testing.T) { 259 | mock := NewMock() 260 | mock.After(1 * time.Millisecond) 261 | 262 | done := make(chan bool, 1) 263 | go func() { 264 | mock.Add(1 * time.Second) 265 | done <- true 266 | }() 267 | 268 | select { 269 | case <-done: 270 | case <-time.After(1 * time.Second): 271 | t.Fatal("mock.Add hung") 272 | } 273 | } 274 | 275 | // Ensure that the mock's AfterFunc executes at the correct time. 276 | func TestMock_AfterFunc(t *testing.T) { 277 | var ok int32 278 | clock := NewMock() 279 | 280 | // Execute function after duration. 281 | clock.AfterFunc(10*time.Second, func() { 282 | atomic.StoreInt32(&ok, 1) 283 | }) 284 | 285 | // Move clock forward to just before the time. 286 | clock.Add(9 * time.Second) 287 | if atomic.LoadInt32(&ok) == 1 { 288 | t.Fatal("too early") 289 | } 290 | 291 | // Move clock forward to the after channel's time. 292 | clock.Add(1 * time.Second) 293 | if atomic.LoadInt32(&ok) == 0 { 294 | t.Fatal("too late") 295 | } 296 | } 297 | 298 | // Ensure that the mock's AfterFunc doesn't execute if stopped. 299 | func TestMock_AfterFunc_Stop(t *testing.T) { 300 | // Execute function after duration. 301 | clock := NewMock() 302 | timer := clock.AfterFunc(10*time.Second, func() { 303 | t.Fatal("unexpected function execution") 304 | }) 305 | gosched() 306 | 307 | // Stop timer & move clock forward. 308 | timer.Stop() 309 | clock.Add(10 * time.Second) 310 | gosched() 311 | } 312 | 313 | // Ensure that the mock's current time can be changed. 314 | func TestMock_Now(t *testing.T) { 315 | clock := NewMock() 316 | if now := clock.Now(); !now.Equal(time.Unix(0, 0)) { 317 | t.Fatalf("expected epoch, got: %v", now) 318 | } 319 | 320 | // Add 10 seconds and check the time. 321 | clock.Add(10 * time.Second) 322 | if now := clock.Now(); !now.Equal(time.Unix(10, 0)) { 323 | t.Fatalf("expected epoch, got: %v", now) 324 | } 325 | } 326 | 327 | func TestMock_Since(t *testing.T) { 328 | clock := NewMock() 329 | 330 | beginning := clock.Now() 331 | clock.Add(500 * time.Second) 332 | if since := clock.Since(beginning); since.Seconds() != 500 { 333 | t.Fatalf("expected 500 since beginning, actually: %v", since.Seconds()) 334 | } 335 | } 336 | 337 | func TestMock_Until(t *testing.T) { 338 | clock := NewMock() 339 | 340 | end := clock.Now().Add(500 * time.Second) 341 | if dur := clock.Until(end); dur.Seconds() != 500 { 342 | t.Fatalf("expected 500s duration between `clock` and `end`, actually: %v", dur.Seconds()) 343 | } 344 | clock.Add(100 * time.Second) 345 | if dur := clock.Until(end); dur.Seconds() != 400 { 346 | t.Fatalf("expected 400s duration between `clock` and `end`, actually: %v", dur.Seconds()) 347 | } 348 | } 349 | 350 | // Ensure that the mock can sleep for the correct time. 351 | func TestMock_Sleep(t *testing.T) { 352 | var ok int32 353 | clock := NewMock() 354 | 355 | // Create a channel to execute after 10 mock seconds. 356 | go func() { 357 | clock.Sleep(10 * time.Second) 358 | atomic.StoreInt32(&ok, 1) 359 | }() 360 | gosched() 361 | 362 | // Move clock forward to just before the sleep duration. 363 | clock.Add(9 * time.Second) 364 | if atomic.LoadInt32(&ok) == 1 { 365 | t.Fatal("too early") 366 | } 367 | 368 | // Move clock forward to after the sleep duration. 369 | clock.Add(1 * time.Second) 370 | if atomic.LoadInt32(&ok) == 0 { 371 | t.Fatal("too late") 372 | } 373 | } 374 | 375 | // Ensure that the mock's Tick channel sends at the correct time. 376 | func TestMock_Tick(t *testing.T) { 377 | var n int32 378 | clock := NewMock() 379 | 380 | // Create a channel to increment every 10 seconds. 381 | go func() { 382 | tick := clock.Tick(10 * time.Second) 383 | for { 384 | <-tick 385 | atomic.AddInt32(&n, 1) 386 | } 387 | }() 388 | gosched() 389 | 390 | // Move clock forward to just before the first tick. 391 | clock.Add(9 * time.Second) 392 | if atomic.LoadInt32(&n) != 0 { 393 | t.Fatalf("expected 0, got %d", n) 394 | } 395 | 396 | // Move clock forward to the start of the first tick. 397 | clock.Add(1 * time.Second) 398 | if atomic.LoadInt32(&n) != 1 { 399 | t.Fatalf("expected 1, got %d", n) 400 | } 401 | 402 | // Move clock forward over several ticks. 403 | clock.Add(30 * time.Second) 404 | if atomic.LoadInt32(&n) != 4 { 405 | t.Fatalf("expected 4, got %d", n) 406 | } 407 | } 408 | 409 | // Ensure that the mock's Ticker channel sends at the correct time. 410 | func TestMock_Ticker(t *testing.T) { 411 | var n int32 412 | clock := NewMock() 413 | 414 | // Create a channel to increment every microsecond. 415 | go func() { 416 | ticker := clock.Ticker(1 * time.Microsecond) 417 | for { 418 | <-ticker.C 419 | atomic.AddInt32(&n, 1) 420 | } 421 | }() 422 | gosched() 423 | 424 | // Move clock forward. 425 | clock.Add(10 * time.Microsecond) 426 | if atomic.LoadInt32(&n) != 10 { 427 | t.Fatalf("unexpected: %d", n) 428 | } 429 | } 430 | 431 | // Ensure that the mock's Ticker channel won't block if not read from. 432 | func TestMock_Ticker_Overflow(t *testing.T) { 433 | clock := NewMock() 434 | ticker := clock.Ticker(1 * time.Microsecond) 435 | clock.Add(10 * time.Microsecond) 436 | ticker.Stop() 437 | } 438 | 439 | // Ensure that the mock's Ticker can be stopped. 440 | func TestMock_Ticker_Stop(t *testing.T) { 441 | var n int32 442 | clock := NewMock() 443 | 444 | // Create a channel to increment every second. 445 | ticker := clock.Ticker(1 * time.Second) 446 | go func() { 447 | for { 448 | <-ticker.C 449 | atomic.AddInt32(&n, 1) 450 | } 451 | }() 452 | gosched() 453 | 454 | // Move clock forward. 455 | clock.Add(5 * time.Second) 456 | if atomic.LoadInt32(&n) != 5 { 457 | t.Fatalf("expected 5, got: %d", n) 458 | } 459 | 460 | ticker.Stop() 461 | 462 | // Move clock forward again. 463 | clock.Add(5 * time.Second) 464 | if atomic.LoadInt32(&n) != 5 { 465 | t.Fatalf("still expected 5, got: %d", n) 466 | } 467 | } 468 | 469 | func TestMock_Ticker_Reset(t *testing.T) { 470 | var n int32 471 | clock := NewMock() 472 | 473 | ticker := clock.Ticker(5 * time.Second) 474 | defer ticker.Stop() 475 | 476 | go func() { 477 | for { 478 | <-ticker.C 479 | atomic.AddInt32(&n, 1) 480 | } 481 | }() 482 | gosched() 483 | 484 | // Move clock forward. 485 | clock.Add(10 * time.Second) 486 | if atomic.LoadInt32(&n) != 2 { 487 | t.Fatalf("expected 2, got: %d", n) 488 | } 489 | 490 | clock.Add(4 * time.Second) 491 | ticker.Reset(5 * time.Second) 492 | 493 | // Advance the remaining second 494 | clock.Add(1 * time.Second) 495 | 496 | if atomic.LoadInt32(&n) != 2 { 497 | t.Fatalf("expected 2, got: %d", n) 498 | } 499 | 500 | // Advance the remaining 4 seconds from the previous tick 501 | clock.Add(4 * time.Second) 502 | 503 | if atomic.LoadInt32(&n) != 3 { 504 | t.Fatalf("expected 3, got: %d", n) 505 | } 506 | } 507 | 508 | func TestMock_Ticker_Stop_Reset(t *testing.T) { 509 | var n int32 510 | clock := NewMock() 511 | 512 | ticker := clock.Ticker(5 * time.Second) 513 | defer ticker.Stop() 514 | 515 | go func() { 516 | for { 517 | <-ticker.C 518 | atomic.AddInt32(&n, 1) 519 | } 520 | }() 521 | gosched() 522 | 523 | // Move clock forward. 524 | clock.Add(10 * time.Second) 525 | if atomic.LoadInt32(&n) != 2 { 526 | t.Fatalf("expected 2, got: %d", n) 527 | } 528 | 529 | ticker.Stop() 530 | 531 | // Move clock forward again. 532 | clock.Add(5 * time.Second) 533 | if atomic.LoadInt32(&n) != 2 { 534 | t.Fatalf("still expected 2, got: %d", n) 535 | } 536 | 537 | ticker.Reset(2 * time.Second) 538 | 539 | // Advance the remaining 2 seconds 540 | clock.Add(2 * time.Second) 541 | 542 | if atomic.LoadInt32(&n) != 3 { 543 | t.Fatalf("expected 3, got: %d", n) 544 | } 545 | 546 | // Advance another 2 seconds 547 | clock.Add(2 * time.Second) 548 | 549 | if atomic.LoadInt32(&n) != 4 { 550 | t.Fatalf("expected 4, got: %d", n) 551 | } 552 | } 553 | 554 | // Ensure that multiple tickers can be used together. 555 | func TestMock_Ticker_Multi(t *testing.T) { 556 | var n int32 557 | clock := NewMock() 558 | 559 | go func() { 560 | a := clock.Ticker(1 * time.Microsecond) 561 | b := clock.Ticker(3 * time.Microsecond) 562 | 563 | for { 564 | select { 565 | case <-a.C: 566 | atomic.AddInt32(&n, 1) 567 | case <-b.C: 568 | atomic.AddInt32(&n, 100) 569 | } 570 | } 571 | }() 572 | gosched() 573 | 574 | // Move clock forward. 575 | clock.Add(10 * time.Microsecond) 576 | gosched() 577 | if atomic.LoadInt32(&n) != 310 { 578 | t.Fatalf("unexpected: %d", n) 579 | } 580 | } 581 | 582 | func ExampleMock_After() { 583 | // Create a new mock clock. 584 | clock := NewMock() 585 | var count counter 586 | 587 | ready := make(chan struct{}) 588 | // Create a channel to execute after 10 mock seconds. 589 | go func() { 590 | ch := clock.After(10 * time.Second) 591 | close(ready) 592 | <-ch 593 | count.incr() 594 | }() 595 | <-ready 596 | 597 | // Print the starting value. 598 | fmt.Printf("%s: %d\n", clock.Now().UTC(), count.get()) 599 | 600 | // Move the clock forward 5 seconds and print the value again. 601 | clock.Add(5 * time.Second) 602 | fmt.Printf("%s: %d\n", clock.Now().UTC(), count.get()) 603 | 604 | // Move the clock forward 5 seconds to the tick time and check the value. 605 | clock.Add(5 * time.Second) 606 | fmt.Printf("%s: %d\n", clock.Now().UTC(), count.get()) 607 | 608 | // Output: 609 | // 1970-01-01 00:00:00 +0000 UTC: 0 610 | // 1970-01-01 00:00:05 +0000 UTC: 0 611 | // 1970-01-01 00:00:10 +0000 UTC: 1 612 | } 613 | 614 | func ExampleMock_AfterFunc() { 615 | // Create a new mock clock. 616 | clock := NewMock() 617 | var count counter 618 | count.incr() 619 | 620 | // Execute a function after 10 mock seconds. 621 | clock.AfterFunc(10*time.Second, func() { 622 | count.incr() 623 | }) 624 | gosched() 625 | 626 | // Print the starting value. 627 | fmt.Printf("%s: %d\n", clock.Now().UTC(), count.get()) 628 | 629 | // Move the clock forward 10 seconds and print the new value. 630 | clock.Add(10 * time.Second) 631 | fmt.Printf("%s: %d\n", clock.Now().UTC(), count.get()) 632 | 633 | // Output: 634 | // 1970-01-01 00:00:00 +0000 UTC: 1 635 | // 1970-01-01 00:00:10 +0000 UTC: 2 636 | } 637 | 638 | func ExampleMock_Sleep() { 639 | // Create a new mock clock. 640 | clock := NewMock() 641 | var count counter 642 | 643 | // Execute a function after 10 mock seconds. 644 | go func() { 645 | clock.Sleep(10 * time.Second) 646 | count.incr() 647 | }() 648 | gosched() 649 | 650 | // Print the starting value. 651 | fmt.Printf("%s: %d\n", clock.Now().UTC(), count.get()) 652 | 653 | // Move the clock forward 10 seconds and print the new value. 654 | clock.Add(10 * time.Second) 655 | fmt.Printf("%s: %d\n", clock.Now().UTC(), count.get()) 656 | 657 | // Output: 658 | // 1970-01-01 00:00:00 +0000 UTC: 0 659 | // 1970-01-01 00:00:10 +0000 UTC: 1 660 | } 661 | 662 | func ExampleMock_Ticker() { 663 | // Create a new mock clock. 664 | clock := NewMock() 665 | var count counter 666 | 667 | ready := make(chan struct{}) 668 | // Increment count every mock second. 669 | go func() { 670 | ticker := clock.Ticker(1 * time.Second) 671 | close(ready) 672 | for { 673 | <-ticker.C 674 | count.incr() 675 | } 676 | }() 677 | <-ready 678 | 679 | // Move the clock forward 10 seconds and print the new value. 680 | clock.Add(10 * time.Second) 681 | fmt.Printf("Count is %d after 10 seconds\n", count.get()) 682 | 683 | // Move the clock forward 5 more seconds and print the new value. 684 | clock.Add(5 * time.Second) 685 | fmt.Printf("Count is %d after 15 seconds\n", count.get()) 686 | 687 | // Output: 688 | // Count is 10 after 10 seconds 689 | // Count is 15 after 15 seconds 690 | } 691 | 692 | func ExampleMock_Timer() { 693 | // Create a new mock clock. 694 | clock := NewMock() 695 | var count counter 696 | 697 | ready := make(chan struct{}) 698 | // Increment count after a mock second. 699 | go func() { 700 | timer := clock.Timer(1 * time.Second) 701 | close(ready) 702 | <-timer.C 703 | count.incr() 704 | }() 705 | <-ready 706 | 707 | // Move the clock forward 10 seconds and print the new value. 708 | clock.Add(10 * time.Second) 709 | fmt.Printf("Count is %d after 10 seconds\n", count.get()) 710 | 711 | // Output: 712 | // Count is 1 after 10 seconds 713 | } 714 | 715 | func TestMock_ReentrantDeadlock(t *testing.T) { 716 | mockedClock := NewMock() 717 | timer20 := mockedClock.Timer(20 * time.Second) 718 | go func() { 719 | v := <-timer20.C 720 | panic(fmt.Sprintf("timer should not have ticked: %v", v)) 721 | }() 722 | mockedClock.AfterFunc(10*time.Second, func() { 723 | timer20.Stop() 724 | }) 725 | 726 | mockedClock.Add(15 * time.Second) 727 | mockedClock.Add(15 * time.Second) 728 | } 729 | 730 | func TestMock_AddAfterFuncRace(t *testing.T) { 731 | // start blocks the goroutines in this test 732 | // until we're ready for them to race. 733 | start := make(chan struct{}) 734 | 735 | var wg sync.WaitGroup 736 | 737 | mockedClock := NewMock() 738 | 739 | var calls counter 740 | defer func() { 741 | if calls.get() == 0 { 742 | t.Errorf("AfterFunc did not call the function") 743 | } 744 | }() 745 | 746 | wg.Add(1) 747 | go func() { 748 | defer wg.Done() 749 | <-start 750 | 751 | mockedClock.AfterFunc(time.Millisecond, func() { 752 | calls.incr() 753 | }) 754 | }() 755 | 756 | wg.Add(1) 757 | go func() { 758 | defer wg.Done() 759 | <-start 760 | 761 | mockedClock.Add(time.Millisecond) 762 | mockedClock.Add(time.Millisecond) 763 | }() 764 | 765 | close(start) // unblock the goroutines 766 | wg.Wait() // and wait for them 767 | } 768 | 769 | func TestMock_AfterRace(t *testing.T) { 770 | mock := NewMock() 771 | 772 | const num = 10 773 | var finished atomic.Int32 774 | 775 | for i := 0; i < num; i++ { 776 | go func() { 777 | <-mock.After(1 * time.Millisecond) 778 | finished.Add(1) 779 | }() 780 | } 781 | 782 | for finished.Load() < num { 783 | mock.Add(time.Second) 784 | gosched() 785 | } 786 | } 787 | 788 | func warn(v ...interface{}) { fmt.Fprintln(os.Stderr, v...) } 789 | func warnf(msg string, v ...interface{}) { fmt.Fprintf(os.Stderr, msg+"\n", v...) } 790 | -------------------------------------------------------------------------------- /context.go: -------------------------------------------------------------------------------- 1 | package clock 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | func (m *Mock) WithTimeout(parent context.Context, timeout time.Duration) (context.Context, context.CancelFunc) { 11 | return m.WithDeadline(parent, m.Now().Add(timeout)) 12 | } 13 | 14 | func (m *Mock) WithDeadline(parent context.Context, deadline time.Time) (context.Context, context.CancelFunc) { 15 | if cur, ok := parent.Deadline(); ok && cur.Before(deadline) { 16 | // The current deadline is already sooner than the new one. 17 | return context.WithCancel(parent) 18 | } 19 | ctx := &timerCtx{clock: m, parent: parent, deadline: deadline, done: make(chan struct{})} 20 | propagateCancel(parent, ctx) 21 | dur := m.Until(deadline) 22 | if dur <= 0 { 23 | ctx.cancel(context.DeadlineExceeded) // deadline has already passed 24 | return ctx, func() {} 25 | } 26 | ctx.Lock() 27 | defer ctx.Unlock() 28 | if ctx.err == nil { 29 | ctx.timer = m.AfterFunc(dur, func() { 30 | ctx.cancel(context.DeadlineExceeded) 31 | }) 32 | } 33 | return ctx, func() { ctx.cancel(context.Canceled) } 34 | } 35 | 36 | // propagateCancel arranges for child to be canceled when parent is. 37 | func propagateCancel(parent context.Context, child *timerCtx) { 38 | if parent.Done() == nil { 39 | return // parent is never canceled 40 | } 41 | go func() { 42 | select { 43 | case <-parent.Done(): 44 | child.cancel(parent.Err()) 45 | case <-child.Done(): 46 | } 47 | }() 48 | } 49 | 50 | type timerCtx struct { 51 | sync.Mutex 52 | 53 | clock Clock 54 | parent context.Context 55 | deadline time.Time 56 | done chan struct{} 57 | 58 | err error 59 | timer *Timer 60 | } 61 | 62 | func (c *timerCtx) cancel(err error) { 63 | c.Lock() 64 | defer c.Unlock() 65 | if c.err != nil { 66 | return // already canceled 67 | } 68 | c.err = err 69 | close(c.done) 70 | if c.timer != nil { 71 | c.timer.Stop() 72 | c.timer = nil 73 | } 74 | } 75 | 76 | func (c *timerCtx) Deadline() (deadline time.Time, ok bool) { return c.deadline, true } 77 | 78 | func (c *timerCtx) Done() <-chan struct{} { return c.done } 79 | 80 | func (c *timerCtx) Err() error { return c.err } 81 | 82 | func (c *timerCtx) Value(key interface{}) interface{} { return c.parent.Value(key) } 83 | 84 | func (c *timerCtx) String() string { 85 | return fmt.Sprintf("clock.WithDeadline(%s [%s])", c.deadline, c.deadline.Sub(c.clock.Now())) 86 | } 87 | -------------------------------------------------------------------------------- /context_test.go: -------------------------------------------------------------------------------- 1 | package clock 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | // Ensure that WithDeadline is cancelled when deadline exceeded. 11 | func TestMock_WithDeadline(t *testing.T) { 12 | m := NewMock() 13 | ctx, _ := m.WithDeadline(context.Background(), m.Now().Add(time.Second)) 14 | m.Add(time.Second) 15 | select { 16 | case <-ctx.Done(): 17 | if !errors.Is(ctx.Err(), context.DeadlineExceeded) { 18 | t.Error("invalid type of error returned when deadline exceeded") 19 | } 20 | default: 21 | t.Error("context is not cancelled when deadline exceeded") 22 | } 23 | } 24 | 25 | // Ensure that WithDeadline does nothing when the deadline is later than the current deadline. 26 | func TestMock_WithDeadlineLaterThanCurrent(t *testing.T) { 27 | m := NewMock() 28 | ctx, _ := m.WithDeadline(context.Background(), m.Now().Add(time.Second)) 29 | ctx, _ = m.WithDeadline(ctx, m.Now().Add(10*time.Second)) 30 | m.Add(time.Second) 31 | select { 32 | case <-ctx.Done(): 33 | if !errors.Is(ctx.Err(), context.DeadlineExceeded) { 34 | t.Error("invalid type of error returned when deadline exceeded") 35 | } 36 | default: 37 | t.Error("context is not cancelled when deadline exceeded") 38 | } 39 | } 40 | 41 | // Ensure that WithDeadline cancel closes Done channel with context.Canceled error. 42 | func TestMock_WithDeadlineCancel(t *testing.T) { 43 | m := NewMock() 44 | ctx, cancel := m.WithDeadline(context.Background(), m.Now().Add(time.Second)) 45 | cancel() 46 | select { 47 | case <-ctx.Done(): 48 | if !errors.Is(ctx.Err(), context.Canceled) { 49 | t.Error("invalid type of error returned after cancellation") 50 | } 51 | case <-time.After(time.Second): 52 | t.Error("context is not cancelled after cancel was called") 53 | } 54 | } 55 | 56 | // Ensure that WithDeadline closes child contexts after it was closed. 57 | func TestMock_WithDeadlineCancelledWithParent(t *testing.T) { 58 | m := NewMock() 59 | parent, cancel := context.WithCancel(context.Background()) 60 | ctx, _ := m.WithDeadline(parent, m.Now().Add(time.Second)) 61 | cancel() 62 | select { 63 | case <-ctx.Done(): 64 | if !errors.Is(ctx.Err(), context.Canceled) { 65 | t.Error("invalid type of error returned after cancellation") 66 | } 67 | case <-time.After(time.Second): 68 | t.Error("context is not cancelled when parent context is cancelled") 69 | } 70 | } 71 | 72 | // Ensure that WithDeadline cancelled immediately when deadline has already passed. 73 | func TestMock_WithDeadlineImmediate(t *testing.T) { 74 | m := NewMock() 75 | ctx, _ := m.WithDeadline(context.Background(), m.Now().Add(-time.Second)) 76 | select { 77 | case <-ctx.Done(): 78 | if !errors.Is(ctx.Err(), context.DeadlineExceeded) { 79 | t.Error("invalid type of error returned when deadline has already passed") 80 | } 81 | default: 82 | t.Error("context is not cancelled when deadline has already passed") 83 | } 84 | } 85 | 86 | // Ensure that WithTimeout is cancelled when deadline exceeded. 87 | func TestMock_WithTimeout(t *testing.T) { 88 | m := NewMock() 89 | ctx, _ := m.WithTimeout(context.Background(), time.Second) 90 | m.Add(time.Second) 91 | select { 92 | case <-ctx.Done(): 93 | if !errors.Is(ctx.Err(), context.DeadlineExceeded) { 94 | t.Error("invalid type of error returned when time is over") 95 | } 96 | default: 97 | t.Error("context is not cancelled when time is over") 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/benbjohnson/clock 2 | 3 | go 1.15 4 | --------------------------------------------------------------------------------