test(mods): exercise the documented RomFS file contract #93
No reviewers
Labels
No labels
area/accessibility
area/audio
area/input
area/install
area/menu
area/renderer
area/saves
area/upscaling
beanstalk: approved @ 1697603
beanstalk: approved @ 2c5a62e
beanstalk: approved @ 4fd82b7
beanstalk: approved @ 64d6d09
beanstalk: approved @ 6f91fae
beanstalk: approved @ 70a78f9
beanstalk: approved @ 8015c9a
beanstalk: approved @ 8d361dc
beanstalk: approved @ 9d8356e
beanstalk: approved @ abf2227
beanstalk: approved @ d82f202
beanstalk: approved @ e5f1e40
beanstalk: approved @ ee8ab5f
ci: approved
good first issue
gpu/amd
gpu/igpu
gpu/intel
gpu/nvidia
help wanted
kind
bug
kind
chore
kind
docs
kind
feature
kind
performance
kind
question
needs measurements
platform/linux
platform/macos
platform/steam-deck
platform/windows
preview
priority
critical
priority
high
priority
low
regression
status
blocked
status
confirmed
status
duplicate
status
invalid
status
needs-info
status
wontfix
testing wanted
ci: approved
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!93
Loading…
Reference in a new issue
No description provided.
Delete branch "mooki/Longshot:test/mods-romfs-contract"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Why
Mod authors need an example of the file route Longshot already supports, without mistaking co-op's internal hooks for a mod interface. This documents that route and runs its authored example through the real loader without a game dump. See #42.
Independent of pull requests 51 and 68. This is documentation and test coverage, not a new loader or mod format. It does not settle the later chest-table, bridge, catalog or native-module decisions.
What changed
docs/mods.md: the current RomFS contract, with source symbols, discovery depth, whole-relative-path precedence, disabling, restart requirements, resource-size/prompt interaction and limitations. A JSON block describes invented text files for a game-free example; it is test input, not a manifest read by Longshot.tests/test_mods_size_table.py,documented_example_checks: materializes that exact documentation example in a temporary folder and runs the existing compiledhost/mods.c/host/prompts.cfixture. Six fresh processes check explicit/default roots, nested discovery, full-path order, non-conflicting files, case/slash lookup, ignored ExeFS, disabled parents and fallback after disabling every file mod. Selected file contents are checked, not just log messages.README.md, Mods: corrects “folder name” to the whole relative path, says to restart after changes, and links the author guide.Flags and settings
No new switches, options, menu rows or command-line flags. These existing settings are documented and exercised, not changed:
HOS_MODSmodsbeside the executableHOS_FS_OVERLAYThe existing test driver's
--ccand--sourceoptions are unchanged. No new setting needs a switch-reference entry.Measurements
Branch state: on main
5e072fc8c2adadba5ea610148be1c47800ba0cca; head9ab5201053e4955d29f8a3cfd330fca187ed6934; whole game-free suite on this head:542 passed, 3 failed, 16 skipped(main5e072fc8c2adadba5ea610148be1c47800ba0cca:542 passed, 3 failed, 16 skipped). The 3 failing tests fail on main the same way:tests/test_framegen_resources.py,tests/test_module_ids.py,tests/test_pad_layout.py. The branch was carried onto this main by hand; what differs from a mechanical carry is named in the comment posted with this update.main:
5eaaf7fb9c(2026-11-12)this PR: test/mods-romfs-contract @ 36539b7fa0797ea9de4a26071e13ee8de0f0a5d7
Hardware: Linux 7.2.15-1-omarchy, x86_64; Python 3.12.16, GCC 16.2.1; the same PC for every run. No GPU used.
Scene: invented text files and resource tables, not a running game.
Method: the expanded
tests/test_mods_size_table.pyin main / this PR / main / this PR order, two runs against each source. Wall time includes fixture compilation and all test subprocesses, measured from process start to exit with a monotonic clock. Counts below are named test-driver results, not an invented count of individual assertions. Every run also prints six successful documented-example starts, 12 file/table-routing cases and seven malformed-table refusals. No case was skipped.The existing
--sourceoption selects the pinned main or PRhostsource. The expanded driver, documentation example, POSIX fixture support and dependencies are the same PR versions for both arms. The exercised loader and prompt source files are byte-identical between these commits: passing both ways is expected for an existing-behavior contract, not evidence of a fixed runtime bug. The wall times describe this source check under shared machine load; they do not establish a game-speed improvement or regression.How to reproduce: from this PR's checkout, run
python -I -B tests/test_mods_size_table.py --cc gcc --sourcewith thehostdirectory of a checkout at the stated main commit, then this PR'shostdirectory, then repeat in that order. Record each process's exit status and elapsed wall time; take the median of each source's two runs. The complete suite command remainspython -I -B tests/run_all.py --jobs 6 --strict, but no whole suite was rerun for this measurement.5eaaf7fb9Recorded process results, in execution order:
All four runs print:
Earlier whole-suite evidence, retained at its actual commits rather than relabelled as the newer main:
77f605a25The updater reference-pack failure is not inherited here: main removed that updater test. These 19 full-suite skips remain explicit platform/device/dump requirements, not passes; they are separate from the zero skips in the four named-driver runs.
Testing
Tested: four interleaved Linux runs of the expanded
tests/test_mods_size_table.py, main5eaaf7fb9/ this PR36539b7fa/ main / this PR, with elapsed wall times and named-driver pass/fail counts above. All four pass with no skips, checking selected bytes from the exact documented example. The earlier whole game-free suite on36539b7fawas 509 passed, 0 failed, 19 skipped; it was not rerun for these measurements. Existing source rules and table/prompt checks are retained; no C fixture source or compiler command changed.Not tested: a game build or game run, a real Clawshot seed, progression, real controller prompts, native Windows execution, macOS filesystem behavior, or an unknown mod's safety. This driver already skips filesystem routing on Windows and macOS; a passing table-only run there would not prove the new example. No native-module, chest-table, checked-location, grant or Archipelago compatibility claim. No performance claim.
Needs other testers:
Screenshots
Nothing on screen. The example uses invented paths the game never requests, so a screenshot would not prove the contract.
Risk and rollback
The risk is misleading documentation or a fixture expectation that drifts from the loader. The example is read from the guide itself and checked through the real loader; the guide separates established behavior from proposals and warns that file mods are not a sandbox. Reverting this commit removes only docs and test coverage. It changes no installed files, saves or runtime defaults.
Reviewer notes
Start with
documented_example_checksand the example table indocs/mods.md; comparehost/mods.c'sfind_mods,mod_cmp,hos_mods_initandhos_mods_romfs. The nested example deliberately makes a folder named00-nestedwin over02-override, demonstrating that the whole relative path is compared.Pull request 51 is the shared menu text editor, not the earlier Mods-tab proposal. Pull request 68 builds core co-op on it;
coop_on_eventis not a native-module ABI. This branch needs neither head. They also edit the README, but not its Mods section; this change does not touch their code.Maintainers still need to choose the chest-table versus bridge-first direction, progression/save/conflict ownership, community recipes versus an official catalog, trusted native modules versus companions, and co-op module-first versus core-first. This test-only slice does not choose for them.
No game proof is needed to establish this documentation/test change: production behavior is unchanged. Before claiming current Longshot/Clawshot compatibility, a separate proof should run a locally generated seed for up to five minutes with a copied save, check a known changed reward and controller prompts, then restart for one minute with the mod disabled and confirm the original route. That run has not been requested or performed here and would not prove the higher tiers.
Issues
See #42
Checklist
For everyone
type(scope): description).main.python -I -B tests/run_all.pyrun and passing (any failure that also fails onmainis named above).third_party/untouched (a new folder with aPROVENANCE.mdinstead).If applicable
Assisted-by:/Co-Authored-By:trailers.tests/that reads changed source updated in this PR, not weakened.New on this forge; not opened on GitHub.
Assisted-by: GPT-6 Astra
Co-Authored-By: GPT-6 Astra [email protected]
Measured the named mod test in main/PR/main/PR order at main
5eaaf7fb9and this PR36539b7fa: all four runs passed, with median wall times of 1.515874 s and 1.517231 s. This is game-free contract coverage, not a game-speed result; native Windows ordering and disable/restart behavior remain the separate request for testers. The test request is open (T-EQ83). Rebased onto main5eaaf7fb9with the change otherwise unchanged; whole suite on the new headf609dd2ab: 510 passed, 0 failed, 19 skipped. Ready for review.36539b7fa0f609dd2abdWIP: test(mods): exercise the documented RomFS file contractto test(mods): exercise the documented RomFS file contractCarried onto main
5e072fc8c; new head9ab520105. The one commit replayed with no conflicts, and its own added and removed lines are unchanged. Only untouched lines around its changes differ, because main changed the neighbouring text: inREADME.mdthe paragraph above the mods section is now main's hold-to-toggle description, and the build-from-source line below it has main's new wording; intests/test_mods_size_table.pymain's prompt-cache change addedCreateDirectoryAto the list just above the new example function. The whole suite on this exact head: 542 passed, 3 failed, 16 skipped; its only failures are main's owntests/test_pad_layout.py,tests/test_module_ids.pyandtests/test_framegen_resources.py.f609dd2abd9ab5201053