Clock is a small library for mocking time in Go.

Related tags

Date and Time clock
Overview

clock

Clock is a small library for mocking time in Go. It provides an interface around the standard library's time package so that the application can use the realtime clock while tests can use the mock 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:

import "github.com/benbjohnson/clock"

type Application struct {
	Clock clock.Clock
}

You could initialize it to use the realtime clock like this:

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:

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:

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 := 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)
Comments
  • Context WithDeadline/WithTimeout methods

    Context WithDeadline/WithTimeout methods

    Hi. I don't know what you think of this, but I make heavy use of contexts in my code and I needed to test some timeouts using the mock clock. So I added on two methods to the Clock interface, as well as their mock implementations (inspired from the core library context.WithDeadline code).

    Let me know what you think, I'm happy to tweak things. Of course, we can also add tests, etc.

    opened by aviddiviner 7
  • Three race conditions in clock.go

    Three race conditions in clock.go

    TestRace1 has a race condition in clock.go. TestRace2 has two race conditions. TestRace2 it is quite flappy, running it ten times should be enough. These races were detected with `go test ./... -race'

    func TestRace1(t *testing.T) {
        clock := NewMock()
        go clock.Add(time.Second)
        go clock.Add(time.Second)
    }
    
    func TestRace2(t *testing.T) {
        clock := NewMock()
        ch := clock.After(3 * time.Second)
        go func() {
            for {
                go clock.Add(time.Millisecond * 100)
            }
        }()
        <-ch
    }
    
    opened by CorgiMan 7
  • Unit tests fail with GOMAXPROCS=8

    Unit tests fail with GOMAXPROCS=8

    Unit tests fail when we increase the number of threads:

    $ export GOMAXPROCS=8
    $ go test
    --- FAIL: TestMock_After-8 (0.00s)
            clock_test.go:203: too late
    --- FAIL: ExampleMock_After (0.00s)
    got:
    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: 0
    want:
    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
    --- FAIL: ExampleMock_Ticker (0.00s)
    got:
    Count is 0 after 10 seconds
    Count is 0 after 15 seconds
    want:
    Count is 10 after 10 seconds
    Count is 15 after 15 seconds
    --- FAIL: ExampleMock_Timer (0.00s)
    got:
    Count is 0 after 10 seconds
    want:
    Count is 1 after 10 seconds
    FAIL
    exit status 1
    FAIL    github.com/benbjohnson/clock    0.304s
    

    The tests that fail change between executions....

    opened by inercia 5
  • Use channels to avoid races in tests

    Use channels to avoid races in tests

    Using a mock for time doesn't mean races aren't important. This tries to set a good Example by using channels rather than shared variables for communication between goroutines.

    opened by djmitche 3
  • Add synchronization

    Add synchronization

    Mock clock tests are prone to be flaky because of sync issues between the threads that start timers, the threads that handle timers, and the thread that is managing the clock. Commonly this is handled by throwing in a bunch of sleeps and hoping for the best.

    This change introduces functionality to actively sync between these threads.

    opened by kraney 2
  • unlock context during fn call

    unlock context during fn call

    Hi!

    There is a dead lock if you create a AfterFunc callback with a call to Now(). This request basically unlocks the context before calling fn so you can trigger other operations.

    opened by MetalBlueberry 2
  • De-race the tests; support concurrent Mock::Add; add Mock::Stop support

    De-race the tests; support concurrent Mock::Add; add Mock::Stop support

    I'm happy to contribute several improvements.

    I removed the tests for the real clock because it's basically impossible to make them not racey. The test now passes w/ Go-1.10 under the race detector.

    opened by jmacd 2
  • Introduce ticker Reset

    Introduce ticker Reset

    Go 1.15 introduces the Reset method on the time.Ticker object. I had a need for this, so I thought I'd share my branch.

    It is a very simple take which makes no attempt at mocking the method, instead ignoring it in the mock implementation. I can of course make changes for Reset to be mocked as well, although I am curious what that would look like in terms of behavior.

    opened by mqnfred 1
  • crash in multi groutines

    crash in multi groutines

    there is a NewMock() in each goroutine, but it crashed.

    runtime.goparkunlock(...) /usr/local/go/src/runtime/proc.go:307 time.Sleep(0xf4240) /usr/local/go/src/runtime/time.go:105 +0x159 github.com/benbjohnson/clock.gosched(...) /Users/*/git/go/src/github.com/benbjohnson/clock/clock.go:327 github.com/benbjohnson/clock.(Mock).Add(0xc050ac6940, 0xdf8475800) /Users//git/go/src/github.com/benbjohnson/clock/clock.go:90 +0xf0

    opened by nifflin 1
  • Issue adding time to mocked clock

    Issue adding time to mocked clock

    In the below code I get an error:

    mockClock := clock.NewMock()
    mockClock.Add(2 * time.Hour)
    

    mockClock.Add undefined (type clock.Clock has no field or method Add)

    Is there another function I should be using instead?

    opened by nathanjohnson320 1
  • Fix unit test failures when running on multiple cpus

    Fix unit test failures when running on multiple cpus

    As reported in #4, unit tests fail when running with multiple CPUs.

    The code was relying on runtime.Gosched() to pause the current goroutine and run some other one for long enough to get its timer, sleep, or whatever into the clock data structures. This was never really reliable, and when the two goroutines are running on different CPUs runtime.Gosched() is likely to do nothing at all.

    These changes remove all use of runtime.Gosched(), either by introducing a ready channel which synchronises the two sides, or by going back to your earlier approach of sleeping for a short time. Sleeping is not 100% reliable either, but far more likely to achieve the desired effect.

    opened by bboreham 1
  • Maintenance status?

    Maintenance status?

    Hey!

    There are some pull requests fixing long standing bugs. Would it be possible to merge them and release a new version. Notably:

    • #42
    • #45 (need an additional patch for tests, https://github.com/benbjohnson/clock/commit/739bd11b5833f87d3d77433c9c4876aa2f6e580a)
    • #46

    Also, could be interesting:

    • #43 (I would also add "1.x" to the list of tested versions)

    I am using these patches: https://github.com/benbjohnson/clock/compare/master...vincentbernat:vbe/master. Tests pass: https://github.com/vincentbernat/go-clock/actions/runs/3111243171.

    opened by vincentbernat 0
  • Allow resetting previously stopped tickers

    Allow resetting previously stopped tickers

    Normal time.Tickers support being Stop()ed, then later restarted by calling Reset(). However, mocked clock.Tickers do not; once they are stopped they can never be restarted.

    This PR fixes that behavior, and adds tests to ensure it isn't broken again.

    opened by cptpcrd 0
  • AfterFunc underlying function is executed in its own goroutine

    AfterFunc underlying function is executed in its own goroutine

    Original AfterFunc executes function in its own goroutine Mock should do it in the same way, otherwise tests doesn't cover races with this kind of timer

    opened by smirnov-vs 1
  • Go race with Mock

    Go race with Mock

    I believe the following demonstrates the issue:

    c := clock.NewMock()
    go c.Ticker(time.Second).Stop()
    c.Add(time.Second)
    

    Unfortunately, it's hard to get Go's race detector to catch this (I only ran it across it in a more complicated case by accident).

    The detailed problem is as follows:

    • The internal method removeClockTimer sorts the timers while holding the mutex. The sort method calls Next(), which reads the internal next field.
    • The internal method runNextTimer calls Tick on the timer without holding the mutex, and Tick sets the internal next field of the timer.

    It seems that solving this might just require removing the sort.Sort call in removeClockTimer.

    opened by stevedalton 0
  • Minimal GitHub Workflow for CI

    Minimal GitHub Workflow for CI

    Add a minimal GitHub Workflow configuration for CI on the project. This will test against both currently supported versions of Go per the Go Compatibility Guarantee.

    opened by abhinav 0
  • Mock: Fix AfterFunc data race

    Mock: Fix AfterFunc data race

    There's a data race if we call Mock.AfterFunc and Mock.Add concurrently.

    The included test demonstrates this with go test -race.

    $ go test -race
    ==================
    WARNING: DATA RACE
    Read at 0x00c0000a64e8 by goroutine 50:
      github.com/benbjohnson/clock.(*internalTimer).Tick()
          [...]/clock/clock.go:314 +0xc4
      github.com/benbjohnson/clock.(*Mock).runNextTimer()
          [...]/clock/clock.go:156 +0x1ce
      github.com/benbjohnson/clock.(*Mock).Add()
          [...]/clock/clock.go:96 +0x97
      github.com/benbjohnson/clock.TestMock_AddAfterFuncRace.func3()
          [...]/clock/clock_test.go:687 +0xa7
    
    Previous write at 0x00c0000a64e8 by goroutine 49:
      github.com/benbjohnson/clock.(*Mock).AfterFunc()
          [...]/clock/clock.go:170 +0x178
      github.com/benbjohnson/clock.TestMock_AddAfterFuncRace.func2()
          [...]/clock/clock_test.go:677 +0xb9
    
    Goroutine 50 (running) created at:
      github.com/benbjohnson/clock.TestMock_AddAfterFuncRace()
          [...]/clock/clock_test.go:683 +0x464
      testing.tRunner()
          [...]/go/1.17.2/libexec/src/testing/testing.go:1259 +0x22f
      testing.(*T).Run·dwrap·21()
          [...]/go/1.17.2/libexec/src/testing/testing.go:1306 +0x47
    
    Goroutine 49 (finished) created at:
      github.com/benbjohnson/clock.TestMock_AddAfterFuncRace()
          [...]/clock/clock_test.go:673 +0x347
      testing.tRunner()
          [...]/go/1.17.2/libexec/src/testing/testing.go:1259 +0x22f
      testing.(*T).Run·dwrap·21()
          [...]/go/1.17.2/libexec/src/testing/testing.go:1306 +0x47
    ==================
    --- FAIL: TestMock_AddAfterFuncRace (0.01s)
        testing.go:1152: race detected during execution of test
    FAIL
    exit status 1
    

    Fix the race by applying the same mutex used while reading t.fn in internalTimer.Tick.

    opened by abhinav 1
Releases(v1.3.0)
Owner
Ben Johnson
Ben Johnson
:clock1: Date and Time - Golang Formatting Library

Kair Date and Time - Golang Formatting Library Setup To get Kair > Go CLI go get github.com/GuilhermeCaruso/kair > Go DEP dep ensure -add github.com/G

Guilherme Caruso 23 Mar 15, 2022
Go time library inspired by Moment.js

Goment Current Version: 1.4.0 Changelog Goment is a port of the popular Javascript datetime library Moment.js. It follows the Moment.js API closely, w

Nick Leeper 195 Sep 20, 2022
Carbon for Golang, an extension for Time

Carbon A simple extension for Time based on PHP's Carbon library. Features: Time is embedded into Carbon (provides access to all of Time's functionali

Uniplaces LTD 727 Sep 27, 2022
:clock8: Better time duration formatting in Go!

durafmt durafmt is a tiny Go library that formats time.Duration strings (and types) into a human readable format. go get github.com/hako/durafmt Why

Wesley Hill 447 Sep 26, 2022
Now is a time toolkit for golang

Now Now is a time toolkit for golang Install go get -u github.com/jinzhu/now Usage Calculating time based on current time import "github.com/jinzhu/n

Jinzhu 3.8k Sep 27, 2022
Golang package to manipulate time intervals.

timespan timespan is a Go library for interacting with intervals of time, defined as a start time and a duration. Documentation API Installation Insta

null 82 Sep 26, 2022
timeutil - useful extensions (Timedelta, Strftime, ...) to the golang's time package

timeutil - useful extensions to the golang's time package timeutil provides useful extensions (Timedelta, Strftime, ...) to the golang's time package.

Kyoung-chan Lee 189 Sep 26, 2022
fasttime - fast time formatting for go

fasttime - fast time formatting for go

phuslu 67 Sep 15, 2022
🌐 A time zone helper

?? A time zone helper tz helps you schedule things across time zones. It is an interactive TUI program that displays time across a few time zones of y

Arnaud Berthomier 685 Sep 27, 2022
A natural language date/time parser with pluggable rules

when when is a natural language date/time parser with pluggable rules and merge strategies Examples tonight at 11:10 pm at Friday afternoon the deadli

Oleg Lebedev 1.2k Sep 12, 2022
Copy of stdlib's time.Duration, but ParseDuration accepts other bigger units such as days, weeks, months and years

duration Copy of stdlib's time.Duration, but ParseDuration accepts other units as well: d: days (7 * 24 * time.Hour) w: weeks (7 * Day) mo: months (30

Carlos Alexandro Becker 11 Jun 21, 2022
time format golang

a simple plugin to change date and time format

Ibnu Surkati 2 Sep 29, 2021
Structural time package for jalali calendar

Jalali Structural time package for jalali calendar. This package support parse from string, json and time. Structures There are three data structures

Bardo Go Framework 1 Mar 21, 2022
Show time by timezone

Show time by timezone

Michael Bruskov 1 Jan 22, 2022
Timediff is a Go package for printing human readable, relative time differences 🕰️

timediff is a Go package for printing human readable, relative time differences. Output is based on ranges defined in the Day.js JavaScript library, and can be customized if needed.

MergeStat 283 Sep 17, 2022
Go-timeparser - Flexible Time Parser for Golang

go-timeparser Flexible Time Parser for Golang Installation Download timeparser w

Kazuaki Yamamoto 1 Jun 9, 2022
timea.go (did you see what I did there?) is a simple library to print given times in

timea.go timea.go (did you see what I did there?) is a simple library to print given times in "time ago" manner. Usage Get it: go get github.com/caarl

Carlos Alexandro Becker 26 Feb 17, 2022
Go-olson-timezone - A Golang library that tries to figure out your local timezone

go-olson-timezone A Golang library that tries to figure out your local timezone.

Carlos Henrique Guardão Gandarez 2 Feb 16, 2022
CPU usage percentage is the ratio of the total time the CPU was active, to the elapsed time of the clock on your wall.

Docker-Kubernetes-Container-CPU-Utilization Implementing CPU Load goroutine requires the user to call the goroutine from the main file. go CPULoadCalc

Ishank Jain 1 Dec 15, 2021
A simple digital clock written in go to show time in hh : mm : ss format in console

Go console clock a simple digital clock written in go to show time in "hh : mm :

Mojtaba Khodami 0 Feb 3, 2022