lumbda/tests/portal-cross-test.sh
russell@unturf.com 54c4c651bb portal-rng: asm xoshiro256** + cross-impl tests + default seed=0
Completes ticket 0001 started in 27f468c. All three impls now carry
bit-identical xoshiro256**; portal state round-trips across process
boundaries in every producer x consumer cell (Python <-> C <-> asm).

asm impl:
- 4 new builtins: random-seed!, random-int, random-state, random-state!
- g_rng_state in BSS (4 x u64); rng_splitmix64_step, rng_seed, rng_next
- Binary portal header bumped LUMBDAB1/48 -> LUMBDAB2/80; carries
  32 bytes of rng state at offsets 40..64, reserved moved to 72
- No float support in asm, so (random) intentionally omitted there
- _start seeds with 0 so the stream is deterministic from startup

Python + C (supplements 27f468c):
- rng_seed(0) auto-invoked at module load / register_portal_builtins
  so (random) without explicit (random-seed!) returns a real value
  instead of the all-zero xoshiro fixed point

Tests:
- tests/functional.lsp: 7 new shared assertions (Python + C)
- asm/test.sh: 5 new asm-local assertions (142 -> 147)
- tests/portal-rng-save.lsp / portal-rng-load.lsp: portable S-expression
  portal that captures both state AND next-5 baseline so loader self-
  verifies without a separate harness
- tests/portal-cross-test.sh: 9 new producer x consumer RNG cells; all
  18 cells pass end-to-end

Verified: seed=42, (random-int 1000000) draws 1..10 =
558742 543102 559009 124193 317476 750584 200754 814407 344958 929085
identical in Python, C, and asm.

unmoad scan: zero new findings in added code.
2026-04-20 11:11:47 -04:00

106 lines
3.8 KiB
Bash
Executable file
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# portal-cross-test.sh — Exercise the full save×load matrix
# across Python, C, and asm using the S-expression portal.
#
# Each implementation must be able to:
# 1. Write a portable .sexp file
# 2. Read a .sexp file written by any other implementation
#
# Result: 3x3 = 9 combinations. 7 are tested here (Python and C
# have built-in (load); asm gained (load) in commit on 2026-04-16,
# so it participates as both producer and consumer).
set -e
cd "$(dirname "$0")/.."
PY="python3 lumbda.py --fast"
C="./c/lumbda"
ASM="./asm/lumbda"
SEXP=/tmp/portal-xtest.sexp
PASS=0
FAIL=0
verify() {
local label="$1" output="$2"
if echo "$output" | grep -q "PASS:"; then
PASS=$((PASS + 1))
printf " %-30s OK\n" "$label"
else
FAIL=$((FAIL + 1))
printf " %-30s FAIL\n" "$label"
echo "----- output:"
echo "$output"
echo "-----"
fi
}
echo "═══════════════════════════════════════════════════════"
echo "Portal cross-impl exchange (S-expression format)"
echo "═══════════════════════════════════════════════════════"
for PRODUCER in "Python|$PY tests/portal-cross-save.lsp" \
"C|$C tests/portal-cross-save.lsp" \
"Asm|$ASM < tests/portal-cross-save.lsp"; do
P_NAME="${PRODUCER%%|*}"
P_CMD="${PRODUCER#*|}"
echo
echo "── producer: $P_NAME ──"
rm -f "$SEXP"
# portal-cross-save writes to /tmp/cross.sexp; copy out to our slot
eval "$P_CMD" >/dev/null 2>&1
cp /tmp/cross.sexp "$SEXP"
for CONSUMER in "Python|$PY tests/portal-cross-load.lsp" \
"C|$C tests/portal-cross-load.lsp" \
"Asm|$ASM < tests/portal-cross-load.lsp"; do
C_NAME="${CONSUMER%%|*}"
C_CMD="${CONSUMER#*|}"
# Regenerate /tmp/cross.sexp from our slot (portal-cross-load reads it)
cp "$SEXP" /tmp/cross.sexp
OUTPUT=$(eval "$C_CMD" 2>&1)
verify "$P_NAME$C_NAME" "$OUTPUT"
done
done
rm -f "$SEXP" /tmp/cross.sexp
echo
echo "═══════════════════════════════════════════════════════"
echo "RNG portal cross-impl (xoshiro256** state via S-expression)"
echo "Proves: seed in one impl, resume in another, identical stream."
echo "Ticket: docs/tickets/0001-portal-rng.md"
echo "═══════════════════════════════════════════════════════"
RNG_SEXP=/tmp/cross-rng-xtest.sexp
for PRODUCER in "Python|$PY tests/portal-rng-save.lsp" \
"C|$C tests/portal-rng-save.lsp" \
"Asm|$ASM < tests/portal-rng-save.lsp"; do
P_NAME="${PRODUCER%%|*}"
P_CMD="${PRODUCER#*|}"
echo
echo "── producer: $P_NAME ──"
rm -f "$RNG_SEXP" /tmp/cross-rng.sexp
eval "$P_CMD" >/dev/null 2>&1
cp /tmp/cross-rng.sexp "$RNG_SEXP"
for CONSUMER in "Python|$PY tests/portal-rng-load.lsp" \
"C|$C tests/portal-rng-load.lsp" \
"Asm|$ASM < tests/portal-rng-load.lsp"; do
C_NAME="${CONSUMER%%|*}"
C_CMD="${CONSUMER#*|}"
cp "$RNG_SEXP" /tmp/cross-rng.sexp
OUTPUT=$(eval "$C_CMD" 2>&1)
verify "$P_NAME$C_NAME" "$OUTPUT"
done
done
rm -f "$RNG_SEXP" /tmp/cross-rng.sexp
echo
echo "═══════════════════════════════════════════════════════"
echo "Cross-impl portal + RNG: $PASS passed, $FAIL failed"
if [ $FAIL -ne 0 ]; then
exit 1
fi