Test time-based GenServers instantly — no waiting, no flaky timing. This repository publishes the clock and the code that builds on it as four packages that share one version, so you add what you use.
| Package | What it is for | Hex | Docs |
|---|---|---|---|
gen_server_virtual_time |
virtual clock, GenServer/gen_statem wrappers, time backends |
hex.pm | hexdocs |
actor_simulation |
actor-system DSL, statistics, sequence diagrams, flowchart reports | hex.pm | hexdocs |
actor_simulation_codegen |
generate runnable C++, Go, Pony, Rust, Java, OMNeT++ projects from a simulation | hex.pm | hexdocs |
gen_server_virtual_time_migrate |
rewrite existing GenServer code for the virtual clock, report first |
— | — |
The idea is the RxJS TestScheduler applied to the BEAM: the clock is yours to advance, so a test describes the timeline it wants instead of sleeping through it.
{:ok, clock} = VirtualClock.start_link()
{:ok, server} = VirtualTimeGenServer.start_link(MyServer, :ok, virtual_clock: clock)
VirtualClock.advance(clock, 100_000) # 100 virtual seconds, ~10ms realThe clock is a plain OTP process, so it does not care what the code around it is written in. Two examples show the same ticker in other languages, and a test that asserts a minute of ticks without waiting for it:
examples/ticker_erlang— aVirtualTimeGenServerwritten in Erlang, on a rebar3 project that pulls the Elixir package withrebar_mixexamples/ticker_gleam— Gleam driving the clock through externals
Each package is a Mix project of its own; the root project is only a workspace that lets the scripts, benchmarks and maintainer tooling run against all four.
./scripts/test.sh # every package, then the tooling tests
./scripts/test.sh --only slow # the same selection everywhere
TEST_PACKAGES=actor_simulation ./scripts/test.sh # one package
cd packages/actor_simulation && mix test # work on one package
mix run benchmarks/virtual_time_speedup.exs # benchmarks run from the root
mix precommit # every CI check; --all adds dialyzer| What | Where |
|---|---|
| Design plan | docs/package_split_plan.md |
| Upgrade notes | mix virtual_time.doctor (shipped in gen_server_virtual_time) |
| Rewriting code | mix virtual_time.migrate (a task in packages/gen_server_virtual_time_migrate, run from a checkout) |
| Release process | elixir scripts/release.exs bump <kind>, then tag; the tag publishes all four |
| Generated examples | examples/ (sources) and generated/ (checked-in output) |
The version lives in VERSION; elixir scripts/release.exs check
fails when the packages disagree, and .github/workflows/publish.yml refuses to
publish a tag whose version they do not share.
MIT License — see LICENSE.