a fake clock for golang

clockwork

Mentioned in Awesome Go

GitHub Workflow Status Go Report Card Go Version go.dev reference

A simple fake clock for Go.

Usage

Replace uses of the time package with the clockwork.Clock interface instead.

For example, instead of using time.Sleep directly:

func myFunc() {
	time.Sleep(3 * time.Second)
	doSomething()
}

Inject a clock and use its Sleep method instead:

func myFunc(clock clockwork.Clock) {
	clock.Sleep(3 * time.Second)
	doSomething()
}

Now you can easily test myFunc with a FakeClock:

func TestMyFunc(t *testing.T) {
	c := clockwork.NewFakeClock()

	// Start our sleepy function
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		myFunc(c)
		wg.Done()
	}()

	// Ensure we wait until myFunc is sleeping
	c.BlockUntil(1)

	assertState()

	// Advance the FakeClock forward in time
	c.Advance(3 * time.Second)

	// Wait until the function completes
	wg.Wait()

	assertState()
}

and in production builds, simply inject the real clock instead:

myFunc(clockwork.NewRealClock())

See example_test.go for a full example.

Credits

clockwork is inspired by @wickman's threaded fake clock, and the Golang playground

License

Apache License, Version 2.0. Please see License File for more information.

Comments
  • Add support for Timers

    Add support for Timers

    Noticed that #13 appears stale. So I made a new branch locally rebased onto @aviddiviner's work, rebased his work onto master, and addressed the PR comments.

    I feel like this code has made the sleeper struct a little obfuscated and could be cleaned up to better reflect what was done for the Ticker stuff. I'm going to try to decouple it myself, but help/suggestions are welcome.

    resolves #10

  • make clockwork modules friendly

    make clockwork modules friendly

    Adding go.mod and go.sum is nice, but more important (and simpler) is tagging new version to include "recent" code changes. Without it go take version 0.10 which is quite old.

  • Refactor to avoid channels and goroutines

    Refactor to avoid channels and goroutines

    In terms of features, the addition of AfterFunc is the main benefit here. Otherwise it addresses a number of bugs, mainly around out-of-order or duplicate delivery of events, as exposed by the various tests added by these changes.

    These changes merge the sleeper and the fakeTimer task. Now a fakeTimer object is used to represent any future event on the timeline of a given fake clock. When the time advances to that point, a callback on the fakeTimer gets called. This makes sending to a channel only one of many possible responses on expiry. Not using channels, but instead an internal callback that gets called with the clock's lock held, allows using the timer far more flexible. It is now also the basis of ticker and of the After method. This change in general will do more things while holding the clock's lock, leading to more consistent behavior and less risk of data races.

    Basing After on Timer instead of the other way round also allows clearing the future events when timers get stopped or reset. This in turn helps BlockUntil actually track active blockers, ignoring obsolete ones that used to exist in the past.

    This fixes https://github.com/jonboulle/clockwork/issues/42.

  • Need help with maintenance?

    Need help with maintenance?

    Hi @jonboulle,

    First of all, thanks for this package, I think it's a quite popular one!

    I noticed there are a couple open issues and PRs and I wonder if you need help with maintenance. Either as a contributor to this repo, or if you don't want to maintain it anymore, as a managed fork somewhere (eg. https://github.com/gofrs).

    Let me know what you think!

  • Add support for fake tickers

    Add support for fake tickers

    I spend a little time on extending your library to add support for fake tickers. Currently this requires surrounding the Advance() calls with BlockUntil() to make sure that the ticker can go to sleep before Advance() is called and afterwards wake up before we start consuming ticks. Let me know what you think.

  • Set NewFakeClock initial time

    Set NewFakeClock initial time

    Using NewFakeClock() to make a Clock at a specific point in time requires a custom implementation (since fakeClock is internal) or calling Advance while depending on the internal detail that the clock starts in 1900. Is this within the desired project scope?

    I previously considered this package for some testing which depended on a specific timestamp for validations.

  • Move most lock logic to clockwork.go.

    Move most lock logic to clockwork.go.

    This uses longer timeouts throughout ticker_test.go to prevent flaky failures in slower runs.

    It uses a timeout of 1 minute, which seems more than reasonable.

    It also leaves the caller to execute multiple runs, if so desired.

  • Add Timers

    Add Timers

    Since both #13 and #22 look abandoned, this is another attempt at implementing fake timers. This doesn't build of the implementation in either previous attempt; rather I tried to implement fake timers in a manner similar to fake tickers.

    Closes #10

  • BlockUntil blocks forever if there are too many blockers

    BlockUntil blocks forever if there are too many blockers

    Consider the following code:

    func TestFakeClock(t *testing.T) {
    	c := clockwork.NewFakeClock()
    
    	go func(){
    		c.Sleep(time.Second)
    	}()
    	go func(){
    		c.Sleep(time.Second)
    	}()
    
    	c.BlockUntil(1)
    }
    

    Most of the time this code will pass! But non-deterministically (if the goroutines are scheduled at just the right time), the BlockUntil will block forever.

    The problem is that numExpected != numActual (2 != 1), so the BlockUntil call will block forever. I think it should be changed so that it blocks until numExpected <= numActual. Would you accept a PR to change this?

    I could also imagine other fixes, like BlockUntil panic-ing if there are more sleepers than numExpected, rather than blocking forever.

  • Timers

    Timers

    Hey. Thanks for the excellent repo, I was looking for a time testing tool and this was a great start. Not sure if you're interested, but I've implemented timers on my fork.

    The fake timers are quite closely inspired by the ones from the standard library and it should all work well. Your old After(d Duration) <-chan Time is now just a shortcut for my NewTimer(d).C (as it is in the standard lib), so I got test coverage from all your existing tests on the new timers too.

    Let me know what you think and if you want me to modify anything. Cheers!

  • add AfterFunc

    add AfterFunc

    I didn't see a way to build the equivalent of time.AfterFunc using the public APIs without leaking a goroutine and the callback func.

    With the internal APIs, it's pretty easy.

  • Move lock logic to fakeClock. Reduce Ticker and Timer code complexity.

    Move lock logic to fakeClock. Reduce Ticker and Timer code complexity.

    • Minimize the custom logic of Tickers and Timers.
    • Don't allow Tickers and Timers to access to the lock of the fakeClock that controls them. Use closures instead.
    • Use a common expirer interface for Timers and Tickers, rather than reusing Timers for both.
    • Create a firer struct to handle commonalities between Tickers and Timers.
    • Use a single function & code path for Timers, regardless of if they are made with NewTimer or AfterFunc.
    • Add Reset to Ticker interface, which was added in go 1.15.
    • Use the mutex hat pattern and add documentation on what the mutex protects.
    • Change field name/type from "sleeper"/"timer" to "waiter"/"expirer" and add documentation.
    • Various documentation updates.
    • Sort expirers using a sort.Slices.
    • NIT: Use the standard layout time as the default for NewFakeClock.
    • Style NIT: Return/continue early where possible.

    Tested: All tests pass, no race conditions from go -race.

  • Fix flaky clockwork tests from #44.

    Fix flaky clockwork tests from #44.

    • Only run TestFakeTicker_DeliveryOrder once per invocation.
    • Use channels to avoid race condition in TestFakeClockTimer_ResetRace.
    • Run tests in parallel.

    Tested:

    • Run 10,000 times with 2 timeout flakes (i.e. 0.02% flake rate, likely due to slow machines)
    • Ran with -race, no race conditions reported.
  • TestFakeTicker_DeliveryOrder and TestFakeTickerTick are flakey at commit 79f9aa2

    TestFakeTicker_DeliveryOrder and TestFakeTickerTick are flakey at commit 79f9aa2

    Fast submission just to get this on record:

    Test clockwork/clockwork_test: FAILED in 114 out of 1000 in 13.3s at commit 79f9aa2:

    TestFakeTicker_DeliveryOrder: fails in 113 out of 1000 runs

    third_party/golang/clockwork/ticker_test.go:119: Expected ticker event didn't arrive!
    

    TestFakeTicker_DeliveryOrder: fails 1 out of 1000 runs

    third_party/golang/clockwork/ticker_test.go:45: expected tick!
        third_party/golang/clockwork/ticker_test.go:55: wrong tick time, got: 0001-01-01 00:00:00.000000001 +0000 UTC, want: 0001-01-01 00:00:00.000000003 +0000 UTC
    

    These issues are not present in v0.3.0, commit 6a247c3:

    10000 runs: clockwork:clockwork_test PASSED in 18.4s

  • Make FakeTicker synchronous

    Make FakeTicker synchronous

    I'm trying to use FakeTicker to test production code that uses clockwork.Ticker. I found that my FakeTicker would "race ahead", since it doesn't block on ft.c being available to receive a tick.

    I make it synchronous, and add a test case showing the behavior.

  • Fake clock advances past intervening timers

    Fake clock advances past intervening timers

    I know that the real clock doesn't guarantee that timers will go off exactly on time, but in practice they go off somewhat close to on schedule.

    I just created a pull request to add AfterFunc. I noticed when testing it that I can advance the clock well past when a timer is supposed to fire. The timer fires, but it thinks the time is well past when it should be.

    So then, the question is: should the clock pause for real time as it advances past timers? I think that for AfterFunc, it should be willing to wait for f() to return or for one real second to pass, whichever comes first.

    I don't think any of the other APIs provide any way to know how long to pause.

goCron: A Golang Job Scheduling Package.

goCron: A Golang Job Scheduling Package.

Jan 9, 2023
golang job dispatcher
golang job dispatcher

go-gearman The shardingkey is hashed to the same queue, each of which is bound to a worker.

Dec 28, 2022
Lightweight, fast and dependency-free Cron expression parser (due checker) for Golang (tested on v1.13 and above)

adhocore/gronx gronx is Golang cron expression parser ported from adhocore/cron-expr. Zero dependency. Very fast because it bails early in case a segm

Dec 30, 2022
A lightweight job scheduler based on priority queue with timeout, retry, replica, context cancellation and easy semantics for job chaining. Build for golang web apps.

Table of Contents Introduction What is RIO? Concern An asynchronous job processor Easy management of these goroutines and chaining them Introduction W

Dec 9, 2022
Simple, efficient background processing for Golang backed by RabbitMQ and Redis
Simple, efficient background processing for Golang backed by RabbitMQ and Redis

Table of Contents How to Use Motivation Requirements Features Examples Setup Config Client/Server Task Worker/Task Hander Register The Handlers Send t

Nov 10, 2022
cpuworker - A Customized Goroutine Scheduler over Golang Runtime
cpuworker - A Customized Goroutine Scheduler over Golang Runtime

cpuworker Status Working in process. Run the Demo Make sure the GOMAXPROCS is bigger than 1 and there is at least GOMAXPROCS physical OS threads avail

Dec 6, 2022
YTask is an asynchronous task queue for handling distributed jobs in golang
YTask is an asynchronous task queue for handling distributed jobs in golang

YTask is an asynchronous task queue for handling distributed jobs in golang

Dec 24, 2022
goInterLock is golang job/task scheduler with distributed locking mechanism (by Using Redis🔒).
goInterLock is golang job/task scheduler with distributed locking mechanism (by Using Redis🔒).

goInterLock is golang job/task scheduler with distributed locking mechanism. In distributed system locking is preventing task been executed in every instant that has the scheduler,

Dec 5, 2022
a self terminating concurrent job queue for indeterminate workloads in golang

jobtracker - a self terminating concurrent job queue for indeterminate workloads in golang This library is primarily useful for technically-recursive

Sep 6, 2022
go-sche is a golang library that lets you schedule your task to be executed later.

go-sche is a golang library that lets you schedule your task to be executed later.

Dec 24, 2022
Scheduler CRUD For Golang
Scheduler CRUD For Golang

scheduler-CRUD 從dbdiagram.io建立table與create語法 在mysql建立table 以sqlc建CRUD function與Transaction 加入viper讀環境變量 以gin開發REST API 加入Dockerfile docker build -t th

Feb 10, 2022
Scheduler: the scheduler of distbuild written in Golang

scheduler Introduction scheduler is the scheduler of distbuild written in Go. Pr

Feb 9, 2022
Delay-tasks - A delayed tasks implementation for golang
Delay-tasks - A delayed tasks implementation for golang

delay-tasks An implementation of delayed tasks. Usage $ git clone https://github

Jan 14, 2022
Tasks - Golang CLI, Task manager

Tasks Golang CLI, Task manager Prerequisites Golang Setup environment variables

Jan 30, 2022
Clock is a small library for mocking time in Go.

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

Dec 30, 2022
GoVector is a vector clock logging library written in Go.
GoVector is a vector clock logging library written in Go.

GoVector is a vector clock logging library written in Go. The vector clock algorithm is used to order events in distributed systems in the absence of a centralized clock. GoVector implements the vector clock algorithm and provides feature-rich logging and encoding infrastructure.

Nov 28, 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

Dec 15, 2021
Simple text-line analog clock

anaclock anaclock prints a simple analog clock as a line of text. Demo $ anaclock 23 . : .| 00 anaclock is easy to use in CLI prompts or anywhere

Dec 31, 2021
Raspberry Pi alarm clock for childs, to let them know whether they can wake up or stay in bed

Miveil Raspberry Pi alarm clock for childs, to let them know whether they can wake up or stay in bed. The idea was to have a simple device that let my

Apr 14, 2022
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 :

Feb 3, 2022