## D-030 Layer 1: Tests for rendering components (tile, fog, entity renderers) ## Validates renderers handle snapshot data correctly. class_name TestRendering extends GdUnitTestSuite var EntityRendererScript = load("res://scripts/rendering/entity_renderer.gd") var FogRendererScript = load("res://scripts/rendering/fog_renderer.gd") var TileRendererScript = load("res://scripts/rendering/tile_renderer.gd") # -- Test data matching Protocol decoded format -- var _test_tiles: Array = [ {"x": 0, "y": 0, "z": 0, "type": "floor"}, {"x": 1, "y": 0, "z": 0, "type": "wall"}, {"x": 2, "y": 0, "z": 0, "type": "door"}, {"x": 3, "y": 0, "z": 0, "type": "object"}, {"x": 0, "y": 1, "z": 0, "type": "floor"}, ] var _test_entities: Array = [ {"entity_id": 1, "x": 5.0, "y": 5.0, "z": 0, "kind": {"variant": "Player", "data": null}}, {"entity_id": 2, "x": 7.0, "y": 5.0, "z": 0, "kind": {"variant": "Npc", "data": null}}, ] # -- Constants -- func test_tile_size_constant() -> void: assert_that(Constants.TILE_SIZE).is_equal(32) # -- GameState: tile and visibility data -- func test_game_state_stores_tiles() -> void: GameState.apply_snapshot({"tick": 1, "tiles": _test_tiles}) assert_that(GameState.visible_tiles.size()).is_equal(5) func test_game_state_stores_visible_positions() -> void: var positions := [{"x": 5, "y": 5}, {"x": 6, "y": 5}] GameState.apply_snapshot({"tick": 1, "visible_positions": positions}) assert_that(GameState.visible_positions.size()).is_equal(2) assert_that(GameState.visible_positions.has(Vector2i(5, 5))).is_true() assert_that(GameState.visible_positions.has(Vector2i(6, 5))).is_true() func test_game_state_clears_old_visible_positions() -> void: GameState.apply_snapshot({"tick": 1, "visible_positions": [{"x": 1, "y": 1}]}) assert_that(GameState.visible_positions.size()).is_equal(1) GameState.apply_snapshot({"tick": 2, "visible_positions": [{"x": 2, "y": 2}, {"x": 3, "y": 3}]}) assert_that(GameState.visible_positions.size()).is_equal(2) assert_that(GameState.visible_positions.has(Vector2i(1, 1))).is_false() func test_game_state_warns_on_missing_player() -> void: GameState.player_entity_id = 999 GameState.player_position = Vector2(5, 5) GameState.apply_snapshot({"tick": 1, "entities": _test_entities}) assert_that(GameState.player_position).is_equal(Vector2(5, 5)) # -- SimBridge: test data completeness -- func test_sim_bridge_test_snapshot_has_tiles() -> void: SimBridge._test_tick = 0 var snap = SimBridge._test_snapshot() assert_that(snap.has("tiles")).is_true() assert_that(snap.tiles.size()).is_greater(0) var tile = snap.tiles[0] assert_that(tile.has("x")).is_true() assert_that(tile.has("y")).is_true() assert_that(tile.has("type")).is_true() func test_sim_bridge_test_snapshot_has_visible_positions() -> void: SimBridge._test_tick = 0 var snap = SimBridge._test_snapshot() assert_that(snap.has("visible_positions")).is_true() assert_that(snap.visible_positions.size()).is_greater(0) var pos = snap.visible_positions[0] assert_that(pos.has("x")).is_true() assert_that(pos.has("y")).is_true() func test_sim_bridge_test_snapshot_has_player_entity() -> void: SimBridge._test_tick = 0 var snap = SimBridge._test_snapshot() var has_player := false for entity in snap.entities: if entity.kind.variant == "Player": has_player = true break assert_that(has_player).is_true() func test_sim_bridge_test_snapshot_has_npc() -> void: SimBridge._test_tick = 0 var snap = SimBridge._test_snapshot() var has_npc := false for entity in snap.entities: if entity.kind.variant == "Npc": has_npc = true break assert_that(has_npc).is_true() func test_sim_bridge_test_tiles_contain_all_types() -> void: SimBridge._test_tick = 0 var snap = SimBridge._test_snapshot() var types: Dictionary = {} for tile in snap.tiles: types[tile.type] = true assert_that(types.has("floor")).is_true() assert_that(types.has("wall")).is_true() assert_that(types.has("door")).is_true() # -- EntityRenderer: lifecycle -- func _make_entity_renderer() -> Node2D: var renderer = Node2D.new() renderer.set_script(EntityRendererScript) add_child(renderer) return renderer func test_entity_renderer_creates_nodes() -> void: var renderer := _make_entity_renderer() renderer.update_entities(_test_entities) assert_that(renderer.entity_nodes.size()).is_equal(2) assert_that(renderer.entity_nodes.has(1)).is_true() assert_that(renderer.entity_nodes.has(2)).is_true() renderer.queue_free() func test_entity_renderer_removes_stale_entities() -> void: var renderer := _make_entity_renderer() renderer.update_entities(_test_entities) assert_that(renderer.entity_nodes.size()).is_equal(2) renderer.update_entities([_test_entities[0]]) assert_that(renderer.entity_nodes.size()).is_equal(1) assert_that(renderer.entity_nodes.has(1)).is_true() renderer.queue_free() func test_entity_renderer_positions_centered() -> void: var renderer := _make_entity_renderer() renderer.update_entities([_test_entities[0]]) var node = renderer.entity_nodes[1] var offset: float = (Constants.TILE_SIZE - 24) / 2.0 var expected_x: float = 5.0 * Constants.TILE_SIZE + offset var expected_y: float = 5.0 * Constants.TILE_SIZE + offset assert_that(node.position.x).is_equal_approx(expected_x, 0.01) assert_that(node.position.y).is_equal_approx(expected_y, 0.01) renderer.queue_free() func test_entity_renderer_skips_missing_entity_id() -> void: var renderer := _make_entity_renderer() renderer.update_entities([{"x": 1.0, "y": 1.0, "z": 0, "kind": {"variant": "Npc", "data": null}}]) assert_that(renderer.entity_nodes.size()).is_equal(0) renderer.queue_free() func test_entity_renderer_player_color_differs_from_npc() -> void: var renderer := _make_entity_renderer() renderer.update_entities(_test_entities) var player_node = renderer.entity_nodes[1] as ColorRect var npc_node = renderer.entity_nodes[2] as ColorRect assert_that(player_node.color != npc_node.color).is_true() renderer.queue_free() func test_entity_renderer_empty_entities_clears_all() -> void: var renderer := _make_entity_renderer() renderer.update_entities(_test_entities) assert_that(renderer.entity_nodes.size()).is_equal(2) renderer.update_entities([]) assert_that(renderer.entity_nodes.size()).is_equal(0) renderer.queue_free() # -- FogRenderer: position registration -- func _make_fog_renderer() -> TileMapLayer: var fog = TileMapLayer.new() fog.set_script(FogRendererScript) return fog func test_fog_renderer_registers_positions() -> void: var fog := _make_fog_renderer() fog.register_tile_positions(_test_tiles) assert_that(fog._all_tile_positions.size()).is_equal(5) assert_that(fog._all_tile_positions.has(Vector2i(0, 0))).is_true() assert_that(fog._all_tile_positions.has(Vector2i(3, 0))).is_true() assert_that(fog._all_tile_positions.has(Vector2i(0, 1))).is_true() fog.free() func test_fog_renderer_clears_on_re_register() -> void: var fog := _make_fog_renderer() fog.register_tile_positions(_test_tiles) assert_that(fog._all_tile_positions.size()).is_equal(5) fog.register_tile_positions([{"x": 10, "y": 10, "z": 0, "type": "floor"}]) assert_that(fog._all_tile_positions.size()).is_equal(1) assert_that(fog._all_tile_positions.has(Vector2i(0, 0))).is_false() fog.free() func test_fog_renderer_handles_empty_data() -> void: var fog := _make_fog_renderer() fog._initialized = true fog.update_fog({}, Vector2.ZERO) fog.register_tile_positions([]) assert_that(fog._all_tile_positions.size()).is_equal(0) fog.free() # -- TileRenderer: tile type constants -- func test_tile_type_map_covers_required_types() -> void: var tile_script = TileRendererScript var required := ["floor", "wall", "door", "object"] for tile_type in required: assert_that(tile_script.TILE_TYPE_MAP.has(tile_type)).is_true()