Skip to content

Commit 2043704

Browse files
coneilenCopilot
andcommitted
Fail fast on remote bridge thread leaks
Make test coordination threads daemonized and assert every timed join completes so a passing unittest run cannot leave Python blocked during hosted validation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Colin Neilens <coneilen@microsoft.com>
1 parent 847513c commit 2043704

1 file changed

Lines changed: 17 additions & 8 deletions

File tree

investigation/spikes/remote-bridge/test_remote_bridge.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -373,11 +373,12 @@ def replace_state():
373373
store.write(replacement)
374374
replacement_done.set()
375375

376-
replacement_thread = threading.Thread(target=replace_state)
376+
replacement_thread = threading.Thread(target=replace_state, daemon=True)
377377
replacement_thread.start()
378378
self.bridge.stop()
379379
replacement_thread.join(1)
380380

381+
self.assertFalse(replacement_thread.is_alive())
381382
self.assertTrue(replacement_done.is_set())
382383
self.assertEqual(store.read(), replacement)
383384

@@ -509,10 +510,10 @@ def rotate():
509510
finally:
510511
rotation_done.set()
511512

512-
holder = threading.Thread(target=hold_state_lock)
513+
holder = threading.Thread(target=hold_state_lock, daemon=True)
513514
holder.start()
514515
lock_ready.wait(1)
515-
rotation = threading.Thread(target=rotate)
516+
rotation = threading.Thread(target=rotate, daemon=True)
516517
rotation.start()
517518
time.sleep(0.2)
518519
self.assertFalse(rotation_done.is_set())
@@ -521,6 +522,8 @@ def rotate():
521522
holder.join(1)
522523
rotation.join(1)
523524

525+
self.assertFalse(holder.is_alive())
526+
self.assertFalse(rotation.is_alive())
524527
self.assertEqual(rotation_errors, [])
525528
state = BridgeStateStore(self.state_path).read()
526529
self.assertGreaterEqual(state["issued_at"], release_at)
@@ -590,7 +593,7 @@ def start_bridge(bridge):
590593
outcomes.append((bridge, "rejected"))
591594

592595
threads = [
593-
threading.Thread(target=start_bridge, args=(bridge,))
596+
threading.Thread(target=start_bridge, args=(bridge,), daemon=True)
594597
for bridge in bridges
595598
]
596599
for thread in threads:
@@ -599,6 +602,7 @@ def start_bridge(bridge):
599602
for thread in threads:
600603
thread.join(1)
601604

605+
self.assertTrue(all(not thread.is_alive() for thread in threads))
602606
started = [bridge for bridge, result in outcomes if result == "started"]
603607
rejected = [
604608
bridge for bridge, result in outcomes if result == "rejected"
@@ -644,8 +648,8 @@ def stop_bridge():
644648
finally:
645649
stop_done.set()
646650

647-
starter = threading.Thread(target=start_bridge)
648-
stopper = threading.Thread(target=stop_bridge)
651+
starter = threading.Thread(target=start_bridge, daemon=True)
652+
stopper = threading.Thread(target=stop_bridge, daemon=True)
649653
starter.start()
650654
publication_started.wait(1)
651655
stopper.start()
@@ -655,6 +659,8 @@ def stop_bridge():
655659
starter.join(1)
656660
stopper.join(1)
657661

662+
self.assertFalse(starter.is_alive())
663+
self.assertFalse(stopper.is_alive())
658664
self.assertEqual(errors, [])
659665
self.assertTrue(stop_done.is_set())
660666
self.assertEqual(self.bridge.active_client_count, 0)
@@ -705,7 +711,9 @@ def read_states():
705711
errors.append(str(error))
706712
time.sleep(0.005)
707713

708-
readers = [threading.Thread(target=read_states) for _ in range(3)]
714+
readers = [
715+
threading.Thread(target=read_states, daemon=True) for _ in range(3)
716+
]
709717
for reader in readers:
710718
reader.start()
711719
try:
@@ -714,8 +722,9 @@ def read_states():
714722
finally:
715723
stop_readers.set()
716724
for reader in readers:
717-
reader.join()
725+
reader.join(1)
718726

727+
self.assertTrue(all(not reader.is_alive() for reader in readers))
719728
self.assertEqual(errors, [])
720729

721730
def test_oversized_frame_is_rejected(self):

0 commit comments

Comments
 (0)