extends SceneTree ## test_scene_tree (Karte t_e3c0310a, U2 Szenen-Baum links): prüft den ## hierarchischen Map-Inhalt-Baum mit Zwei-Wege-Auswahl. ## ## Geprüft wird: ## T1 Baum füllt sich aus dem Dokument (Objekte mit Kategorie-Ebene, ## Index-Meta, Auge-Präfix, Kopf „Objekte (N)“). ## T2 Klick im Baum setzt die Auswahl in 3D (Index stimmt). ## T3 Auswahl in 3D setzt die Baum-Markierung (beide Richtungen). ## T4 Mehrfachauswahl gespiegelt (2 Indizes → 2 markierte Zeilen). ## T5 Gruppen-Ebene: doc-Gruppe erscheint als Knoten mit Mitgliedern. ## T6 Leeres Dokument = Klartext-Hinweis (nicht klickbar). ## T7 Skalierung folgt ui_scale.gd (UIScale.px/v2 — eine Skalierungsquelle). var _fails := 0 var _oks := 0 var main var tree_mod func _check(cond: bool, what: String) -> void: if cond: _oks += 1 print(" ok: ", what) else: _fails += 1 print(" FAIL: ", what) func _init() -> void: print("Testgruppe: Szenen-Baum links (U2 Zwei-Wege-Auswahl)") var scene: PackedScene = load("res://scenes/main.tscn") main = scene.instantiate() root.add_child(main) await process_frame await process_frame await process_frame tree_mod = main.get("tree_mod") _check(tree_mod != null, "tree_mod aus der Registry geladen") _check(tree_mod.get("content_tree") != null, "content_tree im Modul vorhanden") # Drei Objekte ins Doc: 2x props (barrel/crate), 1x sonstiges (fremde ID). (main.doc["objects"] as Array).append({ "id": "barrel_001", "pos": [4.0, 0.0, 4.0], "scale": 1.0, "rot_y_deg": 0.0}) (main.doc["objects"] as Array).append({ "id": "box_001", "pos": [-6.0, 0.0, 2.0], "scale": 2.0, "rot_y_deg": 10.0}) (main.doc["objects"] as Array).append({ "id": "fremd_objekt_xyz", "pos": [0.0, 0.0, 8.0], "scale": 1.0, "rot_y_deg": 0.0}) main.call("_after_structure_change") await process_frame await _t1_baum_fuellt_sich() await _t2_klick_setzt_auswahl() await _t2b_echter_klick() await _t3_auswahl_3d_markiert_baum() await _t4_mehrfach_gespiegelt() await _t5_gruppen_ebene() await _t6_leerer_zustand() await _t7_skalierung() _finish() func _obj_items_under(item: TreeItem) -> Array: ## Alle Kind-Zeilen mit obj_index-Meta (nur DIREKTE Kinder). var out: Array = [] for child in item.get_children(): if child.has_meta("obj_index"): out.append(child) return out func _find_child(item: TreeItem, prefix: String) -> TreeItem: for child in item.get_children(): if str(child.get_text(0)).begins_with(prefix): return child return null func _t1_baum_fuellt_sich() -> void: var tree: Tree = tree_mod.get("content_tree") var root: TreeItem = tree.get_root() var obj_head := _find_child(root, "Objekte (3)") _check(obj_head != null, "T1 Kopf „Objekte (3)“ aus dem Dokument") if obj_head == null: return # Kategorie-Ebene (Karte t_e3c0310a): Objekte hängen unter Kategorie-Knoten. var kat_nodes: int = obj_head.get_child_count() _check(kat_nodes >= 1, "T1 Kategorie-Ebene vorhanden (%d Knoten)" % kat_nodes) var found_idx: Dictionary = {} var all_ok := true for kat in obj_head.get_children(): for oi in _obj_items_under(kat): var idx := int(oi.get_meta("obj_index")) if found_idx.has(idx): all_ok = false found_idx[idx] = true all_ok = all_ok and str(oi.get_text(0)).begins_with("👁 ") _check(found_idx.size() == 3, "T1 alle 3 Objekt-Indizes genau einmal im Baum") _check(all_ok, "T1 Auge-Präfix (nur Anzeige) je Objekt-Zeile") _check(found_idx.has(0) and found_idx.has(1) and found_idx.has(2), "T1 Indizes 0/1/2 stimmen") func _t2_klick_setzt_auswahl() -> void: var tree: Tree = tree_mod.get("content_tree") var root: TreeItem = tree.get_root() var obj_head := _find_child(root, "Objekte (3)") var item1: TreeItem = null for kat in obj_head.get_children(): for oi in _obj_items_under(kat): if int(oi.get_meta("obj_index")) == 1: item1 = oi _check(item1 != null, "T2 Zeile für Objekt-Index 1 gefunden") if item1 == null: return item1.select(0) tree.item_selected.emit() # simuliert den Klick-Pfad (item_selected → _on_tree_selected) await process_frame var sel: Variant = main.interaction.get("selected_index") _check(int(sel) == 1, "T2 Klick im Baum setzt Auswahl in 3D (Index 1 — hat %s)" % str(sel)) ## FIX 25.10.: Im Modus SELECT_MULTI feuert Godot nie item_selected — der Test ## oben simulierte einen Weg, den es in der Praxis nicht gab. Hier ein ECHTER ## Mausklick (Eingabe-Ereignis) auf eine Zeile. func _t2b_echter_klick() -> void: var tree: Tree = tree_mod.get("content_tree") main.interaction.clear_selection() root.size = Vector2i(1600, 900) await process_frame await process_frame var item0: TreeItem = null for kat in _find_child(tree.get_root(), "Objekte (3)").get_children(): for oi in _obj_items_under(kat): if int(oi.get_meta("obj_index")) == 2: item0 = oi _check(item0 != null, "T2b Zeile für Objekt-Index 2 gefunden") if item0 == null: return tree.scroll_to_item(item0) await process_frame var r := tree.get_item_area_rect(item0, 0) var pos := tree.get_global_position() + r.position + Vector2(r.size.x * 0.5, r.size.y * 0.5) _check(tree.get_global_rect().has_point(pos), "T2b Zeile liegt im sichtbaren Baum-Rechteck") var ev := InputEventMouseButton.new() ev.button_index = MOUSE_BUTTON_LEFT ev.position = pos ev.global_position = pos ev.pressed = true root.push_input(ev) await process_frame var up := ev.duplicate() up.pressed = false root.push_input(up) await process_frame await process_frame _check(int(main.interaction.get("selected_index")) == 2, "T2b ECHTER Klick auf die Listenzeile wählt das Objekt in der Szene (Index 2 — hat %s)" % str(main.interaction.get("selected_index"))) func _t3_auswahl_3d_markiert_baum() -> void: var tree: Tree = tree_mod.get("content_tree") var imap: Dictionary = tree_mod.get("_obj_index_map") _check(imap.has(0) and imap.has(2), "T3 Index-Karte deckt 0 und 2 ab") main.call("_mark_tree_selection", 0) await process_frame var item0: TreeItem = imap[0] var item1: TreeItem = imap[1] _check(item0.is_selected(0), "T3 Auswahl in 3D (0) markiert die Baum-Zeile") _check(not item1.is_selected(0), "T3 alte Markierung (1) beim Einzel-Klick abgewählt") # Scroll-zu-Auswahl (Karte t_e3c0310a): im selben Pfad ruft mark_tree_selection_multi # Tree.scroll_to_item auf der Primär-Zeile auf — hier durch die Markierung mitgeprüft. func _t4_mehrfach_gespiegelt() -> void: var imap: Dictionary = tree_mod.get("_obj_index_map") main.call("_mark_tree_selection_multi", [0, 2]) await process_frame _check(imap[0].is_selected(0), "T4 Mehrfachauswahl: Zeile 0 markiert") _check(imap[2].is_selected(0), "T4 Mehrfachauswahl: Zeile 2 markiert") _check(not imap[1].is_selected(0), "T4 Zeile 1 bleibt abgewählt") func _t5_gruppen_ebene() -> void: var gid: int = GroupOps.group(main.doc, [0, 2]) _check(gid >= 0, "T5 Gruppe im Doc angelegt (id %d)" % gid) main.call("_after_structure_change") await process_frame var tree: Tree = tree_mod.get("content_tree") var root: TreeItem = tree.get_root() var grp_head := _find_child(root, "Gruppen (1)") _check(grp_head != null, "T5 Gruppen-Ebene im Baum") if grp_head == null: return var g_node: TreeItem = grp_head.get_first_child() _check(g_node != null and g_node.has_meta("group_id"), "T5 Gruppen-Knoten trägt group_id") var members := _obj_items_under(g_node) _check(members.size() == 2, "T5 Gruppe zeigt 2 Mitglieder im Baum") # Klick auf den Gruppen-Knoten wählt ALLE Mitglieder in 3D (Zwei-Wege). g_node.select(0) tree.item_selected.emit() await process_frame var sel_idx: int = int(main.interaction.get("selected_index")) var sel_many: Array = main.interaction.get("selected_indices") var many_ok: bool = 0 in sel_many and 2 in sel_many _check(many_ok and (sel_idx == 0 or sel_idx == 2), "T5 Gruppen-Klick spiegelt beide Mitglieder in 3D (Anker %d)" % sel_idx) func _t6_leerer_zustand() -> void: (main.doc["objects"] as Array).clear() main.doc["spawn_zones"] = {} main.call("_after_structure_change") await process_frame var tree: Tree = tree_mod.get("content_tree") var root: TreeItem = tree.get_root() var hint := _find_child(root, "Noch keine Objekte") _check(hint != null, "T6 leeres Dokument zeigt Klartext-Hinweis") if hint != null: _check(not hint.is_selectable(0), "T6 Hinweis-Zeile ist nicht anwählbar") func _t7_skalierung() -> void: var tree: Tree = tree_mod.get("content_tree") var want: Vector2 = UIScale.v2(0, 160) _check(tree.custom_minimum_size == want, "T7 Baum-Mindestgröße folgt UIScale.v2 (%s)" % str(want)) _check(int(tree.custom_minimum_size.y) == UIScale.px(160), "T7 Höhe = UIScale.px(160) — eine Skalierungsquelle") func _finish() -> void: print("CHECKS: %d FAILURES: %d" % [_oks + _fails, _fails]) if _fails > 0: print("SCENE_TREE_TEST_FAIL") quit(1) else: print("ALL OK: %d checks" % (_oks + _fails)) quit(0)