kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit e9bbba2b0ac78b989cca2d0bf6ffd66d12a5221a
parent 6557073b7d3c01a99639b5ec31d102bbf0fa77b6
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Sat,  9 May 2026 11:17:08 -0700

test-ar-driver

Diffstat:
Atest/ar/cases/01-verbose-create-list-extract.expected | 15+++++++++++++++
Atest/ar/cases/01-verbose-create-list-extract.sh | 15+++++++++++++++
Atest/ar/cases/02-replace-preserves-others.actual | 13+++++++++++++
Atest/ar/cases/02-replace-preserves-others.expected | 12++++++++++++
Atest/ar/cases/02-replace-preserves-others.sh | 26++++++++++++++++++++++++++
Atest/ar/cases/03-print-header-order.expected | 13+++++++++++++
Atest/ar/cases/03-print-header-order.sh | 24++++++++++++++++++++++++
Atest/ar/run.sh | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/test.mk | 10++++++++--
9 files changed, 205 insertions(+), 2 deletions(-)

diff --git a/test/ar/cases/01-verbose-create-list-extract.expected b/test/ar/cases/01-verbose-create-list-extract.expected @@ -0,0 +1,15 @@ +== cv == +a - a.o +a - b.o +a - c.o +== tv == +4 a.o +8 b.o +12 c.o +== xv == +x - a.o +x - b.o +x - c.o +a.o +b.o +c.o diff --git a/test/ar/cases/01-verbose-create-list-extract.sh b/test/ar/cases/01-verbose-create-list-extract.sh @@ -0,0 +1,15 @@ +# cv → tv → xv: covers verbose write, verbose list, verbose extract. +printf 'aaaa' > a.o +printf 'bbbbbbbb' > b.o +printf 'cccccccccccc' > c.o + +echo "== cv ==" +"$CFREE" ar cv lib.a a.o b.o c.o + +echo "== tv ==" +"$CFREE" ar tv lib.a + +echo "== xv ==" +mkdir extracted +( cd extracted && "$CFREE" ar xv ../lib.a ) +ls extracted | sort diff --git a/test/ar/cases/02-replace-preserves-others.actual b/test/ar/cases/02-replace-preserves-others.actual @@ -0,0 +1,13 @@ +== rv (replace b, add c) == +r - b.o +a - c.o +== tv after rv == +6 b.o +8 b.o +12 c.o +== tv after second r == +6 c.o +8 c.o +12 c.o +2 a.o +2 d.o diff --git a/test/ar/cases/02-replace-preserves-others.expected b/test/ar/cases/02-replace-preserves-others.expected @@ -0,0 +1,12 @@ +== rv (replace b, add c) == +r - b.o +a - c.o +== tv after rv == +4 a.o +6 b.o +12 c.o +== tv after second r == +2 a.o +6 b.o +12 c.o +2 d.o diff --git a/test/ar/cases/02-replace-preserves-others.sh b/test/ar/cases/02-replace-preserves-others.sh @@ -0,0 +1,26 @@ +# Regression: `r` must preserve unlisted members in order, replace +# matching basenames in place, and append truly-new members. Each +# old member's name comes from the iterator's single shared name +# buffer, so the driver must copy it into stable storage before the +# next iter step (otherwise old members collapse to one name). + +printf 'aaaa' > a.o +printf 'bbbbbbbb' > b.o +"$CFREE" ar c lib.a a.o b.o + +# Replace b.o (shorter content) and add c.o. +printf 'BBBBBB' > b.o +printf 'cccccccccccc' > c.o +echo "== rv (replace b, add c) ==" +"$CFREE" ar rv lib.a b.o c.o + +echo "== tv after rv ==" +"$CFREE" ar tv lib.a + +# Add a member that already exists alongside one that doesn't, in +# r mode (no v) — exercises the same code path silently. +printf 'AA' > a.o +printf 'dd' > d.o +"$CFREE" ar r lib.a a.o d.o +echo "== tv after second r ==" +"$CFREE" ar tv lib.a diff --git a/test/ar/cases/03-print-header-order.expected b/test/ar/cases/03-print-header-order.expected @@ -0,0 +1,13 @@ +== p single (no header) == +aaaa +== pv single (header) == +lib.a(a.o): +aaaa +== p multi (header) == +lib.a(a.o): +aaaalib.a(c.o): +cccccccccccc +== pv multi (header) == +lib.a(a.o): +aaaalib.a(c.o): +cccccccccccc diff --git a/test/ar/cases/03-print-header-order.sh b/test/ar/cases/03-print-header-order.sh @@ -0,0 +1,24 @@ +# Regression: `pv` (and `p` with multiple members) prints a per-member +# header via driver_printf and the payload via the stdout writer. Both +# must land in the right order — driver_stdout_writer routes through +# stdio so it shares libc's buffer with printf. + +printf 'aaaa' > a.o +printf 'cccccccccccc' > c.o +"$CFREE" ar c lib.a a.o c.o + +echo "== p single (no header) ==" +"$CFREE" ar p lib.a a.o +echo + +echo "== pv single (header) ==" +"$CFREE" ar pv lib.a a.o +echo + +echo "== p multi (header) ==" +"$CFREE" ar p lib.a a.o c.o +echo + +echo "== pv multi (header) ==" +"$CFREE" ar pv lib.a a.o c.o +echo diff --git a/test/ar/run.sh b/test/ar/run.sh @@ -0,0 +1,79 @@ +#!/bin/sh +# Driver-level `cfree ar` test harness. +# +# Each test/ar/cases/*.sh is a scenario script. The harness allocates a +# fresh sandbox dir per case, cd's in, sources the script with $CFREE +# exported, captures stdout, and diffs against the matching .expected. +# Stderr is forwarded straight through so write/read failures still +# surface. +# +# Honors $CFREE for the binary path; defaults to build/cfree relative +# to the repo root. + +set -u + +script_dir=$(cd "$(dirname "$0")" && pwd) +repo_root=$(cd "$script_dir/../.." && pwd) +cases_dir="$script_dir/cases" + +CFREE="${CFREE:-$repo_root/build/cfree}" +export CFREE + +if [ ! -x "$CFREE" ]; then + echo "ar-driver: cfree binary not found at $CFREE" >&2 + exit 2 +fi + +work_root=$(mktemp -d "${TMPDIR:-/tmp}/cfree-ar-test.XXXXXX") +trap 'rm -rf "$work_root"' EXIT + +pass=0 +fail=0 +failures= + +for sh in "$cases_dir"/*.sh; do + [ -e "$sh" ] || continue + name=$(basename "${sh%.sh}") + expected="${sh%.sh}.expected" + actual="$work_root/$name.actual" + + if [ ! -e "$expected" ]; then + printf 'FAIL %s (missing %s)\n' "$name" "$(basename "$expected")" + fail=$((fail + 1)) + failures="$failures $name" + continue + fi + + sandbox="$work_root/$name" + mkdir -p "$sandbox" + ( cd "$sandbox" && sh "$sh" ) > "$actual" 2>&1 + case_rc=$? + + if [ "$case_rc" -ne 0 ]; then + printf 'FAIL %s (script exit=%d)\n' "$name" "$case_rc" + diff -u "$expected" "$actual" || true + fail=$((fail + 1)) + failures="$failures $name" + continue + fi + + if diff -u "$expected" "$actual" >/dev/null 2>&1; then + printf 'PASS %s\n' "$name" + pass=$((pass + 1)) + else + printf 'FAIL %s\n' "$name" + diff -u "$expected" "$actual" || true + # Keep .actual on failure for debugging by copying out of the + # sandbox before it's cleaned up. + cp "$actual" "$cases_dir/$name.actual" 2>/dev/null || true + fail=$((fail + 1)) + failures="$failures $name" + fi +done + +total=$((pass + fail)) +printf '\nar-driver: %d/%d passed\n' "$pass" "$total" +if [ "$fail" -gt 0 ]; then + printf 'ar-driver: failures:%s\n' "$failures" + exit 1 +fi diff --git a/test/test.mk b/test/test.mk @@ -9,6 +9,9 @@ # libcfree.a. Set CFREE_AR_TEST_HOST=1 to also dump produced bytes # to /tmp and run the host's `ar t` / `nm --print-armap` as a # cross-check. +# - test-ar-driver: scenario-driven CLI harness for `cfree ar`. Each +# case under test/ar/cases/ runs a small script and diffs stdout. +# Depends on the cfree driver binary. # - test-link: linker + JIT behavioral harness in test/link/; three paths # per case (roundtrip R, ELF exec E, JIT J). Depends only on libcfree.a. # Set CFREE_TEST_ALLOW_SKIP=1 to allow skipped layers. @@ -16,9 +19,9 @@ # four paths per case (D direct-JIT, R roundtrip, E exec, J jit-via-file). # Depends only on libcfree.a; reuses test/link harness binaries. -.PHONY: test test-lex test-pp test-pp-err test-elf test-ar test-link test-cg test-lib-deps +.PHONY: test test-lex test-pp test-pp-err test-elf test-ar test-ar-driver test-link test-cg test-lib-deps -test: test-lex test-pp test-pp-err test-elf test-ar test-link test-cg test-lib-deps +test: test-lex test-pp test-pp-err test-elf test-ar test-ar-driver test-link test-cg test-lib-deps test-lex: bin @CFREE=$(abspath $(BIN)) test/lex/run.sh @@ -46,6 +49,9 @@ $(AR_TEST_BIN): test/ar_test.c $(LIB_AR) @mkdir -p $(dir $@) $(CC) $(DRIVER_CFLAGS) test/ar_test.c $(LIB_AR) -o $@ +test-ar-driver: bin + @CFREE=$(abspath $(BIN)) test/ar/run.sh + # Test harness binaries shared by test-elf, test-link, and test-cg. # Declared as Make targets (not built by the run.sh scripts) so they pick # up libcfree.a changes deterministically.