metrique-timesource
A flexible time source abstraction for Rust applications that allows for easy testing and mocking of time-dependent code.
This was originally written to support Metrique, but can be used for any application that wants to use deterministic time in tests.
Features
- Zero-cost abstraction when not compiled with the
custom-timesourceenabled - Built in support for
tokio's timepausewithtokiofeature - Provide a time source manually or via a thread-local
- Compatible with
std::time::Instantandstd::time::SystemTime
Usage
Add the crate to your Cargo.toml:
[]
= "0.1.0"
# If you plan on testing:
[]
= { = "0.1.0", = ["custom-timesource", "tokio"] }
= { = "1", = ["test-util", "full"] }
In production code
In production code, you can choose from two patterns to access a time source. Generally, loading a timesource automatically is the recommended pattern, but some teams prefer the benefits of threading it explicitly (or, e.g. are testing across many threads that makes the mocks difficult to use).
- Manually thread the
TimeSourcewhere needed.
use TimeSource;
- Use
get_time_source/time_sourceto source it automatically:
use ;
get_time_source allows passing an Option<TimeSource> that is used as the highest priority option. This is equivalent to maybe_ts.unwrap_or_else(||time_source()).
use ;
In both cases, you can then use the timesource to source SystemTimes and Instants that can be externally controlled. Even in the time_source case, when the custom-timesource feature is not enabled, the function is inlined to return a zero-sized-type.
Working with TimeSource
TimeSource returns wrapped versions of Instant and SystemTime. This allows elapsed to function as expected, even when a time source is overridden.
They provide many of the same methods as the std variants. If you need a method that is not available, you can use .as_std().
In tests
If you use time_source, you can override the timesource for the current thread with set_time_source which returns a guard:
use ;
use ;
# async
# new_current_thread.build.unwrap.block_on
with_time_source is also provided which allows running a given closure with a time_source installed.
use ;
use UNIX_EPOCH;
let ts = at_time;
let custom = custom;
// Run code with the custom time source
with_time_source;
Writing your own mock time
2 mock time sources are provided:
fake::StaticTimeSourcewhich always returns the same time and instantTokioTimewhich usestokio::time::Instant::now
It is also possible to write your own by implementing the Time trait. See the fakes module for an example.