Files
CosmicClash/Game/tests/cases/test_project_settings.gd
T
Josh Creek 6320b982a8 fix(project): keep comments out of project.godot and guard the settings
Godot's ConfigFile writer does not round-trip comments in project.godot. An
observed rewrite deleted both `;` blocks outright and spliced the three-line
`#` block above run/main_scene.dedicated_server onto the setting's own line,
leaving it commented out — which would send dedicated builds to the
interactive main menu instead of server_boot.tscn, with nothing failing until
someone noticed a server process rendering a menu.

Move the explanations into the code that owns the settings (server_boot.gd for
the dedicated-server override, video_settings.gd for stretch mode and vsync)
so they cannot be destroyed by a rewrite, and leave project.godot holding only
assignments plus Godot's own regenerated header.

Add tests/cases/test_project_settings.gd as the backstop: the feature-override
assertions read project.godot as text and reject a line that has been folded
into a comment, since ProjectSettings resolves `key.<feature>` overrides at
load time and never exposes the suffixed key. Verified by reproducing the
exact corruption, which fails the test, and it also covers the Jolt physics
engine, the required autoloads, and that no test-hook autoload is ever shipped
registered.
2026-08-24 08:40:16 +01:00

91 lines
3.9 KiB
GDScript

extends "res://tests/test_case.gd"
# Guards the project settings that are load-bearing but easy to destroy
# silently. Godot's ConfigFile writer does not round-trip comments in
# project.godot: it drops `;` blocks outright, and a `#` block sitting directly
# above a setting can be spliced onto that setting's own line on rewrite, which
# comments the setting out. A dedicated build would then boot the interactive
# main menu instead of the server, and nothing would fail until someone noticed
# a server process rendering a menu.
#
# The explanations that used to live as comments beside these settings are now
# in the code that owns them — server_boot.gd and video_settings.gd.
#
# The feature-override assertions read project.godot as TEXT rather than through
# ProjectSettings. Godot resolves `key.<feature>` overrides at load time against
# the running build's own feature tags and does not expose the suffixed key, so
# get_setting("run/main_scene.dedicated_server") returns "" in a normal editor/
# headless run even when the line is perfectly intact. Reading the file also
# matches the actual threat, which is textual corruption of the file.
func _project_godot_lines() -> PackedStringArray:
var file := FileAccess.open("res://project.godot", FileAccess.READ)
if file == null:
return PackedStringArray()
return file.get_as_text().split("\n")
# True only if `key="value"` appears as a real, uncommented assignment. A line
# that got spliced into a `#`/`;` comment is deliberately NOT a match — that is
# precisely the corruption being guarded against.
func _has_setting_line(key: String, value: String) -> bool:
var wanted := "%s=\"%s\"" % [key, value]
for raw_line in _project_godot_lines():
var line := raw_line.strip_edges()
if line.begins_with("#") or line.begins_with(";"):
continue
if line == wanted:
return true
return false
func test_project_godot_is_readable() -> void:
# Everything below is vacuously true if the file could not be opened.
assert_true(not _project_godot_lines().is_empty(), "project.godot readable and non-empty")
func test_dedicated_server_feature_override_is_set() -> void:
# Consumed by dedicated exports; see server_boot.gd.
assert_true(
_has_setting_line("run/main_scene.dedicated_server", "res://scenes/server_boot.tscn"),
"run/main_scene.dedicated_server present and uncommented"
)
func test_training_feature_override_is_set() -> void:
assert_true(
_has_setting_line("run/main_scene.training", "res://scenes/training.tscn"),
"run/main_scene.training present and uncommented"
)
func test_physics_engine_is_jolt() -> void:
# The whole flight model and every trained policy assume Jolt. Silently
# reverting to Godot Physics would change ship/ball behaviour under bots
# trained against Jolt, without any other test failing on its own.
var engine: String = ProjectSettings.get_setting("physics/3d/physics_engine", "")
assert_eq(engine, "Jolt Physics", "3D physics engine")
func test_required_autoloads_are_registered() -> void:
# NetworkManager in particular is reached by name from many scripts; losing
# it from [autoload] fails only at the point of use, deep in a smoke test.
for autoload_name in ["GameSettings", "VideoSettings", "NetworkManager", "MatchNet", "MatchSim"]:
assert_true(
ProjectSettings.has_setting("autoload/" + autoload_name),
"autoload/%s registered" % autoload_name
)
func test_test_hook_autoloads_are_not_shipped() -> void:
# main_menu_test_hooks / lobby_test_hooks are added to [autoload] by hand
# when running those scene-level smoke tests, and must be removed again —
# see CLAUDE.md. Shipping one registered would run test code in the real
# game, so fail here rather than discovering it in a build.
for hook_name in ["MainMenuTestHooks", "LobbyTestHooks", "NetworkedMatchTestHooks"]:
assert_true(
not ProjectSettings.has_setting("autoload/" + hook_name),
"test hook autoload/%s must not be registered" % hook_name
)