asm-test¶
A C-hosted unit-testing framework for assembly language.
Write assembly routines, call them from C test cases through the real ABI, and assert on the results — return values, CPU registers, flags, and memory. Tests are auto-discovered, run in isolation, and reported TAP-style with a nonzero exit on failure.
#include "asmtest.h"
extern long add_signed(long a, long b); // routine under test (in add.s)
TEST(arith, adds_two_numbers) {
ASSERT_EQ(add_signed(2, 3), 5);
}
TEST(arith, preserves_callee_saved_and_clears_carry) {
regs_t r;
ASM_CALL2(&r, add_signed, 2, 3);
ASSERT_EQ(r.ret, 5);
ASSERT_ABI_PRESERVED(&r); // rbx, rbp, r12–r15 restored
ASSERT_FLAG_CLEAR(&r, CF);
}
The framework provides main(), discovers every TEST(...), runs each one in a
forked child with a timeout, and prints a colored summary.
How it fits together¶
You write the assembly routines and the C tests; the Makefile assembles, compiles, and links them into one binary per suite; the framework’s runner discovers the tests and drives each routine through the real calling convention — either natively (a capture trampoline on the real CPU) or, opting in, inside a virtual CPU (the emulator tier) — then asserts on the result and reports.
Diagram: Framework build and run pipeline
Why asm-test¶
Existing tools either run a whole binary per case and only see exit status and stdout, or assert from inside the assembly itself. asm-test fills the open niche: a C-hosted framework that calls assembly through the real calling convention and inspects registers, flags, and memory afterward — with proper discovery and reporting. See Design & background for the prior-art comparison and roadmap.
What you get¶
Auto-discovered
TEST(...)cases, a provided runner, per-suiteSETUP/TEARDOWN,SKIP(reason), and colored TAP output.A full assertion library — signed/unsigned integer comparisons, strings, memory (with a hexdump diff), floating-point (ULP-aware), and SIMD lanes.
Register, flag, and ABI-preservation capture through a real call, plus the full System V call model: arbitrary arity, struct returns, struct-by-value, floating-point, and 128-bit vectors.
Differential / property testing against a C reference model over fuzzed inputs, with reproducible seeds.
A robust runner: per-test
fork()isolation, timeouts, crash/hang containment, filtering, shuffling, parallelism, and JUnit output.Benchmark mode reporting cycles per call.
An optional emulator tier (Unicorn) that runs a routine inside a virtual CPU — x86-64, AArch64, RISC-V, ARM32, and the Windows x64 ABI — to read the full register file, catch precise faults, and record execution traces and coverage.
Portability across x86-64, AArch64, and RISC-V (rv64), Linux and macOS, with GAS and NASM assembler backends.
Where to start¶
New here? Read Installation then the Quick start.
Writing your first suite? See Writing tests and the Assertion reference.
Want to see it in action by use case? See Examples — correctness & ABI capture, differential testing, benchmarking, and crash/hang containment.
Want the full capability list and what’s available across architectures, OSes, and languages? See Features & support matrix.
Wondering where something lives in the repository — and which page documents it? See the Code map, a mind map of the tree with every part linked to its docs.
Weighing this against cmocka/Criterion +
.s, raw Unicorn scripting, or qemu+gdb? See asm-test vs. alternatives.Consuming the framework from another project? See Using asm-test in your project, and Using asm-test in your CI for the GitHub Action / GitLab template.
Teaching an assembly course? See Teaching with asm-test for the assignment layout and the GitHub Classroom autograding recipe.
Driving it from another language? See Language bindings for the shared overview, then the per-language page (Python, .NET, Go, Rust, C++, Zig, Node, Java, Ruby, Lua).