254 lines
No EOL
10 KiB
GDScript
254 lines
No EOL
10 KiB
GDScript
extends SceneTree
|
||
## Editor-Test (headless): R3 Resize-Dialog — Karte t_c863b93b.
|
||
## Ausführen: godot --headless --path . --script res://tests/test_resize_dialog.gd
|
||
## Exit 0 = alles grün.
|
||
##
|
||
## Deckt ab:
|
||
## 1. Dialog-Aufbau (Stil wie new_map_dialog): Presets, Clamp 256–4096,
|
||
## Info-Zeile mit aktueller Größe/Auflösung.
|
||
## 2. Preview-Texte (R2 resize_preview): Verlust-Liste beim Verkleinern,
|
||
## „nichts verloren“-Fall, Spawn-Speicher-Warnung (< 4 Punkte/Team, V11),
|
||
## Warnband beim Vergrößern über ~1024 m (Dichte-Leiter-Deckel).
|
||
## 3. Verdrahtung in main.gd: Menü-ID 6 öffnet Dialog, Bestätigen = resize_apply
|
||
## als EINE Undo-Einheit, Undo-Roundtrip speichert die alte Map
|
||
## BYTE-IDENTISCH, Redo stellt den Resize wieder her.
|
||
|
||
var failures := 0
|
||
var checks := 0
|
||
var main: Control
|
||
|
||
|
||
func _initialize() -> void:
|
||
_run_all.call_deferred()
|
||
|
||
|
||
func _run_all() -> void:
|
||
_test_dialog_basics()
|
||
_test_preview_texts()
|
||
await _test_main_wiring()
|
||
_finish()
|
||
|
||
|
||
func _finish() -> void:
|
||
if failures == 0:
|
||
print("ALL OK: %d checks" % checks)
|
||
quit(0)
|
||
else:
|
||
print("FAILED: %d/%d checks" % [failures, checks])
|
||
quit(1)
|
||
|
||
|
||
func _check(cond: bool, label: String) -> void:
|
||
checks += 1
|
||
if cond:
|
||
print(" ok: %s" % label)
|
||
else:
|
||
failures += 1
|
||
print(" FAIL: %s" % label)
|
||
|
||
|
||
# --------------------------------------------------------------- Dialog -----
|
||
|
||
func _make_dialog() -> ConfirmationDialog:
|
||
var dlg: ConfirmationDialog = preload("res://src/ui/resize_map_dialog.gd").new()
|
||
return dlg
|
||
|
||
|
||
func _test_dialog_basics() -> void:
|
||
print("Testgruppe: Dialog-Aufbau + Clamp")
|
||
var dlg := _make_dialog()
|
||
var doc := SvmapDocument.create_new("Test", 1024.0, "mittag")
|
||
dlg.setup(doc)
|
||
_check(dlg.info_label.text.contains("1024"), "Info-Zeile zeigt aktuelle Groesse (1024 m)")
|
||
_check(dlg.info_label.text.contains("129"), "Info-Zeile zeigt Aufloesung (129 Punkte)")
|
||
_check(is_equal_approx(dlg.get_new_size_x(), 1024.0), "Freifeld startet bei aktueller Groesse")
|
||
|
||
# Preset-Button 256: beide Achsen wandern mit (Button sitzt in der Preset-HBox).
|
||
var preset_btn: Button = null
|
||
for child in dlg.get_child(0).get_children():
|
||
if child is HBoxContainer:
|
||
for b in (child as HBoxContainer).get_children():
|
||
if b is Button and b.text == "256 m":
|
||
preset_btn = b
|
||
_check(preset_btn != null, "Preset-Button 256 m vorhanden")
|
||
if preset_btn != null:
|
||
preset_btn.emit_signal("pressed")
|
||
_check(is_equal_approx(dlg.get_new_size_x(), 256.0)
|
||
and is_equal_approx(dlg.get_new_size_z(), 256.0),
|
||
"Preset 256 setzt beide Achsen")
|
||
|
||
# Clamp: SpinBox deckelt auf 256–4096.
|
||
dlg.size_x_edit.value = 99999.0
|
||
_check(is_equal_approx(dlg.get_new_size_x(), 4096.0), "Clamp oben: 4096 m")
|
||
dlg.size_x_edit.value = 1.0
|
||
_check(is_equal_approx(dlg.get_new_size_x(), 256.0), "Clamp unten: 256 m")
|
||
dlg.free()
|
||
|
||
|
||
# -------------------------------------------------------------- Preview -----
|
||
|
||
## Map mit Objekt + Bauteil + Spawn-Punkt draußen (X = ±5000 m) bauen.
|
||
func _doc_with_outsiders(size: float) -> Dictionary:
|
||
var doc := SvmapDocument.create_new("Preview", size, "mittag")
|
||
doc["objects"] = [
|
||
{"id": "drinnen", "pos": [10.0, 0.0, 10.0], "rot": 0.0, "scale": 1.0},
|
||
{"id": "draussen", "pos": [5000.0, 0.0, 0.0], "rot": 0.0, "scale": 1.0},
|
||
]
|
||
doc["gebaeude"] = [
|
||
{"pos": [-30.0, 0.0, -30.0], "rot": 0.0,
|
||
"punkte": [[-2.0, 0.0, -2.0], [2.0, 0.0, 2.0]]},
|
||
{"pos": [-5000.0, 0.0, 0.0], "rot": 0.0,
|
||
"punkte": [[-2.0, 0.0, -2.0], [2.0, 0.0, 2.0]]},
|
||
]
|
||
var zone0: Dictionary = doc["spawn_zones"]["0"]
|
||
var pts: Array = zone0["points"]
|
||
for i in pts.size():
|
||
pts[i]["pos"][0] = 5000.0 + float(i) # Team Blau komplett draußen
|
||
zone0["points"] = pts
|
||
return doc
|
||
|
||
|
||
func _test_preview_texts() -> void:
|
||
print("Testgruppe: Preview-Texte (Verlust-Liste, Spawn-Warnung, Warnband)")
|
||
var dlg := _make_dialog()
|
||
|
||
# 1) Verkleinern mit Verlust: Liste + Entfernen-Hinweis + Spawn-Warnung.
|
||
var doc := _doc_with_outsiders(1024.0)
|
||
dlg.setup(doc)
|
||
dlg.size_x_edit.value = 256.0
|
||
dlg.size_z_edit.value = 256.0
|
||
dlg._update_preview() # auerhalb des Baums feuert value_changed nicht (in-tree: live)
|
||
var text: String = dlg.preview_label.text
|
||
_check(text.contains("draußen") and text.contains("1 Objekt"), "Verlust-Liste nennt 1 Objekt draußen")
|
||
_check(text.contains("1 Bauteil"), "Verlust-Liste nennt 1 Bauteil draußen")
|
||
# 4 verschobene (Team Blau) + 4 Default-Punkte (Team Rot bei ±0,8 × 512 m) = 8 draußen.
|
||
_check(text.contains("8 Spawn-Punkt"), "Verlust-Liste nennt 8 Spawn-Punkte draußen")
|
||
_check(text.contains("ENTFERNT"), "Hinweis: draußen wird entfernt (Checkbox an)")
|
||
_check(text.contains("Team") and text.contains("4 pro Team"), "Spawn-Speicher-Warnung: Team unter 4 Punkte")
|
||
_check(dlg.get_drop_outside(), "Checkbox Entfernen ist Default-an")
|
||
|
||
# 2) Entfernen aus: draußen bleibt, Map ist dann nicht speicherbar (V9).
|
||
dlg.drop_check.button_pressed = false
|
||
dlg._update_preview()
|
||
text = dlg.preview_label.text
|
||
_check(text.contains("bleiben") and text.contains("Rand"), "Ohne Entfernen: draußen bleibt in der Map")
|
||
_check(text.contains("NICHT speicherbar"), "Ohne Entfernen: Warnung vor V9 (Speichern blockiert)")
|
||
_check(not text.contains("ENTFERNT"), "Kein Entfernen-Hinweis bei Checkbox aus")
|
||
dlg.drop_check.button_pressed = true
|
||
|
||
# 3) Verkleinern ohne Verlust: alles innen → Klartext „nichts verloren“.
|
||
var doc_leer := SvmapDocument.create_new("Leer", 512.0, "mittag")
|
||
var z0: Dictionary = doc_leer["spawn_zones"]["0"]
|
||
var z1: Dictionary = doc_leer["spawn_zones"]["1"]
|
||
for zone in [z0, z1]:
|
||
var pts2: Array = zone["points"]
|
||
for i in pts2.size():
|
||
pts2[i]["pos"][0] = 5.0 * float(i) # alle nah am Zentrum
|
||
pts2[i]["pos"][2] = 0.0 # Z ebenfalls rein (Default-Z wäre ±153 m)
|
||
zone["points"] = pts2
|
||
dlg.setup(doc_leer)
|
||
dlg.size_x_edit.value = 256.0
|
||
dlg.size_z_edit.value = 256.0
|
||
dlg._update_preview()
|
||
text = dlg.preview_label.text
|
||
_check(text.contains("nichts verloren"), "Verkleinern ohne Verlust: Klartext-Meldung")
|
||
|
||
# 4) Vergrößern > 1024 m: Warnband (Dichte grobt, Quest-Perf).
|
||
dlg.setup(SvmapDocument.create_new("Grow", 512.0, "mittag"))
|
||
dlg.size_x_edit.value = 2048.0
|
||
dlg.size_z_edit.value = 2048.0
|
||
dlg._update_preview()
|
||
text = dlg.preview_label.text
|
||
_check(text.contains("grober") and text.contains("Quest"), "Warnband bei Grow > 1024 m (Dichte + Quest-Perf)")
|
||
_check(text.contains("flaches Gras") or text.contains("Randzone"), "Grow-Hinweis: neue Randzone wird Gras")
|
||
dlg.free()
|
||
|
||
|
||
# -------------------------------------------------------- main.gd-Drähte ----
|
||
|
||
func _test_main_wiring() -> void:
|
||
print("Testgruppe: main.gd-Verdrahtung + Undo-Roundtrip")
|
||
var scene: PackedScene = load("res://scenes/main.tscn")
|
||
_check(scene != null, "scenes/main.tscn laedt")
|
||
if scene == null:
|
||
return
|
||
main = scene.instantiate()
|
||
root.add_child(main)
|
||
await process_frame
|
||
await process_frame
|
||
|
||
_check(main.resize_dialog != null, "Resize-Dialog in der Main-UI gebaut")
|
||
|
||
main._start_with_new_map()
|
||
var doc: Dictionary = main.doc
|
||
_check(is_equal_approx(float(doc["terrain"]["size_x_m"]), 1024.0), "Start-Map 1024 m")
|
||
|
||
# Team Blau komplett draußen schieben (Verkleinern 1024 → 256).
|
||
var zone0: Dictionary = doc["spawn_zones"]["0"]
|
||
var pts: Array = zone0["points"]
|
||
for i in pts.size():
|
||
pts[i]["pos"][0] = 500.0 + float(i) # drin (±512), aber draußen bei 256
|
||
zone0["points"] = pts
|
||
|
||
# Referenz-Sicherheit: history hält doc als Objekt (keine Kopie).
|
||
_check(is_same(main.history.doc, main.doc), "History haelt doc per Referenz")
|
||
|
||
# Vorher-Zustand speichern → später Byte-Identität prüfen.
|
||
var path_a := "/tmp/r3_vorher.svmap"
|
||
var path_b := "/tmp/r3_nach_undo.svmap"
|
||
var save_a := SvmapIO.save_to_file(path_a, doc)
|
||
_check(bool(save_a["ok"]), "Vorher-Zustand speicherbar")
|
||
|
||
# Dialog öffnen (Menü-Pfad): Info-Zeile + Preview gefüllt.
|
||
main._open_resize_dialog()
|
||
_check(main.resize_dialog.visible, "Menue-Eintrag oeffnet den Dialog")
|
||
_check(main.resize_dialog.info_label.text.contains("1024"), "Dialog zeigt aktuelle Groesse")
|
||
main.resize_dialog.size_x_edit.value = 256.0
|
||
main.resize_dialog.size_z_edit.value = 256.0
|
||
_check(main.resize_dialog.preview_label.text.contains("draußen"),
|
||
"Live-Preview listet Verlust beim Verkleinern")
|
||
|
||
# Bestätigen: resize_apply + EINE Undo-Einheit.
|
||
main._on_resize_confirmed()
|
||
var t: Dictionary = main.doc["terrain"]
|
||
_check(is_equal_approx(float(t["size_x_m"]), 256.0), "Terrain auf 256 m verkleinert")
|
||
_check((main.doc["spawn_zones"]["0"]["points"] as Array).is_empty(),
|
||
"Draußen liegende Spawn-Punkte entfernt (Drop an)")
|
||
_check(bool(main.dirty), "Dirty-Flag gesetzt")
|
||
_check(main.history.can_undo() and main.history.undo_desc().contains("256"),
|
||
"EINE Undo-Einheit: Karte auf 256 m")
|
||
_check(is_same(main.history.doc, main.doc), "doc-Identitaet bleibt beim Resize erhalten")
|
||
|
||
# Strg+Z: kompletter Vorher-Zustand, Map speichert BYTE-IDENTISCH.
|
||
main._do_undo()
|
||
var t2: Dictionary = main.doc["terrain"]
|
||
_check(is_equal_approx(float(t2["size_x_m"]), 1024.0), "Undo: Terrain wieder 1024 m")
|
||
_check((main.doc["spawn_zones"]["0"]["points"] as Array).size() == 4,
|
||
"Undo: entfernte Spawn-Punkte komplett zurueck")
|
||
var save_b := SvmapIO.save_to_file(path_b, main.doc)
|
||
_check(bool(save_b["ok"]), "Nach-Undo-Zustand speicherbar")
|
||
var bytes_a := FileAccess.get_file_as_bytes(path_a)
|
||
var bytes_b := FileAccess.get_file_as_bytes(path_b)
|
||
_check(bytes_a == bytes_b, "Undo-Roundtrip: alte Map BYTE-IDENTISCH")
|
||
|
||
# Strg+Y: Resize wieder hergestellt.
|
||
main._do_redo()
|
||
_check(is_equal_approx(float(main.doc["terrain"]["size_x_m"]), 256.0),
|
||
"Redo: Resize wiederhergestellt")
|
||
_check((main.doc["spawn_zones"]["0"]["points"] as Array).is_empty(),
|
||
"Redo: Spawn-Punkte wieder entfernt")
|
||
|
||
# Drop aus: draußen bleibt in der Map (Dialog-Checkbox aus) — dokumentiertes
|
||
# Verhalten: Map bleibt, ist ABER nicht speicherbar (V9: Spawn außerhalb der
|
||
# Match-Grenzen); der Dialog warnt davor.
|
||
main.resize_dialog.drop_check.button_pressed = false
|
||
main.resize_dialog.size_x_edit.value = 256.0
|
||
main.resize_dialog.size_z_edit.value = 256.0
|
||
# Erst Undo (zurück auf 1024 mit Punkten), dann Resize ohne Drop.
|
||
main._do_undo()
|
||
var before_count := (main.doc["spawn_zones"]["0"]["points"] as Array).size()
|
||
main._on_resize_confirmed()
|
||
_check((main.doc["spawn_zones"]["0"]["points"] as Array).size() == before_count,
|
||
"Drop aus: draußen liegende Spawn-Punkte bleiben in der Map")
|
||
_check(not bool(SvmapIO.save_to_file("/tmp/r3_drop_aus.svmap", main.doc)["ok"]),
|
||
"Drop aus + Punkte draußen: Speichern blockiert (V9), wie im Dialog gewarnt") |