4.3 KiB
G-029 Build packaged test:: #modpod modules from src/tests
Status: proposed Scope: src/make.tcl (test-module packaging step), src/tests/ (source of truth), src/modules/test/ (generated #modpod targets), src/modules/punk/mix/ (modpod tooling as needed) Acceptance: as in root GOALS.md index (canonical).
Context
Module tests currently exist in two forms:
- src/tests/modules//testsuites/ - the source-tree harness form: self-contained tcltest files (own package requires, final cleanupTests, self-locating data) run via src/tests/runtests.tcl. This is where test development happens and is the intended structure going forward.
- src/modules/test/#modpod--/ - the packaged form: a
test:: module (wrapper .tm providing a SUITE/RUN interface and
locating its bundled *_testsuites) that ships in kits and is run in built
executables, e.g
tomlish.exe -app test.
The packaged form is not just kit plumbing - it is a distributable for
module consumers: a user who downloads a built module can optionally
download the matching test:: package and verify the module's
behaviour on their own system/platform, with no source tree or test harness -
just package require test::<modulename> + its RUN interface, or a wrapped
executable's -app test. That end-user scenario is what fixes the packaged
form's requirements: fully self-sufficient (suites + data bundled), locatable
via ordinary module paths, and runnable in a bare interp.
Nothing generates one form from the other, so they drift as parallel hand-maintained copies. The tomlish project (2026-07-07) is the live example: its modpod testsuites were ported to src/tests (with fixes - self-containment, single cleanupTests, self-located data), while the modpod remains the source the built test::tomlish module actually comes from. Both are now fossil-tracked and must be kept in sync by hand until this goal lands.
Approach
- A make.tcl step (natural companion to the existing module build steps) packages src/tests/modules//testsuites/** into the test:: #modpod - punkcheck-tracked so unchanged sources skip.
- The wrapper .tm (SUITE/RUN interface, suitesdir resolution) becomes part of the generation: either templated per module or a shared runtime the modpod references. Its interface must keep serving the -app test dispatch used by wrapped executables.
- Because src/tests files are self-contained, the packaged copies can be byte-identical file copies; the wrapper supplies discovery (suite names from directory structure) rather than environment variables the files depend on. Legacy wrapper variables (e.g ::test::tomlish::suitesdir) should not be reintroduced into test files - self-containment is the contract.
- Data fidelity: suite data (e.g roundtrip toml files with deliberate crlf/mixed line endings) must be copied byte-for-byte; the #modpod directory form is plain files, so no archive-level transformation should apply.
- Aggregate suites in the old modpods (fauxlink-based tests/ and dev/ collections) are a generation-time decision: reproduce via fauxlink, replace with wrapper-level suite selection, or drop - record the choice here.
- Naming: test:: derives from the src/tests path (modules/tomlish -> test::tomlish, modules/punk/args -> test::punk::args). Only modules with testsuites content get a package generated.
Alternatives considered
- Making the packaged modpod the source of truth and generating src/tests from it - rejected: src/tests is where development happens (runtests.tcl ergonomics, agent workflows, per-suite layout) and its self-contained-file contract is the healthier base; the wrapper form is derivable, the reverse requires stripping wrapper dependencies (as the tomlish port demonstrated).
- Keeping dual hand-maintained copies - rejected: that is the current drift the tomlish incident demonstrated; sync-by-discipline does not hold.
Notes
- Related: the tomlish project's src/tests port (its fossil checkins 20bd00dc, feedb6f0 record the dual-copy interim and name this goal); punkshell's own src/modules/test/ packages are further targets once the mechanism exists.
- The packaging step's output is a build artifact by policy even though it lives under src/ - the punkcheck records make regeneration cheap and hand-edits detectable.