Files
matrix-screen-controller/核桃派软件源代码/tests/test_bus_font_cache.py
T

113 lines
4.6 KiB
Python

from __future__ import annotations
import base64
import hashlib
import json
from pathlib import Path
import pytest
from app.fonts.store import FontCatalog, FontValidationError
from app.templates.store import TemplateStore, prepare_scene
from app.animations.store import AnimationStore
ID = "builtin:walnut-bus-8x16"
def scene(font=ID):
return prepare_scene(dict(version=1, width=64, height=64,
pixelRgb=base64.b64encode(bytes(64*64*3)).decode("ascii"),
elements=[dict(id="text",type="text",text="高铁路",font=font,size=16,x=0,y=8,align="left",color="#FFFFFF")]))
class ChangingFont:
def __init__(self, catalog):
self.catalog, self.version = catalog, 1
def render_version(self, identifier):
return str(self.version)*64 if identifier == ID else None
def resolve(self, identifier):
return self.catalog.resolve(ID if self.version == 1 else "builtin:walnut-bus-16x32")
def test_template_cache_rebuilt_without_revision_or_record_change(tmp_path):
owner=ChangingFont(FontCatalog(tmp_path))
store=TemplateStore(tmp_path, font_resolver=owner.resolve)
item=store.create("bus",scene())
path=store.thumbnail_path(item["id"])
record=store._record_path(item["id"])
before_bytes, before_record = path.read_bytes(), record.read_bytes()
assert "font_v=" in item["thumbnail_url"]
owner.version=2
restarted=TemplateStore(tmp_path,font_resolver=owner.resolve)
updated=restarted.list()["templates"][0]
assert updated["revision"] == item["revision"]
assert record.read_bytes() == before_record
assert path.read_bytes() != before_bytes
assert updated["thumbnail_url"] != item["thumbnail_url"]
stable=path.stat().st_mtime_ns
TemplateStore(tmp_path,font_resolver=owner.resolve)
assert path.stat().st_mtime_ns == stable
def test_animation_cache_rebuilt_without_scene_or_revision_change(tmp_path):
owner=ChangingFont(FontCatalog(tmp_path))
store=AnimationStore(tmp_path,font_resolver=owner.resolve)
item=store.create_from_frames("bus",[dict(scene=scene(),duration_ms=100)])
frame=store.get(item["id"])["frames"][0]
stored_frame=store._read(item["id"])["frames"][0]
path=store._thumb_dir(item["id"])/stored_frame["thumbnail"]
record=store._path(item["id"])
before_bytes, before_record = path.read_bytes(), record.read_bytes()
owner.version=2
restarted=AnimationStore(tmp_path,font_resolver=owner.resolve)
updated=restarted.get(item["id"])
assert updated["revision"] == item["revision"]
assert record.read_bytes() == before_record
assert path.read_bytes() != before_bytes
assert updated["frames"][0]["thumbnail_url"] != frame["thumbnail_url"]
stable=path.stat().st_mtime_ns
AnimationStore(tmp_path,font_resolver=owner.resolve)
assert path.stat().st_mtime_ns == stable
def test_unrelated_thumbnails_untouched(tmp_path):
owner=ChangingFont(FontCatalog(tmp_path))
store=TemplateStore(tmp_path,font_resolver=owner.resolve)
item=store.create("normal",scene("default"))
path=store.thumbnail_path(item["id"])
stable=path.read_bytes(),path.stat().st_mtime_ns
assert "font_v=" not in item["thumbnail_url"]
owner.version=2
TemplateStore(tmp_path,font_resolver=owner.resolve)
assert (path.read_bytes(),path.stat().st_mtime_ns) == stable
def test_exact_obsolete_retirement_and_corruption_preservation(tmp_path, monkeypatch):
import app.fonts.store as module
bundle=Path(module.__file__).parent/'bundled'
registration=json.loads((bundle/'manifest.json').read_text(encoding='utf-8'))
old_payload=b'old-font-fixture'
old_digest=hashlib.sha256(old_payload).hexdigest()
registration['obsolete']=[old_digest]
registration['aliases']={f'user:{old_digest}:0':ID}
# Substitute only the registration reader, retaining real bundled font
# files and checksum validation; fixture files never need old private state.
real_read=Path.read_text
def read(path,*args,**kwargs):
if path == bundle/'manifest.json':return json.dumps(registration)
return real_read(path,*args,**kwargs)
monkeypatch.setattr(Path,'read_text',read)
root=tmp_path/'fonts';root.mkdir()
old=root/f'{old_digest}.font';old.write_bytes(old_payload)
unrelated=root/'unregistered.font';unrelated.write_bytes(b'keep')
catalog=FontCatalog(tmp_path)
assert not old.exists()
assert unrelated.read_bytes() == b'keep'
assert catalog.resolve(f'user:{old_digest}:0') == catalog.resolve(ID)
old.write_bytes(b'corrupt')
with pytest.raises(FontValidationError,match='preserved'):
FontCatalog(tmp_path)
assert old.read_bytes() == b'corrupt'