From b501d27d2934c6b47060c83949b71116ca5ab97d Mon Sep 17 00:00:00 2001 From: Mikhail Glushenkov Date: Wed, 26 Aug 2026 05:05:17 +0000 Subject: [PATCH] session-description-handler.spec.ts: Fix flaky NotReadableError Chrome coalesces getUserMedia calls for one device onto a shared device session. Every spec boundary in this file stops all tracks, dropping the session's consumer count to zero and starting an asynchronous teardown in the audio service; the next spec's getUserMedia is then occasionally told the device is already open and joins the dying session, failing with NotReadableError: Could not start audio source. Which spec fails depends on where the race lands, so it presents as an intermittent failure scattered across the interop tests, more frequent on loaded machines. A single audio track acquired for the file's whole run keeps the consumer count above zero, so no teardown ever races a join. Ten consecutive runs in an environment reproducing the failure on roughly two in five runs came back clean. --- .../web/session-description-handler.spec.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/spec/platform/web/session-description-handler.spec.ts b/test/spec/platform/web/session-description-handler.spec.ts index 34675d48d..5f3bd5281 100644 --- a/test/spec/platform/web/session-description-handler.spec.ts +++ b/test/spec/platform/web/session-description-handler.spec.ts @@ -110,6 +110,19 @@ class SessionDescriptionHandlerTimeoutTest extends SessionDescriptionHandler { } describe("Web SessionDescriptionHandler", () => { + // One track held for the file's whole run keeps Chrome's shared fake-device + // session open. Without it every spec boundary drops the consumer count to + // zero, the session starts an asynchronous teardown, and the next spec's + // getUserMedia occasionally joins the dying session and fails with + // NotReadableError: Could not start audio source. + let keepAliveStream: MediaStream; + beforeAll(async () => { + keepAliveStream = await navigator.mediaDevices.getUserMedia({ audio: true }); + }); + afterAll(() => { + keepAliveStream.getTracks().forEach((track) => track.stop()); + }); + beforeEach(() => { // jasmine.clock().install(); });