101 lines
4.0 KiB
Python
101 lines
4.0 KiB
Python
#!/usr/bin/env python3
|
|
import importlib.util
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
import zipfile
|
|
from pathlib import Path
|
|
|
|
|
|
SCRIPTS_DIR = Path(__file__).resolve().parents[1] / "scripts"
|
|
sys.path.insert(0, str(SCRIPTS_DIR))
|
|
|
|
SPEC = importlib.util.spec_from_file_location("export_images", SCRIPTS_DIR / "export_images.py")
|
|
MODULE = importlib.util.module_from_spec(SPEC)
|
|
assert SPEC and SPEC.loader
|
|
SPEC.loader.exec_module(MODULE)
|
|
|
|
|
|
def make_images_zip(path: Path, names=("1.jpeg", "10.jpeg", "2.jpeg")) -> None:
|
|
# 1x1 white JPEG, the smallest valid payload Pillow can open.
|
|
import base64
|
|
|
|
pixel = base64.b64decode(
|
|
"/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAP//////////////////////////"
|
|
"////////////////////////////////////////////2wBDAf//////////////////"
|
|
"////////////////////////////////////////////wAARCAABAAEDASIAAhEBAxEB"
|
|
"/8QAFQABAQAAAAAAAAAAAAAAAAAAAAX/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/9oADAMB"
|
|
"AAIQAxAAAAGf/8QAFBABAAAAAAAAAAAAAAAAAAAAAP/aAAgBAQABBQJ//8QAFBEBAAAA"
|
|
"AAAAAAAAAAAAAAAAAP/aAAgBAwEBPwF//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgB"
|
|
"AgEBPwF//8QAFBABAAAAAAAAAAAAAAAAAAAAAP/aAAgBAQAGPwJ//8QAFBABAAAAAAAA"
|
|
"AAAAAAAAAAAAAP/aAAgBAQABPyF//9oADAMBAAIAAwAAABCf/8QAFBEBAAAAAAAAAAAA"
|
|
"AAAAAAAAAP/aAAgBAwEPEBB//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEPEBB/"
|
|
"/8QAFBABAAAAAAAAAAAAAAAAAAAAAP/aAAgBAQABPxB//9k="
|
|
)
|
|
with zipfile.ZipFile(path, "w") as archive:
|
|
for name in names:
|
|
archive.writestr(name, pixel)
|
|
|
|
|
|
class ExportImagesTests(unittest.TestCase):
|
|
def test_page_sort_key_orders_numeric_stems(self):
|
|
paths = [Path("10.jpeg"), Path("2.jpeg"), Path("cover.jpeg"), Path("1.jpeg")]
|
|
ordered = sorted(paths, key=MODULE.page_sort_key)
|
|
self.assertEqual(
|
|
[path.name for path in ordered],
|
|
["1.jpeg", "2.jpeg", "10.jpeg", "cover.jpeg"],
|
|
)
|
|
|
|
def test_is_image_zip_accepts_image_entries_only(self):
|
|
with tempfile.TemporaryDirectory() as name:
|
|
root = Path(name)
|
|
good = root / "images.zip"
|
|
make_images_zip(good)
|
|
self.assertTrue(MODULE.is_image_zip(good))
|
|
|
|
bad = root / "text.zip"
|
|
with zipfile.ZipFile(bad, "w") as archive:
|
|
archive.writestr("readme.txt", "hello")
|
|
self.assertFalse(MODULE.is_image_zip(bad))
|
|
self.assertFalse(MODULE.is_image_zip(root / "missing.zip"))
|
|
|
|
def test_unzip_images_flattens_and_sorts(self):
|
|
with tempfile.TemporaryDirectory() as name:
|
|
root = Path(name)
|
|
archive_path = root / "images.zip"
|
|
make_images_zip(archive_path, names=("1.jpeg", "10.jpeg", "2.jpeg", "note.txt"))
|
|
images = MODULE.unzip_images(archive_path, root / "pages")
|
|
self.assertEqual(
|
|
[path.name for path in images], ["1.jpeg", "2.jpeg", "10.jpeg"]
|
|
)
|
|
|
|
def test_stitch_overview_grid(self):
|
|
try:
|
|
image_cls, draw_cls, image_font = MODULE.ensure_pillow()
|
|
except MODULE.ExportError:
|
|
self.skipTest("Pillow is not available")
|
|
with tempfile.TemporaryDirectory() as name:
|
|
root = Path(name)
|
|
images = []
|
|
for index in range(1, 5):
|
|
path = root / f"{index}.jpeg"
|
|
image = image_cls.new("RGB", (320, 180), (index * 40 % 255, 30, 60))
|
|
image.save(path, "JPEG")
|
|
images.append(path)
|
|
overview = MODULE.stitch_overview(
|
|
images, root / "overview.jpg", image_cls, draw_cls, image_font
|
|
)
|
|
self.assertTrue(overview.is_file())
|
|
with image_cls.open(overview) as result:
|
|
self.assertEqual(
|
|
result.width,
|
|
3 * MODULE.OVERVIEW_THUMB_WIDTH + 4 * MODULE.OVERVIEW_GAP,
|
|
)
|
|
rows = 2
|
|
cell = MODULE.OVERVIEW_LABEL_HEIGHT + 360
|
|
self.assertEqual(result.height, rows * cell + (rows + 1) * MODULE.OVERVIEW_GAP)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|