Extend test coverage: seed resolution, coordinate invariants, preset shape characteristics #16

Open
opened 2026-08-16 09:08:32 +01:00 by liamjd · 0 comments
Owner

Follow-on from #12, which added GUT and covered seed determinism (PR #15). The framework is in place; these are the remaining properties from the original list plus what setting it up revealed.

The runner and conventions are documented under ## Tests in CLAUDE.md.

1. Seed resolution in main_menu.gd

_read_seed_from_ui() has three branches: empty → random, valid integer → literal, anything else → hash(). Both entry paths — the New Game button and Enter in the seed field — must resolve identically.

A real bug shipped here. The resolved seed was echoed into the field before resolution, so typing a seed and clicking New Game silently used the previous seed, while pressing Enter honoured it. A test comparing the two paths would have caught it immediately.

The awkward part is that resolution currently lives inside a method that also writes to the UI and mutates GameState. Testing it may mean separating "resolve a string to a seed" from "apply it", which is worth doing on its own merits — a pure String -> int function is trivially testable and has no reason to know about LineEdit or autoloads.

2. Coordinate invariants

Carried from the POC, where they caught real bugs:

  • surface_y(L) == map_to_local(Vector3i(0, L, 0)).y + level_height — depends on cell_center_y = false, which _configure_grid() sets deliberately and which nothing currently guards. Flipping it would silently sink every pawn and prop half a level into the ground.
  • world_to_cell(cell_to_world(c)) == c for every cell, including the boundary cells where the clamping in world_to_cell() is doing the work.
  • level_at() on an out-of-bounds cell returns the datum rather than erroring or reading past the end of _levels.

These become load-bearing as soon as anything is placed on the terrain, which is the next feature area.

3. Shape presets are distinguishable

test_presets_differ_from_each_other_under_one_seed already asserts island ≠ plains. Worth extending to characteristics rather than mere inequality, so a preset degenerating toward plains is caught:

  • crater — the rim band sits above the centre
  • canyon — there is a contiguous low channel spanning the map
  • island — the border is below the datum, the interior is not

Deliberately loose. These are shape sanity checks, not golden-image tests, and they should survive tuning the noise parameters.

Known issue to cover once decided

The island preset nearly drowns at small grid sizes, surfaced by the #12 tests:

island, 24x24:  14 walkable (5 on main landmass)
plains, 24x24: 127 walkable (96 on main landmass)

_radial() normalises by the grid centre so the shape is scale-independent, but falloff_start combined with the water threshold is not. At the default 64 it looks fine, which is why it had not shown up.

This needs a decision before it can be tested: either small grids are unsupported (assert a documented minimum) or the falloff should be scale-corrected (assert walkable area stays proportionate across sizes). Note the #12 tests generate at 24x24 for speed, so whichever way this goes, they may need their grid size revisiting.

Out of scope

Rendering. gl_compatibility draws nothing headless, so visual verification stays manual and screenshot-based. CI too — there is no pipeline yet.

Follow-on from #12, which added GUT and covered seed determinism (PR #15). The framework is in place; these are the remaining properties from the original list plus what setting it up revealed. The runner and conventions are documented under `## Tests` in CLAUDE.md. ## 1. Seed resolution in `main_menu.gd` `_read_seed_from_ui()` has three branches: empty → random, valid integer → literal, anything else → `hash()`. Both entry paths — the New Game button and Enter in the seed field — must resolve identically. **A real bug shipped here.** The resolved seed was echoed into the field *before* resolution, so typing a seed and clicking New Game silently used the *previous* seed, while pressing Enter honoured it. A test comparing the two paths would have caught it immediately. The awkward part is that resolution currently lives inside a method that also writes to the UI and mutates `GameState`. Testing it may mean separating "resolve a string to a seed" from "apply it", which is worth doing on its own merits — a pure `String -> int` function is trivially testable and has no reason to know about `LineEdit` or autoloads. ## 2. Coordinate invariants Carried from the POC, where they caught real bugs: - `surface_y(L) == map_to_local(Vector3i(0, L, 0)).y + level_height` — depends on `cell_center_y = false`, which `_configure_grid()` sets deliberately and which nothing currently guards. Flipping it would silently sink every pawn and prop half a level into the ground. - `world_to_cell(cell_to_world(c)) == c` for every cell, including the boundary cells where the clamping in `world_to_cell()` is doing the work. - `level_at()` on an out-of-bounds cell returns the datum rather than erroring or reading past the end of `_levels`. These become load-bearing as soon as anything is *placed* on the terrain, which is the next feature area. ## 3. Shape presets are distinguishable `test_presets_differ_from_each_other_under_one_seed` already asserts island ≠ plains. Worth extending to characteristics rather than mere inequality, so a preset degenerating toward plains is caught: - crater — the rim band sits above the centre - canyon — there is a contiguous low channel spanning the map - island — the border is below the datum, the interior is not Deliberately loose. These are shape sanity checks, not golden-image tests, and they should survive tuning the noise parameters. ## Known issue to cover once decided **The island preset nearly drowns at small grid sizes**, surfaced by the #12 tests: ``` island, 24x24: 14 walkable (5 on main landmass) plains, 24x24: 127 walkable (96 on main landmass) ``` `_radial()` normalises by the grid centre so the *shape* is scale-independent, but `falloff_start` combined with the water threshold is not. At the default 64 it looks fine, which is why it had not shown up. This needs a decision before it can be tested: either small grids are unsupported (assert a documented minimum) or the falloff should be scale-corrected (assert walkable area stays proportionate across sizes). Note the #12 tests generate at 24x24 for speed, so whichever way this goes, they may need their grid size revisiting. ## Out of scope Rendering. `gl_compatibility` draws nothing headless, so visual verification stays manual and screenshot-based. CI too — there is no pipeline yet.
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
liamjd/UntitledColonyBuilder#16
No description provided.