Crafter.Build/examples/tests/README.md
Jorijn van der Graaf 603840879d
All checks were successful
CI / build-test-release (push) Successful in 1h4m52s
new tests
2026-05-27 19:45:05 +02:00

3.1 KiB

tests

Tests are declared in project.cpp via cfg.AddTest(name). One line per test; deps and other options chain off the returned builder.

cd examples/tests
crafter-build test

Layout:

mylib/MyMath.cppm                  # the library being tested
project.cpp                        # declares the library and its tests

tests/Smoke/main.cpp               # zero-config test
tests/UnitMyMath/main.cpp          # test that links MyMath

project.cpp does:

cfg.AddTest("Smoke");                                  // exit 0 = pass
cfg.AddTest("UnitMyMath").Dependencies({ &cfg });      // imports MyMath

AddTest(name) defaults the source to tests/<name>/main.cpp (resolved at the project root) and the target/march/mtune to the parent project's. The builder methods overlay overrides:

  • .Target(triple) / .March(...) / .Mtune(...) / .Sysroot(path)
  • .Define(name, value) / .Debug()
  • .Timeout(seconds)
  • .Args(vec) — runtime args
  • .Requires("tool:foo") / .Requires("file:/path") / .Requires("env:VAR")
  • .Dependencies({ &otherCfg, ... })
  • .Path(p) — rebase the test's source-resolution path
  • .LinkFlag(s) / .CompileFlag(s)

Test conventions

  • Exit code 0 = pass, anything nonzero = fail, 77 = skipped (autoconf convention). Use std::exit(77) for runtime skips like "tool not on PATH".
  • Each test runs in its own subprocess; a segfault doesn't take down the runner.
  • Default timeout is 60 s (crafter-build test --timeout=N overrides).
  • Filter by name: crafter-build test 'Unit*'. List without running: crafter-build test --list.

Linking the parent project

UnitMyMath depends on the library via .Dependencies({ &cfg }) — the test imports MyMath and the build engine links cfg's output into the test exe.

Cross-target test runs

AddTest inherits the parent project's target; for per-test cross-arch runs, override via the builder:

cfg.AddTest("CrossArchAarch64")
   .Target("aarch64-linux-gnu")
   .Sysroot("/opt/aarch64-rootfs")
   .Requires("tool:qemu-aarch64");

crafter-build test sweeps every distinct target declared across the project's tests plus the host triple, so cross-arch tests run by default. --target=<triple> restricts the run to that target.

--runner=<spec> overrides the per-target runner for one invocation. Useful specs: local, cmd:<command> (e.g. cmd:wine, cmd:qemu-aarch64). Persistent override via env: CRAFTER_BUILD_RUNNER_<normalized_target>=<spec>.

SIMD march fan-out

For libraries with per-march SIMD codegen (e.g. Crafter.Math), use AddMarchVariants to produce one Test per tier sharing the same source and interface set:

constexpr MarchTier tiers[] = {
    {"sapphirerapids", "native"},
    {"x86-64-v4",      "generic"},
    {"x86-64-v3",      "generic"},
};
cfg.AddMarchVariants("Vector", libInterfaces, tiers);

Each tier becomes a Test named Vector-<march>, with the library's interfaces rebuilt under that tier's -march/-mtune (so codegen actually varies). Per-arch gating is just an if (target.starts_with("x86_64")) guard around the call — no special toml syntax.