1
0
Fork 0

Adding upstream version 0.0~git20250112.cbee01f.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-24 11:16:24 +02:00
parent 4b058c1d3a
commit 43be5eaea5
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
5 changed files with 1087 additions and 0 deletions

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
The 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.

104
README.md Normal file
View file

@ -0,0 +1,104 @@
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.
[time]: http://golang.org/pkg/time/
## Usage
### Realtime Clock
Your application can maintain a `Clock` variable that will allow realtime and
mock clocks to be interchangable. 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, Jan 1, 1970 UTC).
### 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:
```
mock := clock.NewMock()
count := 0
// Kick off a timer to increment every 1 mock second.
go func() {
ticker := clock.Ticker(1 * time.Second)
for {
<-ticker.C
count++
}
}()
runtime.Gosched()
// Move the clock forward 10 second.
mock.Add(10 * time.Second)
// This prints 10.
fmt.Println(count)
```

360
clock.go Normal file
View file

@ -0,0 +1,360 @@
package clock
import (
"sort"
"sync"
"time"
)
// 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 make forward progress 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
Sleep(d time.Duration)
Tick(d time.Duration) <-chan time.Time
Ticker(d time.Duration) *Ticker
Timer(d time.Duration) *Timer
}
// 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) 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}
}
// 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
gosched func()
}
type MockOpt struct {
// Gosched is a function which is called to schedule other (app-specific)
// goroutines. Default implementation just sleeps for 1 millisecond. It is
// often not acceptable because it slows down tests significantly and is also
// potentialy fragile.
Gosched func()
}
// NewMock returns an instance of a mock clock with the default options.
// See also NewMockOpt
func NewMock() *Mock {
return NewMockOpt(MockOpt{})
}
// NewMockOpt returns an instance of a mock clock.
// The current time of the mock clock on initialization is the Unix epoch.
// See MockOpt for description of available options.
func NewMockOpt(opt MockOpt) *Mock {
mock := &Mock{
now: time.Unix(0, 0),
gosched: opt.Gosched,
}
if mock.gosched == nil {
mock.gosched = func() {
time.Sleep(1 * time.Millisecond)
}
}
return mock
}
// Add moves the current time of the mock clock forward by the 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 the other goroutines get handled.
m.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 the other goroutines get handled.
m.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
// it's next time if after the max time. Returns true if a timer is 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 the mock clocks wall time.
func (m *Mock) Since(t time.Time) time.Duration {
return m.Now().Sub(t)
}
// 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) {
m.mu.Lock()
defer m.mu.Unlock()
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.mu.Unlock()
t.mock.removeClockTimer((*internalTimer)(t))
t.mock.mu.Lock()
defer t.mock.mu.Unlock()
t.stopped = true
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.next = t.mock.now.Add(d)
t.mock.mu.Lock()
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) {
if t.fn != nil {
t.fn()
} else {
t.c <- now
}
t.mock.removeClockTimer((*internalTimer)(t))
t.mock.mu.Lock()
defer t.mock.mu.Unlock()
t.stopped = true
t.mock.gosched()
}
// 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.removeClockTimer((*internalTicker)(t))
}
}
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)
t.mock.gosched()
}

599
clock_test.go Normal file
View file

@ -0,0 +1,599 @@
package clock
import (
"fmt"
"os"
"sync"
"sync/atomic"
"testing"
"time"
)
// Ensure that the clock's After channel sends at the correct time.
func TestClock_After(t *testing.T) {
var ok bool
go func() {
time.Sleep(10 * time.Millisecond)
ok = true
}()
go func() {
time.Sleep(30 * time.Millisecond)
t.Fatal("too late")
}()
gosched()
<-New().After(20 * time.Millisecond)
if !ok {
t.Fatal("too early")
}
}
// Ensure that the clock's AfterFunc executes at the correct time.
func TestClock_AfterFunc(t *testing.T) {
var ok bool
go func() {
time.Sleep(10 * time.Millisecond)
ok = true
}()
go func() {
time.Sleep(30 * time.Millisecond)
t.Fatal("too late")
}()
gosched()
var wg sync.WaitGroup
wg.Add(1)
New().AfterFunc(20*time.Millisecond, func() {
wg.Done()
})
wg.Wait()
if !ok {
t.Fatal("too early")
}
}
// 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) {
var ok bool
go func() {
time.Sleep(10 * time.Millisecond)
ok = true
}()
go func() {
time.Sleep(30 * time.Millisecond)
t.Fatal("too late")
}()
gosched()
New().Sleep(20 * time.Millisecond)
if !ok {
t.Fatal("too early")
}
}
// Ensure that the clock ticks correctly.
func TestClock_Tick(t *testing.T) {
var ok bool
go func() {
time.Sleep(10 * time.Millisecond)
ok = true
}()
go func() {
time.Sleep(50 * time.Millisecond)
t.Fatal("too late")
}()
gosched()
c := New().Tick(20 * time.Millisecond)
<-c
<-c
if !ok {
t.Fatal("too early")
}
}
// Ensure that the clock's ticker ticks correctly.
func TestClock_Ticker(t *testing.T) {
var ok bool
go func() {
time.Sleep(100 * time.Millisecond)
ok = true
}()
go func() {
time.Sleep(200 * time.Millisecond)
t.Fatal("too late")
}()
gosched()
ticker := New().Ticker(50 * time.Millisecond)
<-ticker.C
<-ticker.C
if !ok {
t.Fatal("too early")
}
}
// Ensure that the clock's ticker can stop correctly.
func TestClock_Ticker_Stp(t *testing.T) {
var ok bool
go func() {
time.Sleep(10 * time.Millisecond)
ok = true
}()
gosched()
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 timer waits correctly.
func TestClock_Timer(t *testing.T) {
var ok bool
go func() {
time.Sleep(10 * time.Millisecond)
ok = true
}()
go func() {
time.Sleep(30 * time.Millisecond)
t.Fatal("too late")
}()
gosched()
timer := New().Timer(20 * time.Millisecond)
<-timer.C
if !ok {
t.Fatal("too early")
}
if timer.Stop() {
t.Fatal("timer still running")
}
}
// Ensure that the clock's timer can be stopped.
func TestClock_Timer_Stop(t *testing.T) {
var ok bool
go func() {
time.Sleep(10 * time.Millisecond)
ok = true
}()
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) {
var ok bool
go func() {
time.Sleep(20 * time.Millisecond)
ok = true
}()
go func() {
time.Sleep(30 * time.Millisecond)
t.Fatal("too late")
}()
gosched()
timer := New().Timer(10 * time.Millisecond)
if !timer.Reset(20 * time.Millisecond) {
t.Fatal("timer not running")
}
<-timer.C
if !ok {
t.Fatal("too early")
}
}
// 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())
}
}
// 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 the 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)
}
}
// 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()
count := 0
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 = 100
}()
<-ready
// Print the starting value.
fmt.Printf("%s: %d\n", clock.Now().UTC(), count)
// 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)
// 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)
// 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: 100
}
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()
count := 0
// Execute a function after 10 mock seconds.
go func() {
clock.Sleep(10 * time.Second)
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_Ticker() {
// Create a new mock clock.
clock := NewMock()
count := 0
ready := make(chan struct{})
// Increment count every mock second.
go func() {
ticker := clock.Ticker(1 * time.Second)
close(ready)
for {
<-ticker.C
count++
}
}()
<-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)
// 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)
// Output:
// Count is 10 after 10 seconds
// Count is 15 after 15 seconds
}
func ExampleMock_Timer() {
// Create a new mock clock.
clock := NewMock()
count := 0
ready := make(chan struct{})
// Increment count after a mock second.
go func() {
timer := clock.Timer(1 * time.Second)
close(ready)
<-timer.C
count++
}()
<-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)
// Output:
// Count is 1 after 10 seconds
}
func warn(v ...interface{}) { fmt.Fprintln(os.Stderr, v...) }
func warnf(msg string, v ...interface{}) { fmt.Fprintf(os.Stderr, msg+"\n", v...) }

3
go.mod Normal file
View file

@ -0,0 +1,3 @@
module github.com/dimonomid/clock
go 1.13