open-standards/wai/live-conformance/browser_sink_verify.py
Transaction Science 7488f029cf
Some checks failed
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (push) Successful in 10s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (push) Failing after 1m29s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (push) Failing after 2m24s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Successful in 4m25s
joule-moq-remote host agent / joule-moq-remote · does this change touch it (push) Has been cancelled
joule-moq-remote host agent / joule-moq-remote, every target (linux-arm64) (push) Has been cancelled
joule-moq-remote host agent / joule-moq-remote, every target (macos) (push) Has been cancelled
joule-moq-room room service / joule-moq-room · does this change touch it (push) Has been cancelled
joule-moq-room room service / joule-moq-room, every target (push) Has been cancelled
joule-moq-web browser client / joule-moq-web · does this change touch it (push) Has been cancelled
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (push) Has been cancelled
wai-app and Create / wai-app · does this change touch it (push) Has been cancelled
wai-app and Create / wai-app · tests, clippy, the line language's verifier (linux) (push) Has been cancelled
wai-app and Create / wai-app · Create, browser end-to-end (macos) (push) Has been cancelled
wai-audit collector and player integration / does this change touch it (push) Has been cancelled
wai-audit collector and player integration / collector · linux (push) Has been cancelled
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (push) Has been cancelled
WAI operation catalogue / does this change touch it (push) Has been cancelled
WAI operation catalogue / the catalogue and every door · linux (push) Has been cancelled
wai-fleet service / does this change touch it (push) Has been cancelled
wai-fleet service / service · linux (push) Has been cancelled
wai-fleet service / service · macos (push) Has been cancelled
wai-ledger service / does this change touch it (push) Has been cancelled
wai-ledger service / service · linux (push) Has been cancelled
wai-ledger service / service · macos (push) Has been cancelled
wai-quantum for JavaScript / does this change touch it (push) Has been cancelled
wai-quantum for JavaScript / javascript · linux (push) Has been cancelled
wai-quantum studio and desktop / does this change touch it (push) Has been cancelled
wai-quantum studio and desktop / studio · browser (push) Has been cancelled
wai-quantum studio and desktop / desktop app · macos (push) Has been cancelled
wai-quantum studio and desktop / desktop app · linux (push) Has been cancelled
wai-quantum studio and desktop / desktop app · windows (push) Has been cancelled
wai-sink-android native sink library / does this change touch it (push) Has been cancelled
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (push) Has been cancelled
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (push) Has been cancelled
wai-sink-swift native sink package / does this change touch it (push) Has been cancelled
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (push) Has been cancelled
WAI Studio / does this change touch it (push) Has been cancelled
WAI Studio / the catalogue's actions, the unit tests and the core · linux (push) Has been cancelled
WAI Studio / Studio in Chromium and Firefox · macos (push) Has been cancelled
WAI Studio / Studio in WebKit · linux (push) Has been cancelled
wai: platform decode for every user-agent codec, video and audio, in the composing sink's dispatch
platform-decode §2 reads the query a page asks the platform from the payload of every
capability browser-sink §4.1 gives a user-agent decoder, not only AV1 and HEVC. §2.1 adds VP9
(the codec string from the first key frame's uncompressed header, the level from VP9's level
table over the frame size and the mean rate of the IVF timestamps, the matrix of its colour
space), AVC (avc1 and the SPS's profile, constraint byte and level, the size after the
cropping rectangle) and VP8. §2.2 adds the audio query, asked of AudioDecoder.isConfigSupported
and of decodingInfo with an AudioConfiguration: Ogg Opus (its OpusHead the description), one
Opus packet (the TOC byte), FLAC (STREAMINFO; the marker and metadata the description) and AAC
in ADTS (mp4a.40.<object type>, or mp4a.66 to mp4a.68 for MPEG-2). §2.3 reads, by the same
readers, the chunks a page gives the decoder and which of them are key. §3: answers to several
configurations under one codec string are merged, so no object is routed to a decoder that
refused its configuration.

browser-sink §3 takes the audio answers into the report; §14.3 has the composing page ask
about every alternative platform-decode maps and decode one routed ua through WebCodecs from
§2.3's chunks. wai::platform_decode: Query::Video and Query::Audio, chunks, chunks_json,
merge_answers, media_of. wai-web: platformMapped, platformChunks, platformMerge; compose.js
asks and decodes through them for every mapped capability, video and audio.

platform-decode-conformance/queries.json: the query and chunk summary of real payloads of
every mapped capability (payloads/, written by payloads/make.sh with an independent prober's
readings in prober.tsv, and the identify and encode corpora's streams), and planted faults
with their outcomes. live-conformance/browser_sink_verify.py, given that directory,
recomputes every case with its own readers and requires every mutated copy to fail.
verify_platform_decode.mjs reads every payload through the wasm32 build in Chromium and
WebKit and decodes it where the platform accepts its configuration; platform-decode §5
records the run.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:06:44 -05:00

1729 lines
68 KiB
Python

#!/usr/bin/env python3
"""Independent verifier of the browser-sink corpus (extensions/browser-sink.md).
Written from the extension's text. It shares no code with wai-rs, which generates the
corpus, and never runs it. It reads from the texts themselves the tables it works from:
the user agent's decoders (§4.1), the WebCodecs Codec Registry's rows (§4.2), the reasons
(§11), and the parameter register of capability-constraints §3.1. A capability's kind,
status and replicate class come from the registry export, registry/capabilities.json. It
hashes with ../claim-conformance/_blake3.py and checks Ed25519 signatures with
../claim-conformance/_ed25519.py, WAI's strict rule (SPEC §7 *Signatures*), both written
from their specifications with the standard library.
For every case of vectors.json it recomputes, from the text:
- `routes`: the report read or refused (§3), and each object's route, reason, legs,
`asked`, tier and energy entry (§4, §6);
- `choices`: the choice table of §4.7, each choice's alternatives in SPEC §4's energy
order and the one chosen;
- `constraints`: §5's outcome and reason;
- `bindings`: §7.1's reading and canonical form; `frame_tiers`: §7.2's tier;
- `chain`: each group receipt's signature (jwp-receipts §2.3, legacy form), its root and
each object's inclusion (§2.2), its canonical bytes and link hash (§3 step 3), the
second group's link to the first, whether each object verifies, and the binding a page
writes for it on the wasm route;
- `frames`: §10's state; `tables`: §9.2's side table over each sequence.
It requires every reason of §11 to appear in the corpus and §11 to list exactly the codes
it knows. Then it mutates copies of the corpus, one defect each, and requires every copy to
fail; and it changes the answers the corpus gives for codec strings no registry row lists,
and requires the corpus still to pass, since those answers are never read (§4.2).
python3 -I browser_sink_verify.py <live-conformance/browser> [<platform-decode-conformance>]
With the second directory it also reads platform-decode.md §2 and recomputes, from each
payload of its queries.json, the query a sink asks the platform (every codec string, size,
rate, bit rate and description) and the chunks of §2.3, with its own readers of SPEC §5's AV1
framing, IVF, Annex B, Ogg, FLAC and ADTS; then it mutates copies of those vectors and
requires every copy to fail.
Exit status 0 only if every check holds and every mutated copy fails. Python 3.8 or later;
standard library only.
"""
import copy
import json
import math
import os
import re
import struct
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
WAI = os.path.normpath(os.path.join(HERE, ".."))
sys.path.insert(0, os.path.join(WAI, "claim-conformance"))
import _blake3 as B3 # noqa: E402
import _ed25519 as ED # noqa: E402
TIERS = ["none", "entropy-consistency", "decode-equivalence"]
ALG = "jwp-object-1"
MAX_SAFE = 2 ** 53 - 1
CLASSES = ["CalibratedInstrument", "OnChipCounter", "ModelBased", "Estimator"]
# The registry export's labels of the replicate classes §2's table names.
CLASS_LABEL = {"StandardDefined": "standard-defined", "NeuralIntegerExact": "neural-int-exact",
"NeuralEntropyExact": "neural-entropy-exact"}
def read(p):
with open(p, encoding="utf-8") as f:
return f.read()
def section(text, heading, stop):
i = text.index(heading)
j = text.index(stop, i + len(heading))
return text[i:j]
def ticks(s):
return re.findall(r"`([^`]*)`", s)
# ───────────────────────────── the texts' tables ─────────────────────────────
SPEC = read(os.path.join(WAI, "extensions", "browser-sink.md"))
CC = read(os.path.join(WAI, "extensions", "capability-constraints.md"))
def ua_rows():
rows = []
for line in section(SPEC, "### 4.1 ", "### 4.2 ").splitlines():
if not line.startswith("| `"):
continue
cells = [c.strip() for c in line.strip().strip("|").split("|")]
caps, decoder, forms, tier = ticks(cells[0]), ticks(cells[1])[0], ticks(cells[2]), ticks(cells[3])[0]
rows.append({"caps": caps, "decoder": decoder, "forms": forms, "prefix": "and more" in cells[2], "tier": tier})
assert rows, "§4.1 has no rows"
return rows
def registry_rows():
rows = []
for line in section(SPEC, "### 4.2 ", "### 4.3 ").splitlines():
if line.startswith("| video |") or line.startswith("| audio |"):
rows += ticks(line)
assert rows, "§4.2 has no rows"
return rows
def reasons():
out = []
for line in section(SPEC, "## 11. Reasons", "## 12. ").splitlines():
if line.startswith("| `"):
out.append(ticks(line)[0])
return out
def register():
return [ticks(line)[0] for line in section(CC, "### 3.1 ", "\n## 4.").splitlines() if line.startswith("| `")]
def tier_by_class():
out = {}
for line in section(SPEC, "**Registered tier**", "`StandardDefined` is read").splitlines():
line = line.strip()
if line.startswith("| `") and "every other class" not in line:
cells = line.strip().strip("|").split("|")
for c in ticks(cells[0]):
out[CLASS_LABEL[c]] = ticks(cells[1])[0]
return out
UA_ROWS = ua_rows()
REGISTRY_ROWS = registry_rows()
REASONS = reasons()
REGISTER = register()
TIER_BY_CLASS = tier_by_class()
CAPS = {c["capability"]: c for c in json.loads(read(os.path.join(WAI, "registry", "capabilities.json")))["capabilities"]}
def rank(t):
return TIERS.index(t) if t in TIERS else 0
def read_tier(t):
return t if t in TIERS else "none"
def registered_tier(cap):
c = CAPS.get(cap)
return TIER_BY_CLASS.get(c["replicate"], "none") if c else "none"
def dispatchable(cap):
c = CAPS.get(cap)
if c is None:
return True # a string this revision does not register (SPEC §4)
return c["kind"] == "payload" and c["status"] != "candidate"
def row_matches(row, s):
if row.endswith("*"):
p = row[:-1]
return len(s) > len(p) and s.startswith(p)
return s == row
def gate(codec):
return any(row_matches(r, codec) for r in REGISTRY_ROWS)
def ua_row(cap):
for r in UA_ROWS:
if cap in r["caps"]:
return r
return None
def form_ok(row, codec):
for f in row["forms"]:
if row["prefix"] and f.endswith("."):
if len(codec) > len(f) and codec.startswith(f):
return True
elif codec == f:
return True
return False
# ─────────────────────────────── §5 ───────────────────────────────
def integer(v):
return v if isinstance(v, int) and not isinstance(v, bool) and -MAX_SAFE <= v <= MAX_SAFE else None
def is_value(v):
return isinstance(v, str) or integer(v) is not None
def constraint_ok(c):
if not isinstance(c, dict) or not c or any(k not in ("enum", "minimum", "maximum") for k in c):
return False
if "enum" in c and not (isinstance(c["enum"], list) and c["enum"] and all(is_value(x) for x in c["enum"])):
return False
for k in ("minimum", "maximum"):
if k in c and integer(c[k]) is None:
return False
if "minimum" in c and "maximum" in c and c["minimum"] > c["maximum"]:
return False
return True
def meets(c, v):
if v is None or not is_value(v):
return False
if "enum" in c:
hit = False
for x in c["enum"]:
if isinstance(x, str) and isinstance(v, str) and x == v:
hit = True
if integer(x) is not None and integer(v) is not None and x == v:
hit = True
if not hit:
return False
if "minimum" in c and (integer(v) is None or v < c["minimum"]):
return False
if "maximum" in c and (integer(v) is None or v > c["maximum"]):
return False
return True
def evaluate(sets, params):
"""(outcome, reason)."""
if sets is None:
return "unconstrained", None
if not isinstance(sets, list) or not sets:
return "unevaluable", "constraint_malformed"
for s in sets:
if not isinstance(s, dict) or not all(constraint_ok(c) for c in s.values()):
return "unevaluable", "constraint_malformed"
for s in sets:
if any(k not in REGISTER for k in s):
return "unevaluable", "parameter_unregistered"
params = params if isinstance(params, dict) else {}
for s in sets:
if all(meets(c, params.get(k)) for k, c in s.items()):
return "satisfied", None
return "unsatisfied", "constraints_unmet"
# ─────────────────────────────── §3, §4, §6 ───────────────────────────────
def is_bool_or_null(v):
return v is None or isinstance(v, bool)
def report_reads(r):
if not isinstance(r, dict):
return False
v = r.get("browser_sink")
if isinstance(v, bool) or v != 1 or not isinstance(v, int):
return False
ua = r.get("ua")
if not isinstance(ua, dict):
return False
for a in ua.values():
if not (isinstance(a, dict) and isinstance(a.get("supported"), bool) and "smooth" in a and is_bool_or_null(a["smooth"])
and "power_efficient" in a and is_bool_or_null(a["power_efficient"]) and a.get("probe") in ("match", "mismatch", "not_run")):
return False
if not (isinstance(r.get("host"), dict) and all(isinstance(x, str) for x in r["host"].values())):
return False
if not (isinstance(r.get("wasm"), list) and all(isinstance(x, str) for x in r["wasm"])):
return False
if not isinstance(r.get("constraints"), dict):
return False
if not (isinstance(r.get("energy"), dict) and all(isinstance(x, dict) for x in r["energy"].values())):
return False
objs = r.get("objects")
if not isinstance(objs, list):
return False
for o in objs:
if not (isinstance(o, dict) and isinstance(o.get("name"), str) and isinstance(o.get("capability"), str)
and "codec" in o and (o["codec"] is None or isinstance(o["codec"], str))
and isinstance(o.get("need"), str) and isinstance(o.get("params"), dict)):
return False
if "choice" in o and not (isinstance(o["choice"], str) and o["choice"]):
return False
return True
def positive(v):
i = integer(v)
return i is not None and i > 0
def read_measurement(m):
cls = m.get("class")
if cls not in CLASSES:
return None, "energy_class_invalid"
if integer(m.get("joules_micro")) == 0:
return None, "unmetered_as_zero"
if not all(positive(m.get(k)) for k in ("joules_micro", "decodes", "window_us")):
return None, "energy_count_invalid"
if cls in ("CalibratedInstrument", "OnChipCounter") and not positive(m.get("relative_ppm")):
return None, "energy_evidence_missing"
if cls == "OnChipCounter" and m.get("filtering") not in ("On", "Off", "Unknown"):
return None, "energy_evidence_missing"
if cls == "CalibratedInstrument" and not (isinstance(m.get("calibration_ref"), str) and m["calibration_ref"]):
return None, "energy_evidence_missing"
if m.get("coverage") not in ("Whole", "Partial"):
return None, "energy_evidence_missing"
f = m.get("fabrics")
if not (isinstance(f, list) and f and all(isinstance(x, str) for x in f) and len(set(f)) == len(f)):
return None, "energy_evidence_missing"
out = dict(m)
out["per_decode_micro"] = m["joules_micro"] // m["decodes"]
return out, None
def energy_entry(r, cap, codec, route):
pe = None
if route == "ua" and codec in r["ua"]:
pe = r["ua"][codec]["power_efficient"]
figure, reason = None, None
if route != "none":
m = r["energy"].get(cap + " " + route)
if m is not None:
figure, reason = read_measurement(m)
return {"power_efficient": pe, "figure": figure, "reason": reason}
def route_object(r, o):
cap, codec, need = o["capability"], o["codec"], read_tier(o["need"])
reg = registered_tier(cap)
nr = "not_reached"
def decided(reason):
return {"name": o["name"], "route": "none", "reason": reason, "legs": {"ua": nr, "host": nr, "wasm": nr},
"asked": False, "tier": "none", "energy": energy_entry(r, cap, codec, "none")}
if not dispatchable(cap):
return decided("not_dispatchable")
if rank(need) > rank(reg):
return decided("tier_exceeds_class")
outcome, _ = evaluate(r["constraints"].get(cap), o["params"])
if outcome == "unevaluable":
return decided("constraints_unevaluable")
if outcome == "unsatisfied":
return decided("constraints_unmet")
asked = False
ua, ua_tier = None, None
row = ua_row(cap)
if codec is None:
ua = "no_ua_mapping"
elif not gate(codec):
ua = "codec_unregistered"
elif row is None:
ua = "no_ua_mapping"
elif not form_ok(row, codec):
ua = "codec_mismatch"
else:
asked = True
a = r["ua"].get(codec)
if a is None:
ua = "ua_not_asked"
elif a["supported"] is not True:
ua = "ua_unsupported"
elif rank(need) > rank(row["tier"]):
ua = "ua_inexact"
elif need == "decode-equivalence" and a["probe"] != "match":
ua = "ua_probe_mismatch" if a["probe"] == "mismatch" else "ua_unprobed"
else:
t = row["tier"] if a["probe"] == "match" else "none"
ua_tier = TIERS[min(rank(t), rank(reg))]
host, host_tier = None, None
if cap not in r["host"]:
host = "no_host"
elif rank(read_tier(r["host"][cap])) < rank(need):
host = "host_inexact"
else:
host_tier = TIERS[min(rank(read_tier(r["host"][cap])), rank(reg))]
wasm, wasm_tier = None, None
if cap in r["wasm"]:
wasm_tier = reg
else:
wasm = "no_wasm"
if ua_tier is not None:
route, tier, legs = "ua", ua_tier, {"ua": "taken", "host": nr, "wasm": nr}
elif host_tier is not None:
route, tier, legs = "host", host_tier, {"ua": ua, "host": "taken", "wasm": nr}
elif wasm_tier is not None:
route, tier, legs = "wasm", wasm_tier, {"ua": ua, "host": host, "wasm": "taken"}
else:
route, tier, legs = "none", "none", {"ua": ua, "host": host, "wasm": wasm}
return {"name": o["name"], "route": route, "reason": "no_decoder" if route == "none" else None, "legs": legs,
"asked": asked, "tier": tier, "energy": energy_entry(r, cap, codec, route)}
def route_report(r):
if not report_reads(r):
return None, "report_malformed"
return [route_object(r, o) for o in r["objects"]], None
# ─────────────────────────────── §7 ───────────────────────────────
# SPEC §4, *Energy order*: a learned capability is one whose registry class is not one of the
# three standard classes, or a string this revision does not register.
STANDARD = ("standard-defined", "standard-tolerance", "standard-unclassified")
RANKS = ["first", "smooth", "classical", "learned-without-budget"]
def learned(cap):
c = CAPS.get(cap)
return c is None or c["replicate"] not in STANDARD
def choose_report(r):
"""§4.7: (choice table, None) or (None, reason)."""
table, reason = route_report(r)
if reason is not None:
return None, reason
groups = []
for i, o in enumerate(r["objects"]):
if "choice" not in o:
continue
for g in groups:
if g[0] == o["choice"]:
g[1].append(i)
break
else:
groups.append((o["choice"], [i]))
def rank(i):
e, o = table[i], r["objects"][i]
if e["route"] == "none":
return None
cap = o["capability"]
if learned(cap):
# A sink-metered figure of its decode under its route's key (§6) that reads, with a
# hardware class and coverage Whole; its value is not read.
m = r["energy"].get("%s %s" % (cap, e["route"]))
ok = m is not None and read_measurement(m)[1] is None and m.get("class") in ("OnChipCounter", "CalibratedInstrument") and m.get("coverage") == "Whole"
return "classical" if ok else "learned-without-budget"
if e["route"] == "ua":
a = r["ua"].get(o["codec"]) or {}
if a.get("supported") is True and a.get("smooth") is True and a.get("power_efficient") is True:
return "first"
if a.get("smooth") is True:
return "smooth"
return "classical"
out = []
for choice, members in groups:
ranked = [(rank(i), i) for i in members]
order = sorted(ranked, key=lambda x: (RANKS.index(x[0]) if x[0] is not None else len(RANKS), x[1]))
chosen = next((i for rk, i in order if rk is not None), None)
out.append({"choice": choice, "order": [r["objects"][i]["name"] for _, i in order],
"ranks": [rk for rk, _ in order],
"chosen": None if chosen is None else r["objects"][chosen]["name"],
"reason": None if chosen is not None else "no_alternative"})
return out, None
def hex32(s):
return isinstance(s, str) and re.fullmatch(r"[0-9a-f]{64}", s) is not None
def read_binding(v):
"""(binding dict in member order, None) or (None, reason)."""
if not isinstance(v, dict):
return None, "binding_malformed"
for k in ("alg", "digest", "tier"):
if not isinstance(v.get(k), str):
return None, "binding_malformed"
if not hex32(v["digest"]):
return None, "binding_malformed"
if "receipt" in v and not hex32(v["receipt"]):
return None, "binding_malformed"
if v["alg"] != ALG:
return None, "binding_alg_unknown"
b = {"alg": ALG, "digest": v["digest"]}
if "receipt" in v:
b["receipt"] = v["receipt"]
b["tier"] = read_tier(v["tier"])
return b, None
def canonical(b):
return json.dumps(b, separators=(",", ":"), ensure_ascii=False)
def frame_tier(route, route_tier, format_null, altered):
if altered or (route == "ua" and format_null):
return "none"
return route_tier
def frame_state(binding, obj, chain, prov):
b, reason = (None, None) if binding is None else read_binding(binding)
if b is None:
return {"state": "unbound", "reason": reason, "tier": None, "digest": None, "receipt": None}
if "refused" in (obj, chain, prov):
s = "refused"
elif obj == "unverified" or chain == "held" or prov == "missing":
s = "pending"
else:
s = "verified"
return {"state": s, "reason": None, "tier": b["tier"], "digest": b["digest"], "receipt": b.get("receipt")}
# ─────────────────────────────── §9.2 ───────────────────────────────
def run_table(bound, ops):
pending, frames, refusals, struck = {}, {}, [], 0
STRUCK = object()
for i, op in enumerate(ops):
k = op["op"]
if k == "chunk":
q = pending.setdefault(op["decoder"], [])
b = None
if op.get("binding") is not None:
b, why = read_binding(op["binding"])
if b is None:
raise ValueError("op %d: binding %s" % (i, why))
hit = [e for e in q if e[0] == op["timestamp"]]
if hit:
if hit[0][1] is not STRUCK:
refusals.append({"at": i, "reason": "binding_ambiguous"})
hit[0][1] = STRUCK
else:
q.append([op["timestamp"], b])
if len(q) > bound:
q.pop(0)
struck += 1
elif k == "output":
q = pending.setdefault(op["decoder"], [])
b = None
for j, e in enumerate(q):
if e[0] == op["timestamp"]:
q.pop(j)
b = None if e[1] is STRUCK else e[1]
break
frames[op["frame"]] = b
elif k == "clone":
frames[op["frame"]] = frames[op["from"]]
elif k == "transfer":
b = frames.pop(op["from"])
frames[op["frame"]] = b if op.get("with_binding") is True else None
elif k == "close":
frames.pop(op["frame"], None)
elif k in ("reset", "flush"):
struck += len(pending.pop(op["decoder"], []))
else:
raise ValueError("op %d: %r" % (i, k))
return {"frames": {f: (canonical(b) if b is not None else None) for f, b in frames.items()},
"refusals": refusals, "struck": struck}
# ─────────────────────────────── the chain ───────────────────────────────
_HASHES = {}
def b3(data):
data = bytes(data)
h = _HASHES.get(data)
if h is None:
h = _HASHES[data] = B3.blake3(data)
return h
def u64(n):
return n.to_bytes(8, "big")
def lp(s):
b = s.encode("utf-8")
return len(b).to_bytes(4, "big") + b
def check_chain(chain, load, fails):
prev_link = None
for gi, g in enumerate(chain["groups"]):
r = g["receipt"]
pk, sig = bytes.fromhex(r["signer_pubkey"]), bytes.fromhex(r["sig"])
payload = (b"moq-jwp:group-sign\x01" + u64(r["track_alias"]) + u64(r["group_id"]) + bytes.fromhex(r["root_hash"])
+ u64(r["leaf_count"]) + u64(r["joules_total"]) + lp(r["group_start"]) + lp(r["group_end"]) + pk + lp(r["signer_id"]))
if ED.verify(pk, payload, sig) is not None:
fails.append("chain group %d: the signature does not verify" % gi)
if r["signer_pubkey"] != chain["signer_pubkey"]:
fails.append("chain group %d: another key" % gi)
can = payload + sig
if can.hex() != g["canonical"]:
fails.append("chain group %d: canonical bytes" % gi)
link = b3(b"moq-jwp:receipt-link\x01" + can)
if link.hex() != g["link_hash"]:
fails.append("chain group %d: link hash" % gi)
want_parent = prev_link.hex() if prev_link is not None else None
if r["parent_receipt_hash"] != want_parent:
fails.append("chain group %d: parent link" % gi)
prev_link = link
for o in g["objects"]:
env = load(o["envelope"]["file"]) if isinstance(o["envelope"], dict) else bytes.fromhex(o["envelope"])
orc = o["receipt"]
ch = b3(b"moq-jwp:object\x01" + env)
ok = ch.hex() == orc["content_hash"]
if (orc["track_alias"], orc["group_id"]) != (r["track_alias"], r["group_id"]) or orc["merkle_proof"]["leaf_index"] != orc["object_id"]:
ok = False
h = b3(b"moq-jwp:merkle-leaf\x01" + bytes.fromhex(orc["content_hash"]))
bits = orc["merkle_proof"]["direction_bits"]
for i, s in enumerate(orc["merkle_proof"]["siblings"]):
s = bytes.fromhex(s)
h = b3(b"moq-jwp:merkle-node\x01" + (s + h if (bits >> i) & 1 else h + s))
if h.hex() != r["root_hash"]:
ok = False
want = "verified" if ok else "refused"
if o["outcome"] != want:
fails.append("chain %s: %s, the corpus says %s" % (o["name"], want, o["outcome"]))
if ok:
tier = frame_tier(chain["route"], registered_tier(chain["capability"]), False, False)
b = canonical({"alg": ALG, "digest": ch.hex(), "receipt": link.hex(), "tier": tier})
if o["binding"] != b:
fails.append("chain %s: binding %s" % (o["name"], o["binding"]))
elif o["binding"] is not None:
fails.append("chain %s: a binding for an object that does not verify" % o["name"])
# ─────────────────────────────── the corpus ───────────────────────────────
def verify(doc, load):
fails = []
seen = set()
def take(x):
if isinstance(x, str):
seen.add(x)
for c in doc["routes"]:
table, reason = route_report(c["report"])
if reason is not None:
if c["outcome"] != "refused" or c["reason"] != reason or c["table"] is not None:
fails.append("route %s: refused %s" % (c["name"], reason))
elif c["outcome"] != "routed" or c["reason"] is not None or c["table"] != table:
got = {e["name"]: (e["route"], e["reason"]) for e in table}
fails.append("route %s: %s" % (c["name"], json.dumps(got)[:300]))
take(c["reason"])
for e in c["table"] or []:
take(e["reason"])
take(e["energy"]["reason"])
for leg in e["legs"].values():
take(leg)
for c in doc["choices"]:
got, reason = choose_report(c["report"])
table = route_report(c["report"])[0] if reason is None else None
want = ("refused", reason, None, None) if reason is not None else ("chosen", None, table, got)
if want != (c["outcome"], c["reason"], c["table"], c["choices"]):
fails.append("choice %s: %s" % (c["name"], json.dumps(got if got is not None else reason)[:300]))
take(c["reason"])
for g in c["choices"] or []:
take(g["reason"])
for c in doc["constraints"]:
o, r = evaluate(c["sets"], c["params"])
if (o, r) != (c["outcome"], c["reason"]):
fails.append("constraints %s: %s %s" % (c["name"], o, r))
take(r)
for c in doc["bindings"]:
b, r = read_binding(c["value"])
got = ("read", None, canonical(b), b["tier"]) if b else ("refused", r, None, None)
if got != (c["outcome"], c["reason"], c["canonical"], c["tier"]):
fails.append("binding %s: %s" % (c["name"], got))
take(r)
for c in doc["frame_tiers"]:
if frame_tier(c["route"], c["route_tier"], c["format_null"], c["altered"]) != c["tier"]:
fails.append("frame tier %s" % c)
check_chain(doc["chain"], load, fails)
for g in doc["chain"]["groups"]:
for o in g["objects"]:
if o["outcome"] == "refused":
seen.add("binding_unverified_reference")
for c in doc["frames"]:
s = frame_state(c["binding"], c["object"], c["chain"], c["provenance"])
if s != c["result"]:
fails.append("frame %s: %s" % (c["name"], s))
for c in doc["tables"]:
try:
r = run_table(c["bound"], c["ops"])
except (KeyError, ValueError) as e:
fails.append("table %s: %s" % (c["name"], e))
continue
if r != c["result"]:
fails.append("table %s: %s" % (c["name"], r))
for x in r["refusals"]:
take(x["reason"])
for code in REASONS:
if code not in seen:
fails.append("nothing in the corpus gives %s" % code)
return fails
# ─────────────────── platform-decode §2: the query read from a payload ───────────────────
#
# A second reading of the queries of platform-decode.md §2, from its text: every codec string,
# size, rate, bit rate and description, and the chunks of §2.3, recomputed from each payload of
# platform-decode-conformance/queries.json. It reads §2.1's VP9 level and colour tables from the
# text.
PD = read(os.path.join(WAI, "extensions", "platform-decode.md"))
class Refused(Exception):
pass
def need(cond, why):
if not cond:
raise Refused(why)
def vp9_levels():
out = []
for line in section(PD, "**VP9 levels.**", "**VP9 colour.**").splitlines():
if line.startswith("| `"):
cells = [c.strip().strip("`").replace(" ", "") for c in line.strip().strip("|").split("|")]
out.append((int(cells[0]), int(cells[1]), int(cells[2]), int(cells[3])))
need(len(out) == 14, "platform-decode §2.1 lists %d VP9 levels" % len(out))
return out
def vp9_matrix():
out = {}
for line in section(PD, "**VP9 colour.**", "### 2.2").splitlines():
cells = [c.strip() for c in line.strip().strip("|").split("|")]
if line.startswith("| `") and ticks(cells[0])[0].isdigit():
out[int(ticks(cells[0])[0])] = ticks(cells[2])[0]
return out
class BitReader:
def __init__(self, b):
self.b, self.at = b, 0
def f(self, n):
v = 0
for _ in range(n):
need(self.at < 8 * len(self.b), "a header runs past its data")
v = (v << 1) | ((self.b[self.at >> 3] >> (7 - (self.at & 7))) & 1)
self.at += 1
return v
def ue(self):
z = 0
while self.f(1) == 0:
z += 1
need(z <= 31, "an exp-Golomb code too long")
return (1 << z) - 1 + self.f(z)
def uvlc(self):
z = 0
while self.f(1) == 0:
z += 1
need(z <= 32, "a uvlc() too long")
return 0xFFFFFFFF if z >= 32 else (1 << z) - 1 + self.f(z)
def leb128(b, at):
v = 0
for i in range(8):
need(at + i < len(b), "an OBU size runs past its unit")
v |= (b[at + i] & 0x7F) << (7 * i)
if not b[at + i] & 0x80:
return v, i + 1
raise Refused("an OBU size longer than eight bytes")
def ceil_div(a, b):
return -(-a // b)
def bitrate(nbytes, frames, rate):
if rate is None or frames == 0:
return None
return ceil_div(nbytes * 8 * rate[0], frames * rate[1])
def unescape(nal, header):
out, zeros = bytearray(), 0
for x in nal[header:]:
if zeros >= 2 and x == 3:
zeros = 0
continue
zeros = zeros + 1 if x == 0 else 0
out.append(x)
return bytes(out)
# AV1: SPEC §5's framing and the sequence header (AV1 §5.5).
def av1_sequence(body):
r = BitReader(body)
prof = r.f(3)
need(prof <= 2, "seq_profile %d" % prof)
r.f(1)
reduced = r.f(1)
lvl = tier = 0
if reduced:
lvl = r.f(5)
else:
timing = r.f(1)
dmi, bdl = 0, 0
if timing:
r.f(64)
if r.f(1):
r.uvlc()
dmi = r.f(1)
if dmi:
bdl = r.f(5) + 1
r.f(42)
idp = r.f(1)
for i in range(r.f(5) + 1):
r.f(12)
lv = r.f(5)
ti = r.f(1) if lv > 7 else 0
if dmi and r.f(1):
r.f(2 * bdl + 1)
if idp and r.f(1):
r.f(4)
if i == 0:
lvl, tier = lv, ti
wb, hb = r.f(4) + 1, r.f(4) + 1
r.f(wb + hb)
if not reduced and r.f(1):
r.f(7)
r.f(3)
if not reduced:
r.f(4)
oh = r.f(1)
if oh:
r.f(2)
sct = 2 if r.f(1) else r.f(1)
if sct > 0 and not r.f(1):
r.f(1)
if oh:
r.f(3)
r.f(3)
hbd = r.f(1)
depth = 12 if (prof == 2 and hbd and r.f(1)) else (10 if hbd else 8)
mono = r.f(1) if prof != 1 else 0
desc = (r.f(8), r.f(8), r.f(8)) if r.f(1) else None
if mono:
rng, sx, sy, csp = r.f(1), 1, 1, 0
elif desc == (1, 13, 0):
rng, sx, sy, csp = 1, 0, 0, 0
else:
rng = r.f(1)
if prof == 0:
sx, sy = 1, 1
elif prof == 1:
sx, sy = 0, 0
elif depth == 12:
sx = r.f(1)
sy = r.f(1) if sx else 0
else:
sx, sy = 1, 0
csp = r.f(2) if sx and sy else 0
cp, tc, mc = desc or (1, 1, 1)
head = "av01.%d.%02d%s.%02d" % (prof, lvl, "H" if tier else "M", depth)
tail = ".%d.%d%d%d.%02d.%02d.%02d.%d" % (mono, sx, sy, csp if sx and sy else 0, cp, tc, mc, rng)
return {"codec": head if tail == ".0.110.01.01.01.0" else head + tail, "depth": depth, "reduced": reduced}
def av1_obus(tu):
at = 0
while at < len(tu):
h = tu[at]
need(not h & 0x80, "an OBU whose forbidden bit is set")
need(h & 2, "an OBU without its size field")
hl = 2 if h & 4 else 1
size, sl = leb128(tu, at + hl)
st = at + hl + sl
need(st + size <= len(tu), "an OBU runs past its unit")
yield (h >> 3) & 15, tu[st:st + size]
at = st + size
def av1_units(p):
need(len(p) >= 20, "short AV1 framing")
n, h, w, fn, fd = struct.unpack_from("<5I", p, 0)
need(n and h and w, "no frames or a zero size")
units, at = [], 20
while at < len(p):
need(at + 4 <= len(p), "a unit length runs past")
ln = struct.unpack_from("<I", p, at)[0]
need(ln > 0 and at + 4 + ln <= len(p), "a unit runs past, or is empty")
units.append(p[at + 4:at + 4 + ln])
at += 4 + ln
need(len(units) == n, "%d units where the framing says %d" % (len(units), n))
return n, h, w, fn, fd, units
def av1_query(p):
n, h, w, fn, fd, units = av1_units(p)
seq = None
for t, body in av1_obus(units[0]):
if t == 1:
seq = av1_sequence(body)
break
need(seq, "no sequence header in the first unit")
rate = (fn, fd) if fn and fd else None
return {"media": "video", "codec": seq["codec"], "width": w, "height": h, "codedWidth": w, "codedHeight": h,
"framerate": list(rate) if rate else None, "bitrate": bitrate(len(p), n, rate), "bitDepth": seq["depth"]}
def av1_chunks(p):
units = av1_units(p)[5]
out, reduced = [], None
for tu in units:
key = None
for t, body in av1_obus(tu):
if t == 1:
reduced = av1_sequence(body)["reduced"]
elif t in (3, 6) and key is None:
need(reduced is not None, "a frame header before a sequence header")
r = BitReader(body)
key = True if reduced else (r.f(1) == 0 and r.f(2) == 0)
need(key is not None, "a unit with no frame header")
out.append((tu, key, None))
return None, out
# Annex B (H.264, H.265).
def nal_units(s):
starts, i = [], 0
while i + 3 <= len(s):
if s[i] == 0 and s[i + 1] == 0 and s[i + 2] == 1:
starts.append((i - 1 if i > 0 and s[i - 1] == 0 else i, i + 3))
i += 3
else:
i += 1
out = []
for k, (sc, st) in enumerate(starts):
end = starts[k + 1][0] if k + 1 < len(starts) else len(s)
while end > st and s[end - 1] == 0:
end -= 1
out.append((sc, s[st:end]))
return out
def hevc_sps(nal):
need(len(nal) >= 3 and (nal[0] >> 1) & 63 == 33, "not an SPS")
r = BitReader(unescape(nal, 2))
r.f(4)
subs = r.f(3)
r.f(1)
space, tier, idc = r.f(2), r.f(1), r.f(5)
compat = 0
for j in range(32):
compat |= r.f(1) << j
cons = [r.f(8) for _ in range(6)]
level = r.f(8)
flags = [(r.f(1), r.f(1)) for _ in range(subs)]
if subs:
r.f(2 * (8 - subs))
for pr, lv in flags:
r.f(88 if pr else 0)
r.f(8 if lv else 0)
r.ue()
chroma = r.ue()
need(chroma <= 3, "chroma_format_idc")
sep = r.f(1) if chroma == 3 else 0
w, h = r.ue(), r.ue()
cw, ch = w, h
if r.f(1):
l, rt, t, b = r.ue(), r.ue(), r.ue(), r.ue()
sw, sh = {1: (2, 2), 2: (2, 1)}.get(chroma, (1, 1))
cw, ch = w - sw * (l + rt), h - sh * (t + b)
need(cw >= 0 and ch >= 0, "a window larger than the picture")
depth = r.ue() + 8
codec = "hev1.%s%d.%X.%s%d" % (["", "A", "B", "C"][space], idc, compat, "H" if tier else "L", level)
last = max([i for i, c in enumerate(cons) if c] or [-1])
codec += "".join(".%X" % c for c in cons[:last + 1])
return {"codec": codec, "width": cw, "height": ch, "codedWidth": w, "codedHeight": h, "bitDepth": depth}
AVC_HIGH = (100, 110, 122, 244, 44, 83, 86, 118, 128, 138, 139, 134, 135)
def avc_sps(nal):
need(len(nal) >= 4 and nal[0] & 0x1F == 7 and not nal[0] & 0x80, "not an SPS")
r = BitReader(unescape(nal, 1))
prof, cons, level = r.f(8), r.f(8), r.f(8)
r.ue()
chroma, sep, depth = 1, 0, 8
if prof in AVC_HIGH:
chroma = r.ue()
need(chroma <= 3, "chroma_format_idc")
if chroma == 3:
sep = r.f(1)
depth = r.ue() + 8
r.ue()
r.f(1)
if r.f(1):
for i in range(8 if chroma != 3 else 12):
if r.f(1):
last = nxt = 8
for _ in range(16 if i < 6 else 64):
if nxt:
c = r.ue()
nxt = (last + ((c + 1) // 2 if c % 2 else -(c // 2))) % 256
last = nxt or last
need(depth <= 14, "bit depth")
r.ue()
poc = r.ue()
if poc == 0:
r.ue()
elif poc == 1:
r.f(1)
r.ue()
r.ue()
k = r.ue()
need(k <= 255, "a long POC cycle")
for _ in range(k):
r.ue()
else:
need(poc == 2, "pic_order_cnt_type")
r.ue()
r.f(1)
wm, hu = r.ue() + 1, r.ue() + 1
fmo = r.f(1)
if not fmo:
r.f(1)
r.f(1)
l = rt = t = b = 0
if r.f(1):
l, rt, t, b = r.ue(), r.ue(), r.ue(), r.ue()
arr = 0 if sep else chroma
sw, sh = {1: (2, 2), 2: (2, 1)}.get(arr, (1, 1))
fld = 1 if fmo else 2
cx, cy = (1, fld) if arr == 0 else (sw, sh * fld)
w, h = wm * 16, hu * 16 * fld
cw, ch = w - cx * (l + rt), h - cy * (t + b)
need(cw > 0 and ch > 0, "a cropping rectangle that leaves no picture")
return {"codec": "avc1.%02X%02X%02X" % (prof, cons, level), "width": cw, "height": ch, "codedWidth": w, "codedHeight": h,
"bitDepth": depth}
def first_slice(nal, avc):
if avc:
return len(nal) >= 2 and nal[0] & 0x1F in (1, 5) and nal[1] & 0x80
return len(nal) >= 3 and (nal[0] >> 1) & 63 <= 31 and nal[2] & 0x80
def annexb_query(p, rate, avc):
nals = nal_units(p)
sps = [n for _, n in nals if n and (n[0] & 0x1F == 7 if avc else len(n) >= 2 and (n[0] >> 1) & 63 == 33)]
need(sps, "no SPS")
q = (avc_sps if avc else hevc_sps)(sps[0])
pics = sum(1 for _, n in nals if first_slice(n, avc))
q.update(media="video", framerate=list(rate) if rate else None, bitrate=bitrate(len(p), pics, rate))
return q
def annexb_chunks(p, avc):
units = []
for sc, n in nal_units(p):
if not n:
continue
t = n[0] & 0x1F if avc else (n[0] >> 1) & 63
vcl = t in (1, 5) if avc else t <= 31
starts = t in (6, 7, 8, 9, 14, 15, 16, 17, 18) if avc else t in (32, 33, 34, 35, 39, 41, 42, 43, 44) or 48 <= t <= 55
if not units or (units[-1][1] and (starts or first_slice(n, avc))):
units.append([sc, False, False])
if vcl:
units[-1][1] = True
units[-1][2] |= (t == 5) if avc else 16 <= t <= 23
if len(units) > 1 and not units[-1][1]:
units.pop()
need(any(u[1] for u in units), "no VCL unit")
out = []
for k, (st, _, key) in enumerate(units):
end = units[k + 1][0] if k + 1 < len(units) else len(p)
out.append((p[st:end], key, None))
need(out[0][1], "the first access unit is not a key one")
return None, out
# IVF (VP9, VP8).
def ivf(p, fourcc):
need(len(p) >= 32 and p[:4] == b"DKIF", "not IVF")
ver, hl = struct.unpack_from("<HH", p, 4)
need(ver == 0 and 32 <= hl <= len(p), "IVF header")
need(p[8:12] == fourcc, "IVF FourCC")
den, num = struct.unpack_from("<II", p, 16)
frames, at = [], hl
while at < len(p):
need(at + 12 <= len(p), "a frame header runs past")
n, pts = struct.unpack_from("<IQ", p, at)
need(n > 0 and at + 12 + n <= len(p), "a frame runs past, or is empty")
frames.append((pts, p[at + 12:at + 12 + n]))
at += 12 + n
need(frames, "no frames")
rate = None
k = len(frames)
if k >= 2 and den and num and frames[-1][0] > frames[0][0]:
a, b = (k - 1) * den, (frames[-1][0] - frames[0][0]) * num
g = math.gcd(a, b)
if a // g < 2 ** 32 and b // g < 2 ** 32:
rate = (a // g, b // g)
return frames, rate
def vp9_key(f):
r = BitReader(f)
need(r.f(2) == 2, "frame_marker")
lo = r.f(1)
prof = (r.f(1) << 1) | lo
if prof == 3:
need(r.f(1) == 0, "reserved_zero")
if r.f(1):
return None
ft = r.f(1)
r.f(2)
if ft:
return None
need(r.f(24) == 0x498342, "frame_sync_code")
depth = (12 if r.f(1) else 10) if prof >= 2 else 8
cs = r.f(3)
if cs != 7:
rng = r.f(1)
if prof in (1, 3):
sx, sy = r.f(1), r.f(1)
r.f(1)
else:
sx = sy = 1
else:
need(prof in (1, 3), "RGB in profile 0 or 2")
rng, sx, sy = 1, 0, 0
r.f(1)
need(cs != 6, "the reserved colour space")
return {"profile": prof, "depth": depth, "cs": cs, "range": rng, "sx": sx, "sy": sy, "w": r.f(16) + 1, "h": r.f(16) + 1}
def vp9_query(p, given):
frames, rate = ivf(p, b"VP90")
k = vp9_key(frames[0][1])
need(k, "the first frame is not a key frame")
rate = rate or given
w, h = k["w"], k["h"]
lvl = None
for value, max_rate, max_size, breadth in vp9_levels():
if w * h <= max_size and max(w, h) <= breadth and (rate is None or w * h * rate[0] <= max_rate * rate[1]):
lvl = value
break
need(lvl is not None, "past every level")
sub = {(1, 1): "01", (1, 0): "02", (0, 0): "03"}.get((k["sx"], k["sy"]))
need(sub, "4:4:0")
head = "vp09.%02d.%02d.%02d" % (k["profile"], lvl, k["depth"])
tail = ".%s.01.01.%s.%02d" % (sub, vp9_matrix()[k["cs"]], k["range"])
return {"media": "video", "codec": head if tail == ".01.01.01.01.00" else head + tail, "width": w, "height": h,
"codedWidth": w, "codedHeight": h, "framerate": list(rate) if rate else None,
"bitrate": bitrate(len(p), len(frames), rate), "bitDepth": k["depth"]}
def vp8_key(f):
need(len(f) >= 3, "a short frame")
tag = f[0] | f[1] << 8 | f[2] << 16
need((tag >> 1) & 7 <= 3, "a VP8 version past 3")
if tag & 1:
return None
need(len(f) >= 10 and f[3:6] == b"\x9d\x01\x2a", "the key frame start code")
w, h = struct.unpack_from("<HH", f, 6)
need(w & 0x3FFF and h & 0x3FFF, "a zero size")
return w & 0x3FFF, h & 0x3FFF
def vp8_query(p, given):
frames, rate = ivf(p, b"VP80")
k = vp8_key(frames[0][1])
need(k, "the first frame is not a key frame")
rate = rate or given
return {"media": "video", "codec": "vp8", "width": k[0], "height": k[1], "codedWidth": k[0], "codedHeight": k[1],
"framerate": list(rate) if rate else None, "bitrate": bitrate(len(p), len(frames), rate), "bitDepth": 8}
def ivf_chunks(p, fourcc):
frames, _ = ivf(p, fourcc)
out = [(f, (vp9_key(f) if fourcc == b"VP90" else vp8_key(f)) is not None, None) for _, f in frames]
need(out[0][1], "the first frame is not a key frame")
return None, out
# Audio.
def audio(codec, rate, ch, br, depth, desc):
q = {"media": "audio", "codec": codec, "sampleRate": rate, "numberOfChannels": ch, "bitrate": br, "bitDepth": depth,
"description": desc.hex() if desc is not None else None}
a = {"contentType": 'audio/mp4; codecs="%s"' % codec, "channels": str(ch), "samplerate": rate}
if br is not None:
a["bitrate"] = br
q["decodingConfiguration"] = {"type": "media-source", "audio": a}
q["decoderConfig"] = {"codec": codec, "sampleRate": rate, "numberOfChannels": ch}
return q
def rate_over(nbytes, samples, rate):
return ceil_div(nbytes * 8 * rate, samples) if samples > 0 else None
def crc32_ogg(page):
c = 0
for i, x in enumerate(page):
c ^= (0 if 22 <= i < 26 else x) << 24
for _ in range(8):
c = ((c << 1) ^ 0x104C11DB7) if c & 0x80000000 else c << 1
return c & 0xFFFFFFFF
def ogg(p):
pkts, cur, at, serial, seq, gran = [], None, 0, None, None, None
while at < len(p):
need(at + 27 <= len(p) and p[at:at + 4] == b"OggS" and p[at + 4] == 0, "an Ogg page header")
flags = p[at + 5]
g, s, n, crc = struct.unpack_from("<QIII", p, at + 6)
nseg = p[at + 26]
lace = p[at + 27:at + 27 + nseg]
end = at + 27 + nseg + sum(lace)
need(len(lace) == nseg and end <= len(p), "an Ogg page runs past")
need(crc32_ogg(p[at:end]) == crc, "an Ogg CRC")
if serial is None:
need(flags & 2, "the first page does not begin the stream")
serial = s
else:
need(s == serial and not flags & 2, "a multiplexed or chained stream")
need(n == (seq + 1) & 0xFFFFFFFF, "a page out of sequence")
seq = n
need(bool(flags & 1) == (cur is not None), "a continuation flag")
o = at + 27 + nseg
for x in lace:
cur = (cur or b"") + p[o:o + x]
o += x
if x < 255:
pkts.append(cur)
cur = None
if g != 0xFFFFFFFFFFFFFFFF:
gran = g
at = end
need(cur is None and serial is not None, "a packet runs past the last page")
return pkts, gran
def opus_samples(pk):
need(len(pk) >= 1, "an empty packet")
cfg = pk[0] >> 3
frame = (480, 960, 1920, 2880)[cfg % 4] if cfg < 12 else ((480, 960)[cfg % 2] if cfg < 16 else (120, 240, 480, 960)[cfg % 4])
code = pk[0] & 3
if code == 0:
cnt = 1
elif code in (1, 2):
cnt = 2
else:
need(len(pk) >= 2 and pk[1] & 0x3F, "a code 3 packet with no count")
cnt = pk[1] & 0x3F
need(frame * cnt <= 5760, "more than 120 ms")
return frame * cnt
def ogg_opus(p):
pkts, gran = ogg(p)
need(pkts and len(pkts[0]) >= 19 and pkts[0][:8] == b"OpusHead" and pkts[0][8] >> 4 == 0, "OpusHead")
head = pkts[0]
ch, fam = head[9], head[18]
need(ch >= 1 and not (fam == 0 and ch > 2), "channels")
if fam:
need(len(head) >= 21 + ch and head[19] >= 1 and head[20] <= head[19], "a channel mapping table")
need(len(pkts) >= 3 and pkts[1][:8] == b"OpusTags", "OpusTags and an audio packet")
for a in pkts[2:]:
opus_samples(a)
return pkts, gran, ch, struct.unpack_from("<H", head, 10)[0]
def opus_query(p):
pkts, gran, ch, skip = ogg_opus(p)
br = rate_over(len(p), gran - skip, 48000) if gran is not None and gran >= skip else None
return audio("opus", 48000, ch, br, None, pkts[0])
def opus_chunks(p):
pkts = ogg_opus(p)[0]
return pkts[0], [(a, True, opus_samples(a)) for a in pkts[2:]]
def packet_query(p):
n = opus_samples(p)
return audio("opus", 48000, 2 if p[0] & 4 else 1, rate_over(len(p), n, 48000), None, None)
def flac_meta(p):
need(p[:4] == b"fLaC", "fLaC")
at, info = 4, None
while True:
need(at + 4 <= len(p), "a block header runs past")
last, kind = p[at] >> 7, p[at] & 0x7F
ln = int.from_bytes(p[at + 1:at + 4], "big")
need(at + 4 + ln <= len(p) and kind != 127, "a metadata block")
if info is None:
need(kind == 0 and ln == 34, "STREAMINFO first")
b = p[at + 4:at + 38]
v = int.from_bytes(b[10:18], "big")
info = {"min": int.from_bytes(b[0:2], "big"), "max": int.from_bytes(b[2:4], "big"), "rate": v >> 44,
"ch": ((v >> 41) & 7) + 1, "bps": ((v >> 36) & 31) + 1, "total": v & 0xFFFFFFFFF}
need(info["rate"] and info["bps"] >= 4 and 16 <= info["min"] <= info["max"], "STREAMINFO values")
else:
need(kind != 0, "a second STREAMINFO")
at += 4 + ln
if last:
return info, at
def crc8(b):
c = 0
for x in b:
c ^= x
for _ in range(8):
c = ((c << 1) ^ 0x07) & 0xFF if c & 0x80 else (c << 1) & 0xFF
return c
def crc16(c, x):
c ^= x << 8
for _ in range(8):
c = ((c << 1) ^ 0x8005) & 0xFFFF if c & 0x8000 else (c << 1) & 0xFFFF
return c
def flac_header(p, at):
b = p[at:at + 32]
if len(b) < 6 or b[0] != 0xFF or b[1] & 0xFE != 0xF8:
return None
bs, sr, ch, dp = b[2] >> 4, b[2] & 15, b[3] >> 4, (b[3] >> 1) & 7
if bs == 0 or sr == 15 or ch > 10 or dp == 3 or b[3] & 1:
return None
ones = 0
while ones < 8 and b[4] & (0x80 >> ones):
ones += 1
if ones == 1 or ones == 8:
return None
o = 5
for _ in range(max(ones - 1, 0)):
if o >= len(b) or b[o] & 0xC0 != 0x80:
return None
o += 1
if bs == 1:
blk = 192
elif bs <= 5:
blk = 576 << (bs - 2)
elif bs == 6:
if o >= len(b):
return None
blk, o = b[o] + 1, o + 1
elif bs == 7:
if o + 1 >= len(b):
return None
blk, o = (b[o] << 8 | b[o + 1]) + 1, o + 2
else:
blk = 256 << (bs - 8)
o += {12: 1, 13: 2, 14: 2}.get(sr, 0)
if o >= len(b) or crc8(b[:o]) != b[o]:
return None
return o + 1, blk
def flac_query(p):
info, first = flac_meta(p)
return audio("flac", info["rate"], info["ch"], rate_over(len(p), info["total"], info["rate"]), info["bps"], p[:first])
def flac_chunks(p):
_, first = flac_meta(p)
out, st = [], first
while st < len(p):
hd = flac_header(p, st)
need(hd, "a frame header that does not read")
c = 0
for x in p[st:st + hd[0]]:
c = crc16(c, x)
i = st + hd[0]
while True:
if i >= len(p):
need(c == 0, "a frame whose CRC-16 never matches")
break
if c == 0 and i >= st + hd[0] + 2 and p[i] == 0xFF and flac_header(p, i):
break
c = crc16(c, p[i])
i += 1
out.append((p[st:i], True, hd[1]))
st = i
need(out, "no frame")
return p[:first], out
AAC_RATES = (96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350)
def adts(p):
out, at, first = [], 0, None
while at < len(p):
b = p[at:at + 7]
need(len(b) == 7 and b[0] == 0xFF and b[1] >> 4 == 15 and (b[1] >> 1) & 3 == 0, "an ADTS sync word or layer")
cfg = ((b[1] >> 3) & 1, b[2] >> 6, (b[2] >> 2) & 15, (b[2] & 1) << 2 | b[3] >> 6)
ln = (b[3] & 3) << 11 | b[4] << 3 | b[5] >> 5
need(cfg[2] <= 12 and cfg[3] != 0 and not (cfg[0] == 1 and cfg[1] == 3), "an ADTS configuration")
need(ln >= (7 if b[1] & 1 else 9) and at + ln <= len(p), "an ADTS frame length")
first = first or cfg
need(cfg == first, "another configuration")
out.append((p[at:at + ln], True, 1024 * ((b[6] & 3) + 1)))
at += ln
need(out, "no frame")
return first, out
def aac_query(p):
(iid, prof, sf, chc), frames = adts(p)
codec = "mp4a.40.%d" % (prof + 1) if iid == 0 else "mp4a.%02x" % (0x66 + prof)
rate = AAC_RATES[sf]
return audio(codec, rate, 8 if chc == 7 else chc, rate_over(len(p), sum(f[2] for f in frames), rate), None, None)
def video_configs(q):
if q["media"] != "video":
return q
v = {"contentType": 'video/mp4; codecs="%s"' % q["codec"], "width": q["width"], "height": q["height"],
"bitrate": q["bitrate"], "framerate": q["framerate"][0] / q["framerate"][1] if q["framerate"] else None}
q["decodingConfiguration"] = {"type": "media-source", "video": v} if q["framerate"] and q["bitrate"] is not None else None
for k, hw in (("decoderConfigPreferHardware", "prefer-hardware"), ("decoderConfigNoPreference", "no-preference")):
q[k] = {"codec": q["codec"], "codedWidth": q["codedWidth"], "codedHeight": q["codedHeight"], "hardwareAcceleration": hw}
return q
QUERY = {
"wai.video.av1": lambda p, r: av1_query(p), "wai.video.av1.lossless": lambda p, r: av1_query(p),
"wai.video.hevc": lambda p, r: annexb_query(p, r, False), "wai.video.avc": lambda p, r: annexb_query(p, r, True),
"wai.video.vp9": vp9_query, "wai.video.vp8": vp8_query,
"wai.audio.opus": lambda p, r: opus_query(p), "wai.audio.opus.packet": lambda p, r: packet_query(p),
"wai.audio.flac": lambda p, r: flac_query(p), "wai.audio.aac": lambda p, r: aac_query(p),
}
CHUNKS = {
"wai.video.av1": av1_chunks, "wai.video.av1.lossless": av1_chunks,
"wai.video.hevc": lambda p: annexb_chunks(p, False), "wai.video.avc": lambda p: annexb_chunks(p, True),
"wai.video.vp9": lambda p: ivf_chunks(p, b"VP90"), "wai.video.vp8": lambda p: ivf_chunks(p, b"VP80"),
"wai.audio.opus": opus_chunks, "wai.audio.opus.packet": lambda p: (None, [(p, True, opus_samples(p))]),
"wai.audio.flac": flac_chunks, "wai.audio.aac": lambda p: (None, adts(p)[1]),
}
def mapped_caps():
"""The capabilities platform-decode §2's tables map, read from the text."""
out = []
for head, stop in (("### 2.1 ", "**VP9 levels.**"), ("### 2.2 ", "### 2.3 ")):
for line in section(PD, head, stop).splitlines():
if line.startswith("| `wai."):
out += [c for c in ticks(line.split("|")[1]) if c.startswith("wai.")]
return out
def envelope_payload(b):
need(b[:4] == b"WAI1", "not a WAI1 envelope")
ml = struct.unpack_from("<I", b, 4)[0]
cap = json.loads(b[8:8 + ml])["model_requirement"]["capability"]
pl = struct.unpack_from("<I", b, 8 + ml)[0]
return cap, b[12 + ml:12 + ml + pl]
def chunk_summary(desc, chunks):
samples = [c[2] for c in chunks]
return {"chunks": len(chunks), "keys": sum(1 for c in chunks if c[1]), "firstKey": chunks[0][1] if chunks else None,
"bytes": sum(len(c[0]) for c in chunks), "samples": None if None in samples else sum(samples),
"descriptionBytes": None if desc is None else len(desc)}
def verify_queries(qdoc, root):
fails, n = [], 0
caps = mapped_caps()
if sorted(caps) != sorted(QUERY):
fails.append("platform-decode §2 maps %s, this verifier reads %s" % (sorted(caps), sorted(QUERY)))
for c in qdoc["cases"]:
cid, cap = c["id"], c["capability"]
try:
if "sps_hex" in c:
got = hevc_sps(bytes.fromhex(c["sps_hex"]))
summary = None
else:
b = open(os.path.join(root, c.get("payload_file") or c["payload_raw"]), "rb").read()
if "payload_file" in c:
ecap, b = envelope_payload(b)
need(ecap == cap, "the envelope's capability")
for at, x in c.get("mutate", []):
b = b[:at] + bytes([b[at] ^ x]) + b[at + 1:]
rate = tuple(c["rate"]) if c.get("rate") else None
got = video_configs(QUERY[cap](b, rate))
d, ch = CHUNKS[cap](b)
summary = chunk_summary(d, ch)
if c.get("refused"):
fails.append("query %s: read where the case says it is refused" % cid)
continue
for k, want in c["expect"].items():
if got.get(k, "absent") != want:
fails.append("query %s: %s is %s, the case says %s" % (cid, k, json.dumps(got.get(k, "absent")), json.dumps(want)))
for k, want in (c.get("chunks") or {}).items():
if summary is None or summary.get(k, "absent") != want:
fails.append("query %s: chunks %s is %s, the case says %s" % (cid, k, summary and summary.get(k), want))
except Refused as e:
if not c.get("refused"):
fails.append("query %s: refused (%s)" % (cid, e))
n += 1
return fails, n
def query_mutations(qdoc):
"""Copies of the query vectors, one defect each, every one of which must fail."""
out = []
def case(d, cid):
return next(c for c in d["cases"] if c["id"] == cid)
for c in qdoc["cases"]:
cid = c["id"]
if c.get("refused"):
out.append(("%s read" % cid, lambda d, cid=cid: case(d, cid).update(refused=False, expect={"codec": "?"})))
continue
out.append(("%s's codec string changed" % cid, lambda d, cid=cid: case(d, cid)["expect"].update(codec=case(d, cid)["expect"]["codec"] + "0")))
for k in ("bitrate", "width", "sampleRate", "numberOfChannels"):
if isinstance(c["expect"].get(k), int):
out.append(("%s's %s moved" % (cid, k), lambda d, cid=cid, k=k: case(d, cid)["expect"].update({k: case(d, cid)["expect"][k] + 1})))
if isinstance((c.get("chunks") or {}).get("chunks"), int):
out.append(("%s's chunk count moved" % cid, lambda d, cid=cid: case(d, cid)["chunks"].update(chunks=case(d, cid)["chunks"]["chunks"] + 1)))
out.append(("%s's key count moved" % cid, lambda d, cid=cid: case(d, cid)["chunks"].update(keys=case(d, cid)["chunks"]["keys"] - 1)))
if c.get("payload_raw") and not c.get("mutate"):
out.append(("%s's payload cut" % cid, lambda d, cid=cid: case(d, cid).update(mutate=[[0, 0xFF]])))
return out
KNOWN = ["report_malformed", "not_dispatchable", "tier_exceeds_class", "constraints_unevaluable", "constraints_unmet",
"no_ua_mapping", "codec_mismatch", "codec_unregistered", "ua_not_asked", "ua_unsupported", "ua_inexact",
"ua_unprobed", "ua_probe_mismatch", "no_host", "host_inexact", "no_wasm", "no_decoder", "no_alternative",
"constraint_malformed",
"parameter_unregistered", "energy_class_invalid", "unmetered_as_zero", "energy_count_invalid",
"energy_evidence_missing", "binding_malformed", "binding_alg_unknown", "binding_unverified_reference",
"binding_ambiguous"]
def find(doc, family, name):
for c in doc[family]:
if c["name"] == name:
return c
raise KeyError(name)
def entry(doc, case, obj):
for e in find(doc, "routes", case)["table"]:
if e["name"] == obj:
return e
raise KeyError(obj)
def mutations(doc):
"""(name, change to a copy of the document, change to the files or None)."""
def m(f):
return f
def flip_hex(s, i=0):
return s[:i] + ("0" if s[i] != "0" else "1") + s[i + 1:]
out = []
out.append(("a route changed", m(lambda d: entry(d, "room_tracks", "wai.video.vp9").update(route="wasm")), None))
out.append(("a supported answer withdrawn", m(lambda d: find(d, "routes", "room_tracks")["report"]["ua"]["vp09.00.10.08"].update(supported=False)), None))
out.append(("a probe matched", m(lambda d: find(d, "routes", "need_decode_equivalence")["report"]["ua"]["vp09.00.10.08"].update(probe="match")), None))
out.append(("the registry gate ignored", m(lambda d: entry(d, "unregistered_codecs", "av2").update(asked=True)), None))
out.append(("a leg's reason changed", m(lambda d: entry(d, "fallbacks", "int_motion")["legs"].update(host="no_host")), None))
out.append(("a figure's floor changed", m(lambda d: entry(d, "room_tracks", "wai.video.vp9")["energy"]["figure"].update(per_decode_micro=3051)), None))
out.append(("powerEfficient stated off the ua route", m(lambda d: entry(d, "energy", "estimator_wasm")["energy"].update(power_efficient=True)), None))
out.append(("powerEfficient inverted", m(lambda d: find(d, "routes", "room_tracks")["report"]["ua"]["vp09.00.10.08"].update(power_efficient=False)), None))
out.append(("a zero read as a figure", m(lambda d: entry(d, "energy", "zero_with_class")["energy"].update(reason=None)), None))
out.append(("a learned alternative chosen above a classical one", m(lambda d: find(d, "choices", "classical_before_learned")["choices"][0].update(order=["int_hyper", "int_motion"], chosen="int_hyper")), None))
out.append(("the power-efficient answer withdrawn", m(lambda d: find(d, "choices", "hardware_first")["report"]["ua"]["av01.0.04M.08"].update(power_efficient=False)), None))
out.append(("no answer read as power-efficient", m(lambda d: find(d, "choices", "power_efficient_not_stated")["report"]["ua"]["avc1.64001f"].update(power_efficient=True)), None))
out.append(("an alternative routed none chosen", m(lambda d: find(d, "choices", "no_alternative")["choices"][0].update(chosen="hevc", reason=None)), None))
out.append(("an empty choice read", m(lambda d: find(d, "choices", "empty_choice")["report"]["objects"][0].update(choice="clip")), None))
out.append(("a partial figure read as qualifying", m(lambda d: find(d, "choices", "learned_with_a_partial_measurement")["report"]["energy"]["wai.video.int_hyper wasm"].update(coverage="Whole")), None))
out.append(("a modelled figure read as qualifying", m(lambda d: find(d, "choices", "learned_with_a_modelled_figure")["choices"][0].update(order=["int_hyper", "int_motion"], ranks=["classical", "classical"], chosen="int_hyper")), None))
out.append(("a host-routed decode ranked on the user agent's answer", m(lambda d: find(d, "choices", "power_efficient_answer_for_a_host_routed_decode")["choices"][0].update(order=["hevc", "av1"], ranks=["first", "smooth"], chosen="hevc")), None))
out.append(("a power-efficient answer that is not smooth put first", m(lambda d: find(d, "choices", "smooth_before_power_efficient_but_not_smooth")["choices"][0].update(order=["hevc", "av1"], ranks=["first", "smooth"], chosen="hevc")), None))
out.append(("a rank renamed", m(lambda d: find(d, "choices", "hardware_first")["choices"][0]["ranks"].__setitem__(1, "classical")), None))
out.append(("a route of none ranked before a learned alternative", m(lambda d: find(d, "choices", "learned_when_nothing_else_routes")["choices"][0].update(order=["hevc", "int_hyper"], ranks=[None, "learned-without-budget"])), None))
out.append(("a constraint bound moved", m(lambda d: find(d, "constraints", "bounds_inclusive")["sets"][0]["wai:frame_height"].update(minimum=1081)), None))
out.append(("an unregistered parameter registered", m(lambda d: find(d, "constraints", "unregistered").update(outcome="unsatisfied", reason="constraints_unmet")), None))
out.append(("a digest upper-cased", m(lambda d: find(d, "bindings", "canonical")["value"].update(digest="1F" * 32)), None))
out.append(("an unknown tier kept", m(lambda d: find(d, "bindings", "unknown_tier").update(tier="bit-exact")), None))
out.append(("a frame tier kept through an alteration", m(lambda d: [c.update(tier=c["route_tier"]) for c in d["frame_tiers"] if c["altered"]]), None))
out.append(("a group signature changed", m(lambda d: d["chain"]["groups"][0]["receipt"].update(sig=flip_hex(d["chain"]["groups"][0]["receipt"]["sig"], 3))), None))
out.append(("an object's bytes changed", m(lambda d: d["chain"]["groups"][0]["objects"].__setitem__(1, dict(d["chain"]["groups"][0]["objects"][1], envelope=flip_hex(d["chain"]["groups"][0]["objects"][1]["envelope"], 40)))), None))
out.append(("a link hash changed", m(lambda d: d["chain"]["groups"][0].update(link_hash=flip_hex(d["chain"]["groups"][0]["link_hash"]))), None))
out.append(("a binding names another receipt", m(lambda d: d["chain"]["groups"][1]["objects"][0].update(binding=d["chain"]["groups"][0]["objects"][0]["binding"])), None))
out.append(("a changed object bound", m(lambda d: d["chain"]["groups"][0]["objects"][2].update(binding=d["chain"]["groups"][0]["objects"][1]["binding"], outcome="verified")), None))
out.append(("the second group unlinked", m(lambda d: d["chain"]["groups"][1]["receipt"].update(parent_receipt_hash=None)), None))
def forged(d):
# The last group's signature changed, and everything derived from it made to agree:
# its canonical bytes, its link hash, and the binding of its object. Only the
# signature check can catch it.
g = d["chain"]["groups"][-1]
sig = flip_hex(g["receipt"]["sig"], 5)
g["receipt"]["sig"] = sig
can = bytes.fromhex(g["canonical"][:-128] + sig)
g["canonical"] = can.hex()
link = b3(b"moq-jwp:receipt-link\x01" + can).hex()
g["link_hash"] = link
for o in g["objects"]:
if o["binding"]:
b = json.loads(o["binding"])
b["receipt"] = link
o["binding"] = canonical(b)
out.append(("a signature forged, everything derived from it agreeing", forged, None))
out.append(("the envelope file changed", None, lambda name, b: b[:-1] + bytes([b[-1] ^ 1]) if name == "int_motion.wai" else b))
out.append(("refused provenance shown verified", m(lambda d: find(d, "frames", "cmaf_provenance_refused")["result"].update(state="verified")), None))
out.append(("a held chain shown verified", m(lambda d: find(d, "frames", "chain_held").update(chain="linked")), None))
out.append(("an ambiguous timestamp bound", m(lambda d: find(d, "tables", "same_timestamp").update(ops=find(d, "tables", "same_timestamp")["ops"][:1] + find(d, "tables", "same_timestamp")["ops"][3:])), None))
out.append(("a clone without its binding", m(lambda d: find(d, "tables", "clone_and_close")["result"]["frames"].update(f0c=None)), None))
out.append(("a bound raised", m(lambda d: find(d, "tables", "bound").update(bound=3)), None))
out.append(("a reason's only case removed", m(lambda d: d["tables"].remove(find(d, "tables", "same_timestamp"))), None))
return out
def never_read(doc):
"""Answers the corpus gives for codec strings no registry row lists, changed every way."""
out = []
for flip in ({"supported": False}, {"probe": "mismatch"}, {"power_efficient": False, "smooth": False}):
d = copy.deepcopy(doc)
for c in d["routes"]:
ua = c["report"].get("ua") if isinstance(c["report"], dict) else None
for k, a in (ua or {}).items():
if not gate(k):
a.update(flip)
out.append((json.dumps(flip), d))
return out
def main():
if len(sys.argv) not in (2, 3):
print(__doc__)
return 2
d = sys.argv[1]
with open(os.path.join(d, "vectors.json"), encoding="utf-8") as f:
doc = json.load(f)
files = {}
def loader(change=None):
def load(name):
if name not in files:
with open(os.path.join(d, name), "rb") as f:
files[name] = f.read()
b = files[name]
return change(name, b) if change else b
return load
bad = 0
if sorted(REASONS) != sorted(KNOWN) or len(REASONS) != len(set(REASONS)):
print("FAIL §11 lists %s, this verifier knows %s" % (sorted(set(REASONS) ^ set(KNOWN)), len(KNOWN)))
bad += 1
fails = verify(doc, loader())
for f in fails:
print("FAIL " + f)
n = sum(len(doc[k]) for k in ("routes", "choices", "constraints", "bindings", "frame_tiers", "frames", "tables"))
n += sum(len(g["objects"]) for g in doc["chain"]["groups"])
print("%s every case gets the outcome the corpus states (%d cases; every reason of §11 given)" % ("PASS" if not fails else "FAIL", n))
bad += bool(fails)
for flip, d2 in never_read(doc):
f2 = verify(d2, loader())
print("%s answers for unregistered codec strings changed (%s) are never read" % ("PASS" if not f2 else "FAIL", flip))
bad += bool(f2)
caught = 0
muts = mutations(doc)
for name, change, file_change in muts:
d2 = copy.deepcopy(doc)
# A mutation may change the registry export as read; each is undone after its run.
saved = copy.deepcopy(CAPS)
if change:
change(d2)
f2 = verify(d2, loader(file_change))
CAPS.clear()
CAPS.update(saved)
if f2:
caught += 1
else:
print("FAIL the mutation '%s' is not caught" % name)
print("%s %d of %d mutated copies fail" % ("PASS" if caught == len(muts) else "FAIL", caught, len(muts)))
bad += caught != len(muts)
if len(sys.argv) == 3:
bad += main_queries(sys.argv[2])
return 1 if bad else 0
def main_queries(qdir):
"""platform-decode §2 over <platform-decode-conformance>/queries.json: every case's query
and chunks recomputed, and every mutated copy of the vectors failing."""
with open(os.path.join(qdir, "queries.json"), encoding="utf-8") as f:
qdoc = json.load(f)
bad = 0
fails, n = verify_queries(qdoc, WAI)
for f in fails:
print("FAIL " + f)
print("%s every query and its chunks are the ones the vectors state (%d cases)" % ("PASS" if not fails else "FAIL", n))
bad += bool(fails)
muts = query_mutations(qdoc)
caught = 0
for name, change in muts:
d2 = copy.deepcopy(qdoc)
change(d2)
if verify_queries(d2, WAI)[0]:
caught += 1
else:
print("FAIL the mutation '%s' of the query vectors is not caught" % name)
print("%s %d of %d mutated copies of the query vectors fail" % ("PASS" if caught == len(muts) else "FAIL", caught, len(muts)))
bad += caught != len(muts)
return bad
if __name__ == "__main__":
sys.exit(main())