The problem with non-deterministic robot tests
A typical robotics callback-based system couples your logic directly to a live transport. To test the logic you must:- Stand up the full transport stack
- Time message delivery correctly
- Accept that the system clock, thread scheduling, and network conditions all affect results
How Basis enables deterministic testing
Basis separates your logic from the transport. Every handler in a Unit follows the same contract:HandlerPubSub structs generated for each handler expose a type_erased_callbacks map. Each entry maps a topic name to a callback that can receive a pre-deserialized message and, when the synchronizer fires, returns a TopicMap of output messages — all without touching the live transport.
The Unit base class grants DeterministicReplayer friendship explicitly so it can drive handlers directly:
DeterministicReplayer can inject recorded messages into a Unit’s handler map in the exact order and at the exact timestamps they were originally observed, producing identical outputs every time.
The two testing approaches
Unit testing
Instantiate a Unit directly in a test binary, call
Update() by hand, and publish messages manually. No coordinator or transport daemon needed.Deterministic replay
Record a real robot run to an MCAP file, then replay it in CI. The
DeterministicReplayer feeds messages back through each Unit’s handler map and produces identical outputs on every run.Unit testing
BecauseUnit::Update() is a plain virtual method, you can instantiate any Unit subclass in a test binary and drive it without a live coordinator:
Instantiate the Unit
Construct your Unit and call
CreateTransportManager(). You can pass a RecorderInterface* if you want to capture output messages.Initialize without subscribers
Pass
UnitInitializeOptions{.create_subscribers = false} to skip creating real transport subscribers. The handler map is still populated, which is what you need for injecting messages.Publish test messages and call Update()
Advertise test publishers and publish messages directly, then call
Update() to drain the callback queue.Unit testing as described above works today without any premium features. Full deterministic replay — where a
DeterministicReplayer drives handlers from a recorded MCAP file in CI — is a premium feature. The open-source Replayer class (which publishes messages back through the transport layer) is available to all users.Next steps
Recording
Record topics to MCAP files using the
Recorder and AsyncRecorder classesDeterministic replay
Replay recordings through the
Replayer or DeterministicReplayer to reproduce robot runs in CI