tests/
Test suites for the boot2 chain. The top-level Makefile includes
tests/Makefile, so make test from the repo root resolves here. You
can also make -C tests test-…; that delegates back to the parent.
Layout
tests/
├── Makefile # suite + build-dep rules (included from top-level)
├── run.sh # host-side dispatcher (per-arch podman, lint preflight)
├── run-suite.sh # in-container suite driver (one invocation per arch)
├── lib-runner.sh # shared helpers sourced by run-suite.sh
├── build-p1.sh # in-container .P1 -> ELF (M0 + hex2 chain)
├── build-p1pp.sh # in-container .P1pp -> ELF (self-hosted M1pp/hex2pp)
├── build-cc.sh # in-container .c -> .P1pp (scheme1 + cc.scm)
├── seed-accept.sh # seed-driver acceptance (kernel | boot34 | boot5)
├── M1pp/ # macro-expander parity tests
├── P1/ # P1pp pipeline + standalone .P1 tests
├── scheme1/ # scheme1 interpreter tests
├── cc-util/ # cc.scm prelude + util byte-diff
├── cc-lex/ # cc.scm lex pipeline byte-diff
├── cc-pp/ # cc.scm pp pipeline byte-diff
├── cc-cg/ # cc.scm cg emit -> P1pp -> ELF -> run
├── cc/ # full cc.scm: .c -> P1pp -> ELF -> run
├── cc-libc/ # cc + mes-libc link -> ELF -> run
└── simple-patches/ # patch-application tests
vendor/c-testsuite/single-exec/ (outside tests/) is the input set
for the cc-ext suite.
Running
make test # all suites, default ARCH
make test SUITE=cc # one suite
make test SUITE=cc NAMES='001 042' # filter by fixture-name prefix
make test SUITE=cc-libc ARCH=amd64 # one arch
make test SUITE=tcc-cc ARCH=amd64 STAGE=2 # tcc-built test runners
make image builds the per-arch boot2-busybox-test container used by all
podman-driven suites.
Per-suite contract
Every suite picks fixtures by <name>.<input-ext> under its directory
and compares against <name>.expected (for the suite's primary output)
and/or <name>.expected-exit (for the runtime exit code). Missing
expected files default to empty stdout / exit 0.
Names start with a 3-digit prefix (NNN-) so listings stay stable as
suites grow.
| Suite | Fixture | Goldens | Pipeline |
|---|---|---|---|
m1pp |
<name>.M1pp |
.expected |
M1pp <fixture> > out; diff out vs .expected |
p1 |
<name>.P1pp |
.expected |
build-p1pp.sh → ELF → run; diff stdout |
scheme1 |
<name>.scm |
.expected, .expected-exit |
scheme1 <fixture>; diff stdout + exit |
cc-util |
<name>.scm |
.expected, .expected-exit |
catm prelude+cc.scm+<fixture> → scheme1; diff stdout + exit |
cc-lex |
<name>.c |
.expected, .expected-exit |
lex pipeline; diff stdout (;; lines stripped) |
cc-pp |
<name>.c/.scm |
.expected, .expected-exit |
pp pipeline (.c) or unit (.scm); diff stdout |
cc-cg |
<name>.scm |
.expected, .expected-exit |
cg emit → build-p1pp.sh → ELF → run; diff stdout + exit |
cc |
<name>.c |
.expected, .expected-exit |
cc.scm → build-p1pp.sh → ELF → run; diff stdout + exit |
cc-libc |
<name>.c |
.expected, .expected-exit |
cc.scm (lib mode) + entry-libc + libc.P1pp → ELF; diff |
cc-ext |
<name>.c |
<name>.c.expected, <name>.c.tags |
upstream c-testsuite fixtures; needs-libc tag forces libc link |
tcc-cc |
tests/cc/... |
(reuses cc/) | self-built tcc → ELF → run; STAGE=2 (tcc-tcc) or 3 (tcc-tcc-tcc) |
tcc-libc |
tests/cc-libc/... |
(reuses cc-libc/) | self-built tcc + per-arch start/sys_stubs/libc.o → ELF → run |
The cc-pp row covers two fixture passes inside one suite: .c
fixtures go through the lex+pp pipeline; the lone .scm fixture
(022-initial-defines.scm at time of writing) drives the unit-suite
path because it exercises -D initial-define plumbing the .c fixture
driver doesn't expose.
Exit semantics
- A suite returns 0 only if every fixture PASSed.
- A fixture FAILs if any sub-step (compile, assemble, run) errors, or if stdout or exit doesn't match the golden.
- Compile/assemble failures dump the relevant
.logfrombuild/<arch> /.work/tests/<suite>/<name>/for triage; the binary itself is not retained when assembly fails.
Adding a fixture
- Pick the lowest unused
NNN-index in the target suite. - Drop
<NNN>-<name>.<input-ext>plus.expected(and.expected-exitif the program is supposed to exit non-zero). - Run
make test SUITE=<suite> NAMES=<NNN>-<name>to verify.
seed-driver acceptance
tests/seed-accept.sh is separate from make test. It runs the
seed-kernel under QEMU and checks byte-equivalence against podman-built
artifacts:
tests/seed-accept.sh # full Tier-2 (default mode=kernel)
tests/seed-accept.sh boot34 # boot3/4 byte-eq vs podman
WITH_BOOT4=1 tests/seed-accept.sh boot34
tests/seed-accept.sh boot5 # boot5 byte-eq vs podman
ARCH is fixed to aarch64 since that's the only seed-driver-complete
arch today. Prereq for every mode: ./boot/boot.sh aarch64 has run
under the default DRIVER=podman so build/aarch64/podman/boot{0..6}/
is populated.