wgt01
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
"""Guard the invariants that silently break the toolset (AGENTS.md §5, §2.3):
|
||||
|
||||
python test_namesync.py # asserts; no nwn_gff / Blender needed
|
||||
|
||||
1. Every .itp Features leaf is backed by a matching .set [GROUP#] whose first tile
|
||||
is that leaf's model (Name + Tile0).
|
||||
2. Paint tiles (fills + terrain transitions) stay OUT of every group/feature, so the
|
||||
brush pool is non-empty — an empty pool crashes the New Area wizard (the
|
||||
2026-07-04 access violation). Regression guard for that crash.
|
||||
3. The cobble/canal paint matrix covers every corner pattern the brush can request
|
||||
(an incomplete matrix = paint crash).
|
||||
4. The .itp Terrain folder exposes exactly the paintable terrains + eraser.
|
||||
Runs on make_set.build() and make_itp.build() directly, so it fails fast if
|
||||
group_name / resref / is_paint / placements drift apart.
|
||||
"""
|
||||
import re
|
||||
|
||||
import tiles
|
||||
import make_set
|
||||
import make_itp
|
||||
|
||||
|
||||
def _groups(setf):
|
||||
return {m.group(1): int(m.group(2))
|
||||
for m in re.finditer(r"\[GROUP\d+\]\nName=(.+?)\n(?:.*\n)*?Tile0=(\d+)", setf)}
|
||||
|
||||
|
||||
def _tile_models(setf):
|
||||
return {int(m.group(1)): m.group(2)
|
||||
for m in re.finditer(r"\[TILE(\d+)\]\nModel=(\S+)", setf)}
|
||||
|
||||
|
||||
def _features():
|
||||
return [l for c in make_itp.build()["MAIN"]["value"] if c["ID"]["value"] == 0
|
||||
for l in c["LIST"]["value"]]
|
||||
|
||||
|
||||
def test_features_back_every_group():
|
||||
setf = "\n".join(make_set.build())
|
||||
groups, models = _groups(setf), _tile_models(setf)
|
||||
feat = _features()
|
||||
nonpaint = [s for s in tiles.TILES if not tiles.is_paint(s)]
|
||||
assert len(feat) == len(groups) == len(nonpaint), \
|
||||
f"count mismatch: {len(feat)} features / {len(groups)} groups / {len(nonpaint)} non-paint"
|
||||
for lf in feat:
|
||||
name, resref = lf["NAME"]["value"], lf["RESREF"]["value"]
|
||||
assert name in groups, f".itp feature {name!r} has no matching .set [GROUP#]"
|
||||
tile0 = groups[name]
|
||||
assert models[tile0] == resref, \
|
||||
f"{name}: group Tile0={tile0} model {models[tile0]!r} != leaf RESREF {resref!r}"
|
||||
|
||||
|
||||
def test_paint_tiles_stay_in_brush_pool():
|
||||
paint = [s for s in tiles.TILES if tiles.is_paint(s)]
|
||||
assert paint, "no paint tiles -> empty brush pool -> New Area crash"
|
||||
grouped = set(_groups("\n".join(make_set.build())))
|
||||
featured = {l["NAME"]["value"] for l in _features()}
|
||||
for s in paint:
|
||||
gn = tiles.group_name(s)
|
||||
assert gn not in grouped, f"paint tile {gn!r} is grouped -> dropped from brush pool"
|
||||
assert gn not in featured, f"paint tile {gn!r} is a feature -> dropped from brush pool"
|
||||
|
||||
|
||||
def test_canal_matrix_complete():
|
||||
want, _covered, missing = tiles.canal_paint_matrix()
|
||||
assert not missing, f"paint matrix missing {len(missing)} pattern(s): {sorted(missing)}"
|
||||
assert want, "empty matrix?"
|
||||
|
||||
|
||||
def test_terrain_folder_is_paintables_plus_eraser():
|
||||
terr = [l for c in make_itp.build()["MAIN"]["value"] if c["ID"]["value"] == 2
|
||||
for l in c["LIST"]["value"]]
|
||||
names = {l["RESREF"]["value"] for l in terr}
|
||||
assert names == {"cobble", "canal", "eraser"}, names
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_features_back_every_group()
|
||||
test_paint_tiles_stay_in_brush_pool()
|
||||
test_canal_matrix_complete()
|
||||
test_terrain_folder_is_paintables_plus_eraser()
|
||||
print("namesync OK")
|
||||
Reference in New Issue
Block a user