89 lines
4.4 KiB
Python
89 lines
4.4 KiB
Python
"""Validate a qualification passport, not browser capability or app performance."""
|
|
import copy
|
|
import json
|
|
import math
|
|
from pathlib import Path
|
|
|
|
HERE = Path(__file__).resolve().parent
|
|
REQUIRED_FALLBACKS = {
|
|
"secure-context", "crypto-worker-wasm", "indexeddb", "persistence", "push",
|
|
"install", "media", "recording-codec", "geolocation", "webauthn",
|
|
"permissions-query", "lifecycle",
|
|
}
|
|
REPRODUCTION = {
|
|
"target_id", "run_utc", "device_model", "cpu", "ram_gib", "os_full_build",
|
|
"browser_full_build", "browser_channel", "installer_source",
|
|
"installer_sha256_or_os_build", "viewport_css", "actual_inner_size_css",
|
|
"dpr", "refresh_hz", "display_mode", "app_commit", "asset_sha256",
|
|
"fixture_sha256", "profile_reset_recipe", "network_profile",
|
|
"measurement_tool_version",
|
|
}
|
|
|
|
|
|
def validate(data):
|
|
targets = data["targets"]
|
|
ids = [t["id"] for t in targets]
|
|
assert len(ids) == len(set(ids)), "Duplicate target"
|
|
assert set(ids) == {"win-chrome", "win-edge", "linux-firefox", "mac-safari",
|
|
"android-chrome", "ios-safari"}, "Missing engine/device target"
|
|
assert set(data["freeze_fields"]) >= REPRODUCTION, "Incomplete reproduction record"
|
|
for t in targets:
|
|
for key in ("device", "cpu", "os", "browser", "channel", "budget"):
|
|
assert isinstance(t[key], str) and t[key].strip(), f"Missing {key}"
|
|
assert t["channel"] == "stable", "Unqualified channel"
|
|
assert t["ram_gib"] > 0 and t["dpr"] > 0 and t["hz"] == 60
|
|
assert len(t["viewport_css"]) == 2 and min(t["viewport_css"]) >= 320
|
|
assert "tab" in t["modes"] and len(t["modes"]) == len(set(t["modes"]))
|
|
floor = {"Chrome":111, "Edge":111, "Firefox":114, "Safari":18}[t["browser"]]
|
|
assert t["minimum_major"] >= floor, "Target below accepted syntax/OS floor"
|
|
b = data["budgets"][t["budget"]]
|
|
assert all(isinstance(v, (int, float)) and v > 0 for v in b.values())
|
|
assert b["warm_start_p95_ms"] <= b["cold_start_p95_ms"]
|
|
assert b["raf_gap_p95_ms"] <= b["raf_gap_max_ms"]
|
|
assert b["ram_growth_mib"] < b["ram_peak_mib"] < t["ram_gib"] * 1024
|
|
assert set(data["fallbacks"]) >= REQUIRED_FALLBACKS, "Missing capability fallback"
|
|
for rule in data["fallbacks"].values():
|
|
assert len(rule["detection"].strip()) >= 20, "No actionable detection"
|
|
assert len(rule["fallback"].strip()) >= 50, "No actionable fallback"
|
|
ios = next(t for t in targets if t["id"] == "ios-safari")
|
|
assert "home-screen" in ios["modes"], "iOS push mode missing"
|
|
|
|
|
|
def p95(values):
|
|
assert values and all(math.isfinite(v) and v >= 0 for v in values)
|
|
return sorted(values)[math.ceil(0.95 * len(values)) - 1]
|
|
|
|
|
|
def main():
|
|
data = json.loads((HERE / "matrix.json").read_text())
|
|
validate(data)
|
|
# Reject plausible omissions and unsafe qualification drift.
|
|
mutants = []
|
|
m = copy.deepcopy(data); m["targets"].pop(); mutants.append(m)
|
|
m = copy.deepcopy(data); m["targets"][0]["cpu"] = ""; mutants.append(m)
|
|
m = copy.deepcopy(data); m["freeze_fields"].remove("browser_full_build"); mutants.append(m)
|
|
m = copy.deepcopy(data); del m["fallbacks"]["crypto-worker-wasm"]; mutants.append(m)
|
|
m = copy.deepcopy(data); m["fallbacks"]["media"]["fallback"] = ""; mutants.append(m)
|
|
m = copy.deepcopy(data); m["targets"][0]["minimum_major"] = 80; mutants.append(m)
|
|
m = copy.deepcopy(data); m["targets"][-1]["modes"] = ["tab"]; mutants.append(m)
|
|
m = copy.deepcopy(data); m["budgets"]["mobile"]["ram_peak_mib"] = 10000; mutants.append(m)
|
|
for i, m in enumerate(mutants, 1):
|
|
try:
|
|
validate(m)
|
|
except AssertionError:
|
|
continue
|
|
raise AssertionError(f"Invalid passport accepted: mutation {i}")
|
|
# Thirty-run nearest-rank semantics: two slow runs fail a 2500 ms p95 gate;
|
|
# a single tail still remains in raw evidence, not silently discarded.
|
|
assert p95([1000] * 28 + [3000, 4000]) == 3000
|
|
assert p95([2500] * 30) == 2500
|
|
assert p95([1000] * 29 + [4000]) == 1000
|
|
print(f"PASS: {len(data['targets'])} target recipes, {len(REPRODUCTION)} freeze fields, "
|
|
f"{len(data['fallbacks'])} capability fallbacks; {len(mutants)} negative mutations rejected; "
|
|
"nearest-rank percentile fixtures passed.")
|
|
print("LIMIT: document validation only; no browser/device execution or app budget pass.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|