mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-10 16:04:04 +00:00
4533da34e0
Lands tasks 1.0-1.8 of multiplayer-todo.md: the pure-function test runner, net_codec (wire format quantizers/pack-unpack), NetworkManager (ENet transport, manual polling, min-RTT clock sync), MatchNet (handshake, protocol/tick-rate gating, roster with team+ready state), lobby.tscn (team columns, switch team, ready toggle), server_boot.tscn (headless dedicated server with structured logging and an overrun watchdog), and main_menu.gd's Host/Join-by-IP UI (connecting overlay, cancel, bounded failure path). Followed by an adversarial review (Opus subagent) that found and fixed two real bugs - an unvalidated player_name broadcast that let one client's oversized name head-of-line-block the reliable channel for everyone, and a server-side roster leak across a host/re-host cycle - plus three gaps in the test suite itself where a claim of "verified" wasn't actually backed by what the test checked. All five two-process smoke tests plus the pure-function suite are green with the strengthened assertions in place.
39 lines
1.7 KiB
GDScript
39 lines
1.7 KiB
GDScript
extends RefCounted
|
|
|
|
# Base class for pure-function unit tests run by test_runner.gd. A test case
|
|
# script extends this and defines any number of test_*() methods; the runner
|
|
# discovers them by name, not by registration, so adding a test is just
|
|
# adding a method.
|
|
#
|
|
# Test case scripts should `extends "res://tests/test_case.gd"` (path-based),
|
|
# not `extends TestCase` (the bare class_name). On a fresh headless run the
|
|
# global script class cache isn't guaranteed populated yet, so a bare-name
|
|
# reference can fail to resolve; the path form and test_runner.gd's own
|
|
# `preload()` both sidestep that.
|
|
|
|
var failures: Array[String] = []
|
|
# Adversarial-review regression: GDScript has no exceptions, so a runtime
|
|
# error partway through a test method (e.g. a null dereference) just logs a
|
|
# SCRIPT ERROR and returns — `failures` stays empty exactly as if every
|
|
# assertion had passed, and the runner counted it as a PASS. assertions_made
|
|
# is incremented by every assert_* call; test_runner.gd now treats a test
|
|
# that completes with zero assertions as a failure in its own right, so a
|
|
# test that crashes before reaching its first assert_* can no longer read
|
|
# as a silent pass.
|
|
var assertions_made := 0
|
|
|
|
func assert_true(condition: bool, message: String) -> void:
|
|
assertions_made += 1
|
|
if not condition:
|
|
failures.append(message)
|
|
|
|
func assert_eq(actual, expected, message: String) -> void:
|
|
assertions_made += 1
|
|
if actual != expected:
|
|
failures.append("%s: expected %s, got %s" % [message, expected, actual])
|
|
|
|
func assert_almost_eq(actual: float, expected: float, tolerance: float, message: String) -> void:
|
|
assertions_made += 1
|
|
if absf(actual - expected) > tolerance:
|
|
failures.append("%s: expected %s ± %s, got %s" % [message, expected, tolerance, actual])
|