scopedvr-map-editor/tests/test_scene_tree.gd
2026-11-01 19:34:05 +01:00

238 lines
No EOL
8.7 KiB
GDScript

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://core/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)