fix(test): a generic function called from a test block works

Test blocks were never type-checked, and the checker is what makes a
generic call real; they are checked like entry now. Reseed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-25 04:46:08 +03:00
parent 9faaa2094d
commit a2012a5afe
7 changed files with 10045 additions and 9933 deletions

View file

@ -1782,7 +1782,8 @@ ludicc app.ludic --emit-llvm -o app.ll # stop at LLVM IR
```
A test program is a file of `test "name" { ... }` blocks with `expect(cond)`, `expect_eq(a, b)` and
`expect_near(a, b, tol)` in them. `ludic test` finds `tests/*.ludic` and `src/**/*_test.ludic`;
`expect_near(a, b, tol)` in them; a test block is type-checked like `entry`, so a generic function
called from one works as it does anywhere. `ludic test` finds `tests/*.ludic` and `src/**/*_test.ludic`;
given a directory, it runs every `*_test.ludic` under it and every file straight inside a `tests/`
directory under it. **Each test block runs in a process of its own**, so a global one test changes
is back to its initial value in the next - no test depends on another having run, or not. A

5
changes/test-generics.md Normal file
View file

@ -0,0 +1,5 @@
bump: patch
type: fix
**A generic function called from a `test` block works.** Test blocks were never type-checked, and
the checker is what makes a generic call real, so `q_new()` inside a test was "unknown function".
They are checked like `entry` now - which also means a type error in a test is reported.

View file

@ -0,0 +1,25 @@
# testing_generic.ludic — a test block is checked like `entry`, so a generic function called from
# one is made real for its type arguments there, as it is from any function. Its runner prints:
# == 2 passed, 0 failed ==
program TestingGeneric {
property Box<T> { items: []T = null }
function box_new<T>() -> Box<T> {
let b = new Box<T>
b.items = new []T
return b
}
function box_push<T>(b: Box<T>, v: T) -> void {
push(b.items, v)
}
test "a generic call in a test" {
let b: Box<int> = box_new()
box_push(b, 4)
box_push(b, 5)
expect_eq(len(b.items), 2)
}
test "another instance of it" {
let b: Box<string> = box_new()
box_push(b, "trout")
expect(b.items[0] == "trout")
}
}

View file

@ -185,6 +185,12 @@ function check_program() -> void {
ck_listener(g_onlisten[j])
j += 1
}
# a test block is checked like `entry`: that is also what makes its generic calls real
j = 0
while j < len(g_tests) {
ck_block(g_tests[j].a)
j += 1
}
gen_finish()
if ck_errors > 0 and not ck_reporting() {
let m = `{itoa(ck_errors)} type error(s)\n`

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -713,6 +713,7 @@ function cmd_dev_test() -> int {
controller_case("library/combat_kit", "xxxxxxxxxxxx", "6846 153196 31 107110 6000 5002 1 80 97 11 10 700 110", "combat_kit.ludic (Dash, Melee + MeleeHit, build stats in Combat.damage + Crit, Stats.add, Dungeon arena / exits / entry points, Prefs.max)")
controller_case("games/rpg_demo", "", "1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1", "rpg_demo.ludic (ludic.rpg #59 all 7 modules: grid move+veto+interact, inventory+equip, crafting, quests, dialog graph, sokoban+signal gate, poison status)")
spec_case("library/testing", "== 6 passed, 0 failed ==")
spec_case("library/testing_generic", "== 2 passed, 0 failed ==")
spec_case("library/coverage", "== 3 passed, 0 failed ==")
feat_case("library/errors", "", "5 10 0 7 1", "errors.ludic (assert guards an invariant, holds -> runs to the end; issue #8 success path)")
feat_case("library/recover", "", "2600 100 1 1 42 7", "recover.ludic (try/else recovers a result value + fallback, error binding, is_ok/is_err; issue #46)")