Proposal: built-in testing framework + coverage (Go-style) — one command, low ceremony for game devs #12
Labels
No labels
area:ci
area:docs
area:input
area:net
area:rendering
area:repo
area:stdlib
area:tooling
area:types
cleanup
dx
priority:high
priority:low
priority:medium
proposal
status:in-progress
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: workshopsoft/ludic#12
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
Summary
A built-in testing framework with coverage, in the spirit of Go's
go test:tests live next to code, run with one command, and report pass/fail + line
coverage. This is the quality backbone for every other library in this batch.
Why it matters
tests to be trustworthy; we should build the harness first-class rather than
shell scripts.
feel like writing a function, and you should be able to ignore it until you
want it. It's opt-in, never in the way of shipping a prototype.
Proposed design (illustrative)
test "name" { … }block (or@test fn) discovered automatically.expect(x).to_equal/to_be_true/to_be_near/…(
to_be_nearfor fixed-point/float — games need tolerance-based checks).bin/xtask runner:bin/x testruns all, prints asummary;
--coveragereports per-file line coverage.Considerations
lift; design the IR hooks with the type-system/compiler work in mind.
bin/xrunner and CI on push tomain.doc-checktooling.Scope / acceptance
testblocks + auto-discovery in the compiler.bin/x testrunner with a clean summary; non-zero exit on failure.--coveragewith per-file line coverage.selfhost/tests/*sample to the new form.Related: error handling (asserting failures), and every stdlib issue in this batch.
Shipped in
ea2c6ab— a built-in testing framework in the spirit ofgo test: tests live next to the code, run with one command, report pass/fail, and exit non-zero on failure.What landed:
test "name" { … }blocks, discovered automatically. Noentryto write, nothing to register — the compiler synthesises a runner@mainthat runs every test in the file.expect(cond),expect_eq(a, b), andexpect_near(a, b, tol).expect_nearcarries the tolerance fixed-point and accumulated-integer game math need (the KAT-styleto_be_nearthe proposal called out). On failure they printfile:line: <what> failed (got G, want W)and mark the test failed without aborting, so one run reports every failure.ok - name/FAIL - nameper test, a== N passed, M failed ==summary, and a non-zero exit if anything failed — soludic spec_test.ludicdrops straight intobin/xand CI.Example:
How it's built: a new
testdeclaration keyword (parse_test→N_TEST, collected ing_tests); call nodes now carry their source line for the messages;emit_test_runnerlowers each block to a@fn__test_iand generates the runner@main;expect*lower to a branch-print-flag tail. The compiler's own source has notestblocks, so its self-compiled IR is byte-identical and the C-free bootstrap fixpoint still holds (verified).testis wired into the vocabulary (ludic_syntax.h, the JetBrains lexer, the TextMate grammar —x test-toolsconfirms they're in sync) and documented atdocs/language/testing/. A passing spec (examples/library/testing.ludic) is guarded in the regression suite by a newspec_case(build, run, require exit 0 + the expected summary);x testis now 67 checks.Acceptance: ✅ test blocks + auto-discovery, ✅ assertion library incl. tolerance, ✅ one-command run with a clean summary + non-zero exit on failure, ✅ docs page + a converted-form spec. Deferred:
--coverage(per-file line coverage) — the issue's own "biggest lift," it needs IR line-hit instrumentation and is best done as its own change so the fixpoint stays easy to reason about. Closing the framework; a follow-up will track coverage.