mirror of
https://github.com/mikecarper/MeshCore.git
synced 2026-10-21 19:02:04 +00:00
Keep Companion and Terminal Chat profiles unchanged; retain full-sensor and reduced-sensor OTA builds. Add compact crypto and OTA names, qualified display asset compression, font license packaging, and scope regression tests.
130 lines
6.3 KiB
Python
130 lines
6.3 KiB
Python
"""Production Noto compression preserves all selected font pixels and metrics."""
|
|
from pathlib import Path
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
import zlib
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT / "scripts"))
|
|
import generate_compact_noto as generate
|
|
import generate_compact_arial as codec
|
|
|
|
|
|
HEADER = ROOT / "src/helpers/ui/CompactNotoFonts.h"
|
|
HARNESS = ROOT / "test/fixtures/compact_noto/test.cpp"
|
|
|
|
|
|
class CompactNotoTest(unittest.TestCase):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
cls.fonts, cls.license = generate.read_fonts()
|
|
cls.header = HEADER.read_text(encoding="utf-8")
|
|
|
|
def compile_and_run(self, header=None, invalid=False):
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
folder = Path(directory)
|
|
if header is not None:
|
|
include = folder / "helpers/ui"
|
|
include.mkdir(parents=True)
|
|
(include / "CompactNotoFonts.h").write_text(header, encoding="utf-8")
|
|
(folder / "helpers/ota/tinf").mkdir(parents=True)
|
|
(folder / "helpers/ota/tinf/tinf.h").write_text(
|
|
(ROOT / "src/helpers/ota/tinf/tinf.h").read_text(encoding="utf-8"), encoding="utf-8")
|
|
decoder = folder / "tinf.o"
|
|
subprocess.run([
|
|
"cc", "-Os", "-DMESHCORE_TINF_IMPLEMENTATION=1", "-c",
|
|
str(ROOT / "src/helpers/ota/tinf/tinflate.c"), "-o", str(decoder),
|
|
], check=True)
|
|
executable = folder / "fonts"
|
|
subprocess.run([
|
|
"c++", "-std=c++11", "-O2", "-Wall", "-Wextra", "-Werror",
|
|
f"-I{folder}", f"-I{ROOT / 'src'}", str(HARNESS),
|
|
str(HARNESS.with_name("other.cpp")), str(decoder), "-o", str(executable),
|
|
], check=True)
|
|
return subprocess.run(
|
|
[str(executable)] + (["invalid"] if invalid else []),
|
|
text=True, capture_output=True, check=True).stdout.splitlines()
|
|
|
|
def test_generated_header_matches_authoritative_mode7_and_has_required_notice(self):
|
|
self.assertEqual(self.header, generate.generate_header(self.fonts, self.license))
|
|
self.assertEqual([len(font.original) for font in self.fonts], [4419, 8176])
|
|
self.assertNotIn("FONT_0", self.header)
|
|
self.assertNotIn("MESH_ARIAL_EXPERIMENT", self.header)
|
|
self.assertNotIn("decodeRle", self.header)
|
|
self.assertIn("SIL OPEN FONT LICENSE Version 1.1", self.header)
|
|
self.assertIn("Copyright 2015 Google Inc. All Rights Reserved.", self.header)
|
|
self.assertIn("4efc2789b9fcaa87b6b55f19c25ac9a2919e4f9ca31d6b5b97bea7977d729d4f", self.header)
|
|
self.assertIn("Pillow 12.3.0 / FreeType 2.14.3", self.header)
|
|
|
|
def test_all_glyph_chunks_roundtrip_in_at_most_512_decoded_bytes(self):
|
|
total = 0
|
|
for font in self.fonts:
|
|
packed, rows, offsets, lengths = codec.chunk_font(font)
|
|
raw = [zlib.decompress(packed[offsets[group]:offsets[group + 1]], -15)
|
|
for group in range(len(lengths))]
|
|
self.assertEqual(list(map(len, raw)), list(lengths))
|
|
self.assertLessEqual(max(lengths), 512)
|
|
self.assertEqual(len(rows), 224)
|
|
self.assertEqual(sum(bool(glyph.bitmap) for glyph in font.glyphs), 189)
|
|
for glyph, (location, length, width) in zip(font.glyphs, rows):
|
|
self.assertEqual((length, width), (len(glyph.bitmap), glyph.width))
|
|
if length:
|
|
self.assertEqual(raw[location >> 9][(location & 511):(location & 511) + length], glyph.bitmap)
|
|
else:
|
|
self.assertEqual(location, 0xffff)
|
|
total += len(packed) + len(codec.descriptor(font, rows)) + 2 * (len(offsets) + len(lengths))
|
|
self.assertEqual(total, 6777)
|
|
|
|
def test_google_zopfli_requires_1000_iterations(self):
|
|
from unittest.mock import patch
|
|
codec.chunk_font.cache_clear()
|
|
original = codec.raw_deflate_compress
|
|
with patch.object(codec, "raw_deflate_compress", wraps=original) as compressor:
|
|
generate.generate_header(self.fonts, self.license)
|
|
self.assertEqual(compressor.call_count, 22)
|
|
self.assertTrue(all(call.kwargs == {"numiterations": 1000} for call in compressor.call_args_list))
|
|
|
|
def test_cpp_all448_glyphs_match_raw_mode7_without_experimental_macro(self):
|
|
expected = []
|
|
for id, font in enumerate(self.fonts, 1):
|
|
expected.append("F " + " ".join(map(str, (id, *font.header))))
|
|
for slot, glyph in enumerate(font.glyphs):
|
|
expected.append(f"G {id} {slot + font.header[2]} {glyph.width} {len(glyph.bitmap)} {glyph.bitmap.hex()}")
|
|
self.assertEqual(self.compile_and_run(), expected)
|
|
|
|
def test_cpp_rejects_corrupt_descriptor_deflate_truncation_and_trailing_bytes(self):
|
|
invalid_headers = [re.sub(
|
|
r"(static const uint8_t FONT_1_DESCRIPTOR\[\] = \{\s*"
|
|
r"(?:0x[0-9a-f]{2},\s*){8})0x[0-9a-f]{2},\s*0x[0-9a-f]{2}",
|
|
lambda match: match[1] + "0xff, 0xfe", self.header)]
|
|
invalid_headers.append(re.sub(
|
|
r"(static const uint8_t FONT_1_DATA\[\] = \{\s*)0x[0-9a-f]{2}",
|
|
lambda match: match[1] + "0x07", self.header))
|
|
offsets = re.search(r"static const uint16_t FONT_1_OFFSETS\[\] = \{\s*0, (\d+)", self.header)
|
|
for end in (1, int(offsets[1]) + 1):
|
|
invalid_headers.append(re.sub(
|
|
r"(static const uint16_t FONT_1_OFFSETS\[\] = \{\s*0, )\d+",
|
|
lambda match: match[1] + str(end), self.header))
|
|
for header in invalid_headers:
|
|
self.assertNotEqual(header, self.header)
|
|
self.compile_and_run(header, invalid=True)
|
|
|
|
def test_reader_rejects_missing_source_coverage_or_license(self):
|
|
source = generate.SOURCE.read_text(encoding="utf-8")
|
|
invalid = [source.replace("#if MESH_ARIAL_EXPERIMENT == 7", "#if MESH_ARIAL_EXPERIMENT == 17"),
|
|
source.replace("SIL OPEN FONT LICENSE Version 1.1", "removed license")]
|
|
with tempfile.TemporaryDirectory() as folder:
|
|
path = Path(folder) / "source.h"
|
|
for content in invalid:
|
|
path.write_text(content, encoding="utf-8")
|
|
with self.assertRaises(ValueError):
|
|
generate.read_fonts(path)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|