Files
VRyHungry1/test/run_tests_in_editor.gd
T
algodoogle 96ea2dd1ea mp fix
2026-07-28 19:41:31 +01:00

138 lines
5.0 KiB
GDScript

@tool
extends EditorScript
## Runs the whole multiplayer test suite from inside the Godot editor.
##
## HOW TO USE
## 1. Open this file in the editor's script panel.
## 2. File > Run (Ctrl+Shift+X).
##
## It launches two copies of the game (server + client) as separate processes,
## waits for them to finish, then prints the full pass/fail report into the
## Output panel and builds the side-by-side GIF.
##
## Everything it produces lands in logs/:
## mptest_report.txt every check, pass/fail, plus details for failures
## mptest_run.gif both peers side by side, one frame per step
## mptest_{server,client}.log full step logs
##
## NOTE: the editor is blocked while the run is in progress (a couple of
## minutes). Watch the two game windows to follow along.
## Give up if the server hasn't finished in this long.
const TIMEOUT_SEC := 400
## Seconds each step is held, so the run is watchable and the GIF is readable.
const STEP_PAUSE := 0.4
## Seconds the final state stays on screen before the instances close.
const END_HOLD := 3.0
## The peers load different scenes on purpose, mirroring the real game: the host
## opens whatever world it is running, the client opens the bare multiplayer
## scene and has to receive the whole layout over the network — the same path a
## client joining mid-session takes.
const SERVER_SCENE := "res://test/multiPlayerTest.tscn"
const CLIENT_SCENE := "res://Scenes/multiPlayer.tscn"
func _run() -> void:
var exe := OS.get_executable_path()
var project_dir := ProjectSettings.globalize_path("res://").rstrip("/")
var logs_dir := ProjectSettings.globalize_path("res://logs")
DirAccess.make_dir_recursive_absolute(logs_dir)
_clear_previous_results(logs_dir)
print_rich("[b]Running the multiplayer test suite...[/b]")
print(" the editor will be unresponsive until it finishes (~2 minutes)")
var server_pid := _launch(exe, project_dir, ["--server"], SERVER_SCENE)
if server_pid <= 0:
push_error("Could not start the server instance")
return
# Give the host time to come up and open its port before the client dials in.
OS.delay_msec(5000)
var client_pid := _launch(exe, project_dir, ["--join", "127.0.0.1"], CLIENT_SCENE)
if client_pid <= 0:
push_error("Could not start the client instance")
OS.kill(server_pid)
return
var waited := 0.0
while OS.is_process_running(server_pid) and waited < TIMEOUT_SEC:
OS.delay_msec(500)
waited += 0.5
if OS.is_process_running(server_pid):
print(" timed out after %ds, stopping the instances" % TIMEOUT_SEC)
OS.kill(server_pid)
if OS.is_process_running(client_pid):
OS.kill(client_pid)
OS.delay_msec(500)
_print_report(logs_dir)
_build_gif(project_dir, logs_dir)
func _launch(exe: String, project_dir: String, extra: Array, scene: String) -> int:
var args := PackedStringArray([
"--xr-mode", "off", "--resolution", "900x600",
"--path", project_dir, scene, "--",
])
for a in extra:
args.append(a)
# --mptest runs the scripted sequence; --mptest-frames captures the frame per
# step that the GIF is stitched from (needs a real window, hence no
# --headless here).
args.append_array(PackedStringArray([
"--mptest", "--mptest-frames",
"--mptest-pause", str(STEP_PAUSE), "--mptest-hold", str(END_HOLD),
]))
return OS.create_process(exe, args)
# Remove the previous run's output so a failed launch can't leave stale results
# looking like this run's.
func _clear_previous_results(logs_dir: String) -> void:
for name in ["mptest_report.txt", "mptest_run.gif", "mptest_server.log", "mptest_client.log"]:
DirAccess.remove_absolute(logs_dir.path_join(name))
for role in ["server", "client"]:
var frames := logs_dir.path_join("mptest_frames_%s" % role)
var dir := DirAccess.open(frames)
if dir:
for f in dir.get_files():
if f.ends_with(".png"):
dir.remove(f)
func _print_report(logs_dir: String) -> void:
var path := logs_dir.path_join("mptest_report.txt")
if not FileAccess.file_exists(path):
push_error("No report at %s — the run did not finish. Check logs/mptest_server.log" % path)
return
var text := FileAccess.get_file_as_string(path)
print("")
# Colour the summary so a failure is obvious in the Output panel.
for line in text.split("\n"):
if line.begins_with("FAIL") or line.contains("RESULT: FAILED"):
print_rich("[color=red]%s[/color]" % line)
elif line.contains("ALL CHECKS PASSED"):
print_rich("[color=green][b]%s[/b][/color]" % line)
elif line.begins_with("PASS"):
print_rich("[color=gray]%s[/color]" % line)
else:
print(line)
print("report: %s" % path)
func _build_gif(project_dir: String, logs_dir: String) -> void:
var script := project_dir.path_join("test/make_gif.ps1")
var out := []
var code := OS.execute("powershell", [
"-ExecutionPolicy", "Bypass", "-File", script,
], out, true)
for line in out:
print(line)
var gif := logs_dir.path_join("mptest_run.gif")
if code == 0 and FileAccess.file_exists(gif):
print_rich("[b]gif:[/b] %s" % gif)
else:
push_warning("GIF was not produced (is ffmpeg on PATH?). See the output above.")