Diorama is a Swift library for recording and replaying deterministic representations of nondeterministic dependencies during tests.
The name reflects its purpose: a carefully authored, static representation of a complex environment. A scenario may contain several independently named systems, including multiple instances of the same system, and consumers may add their own systems alongside the first-party implementations.
The accepted design covers:
- a concurrency-safe scenario execution with record, replay, and passthrough modes;
- heterogeneous, independently keyed system attachments;
- optional deterministic
Codablepersistence and external resources; - structured diagnostics and opt-in test failure integration;
- one-to-one real-time replay scheduling;
- a small random-number proving system;
- wall-clock observation and monotonic sleeping;
- Apple-only Core Location recording and replay with native location delivery;
- shared HTTP semantics and a URLSession adapter for Apple Foundation and FoundationNetworking.
Replay never contacts a live dependency when a recording is missing, ambiguous, incompatible, or exhausted.
Start with the documentation index. The design decision index links the seventeen accepted decisions, and the design overview consolidates their architecture and deferred scope.
Dependency adoption is governed by the dependency policy. Formatting, linting, platform CI, strict concurrency, and coverage expectations are defined by the quality gates and CI policy.
Import Diorama when writing tests, together with the systems you use, such as
DioramaRandom. Setup takes a string scenario ID and default mode, accepts a
file URL, and coordinates in-memory or file-backed runs.
Diorama is a final class that supports sequential reuse. References to the
same instance share one execution gate: overlapping or nested execute calls
throw DioramaExecutionError.executionAlreadyInProgress before startup.
Separate instances can execute concurrently.
After execute returns or rethrows the original body error, diorama.report
contains immutable finalization and publication evidence. A new admitted
attempt clears it; it is nil during execution and after startup failure.
Startup errors carry their own evidence. Inspect or retain the report before
starting another attempt. For example, a test can use defer to render the
report while allowing its body error to reach the test framework:
defer {
if let report = diorama.report {
print(report.rendered())
}
}
_ = try await diorama.execute { context, dependency in
try await exerciseApplication(using: dependency)
}The example assumes an existing setup with one dependency. Reporting does not decide whether the test passes; body errors still propagate unchanged, and configured publication still depends on recording health rather than body success.
Diagnostics are retained as they occur and exposed in completed reports. There
are no live diagnostic handlers or in-flight health checks. At the Core level,
DiagnosticReporter.report is nil until freezing; finish() and startup errors
provide the completed evidence. Later misuse enters a separately inspectable
postFinishDiagnostics snapshot without changing the final report. Future
checks and checkpoint/cursor APIs are considered with test integration in the
inspection notes.
DioramaCore provides the execution engine and system authoring contracts.
DioramaPersistence provides codecs, repositories, and storage over core
definitions. Both remain available independently. Importing the consumer module
links persistence internally; in-memory runs require neither a store nor
persistable systems.
System authors and callers constructing low-level tracks import DioramaCore.
Callers configuring codecs or custom storage import DioramaPersistence.
The repository includes an in-memory and file-backed random record/replay example:
swift run --package-path Examples DioramaRandomUsageSee the Examples guide for the runnable workflow and a compiled consumer-system persistence registration.
It passes the recording's resulting definition directly into in-memory replay
and prints matching values. It also records and replays a temporary scenario
file, removing that example file afterward. Ordinary setup uses Diorama and
DioramaRandom; the example imports core to name types in its helper functions.
Each scoped execution finalizes and reports optional publication separately.
It lives in a separate examples package that depends on Diorama by a relative
path, so the Diorama library package remains library-only.
Development uses Xcode 27.2 and its Swift 6.4.2 toolchain. Install Mint 0.18.0, then install the repository's exact SwiftFormat and SwiftLint versions:
mint bootstrapUse scripts/format to apply formatting, scripts/lint for non-mutating format
and lint checks, scripts/test for debug tests and a release build, and
scripts/check for the complete non-mutating local gate. A missing or different
Mint version fails without downloading code; a missing executable also prints
the exact version and installation URL.
Use scripts/coverage swiftpm macos for local SwiftPM coverage or
scripts/coverage ios for the pinned iOS Simulator build, test, and coverage
run. Every platform exports repository-relative LCOV for Codecov; coverage
artifacts stay under the ignored .build directory.
All work takes place on feature branches. The owner confirms each atomic slice's scope before implementation; feature-branch commits and history rewrites, including published history, are permitted within that scope. The complete branch diff is reviewed before approval to push and create a PR. Merging requires passing required CI and a separate explicit owner request. See AGENTS.md for repository operating rules and the implementation plan for the review units and workflow.
The initial platform objective is macOS 15 and later, iOS 18 and later, and Linux. Individual systems may have narrower live-recording availability while retaining portable stable models and replay behavior.
See LICENSE.