Spaces:
Runtime error
Runtime error
| 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) | |