Skip to content

Commit 8c2fd1c

Browse files
test: poll for large-frame arrival instead of a fixed settle window (#62)
The large-raw-frame roundtrips (test_large_frame_above_data_packet_cap, test_4k_frame_roundtrip[raw]) flaked because they slept a fixed window then asserted the frame had arrived. A 25 MB raw frame takes ~4.6s over the byte-stream path, essentially equal to the 4.6s window, so under any load the assert raced the transfer and failed. The transfer itself is correct and byte-exact. Add a `wait_for` polling helper to conftest and use it in both tests: poll for arrival up to a generous ceiling (15s / 30s) instead of racing a fixed sleep. Returns as soon as the frame lands, so the fast path stays fast and only genuine failures wait out the timeout. Fixes #60
1 parent 5d18112 commit 8c2fd1c

3 files changed

Lines changed: 38 additions & 8 deletions

File tree

python/packages/livekit-portal/tests/integration/conftest.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,31 @@
4949
)
5050

5151

52+
async def wait_for(
53+
predicate,
54+
timeout_s: float = 15.0,
55+
interval_s: float = 0.05,
56+
) -> bool:
57+
"""Poll `predicate` until it returns truthy or `timeout_s` elapses, then
58+
return its final value.
59+
60+
Use this instead of a fixed `asyncio.sleep` before a receive assertion.
61+
Large byte-stream payloads (e.g. a 25 MB raw frame) can take several
62+
seconds to traverse the SFU, so a fixed settle window races the transfer
63+
and flakes under load. Polling returns as soon as the data arrives and
64+
only waits out the full timeout on genuine failure.
65+
"""
66+
import asyncio
67+
68+
loop = asyncio.get_running_loop()
69+
deadline = loop.time() + timeout_s
70+
while loop.time() < deadline:
71+
if predicate():
72+
return True
73+
await asyncio.sleep(interval_s)
74+
return bool(predicate())
75+
76+
5277
def _make_token(
5378
identity: str,
5479
room: str,

python/packages/livekit-portal/tests/integration/test_frame_video.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -262,9 +262,11 @@ async def test_large_frame_above_data_packet_cap(pair):
262262
)
263263
sent = _gradient(1280, 720, seed=3)
264264
pair.robot.send_video_frame("cam", sent)
265-
# Larger payloads need more settle time.
266-
await asyncio.sleep(SETTLE_S + 1.0)
267-
assert len(received) == 1
265+
# ~2.7 MB raw over the byte-stream path. Poll for arrival rather than
266+
# race a fixed settle window, which flakes under load (issue #60).
267+
from integration.conftest import wait_for
268+
269+
assert await wait_for(lambda: len(received) == 1, timeout_s=15.0)
268270
arr = frame_bytes_to_numpy_rgb(
269271
bytes(received[0].data), received[0].width, received[0].height
270272
)

python/packages/livekit-portal/tests/integration/test_frame_video_stress.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,16 @@ async def test_4k_frame_roundtrip(pair, codec):
8989

9090
t0 = time.perf_counter()
9191
pair.robot.send_video_frame("cam", sent)
92-
# Generous settle — 25 MB has to traverse the SFU and decode locally.
93-
await asyncio.sleep(SETTLE_S + 4.0)
92+
# 25 MB raw can take several seconds to traverse the SFU and decode.
93+
# Poll up to a generous ceiling rather than race a fixed settle window,
94+
# which flakes under load (issue #60).
95+
from integration.conftest import wait_for
96+
97+
timeout_s = 30.0
98+
ok = await wait_for(lambda: len(received) == 1, timeout_s=timeout_s)
9499
elapsed = time.perf_counter() - t0
95100

96-
assert len(received) == 1, (
97-
f"4K {codec} not received within {SETTLE_S + 4.0}s; got {len(received)}"
98-
)
101+
assert ok, f"4K {codec} not received within {timeout_s}s; got {len(received)}"
99102
got = received[0]
100103
assert (got.width, got.height) == (3840, 2160)
101104
arr = frame_bytes_to_numpy_rgb(bytes(got.data), got.width, got.height)

0 commit comments

Comments
 (0)