extends SceneTree ## Proves the client-side ChunkDataDecoder decodes the backend's exact golden ## fixture files (backend/tests/fixtures/chunks/*.bin) with byte-identical ## logical values -- the actual cross-language contract this whole ## persistence system depends on. Also regression-covers the float32 ## int<->float numeric-cast bug (fixed alongside this test): `bits as float` ## silently corrupted every non-integer value (1.5 decoded as 1069547520.0). ## ## Run: ~/godot/godot --headless --script res://scripts/smoke_test_chunk_codec_interop.gd const FIXTURES_DIR := "res://backend/tests/fixtures/chunks/" var passed := 0 var failed := 0 func _check(label: String, cond: bool) -> void: if cond: passed += 1 print(" PASS: ", label) else: failed += 1 print(" FAIL: ", label) func _init() -> void: _test_float32_roundtrip() _test_empty_chunk_fixture() _test_simple_tile_chunk_fixture() _test_chunk_with_objects_fixture() print("\n=== SMOKE RESULT: %d/%d passed ===" % [passed, passed + failed]) quit(0 if failed == 0 else 1) func _read_fixture(name: String) -> PackedByteArray: var f := FileAccess.open(FIXTURES_DIR + name, FileAccess.READ) if f == null: push_error("Could not open fixture: " + name) return PackedByteArray() return f.get_buffer(f.get_length()) func _test_float32_roundtrip() -> void: print("--- float32 bitcast regression (the actual bug this fix addresses) ---") for val in [1.5, -110.25, 0.5, 3.14159, -0.001, 0.0, 100.0]: var encoded: PackedByteArray = ChunkDataDecoder._write_float32(val) var decoded: float = ChunkDataDecoder._read_float32(encoded, 0) _check("float32 round-trip for %s (got %s)" % [val, decoded], is_equal_approx(decoded, val)) # The specific regression case: 1.5 must NOT decode as 1069547520.0 # (the old `bits as float` numeric-cast bug's actual observed output). var bad_encoded := ChunkDataDecoder._write_float32(1.5) var bad_decoded: float = ChunkDataDecoder._read_float32(bad_encoded, 0) _check("1.5 does not decode as the old numeric-cast bug's 1069547520.0", not is_equal_approx(bad_decoded, 1069547520.0)) func _test_empty_chunk_fixture() -> void: print("--- backend golden fixture: empty_chunk.bin ---") var data := _read_fixture("empty_chunk.bin") _check("fixture file read (non-empty)", data.size() > 0) var payload: ChunkDataDecoder.ChunkPayload = ChunkDataDecoder.decode_binary(data) _check("decoded without error", payload != null) if payload == null: return _check("world_id matches backend fixture (7)", payload.world_id == "7") _check("no tile layers", payload.tile_layers.is_empty()) _check("no objects", payload.objects.is_empty()) _check("revision matches backend fixture (1)", payload.revision == 1) func _test_simple_tile_chunk_fixture() -> void: print("--- backend golden fixture: simple_tile_chunk.bin ---") var data := _read_fixture("simple_tile_chunk.bin") var payload: ChunkDataDecoder.ChunkPayload = ChunkDataDecoder.decode_binary(data) _check("decoded without error", payload != null) if payload == null: return _check("world_id matches (12)", payload.world_id == "12") _check("coord matches (3,-1,0)", payload.coord == Vector3i(3, -1, 0)) _check("bounds.position matches (96.0,-32.0,0.0) -- non-trivial floats", payload.bounds.position.is_equal_approx(Vector3(96.0, -32.0, 0.0))) _check("bounds.size matches (32.0,32.0,1.0)", payload.bounds.size.is_equal_approx(Vector3(32.0, 32.0, 1.0))) _check("one tile layer parsed", payload.tile_layers.size() == 1) if payload.tile_layers.size() == 1: var layer: ChunkDataDecoder.TileLayerData = payload.tile_layers[0] _check("tile layer dims 4x4x1", layer.width == 4 and layer.height == 4 and layer.depth == 1) _check("tile data matches backend fixture", layer.tiles == PackedInt32Array([0, 1, 1, 0, 1, 2, 2, 1, 1, 2, 2, 1, 0, 1, 1, 0])) _check("revision matches (2)", payload.revision == 2) func _test_chunk_with_objects_fixture() -> void: print("--- backend golden fixture: chunk_with_objects.bin (real non-integer floats) ---") var data := _read_fixture("chunk_with_objects.bin") var payload: ChunkDataDecoder.ChunkPayload = ChunkDataDecoder.decode_binary(data) _check("decoded without error", payload != null) if payload == null: return _check("one object parsed", payload.objects.size() >= 1) if payload.objects.is_empty(): return var obj: ChunkDataDecoder.WorldObjectData = payload.objects[0] _check("object id matches (obj_0001)", obj.id == "obj_0001") _check("object asset_id matches (props/chest)", obj.asset_id == "props/chest") # The core regression check: these are exactly the kind of non-integer # values that decoded as garbage under the old `bits as float` bug. _check("object position.x matches 101.5 exactly (was the corrupted value)", is_equal_approx(obj.position.x, 101.5)) _check("object position.y matches -110.25 exactly (was the corrupted value)", is_equal_approx(obj.position.y, -110.25)) _check("object position.z matches 0.5 exactly (was the corrupted value)", is_equal_approx(obj.position.z, 0.5))