kneifftools / tests /test_image_conversion.py
kneiff's picture
chore(history)!: publish anonymous repository root
2857cf3
Raw
History Blame Contribute Delete
4.56 kB
from __future__ import annotations
from io import StringIO
from pathlib import Path
from PIL import Image
import pytest
from rich.console import Console
from typer.testing import CliRunner
import kneiff.cli.image as cli_image
from kneiff.utils.image import concat as concat_utils
from kneiff.utils.image import png2jpg as png2jpg_utils
from kneiff.progress import ProgressUpdate
from tests._cli_helpers import invoke_cli
def test_png2jpg_composites_alpha_and_writes_subdirectory(tmp_path: Path) -> None:
"""Cover PNG alpha handling and destination routing."""
image_path = tmp_path / "source.png"
image = Image.new("RGBA", (2, 1), color=(255, 0, 0, 128))
image.save(image_path)
exit_code = png2jpg_utils.png2jpg(
tmp_path,
out_subdir="jpg",
quality=95,
subsampling=0,
bg="#ffffff",
)
jpg_path = tmp_path / "jpg" / "source.jpg"
assert exit_code == 0
assert jpg_path.exists()
with Image.open(jpg_path) as converted:
assert converted.mode == "RGB"
red, green, blue = converted.getpixel((0, 0))
assert red > green
assert red > blue
def test_png2jpg_dry_run_does_not_write_files(tmp_path: Path) -> None:
"""Ensure dry-run mode reports success without touching output."""
image_path = tmp_path / "source.png"
Image.new("RGB", (2, 1), color="red").save(image_path)
exit_code = png2jpg_utils.png2jpg(tmp_path, dry_run=True)
assert exit_code == 0
assert not (tmp_path / "jpg" / "source.jpg").exists()
def test_convert_png_jobs_reports_every_status_and_progress(tmp_path: Path) -> None:
converted_source = tmp_path / "converted.png"
skipped_source = tmp_path / "skipped.png"
other_source = tmp_path / "other.png"
for path in (converted_source, skipped_source, other_source):
Image.new("RGB", (2, 1), color="red").save(path)
jobs = png2jpg_utils.discover_png_conversion_jobs(tmp_path, out_subdir="jpg")
skipped_output = tmp_path / "jpg" / "skipped.jpg"
skipped_output.parent.mkdir()
Image.new("RGB", (2, 1), color="blue").save(skipped_output)
updates: list[ProgressUpdate] = []
results = png2jpg_utils.convert_png_jobs(
jobs,
quality=95,
subsampling=0,
bg_rgb=(255, 255, 255),
overwrite=False,
dry_run=False,
progress_callback=updates.append,
)
assert [result.status for result in results] == [
"converted",
"converted",
"skipped",
]
assert len(updates) == 3
assert sum(update.advance for update in updates) == 3
def test_img_png2jpg_redirected_output_keeps_legacy_lines(
tmp_path: Path,
isolated_cli_project: Path,
) -> None:
del isolated_cli_project
source = tmp_path / "source.png"
Image.new("RGB", (2, 1), color="red").save(source)
result = invoke_cli(CliRunner(), ["img", "png2jpg", str(tmp_path)])
assert result.exit_code == 0, result.output
assert f"[ok] {source} -> {tmp_path / 'jpg' / 'source.jpg'}" in result.output
assert "\x1b[" not in result.output
def test_img_png2jpg_terminal_replaces_item_lines_with_progress(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
isolated_cli_project: Path,
) -> None:
del isolated_cli_project
source = tmp_path / "source.png"
Image.new("RGB", (2, 1), color="red").save(source)
terminal_output = StringIO()
console = Console(
file=terminal_output,
force_terminal=True,
color_system=None,
width=100,
)
monkeypatch.setattr(cli_image, "stderr_progress_console", lambda: console)
result = invoke_cli(CliRunner(), ["img", "png2jpg", str(tmp_path)])
assert result.exit_code == 0, result.output
assert "[ok]" not in result.output
assert "PNG conversion complete" in terminal_output.getvalue()
assert "1/1" in terminal_output.getvalue()
def test_concat_imgs_scales_to_exact_width(tmp_path: Path) -> None:
"""Cover horizontal stacking and rounding drift correction."""
left = tmp_path / "left.png"
right = tmp_path / "right.png"
output = tmp_path / "collage.png"
Image.new("RGBA", (10, 5), color=(255, 0, 0, 255)).save(left)
Image.new("RGBA", (20, 10), color=(0, 255, 0, 255)).save(right)
result = concat_utils.concat_imgs(
[left, right],
output=output,
width=33,
gap=3,
bg="transparent",
align="bottom",
)
assert result == output
with Image.open(output) as collage:
assert collage.size == (33, 10)