extends SceneTree # Headless test: run with # godot --headless -s res://tests/test_definitions_intact.gd # # A guard against a failure mode that has now cost this project three times, # and every time silently. # # Opening the project in the Godot editor re-saves .tres files, and its writer # DROPS properties it considers default-valued. An empty-looking # PackedStringArray is the usual casualty: GasBurnerBuildingDefinition lost # `capabilities` and `collected_outputs` once (which took the gas burner's # whole conversion with it), and InterceptorVesselDefinition later lost # `capabilities`, `hull_points`, `build_time`, `icon` and `glow_color` - which # cost the starting vessel its cargo hold, so nothing could be mined. # # Neither showed up as an error. The resource still loaded; it was just # quietly less than it had been, and the failure surfaced somewhere else # entirely - a full suite reporting ten unrelated tests broken. # # The checks themselves have moved to core/CatalogCheck.gd, because #173 was # the same damage a THIRD time and reached a shipped web build: a fleet that # could not store any resources. A test only guards the tree at the moment # somebody runs it, and an export is taken from the tree at whatever moment # the editor last touched it. The same function now runs at startup in every # build (GameState._ready) and here, so the two can never drift apart and a # damaged build says so on its own. # # Covers: # A. the catalogue is whole - every vessel and building carries the things # that make it what it is, and the paired facts agree # B. the shipped build asks the same question, from the same code, so this # test is not a promise the game does not keep var stage: int = 0 func _initialize() -> void: # --- A. nothing has been eaten --------------------------------------- var faults: PackedStringArray = CatalogCheck.faults() if not faults.is_empty(): for message in faults: print(" - %s" % message) print("TEST_DEFINITIONS_INTACT: FAIL (%d definition(s) came back incomplete)" \ % faults.size()) quit(1) return print("%d vessels and %d buildings, all intact" \ % [VesselDefinitions.all().size(), BuildingDefinitions.all().size()]) func _process(_delta: float) -> bool: if stage != 0: return false stage = 1 # --- B. and the build itself checked, from the same code ------------- # The whole point of #173 is that the suite is not where this damage gets # noticed. GameState runs the check on startup and keeps what it found, so # a build with an eaten .tres reports it instead of quietly failing to # mine - on the web, into the on-page error log. var game_state: Node = root.get_node("GameState") if not (game_state.catalog_faults is PackedStringArray): return _fail("GameState does not record what the startup check found") if not game_state.catalog_faults.is_empty(): return _fail("the running build reports %d catalog fault(s) this test did not" \ % game_state.catalog_faults.size()) var source: String = FileAccess.get_file_as_string("res://core/GameState.gd") if "CATALOG_CHECK.faults()" not in source or "push_error" not in source: return _fail("GameState no longer runs the catalog check at startup, so a damaged build would ship silently again") print("the running build ran the same check on startup and found nothing") print("TEST_DEFINITIONS_INTACT: PASS") quit(0) return true func _fail(message: String) -> bool: print("TEST_DEFINITIONS_INTACT: FAIL (%s)" % message) quit(1) return true