scopedvr-map-editor/tests/test_map_tint.gd

242 lines
9.8 KiB
GDScript

extends SceneTree
## Editor-Test (headless): Farbe je Objekt (Tint) im Inspektor + doc + View —
## Karte t_b0edb807.
## Ausführen: godot --headless --path . --script res://tests/test_map_tint.gd
## Exit 0 = alles grün.
##
## T1 Palette: 7 feste Farben mit den vereinbarten Namen.
## T2 Farbfeld-Klick → doc-Feld "tint" ([r,g,b], 0..1) + Status „Farbe: Oliv“;
## gleiche Farbe erneut = KEIN Leer-Undo-Schritt.
## T3 Editor-Ansicht: unmarkiertes Objekt zeigt tint (Surface-Override-
## Material albedo = doc-Wert); Auswahl-Orange gewinnt weiter
## (material_override), nach dem Abwählen ist die Farbe wieder sichtbar.
## T4 Mehrfachauswahl: eine Farbe auf beide Objekte = GENAU EIN Undo-Schritt.
## T5 Zurücksetzen (Pack-Farbe): Feld entfernt, Override weg, EIN Undo-Schritt.
## T6 save→load-Roundtrip: tint überlebt; alte Karte ohne Feld lädt
## unverändert (Validierung ohne Fehler, kein Feld erfunden).
var failures := 0
var checks := 0
var main
var insp
func _initialize() -> void:
_run_all.call_deferred()
func _check(cond: bool, label: String) -> void:
checks += 1
print("%s: %s" % ["ok " if cond else "FAIL", label])
if not cond:
failures += 1
func _undo_steps_to_clear() -> int:
var n := 0
while main.history.can_undo() and n < 50:
main.call("_do_undo")
n += 1
return n
func _sel_obj(idx: int) -> Dictionary:
return (main.doc["objects"] as Array)[idx]
func _swatch(name: String) -> Button:
for kind in insp.get("_farben_grid").get_children():
if kind is Button and str(kind.tooltip_text).begins_with(name):
return kind
return null
func _marker_mi(idx: int) -> MeshInstance3D:
var marker = main.view_builder.object_markers[idx]
return marker.get_child(0) as MeshInstance3D
func _run_all() -> void:
var scene: PackedScene = load("res://scenes/main.tscn")
_check(scene != null, "scenes/main.tscn laedt")
if scene == null:
_finish()
return
main = scene.instantiate()
root.add_child(main)
await process_frame
await process_frame
insp = main.get("inspector")
_check(insp != null, "Inspektor ist in main eingebaut")
if insp == null:
_finish()
return
# Zwei Objekte ins Doc: barrel_001 (echtes GLB) + crate_wood (Platzhalter).
(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": "crate_wood", "pos": [-6.0, 0.0, 2.0], "scale": 2.0, "rot_y_deg": 10.0})
main.call("_after_structure_change")
main.history.clear()
await process_frame
await _t1_palette()
await _t2_farbe_doc()
await _t3_editor_ansicht()
await _t4_mehrfach()
await _t5_reset()
await _t6_roundtrip()
_finish()
func _t1_palette() -> void:
var palette: Array = SvmapTint.PALETTE
_check(palette.size() == 7, "T1 Palette hat 7 Farben (ist %d)" % palette.size())
var namen := PackedStringArray()
for e in palette:
namen.append(str(e[0]))
var soll := ["Wüstensand", "Oliv", "Tarn-Grün", "Stahlgrau", "Holzbraun", "Dunkelbraun", "Weiß"]
var alle := true
for n in soll:
if not namen.has(n):
alle = false
_check(alle, "T1 Paletten-Namen wie auf der Karte (%s)" % str(namen))
# Farbstatus ohne Auswahl/ohne Feld.
main.interaction.call("select_object", 0)
await process_frame
_check(str(insp.get("_farbe_status").text) == "Farbe: Pack-Farbe",
"T1 Status ohne tint = „Farbe: Pack-Farbe“ (ist „%s“)" % str(insp.get("_farbe_status").text))
_check(insp.get("_farben_grid").get_child_count() == 7, "T1 Farbfeld-Raster zeigt 7 Felder")
func _t2_farbe_doc() -> void:
main.interaction.call("select_object", 0)
await process_frame
var oliv: Color = SvmapTint.PALETTE[1][1]
insp.call("_on_farbe_gewaehlt", oliv, "Oliv")
var t: Variant = _sel_obj(0).get("tint", null)
_check(SvmapTint.gueltig(t), "T2 Klick auf Oliv schreibt tint-Feld ins doc")
var arr: Array = t
_check(absf(float(arr[0]) - oliv.r) < 0.001 and absf(float(arr[1]) - oliv.g) < 0.001
and absf(float(arr[2]) - oliv.b) < 0.001,
"T2 tint = Anzeige-Farbwerte der Palette (%s)" % str(arr))
_check(str(insp.get("_farbe_status").text) == "Farbe: Oliv", "T2 Status zeigt „Farbe: Oliv“")
# Gleiche Farbe erneut → kein Leer-Undo-Schritt.
main.history.clear()
insp.call("_on_farbe_gewaehlt", oliv, "Oliv")
_check(not main.history.can_undo(), "T2 gleiche Farbe erneut = KEIN Leer-Schritt")
# Anderer Farbe → EIN Schritt; Undo stellt die VORHERIGE Farbe (Oliv) her.
insp.call("_on_farbe_gewaehlt", SvmapTint.PALETTE[6][1], "Weiß")
_check(_undo_steps_to_clear() == 1, "T2 Farbwechsel = GENAU EIN Undo-Schritt")
_check(SvmapTint.gleich(SvmapTint.von(_sel_obj(0)), oliv),
"T2 Undo stellt die vorherige Farbe (Oliv) wieder her")
func _t3_editor_ansicht() -> void:
# Farbe setzen (Objekt ist markiert → orange gewinnt, doc hat das Feld).
main.interaction.call("select_object", 0)
await process_frame
insp.call("_on_farbe_gewaehlt", SvmapTint.PALETTE[3][1], "Stahlgrau")
var c: Color = SvmapTint.von(_sel_obj(0))
var mi := _marker_mi(0)
_check(mi.material_override != null, "T3 markiertes Objekt bleibt orange (material_override an)")
# Abwählen → Farbe sichtbar: Surface-Override mit albedo = doc-Wert.
main.interaction.call("clear_selection", true)
await process_frame
var ov := mi.get_surface_override_material(0)
_check(ov is BaseMaterial3D, "T3 abgewählt: Surface-Override-Material gesetzt")
if ov is BaseMaterial3D:
var ab: Color = (ov as BaseMaterial3D).albedo_color
_check(SvmapTint.gleich(ab, c), "T3 albedo des Overrides = doc-Farbe (%s vs %s)" % [str(ab), str(c)])
# Platzhalter-Objekt (crate_wood) bekommt die Farbe ebenfalls.
main.interaction.call("select_object", 1)
await process_frame
insp.call("_on_farbe_gewaehlt", SvmapTint.PALETTE[4][1], "Holzbraun")
main.interaction.call("clear_selection", true)
await process_frame
var mi1 := _marker_mi(1)
var ov1 := mi1.get_surface_override_material(0)
_check(ov1 is BaseMaterial3D and SvmapTint.gleich((ov1 as BaseMaterial3D).albedo_color,
SvmapTint.von(_sel_obj(1))), "T3 Platzhalter zeigt doc-Farbe (Holzbraun)")
# Orange gewinnt weiter, FARBE KOMMT ZURÜCK beim Abwählen (Auswahl des
# Objekts 0 lässt 1 unberührt).
main.interaction.call("select_object", 1)
await process_frame
_check(_marker_mi(1).material_override != null, "T3 Auswahl-Orange gewinnt weiter")
func _t4_mehrfach() -> void:
main.interaction.call("clear_selection", true)
main.interaction.call("select_many", [0, 1])
await process_frame
main.history.clear()
insp.call("_on_farbe_gewaehlt", SvmapTint.PALETTE[0][1], "Wüstensand")
_check(SvmapTint.hat(_sel_obj(0)) and SvmapTint.hat(_sel_obj(1)),
"T4 Mehrfach: beide Objekte haben tint")
_check(SvmapTint.gleich(SvmapTint.von(_sel_obj(0)), SvmapTint.von(_sel_obj(1))),
"T4 Mehrfach: beide dieselbe Farbe")
_check(_undo_steps_to_clear() == 1, "T4 Mehrfach-Farbe = GENAU EIN Undo-Schritt")
# Undo stellt die vorherigen Farben wieder her (T3: Stahlgrau / Holzbraun).
_check(SvmapTint.gleich(SvmapTint.von(_sel_obj(0)), SvmapTint.PALETTE[3][1])
and SvmapTint.gleich(SvmapTint.von(_sel_obj(1)), SvmapTint.PALETTE[4][1]),
"T4 Undo stellt die vorherigen Farben wieder her")
func _t5_reset() -> void:
main.interaction.call("select_many", [0, 1])
await process_frame
insp.call("_on_farbe_gewaehlt", SvmapTint.PALETTE[2][1], "Tarn-Grün")
insp.call("_on_farbe_reset")
_check(not SvmapTint.hat(_sel_obj(0)) and not SvmapTint.hat(_sel_obj(1)),
"T5 Reset entfernt tint-Felder")
_check(str(insp.get("_farbe_status").text) == "Farbe: Pack-Farbe",
"T5 Status zeigt „Farbe: Pack-Farbe“")
# Farbe setzen und Zurücksetzen sind ZWEI getrennte Nutzeraktionen
# (= 2 Undo-Schritte); die FARBÄNDERUNG selbst ist genau 1 Schritt.
_check(_undo_steps_to_clear() == 2, "T5 Farbe + Reset = 2 getrennte Undo-Schritte")
# Reset ohne vorhandene Farbe = kein Leer-Schritt: Feld neu setzen,
# History leeren, Reset → ohne Änderung kein Commit.
main.interaction.call("select_object", 0)
await process_frame
insp.call("_on_farbe_gewaehlt", SvmapTint.PALETTE[5][1], "Dunkelbraun")
main.history.clear()
insp.call("_on_farbe_reset")
_check(not SvmapTint.hat(_sel_obj(0)), "T5 Reset entfernt das Dunkelbraun-Feld")
main.history.clear()
insp.call("_on_farbe_reset")
_check(not main.history.can_undo(), "T5 Reset ohne Farbe = KEIN Leer-Schritt")
main.interaction.call("clear_selection", true)
await process_frame
_check(_marker_mi(0).get_surface_override_material(0) == null,
"T5 keine Rest-Overrides nach Reset (Pack-Farbe sichtbar)")
func _t6_roundtrip() -> void:
# tint in doc → save → load: Feld überlebt bitgenau (gerundet).
insp.call("_on_farbe_gewaehlt", SvmapTint.PALETTE[1][1], "Oliv")
var pfad: String = ProjectSettings.globalize_path("user://tint_test.svmap")
var save_res: Dictionary = SvmapIO.save_to_file("user://tint_test.svmap", main.doc)
_check(save_res.ok, "T6 save_to_file ok (%s)" % str(save_res.get("errors", [])))
var geladen: Dictionary = SvmapIO.load_from_file("user://tint_test.svmap")
_check(bool(geladen.get("ok", false)), "T6 load_from_file ok (%s)" % str(geladen.get("errors", [])))
if bool(geladen.get("ok", false)):
var objs: Array = (geladen["data"] as Dictionary)["objects"]
_check(SvmapTint.gleich(SvmapTint.von(objs[0]), SvmapTint.von(_sel_obj(0))),
"T6 tint überlebt den Roundtrip unverändert")
# Alte Karte ohne Feld: Validierung ohne Fehler, View ohne Override.
var alte: Dictionary = main.doc.duplicate(true)
for obj in (alte["objects"] as Array):
SvmapTint.entferne(obj)
SvmapIO.sign_map(alte)
var fehler: PackedStringArray = SvmapIO.validate_map(alte)
_check(fehler.is_empty(), "T6 Karte ohne tint validiert ohne Fehler (%s)" % str(fehler))
if FileAccess.file_exists(pfad):
DirAccess.remove_absolute(pfad)
func _finish() -> void:
print("--------------------------------------------------")
print("CHECKS: %d FAILURES: %d" % [checks, failures])
quit(0 if failures == 0 else 1)