phase: 02_sharp_capture
**Phase 02 final verification pass — complete, 100% green.** **Verified (all 4 tasks in `complete/02_sharp_capture/`):** - Task 1: `pickSurface()` requests `screen × devicePixelRatio` (capped 3840×2160) instead of window size - Task 2: `degradationPreference: "maintain-resolution"` + per-leg `maxBitrate`, re-applied after `setLocalDescription` *and* on `connected` (both call sites asserted in `test_app.py`) - Task 3: default cap raised to 25 Mbit/s — `config.py` constant, `net.js` offline fallback, `.env.example`, README, and the drift-guard test all agree - Task 4: realdisplay sharpness floor (viewer decoded width ≥ 1152px = 90% of Xvfb 1280px) in `test_sharp_picture_reaches_the_wall` **Gates (all green):** - `uv run ruff check . && uv run ruff format --check .` → clean - `uv run pyright` → 0 errors - `uv run pytest` → 231 passed, 3 skipped (realdisplay), coverage 95% - `MIRROR_REALDISPLAY=1 uv run pytest -m realdisplay` → 3 passed on a real Xvfb + headed Chromium **Completion criteria:** (1) 90% width floor asserted on viewer's decoded pixels — PASS · (2) `/api/config` `maxBitrateBps: 25000000` default, net.js fallback agrees — PASS · (3) `#stat-resolution` matches viewer-decoded size incl. documented window downscale (1100×620, 1280×720 fake-device tests) — PASS · (4) both suites green, aspect-ratio guard (`1280/800` ± 0.06 + `≠ 1280×720`) still distinguishes real from fake capture — PASS · (5) README Configuration documents `25000000` — PASS; `PLAN.md` §6 "default 12 Mbit/s" left untouched as instructed — **flag for the user to amend**. **Docs:** README — fixed suite timing `~20s → ~50s` (measured 49s); all other claims verified (CLI flags, dev extra, config table, kiosk key table vs. `watch.js`/`share.js` routers, kiosk commands vs. `docs/kiosk-silverblue.md`). AGENTS.md — same stale timing fixed, nothing else stale (all referenced test names/files exist). **Notable:** `docs/kiosk-silverblue.md` + its scripts use `mirror.reeselink.com` while README/SECURITY.md/Containerfile use `mirror.reeseapps.com` — a pre-existing cross-file mismatch I could not resolve from the repo (both in same-day commits; live-infra fact). Left untouched; user should confirm the live kiosk URL. No live changes made. **Next pending phase:** `03_audio_share`.
This commit is contained in:
@@ -130,6 +130,67 @@ class TestClientScripts:
|
||||
assert f'src="/static/{script}"' in html
|
||||
assert 'type="module"' in html
|
||||
|
||||
def test_share_never_asks_for_an_exact_capture_size(self):
|
||||
"""A hard size breaks pickers: `exact` fails or hides picker entries on
|
||||
displays/DPRs the browser cannot honour. The size request must stay a
|
||||
preference the browser can decline while still capturing."""
|
||||
source = (STATIC / "share.js").read_text()
|
||||
assert not re.search(r"\bexact\s*:", source), (
|
||||
"share.js must not use an `exact` constraint key"
|
||||
)
|
||||
|
||||
def test_server_and_client_agree_on_the_bitrate_default(self):
|
||||
"""`/api/config` advertises the server's per-leg cap; a browser that cannot
|
||||
reach it falls back to the hardcoded literal in `net.js`. If the two drift,
|
||||
offline clients silently get a different picture quality and nothing fails.
|
||||
One number, two places, one test that notices."""
|
||||
server = int(
|
||||
re.search(
|
||||
r"DEFAULT_MAX_BITRATE_BPS\s*=\s*(\d[\d_]*)",
|
||||
(STATIC.parent / "config.py").read_text(),
|
||||
)
|
||||
.group(1)
|
||||
.replace("_", "")
|
||||
)
|
||||
client = int(re.search(r"maxBitrateBps:\s*(\d+)", (STATIC / "net.js").read_text()).group(1))
|
||||
assert server == client, (
|
||||
f"the server default ({server}) and the net.js fallback ({client}) "
|
||||
"have drifted: a client that cannot reach /api/config would use "
|
||||
"a different bitrate silently"
|
||||
)
|
||||
|
||||
def test_sender_quality_is_reapplied_on_negotiation_and_connection(self):
|
||||
"""`setParameters` is per-negotiation: a re-offer (a surface switch,
|
||||
a viewer that re-joined) resets the encodings. The cap must therefore
|
||||
be re-applied after `setLocalDescription` *and* when the leg becomes
|
||||
`connected`. Both call sites are asserted so a future edit cannot drop
|
||||
one and silently soften the wall."""
|
||||
source = (STATIC / "share.js").read_text()
|
||||
assert source.count('"maintain-resolution"') == 1, (
|
||||
"the degradation preference must be a single, named decision"
|
||||
)
|
||||
assert "async function applySenderQuality" in source
|
||||
|
||||
def section(start: str, stop: str) -> str:
|
||||
begin = source.index(start)
|
||||
return source[begin : source.index(stop, begin)]
|
||||
|
||||
negotiate = section("async function negotiate(viewerId)", "async function onSignal")
|
||||
assert "applySenderQuality" in negotiate, (
|
||||
"the cap is not re-applied after setLocalDescription"
|
||||
)
|
||||
assert negotiate.index("setLocalDescription") < negotiate.index("applySenderQuality"), (
|
||||
"the cap is applied before the encodings exist"
|
||||
)
|
||||
|
||||
connected = section(
|
||||
"pc.onconnectionstatechange = () => {",
|
||||
"for (const track of state.stream.getTracks())",
|
||||
)
|
||||
assert re.search(
|
||||
r'connectionState\s*===\s*"connected".*applySenderQuality', connected, re.S
|
||||
), "the cap is not re-applied when the leg becomes connected"
|
||||
|
||||
def test_the_two_pages_share_no_element_ids_but_the_shared_chrome(self):
|
||||
"""Both pages consume one stylesheet, so a duplicated id means a colliding rule.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user