Record the finite dependency search, Wi-Fi maintenance blocker, and pinned icon provenance. Add bounded host orchestration and fixture coverage, and update release documentation with current evidence.
174 lines
8.8 KiB
Python
174 lines
8.8 KiB
Python
# SPDX-License-Identifier: GPL-3.0-only
|
|
"""Offline checks of actual project icon derivatives, not a raster generator."""
|
|
|
|
import copy
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
import re
|
|
import unittest
|
|
import xml.etree.ElementTree as ET
|
|
|
|
PROJECT = Path(__file__).absolute().parents[2]
|
|
ICONS = PROJECT / "third_party/material-design-icons"
|
|
UPSTREAM = ICONS / "upstream-7.4.47"
|
|
COMMIT = "9e04201d4557e729822fb57f62a316c3dea1d4a8"
|
|
TAG = "5edde266e281d26a03dcfa89fb651183cbab0f2e"
|
|
SVG = "{http://www.w3.org/2000/svg}"
|
|
PINS = {
|
|
"usb.svg": "c9918e9a983fbd788378ca4c524e7a07a0d5eedcaeff73d19814e6f6ae221f22",
|
|
"wifi-strength-4.svg": "89d14daf863076b0f73c76d913212875f2e9bcddaaf49c9e5d1825e0b2dc2d5f",
|
|
"LICENSE": "f3bc8715bad84b26396bb42d2abd11f919cf58163be158eab79ecbaabf84cdf2",
|
|
"package.json": "ffeb0e4cc17b4cb124408bd0068667b9a35e985971792978e40d4cd69c7ca8b7",
|
|
"README.md": "db5acca5eb4e2c9113c3408ed1ae7d17c76f653340a5d4e3f8a58d2bcdf50b7d",
|
|
"meta.json": "ed9d7c7a5ca3ce7dee023fb6c1d62715dbec5b27503d268cbc10b9c8894ef3e8",
|
|
"tag.json": "f02dc7fc1e862c344c66877828b1914543f0656db8544c05a1ed3b7d990b6d75",
|
|
"tree.json": "b75710c2b9ee4d0ad3f79016e9767a85103f16df6cbbe5f0629efc3b470d1569",
|
|
"Apache-2.0.txt": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30",
|
|
}
|
|
|
|
|
|
def load(name):
|
|
return json.loads((UPSTREAM / name).read_bytes())
|
|
|
|
|
|
def geometry(element):
|
|
"""Strict comparison: no coordinate normalization or hidden transforms."""
|
|
if set(element.attrib) != {"id", "viewBox"}:
|
|
raise ValueError("unexpected root/symbol geometry attributes")
|
|
if element.attrib["viewBox"] != "0 0 24 24":
|
|
raise ValueError("unexpected viewBox")
|
|
if len(element) != 1 or element[0].tag != SVG + "path":
|
|
raise ValueError("expected exactly one path")
|
|
if set(element[0].attrib) != {"d"}:
|
|
raise ValueError("unexpected path geometry attributes")
|
|
return element[0].attrib["d"]
|
|
|
|
|
|
class IconProvenanceTests(unittest.TestCase):
|
|
def test_download_pins_and_git_blobs(self):
|
|
records = load("downloads.json")
|
|
self.assertEqual({r["path"] for r in records}, set(PINS))
|
|
self.assertEqual(len(records), len(PINS))
|
|
tree = {x["path"]: x for x in load("tree.json")["tree"]}
|
|
for record in records:
|
|
name = record["path"]
|
|
with self.subTest(name=name):
|
|
data = (UPSTREAM / name).read_bytes()
|
|
self.assertEqual(len(data), record["size"])
|
|
self.assertEqual(hashlib.sha256(data).hexdigest(), PINS[name])
|
|
self.assertEqual(record["sha256"], PINS[name])
|
|
if name not in ("tag.json", "tree.json", "Apache-2.0.txt"):
|
|
path = "svg/" + name if name.endswith(".svg") else name
|
|
self.assertEqual(record["url"],
|
|
"https://raw.githubusercontent.com/Templarian/"
|
|
f"MaterialDesign-SVG/{COMMIT}/{path}")
|
|
self.assertEqual(tree[path]["size"], len(data))
|
|
blob = b"blob " + str(len(data)).encode() + b"\0" + data
|
|
self.assertEqual(hashlib.sha1(blob).hexdigest(), tree[path]["sha"])
|
|
for name in ("usb.svg", "wifi-strength-4.svg", "LICENSE"):
|
|
self.assertEqual((ICONS / name).read_bytes(), (UPSTREAM / name).read_bytes())
|
|
|
|
def test_release_license_authors_and_notice_scope(self):
|
|
tag = load("tag.json")
|
|
self.assertEqual(tag["sha"], TAG)
|
|
self.assertEqual(tag["tag"], "v7.4.47")
|
|
self.assertEqual(tag["object"]["sha"], COMMIT)
|
|
self.assertEqual(tag["object"]["type"], "commit")
|
|
self.assertFalse(tag["verification"]["verified"])
|
|
package = load("package.json")
|
|
self.assertEqual((package["name"], package["version"], package["license"]),
|
|
("@mdi/svg", "7.4.47", "Apache-2.0"))
|
|
expected = {
|
|
"usb": ("Google", "25033E0B-3AD4-414D-9972-559F2690FC1D", "1.5.54"),
|
|
"wifi-strength-4": ("Simran", "41B86B22-7245-4A97-9BAA-3E9EBD44CEB0", "2.3.50"),
|
|
}
|
|
selected = [x for x in load("meta.json") if x["name"] in expected]
|
|
self.assertEqual(len(selected), 2)
|
|
for icon in selected:
|
|
self.assertEqual((icon["author"], icon["id"], icon["version"]), expected[icon["name"]])
|
|
self.assertNotIn("license", icon)
|
|
tree = load("tree.json")
|
|
self.assertFalse(tree["truncated"])
|
|
self.assertFalse([x["path"] for x in tree["tree"] if "notice" in x["path"].lower()])
|
|
|
|
def test_exact_retained_geometry_and_negative_changes(self):
|
|
for name in ("usb.svg", "wifi-strength-4.svg"):
|
|
original = ET.parse(UPSTREAM / name).getroot()
|
|
local = ET.parse(ICONS / name).getroot()
|
|
self.assertEqual(geometry(local), geometry(original))
|
|
changed = copy.deepcopy(local)
|
|
changed[0].set("d", changed[0].get("d").replace("M", "M0,0L", 1))
|
|
self.assertNotEqual(geometry(changed), geometry(original))
|
|
for node in (0, None):
|
|
changed = copy.deepcopy(local)
|
|
(changed if node is None else changed[node]).set("transform", "translate(1 0)")
|
|
with self.assertRaises(ValueError):
|
|
geometry(changed)
|
|
|
|
def test_actual_mockup_paths_and_transforms(self):
|
|
root = ET.parse(PROJECT / "docs/phase7c_icon_mockup.svg").getroot()
|
|
parents = {child: parent for parent in root.iter() for child in parent}
|
|
symbols = {x.get("id"): x for x in root.iter(SVG + "symbol")}
|
|
self.assertEqual(geometry(symbols["usb"]), geometry(ET.parse(UPSTREAM / "usb.svg").getroot()))
|
|
self.assertNotEqual(geometry(symbols["wifi"]),
|
|
geometry(ET.parse(UPSTREAM / "wifi-strength-4.svg").getroot()))
|
|
uses = [x for x in root.iter(SVG + "use") if x.get("href") == "#usb"]
|
|
self.assertEqual([x.attrib for x in uses], [
|
|
{"href": "#usb", "x": "150", "y": "4", "width": "30", "height": "30"},
|
|
{"href": "#usb", "x": "8", "y": "72", "width": "30", "height": "30"},
|
|
])
|
|
|
|
def ancestry(node):
|
|
result = []
|
|
while node in parents:
|
|
node = parents[node]
|
|
if "transform" in node.attrib:
|
|
result.append(node.get("transform"))
|
|
# Nested SVG viewports would require additional mappings.
|
|
if node is not root:
|
|
self.assertNotEqual(node.tag, SVG + "svg")
|
|
return result
|
|
|
|
self.assertEqual(ancestry(uses[0]), [])
|
|
self.assertEqual(parents[parents[uses[0]]].get("id"), "status")
|
|
self.assertEqual(ancestry(uses[1]), ["translate(62 438)"])
|
|
status_uses = [x for x in root.iter(SVG + "use") if x.get("href") == "#status"]
|
|
self.assertEqual([x.attrib for x in status_uses], [{"href": "#status"}] * 4)
|
|
self.assertEqual([ancestry(x) for x in status_uses], [
|
|
["translate(62 98)"], ["translate(592 98)"],
|
|
["translate(62 438)"], ["translate(592 438)"],
|
|
])
|
|
|
|
def test_actual_manual_masks_not_inferred_rasterization(self):
|
|
source = (PROJECT / "src/local_status_ui.c").read_text()
|
|
self.assertIn("hand-rasterized 8x8 derivative", source)
|
|
self.assertIn('adaptations of "wifi-strength-4"', source)
|
|
masks = {
|
|
"usb": "18 3c 18 5a 3e 18 3c 18",
|
|
"wifi_full": "7e 81 3c 42 18 24 00 18",
|
|
"wifi_three": "00 00 3c 42 18 24 00 18",
|
|
"wifi_two": "00 00 00 00 18 24 00 18",
|
|
"wifi_one": "00 00 00 00 00 00 00 18",
|
|
}
|
|
for name, expected in masks.items():
|
|
match = re.search(r"s_icon_" + name + r"\[LOCAL_STATUS_UI_ICON_SIZE\]\s*=\s*\{([^}]+)\}", source)
|
|
self.assertIsNotNone(match)
|
|
actual = re.findall(r"0x([0-9a-fA-F]{2})U", match[1])
|
|
self.assertEqual(bytes.fromhex(" ".join(actual)), bytes.fromhex(expected))
|
|
|
|
def test_all_icon_evidence_is_pinned_in_catalog(self):
|
|
catalog = json.loads((PROJECT / "third_party/release-notices/inputs.json").read_bytes())
|
|
entries = {(x["root"], x["path"]): x for x in catalog["inputs"]}
|
|
paths = [UPSTREAM / name for name in (*PINS, "downloads.json")]
|
|
paths += [ICONS / name for name in ("LICENSE", "usb.svg", "wifi-strength-4.svg")]
|
|
paths += [PROJECT / "docs/icon_provenance.md"]
|
|
for path in paths:
|
|
with self.subTest(path=path.name):
|
|
entry = entries[("project", path.relative_to(PROJECT).as_posix())]
|
|
data = path.read_bytes()
|
|
self.assertIsNone(entry["range"])
|
|
self.assertEqual(entry["size"], len(data))
|
|
self.assertEqual(entry["sha256"], hashlib.sha256(data).hexdigest())
|
|
self.assertEqual(entry["output_sha256"], entry["sha256"])
|