Files
Bullosseum/tests/level_switch_test.gd
T
richard 7b6a141767 Reorganize the codebase into role-based folders (snake_case)
Move the 40-odd root-level scripts and scenes into a tree that groups by role,
per the approved roadmap (WS2/WS3): actors/{player,matador,bear}, systems/{combat,
fx,input,debug,render,crowd,config}, ui/, props/ (was tscn_s/). Rename the shell
scene.tscn -> levels/game_shell.tscn and every PascalCase scene to snake_case
(Player.tscn -> player.tscn, MainMenu.tscn -> main_menu.tscn, etc.) so the
case-insensitive web/macOS filesystems can't alias two files together.

Done as a scripted move: each file travels with its .uid; every quoted res:// path
across scenes, scripts, .tres, and project.godot's 9 autoloads is rewritten; the
.godot uid/class cache is rebuilt from scratch. Scenes still resolve by uid, so the
rewrite is belt-and-suspenders. Lint globs (run_tests.sh + both CI workflows) now
walk the new dirs; an orphan animation_tree.gd.uid and 7 pre-existing trailing-
whitespace lints (barrel/torch, never covered by the old root-only glob) are cleaned.

Full suite green on both sides. Asset consolidation (Assets/Animations/fonts ->
art/) is deferred — moving imported .glb/.png/.ttf is safer done in the editor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017KoYBYQgRyY1zDHXMsXY3V
2026-09-06 00:04:41 +03:00

75 lines
2.6 KiB
GDScript

extends SceneTree
## Level-isolation test: when the shell loads the Bear's Den, none of the arena level's
## geometry — crucially its wall/floor colliders and audience — may survive into it.
## Guards the modular-level contract: switching levels swaps the whole module, it never
## hides old nodes (hidden colliders still collide). Run:
## godot --headless --script tests/level_switch_test.gd
var _pass := 0
var _fail := 0
func _init() -> void:
_run.call_deferred()
func _ok(c: bool, m: String) -> void:
if c:
_pass += 1
print(" PASS: ", m)
else:
_fail += 1
print(" FAIL: ", m)
func _count_named(node: Node, wanted: String, acc: int = 0) -> int:
if node.name == wanted:
acc += 1
for child: Node in node.get_children():
acc = _count_named(child, wanted, acc)
return acc
func _count_type(node: Node, klass: String, acc: int = 0) -> int:
if node.is_class(klass):
acc += 1
for child: Node in node.get_children():
acc = _count_type(child, klass, acc)
return acc
func _run() -> void:
var run: Node = root.get_node("Run")
run.ensure_run()
# Force the shell to build the Bear's Den regardless of the generated map.
run.debug_level_override = run.get_level(&"bear")
var scene: Node = load("res://levels/game_shell.tscn").instantiate()
root.add_child(scene)
current_scene = scene
# Let the shell instance the level and the spawner's deferred spawn run.
await create_timer(0.8).timeout
# Shell survives and hosts exactly one level module.
var level_root: Node = scene.get_node_or_null("LevelRoot")
_ok(level_root != null, "shell has a LevelRoot")
# LevelRoot holds exactly the one level module.
var module_children := level_root.get_child_count() if level_root != null else 0
_ok(module_children == 1, "exactly one level module loaded")
_ok(not get_nodes_in_group(&"player").is_empty(), "player (shell) present")
# The leak we are guarding against: no arena geometry may exist in the bear level.
_ok(_count_named(scene, "arena_placeholder_v01") == 0, "arena placeholder (+ its wall collider) gone")
_ok(_count_named(scene, "Arena2") == 0, "arena mesh 'Arena2' gone")
_ok(_count_named(scene, "PublikNode") == 0, "arena audience gone from bear level")
# The bear level brings its own geometry, one floor collider, and its boss.
_ok(_count_named(scene, "bear_level_arena") == 1, "bear arena mesh present")
_ok(_count_type(level_root, "StaticBody3D") >= 1, "bear level has its own floor collider")
await create_timer(0.4).timeout
_ok(not get_nodes_in_group(&"bear").is_empty(), "bear boss spawned in bear level")
print("Results: %d passed, %d failed" % [_pass, _fail])
quit(1 if _fail > 0 else 0)