from types import SimpleNamespace from unittest.mock import Mock import pytest from PIL import Image from tests.anyimage.server.support import load_server_module @pytest.fixture def media(): return load_server_module("media.input") @pytest.mark.parametrize("suffix", [".PNG", ".MP4", ".webp", ".mts"]) def test_supported_format_is_case_insensitive(media, tmp_path, suffix): path = tmp_path / f"input{suffix}" path.touch() assert media.validate_input_path(path) == path @pytest.mark.parametrize("suffix", [".tiff", ""]) def test_frontend_and_backend_share_format_reason(media, tmp_path, suffix): path = tmp_path / f"input{suffix}" path.touch() with pytest.raises(RuntimeError) as preflight: media.validate_input_path(path) with pytest.raises(RuntimeError) as backend: media.prepare_input(path, 16, tmp_path) assert str(preflight.value) == str(backend.value) assert f"Unsupported input format: {suffix or '(no extension)'}" in str(backend.value) assert "Supported images: .jpeg, .jpg, .png, .webp." in str(backend.value) assert "Supported videos:" in str(backend.value) @pytest.mark.parametrize("suffix,kind", [(".png", "image"), (".mp4", "video")]) def test_missing_path_precedes_format_validation(media, tmp_path, suffix, kind): with pytest.raises(RuntimeError, match=f"Source {kind} file does not exist"): media.validate_input_path(tmp_path / f"missing{suffix}") def test_directory_filtering_and_empty_reason(media, tmp_path): (tmp_path / "note.txt").touch() (tmp_path / "frame.exr").touch() with pytest.raises(RuntimeError, match="No supported image frames found"): media.validate_input_path(tmp_path) for name in ["b.PNG", "a.jpg"]: (tmp_path / name).touch() frames, temporary = media.prepare_input(tmp_path, 1, tmp_path, start_frame=2) assert frames == [tmp_path / "b.PNG"] assert temporary is None def test_image_subset_rejects_video(media, tmp_path): path = tmp_path / "clip.mp4" path.touch() with pytest.raises(RuntimeError) as error: media.validate_input_path(path, images_only=True) assert "Supported images:" in str(error.value) assert "Supported videos:" not in str(error.value) def test_image_decode_error_keeps_cause(media, tmp_path): path = tmp_path / "broken.png" path.write_bytes(b"broken") with pytest.raises(RuntimeError, match="Unable to read image") as error: with media.open_image(path): pytest.fail("Broken image must not reach processing") assert isinstance(error.value.__cause__, OSError) def test_processing_error_is_not_relabelled(media, tmp_path): path = tmp_path / "valid.png" Image.new("RGB", (2, 2)).save(path) failure = OSError("inference failed") with pytest.raises(OSError) as error: with media.open_image(path): raise failure assert error.value is failure def test_video_decode_error_closes_container(media, tmp_path): failure = ValueError("broken packet") def frames(): raise failure yield container = SimpleNamespace(streams=SimpleNamespace(video=[object()]), decode=lambda _stream: frames(), close=Mock()) with pytest.raises(RuntimeError, match="Unable to read video") as error: media.extract_video_frames("broken.mp4", tmp_path, 2, SimpleNamespace(open=lambda _path: container)) assert error.value.__cause__ is failure container.close.assert_called_once()