Testing with cabin test
cabin test is a small wrapper around the existing build
pipeline: it builds the selected test targets, runs each
linked executable in deterministic order, and reports a summary.
It is intentionally not a testing framework. It does no test- case discovery inside binaries and parses no framework-specific output. The unit of execution is the entire test executable — its exit status decides pass / fail.
Declaring a test target
[target.demo_test]
type = "test"
sources = ["tests/lib_test.cc"]
deps = ["demo"]
The full test syntax is documented in
docs/targets.md.
Running tests
cabin test # every test in the default selection
cabin test --workspace # every test in every workspace member
cabin test -p demo # only demo's tests
cabin test --release # compile with the release profile
cabin test --features simd # forward features to the test build
cabin test does not offer a single-test selector flag.
Narrow the run by narrowing the package selection (--package
/ --workspace / --exclude).
cabin test shares its core flags with cabin build:
--profile, --release, --features, --no-default-features,
--all-features, --locked, --frozen, --no-patches, the
toolchain
overrides (--cc / --cxx / --ar / --compiler-wrapper),
the workspace-selection bundle (--workspace / -p /
--default-members / --exclude), and the index/cache
locations (--index-path / --index-url / --cache-dir).
--allow-no-tests opts the user out of the empty-selection
error: by default, cabin test exits with an error when the
selected packages declare no test target, so CI does not
silently pass when tests have not been added yet. Pass
--allow-no-tests for cases where empty is expected.
Output and exit status
For each test executable Cabin prints:
running test <pkg>:<target>
... (the executable's stdout, prefixed by a "stdout:" header)
... (the executable's stderr, prefixed by a "stderr:" header)
test <pkg>:<target> ... ok
A failed test exits non-zero; Cabin records the exit code and writes:
test <pkg>:<target> ... FAILED (exit 17)
If any test fails, cabin test itself exits non-zero and
writes the rendered test summary to stdout, followed by the
top-level error on stderr:
test result: FAILED. P passed; F failed (of N)
error: test failures: F of N test executables failed
A test killed by a signal renders as FAILED (terminated by signal).
Working directory and environment
Each test executable runs with its working directory set to the owning package’s manifest directory. Tests that read fixture data from paths relative to the package root therefore see the same files in CI and on a developer’s machine.
The environment the test inherits is the same as cabin test’s
own — Cabin does not set test-framework-specific variables. If
you need a particular variable, set it in the parent process.
Determinism
Test ordering is (package_name, target_name) ascending,
regardless of the order targets appear in cabin.toml. The
runner is sequential: each test runs to completion before the
next starts.
Dev-dependencies
cabin test activates [dev-dependencies] for the selected
packages as real graph edges. A test target may therefore depend
on packages declared only under [dev-dependencies]:
[dependencies]
demo = { path = "../demo" }
[dev-dependencies]
gtest = "^1.14"
[target.demo_test]
type = "test"
sources = ["tests/lib_test.cc"]
deps = ["demo", "gtest"]
cabin build continues to ignore [dev-dependencies], so
ordinary production builds remain unaffected. Dev-dep
activation never propagates: a transitive package’s own
[dev-dependencies] stay declaration-only even under
cabin test.
Lockfile behavior
--locked and --frozen apply to cabin test exactly as they
do to cabin build:
--lockedrequires the lockfile to satisfy the resolver’s pick (and the patch / source-replacement state to match);--frozenadditionally forbids state-writing side effects (cache population, lockfile mutation).
Because cabin test includes [dev-dependencies] in
resolution, projects that have never run cabin test may need
one un---locked invocation to add dev-deps to the lockfile.
What cabin test is not
- It does not run examples.
exampletargets are not selectable from the command line — they reach the build graph only as transitive deps of another selected target. - It does not parse GoogleTest / Catch2 / doctest output, nor emit XML / JUnit reports.
- It does not provide test filtering inside an executable, and
there is no single-test selector flag; narrow the run by
narrowing the package selection (
-p <package>/--workspace/--exclude).
Contributing tests to Cabin
The rules for writing Cabin’s own test suite — environment
isolation via the shared cabin() helper, tool-availability
gating, host-path and driver-name conventions, and the CI
portability boundary — are contributor guidance, not part of
the cabin test user surface. They live in
AGENTS.md.