A/B trial boot simulation
This C++17 standard-library example follows the trial boot diagram, chapter 04, and ADR-014. It illustrates boot decisions; it does not install software or access a device. No third-party libraries are required.
Build and run from the repository root with CMake 3.20 or later and a C++17 compiler. These commands use a Linux temporary build directory to keep generated files outside the repository:
cmake -S examples/simulations/ab-trial-boot -B /tmp/ota-ab-build -DCMAKE_BUILD_TYPE=Debug
cmake --build /tmp/ota-ab-build
/tmp/ota-ab-build/ab-simulate all
/tmp/ota-ab-build/ab-simulate attempts-exhausted
ctest --test-dir /tmp/ota-ab-build --output-on-failure
| Scenario | Expected result |
|---|---|
success |
Checks pass; B is confirmed and selected on the next boot. |
health-failure |
B fails checks; reset selects A; recovery checks pass. |
attempts-exhausted |
B never reaches checks; two unconfirmed attempts lead to A. |
restart-during-trial |
A reset preserves the consumed attempt; the second trial passes. |
interrupted-preparation |
Incomplete B remains ineligible; reset selects A. |
recovery-failure |
A also fails; stop at the boundary of a separate recovery policy. |
With no argument, ab-simulate runs all scenarios. Use --help to list the accepted arguments. Invalid arguments return a nonzero exit status.
The attempt limit of two is an illustrative choice. Each reset reconstructs the device from a versioned text record (boot-state.txt) in a temporary directory. The running slot is volatile. The saved normal slot stays A until B is confirmed. Boot control saves each consumed attempt before returning B as the selected system. A failed health check requests recovery immediately rather than spending the remaining trial attempts.
The example keeps boot state outside the simulated slots, as shown in the storage diagram. Each scenario uses a separate temporary directory, removed when it finishes. The model covers one A-to-B update; it does not model subsequent releases.
Assumptions and limits
Installation and artifact verification are represented by one preparation operation. Health checks are supplied boolean outcomes, including a timeout represented as failure. Resets are explicit calls; watchdog coverage and real startup timing are not simulated. Recovery assumes that A and shared data remain usable, unless the recovery-failure scenario supplies a failed check.
The text file is a teaching stand-in for boot state, using standard C++ streams and std::filesystem. It records the phase, normal slot, optional trial slot, attempts remaining, slot readiness, and outcome. Replacement uses a temporary file and rename, but does not flush storage, implement redundant records, or recover invalid records. Unreadable records stop the simulation with an error; they are never treated as confirmation. This is not a power-loss-safe boot-state implementation. The simulation assumes completed valid writes; it does not test interruption within a write, coordination with OTA progress, data migration, signatures, bootloader integration, or hardware failure. These need platform-specific implementation and failure-injection tests.
Outcomes remain local in the model. There is no server dependency or reporting transport. A real implementation must retain pending reports separately and send them when connectivity returns.
Source and checks
- simulation.hpp and simulation.cpp: typed state model, saved records, reset behavior, and scenarios.
- main.cpp: command-line runner.
- tests.cpp: scenario outcomes and invariants for saved attempts, incomplete slots, confirmation, and unreadable state. Checks remain enabled in Release builds.
- CMakeLists.txt: builds the runner and registers the invariant and CLI checks with CTest.