Files
Grovewright/game/tests/support/test_files.gd
T
Steven Wroblewski dff213cc61 Testdateien: Löschen nur unter user://tests, Altlasten räumen, Wortlaut
user://tests liegt im echten Benutzerordner des Spiels, neben Spielstand,
Schlüssel und Aufzeichnungen des Spielers. TestFiles.remove_dir löschte
jeden Pfad, den es bekam; ein falsches Argument hätte echte Spielerdateien
gelöscht. Tests, die früh mit einem Laufzeitfehler abbrachen, ließen ihren
Ordner zurück (es lagen 15 solche Ordner im Benutzerordner).

- remove_dir vereinfacht den Pfad (simplify_path) und verweigert alles,
  was nicht user://tests oder darunter liegt, mit push_error und ohne zu
  löschen. Die Wurzel steht in der Konstante TestFiles.ROOT, die auch
  temporary_dir benutzt.
- run_tests.gd entfernt user://tests zu Beginn von _run_all, vor dem
  Fehlerzähler, aber nur wenn der Ordner existiert, weil Godot beim
  Auflisten eines fehlenden Ordners einen Fehler meldet.
- CLAUDE.md sagt jetzt, dass die Tests unter user://tests schreiben und
  nie die echten Dateien des Spielers anfassen.

Das Verweigern wurde einmal von Hand geprüft, mit einem Wegwerfskript auf
selbst angelegten Ordnern: Ohne Schutz löschte remove_dir sie, mit Schutz
bleiben sie samt Datei stehen und es erscheint ein Fehler, auch für einen
Geschwisterordner user://tests_probe und für user://tests/../…. Danach
waren die 15 Altlasten verschwunden und die Ausgabe fehlerfrei, auch ohne
vorhandenen Ordner user://tests.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 13:23:01 +02:00

30 lines
1.2 KiB
GDScript

class_name TestFiles
extends RefCounted
## Temporary folders below user://tests for tests that write files. That
## folder lies inside the game's real user folder, next to the player's own
## key, save and recordings, so nothing outside it may be deleted here.
## The only folder the tests write to and remove_dir deletes from.
const ROOT := "user://tests"
static func temporary_dir(label: String) -> String:
var path := ROOT.path_join("%s-%d" % [label, Time.get_ticks_usec()])
DirAccess.make_dir_recursive_absolute(path)
return path
## Deletes the folder `path` with everything in it. Refuses every path that is
## not ROOT or below it, with an error and without deleting anything: a wrong
## argument must never delete the player's real files.
static func remove_dir(path: String) -> void:
var clean := path.simplify_path()
if clean != ROOT and not clean.begins_with(ROOT + "/"):
push_error("TestFiles.remove_dir refuses %s: it is not %s or below it" % [path, ROOT])
return
for file in DirAccess.get_files_at(clean):
DirAccess.remove_absolute(clean.path_join(file))
for directory in DirAccess.get_directories_at(clean):
remove_dir(clean.path_join(directory))
DirAccess.remove_absolute(clean)