test(client): retry input-roundtrip server spawn on fresh port when it dies pre-connect
The server binds AFTER ~250ms of plugin/sim init, so a random-port collision (os error 98) escapes the 0.15s spawn-liveness check and surfaced as a hard test failure at the push gate. Death during the connect window now retries on a fresh port within MAX_PORT_ATTEMPTS. Verified: targeted suite 6/6. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -26,17 +26,13 @@ static func _random_test_port() -> int:
|
||||
|
||||
|
||||
func _spawn_server(server_path: String) -> bool:
|
||||
for attempt in range(MAX_PORT_ATTEMPTS):
|
||||
_test_port = _random_test_port()
|
||||
var addr := "127.0.0.1:%d" % _test_port
|
||||
_server_pid = OS.create_process(server_path, [addr])
|
||||
if _server_pid <= 0:
|
||||
continue
|
||||
await get_tree().create_timer(0.15).timeout
|
||||
if OS.is_process_running(_server_pid):
|
||||
return true
|
||||
_test_port = _random_test_port()
|
||||
var addr := "127.0.0.1:%d" % _test_port
|
||||
_server_pid = OS.create_process(server_path, [addr])
|
||||
if _server_pid <= 0:
|
||||
_server_pid = -1
|
||||
return false
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
func after_test() -> void:
|
||||
@@ -51,37 +47,48 @@ func after_test() -> void:
|
||||
## Spawn the server, connect, and complete the protocol handshake.
|
||||
## Binary absence is handled by per-test `_do_skip` — by the time this runs the
|
||||
## binary exists, so any failure here is a real failure (asserted loudly).
|
||||
## The server binds its port AFTER plugin/simulation init (~250ms), so a
|
||||
## random-port collision surfaces as a mid-connect process death, not an
|
||||
## instant spawn failure — a death during the connect window therefore
|
||||
## retries on a fresh port instead of failing the test (the push gate hit
|
||||
## exactly that race: "Failed to bind ... Address already in use").
|
||||
func _connect_to_server() -> bool:
|
||||
var server_path := _server_binary_path()
|
||||
|
||||
var spawned := await _spawn_server(server_path)
|
||||
assert_bool(spawned).override_failure_message(
|
||||
"server spawn failed after %d port attempts" % MAX_PORT_ATTEMPTS
|
||||
).is_true()
|
||||
if not spawned:
|
||||
return false
|
||||
|
||||
_bridge = LocalBridge.new()
|
||||
var connected := false
|
||||
var elapsed := 0.0
|
||||
while elapsed < CONNECT_TIMEOUT:
|
||||
if _server_pid > 0 and not OS.is_process_running(_server_pid):
|
||||
push_warning("Server process died during connection")
|
||||
for _attempt in range(MAX_PORT_ATTEMPTS):
|
||||
if not _spawn_server(server_path):
|
||||
continue
|
||||
_bridge = LocalBridge.new()
|
||||
var elapsed := 0.0
|
||||
while elapsed < CONNECT_TIMEOUT:
|
||||
if not OS.is_process_running(_server_pid):
|
||||
push_warning(
|
||||
"Server died pre-connect (port %d likely in use) — retrying" % _test_port
|
||||
)
|
||||
break
|
||||
if _bridge.get_status() == StreamPeerTCP.STATUS_NONE:
|
||||
_bridge.connect_to_server("127.0.0.1", _test_port)
|
||||
_bridge.poll()
|
||||
if _bridge.get_status() == StreamPeerTCP.STATUS_CONNECTED:
|
||||
connected = true
|
||||
break
|
||||
if _bridge.get_status() == StreamPeerTCP.STATUS_ERROR:
|
||||
_bridge.disconnect_from_server()
|
||||
_bridge.reset()
|
||||
await get_tree().create_timer(0.1).timeout
|
||||
elapsed += 0.1
|
||||
if connected:
|
||||
break
|
||||
if _bridge.get_status() == StreamPeerTCP.STATUS_NONE:
|
||||
_bridge.connect_to_server("127.0.0.1", _test_port)
|
||||
_bridge.poll()
|
||||
if _bridge.get_status() == StreamPeerTCP.STATUS_CONNECTED:
|
||||
connected = true
|
||||
break
|
||||
if _bridge.get_status() == StreamPeerTCP.STATUS_ERROR:
|
||||
_bridge.disconnect_from_server()
|
||||
_bridge.reset()
|
||||
await get_tree().create_timer(0.1).timeout
|
||||
elapsed += 0.1
|
||||
_bridge.disconnect_from_server()
|
||||
_bridge = null
|
||||
if _server_pid > 0 and OS.is_process_running(_server_pid):
|
||||
OS.kill(_server_pid)
|
||||
_server_pid = -1
|
||||
|
||||
assert_bool(connected).override_failure_message(
|
||||
"TCP connect to spawned server failed within %.1fs" % CONNECT_TIMEOUT
|
||||
"TCP connect to spawned server failed within %.1fs across %d port attempts"
|
||||
% [CONNECT_TIMEOUT, MAX_PORT_ATTEMPTS]
|
||||
).is_true()
|
||||
if not connected:
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user