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:
parent
9faaa2094d
commit
a2012a5afe
7 changed files with 10045 additions and 9933 deletions
|
|
@ -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
|
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/`
|
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
|
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
|
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
5
changes/test-generics.md
Normal 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.
|
||||||
25
examples/library/testing_generic.ludic
Normal file
25
examples/library/testing_generic.ludic
Normal 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -185,6 +185,12 @@ function check_program() -> void {
|
||||||
ck_listener(g_onlisten[j])
|
ck_listener(g_onlisten[j])
|
||||||
j += 1
|
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()
|
gen_finish()
|
||||||
if ck_errors > 0 and not ck_reporting() {
|
if ck_errors > 0 and not ck_reporting() {
|
||||||
let m = `{itoa(ck_errors)} type error(s)\n`
|
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
|
|
@ -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("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)")
|
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", "== 6 passed, 0 failed ==")
|
||||||
|
spec_case("library/testing_generic", "== 2 passed, 0 failed ==")
|
||||||
spec_case("library/coverage", "== 3 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/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)")
|
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)")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue