""" A big film stops at around 100 MB. Reported against 0.5: upload a 500 MB video, play it, and the player loads roughly 100 MB and then hangs on "buffering" for good. 100 MB is not a number in our code. It is where the browser stops: a video SourceBuffer is capped at a few hundred megabytes and `appendBuffer` throws QuotaExceededError past it. The node remuxes with `-c copy`, so the bytes on the wire are the file's own — a 500 MB film really does try to put 500 MB into that buffer, and on a fast link it reaches the ceiling in the first minute, long before anyone has watched enough for eviction to have anything to drop. Two defects, and the second is the one that makes it permanent. **Nothing bounded how far ahead we pulled.** Credit was granted once per append: the node sent exactly as fast as the browser could append, which is as fast as the network allows, which for a film is very much faster than watching it. Memory was bounded by the browser's ceiling rather than by anything we chose. Buffering by *time* past the playhead instead makes a two-hour film cost the same as a two-minute clip. **The pipeline could not restart itself.** An append refused for quota fires no `updateend`. `updateend` was where credit was granted, so no credit went out; the node then sent nothing, so no segment arrived to call `flushQueue` again. Every wakeup the append path had was downstream of the append that had just failed. Playback continuing past the segment, which is exactly what frees the room needed to recover, woke nothing at all. The player deadlocked against itself. The first version of these tests modelled the pipeline and passed while the player still hung, because a model of a fix written by whoever wrote the fix agrees with it by construction. They now run the shipped `bufferedAhead`, `evictBehind`, `flushQueue` and `pump`, lifted out of app.js as text, against a fake SourceBuffer — see harness/mse_harness.mjs. What is modelled is the browser, not us. """ import json import re import shutil import subprocess from pathlib import Path import pytest STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" APP = STATIC / "app.js" NODE_SERVER = (Path(__file__).resolve().parents[2] / "meshbay-node" / "src" / "meshbay_node" / "transport" / "webrtc_server.py") pytestmark = pytest.mark.skipif( shutil.which("node") is None or not APP.exists(), reason="node or the SPA sources are not available") @pytest.fixture(scope="module") def app(): return APP.read_text() def _player(app: str) -> str: i = app.index("function VideoPlayer(") return app[i:app.index("\nfunction ", i + 1)] # ── The shipped functions, run against a browser that has a ceiling ─────────── HARNESS = Path(__file__).parent / "harness" / "mse_harness.mjs" def _harness(**cfg) -> dict: proc = subprocess.run( ["node", str(HARNESS), str(APP), json.dumps(cfg)], capture_output=True, text=True) assert proc.returncode == 0, proc.stderr return json.loads(proc.stdout) @pytest.fixture(scope="module") def idle(): """Nobody pressed play — autoplay is blocked on a phone more often than not.""" return _harness(playing=False) @pytest.fixture(scope="module") def watched(): return _harness(playing=True) def test_the_ceiling_is_never_reached_when_nobody_presses_play(idle): """The reported hang, from the side that produces it. A film left on the loading screen used to pull until the browser refused an append, and that refusal was unrecoverable. Nothing should get near it. """ assert not idle["hitCeiling"], ( f"filled the buffer to the ceiling ({idle['heldInBufferMB']} MB)") assert idle["quotaRefusals"] == 0, ( f"{idle['quotaRefusals']} appends refused for quota — the state the " "player cannot get out of on its own") def test_the_ceiling_is_never_reached_while_watching(watched): assert not watched["hitCeiling"], ( f"filled the buffer to the ceiling ({watched['heldInBufferMB']} MB)") assert watched["quotaRefusals"] == 0 def test_the_read_ahead_is_bounded_by_the_playhead(idle, watched): """What replaced "as fast as the network allows".""" ahead = int(re.search(r"const BUFFER_AHEAD_S = (\d+)", APP.read_text()).group(1)) for name, run in (("idle", idle), ("watching", watched)): assert run["bufferedAheadS"] < ahead * 2, ( f"{name}: {run['bufferedAheadS']}s buffered against a {ahead}s " "bound — the gate is not holding") def test_a_watched_film_keeps_being_fed(watched): """The gate must throttle the stream, not stop it. Holding credit for good would be just as broken as never holding it, and would look the same from the sofa. """ assert watched["watchedS"] > 500, ( "playback did not advance, so this run says nothing about throttling") assert watched["sentMB"] > 40, ( f"only {watched['sentMB']} MB reached the player in ten minutes of " "playback — the gate is holding credit it should have released") assert watched["removes"] > 0, "nothing was ever evicted behind the playhead" def test_memory_stays_bounded_over_a_long_watch(watched): assert watched["heldInBufferMB"] < 40, ( f"holding {watched['heldInBufferMB']} MB — eviction is not keeping up") # ── The shape the fix depends on ────────────────────────────────────────────── def test_credit_is_granted_in_exactly_one_place(app): """Granting from `updateend` is the deadlock. It must not come back. A second grant site is how this regresses: it would work, until the append it hangs off is the one the ceiling refuses. """ player = _player(app) sites = player.count("grantStreamCredit(") assert sites == 2, ( f"{sites} calls to grantStreamCredit — expected exactly two, both " "inside pump(): the keepalive and the release") pump = player[player.index("const pump = useCallback("):] pump = pump[:pump.index("\n }, [")] assert pump.count("grantStreamCredit(") == 2, ( "credit is granted outside pump(), so an append that is refused for " "quota can still be the only thing that would have woken the pipeline") def test_something_other_than_data_drives_the_pipeline(app): """The recovery path cannot depend on a segment arriving.""" player = _player(app) assert "setInterval(pump" in player, ( "no clock drives pump(): once the ceiling refuses an append, nothing " "arrives and nothing retries") assert "clearInterval(pumpTimer)" in player, "the pump timer outlives the player" assert "addEventListener('timeupdate', pump)" in player, ( "playback progress is what frees room to evict, and it wakes nothing") def test_a_buffered_viewer_still_tells_the_node_it_is_there(app): """Holding credit back must not read as a closed tab.""" player = _player(app) assert "grantStreamCredit(0)" in player, ( "a viewer that is far enough ahead grants nothing and says nothing, so " "the node's stall timeout ends a film that is merely paused") @pytest.mark.skipif(not NODE_SERVER.exists(), reason="node sources unavailable") def test_the_node_ends_a_stream_on_silence_not_on_stinginess(): """The other half of the keepalive: the node has to honour it.""" text = NODE_SERVER.read_text() i = text.index("async def _await_stream_credit") body = text[i:text.index("\n async def ", i + 1)] assert "self._stream_heard_at" in body, ( "the stall budget still accumulates over the whole wait, so a keepalive " "that grants no credit cannot keep a paused film alive") assert "waited += STREAM_CREDIT_POLL" not in body, ( "the budget still accumulates over the whole wait rather than being " "measured from the last thing the peer said") grant = text[text.index("def _grant_stream_credit"):] grant = grant[:grant.index("\n def ", 1)] assert "self._stream_heard_at = time.monotonic()" in grant, ( "n=0 does not refresh the timeout, so the keepalive is a no-op") def test_appending_does_not_earn_credit(app): """What may be in flight is a question about the buffer, not about appends. Tying the two was the original design and it was wrong twice over. `updateend` fires for `remove()` as well, so the player paid the node for its own evictions; and crediting per append meant taking segments as fast as they could be written, which is as fast as the network allows. """ player = _player(app) handler = player[player.index("sb.addEventListener('updateend'"):] handler = handler[:handler.index("\n });")] assert "grantStreamCredit" not in handler, ( "credit is granted from updateend, which fires for remove() too") assert "outstandingRef" not in handler, ( "the in-flight window is adjusted from updateend rather than from the " "buffer, so an eviction still counts as room for another segment") def test_credit_is_a_window_and_not_a_debt(app): """It must be topped up, not paid off. Accumulating a credit per append and handing over the whole balance when the buffer finally had room sent six megabytes in one burst, overshot the target by a minute of film, and then said nothing for forty-six seconds. Measured in Chrome against real fragmented MP4. """ player = _player(app) pump = player[player.index("const pump = useCallback("):] pump = pump[:pump.index("\n }, [")] assert "STREAM_WINDOW - outstandingRef.current" in pump, ( "pump() no longer tops a window up to what is allowed in flight") src = APP.read_text() window = int(re.search(r"const STREAM_WINDOW = (\d+)", src).group(1)) assert 2 <= window <= 16, ( f"a window of {window} segments is either too small to keep the pipe " "busy or big enough to be a burst again") assert "outstandingRef.current = Math.max(0, outstandingRef.current - 1)" in player, ( "nothing decrements the window when a segment lands, so it fills once " "and never reopens")