1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
|
"""
The desktop shell's security contract, pinned by reading its source.
There is no npm on the development machine, so the Electron application cannot
be installed or launched here. That is stated plainly rather than worked around:
**nothing below proves the app runs.** What it does prove is that the properties
the design depends on are present in the source, and it fails if one is removed
— which is the same treatment `test_downloads.py` gives the three
browser-specific save paths, for the same reason.
Every assertion here corresponds to a sentence in `docs/MESHBAY_DESIGN.md`
§8.2. Weak evidence, and the only evidence available without a packaged build; a
person with an installed client is what confirms the rest.
"""
import json
import re
from pathlib import Path
import pytest
CLIENT = Path(__file__).resolve().parents[2] / "meshbay-client"
MAIN = CLIENT / "src" / "main.js"
PRELOAD = CLIENT / "src" / "preload.js"
INDEX = CLIENT / "scripts" / "index.html"
pytestmark = pytest.mark.skipif(
not MAIN.exists(), reason="desktop client sources not present")
def _main() -> str:
return MAIN.read_text(encoding="utf-8")
def _preload() -> str:
return PRELOAD.read_text(encoding="utf-8")
# ── The renderer is confined ────────────────────────────────────────────────
@pytest.mark.parametrize("setting", [
"sandbox: true",
"contextIsolation: true",
"nodeIntegration: false",
])
def test_the_renderer_keeps_its_sandbox(setting):
"""
Electron with these keeps the Chromium renderer sandbox — the strongest
available, and the reason "native costs the browser sandbox" is false for
this shell. Without contextIsolation the preload's objects are reachable and
mutable from page script, which would make the bridge decorative.
"""
assert setting in _main(), f"{setting} is missing from the window"
def test_the_interface_is_never_loaded_from_the_hub():
"""
The whole reason this application exists. A shell pointing a WebView at the
hub's /app/ is a browser with a different icon and fixes nothing (T3).
"""
source = _main()
assert "loadURL(`${SCHEME}://" in source or "loadURL('app://" in source
assert "loadURL('http" not in source and 'loadURL("http' not in source
assert "loadURL(`http" not in source
def test_navigation_away_from_the_package_is_refused():
source = _main()
assert "will-navigate" in source
assert "setWindowOpenHandler" in source
assert "event.preventDefault()" in source
def _granted_permissions() -> set[str]:
"""The allowlist, read out of the source rather than described here."""
match = re.search(r"GRANTED_PERMISSIONS = new Set\(\[([^\]]*)\]\)", _main())
assert match, "the permission allowlist is gone or was renamed"
return set(re.findall(r"'([^']+)'", match.group(1)))
def test_the_page_gets_no_camera_microphone_or_location():
denied = {"media", "geolocation", "midi", "midiSysex", "notifications",
"pointerLock", "openExternal", "clipboard-read", "hid", "serial",
"usb", "idle-detection", "window-management"}
assert not (_granted_permissions() & denied)
def test_video_may_go_fullscreen():
"""
The regression this replaced a blanket denial to fix, and the reason it is
worth a test: **a denied `fullscreen` does not reject.** Chromium's own
video controls ask for it, `requestFullscreen()` returns a promise that
never settles, and the button does nothing with no error raised anywhere.
Nothing observable says "permission" — so nothing would have led back here.
"""
assert "fullscreen" in _granted_permissions()
def test_copy_buttons_may_write_the_clipboard_and_nothing_may_read_it():
"""
Every Copy button calls `navigator.clipboard.writeText`, which Chromium
gates on `clipboard-sanitized-write`. With only `fullscreen` granted it was
refused, and each button did nothing — reported on the invitation link's.
"""
granted = _granted_permissions()
assert "clipboard-sanitized-write" in granted
assert "clipboard-read" not in granted
def test_both_permission_handlers_answer_from_the_same_list():
"""`Permissions.query` takes the check handler and a request takes the
other; two lists would eventually disagree about what the page may do."""
source = _main()
for handler in ("setPermissionRequestHandler", "setPermissionCheckHandler"):
assert handler in source
after = source.split(handler, 1)[1][:400]
assert "GRANTED_PERMISSIONS" in after, (
f"{handler} decides on its own rather than from the allowlist")
# ── The custom scheme ───────────────────────────────────────────────────────
@pytest.mark.parametrize("privilege", [
"standard: true",
"secure: true",
"supportFetchAPI: true",
"stream: true",
])
def test_the_scheme_is_privileged(privilege):
"""
`secure` is what makes it a secure context, and without it **the whole of
`crypto.subtle` is undefined** — measured, not assumed: the first probe
loaded a `data:` URL and every algorithm failed with TypeError, AES-GCM
included. `standard` gives a real origin, so IndexedDB survives an update
instead of being keyed to something that moves.
An earlier version of this docstring said `secure` was what let the service
worker register. That is wrong: Chromium refuses to register a worker on a
custom scheme whatever its privileges — "The URL protocol of the current
origin ('app://meshbay') is not supported". The application therefore has no
service worker and does not need one; it saves files through a native
dialog, which is better than the path the worker exists to provide.
"""
assert privilege in _main(), f"{privilege} missing from the scheme privileges"
def test_the_protocol_handler_cannot_be_walked_out_of():
"""
The renderer parses decrypted content from nodes, which is
attacker-controlled input. A traversal here would hand it the filesystem.
"""
source = _main()
assert "path.resolve(UI_DIR" in source
assert "startsWith(root + path.sep)" in source
assert "status: 404" in source
# ── Content Security Policy ─────────────────────────────────────────────────
def test_the_policy_is_sent_as_a_header():
"""
A <meta> policy cannot carry `frame-ancestors`, and having it there means
one directive of the policy is decoration. The handler is also the only
thing that serves the interface, so this is one source rather than two.
"""
source = _main()
assert "'Content-Security-Policy': CSP" in source
# Comments stripped, all of them, rather than skipping past the first
# `-->`. The page's only mention of a policy is the comment explaining why
# it is not here, so the check has to see the markup with every comment
# gone — the earlier version took `split("-->")[1]`, which meant adding a
# second comment anywhere above made it read that comment's own text and
# fail on correct markup. A guard that depends on how many comments precede
# it is not guarding the thing it names.
markup = re.sub(r"<!--.*?-->", "", INDEX.read_text(encoding="utf-8"),
flags=re.S)
assert "Content-Security-Policy" not in markup, \
"the packaged page still carries a policy of its own"
def test_the_policy_keeps_wasm_unsafe_eval():
"""
The bundle key is Argon2id in WebAssembly. A policy that forbids it does not
degrade anything — it locks every user out of their keys.
"""
assert "'wasm-unsafe-eval'" in _directive("script-src")
def _policy() -> str:
"""
The policy the protocol handler sends, read out of the CSP constant.
Not a <meta> tag: `frame-ancestors` is ignored there, and a directive that
silently does nothing is worse than one that is absent. Chromium said so in
the console the first time the application was launched.
"""
import re
source = _main()
# The array mixes plain strings and one `${RECAPTCHA_SRC}` template literal;
# resolve the constant so every directive reads as plain text.
rec = re.search(r"const RECAPTCHA_SRC = '([^']*)'", source)
match = re.search(r"const CSP = \[(.*?)\]\.join", source, re.S)
assert match, "no CSP constant in the main process"
body = match.group(1)
if rec:
body = body.replace("${RECAPTCHA_SRC}", rec.group(1))
# Skip the `//` comments inside the array. Reading one as a directive is
# the mistake this file already records against the packaged unit test,
# which matched the comment explaining why `User=` was absent: parse
# directives, not text. A comment line here yielded a phantom `//`
# directive the moment one was written.
lines = [line.strip() for line in body.splitlines()]
return "; ".join(
line.strip('`",').strip('`"')
for line in lines if line and not line.startswith("//"))
def _directive(name: str) -> str:
for part in _policy().split(";"):
part = part.strip()
if part.startswith(name + " "):
return part
return ""
def test_the_hub_is_reachable_but_never_executable():
"""
connect-src allows the hub's API and its signaling socket. script-src does
not: nothing the hub returns is ever executed. The only script sources are
'self', the wasm eval token, and the two reCAPTCHA hosts (see the next
test) — never a bare `https:` scheme, which would let the hub's own origin
serve script.
"""
connect = _directive("connect-src")
assert "https:" in connect and "wss:" in connect
script = _directive("script-src")
assert script, "no script-src directive"
sources = script.split()[1:] # drop the "script-src" keyword itself
allowed = {
"'self'", "'wasm-unsafe-eval'",
"https://www.google.com", "https://www.gstatic.com",
}
assert set(sources) <= allowed, \
f"unexpected script-src source: {set(sources) - allowed}"
assert "https:" not in sources, "a bare https: scheme lets the hub serve script"
assert "'unsafe-eval'" not in script.replace("'wasm-unsafe-eval'", "")
assert "default-src 'none'" in _policy()
def test_recaptcha_is_the_only_third_party_and_stays_scoped_to_it():
"""
reCAPTCHA gates sign-up in the app the same way it does in the browser.
www.google.com and www.gstatic.com are allowed under script-src, frame-src
and img-src for that — and no other external origin appears anywhere in the
policy. Remove this expectation only alongside the reCAPTCHA widget.
"""
hosts = {"https://www.google.com", "https://www.gstatic.com"}
for directive in ("script-src", "frame-src", "img-src"):
srcs = set(_directive(directive).split()[1:])
assert hosts <= srcs, f"{directive} is missing a reCAPTCHA host"
for part in _policy().split(";"):
for tok in part.strip().split()[1:]:
if tok.startswith(("http://", "https://")):
assert tok in hosts, f"unexpected external origin in CSP: {tok}"
def test_the_pdf_preview_has_both_permissions_it_needs():
"""
The application shows a PDF the same way the browser does, and needs the
same two permissions to do it — this is where it was missing them.
`files-app.js` decrypts the file in the renderer and hands it to
`<object type="application/pdf">` from a Blob. Chromium's viewer loads that
as plugin data (`object-src`) and renders it in an internal frame
(`frame-src`); with either refused, the "will not display the PDF inline"
fallback is what the user gets. Measured on Electron 44 (Chromium 152):
with both opened the viewer renders, with `plugins` left at its default
`false` — the built-in viewer is not behind that flag, so do not turn it on
to fix a PDF.
`'self'` would not do: a same-origin `blob:` URL is not matched by it in
either directive.
"""
for name in ("object-src", "frame-src"):
directive = _directive(name)
assert directive, f"no {name} directive in the main process CSP"
sources = directive.split()[1:]
assert "blob:" in sources, (
f"{name} refuses the decrypted PDF; the preview shows its fallback "
f"message in the desktop client")
assert "*" not in sources
def test_the_two_policies_stay_in_step():
"""
One interface, two policies: this one and the hub's
(`meshbay_hub.api.webapp.CSP`), sent for the very same files. A directive
added to one and not the other is a feature that works in the browser and
not in the application, or the reverse. The PDF preview is what made the
duplication visible: the client sent a policy from its first launch and the
hub sent none until 2026-09-01, so the same markup worked in Chrome and not
in the application, and the difference read as a missing native feature
rather than as a policy nobody had compared.
Two differences are deliberate and named here. Everything else must match.
"""
from meshbay_hub.api.webapp import CSP as HUB_CSP
def directives(policy: str) -> dict[str, set[str]]:
out = {}
for part in policy.split(";"):
tokens = part.strip().split()
if tokens:
out[tokens[0]] = set(tokens[1:])
return out
app, hub = directives(_policy()), directives(HUB_CSP)
assert set(app) == set(hub), (
f"a directive exists in one policy only: {set(app) ^ set(hub)}")
# Nothing frames an `app://` page, so the client refuses every ancestor;
# the hub allows itself, for the streamed download's hidden iframe.
assert app["frame-ancestors"] == {"'none'"}
assert hub["frame-ancestors"] == {"'self'"}
# That same iframe is why the hub's frame-src carries `'self'`. The client
# has no service worker to ask (Chromium refuses one on a custom scheme),
# so it saves through the native dialog and needs no same-origin frame.
assert hub["frame-src"] - app["frame-src"] == {"'self'"}
for name in sorted(set(app) - {"frame-ancestors", "frame-src"}):
assert app[name] == hub[name], (
f"{name} has drifted: app {sorted(app[name])} vs "
f"hub {sorted(hub[name])}")
# ── The bridge ──────────────────────────────────────────────────────────────
def test_the_bridge_is_the_only_way_in():
source = _preload()
assert "contextBridge.exposeInMainWorld" in source
# Handing the raw ipcRenderer to the page would expose every channel in the
# main process, named or not.
assert "exposeInMainWorld('meshbay', ipcRenderer" not in source
assert "ipcRenderer)" not in source.replace("require('electron');", "")
def test_the_renderer_never_names_a_path():
"""
It asks for a dialog and receives an opaque id; the main process holds the
handle. A channel that took a path from the renderer and wrote to it would
be the whole confinement undone.
"""
source = _preload()
assert "save:begin" in source
assert "handle.id" in source
assert "filePath" not in source, "the preload passes a filesystem path around"
def test_the_hub_address_is_not_fetched_synchronously_over_ipc():
"""
`platform.hubBase()` runs while the module graph is loading, before anything
can await. Synchronous IPC would block the renderer on every call for a
value that cannot change within a run.
"""
source = _preload()
assert "--meshbay-hub=" in source
assert "sendSync" not in source
def test_plain_http_is_refused_except_to_loopback():
"""Anywhere else it would put the session token on the wire in clear."""
source = _main()
assert "must be https" in source
# The guard is a regular expression, so the dot is escaped in the source.
assert "127\\." in source and "localhost" in source
# ── One interface, one source ───────────────────────────────────────────────
def test_the_interface_is_copied_not_forked():
"""
docs/MESHBAY_DESIGN.md §8.3: the hub's static directory is the single
source. A silent fork is the only real way to end up maintaining the
interface twice, so the copy is generated and the generated tree is not
committed.
"""
sync = (CLIENT / "scripts" / "sync-ui.js").read_text(encoding="utf-8")
assert "meshbay-hub" in sync and "static" in sync
assert "rmSync" in sync, "a stale file could survive a rebuild"
gitignore = (CLIENT.parents[1] / ".gitignore").read_text(encoding="utf-8")
assert "meshbay-client/ui/" in gitignore, (
"the generated copy is committed, which is how a fork begins")
def test_the_packaged_page_loads_the_shared_modules():
"""The app's index.html is its own — the hub's carries a /a/<hash>/ prefix
that would point back at the hub — but it must load the same files."""
page = INDEX.read_text(encoding="utf-8")
for module in ("keyderive.js", "crypto.js", "transport.js", "app.js",
"style.css", "argon2.min.js"):
assert module in page, f"{module} is not loaded by the packaged page"
assert "/a/" not in page, "the packaged page points at the hub's asset prefix"
# ── Where downloads land ────────────────────────────────────────────────────
def test_automatic_saving_never_opens_a_dialog_for_want_of_a_folder():
"""
"Save automatically" opened a Save As dialog on the first download, because
the automatic path required a folder to have been chosen first and nobody
had chosen one. A browser does not ask before it will save a file; the
system Downloads folder is the answer when there is no other.
"""
source = _main()
begin = source.split("ipcMain.handle('save:begin'", 1)[1].split("ipcMain.handle", 1)[0]
assert "defaultDownloadDir()" in begin, (
"the automatic path has no destination when no folder was chosen")
assert "app.getPath('downloads')" in source
def test_a_chosen_folder_that_has_gone_is_not_silently_replaced():
"""Someone who picked an external drive should be told it is not there,
not find the film in their home directory a week later."""
source = _main()
begin = source.split("ipcMain.handle('save:begin'", 1)[1].split("ipcMain.handle", 1)[0]
assert "config.downloadDir && !chosen" in begin, (
"a chosen-but-missing folder falls through to the default instead of asking")
assert "showSaveDialog" in begin
# ── one definition of the package, not two ───────────────────────────────────
def test_package_json_does_not_define_a_second_linux_package():
"""
`packaging/build/` builds the client's `.deb` and `.rpm`: `build-client.sh`
runs electron-builder for `--dir` only, then the tree is assembled by hand
and `dpkg-deb`/`rpmbuild` package it from `packaging/deb/meshbay-client/
DEBIAN/control` and `packaging/rpm/meshbay-client.spec`.
`package.json` used to *also* declare `linux.target: [deb, rpm]` with its
own `deb.depends`/`rpm.depends`, so `npm run dist` produced a second
package under the same name — and the two had already drifted. The
electron-builder one installed to `/opt/MeshBay/meshbay-client` (against
`/opt/meshbay-client/meshbay`), and declared `Depends:
python3-meshbay-common` while naming none of the Electron runtime
libraries the real control file lists — so it would have installed
cleanly and then refused to start.
Nothing referenced `npm run dist`, which is why nobody noticed. It now
delegates to `build-client.sh`, and this keeps the second definition from
growing back.
"""
pkg = json.loads((CLIENT / "package.json").read_text(encoding="utf-8"))
build = pkg.get("build", {})
for key in ("deb", "rpm", "appImage", "snap", "pacman"):
assert key not in build, (
f"package.json's build.{key} defines packaging that "
"packaging/build/ already owns — two definitions of one package "
"drift, and the last pair already had")
targets = build.get("linux", {}).get("target")
assert not targets, (
f"build.linux.target is {targets!r}: electron-builder is used for "
"--dir only. A target list here makes `electron-builder` emit a "
"package that competes with the one packaging/build/ ships")
dist = pkg.get("scripts", {}).get("dist", "")
assert "electron-builder" not in dist, (
"the dist script builds packages with electron-builder again; it "
"should delegate to packaging/build/build-client.sh")
# ── System tray is cross-platform ────────────────────────────────────────────
def test_tray_capability_and_main_process_agree_on_which_platforms():
"""
The tray (GNOME, then Windows) has exactly two platform gates: the `tray`
capability preload.js exposes, which is all app.js's nav button is keyed
on, and main.js's `trayOS()`, which every path that creates or uses the
indicator goes through. Both must name the same platform set, or one of
two broken states results: a button that renders but does nothing (main
stricter than the capability), or a hidden window with no way back
(capability stricter than main, on a desktop with no tray to have hidden
it into). Everything else the tray uses (`ensureTray`, `tray.on('click')`,
the poll-refresh, `nodeService.status/stop/restart`) is unconditional
Electron/abstraction code with no platform check of its own.
"""
preload = _preload()
cap_line = next(
(line for line in preload.splitlines() if "tray:" in line
and "process.platform" in line), None)
assert cap_line, "no platform-gated `tray:` capability found in preload.js"
main = _main()
gate = next(
(line for line in main.splitlines()
if line.startswith("const trayOS =") and "process.platform" in line), None)
assert gate, "no `trayOS` platform gate found in main.js"
for plat in ("'linux'", "'win32'"):
assert plat in cap_line, f"tray capability must name {plat}"
assert plat in gate, f"trayOS must name {plat}"
def test_every_tray_path_goes_through_the_one_gate():
"""
There is one platform test, not a copy per call site. A second inline
`process.platform === ...` beside a tray call is how the two drift, which
is the whole failure the test above exists to catch — it can only compare
what it can find.
Both entry points are named explicitly: the launch-time creation (the
indicator is there before anyone minimises, which is most of what a tray
is for) and the minimise handler.
"""
lines = _main().splitlines()
# Both entry points still exist, so a rename cannot make this vacuous.
for marker in ("ensureTray()", "'window:minimize-to-tray'", "'tray:labels'"):
assert any(marker in line for line in lines), f"{marker} not found in main.js"
inline = [line for line in lines
if "process.platform" in line and "tray" in line.lower()
and not line.startswith("const trayOS =")]
assert not inline, (
"a tray path is testing process.platform itself instead of calling "
f"trayOS(): {inline}")
def test_the_tray_is_created_at_launch():
"""
Not on the first minimise. An indicator that only appears once you have
already hidden the window cannot be used to find the application.
`ensureTray` must be called after `registerBridge`, because buildTrayMenu
reads the `nodeService` registerBridge assigns: the other order puts the
Start/Stop entry on the menu one five-second poll late.
"""
main = _main()
ready = main.index("app.whenReady()")
bridge = main.index("registerBridge();", ready)
launch = main.index("ensureTray()", ready)
window = main.index("createWindow();", ready)
assert bridge < launch, "ensureTray must come after registerBridge"
assert launch < window, "the tray is created as part of startup"
# ── Chromium's command line ─────────────────────────────────────────────────
def test_no_feature_list_is_ever_written_directly():
"""
`--enable-features` and `--disable-features` are one comma-joined list
each, and `appendSwitch` **replaces** the value rather than appending to
it. A second direct call therefore cancels the first with no error and
nothing in a log — the symptom is a feature that is simply not on, which
is how the mDNS switch below and the VA-API switches could silently
cancel each other. Every caller goes through `addFeatures`, which merges.
"""
direct = re.findall(r"appendSwitch\(\s*['\"](?:enable|disable)-features",
_main())
assert not direct, (
"a feature list is being written with appendSwitch directly; it "
"overwrites whatever another line already put there — use addFeatures")
def test_the_mdns_concealment_is_still_disabled():
"""Chromium's `.local` host candidates are dropped by aiortc, which has no
mDNS resolver: concealing the local IP removes the only LAN-routable
candidate rather than degrading it."""
assert "addFeatures('disable-features', ['WebRtcHideLocalIpsWithMdns'])" in _main()
def test_hardware_video_decoding_is_asked_for_and_then_verified():
"""
Turning VA-API on is half the job. Chromium renames these features across
releases and the client is rebuilt against the newest Electron every time,
so a name that stops matching must not pass for a feature that is on: the
result is measured through `navigator.mediaCapabilities` and remembered,
and `--ignore-gpu-blocklist` is dropped again on a machine where the
measurement never says hardware.
"""
main = _main()
for feature in ("VaapiVideoDecodeLinuxGL", "AcceleratedVideoDecodeLinuxGL"):
assert feature in main, f"{feature} is no longer requested"
assert "ignore-gpu-blocklist" in main
assert "mediaCapabilities" in main and "powerEfficient" in main, (
"the switches are applied but nothing checks whether they worked")
def test_hardware_decoding_can_be_turned_off_without_a_rebuild():
"""A machine whose GL stack misbehaves in a way the measurement does not
catch needs an answer that is not "reinstall": config.json, the same file
every other client setting lives in."""
assert "config.videoAcceleration" in _main()
|