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://surface/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