Skip to content

Commit b519ed5

Browse files
coneilenCopilot
andcommitted
Relax only stressed remote bridge waits
Apply a bounded test-only timeout multiplier to the remote suite when it runs concurrently with privacy scans. Production defaults and the standalone strict suite remain unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Colin Neilens <coneilen@microsoft.com>
1 parent c4f7650 commit b519ed5

3 files changed

Lines changed: 43 additions & 18 deletions

File tree

Tools/windows/Tests/RemoteBridgePrivacyRace.Tests.ps1

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ if (-not $python) {
1111
throw "Python 3 was not found for the remote bridge race regression"
1212
}
1313

14-
function Start-CapturedProcess([string] $fileName, [string[]] $arguments) {
14+
function Start-CapturedProcess(
15+
[string] $fileName,
16+
[string[]] $arguments,
17+
[hashtable] $environment = @{}
18+
) {
1519
$startInfo = [Diagnostics.ProcessStartInfo]::new()
1620
$startInfo.FileName = $fileName
1721
$startInfo.UseShellExecute = $false
@@ -21,6 +25,9 @@ function Start-CapturedProcess([string] $fileName, [string[]] $arguments) {
2125
foreach ($argument in $arguments) {
2226
[void] $startInfo.ArgumentList.Add($argument)
2327
}
28+
foreach ($entry in $environment.GetEnumerator()) {
29+
$startInfo.Environment[$entry.Key] = $entry.Value
30+
}
2431
$process = [Diagnostics.Process]::new()
2532
$process.StartInfo = $startInfo
2633
[void] $process.Start()
@@ -51,7 +58,9 @@ $privacyProcessCount = [Math]::Min(24, [Math]::Max(4, $processorCount * 2))
5158
Write-Host "Remote bridge privacy race: processors=$processorCount, remote=$remoteProcessCount, privacy=$privacyProcessCount"
5259
$remoteProcesses = @(
5360
1..$remoteProcessCount | ForEach-Object {
54-
Start-CapturedProcess $python.Source $remoteArguments
61+
Start-CapturedProcess $python.Source $remoteArguments @{
62+
GRAPHCODE_REMOTE_BRIDGE_TEST_TIMEOUT_MULTIPLIER = "3"
63+
}
5564
}
5665
)
5766
$privacyProcesses = @(

Tools/windows/Tests/ValidationRunner.Tests.ps1

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ try {
107107
(Join-Path $repoRoot "Tools\windows\Tests\RemoteBridgePrivacyRace.Tests.ps1") -Raw
108108
if ($privacyRaceSource -notmatch '\$AvailableProcessorCount = \[Environment\]::ProcessorCount' -or
109109
$privacyRaceSource -notmatch '\$remoteProcessCount = 1' -or
110-
$privacyRaceSource -notmatch '\[Math\]::Min\(24, \[Math\]::Max\(4, \$processorCount \* 2\)\)') {
110+
$privacyRaceSource -notmatch '\[Math\]::Min\(24, \[Math\]::Max\(4, \$processorCount \* 2\)\)' -or
111+
$privacyRaceSource -notmatch 'GRAPHCODE_REMOTE_BRIDGE_TEST_TIMEOUT_MULTIPLIER = "3"') {
111112
throw "RED: remote bridge privacy race does not scale bounded concurrency to runner capacity"
112113
}
113114
if ($windowsWorkflow -notmatch "(?s)environment:.*Hardening\.Tests\.ps1 -Environment -SchemaOnly") {

investigation/spikes/remote-bridge/test_remote_bridge.py

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,15 @@
1313

1414

1515
SPIKE_ROOT = Path(__file__).resolve().parent
16-
THREAD_TIMEOUT = 5.0
16+
TIMEOUT_MULTIPLIER = float(
17+
os.environ.get("GRAPHCODE_REMOTE_BRIDGE_TEST_TIMEOUT_MULTIPLIER", "1")
18+
)
19+
if not math.isfinite(TIMEOUT_MULTIPLIER) or not 1 <= TIMEOUT_MULTIPLIER <= 10:
20+
raise ValueError(
21+
"GRAPHCODE_REMOTE_BRIDGE_TEST_TIMEOUT_MULTIPLIER must be between 1 and 10"
22+
)
23+
SOCKET_TIMEOUT = 1.0 * TIMEOUT_MULTIPLIER
24+
THREAD_TIMEOUT = 5.0 * TIMEOUT_MULTIPLIER
1725
sys.path.insert(0, str(SPIKE_ROOT))
1826

1927
from remote_bridge import ( # noqa: E402
@@ -40,6 +48,7 @@ def setUp(self):
4048
self.backend.address,
4149
ttl_seconds=30.0,
4250
previous_overlap_seconds=0.4,
51+
request_timeout=2.0 * TIMEOUT_MULTIPLIER,
4352
)
4453
self.bridge.start()
4554

@@ -57,7 +66,7 @@ def raw_request(
5766
omit_capability=False,
5867
):
5968
with socket.create_connection(
60-
(state["host"], state["port"]), timeout=1.0
69+
(state["host"], state["port"]), timeout=SOCKET_TIMEOUT
6170
) as connection:
6271
message = {
6372
"generation": generation or state["generation"],
@@ -99,21 +108,21 @@ def test_slow_drip_connections_are_bounded_by_cumulative_deadline(self):
99108
for _ in range(2):
100109
connection = socket.create_connection(
101110
(state["host"], state["port"]),
102-
timeout=1.0,
111+
timeout=SOCKET_TIMEOUT,
103112
)
104113
connection.sendall(b"\0")
105114
connections.append(connection)
106-
deadline = time.time() + 1
115+
deadline = time.time() + SOCKET_TIMEOUT
107116
while self.bridge.active_client_count < 2:
108117
if time.time() >= deadline:
109118
self.fail("bounded client workers did not start")
110119
time.sleep(0.01)
111120

112121
rejected = socket.create_connection(
113122
(state["host"], state["port"]),
114-
timeout=1.0,
123+
timeout=SOCKET_TIMEOUT,
115124
)
116-
rejected.settimeout(1.0)
125+
rejected.settimeout(SOCKET_TIMEOUT)
117126
try:
118127
self.assertEqual(rejected.recv(1), b"")
119128
finally:
@@ -125,15 +134,18 @@ def test_slow_drip_connections_are_bounded_by_cumulative_deadline(self):
125134
connection.sendall(b"\0")
126135
except OSError:
127136
pass
128-
expiry = time.monotonic() + 0.35
137+
expiry = time.monotonic() + 0.35 * TIMEOUT_MULTIPLIER
129138
while self.bridge.active_client_count:
130139
if time.monotonic() >= expiry:
131140
self.fail("slow-drip worker exceeded cumulative deadline plus scheduler margin")
132141
time.sleep(0.005)
133142
self.assertEqual(self.bridge.active_client_count, 0)
134143
stop_started = time.monotonic()
135144
self.bridge.stop()
136-
self.assertLess(time.monotonic() - stop_started, 1.0)
145+
self.assertLess(
146+
time.monotonic() - stop_started,
147+
1.0 * TIMEOUT_MULTIPLIER,
148+
)
137149
self.assertEqual(self.bridge.worker_count, 0)
138150
finally:
139151
for connection in connections:
@@ -144,18 +156,18 @@ def test_stop_closes_active_client_sockets(self):
144156
state = BridgeStateStore(self.state_path).read()
145157
connection = socket.create_connection(
146158
(state["host"], state["port"]),
147-
timeout=1.0,
159+
timeout=SOCKET_TIMEOUT,
148160
)
149161
connection.sendall(b"\0")
150-
deadline = time.time() + 1
162+
deadline = time.time() + SOCKET_TIMEOUT
151163
while self.bridge.active_client_count < 1:
152164
if time.time() >= deadline:
153165
connection.close()
154166
self.fail("client worker did not start")
155167
time.sleep(0.01)
156168

157169
self.bridge.stop()
158-
connection.settimeout(1.0)
170+
connection.settimeout(SOCKET_TIMEOUT)
159171
try:
160172
result = connection.recv(1)
161173
except ConnectionResetError:
@@ -215,7 +227,10 @@ def test_state_timestamps_must_be_finite(self):
215227
invalid_path.unlink(missing_ok=True)
216228

217229
def test_client_reads_state_and_bridges_framed_request_response(self):
218-
client = RemoteBridgeClient(self.state_path)
230+
client = RemoteBridgeClient(
231+
self.state_path,
232+
timeout=2.0 * TIMEOUT_MULTIPLIER,
233+
)
219234

220235
response = client.request({"command": "status"})
221236

@@ -226,7 +241,7 @@ def test_client_reads_state_and_bridges_framed_request_response(self):
226241
def test_authenticated_session_relays_multiple_frames_on_one_connection(self):
227242
state = BridgeStateStore(self.state_path).read()
228243
with socket.create_connection(
229-
(state["host"], state["port"]), timeout=1.0
244+
(state["host"], state["port"]), timeout=SOCKET_TIMEOUT
230245
) as connection:
231246
bodies = ({"command": "openProject"}, {"command": "status"})
232247
for body in bodies[:1]:
@@ -339,7 +354,7 @@ def test_malformed_frame_is_rejected(self):
339354
state = BridgeStateStore(self.state_path).read()
340355
payload = b"not-json"
341356
with socket.create_connection(
342-
(state["host"], state["port"]), timeout=1.0
357+
(state["host"], state["port"]), timeout=SOCKET_TIMEOUT
343358
) as connection:
344359
connection.sendall(len(payload).to_bytes(4, "big") + payload)
345360
response = read_frame(connection)
@@ -731,7 +746,7 @@ def read_states():
731746
def test_oversized_frame_is_rejected(self):
732747
state = BridgeStateStore(self.state_path).read()
733748
with socket.create_connection(
734-
(state["host"], state["port"]), timeout=1.0
749+
(state["host"], state["port"]), timeout=SOCKET_TIMEOUT
735750
) as connection:
736751
connection.sendall((1_048_577).to_bytes(4, "big"))
737752
response = read_frame(connection)

0 commit comments

Comments
 (0)