Packages

Turn regular ExUnit tests into readable behavior reports

Current section

Files

Jump to
ex_unit_atlas docs architecture.md
Raw

docs/architecture.md

# Architecture
ExUnit Atlas is intentionally small. The public API consists of
`ExUnitAtlas`, `step/2`, `check/2`, and the formatter users register with
ExUnit.
## Data flow
```text
test process
└─ step/check
└─ Recorder
└─ Formatter receives test_finished
└─ Report normalization
├─ report.json
└─ index.html
```
## Components
### `ExUnitAtlas`
Provides `use ExUnitAtlas`, imports the two macros, and registers a setup
callback that derives the internal owner key from the public ExUnit context.
Each macro:
1. validates its name;
2. records the start time;
3. executes the block exactly once;
4. records success or failure;
5. returns the original result or re-raises the original failure.
### Recorder
A named GenServer which stores one ordered item stream per owner key. Sequence
numbers are internal. Taking a completed test's items atomically removes that
owner from state.
If a test ends while an item is open, the recorder finalizes it as
`interrupted`. If the recorder is unavailable, instrumentation becomes a
best-effort no-op so it cannot mask user code or test failures.
### Formatter
Receives public ExUnit formatter events. On `test_finished`, it associates the
test with its recorded items. On `suite_finished`, it builds the normalized
report and writes both output formats.
It does not replace the standard ExUnit CLI formatter, print individual test
results, or control the suite exit status.
### Report normalization
Converts ExUnit structures and recorder items into a deterministic,
JSON-safe tree. Internal PIDs, references, functions, structs, and ownership
keys never enter the report.
### JSON encoding
A strict encoder for the limited normalized data model. It accepts only null,
booleans, integers, UTF-8 strings, lists, and maps with string keys. Output is
written through a temporary file followed by rename.
### HTML rendering
Renders the same normalized report as a self-contained HTML document. It has
no external assets, JavaScript, server requirement, or build step. All
user-controlled strings are escaped.
## Invariants
- Instrumented blocks execute exactly once.
- Return values are not transformed.
- `error`, `exit`, and `throw` preserve kind, reason, and stacktrace.
- Items remain ordered and isolated between async tests.
- Reports are written after both successful and failed suites.
- Generated files never contain arbitrary Elixir terms.