extends SceneTree ## Headless smoke test for Creator Studio Visual Property & Behavior Inspector const FIXTURE_PATH := "res://backend/tests/fixtures/chunks/simple_tile_chunk.bin" const ChunkEditorModelClass := preload("res://scripts/editor/chunk_editor_model.gd") const EditorObjectCatalogClass := preload("res://scripts/editor/object_catalog.gd") const EditorCoreClass := preload("res://scripts/editor/editor_core.gd") const InspectorPanelClass := preload("res://scripts/editor/ui/inspector_panel.gd") const StaticObjectNodeClass := preload("res://scripts/static_object_node.gd") const WorldObjectClass := preload("res://scripts/world_object.gd") const ChunkDataDecoderClass := preload("res://scripts/chunk_data_decoder.gd") var passed := 0 var failed := 0 func _check(label: String, cond: bool) -> void: if cond: passed += 1 print(" PASS: ", label) else: failed += 1 printerr(" FAIL: ", label) func _initialize() -> void: await _run() func _read_fixture() -> PackedByteArray: var f := FileAccess.open(FIXTURE_PATH, FileAccess.READ) if f == null: push_error("Could not open fixture: " + FIXTURE_PATH) return PackedByteArray() return f.get_buffer(f.get_length()) func _run() -> void: print("=== Running Creator Studio Inspector & Behavior Smoke Tests ===") var root := Node.new() root.name = "TestRoot" get_root().add_child(root) var fixture_bytes := _read_fixture() _check("Loaded fixture binary bytes", fixture_bytes.size() > 0) if fixture_bytes.is_empty(): quit(1) return var model := ChunkEditorModelClass.from_binary(fixture_bytes) _check("Decoded ChunkEditorModel from binary", model != null) var catalog := EditorObjectCatalogClass.new() var cat_err := catalog.load_catalog() _check("Loaded EditorObjectCatalog", cat_err == OK) var core := EditorCoreClass.new(model, catalog) _check("Initialized EditorCore", core != null) # 1. Place an object var obj_id := core.place_object("crate_small", Vector3(1.0, 1.0, 0.0)) _check("Placed object via EditorCore", obj_id != "") var obj := model.find_object(obj_id) _check("Object found in model", obj != null) # 2. Attach and verify InspectorPanel var inspector := InspectorPanelClass.new() root.add_child(inspector) await process_frame inspector.setup(core) inspector.show_object(obj_id) _check("InspectorPanel loaded object view", true) # 3. Add behavior rules to object var test_behavior: Dictionary = { "rules": [ { "trigger": {"type": "on_interact"}, "conditions": [], "actions": [ {"verb": "give_item", "item_id": "buffalo_chips", "count": 10} ] }, { "trigger": {"type": "on_timer", "interval": 1.5}, "conditions": [], "actions": [ {"verb": "rotate", "degrees": 90.0, "axis": "y", "duration": 0.4} ] } ] } var set_ok := core.set_object_behavior(obj_id, test_behavior) _check("set_object_behavior executed via ToolRegistry", set_ok) _check("Object properties has script_behavior", obj.properties.has("script_behavior")) var attached = obj.properties.get("script_behavior", {}) _check("Attached behavior contains 2 rules", attached.get("rules", []).size() == 2) # 4. Undo and Redo verification var undo_ok := core.undo_redo.undo() _check("Undo executed successfully", undo_ok) _check("Undo cleared script_behavior property", not obj.properties.has("script_behavior")) var redo_ok := core.undo_redo.redo() _check("Redo executed successfully", redo_ok) _check("Redo restored script_behavior property", obj.properties.has("script_behavior")) _check("Redo preserved both rules", obj.properties.get("script_behavior", {}).get("rules", []).size() == 2) # 5. Roundtrip through Chunk binary encode/decode var re_encoded := model.encode_binary() _check("Model encoded to binary bytes with behavior", re_encoded.size() > 0) var re_decoded := ChunkDataDecoderClass.decode_binary(re_encoded) _check("Decoded re-encoded binary chunk", re_decoded != null) var persisted_obj: ChunkDataDecoderClass.WorldObjectData = null for o in re_decoded.objects: if o.id == obj_id: persisted_obj = o break _check("Persisted object found in binary decoded chunk", persisted_obj != null) _check("Persisted object retained script_behavior rules", persisted_obj.properties.has("script_behavior")) # 6. StaticObjectNode & ScriptInterpreterNode verification var wo := WorldObjectClass.new() wo.id = obj.id wo.type = obj.type wo.asset_id = obj.asset_id wo.position = obj.position wo.rotation = obj.rotation wo.scale = obj.scale wo.properties = obj.properties.duplicate(true) var static_node := StaticObjectNodeClass.new() static_node.world_object = wo root.add_child(static_node) await process_frame var interpreter := static_node.get_node_or_null("ScriptInterpreter") _check("StaticObjectNode created ScriptInterpreter child", interpreter != null) if interpreter != null: _check("ScriptInterpreter parsed valid behavior", interpreter.behavior != null) if interpreter.behavior != null: _check("ScriptInterpreter rules count is 2", interpreter.behavior.rules.size() == 2) # Refresh inspector display with rules inspector.show_object(obj_id) _check("Inspector refreshed object display", true) print("Smoke test complete. Passed: %d, Failed: %d" % [passed, failed]) if failed > 0: quit(1) else: quit(0)