pax_global_header00006660000000000000000000000064141747773630014535gustar00rootroot0000000000000052 comment=017fe08b263934dd0633ad02357dcdbca4a81d02 golang-github-benbjohnson-clock-1.3.0/000077500000000000000000000000001417477736300176615ustar00rootroot00000000000000golang-github-benbjohnson-clock-1.3.0/LICENSE000066400000000000000000000020661417477736300206720ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2014 Ben Johnson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. golang-github-benbjohnson-clock-1.3.0/README.md000066400000000000000000000044661417477736300211520ustar00rootroot00000000000000clock ===== [![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) Clock is a small library for mocking time in Go. It provides an interface around the standard library's [`time`][time] package so that the application can use the realtime clock while tests can use the mock clock. The module is currently maintained by @djmitche. [time]: https://pkg.go.dev/github.com/benbjohnson/clock ## Usage ### Realtime Clock Your application can maintain a `Clock` variable that will allow realtime and mock clocks to be interchangeable. For example, if you had an `Application` type: ```go import "github.com/benbjohnson/clock" type Application struct { Clock clock.Clock } ``` You could initialize it to use the realtime clock like this: ```go var app Application app.Clock = clock.New() ... ``` Then all timers and time-related functionality should be performed from the `Clock` variable. ### Mocking time In your tests, you will want to use a `Mock` clock: ```go import ( "testing" "github.com/benbjohnson/clock" ) func TestApplication_DoSomething(t *testing.T) { mock := clock.NewMock() app := Application{Clock: mock} ... } ``` Now that you've initialized your application to use the mock clock, you can adjust the time programmatically. The mock clock always starts from the Unix epoch (midnight UTC on Jan 1, 1970). ### Controlling time The mock clock provides the same functions that the standard library's `time` package provides. For example, to find the current time, you use the `Now()` function: ```go mock := clock.NewMock() // Find the current time. mock.Now().UTC() // 1970-01-01 00:00:00 +0000 UTC // Move the clock forward. mock.Add(2 * time.Hour) // Check the time again. It's 2 hours later! mock.Now().UTC() // 1970-01-01 02:00:00 +0000 UTC ``` Timers and Tickers are also controlled by this same mock clock. They will only execute when the clock is moved forward: ```go mock := clock.NewMock() count := 0 // Kick off a timer to increment every 1 mock second. go func() { ticker := mock.Ticker(1 * time.Second) for { <-ticker.C count++ } }() runtime.Gosched() // Move the clock forward 10 seconds. mock.Add(10 * time.Second) // This prints 10. fmt.Println(count) ``` golang-github-benbjohnson-clock-1.3.0/clock.go000066400000000000000000000224611417477736300213100ustar00rootroot00000000000000package clock import ( "context" "sort" "sync" "time" ) // Re-export of time.Duration type Duration = time.Duration // Clock represents an interface to the functions in the standard library time // package. Two implementations are available in the clock package. The first // is a real-time clock which simply wraps the time package's functions. The // second is a mock clock which will only change when // programmatically adjusted. type Clock interface { After(d time.Duration) <-chan time.Time AfterFunc(d time.Duration, f func()) *Timer Now() time.Time Since(t time.Time) time.Duration Until(t time.Time) time.Duration Sleep(d time.Duration) Tick(d time.Duration) <-chan time.Time Ticker(d time.Duration) *Ticker Timer(d time.Duration) *Timer WithDeadline(parent context.Context, d time.Time) (context.Context, context.CancelFunc) WithTimeout(parent context.Context, t time.Duration) (context.Context, context.CancelFunc) } // New returns an instance of a real-time clock. func New() Clock { return &clock{} } // clock implements a real-time clock by simply wrapping the time package functions. type clock struct{} func (c *clock) After(d time.Duration) <-chan time.Time { return time.After(d) } func (c *clock) AfterFunc(d time.Duration, f func()) *Timer { return &Timer{timer: time.AfterFunc(d, f)} } func (c *clock) Now() time.Time { return time.Now() } func (c *clock) Since(t time.Time) time.Duration { return time.Since(t) } func (c *clock) Until(t time.Time) time.Duration { return time.Until(t) } func (c *clock) Sleep(d time.Duration) { time.Sleep(d) } func (c *clock) Tick(d time.Duration) <-chan time.Time { return time.Tick(d) } func (c *clock) Ticker(d time.Duration) *Ticker { t := time.NewTicker(d) return &Ticker{C: t.C, ticker: t} } func (c *clock) Timer(d time.Duration) *Timer { t := time.NewTimer(d) return &Timer{C: t.C, timer: t} } func (c *clock) WithDeadline(parent context.Context, d time.Time) (context.Context, context.CancelFunc) { return context.WithDeadline(parent, d) } func (c *clock) WithTimeout(parent context.Context, t time.Duration) (context.Context, context.CancelFunc) { return context.WithTimeout(parent, t) } // Mock represents a mock clock that only moves forward programmically. // It can be preferable to a real-time clock when testing time-based functionality. type Mock struct { mu sync.Mutex now time.Time // current time timers clockTimers // tickers & timers } // NewMock returns an instance of a mock clock. // The current time of the mock clock on initialization is the Unix epoch. func NewMock() *Mock { return &Mock{now: time.Unix(0, 0)} } // Add moves the current time of the mock clock forward by the specified duration. // This should only be called from a single goroutine at a time. func (m *Mock) Add(d time.Duration) { // Calculate the final current time. t := m.now.Add(d) // Continue to execute timers until there are no more before the new time. for { if !m.runNextTimer(t) { break } } // Ensure that we end with the new time. m.mu.Lock() m.now = t m.mu.Unlock() // Give a small buffer to make sure that other goroutines get handled. gosched() } // Set sets the current time of the mock clock to a specific one. // This should only be called from a single goroutine at a time. func (m *Mock) Set(t time.Time) { // Continue to execute timers until there are no more before the new time. for { if !m.runNextTimer(t) { break } } // Ensure that we end with the new time. m.mu.Lock() m.now = t m.mu.Unlock() // Give a small buffer to make sure that other goroutines get handled. gosched() } // runNextTimer executes the next timer in chronological order and moves the // current time to the timer's next tick time. The next time is not executed if // its next time is after the max time. Returns true if a timer was executed. func (m *Mock) runNextTimer(max time.Time) bool { m.mu.Lock() // Sort timers by time. sort.Sort(m.timers) // If we have no more timers then exit. if len(m.timers) == 0 { m.mu.Unlock() return false } // Retrieve next timer. Exit if next tick is after new time. t := m.timers[0] if t.Next().After(max) { m.mu.Unlock() return false } // Move "now" forward and unlock clock. m.now = t.Next() m.mu.Unlock() // Execute timer. t.Tick(m.now) return true } // After waits for the duration to elapse and then sends the current time on the returned channel. func (m *Mock) After(d time.Duration) <-chan time.Time { return m.Timer(d).C } // AfterFunc waits for the duration to elapse and then executes a function. // A Timer is returned that can be stopped. func (m *Mock) AfterFunc(d time.Duration, f func()) *Timer { t := m.Timer(d) t.C = nil t.fn = f return t } // Now returns the current wall time on the mock clock. func (m *Mock) Now() time.Time { m.mu.Lock() defer m.mu.Unlock() return m.now } // Since returns time since `t` using the mock clock's wall time. func (m *Mock) Since(t time.Time) time.Duration { return m.Now().Sub(t) } // Until returns time until `t` using the mock clock's wall time. func (m *Mock) Until(t time.Time) time.Duration { return t.Sub(m.Now()) } // Sleep pauses the goroutine for the given duration on the mock clock. // The clock must be moved forward in a separate goroutine. func (m *Mock) Sleep(d time.Duration) { <-m.After(d) } // Tick is a convenience function for Ticker(). // It will return a ticker channel that cannot be stopped. func (m *Mock) Tick(d time.Duration) <-chan time.Time { return m.Ticker(d).C } // Ticker creates a new instance of Ticker. func (m *Mock) Ticker(d time.Duration) *Ticker { m.mu.Lock() defer m.mu.Unlock() ch := make(chan time.Time, 1) t := &Ticker{ C: ch, c: ch, mock: m, d: d, next: m.now.Add(d), } m.timers = append(m.timers, (*internalTicker)(t)) return t } // Timer creates a new instance of Timer. func (m *Mock) Timer(d time.Duration) *Timer { m.mu.Lock() defer m.mu.Unlock() ch := make(chan time.Time, 1) t := &Timer{ C: ch, c: ch, mock: m, next: m.now.Add(d), stopped: false, } m.timers = append(m.timers, (*internalTimer)(t)) return t } func (m *Mock) removeClockTimer(t clockTimer) { for i, timer := range m.timers { if timer == t { copy(m.timers[i:], m.timers[i+1:]) m.timers[len(m.timers)-1] = nil m.timers = m.timers[:len(m.timers)-1] break } } sort.Sort(m.timers) } // clockTimer represents an object with an associated start time. type clockTimer interface { Next() time.Time Tick(time.Time) } // clockTimers represents a list of sortable timers. type clockTimers []clockTimer func (a clockTimers) Len() int { return len(a) } func (a clockTimers) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a clockTimers) Less(i, j int) bool { return a[i].Next().Before(a[j].Next()) } // Timer represents a single event. // The current time will be sent on C, unless the timer was created by AfterFunc. type Timer struct { C <-chan time.Time c chan time.Time timer *time.Timer // realtime impl, if set next time.Time // next tick time mock *Mock // mock clock, if set fn func() // AfterFunc function, if set stopped bool // True if stopped, false if running } // Stop turns off the ticker. func (t *Timer) Stop() bool { if t.timer != nil { return t.timer.Stop() } t.mock.mu.Lock() registered := !t.stopped t.mock.removeClockTimer((*internalTimer)(t)) t.stopped = true t.mock.mu.Unlock() return registered } // Reset changes the expiry time of the timer func (t *Timer) Reset(d time.Duration) bool { if t.timer != nil { return t.timer.Reset(d) } t.mock.mu.Lock() t.next = t.mock.now.Add(d) defer t.mock.mu.Unlock() registered := !t.stopped if t.stopped { t.mock.timers = append(t.mock.timers, (*internalTimer)(t)) } t.stopped = false return registered } type internalTimer Timer func (t *internalTimer) Next() time.Time { return t.next } func (t *internalTimer) Tick(now time.Time) { // a gosched() after ticking, to allow any consequences of the // tick to complete defer gosched() t.mock.mu.Lock() if t.fn != nil { // defer function execution until the lock is released, and defer t.fn() } else { t.c <- now } t.mock.removeClockTimer((*internalTimer)(t)) t.stopped = true t.mock.mu.Unlock() } // Ticker holds a channel that receives "ticks" at regular intervals. type Ticker struct { C <-chan time.Time c chan time.Time ticker *time.Ticker // realtime impl, if set next time.Time // next tick time mock *Mock // mock clock, if set d time.Duration // time between ticks } // Stop turns off the ticker. func (t *Ticker) Stop() { if t.ticker != nil { t.ticker.Stop() } else { t.mock.mu.Lock() t.mock.removeClockTimer((*internalTicker)(t)) t.mock.mu.Unlock() } } // Reset resets the ticker to a new duration. func (t *Ticker) Reset(dur time.Duration) { if t.ticker != nil { t.ticker.Reset(dur) return } t.mock.mu.Lock() defer t.mock.mu.Unlock() t.d = dur t.next = t.mock.now.Add(dur) } type internalTicker Ticker func (t *internalTicker) Next() time.Time { return t.next } func (t *internalTicker) Tick(now time.Time) { select { case t.c <- now: default: } t.next = now.Add(t.d) gosched() } // Sleep momentarily so that other goroutines can process. func gosched() { time.Sleep(1 * time.Millisecond) } var ( // type checking _ Clock = &Mock{} ) golang-github-benbjohnson-clock-1.3.0/clock_test.go000066400000000000000000000343121417477736300223450ustar00rootroot00000000000000package clock import ( "fmt" "os" "sync" "sync/atomic" "testing" "time" ) // counter is an atomic uint32 that can be incremented easily. It's // useful for asserting things have happened in tests. type counter struct { count uint32 } func (c *counter) incr() { atomic.AddUint32(&c.count, 1) } func (c *counter) get() uint32 { return atomic.LoadUint32(&c.count) } // Ensure that the clock's After channel sends at the correct time. func TestClock_After(t *testing.T) { start := time.Now() <-New().After(20 * time.Millisecond) dur := time.Since(start) if dur < 20*time.Millisecond || dur > 40*time.Millisecond { t.Fatalf("Bad duration: %s", dur) } } // Ensure that the clock's AfterFunc executes at the correct time. func TestClock_AfterFunc(t *testing.T) { var ok bool var wg sync.WaitGroup wg.Add(1) start := time.Now() New().AfterFunc(20*time.Millisecond, func() { ok = true wg.Done() }) wg.Wait() dur := time.Since(start) if dur < 20*time.Millisecond || dur > 40*time.Millisecond { t.Fatalf("Bad duration: %s", dur) } if !ok { t.Fatal("Function did not run") } } // Ensure that the clock's time matches the standary library. func TestClock_Now(t *testing.T) { a := time.Now().Round(time.Second) b := New().Now().Round(time.Second) if !a.Equal(b) { t.Errorf("not equal: %s != %s", a, b) } } // Ensure that the clock sleeps for the appropriate amount of time. func TestClock_Sleep(t *testing.T) { start := time.Now() New().Sleep(20 * time.Millisecond) dur := time.Since(start) if dur < 20*time.Millisecond || dur > 40*time.Millisecond { t.Fatalf("Bad duration: %s", dur) } } // Ensure that the clock ticks correctly. func TestClock_Tick(t *testing.T) { start := time.Now() c := New().Tick(20 * time.Millisecond) <-c <-c dur := time.Since(start) if dur < 20*time.Millisecond || dur > 50*time.Millisecond { t.Fatalf("Bad duration: %s", dur) } } // Ensure that the clock's ticker ticks correctly. func TestClock_Ticker(t *testing.T) { start := time.Now() ticker := New().Ticker(50 * time.Millisecond) <-ticker.C <-ticker.C dur := time.Since(start) if dur < 100*time.Millisecond || dur > 200*time.Millisecond { t.Fatalf("Bad duration: %s", dur) } } // Ensure that the clock's ticker can stop correctly. func TestClock_Ticker_Stp(t *testing.T) { ticker := New().Ticker(20 * time.Millisecond) <-ticker.C ticker.Stop() select { case <-ticker.C: t.Fatal("unexpected send") case <-time.After(30 * time.Millisecond): } } // Ensure that the clock's ticker can reset correctly. func TestClock_Ticker_Rst(t *testing.T) { start := time.Now() ticker := New().Ticker(20 * time.Millisecond) <-ticker.C ticker.Reset(5 * time.Millisecond) <-ticker.C dur := time.Since(start) if dur >= 30*time.Millisecond { t.Fatal("took more than 30ms") } ticker.Stop() } // Ensure that the clock's timer waits correctly. func TestClock_Timer(t *testing.T) { start := time.Now() timer := New().Timer(20 * time.Millisecond) <-timer.C dur := time.Since(start) if dur < 20*time.Millisecond || dur > 40*time.Millisecond { t.Fatalf("Bad duration: %s", dur) } if timer.Stop() { t.Fatal("timer still running") } } // Ensure that the clock's timer can be stopped. func TestClock_Timer_Stop(t *testing.T) { timer := New().Timer(20 * time.Millisecond) if !timer.Stop() { t.Fatal("timer not running") } if timer.Stop() { t.Fatal("timer wasn't cancelled") } select { case <-timer.C: t.Fatal("unexpected send") case <-time.After(30 * time.Millisecond): } } // Ensure that the clock's timer can be reset. func TestClock_Timer_Reset(t *testing.T) { start := time.Now() timer := New().Timer(10 * time.Millisecond) if !timer.Reset(20 * time.Millisecond) { t.Fatal("timer not running") } <-timer.C dur := time.Since(start) if dur < 20*time.Millisecond || dur > 40*time.Millisecond { t.Fatalf("Bad duration: %s", dur) } } // Ensure reset can be called immediately after reading channel func TestClock_Timer_Reset_Unlock(t *testing.T) { clock := NewMock() timer := clock.Timer(1 * time.Second) var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() select { case <-timer.C: timer.Reset(1 * time.Second) } select { case <-timer.C: } }() clock.Add(2 * time.Second) wg.Wait() } // Ensure that the mock's After channel sends at the correct time. func TestMock_After(t *testing.T) { var ok int32 clock := NewMock() // Create a channel to execute after 10 mock seconds. ch := clock.After(10 * time.Second) go func(ch <-chan time.Time) { <-ch atomic.StoreInt32(&ok, 1) }(ch) // Move clock forward to just before the time. clock.Add(9 * time.Second) if atomic.LoadInt32(&ok) == 1 { t.Fatal("too early") } // Move clock forward to the after channel's time. clock.Add(1 * time.Second) if atomic.LoadInt32(&ok) == 0 { t.Fatal("too late") } } // Ensure that the mock's After channel doesn't block on write. func TestMock_UnusedAfter(t *testing.T) { mock := NewMock() mock.After(1 * time.Millisecond) done := make(chan bool, 1) go func() { mock.Add(1 * time.Second) done <- true }() select { case <-done: case <-time.After(1 * time.Second): t.Fatal("mock.Add hung") } } // Ensure that the mock's AfterFunc executes at the correct time. func TestMock_AfterFunc(t *testing.T) { var ok int32 clock := NewMock() // Execute function after duration. clock.AfterFunc(10*time.Second, func() { atomic.StoreInt32(&ok, 1) }) // Move clock forward to just before the time. clock.Add(9 * time.Second) if atomic.LoadInt32(&ok) == 1 { t.Fatal("too early") } // Move clock forward to the after channel's time. clock.Add(1 * time.Second) if atomic.LoadInt32(&ok) == 0 { t.Fatal("too late") } } // Ensure that the mock's AfterFunc doesn't execute if stopped. func TestMock_AfterFunc_Stop(t *testing.T) { // Execute function after duration. clock := NewMock() timer := clock.AfterFunc(10*time.Second, func() { t.Fatal("unexpected function execution") }) gosched() // Stop timer & move clock forward. timer.Stop() clock.Add(10 * time.Second) gosched() } // Ensure that the mock's current time can be changed. func TestMock_Now(t *testing.T) { clock := NewMock() if now := clock.Now(); !now.Equal(time.Unix(0, 0)) { t.Fatalf("expected epoch, got: %v", now) } // Add 10 seconds and check the time. clock.Add(10 * time.Second) if now := clock.Now(); !now.Equal(time.Unix(10, 0)) { t.Fatalf("expected epoch, got: %v", now) } } func TestMock_Since(t *testing.T) { clock := NewMock() beginning := clock.Now() clock.Add(500 * time.Second) if since := clock.Since(beginning); since.Seconds() != 500 { t.Fatalf("expected 500 since beginning, actually: %v", since.Seconds()) } } func TestMock_Until(t *testing.T) { clock := NewMock() end := clock.Now().Add(500 * time.Second) if dur := clock.Until(end); dur.Seconds() != 500 { t.Fatalf("expected 500s duration between `clock` and `end`, actually: %v", dur.Seconds()) } clock.Add(100 * time.Second) if dur := clock.Until(end); dur.Seconds() != 400 { t.Fatalf("expected 400s duration between `clock` and `end`, actually: %v", dur.Seconds()) } } // Ensure that the mock can sleep for the correct time. func TestMock_Sleep(t *testing.T) { var ok int32 clock := NewMock() // Create a channel to execute after 10 mock seconds. go func() { clock.Sleep(10 * time.Second) atomic.StoreInt32(&ok, 1) }() gosched() // Move clock forward to just before the sleep duration. clock.Add(9 * time.Second) if atomic.LoadInt32(&ok) == 1 { t.Fatal("too early") } // Move clock forward to after the sleep duration. clock.Add(1 * time.Second) if atomic.LoadInt32(&ok) == 0 { t.Fatal("too late") } } // Ensure that the mock's Tick channel sends at the correct time. func TestMock_Tick(t *testing.T) { var n int32 clock := NewMock() // Create a channel to increment every 10 seconds. go func() { tick := clock.Tick(10 * time.Second) for { <-tick atomic.AddInt32(&n, 1) } }() gosched() // Move clock forward to just before the first tick. clock.Add(9 * time.Second) if atomic.LoadInt32(&n) != 0 { t.Fatalf("expected 0, got %d", n) } // Move clock forward to the start of the first tick. clock.Add(1 * time.Second) if atomic.LoadInt32(&n) != 1 { t.Fatalf("expected 1, got %d", n) } // Move clock forward over several ticks. clock.Add(30 * time.Second) if atomic.LoadInt32(&n) != 4 { t.Fatalf("expected 4, got %d", n) } } // Ensure that the mock's Ticker channel sends at the correct time. func TestMock_Ticker(t *testing.T) { var n int32 clock := NewMock() // Create a channel to increment every microsecond. go func() { ticker := clock.Ticker(1 * time.Microsecond) for { <-ticker.C atomic.AddInt32(&n, 1) } }() gosched() // Move clock forward. clock.Add(10 * time.Microsecond) if atomic.LoadInt32(&n) != 10 { t.Fatalf("unexpected: %d", n) } } // Ensure that the mock's Ticker channel won't block if not read from. func TestMock_Ticker_Overflow(t *testing.T) { clock := NewMock() ticker := clock.Ticker(1 * time.Microsecond) clock.Add(10 * time.Microsecond) ticker.Stop() } // Ensure that the mock's Ticker can be stopped. func TestMock_Ticker_Stop(t *testing.T) { var n int32 clock := NewMock() // Create a channel to increment every second. ticker := clock.Ticker(1 * time.Second) go func() { for { <-ticker.C atomic.AddInt32(&n, 1) } }() gosched() // Move clock forward. clock.Add(5 * time.Second) if atomic.LoadInt32(&n) != 5 { t.Fatalf("expected 5, got: %d", n) } ticker.Stop() // Move clock forward again. clock.Add(5 * time.Second) if atomic.LoadInt32(&n) != 5 { t.Fatalf("still expected 5, got: %d", n) } } func TestMock_Ticker_Reset(t *testing.T) { var n int32 clock := NewMock() ticker := clock.Ticker(5 * time.Second) defer ticker.Stop() go func() { for { <-ticker.C atomic.AddInt32(&n, 1) } }() gosched() // Move clock forward. clock.Add(10 * time.Second) if atomic.LoadInt32(&n) != 2 { t.Fatalf("expected 2, got: %d", n) } clock.Add(4 * time.Second) ticker.Reset(5 * time.Second) // Advance the remaining second clock.Add(1 * time.Second) if atomic.LoadInt32(&n) != 2 { t.Fatalf("expected 2, got: %d", n) } // Advance the remaining 4 seconds from the previous tick clock.Add(4 * time.Second) if atomic.LoadInt32(&n) != 3 { t.Fatalf("expected 3, got: %d", n) } } // Ensure that multiple tickers can be used together. func TestMock_Ticker_Multi(t *testing.T) { var n int32 clock := NewMock() go func() { a := clock.Ticker(1 * time.Microsecond) b := clock.Ticker(3 * time.Microsecond) for { select { case <-a.C: atomic.AddInt32(&n, 1) case <-b.C: atomic.AddInt32(&n, 100) } } }() gosched() // Move clock forward. clock.Add(10 * time.Microsecond) gosched() if atomic.LoadInt32(&n) != 310 { t.Fatalf("unexpected: %d", n) } } func ExampleMock_After() { // Create a new mock clock. clock := NewMock() var count counter ready := make(chan struct{}) // Create a channel to execute after 10 mock seconds. go func() { ch := clock.After(10 * time.Second) close(ready) <-ch count.incr() }() <-ready // Print the starting value. fmt.Printf("%s: %d\n", clock.Now().UTC(), count.get()) // Move the clock forward 5 seconds and print the value again. clock.Add(5 * time.Second) fmt.Printf("%s: %d\n", clock.Now().UTC(), count.get()) // Move the clock forward 5 seconds to the tick time and check the value. clock.Add(5 * time.Second) fmt.Printf("%s: %d\n", clock.Now().UTC(), count.get()) // Output: // 1970-01-01 00:00:00 +0000 UTC: 0 // 1970-01-01 00:00:05 +0000 UTC: 0 // 1970-01-01 00:00:10 +0000 UTC: 1 } func ExampleMock_AfterFunc() { // Create a new mock clock. clock := NewMock() count := 0 // Execute a function after 10 mock seconds. clock.AfterFunc(10*time.Second, func() { count = 100 }) gosched() // Print the starting value. fmt.Printf("%s: %d\n", clock.Now().UTC(), count) // Move the clock forward 10 seconds and print the new value. clock.Add(10 * time.Second) fmt.Printf("%s: %d\n", clock.Now().UTC(), count) // Output: // 1970-01-01 00:00:00 +0000 UTC: 0 // 1970-01-01 00:00:10 +0000 UTC: 100 } func ExampleMock_Sleep() { // Create a new mock clock. clock := NewMock() var count counter // Execute a function after 10 mock seconds. go func() { clock.Sleep(10 * time.Second) count.incr() }() gosched() // Print the starting value. fmt.Printf("%s: %d\n", clock.Now().UTC(), count.get()) // Move the clock forward 10 seconds and print the new value. clock.Add(10 * time.Second) fmt.Printf("%s: %d\n", clock.Now().UTC(), count.get()) // Output: // 1970-01-01 00:00:00 +0000 UTC: 0 // 1970-01-01 00:00:10 +0000 UTC: 1 } func ExampleMock_Ticker() { // Create a new mock clock. clock := NewMock() var count counter ready := make(chan struct{}) // Increment count every mock second. go func() { ticker := clock.Ticker(1 * time.Second) close(ready) for { <-ticker.C count.incr() } }() <-ready // Move the clock forward 10 seconds and print the new value. clock.Add(10 * time.Second) fmt.Printf("Count is %d after 10 seconds\n", count.get()) // Move the clock forward 5 more seconds and print the new value. clock.Add(5 * time.Second) fmt.Printf("Count is %d after 15 seconds\n", count.get()) // Output: // Count is 10 after 10 seconds // Count is 15 after 15 seconds } func ExampleMock_Timer() { // Create a new mock clock. clock := NewMock() var count counter ready := make(chan struct{}) // Increment count after a mock second. go func() { timer := clock.Timer(1 * time.Second) close(ready) <-timer.C count.incr() }() <-ready // Move the clock forward 10 seconds and print the new value. clock.Add(10 * time.Second) fmt.Printf("Count is %d after 10 seconds\n", count.get()) // Output: // Count is 1 after 10 seconds } func TestMock_ReentrantDeadlock(t *testing.T) { mockedClock := NewMock() timer20 := mockedClock.Timer(20 * time.Second) go func() { v := <-timer20.C panic(fmt.Sprintf("timer should not have ticked: %v", v)) }() mockedClock.AfterFunc(10*time.Second, func() { timer20.Stop() }) mockedClock.Add(15 * time.Second) mockedClock.Add(15 * time.Second) } func warn(v ...interface{}) { fmt.Fprintln(os.Stderr, v...) } func warnf(msg string, v ...interface{}) { fmt.Fprintf(os.Stderr, msg+"\n", v...) } golang-github-benbjohnson-clock-1.3.0/context.go000066400000000000000000000040051417477736300216730ustar00rootroot00000000000000package clock import ( "context" "fmt" "sync" "time" ) func (m *Mock) WithTimeout(parent context.Context, timeout time.Duration) (context.Context, context.CancelFunc) { return m.WithDeadline(parent, m.Now().Add(timeout)) } func (m *Mock) WithDeadline(parent context.Context, deadline time.Time) (context.Context, context.CancelFunc) { if cur, ok := parent.Deadline(); ok && cur.Before(deadline) { // The current deadline is already sooner than the new one. return context.WithCancel(parent) } ctx := &timerCtx{clock: m, parent: parent, deadline: deadline, done: make(chan struct{})} propagateCancel(parent, ctx) dur := m.Until(deadline) if dur <= 0 { ctx.cancel(context.DeadlineExceeded) // deadline has already passed return ctx, func() {} } ctx.Lock() defer ctx.Unlock() if ctx.err == nil { ctx.timer = m.AfterFunc(dur, func() { ctx.cancel(context.DeadlineExceeded) }) } return ctx, func() { ctx.cancel(context.Canceled) } } // propagateCancel arranges for child to be canceled when parent is. func propagateCancel(parent context.Context, child *timerCtx) { if parent.Done() == nil { return // parent is never canceled } go func() { select { case <-parent.Done(): child.cancel(parent.Err()) case <-child.Done(): } }() } type timerCtx struct { sync.Mutex clock Clock parent context.Context deadline time.Time done chan struct{} err error timer *Timer } func (c *timerCtx) cancel(err error) { c.Lock() defer c.Unlock() if c.err != nil { return // already canceled } c.err = err close(c.done) if c.timer != nil { c.timer.Stop() c.timer = nil } } func (c *timerCtx) Deadline() (deadline time.Time, ok bool) { return c.deadline, true } func (c *timerCtx) Done() <-chan struct{} { return c.done } func (c *timerCtx) Err() error { return c.err } func (c *timerCtx) Value(key interface{}) interface{} { return c.parent.Value(key) } func (c *timerCtx) String() string { return fmt.Sprintf("clock.WithDeadline(%s [%s])", c.deadline, c.deadline.Sub(c.clock.Now())) } golang-github-benbjohnson-clock-1.3.0/context_test.go000066400000000000000000000056701417477736300227430ustar00rootroot00000000000000package clock import ( "context" "errors" "testing" "time" ) // Ensure that WithDeadline is cancelled when deadline exceeded. func TestMock_WithDeadline(t *testing.T) { m := NewMock() ctx, _ := m.WithDeadline(context.Background(), m.Now().Add(time.Second)) m.Add(time.Second) select { case <-ctx.Done(): if !errors.Is(ctx.Err(), context.DeadlineExceeded) { t.Error("invalid type of error returned when deadline exceeded") } default: t.Error("context is not cancelled when deadline exceeded") } } // Ensure that WithDeadline does nothing when the deadline is later than the current deadline. func TestMock_WithDeadlineLaterThanCurrent(t *testing.T) { m := NewMock() ctx, _ := m.WithDeadline(context.Background(), m.Now().Add(time.Second)) ctx, _ = m.WithDeadline(ctx, m.Now().Add(10*time.Second)) m.Add(time.Second) select { case <-ctx.Done(): if !errors.Is(ctx.Err(), context.DeadlineExceeded) { t.Error("invalid type of error returned when deadline exceeded") } default: t.Error("context is not cancelled when deadline exceeded") } } // Ensure that WithDeadline cancel closes Done channel with context.Canceled error. func TestMock_WithDeadlineCancel(t *testing.T) { m := NewMock() ctx, cancel := m.WithDeadline(context.Background(), m.Now().Add(time.Second)) cancel() select { case <-ctx.Done(): if !errors.Is(ctx.Err(), context.Canceled) { t.Error("invalid type of error returned after cancellation") } case <-time.After(time.Second): t.Error("context is not cancelled after cancel was called") } } // Ensure that WithDeadline closes child contexts after it was closed. func TestMock_WithDeadlineCancelledWithParent(t *testing.T) { m := NewMock() parent, cancel := context.WithCancel(context.Background()) ctx, _ := m.WithDeadline(parent, m.Now().Add(time.Second)) cancel() select { case <-ctx.Done(): if !errors.Is(ctx.Err(), context.Canceled) { t.Error("invalid type of error returned after cancellation") } case <-time.After(time.Second): t.Error("context is not cancelled when parent context is cancelled") } } // Ensure that WithDeadline cancelled immediately when deadline has already passed. func TestMock_WithDeadlineImmediate(t *testing.T) { m := NewMock() ctx, _ := m.WithDeadline(context.Background(), m.Now().Add(-time.Second)) select { case <-ctx.Done(): if !errors.Is(ctx.Err(), context.DeadlineExceeded) { t.Error("invalid type of error returned when deadline has already passed") } default: t.Error("context is not cancelled when deadline has already passed") } } // Ensure that WithTimeout is cancelled when deadline exceeded. func TestMock_WithTimeout(t *testing.T) { m := NewMock() ctx, _ := m.WithTimeout(context.Background(), time.Second) m.Add(time.Second) select { case <-ctx.Done(): if !errors.Is(ctx.Err(), context.DeadlineExceeded) { t.Error("invalid type of error returned when time is over") } default: t.Error("context is not cancelled when time is over") } } golang-github-benbjohnson-clock-1.3.0/go.mod000066400000000000000000000000551417477736300207670ustar00rootroot00000000000000module github.com/benbjohnson/clock go 1.15