155 lines
6.7 KiB
GDScript3
155 lines
6.7 KiB
GDScript3
|
|
extends SceneTree
|
||
|
|
|
||
|
|
# Headless test: run with
|
||
|
|
# godot --headless -s res://tests/test_setup_editor_delete.gd
|
||
|
|
# The map editor could write setups but never remove one, so a mistyped name
|
||
|
|
# or an abandoned experiment stayed in the pool forever - and every planet of
|
||
|
|
# that type kept drawing it. It also listed setups in a font small enough to
|
||
|
|
# be awkward to hit on a phone.
|
||
|
|
#
|
||
|
|
# Deleting is destructive and the list is the only place it can be triggered
|
||
|
|
# from, so the panel is explicitly MODAL about it: a tap loads, unless the
|
||
|
|
# delete button has been pressed first, and the panel says which it is.
|
||
|
|
#
|
||
|
|
# Covers:
|
||
|
|
# A. the load panel lists saved setups and a tap loads one (unarmed)
|
||
|
|
# B. arming changes what a tap means, and says so in the title
|
||
|
|
# C. an armed tap deletes the file and drops it from the registry
|
||
|
|
# D. one tap deletes one setup - it disarms itself afterwards
|
||
|
|
# E. deleting the setup currently being edited leaves a fresh one open,
|
||
|
|
# rather than one whose next save would recreate the file
|
||
|
|
# F. the list is laid out at a size worth tapping at
|
||
|
|
|
||
|
|
const SCRATCH_ID: String = "test_delete_scratch"
|
||
|
|
const SCRATCH_PATH: String = "res://PlanetSetups/%s.tres" % SCRATCH_ID
|
||
|
|
const KEEP_ID: String = "test_delete_keeper"
|
||
|
|
const KEEP_PATH: String = "res://PlanetSetups/%s.tres" % KEEP_ID
|
||
|
|
const MIN_LIST_FONT_SIZE: int = 30
|
||
|
|
|
||
|
|
var editor: Node
|
||
|
|
var elapsed: float = 0.0
|
||
|
|
var stage: int = 0
|
||
|
|
|
||
|
|
|
||
|
|
func _initialize() -> void:
|
||
|
|
_remove(SCRATCH_PATH)
|
||
|
|
_remove(KEEP_PATH)
|
||
|
|
PlanetSetups.rescan()
|
||
|
|
editor = load("res://PlanetSetupEditor.tscn").instantiate()
|
||
|
|
root.add_child(editor)
|
||
|
|
|
||
|
|
|
||
|
|
func _process(delta: float) -> bool:
|
||
|
|
elapsed += delta
|
||
|
|
if stage != 0:
|
||
|
|
return false
|
||
|
|
# _ready() (palette build, initial new_setup) needs a frame to flush -
|
||
|
|
# same hazard as tests/test_setup_editor.gd.
|
||
|
|
if elapsed < 0.1:
|
||
|
|
return false
|
||
|
|
stage = 1
|
||
|
|
|
||
|
|
# --- F. the list is tappable --------------------------------------------
|
||
|
|
var list: ItemList = editor.load_list
|
||
|
|
var font_size: int = list.get_theme_font_size("font_size")
|
||
|
|
if font_size < MIN_LIST_FONT_SIZE:
|
||
|
|
return _fail("the setup list draws at font size %d; %d or more is what makes the rows worth tapping at" \
|
||
|
|
% [font_size, MIN_LIST_FONT_SIZE])
|
||
|
|
print("setup list font size %d" % font_size)
|
||
|
|
|
||
|
|
# Two setups, so deleting one still leaves a populated list - a panel that
|
||
|
|
# empties would hide itself and mask a wrong-row deletion.
|
||
|
|
editor.new_setup(KEEP_ID, "Delete Keeper", "rocky")
|
||
|
|
editor.add_capture_point(Vector2(10, 20))
|
||
|
|
if not editor.save_setup().ok:
|
||
|
|
return _fail("could not save the keeper setup")
|
||
|
|
editor.new_setup(SCRATCH_ID, "Delete Scratch", "rocky")
|
||
|
|
editor.add_capture_point(Vector2(30, 40))
|
||
|
|
if not editor.save_setup().ok:
|
||
|
|
return _fail("could not save the scratch setup")
|
||
|
|
|
||
|
|
# --- A. the panel lists them, and an unarmed tap loads -------------------
|
||
|
|
editor._on_load_pressed()
|
||
|
|
if not editor.load_panel.visible:
|
||
|
|
return _fail("the load panel did not open")
|
||
|
|
if editor.load_title_label.text != "Load a setup":
|
||
|
|
return _fail("the panel opened in delete mode: '%s'" % editor.load_title_label.text)
|
||
|
|
var keep_index: int = Array(editor._load_list_setup_ids).find(KEEP_ID)
|
||
|
|
var scratch_index: int = Array(editor._load_list_setup_ids).find(SCRATCH_ID)
|
||
|
|
if keep_index < 0 or scratch_index < 0:
|
||
|
|
return _fail("the panel listed %s, missing one of the two just saved" \
|
||
|
|
% str(editor._load_list_setup_ids))
|
||
|
|
editor._on_load_list_item_selected(keep_index)
|
||
|
|
if String(editor.current_setup.setup_id) != KEEP_ID:
|
||
|
|
return _fail("an unarmed tap did not load '%s' (got '%s')" % [KEEP_ID, editor.current_setup.setup_id])
|
||
|
|
if editor.load_panel.visible:
|
||
|
|
return _fail("loading left the panel open")
|
||
|
|
print("unarmed tap loaded '%s' and closed the panel" % KEEP_ID)
|
||
|
|
|
||
|
|
# --- B. arming changes the meaning of a tap, visibly ---------------------
|
||
|
|
editor._on_load_pressed()
|
||
|
|
editor._on_delete_map_pressed()
|
||
|
|
if not editor._delete_map_armed:
|
||
|
|
return _fail("pressing the delete button did not arm delete mode")
|
||
|
|
if editor.load_title_label.text == "Load a setup":
|
||
|
|
return _fail("delete mode did not change the panel title, so nothing tells the player a tap now deletes")
|
||
|
|
print("armed: panel now reads '%s'" % editor.load_title_label.text)
|
||
|
|
|
||
|
|
# --- C. an armed tap deletes ---------------------------------------------
|
||
|
|
scratch_index = Array(editor._load_list_setup_ids).find(SCRATCH_ID)
|
||
|
|
editor._on_load_list_item_selected(scratch_index)
|
||
|
|
if FileAccess.file_exists(ProjectSettings.globalize_path(SCRATCH_PATH)):
|
||
|
|
return _fail("the setup file is still on disk after an armed tap")
|
||
|
|
for setup in PlanetSetups.all():
|
||
|
|
if String(setup.setup_id) == SCRATCH_ID:
|
||
|
|
return _fail("the registry still offers '%s' after deleting it" % SCRATCH_ID)
|
||
|
|
if Array(editor._load_list_setup_ids).find(KEEP_ID) < 0:
|
||
|
|
return _fail("deleting one setup also removed '%s' from the list" % KEEP_ID)
|
||
|
|
print("armed tap deleted '%s'; '%s' untouched" % [SCRATCH_ID, KEEP_ID])
|
||
|
|
|
||
|
|
# --- D. one tap, one deletion -------------------------------------------
|
||
|
|
if editor._delete_map_armed:
|
||
|
|
return _fail("delete mode stayed armed after a deletion, so the next tap would delete too")
|
||
|
|
if editor.load_title_label.text != "Load a setup":
|
||
|
|
return _fail("the panel still reads '%s' after disarming" % editor.load_title_label.text)
|
||
|
|
print("delete mode disarmed itself after one deletion")
|
||
|
|
|
||
|
|
# --- E. deleting what is open leaves a fresh setup ------------------------
|
||
|
|
editor.load_setup(KEEP_ID)
|
||
|
|
if String(editor.current_setup.setup_id) != KEEP_ID:
|
||
|
|
return _fail("could not reopen '%s' to test deleting the open setup" % KEEP_ID)
|
||
|
|
var outcome: Dictionary = editor.delete_setup(KEEP_ID)
|
||
|
|
if not outcome.ok:
|
||
|
|
return _fail("deleting the open setup failed: %s" % outcome.message)
|
||
|
|
if String(editor.current_setup.setup_id) == KEEP_ID:
|
||
|
|
return _fail("the editor is still holding '%s' after deleting it - the next save would put the file straight back" % KEEP_ID)
|
||
|
|
if not editor.current_setup.capture_points.is_empty():
|
||
|
|
return _fail("the replacement setup carried the deleted one's capture points over")
|
||
|
|
print("deleting the open setup left a fresh '%s' being edited" % editor.current_setup.setup_id)
|
||
|
|
|
||
|
|
# Deleting something that is not there is refused, not silently "ok".
|
||
|
|
if editor.delete_setup(SCRATCH_ID).ok:
|
||
|
|
return _fail("deleting an already-deleted setup reported success")
|
||
|
|
|
||
|
|
_remove(SCRATCH_PATH)
|
||
|
|
_remove(KEEP_PATH)
|
||
|
|
PlanetSetups.rescan()
|
||
|
|
print("TEST_SETUP_EDITOR_DELETE: PASS")
|
||
|
|
quit(0)
|
||
|
|
return true
|
||
|
|
|
||
|
|
|
||
|
|
func _remove(res_path: String) -> void:
|
||
|
|
var path: String = ProjectSettings.globalize_path(res_path)
|
||
|
|
if FileAccess.file_exists(path):
|
||
|
|
DirAccess.remove_absolute(path)
|
||
|
|
if FileAccess.file_exists(path + ".uid"):
|
||
|
|
DirAccess.remove_absolute(path + ".uid")
|
||
|
|
|
||
|
|
|
||
|
|
func _fail(message: String) -> bool:
|
||
|
|
_remove(SCRATCH_PATH)
|
||
|
|
_remove(KEEP_PATH)
|
||
|
|
print("TEST_SETUP_EDITOR_DELETE: FAIL (%s)" % message)
|
||
|
|
quit(1)
|
||
|
|
return true
|