aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/tests')
-rw-r--r--packages/meshbay-hub/tests/test_downloads.py20
-rw-r--r--packages/meshbay-hub/tests/test_streamed_download_reliability.py61
2 files changed, 76 insertions, 5 deletions
diff --git a/packages/meshbay-hub/tests/test_downloads.py b/packages/meshbay-hub/tests/test_downloads.py
index 41ae5d3..afb85d6 100644
--- a/packages/meshbay-hub/tests/test_downloads.py
+++ b/packages/meshbay-hub/tests/test_downloads.py
@@ -175,9 +175,23 @@ def test_backpressure_is_real(tmp_path):
# The transfer list may carry more than the stream — a reply port rides
# along now — so this asserts that `readable` is transferred, not the exact
# shape of the list.
- transfer = fn[fn.index("worker.postMessage("):]
- transfer = transfer[transfer.index("["):transfer.index("]") + 1]
- assert "readable" in transfer, "the readable half must be transferred, not copied"
+ #
+ # And every `postMessage` in here, not the first: a ping is sent to wake the
+ # worker before it is handed anything, and it carries only a port. Reading
+ # the first one would have moved this check onto the ping the day it was
+ # added, leaving the stream unguarded while still passing.
+ posts = []
+ rest = fn
+ while "worker.postMessage(" in rest:
+ rest = rest[rest.index("worker.postMessage("):]
+ # Bounded by the call's own end: the keep-alive ping transfers nothing
+ # at all, and reaching past it for a `[` would read the next call's.
+ posts.append(rest[:rest.index(");") + 2])
+ rest = rest[len("worker.postMessage("):]
+ lists = [c[c.index("["):c.index("]") + 1] for c in posts if "[" in c]
+ assert len(posts) >= 2, "the wake-up and the stream are both posted from here"
+ assert any("readable" in t for t in lists), (
+ "the readable half must be transferred, not copied")
assert "writer.write(bytes)" in fn
assert "return null" in fn, "a browser that cannot transfer streams must say so"
diff --git a/packages/meshbay-hub/tests/test_streamed_download_reliability.py b/packages/meshbay-hub/tests/test_streamed_download_reliability.py
index bfd4a89..e1b3800 100644
--- a/packages/meshbay-hub/tests/test_streamed_download_reliability.py
+++ b/packages/meshbay-hub/tests/test_streamed_download_reliability.py
@@ -50,15 +50,37 @@ globalThis.localStorage = {
};
const PLAN = %(plan)s;
const log = { registers: 0, claims: 0, navigations: 0, served: 0,
- unregisters: 0 };
+ unregisters: 0, wakes: 0 };
// The worker as the page sees it: something with postMessage. It answers a
// navigation by posting mbdl-serving back on the port it was handed, which is
// exactly the confirmation the real sw.js sends from its fetch handler.
let controller = null;
const pendingByFrame = new Map();
+// Set before the controller exists, because the declaration below is what
+// the temporal dead zone protects.
+let asleep = PLAN.workerAsleep;
const makeController = () => ({
postMessage: (msg, transfer) => {
+ // A worker with nothing to do is terminated, and `pending` goes with it.
+ // A ping wakes it; anything else posted while it sleeps is simply lost,
+ // which is what makes this failure silent.
+ if (asleep) {
+ if (msg.type === 'mbdl-ping') {
+ asleep = false;
+ log.wakes += 1;
+ if (msg.ports || (transfer && transfer[0])) {
+ const port = (transfer && transfer[0]) || null;
+ if (port) setTimeout(() => port.postMessage({type: 'mbdl-pong'}), 0);
+ }
+ }
+ return;
+ }
+ if (msg.type === 'mbdl-ping') {
+ const port = (transfer && transfer[0]) || null;
+ if (port) setTimeout(() => port.postMessage({type: 'mbdl-pong'}), 0);
+ return;
+ }
if (msg.type === 'mbdl-claim') {
log.claims += 1;
// A worker that actually claims when asked, which is what sw.js does.
@@ -70,6 +92,9 @@ const makeController = () => ({
}
if (msg.type !== 'mbdl') return;
pendingByFrame.set('/_mbdl/' + msg.id, msg.port);
+ // The worker says it has it, which is what the page waits for.
+ if (msg.port) setTimeout(() => msg.port.postMessage({type: 'mbdl-ready',
+ id: msg.id}), 0);
},
});
@@ -167,7 +192,8 @@ def _run(tmp_path, body, *, control_after_ms=0, active=True,
serve="always", register_throws=False, control_budget_ms=800,
ready_settles=True, register_hangs=False,
active_after_unregister=False, control_on_claim=False,
- controlled_at_load=False, registered_at_load=False):
+ controlled_at_load=False, registered_at_load=False,
+ worker_asleep=False):
module = tmp_path / "downloads.mjs"
module.write_text(DOWNLOADS.read_text())
(tmp_path / "package.json").write_text('{"type":"module"}')
@@ -181,6 +207,7 @@ def _run(tmp_path, body, *, control_after_ms=0, active=True,
"activeAfterUnregister": active_after_unregister,
"controlOnClaim": control_on_claim,
"registeredAtLoad": registered_at_load,
+ "workerAsleep": worker_asleep,
}
script = tmp_path / "case.mjs"
script.write_text(
@@ -530,3 +557,33 @@ def test_the_streamed_target_says_it_cannot_be_paused(tmp_path):
if (target) await target.writable.close();
""")
assert out["pausable"] is False
+
+
+# ── a worker that was asleep when we posted ─────────────────────────────────
+
+def test_a_sleeping_worker_is_woken_before_it_is_handed_a_stream(tmp_path):
+ """Reported from Chrome: a download started while an upload was running took
+ thirty seconds to begin, every time.
+
+ `pending` lives in the worker's memory and a worker with nothing to do is
+ terminated — which is what a long upload leaves it, for minutes, since a
+ WebRTC transfer gives it no events at all. The stream posted to it was lost;
+ the iframe then woke it with nothing to find and the request fell through to
+ the network, measured in the console as a 404 from the hub and fifteen
+ seconds of silence, twice.
+
+ `mbdl-ping` already existed — sent every ten seconds *while* writing, for
+ the same reason. Nothing sent one before *starting*.
+ """
+ out = _run(tmp_path, """
+ const target = await M.openStreamedDownload('film.mkv', 20e9, FAST);
+ out.target = target !== null;
+ out.wakes = log.wakes;
+ out.navigations = log.navigations;
+ if (target) await target.writable.close();
+ """, worker_asleep=True)
+ assert out["target"] is True, "the download never started"
+ assert out["wakes"] == 1, "the worker was handed a stream while asleep"
+ assert out["navigations"] == 1, (
+ f"took {out['navigations']} attempts — the first one was wasted on a "
+ "worker that had not been woken")