#!/bin/sh set -eu LC_ALL=C export LC_ALL repo=$(CDPATH= cd -- "$(dirname -- "$0")/../.." && pwd) tool=$repo/out/bin/wwfixture work=$(mktemp -d "${TMPDIR:-/tmp}/wwfixture-test.XXXXXXXX") coordinator= cleanup() { if [ -n "$coordinator" ]; then kill -KILL "$coordinator" 2> /dev/null || : wait "$coordinator" 2> /dev/null || : fi for stream in "$work"/*.tab; do [ -f "$stream" ] || continue awk -F '\t' '$1 == "case" && $10 ~ /^\/tmp\/[0-9a-f]{16}\/[0-9]{4}$/ { p=$10; sub("/[0-9]{4}$", "", p); print p }' "$stream" done | sort -u | while IFS= read -r run; do if [ -d "$run" ]; then find "$run" -depth -delete fi done find "$work" -depth -delete } trap cleanup EXIT HUP INT TERM fail() { echo "test/wwfixture: $*" >&2 exit 1 } clone_corpus() { dest=$1 mkdir -p "$dest/test/wcc/data" for source in "$repo"/test/wcc/data/*/case.ww; do name=${source%/case.ww} name=${name##*/} mkdir "$dest/test/wcc/data/$name" cp "$source" "$dest/test/wcc/data/$name/case.ww" done ln -s "$repo/out" "$dest/out" ln -s "$repo/lib" "$dest/lib" } cleanup_capture() { stream=$1 capture=$(awk -F '\t' '$1 == "case" && $10 != "-" { print $10; exit }' \ "$stream") [ -n "$capture" ] || return 0 run=${capture%/*} id=${run#/tmp/} [ "$run" != "$id" ] || fail "capture outside /tmp" [ "${#id}" -eq 16 ] || fail "malformed capture run ID" case $id in *[!0-9a-f]*) fail "unsafe capture run ID" ;; esac [ -d "$run" ] || fail "missing retained capture" find "$run" -depth -delete } cleanup_run() { run=$1 id=${run#/tmp/} [ "$run" != "$id" ] || fail "retained run outside /tmp" [ "${#id}" -eq 16 ] || fail "malformed retained run ID" case $id in *[!0-9a-f]*) fail "unsafe retained run ID" ;; esac [ -d "$run" ] || fail "missing retained run" find "$run" -depth -delete } replace_directive() { path=$1 line=$2 tmp=$path.new { printf '%s\n' "$line" sed -n '2,$p' "$path" } > "$tmp" mv "$tmp" "$path" } expect_invalid() { root=$1 pattern=$2 stream=$3 if "$tool" validate -root "$root" -run "$pattern" "$stream" \ > /dev/null 2>&1; then fail "validator accepted $(basename -- "$stream")" fi } base=$work/base clone_corpus "$base" # Filtering and listing publish identities only; no synthetic SKIP records. list=$work/list.tab "$tool" -root "$base" -run 'compiler.runww_dup_main_reject/c' -list > "$list" [ "$(wc -l < "$list")" -eq 1 ] || fail "filtered list count" [ "$(sed -n '1p' "$list")" = "compiler.runww_dup_main_reject c" ] \ || fail "filtered list identity" # Real positive fixtures must build successfully before their programs run. runstream=$work/run-pass.tab "$tool" -root "$base" -run 'compiler.idx_dot_aggret_subtail_run/c' \ -j 1 > "$runstream" "$tool" validate -root "$base" \ -run 'compiler.idx_dot_aggret_subtail_run/c' "$runstream" awk -F '\t' 'NR == 2 && $5 == "run" && $6 == "pass" && \ $7 == "exit" && $8 == "0" && $10 == "-" { ok=1 } \ END { exit !ok }' "$runstream" || fail "run fixture terminal row" runexitstream=$work/run-exit-pass.tab "$tool" -root "$base" -run 'compiler.runww_enum_run/c' -j 1 \ > "$runexitstream" "$tool" validate -root "$base" -run 'compiler.runww_enum_run/c' \ "$runexitstream" awk -F '\t' 'NR == 2 && $5 == "run" && $6 == "pass" && \ $7 == "exit" && $8 == "6" && $10 == "-" { ok=1 } \ END { exit !ok }' "$runexitstream" || fail "run-exit fixture terminal row" # Compile fixtures stop at the frontend and publish one terminal row per stage. compilepattern=compiler.r78_strict_package_main_accept compilestream=$work/compile-pass.tab "$tool" -root "$base" -run "$compilepattern" -j 1 > "$compilestream" "$tool" validate -root "$base" -run "$compilepattern" "$compilestream" awk -F '\t' 'NR == 1 && $7 == "2" { h=1 } \ $1 == "case" && $4 == "c" && $5 == "compile" && $6 == "pass" && \ $7 == "exit" && $8 == "0" && $10 == "-" { c=1 } \ $1 == "case" && $4 == "ww" && $5 == "compile" && $6 == "pass" && \ $7 == "exit" && $8 == "0" && $10 == "-" { w=1 } \ END { exit !(h && c && w) }' "$compilestream" \ || fail "compile fixture terminal rows" # A compile directive cannot turn a frontend rejection into a pass. compilefailroot=$work/compile-failure cp -R "$base" "$compilefailroot" compilecase=$compilefailroot/test/wcc/data/r78_strict_package_main_accept/case.ww printf '%s\n' '//ww:compile' 'package main;' 'fn main( {' > "$compilecase" compilefailstream=$work/compile-failure.tab if "$tool" -root "$compilefailroot" -run "$compilepattern/c" -j 1 \ > "$compilefailstream"; then fail "compile rejection satisfied compile directive" fi "$tool" validate -root "$compilefailroot" -run "$compilepattern/c" \ "$compilefailstream" awk -F '\t' 'NR == 2 && $5 == "compile" && $6 == "fail" && \ $7 == "exit" && $8 != "0" && $10 != "-" { ok=1 } \ END { exit !ok }' "$compilefailstream" \ || fail "compile failure terminal row" cleanup_capture "$compilefailstream" # A build exit equal to the requested runtime exit is still a build failure. buildroot=$work/build-failure cp -R "$base" "$buildroot" buildpattern=compiler.r816_one_elem buildcase=$buildroot/test/wcc/data/r816_one_elem/case.ww printf '%s\n' '//ww:run-exit 1' 'package main;' \ 'fn main( {' > "$buildcase" buildstream=$work/build-failure.tab if "$tool" -root "$buildroot" \ -run "$buildpattern/c" -j 1 > "$buildstream"; then fail "build failure satisfied runtime expectation" fi "$tool" validate -root "$buildroot" \ -run "$buildpattern/c" "$buildstream" awk -F '\t' 'NR == 2 && $5 == "build" && $6 == "fail" && \ $7 == "exit" && $8 == "1" { ok=1 } END { exit !ok }' "$buildstream" \ || fail "build failure terminal row" cleanup_capture "$buildstream" # A normal rejecting compile without the required stderr fragment fails. diagroot=$work/diagnostic-mismatch cp -R "$base" "$diagroot" replace_directive "$diagroot/test/wcc/data/runww_dup_main_reject/case.ww" \ '//ww:error "fragment that is deliberately absent"' diagstream=$work/diagnostic-mismatch.tab if "$tool" -root "$diagroot" -run 'compiler.runww_dup_main_reject/c' \ -j 1 > "$diagstream"; then fail "diagnostic mismatch passed" fi "$tool" validate -root "$diagroot" -run 'compiler.runww_dup_main_reject/c' \ "$diagstream" awk -F '\t' 'NR == 2 && $5 == "compile" && $6 == "fail" && \ $7 == "exit" && $8 != "0" && $10 != "-" { ok=1 } \ END { exit !ok }' "$diagstream" || fail "diagnostic mismatch terminal row" cleanup_capture "$diagstream" # A labeled error directive selects the exact diagnostic for each stage. stagediagpattern=compiler.r828_arrlit_ret_overlong stagediagstream=$work/stage-diagnostic-pass.tab "$tool" -root "$base" -run "$stagediagpattern" -j 1 > "$stagediagstream" "$tool" validate -root "$base" -run "$stagediagpattern" "$stagediagstream" awk -F '\t' '$1 == "case" && $4 == "c" && $5 == "compile" && \ $6 == "pass" && $7 == "exit" && $8 != "0" && $10 == "-" { c=1 } \ $1 == "case" && $4 == "ww" && $5 == "compile" && \ $6 == "pass" && $7 == "exit" && $8 != "0" && $10 == "-" { w=1 } \ END { exit !(c && w) }' "$stagediagstream" \ || fail "stage-specific diagnostic terminal rows" # Swapping the labeled fragments must fail both cells. This catches either # fragment being shared across stages or treated as an unordered alternative. stageswaproot=$work/stage-diagnostic-swapped cp -R "$base" "$stageswaproot" replace_directive \ "$stageswaproot/test/wcc/data/r828_arrlit_ret_overlong/case.ww" \ '//ww:error c "array literal has 3 elements but declared array holds 2" ww "return [3]int not assignable to [2]int"' stageswapstream=$work/stage-diagnostic-swapped.tab if "$tool" -root "$stageswaproot" -run "$stagediagpattern" -j 1 \ > "$stageswapstream"; then fail "swapped stage-specific diagnostics passed" fi "$tool" validate -root "$stageswaproot" \ -run "$stagediagpattern" "$stageswapstream" awk -F '\t' '$1 == "case" && $4 == "c" && $5 == "compile" && \ $6 == "fail" && $7 == "exit" && $8 != "0" && $10 != "-" { c=1 } \ $1 == "case" && $4 == "ww" && $5 == "compile" && \ $6 == "fail" && $7 == "exit" && $8 != "0" && $10 != "-" { w=1 } \ END { exit !(c && w) }' "$stageswapstream" \ || fail "stage-specific diagnostics were shared across stages" cleanup_capture "$stageswapstream" # Signals remain signals and can never satisfy a numeric runtime expectation. signalroot=$work/signal cp -R "$base" "$signalroot" signalcase=$signalroot/test/wcc/data/idx_dot_aggret_subtail_run/case.ww printf '%s\n' '//ww:run' 'package main;' 'import os;' \ 'export fn main() int = {' \ ' os.kill(os.getpid(), os.SIGKILL);' ' return 0;' '};' > "$signalcase" signalstream=$work/signal.tab if "$tool" -root "$signalroot" -run 'compiler.idx_dot_aggret_subtail_run/c' \ -j 1 > "$signalstream"; then fail "signaled fixture passed" fi "$tool" validate -root "$signalroot" \ -run 'compiler.idx_dot_aggret_subtail_run/c' "$signalstream" awk -F '\t' 'NR == 2 && $5 == "run" && $6 == "fail" && \ $7 == "signal" && $8 == "9" { ok=1 } END { exit !ok }' "$signalstream" \ || fail "signal terminal row" cleanup_capture "$signalstream" # One total deadline covers build and run; the busy program is terminated. timeoutroot=$work/timeout cp -R "$base" "$timeoutroot" timeoutcase=$timeoutroot/test/wcc/data/idx_dot_aggret_subtail_run/case.ww printf '%s\n' '//ww:run' 'package main;' 'export fn main() int = {' \ ' for (true) { };' ' return 0;' '};' > "$timeoutcase" timeoutstream=$work/timeout.tab if "$tool" -root "$timeoutroot" -run 'compiler.idx_dot_aggret_subtail_run/c' \ -j 1 -timeout-ms 250 > "$timeoutstream"; then fail "timed out fixture passed" fi "$tool" validate -root "$timeoutroot" \ -run 'compiler.idx_dot_aggret_subtail_run/c' "$timeoutstream" awk -F '\t' 'NR == 2 && $6 == "fail" && $7 == "timeout" && \ $8 == "0" && $9 >= 200000000 { ok=1 } END { exit !ok }' "$timeoutstream" \ || fail "timeout terminal row" cleanup_capture "$timeoutstream" # The real coordinator consumes SIGTERM, owns active cleanup, and still emits # exactly one terminal record for the active cell and the queued cell. interruptstream=$work/interrupted.tab "$tool" -root "$timeoutroot" -run 'compiler.idx_dot_aggret_subtail_run' \ -j 1 -timeout-ms 10000 > "$interruptstream" & coordinator=$! program= tries=0 while [ "$tries" -lt 300 ]; do if [ -r "/proc/$coordinator/task/$coordinator/children" ]; then for child in $(sed -n '1p' "/proc/$coordinator/task/$coordinator/children"); do exe=$(readlink "/proc/$child/exe" 2> /dev/null || :) case $exe in /tmp/*/program) program=$child ;; esac done fi [ -z "$program" ] || break tries=$((tries + 1)) sleep 0.01 done [ -n "$program" ] || fail "coordinator runtime child did not start" kill -TERM "$coordinator" if wait "$coordinator"; then fail "interrupted coordinator returned success" fi coordinator= [ ! -e "/proc/$program" ] || fail "interrupted leader was not reaped" "$tool" validate -root "$timeoutroot" \ -run 'compiler.idx_dot_aggret_subtail_run' "$interruptstream" awk -F '\t' 'NR == 1 && $7 == "2" { h=1 } \ NR == 2 && $2 == "1" && $5 == "run" && $6 == "error" && \ $7 == "signal" && $8 == "15" { a=1 } \ NR == 3 && $2 == "2" && $5 == "build" && $6 == "error" && \ $7 == "setup" && $8 == "-4" && $9 == "0" { q=1 } \ NR == 4 && $1 == "end" && $2 == "2" && $3 == "2" { e=1 } \ END { exit !(h && a && q && e) }' "$interruptstream" \ || fail "interrupted coordinator terminal accounting" cleanup_capture "$interruptstream" # Directive parsing and corpus pinning happen before filtering or execution. malformed=$work/malformed cp -R "$base" "$malformed" replace_directive "$malformed/test/wcc/data/runww_dup_main_reject/case.ww" \ '//ww:run-exit 01' if "$tool" -root "$malformed" -list > "$work/malformed.out" \ 2> "$work/malformed.err"; then fail "malformed directive accepted" fi [ ! -s "$work/malformed.out" ] || fail "malformed directive published output" stagemalformed=$work/stage-malformed cp -R "$base" "$stagemalformed" stagemalformedcase=$stagemalformed/test/wcc/data/r828_arrlit_ret_overlong/case.ww stagemalformedn=0 for directive in \ '//ww:error c "" ww "WW fragment"' \ '//ww:error c "C fragment" ww ""' \ '//ww:error c "C fragment" c "WW fragment"' \ '//ww:error c "C fragment" ww "WW fragment" trailing' do stagemalformedn=$((stagemalformedn + 1)) replace_directive "$stagemalformedcase" "$directive" out=$work/stage-malformed-$stagemalformedn.out err=$work/stage-malformed-$stagemalformedn.err if "$tool" -root "$stagemalformed" -list > "$out" 2> "$err"; then fail "malformed stage-specific directive accepted" fi [ ! -s "$out" ] \ || fail "malformed stage-specific directive published output" grep -F 'malformed stage-specific error directive' "$err" > /dev/null \ || fail "wrong malformed stage-specific diagnostic" done countdrift=$work/count-drift cp -R "$base" "$countdrift" find "$countdrift/test/wcc/data/a2s_mismatch_callarg" -depth -delete if "$tool" -root "$countdrift" -list > "$work/count.out" \ 2> "$work/count.err"; then fail "corpus count drift accepted" fi [ ! -s "$work/count.out" ] || fail "count drift published output" hashdrift=$work/hash-drift cp -R "$base" "$hashdrift" mv "$hashdrift/test/wcc/data/a2s_mismatch_callarg" \ "$hashdrift/test/wcc/data/a2s_mismatch_callarg_changed" if "$tool" -root "$hashdrift" -list > "$work/hash.out" \ 2> "$work/hash.err"; then fail "corpus hash drift accepted" fi [ ! -s "$work/hash.out" ] || fail "hash drift published output" # A terminal-stream sink failure must force a nonzero producer status. if "$tool" -root "$base" -run 'compiler.runww_dup_main_reject/c' -j 1 \ > /dev/full 2> "$work/full.err"; then fail "result publication failure returned success" fi publication=$(sed -n 's/^wwfixture: .*; capture: \(\/tmp\/[0-9a-f][0-9a-f]*\)$/\1/p' \ "$work/full.err") [ -n "$publication" ] || fail "publication failure capture was not retained" [ -f "$publication/publication.txt" ] \ || fail "publication failure diagnostic was not retained" cleanup_run "$publication" # Strict decoder: start with an independently produced, valid two-cell stream. good=$work/good.tab pattern=compiler.runww_dup_main_reject "$tool" -root "$base" -run "$pattern" -j 1 > "$good" "$tool" validate -root "$base" -run "$pattern" "$good" bad=$work/bad-no-header.tab sed '1d' "$good" > "$bad" expect_invalid "$base" "$pattern" "$bad" bad=$work/bad-version.tab awk -F '\t' 'BEGIN { OFS="\t" } NR == 1 { $2=2 } { print }' "$good" > "$bad" expect_invalid "$base" "$pattern" "$bad" bad=$work/bad-corpus.tab awk -F '\t' 'BEGIN { OFS="\t" } NR == 1 { $4=105 } { print }' "$good" > "$bad" expect_invalid "$base" "$pattern" "$bad" bad=$work/bad-missing-ordinal.tab sed '2d' "$good" > "$bad" expect_invalid "$base" "$pattern" "$bad" bad=$work/bad-duplicate-ordinal.tab awk 'NR == 2 { print; print; next } { print }' "$good" > "$bad" expect_invalid "$base" "$pattern" "$bad" bad=$work/bad-identity.tab awk -F '\t' 'BEGIN { OFS="\t" } NR == 2 { $3="compiler.not_selected" } \ { print }' "$good" > "$bad" expect_invalid "$base" "$pattern" "$bad" bad=$work/bad-stage.tab awk -F '\t' 'BEGIN { OFS="\t" } NR == 2 { $4="ww" } { print }' \ "$good" > "$bad" expect_invalid "$base" "$pattern" "$bad" bad=$work/bad-field-count.tab awk -F '\t' 'BEGIN { OFS="\t" } NR == 2 { print $0, "extra"; next } \ { print }' "$good" > "$bad" expect_invalid "$base" "$pattern" "$bad" bad=$work/bad-enum.tab awk -F '\t' 'BEGIN { OFS="\t" } NR == 2 { $6="bogus" } { print }' \ "$good" > "$bad" expect_invalid "$base" "$pattern" "$bad" bad=$work/bad-contradiction.tab awk -F '\t' 'BEGIN { OFS="\t" } NR == 2 { $8=0 } { print }' "$good" > "$bad" expect_invalid "$base" "$pattern" "$bad" bad=$work/bad-incomplete-line.tab bytes=$(wc -c < "$good") dd if="$good" of="$bad" bs=1 count=$((bytes - 1)) 2> /dev/null expect_invalid "$base" "$pattern" "$bad" bad=$work/bad-missing-footer.tab sed '$d' "$good" > "$bad" expect_invalid "$base" "$pattern" "$bad" bad=$work/bad-footer.tab awk -F '\t' 'BEGIN { OFS="\t" } $1 == "end" { $3=1 } { print }' \ "$good" > "$bad" expect_invalid "$base" "$pattern" "$bad" bad=$work/bad-trailing.tab cp "$good" "$bad" printf '%s\n' 'case 3 compiler.trailing c compile pass exit 1 0 -' \ >> "$bad" expect_invalid "$base" "$pattern" "$bad" echo "wwfixture integration: PASS"