PL/SW's regression suite uses reg-rs
-- the same golden-output convention used across other sw-cor24-*
language projects (sw-cor24-basic, etc.). Each test is a
(.rgt, .out, .err) triple under reg-rs/: .rgt is the test
metadata (command + exit code), .out is the captured golden
stdout, and .err is the captured golden stderr. Re-running a
test executes its command and diffs the live output against the
golden -- any mismatch in stdout, stderr, or exit code is a
regression. (.tdb files are reg-rs's internal SQLite index,
regenerated from the triples on demand, and are gitignored.)
tests/
driver.sh per-case dispatcher; calls scripts/pipeline.sh
with the right .msw includes for each example
reg-rs/
plsw_<case>.rgt test metadata (TOML: command + exit_code)
plsw_<case>.out golden stdout
plsw_<case>.err golden stderr
*.tdb / *.tdb.lock reg-rs SQLite index (gitignored)
scripts/
test.sh wraps `reg-rs run -p plsw_ --parallel`
bootstrap-goldens.sh wraps `reg-rs create -t plsw_<case> -c ...`
for each case; used to (re)create goldens
| Case | Fixture | Includes |
|---|---|---|
hello |
examples/hello.plsw |
-- |
led |
examples/led.plsw |
-- |
loop |
examples/loop.plsw |
-- |
record |
examples/record.plsw |
-- |
define |
examples/define.plsw |
-- |
select_demo |
examples/select_demo.plsw |
-- |
select_nested |
examples/select_nested.plsw |
-- |
macro |
examples/macro.plsw |
examples/system.msw |
hello_macro |
examples/hello_macro.plsw |
examples/greet.msw |
chain |
examples/chain.plsw |
include/{cvt,ascb,asxb,tcb}.msw |
Each case is exposed as a reg-rs test named plsw_<case>. The
tests/driver.sh <case> script encodes the include list per case
and invokes scripts/pipeline.sh with the right argument order.
just test # run all reg-rs tests; non-zero exit on regression
just test-linker # run components/linker/tests/demo-*.sh end-to-end
just smoke # interactive compiler smoke (was the old `just test`)just test is the regression gate -- if it's green, no observable
behavior of any .plsw example changed. It depends on
just build-lgo so a fresh clone produces build/plsw.lgo before
any test fixture invokes pipeline.sh; running just test
end-to-end on a clean tree is the supported "is everything wired
up" check. just test-linker is separate because it currently
surfaces a pre-existing failure (see Blockers below) that's
unrelated to ordinary PL/SW changes; it depends on build-lgo
too because demo-plsw-modular.sh needs the compiler artifact.
just smoke is a manual sanity check that just runs the compiler
binary interactively for 100M cycles.
reg-rs run --parallel fires the 15 cases concurrently. For that
to be deterministic, pipeline.sh must never write to a shared
path during a test:
build/plsw.sandbuild/plsw.lgoare read-only afterjust build-lgofinishes.pipeline.sherrors cleanly if either is missing or stale rather than auto-rebuilding -- a hidden auto-rebuild would let two parallel pipelines race on writing the same.lgo. Runjust build-lgo(orjust testwhich depends on it) before invokingpipeline.shdirectly.- Per-invocation scratch lives in a
mktemp -d /tmp/plsw-XXXXXXdirectory.program.sandprogram.lgoare deterministic filenames inside that unique directory. This sidesteps the Linux-vs-BSDmktemptemplate behavior (BSDmktempdoes not reliably substituteXXXXXXwhen followed by a literal extension like.s) and gives every parallel pipeline its own filesystem space.
Concretely: a non-deterministic failure set across runs (case
A fails one run, case B the next, no code change in between)
almost always means a parallel write race somewhere. The two
known vectors above are closed; if a new race appears, look for
newly-introduced shared paths in the test path.
The first time the suite is established (or after an intentional behavior change lands), run:
just test-bootstrap-goldensThis invokes reg-rs create -t plsw_<case> -c './tests/driver.sh <case>'
for each case. The command is run, its stdout+stderr+exit code
captured, and the resulting reg-rs/plsw_<case>.{rgt,out,err}
triples land on disk.
Captured goldens are not authoritative until a human has
reviewed them. Reg-rs faithfully reproduces whatever was
captured -- including a demo that fails to compile, an OOM, or
any other "broken" state. If the bootstrap captures a broken
state, every subsequent just test will report green because
the broken state is reproducible.
The plsw_record incident (2026-05-09) is the worked example.
examples/record.plsw was using ADDR('literal'), which the
codegen rejects with ADDR requires a variable, field, or array element. Bootstrap captured exit_code = 1 and an empty .out,
which then "passed" forever -- masking a real demo regression
nobody noticed until they tried to run the demo.
Before git add reg-rs/..., open each
reg-rs/plsw_<case>.{rgt,out,err} triple and confirm:
.rgt:exit_code = 0(unless the demo's intended state really is non-zero -- rare; document why here in this file)..out: non-empty and contains the expected program output..err: diagnostics look clean (no unexpected stack traces, no compilation errors).
scripts/bootstrap-goldens.sh catches the easy case (non-zero
exit code) and aborts with a non-zero exit unless
--allow-failures is passed. That guard is a backstop, not a
substitute for review.
Commit all three files in the triple:
git add reg-rs/plsw_*.rgt reg-rs/plsw_*.out reg-rs/plsw_*.errSubsequent just test runs compare against those committed
baselines. To rebase a single test after an intentional change,
use reg-rs rebase plsw_<case> (re-runs the command and accepts
the new output as the baseline).
- Add a new
.plsw(and any.mswincludes) underexamples/orinclude/. - Add a
casearm intests/driver.shmapping the case name to the right pipeline.sh argument list. - Add the case to the
cases=(...)list inscripts/bootstrap-goldens.sh. - Run
just test-bootstrap-goldensto capture the golden. git add reg-rs/plsw_<new-case>.{rgt,out,err}and commit.
components/linker/tests/demo-fixup.sh and
components/linker/tests/demo-plsw-modular.sh exercise the
separate-compilation pipeline (assemble -> meta-gen -> link24 ->
cor24-emu). They're run via just test-linker rather than
just test because they use hand-written .s fixtures under
components/linker/tests/fixtures-{fixup,plsw}/, not the
reg-rs convention.
Status as of this saga (against the May-7 toolchain on PATH):
| Demo | Status | Notes |
|---|---|---|
demo-plsw-modular.sh |
✅ PASS | end-to-end .plsw -> .lgo via PL/SW + link24 + meta-gen + cor24-emu; expected output HI matches |
demo-fixup.sh |
❌ garble | UART output is ~eU\t: repeated 3x instead of the expected main:enter\n...main:leave sequence |
demo-fixup.sh runs cleanly (no script error, exits 0), assembles
all four hand-written modules, links them, loads via
cor24-emu --load-binary, and reads stable garbled bytes from the
program's UART writes. The script's awk filter strips the
expected header (Loaded N bytes.../Entry point:/Running...)
and isolates the program's UART traffic, leaving:
~eU :
z{|'~eU :
~eU :
Hex view of the bytes: 0x7E 0x65 0x55 0x09 0x3A 0x0A repeated
with minor variation. The expected output is ASCII
main:enter\nliba:enter\nutil:inc\nliba:leave\nlibb:enter\nutil:inc\nlibb:leave\nmain:leave\n,
none of which appears.
Probable causes (not investigated in this saga):
- Stale hand-written
.sfixtures.fixtures-fixup/{main,liba,libb,util}.smay target older addressing or directive conventions. The modular-plsw path (which compiles fresh.plswto.sviapl-sw) works correctly, so the toolchain itself is fine — the bug is upstream in the fixture text. la <reg>, <imm>resolution drift. If the fixtures use absolute-addresslaforms thatcor24-asm's newer encoding handles differently, or if the--base-addrsemantics in pass-2 reassembly produce different bytes than the oldercor24-run --assembledid, the resulting program would jump into wrong code/data. The 3-cycle repetition in the garble (each module entering and outputting the same bad string) matches what you'd see if all three modules'_UART_PUTScalls resolve to the same wrong data address.fixtures-fixupwas always broken and the comment-stated "Expected output" was aspirational rather than empirical. Git blame on the fixture files would establish whether they ever produced themain:entersequence.
This is a separate blocker from the PL/SW goldens work — the production .plsw compilation path is exercised by the modular demo and works. Out of scope for this saga; capture for a future investigation.