| """Task 1 cross-codec adapterの契約テスト。""" |
|
|
| from __future__ import annotations |
|
|
| from dataclasses import FrozenInstanceError, replace |
| from io import BytesIO |
|
|
| import numpy as np |
| import pytest |
| from PIL import Image |
|
|
| from pixelmodel_robustness.cross_codec import ( |
| CODEC_SPECS, |
| CONDITION_IDS, |
| CONDITION_REGISTRY, |
| CodecSpec, |
| ConditionSpec, |
| JxlCodecError, |
| ProtectedArtifact, |
| decode_l_plane, |
| decode_protected_high, |
| decode_rgb, |
| encode_l_plane, |
| encode_protected_high, |
| encode_rgb, |
| preflight_codecs, |
| repair_nonfinite_zero, |
| semantic_hash, |
| ) |
|
|
|
|
| def rgb_image() -> Image.Image: |
| return Image.fromarray( |
| np.array( |
| [[[0, 1, 2], [20, 40, 60]], [[100, 120, 140], [240, 220, 200]]], |
| dtype=np.uint8, |
| ), |
| "RGB", |
| ) |
|
|
|
|
| def gray_plane() -> np.ndarray: |
| return np.arange(16, dtype=np.uint8).reshape(4, 4) * 13 |
|
|
|
|
| def test_registry_has_exact_order_and_stable_semantic_hash() -> None: |
| assert CONDITION_IDS == ( |
| "png_baseline", "webp_lossless", "jpeg_q100_raw", "jpeg_q100_repair_zero", |
| "jpeg_q100_protected_high", "jpeg_q80_raw", "jpeg_q80_repair_zero", |
| "jpeg_q80_protected_high", "webp_q80_raw", "webp_q80_repair_zero", |
| "webp_q80_protected_high", "avif_q70_raw", "avif_q70_repair_zero", |
| "avif_q70_protected_high", "jxl_d1_raw", "jxl_d1_repair_zero", |
| "jxl_d1_protected_high", |
| ) |
| assert tuple(CONDITION_REGISTRY) == CONDITION_IDS |
| assert semantic_hash(CONDITION_REGISTRY) == semantic_hash(tuple(CONDITION_REGISTRY)) |
| with pytest.raises(TypeError): |
| CONDITION_REGISTRY["new"] = CONDITION_REGISTRY["png_baseline"] |
|
|
|
|
| def test_codec_spec_is_immutable_and_options_are_fixed() -> None: |
| spec = CODEC_SPECS["jpeg_q80"] |
| assert isinstance(spec, CodecSpec) |
| assert spec.quality == 80 |
| assert spec.options["subsampling"] == 0 |
| with pytest.raises(FrozenInstanceError): |
| spec.quality = 99 |
| assert CODEC_SPECS["webp_lossless"].options == {"lossless": True, "exact": True, "method": 6} |
| assert CODEC_SPECS["jxl_d1"].quality == 90 |
| assert CODEC_SPECS["jxl_d1"].metadata["libjxl_distance"] == 1.0 |
| assert "jxl:distance" not in str(CODEC_SPECS["jxl_d1"].options) |
|
|
|
|
| def test_all_codec_options_and_nested_registry_objects_are_immutable() -> None: |
| expected = { |
| "png": {}, |
| "webp_lossless": {"lossless": True, "exact": True, "method": 6}, |
| "jpeg_q100": {"quality": 100, "subsampling": 0, "optimize": False}, |
| "jpeg_q80": {"quality": 80, "subsampling": 0, "optimize": False}, |
| "webp_q80": {"quality": 80, "lossless": False, "method": 6, "exact": True}, |
| "avif_q70": {"quality": 70, "subsampling": "4:4:4", "speed": 6, "range": "full", "codec": "aom", "max_threads": 1, "autotiling": False}, |
| "jxl_d1": {"quality": 90}, |
| } |
| assert {name: dict(spec.options) for name, spec in CODEC_SPECS.items()} == expected |
| assert all(spec.grayscale is True for spec in CODEC_SPECS.values()) |
| for spec in CODEC_SPECS.values(): |
| with pytest.raises(TypeError): |
| spec.options["quality"] = 1 |
| with pytest.raises(TypeError): |
| spec.metadata["changed"] = True |
| entry = CONDITION_REGISTRY["png_baseline"] |
| assert isinstance(entry, ConditionSpec) |
| with pytest.raises(FrozenInstanceError): |
| entry.mode = "tampered" |
|
|
|
|
| @pytest.mark.parametrize("codec", ("png", "webp_lossless", "jpeg_q100", "jpeg_q80", "webp_q80", "avif_q70")) |
| def test_rgb_encode_decode_uses_fixed_contract(codec: str) -> None: |
| available = {item.name: item for item in preflight_codecs()} |
| if not available[codec].available: |
| pytest.skip(available[codec].reason or "codec unavailable") |
| payload = encode_rgb(rgb_image(), CODEC_SPECS[codec]) |
| decoded = decode_rgb(payload, CODEC_SPECS[codec]) |
| assert isinstance(payload, bytes) |
| assert decoded.mode == "RGB" |
| assert decoded.size == (2, 2) |
| if codec in {"png", "webp_lossless"}: |
| np.testing.assert_array_equal(np.asarray(decoded), np.asarray(rgb_image())) |
|
|
|
|
| def test_grayscale_plane_round_trip_for_lossless_png() -> None: |
| payload = encode_l_plane(gray_plane(), CODEC_SPECS["png"]) |
| decoded = decode_l_plane(payload, CODEC_SPECS["png"]) |
| np.testing.assert_array_equal(decoded, gray_plane()) |
|
|
|
|
| def test_l_plane_adapter_passes_single_channel_image_to_encoder(monkeypatch: pytest.MonkeyPatch) -> None: |
| import pixelmodel_robustness.cross_codec as module |
|
|
| modes: list[str] = [] |
| original_save = Image.Image.save |
|
|
| def capture_save(self: Image.Image, *args: object, **kwargs: object) -> object: |
| modes.append(self.mode) |
| return original_save(self, *args, **kwargs) |
|
|
| monkeypatch.setattr(Image.Image, "save", capture_save) |
| encode_l_plane(gray_plane(), CODEC_SPECS["png"]) |
| assert modes[-1] == "L" |
| assert module.decode_l_plane(encode_l_plane(gray_plane(), CODEC_SPECS["png"]), CODEC_SPECS["png"]).dtype == np.uint8 |
|
|
|
|
| def test_jxl_l_plane_writes_l_mode_source_png(monkeypatch: pytest.MonkeyPatch) -> None: |
| import pixelmodel_robustness.cross_codec as module |
|
|
| source_modes: list[str] = [] |
| seen_argv: list[list[str]] = [] |
|
|
| def fake_jxl_run(argv: list[str]) -> object: |
| seen_argv.append(argv) |
| with Image.open(argv[2]) as source: |
| source_modes.append(source.mode) |
| Image.new("L", (4, 4)).save(argv[-1], format="PNG") |
| return type("Completed", (), {"returncode": 0, "stdout": "", "stderr": ""})() |
|
|
| monkeypatch.setattr(module, "_jxl_run", fake_jxl_run) |
| module._jxl_encode(Image.fromarray(gray_plane(), "L")) |
| assert source_modes == ["L"] |
| assert seen_argv[0][0:2] == ["-quality", "90"] |
| assert seen_argv[0][2].endswith("source.png") |
| assert seen_argv[0][3].endswith("payload.jxl") |
|
|
|
|
| def test_protected_high_is_exact_and_payload_is_combined_bytes() -> None: |
| values = np.array([0.0, -0.0, 1.5, -2.25, 3.125, -4.5], dtype=np.float16) |
| artifact = encode_protected_high(values, (2, 3), CODEC_SPECS["jpeg_q80"]) |
| assert isinstance(artifact, ProtectedArtifact) |
| decoded, metadata = decode_protected_high(artifact, (2, 3), values) |
| np.testing.assert_array_equal(decoded.view(np.uint16) >> 8, values.view(np.uint16) >> 8) |
| assert metadata["decoded_high_exact"] is True |
| assert metadata["high_mode"] == metadata["low_mode"] == "L" |
| assert metadata["dimensions"] == [2, 3] |
| assert metadata["combined_payload_bytes"] == len(artifact.payload) |
| assert metadata["combined_payload_sha256"] |
| with Image.open(BytesIO(artifact.low_codec_bytes)) as low_image: |
| assert metadata["low_mode"] == low_image.mode |
| assert metadata["high_encoded_bytes"] == len(artifact.high_png_bytes) |
| assert metadata["low_encoded_bytes"] == len(artifact.low_codec_bytes) |
| assert metadata["high_encoded_sha256"] |
| assert metadata["low_encoded_sha256"] |
| assert metadata["decoded_low_sha256"] |
| assert metadata["high_dimensions"] == metadata["low_dimensions"] == [2, 3] |
|
|
|
|
| @pytest.mark.parametrize("mode", ["RGB", "P"]) |
| def test_protected_high_rejects_non_l_high_plane(mode: str) -> None: |
| values = np.array([0.0, 1.5, -2.25, 3.125, -4.5, 5.0], dtype=np.float16) |
| artifact = encode_protected_high(values, (2, 3), CODEC_SPECS["jpeg_q80"]) |
| high = np.asarray(Image.open(BytesIO(artifact.high_png_bytes)).convert("L"), dtype=np.uint8) |
| stream = BytesIO() |
| tampered_image = np.stack([high] * 3, axis=-1) if mode == "RGB" else high |
| Image.fromarray(tampered_image, mode).save(stream, format="PNG") |
| tampered = ProtectedArtifact(stream.getvalue(), artifact.low_codec_bytes, artifact.low_codec) |
| with pytest.raises(ValueError, match="high plane mode"): |
| decode_protected_high(tampered, (2, 3), values) |
|
|
|
|
| def test_protected_high_rejects_high_shape_tamper() -> None: |
| values = np.arange(6, dtype=np.float16) |
| artifact = encode_protected_high(values, (2, 3), CODEC_SPECS["jpeg_q80"]) |
| stream = BytesIO() |
| Image.fromarray(np.zeros((1, 6), dtype=np.uint8), "L").save(stream, format="PNG") |
| tampered = ProtectedArtifact(stream.getvalue(), artifact.low_codec_bytes, artifact.low_codec) |
| with pytest.raises(ValueError, match="dimensions"): |
| decode_protected_high(tampered, (2, 3), values) |
|
|
|
|
| def test_protected_high_zero_pads_to_capacity_and_checks_full_high_plane() -> None: |
| values = np.array([1.5, -2.25, 3.125], dtype=np.float16) |
| artifact = encode_protected_high(values, (2, 3), CODEC_SPECS["jpeg_q80"]) |
| decoded, metadata = decode_protected_high(artifact, (2, 3), values) |
| expected_high = np.zeros(6, dtype=np.uint16) |
| expected_high[:3] = values.view(np.uint16) |
| np.testing.assert_array_equal(decoded.view(np.uint16) >> 8, expected_high >> 8) |
| assert decoded.size == 6 |
| assert metadata["source_value_count"] == 3 |
| with pytest.raises(ValueError, match="capacity"): |
| encode_protected_high(np.zeros(7, dtype=np.float16), (2, 3), CODEC_SPECS["jpeg_q80"]) |
|
|
|
|
| def test_zero_repair_changes_only_nonfinite_fp16_words() -> None: |
| raw = np.array([0.0, -0.0, np.nan, np.inf, -np.inf, 2.0], dtype=np.float16) |
| result = repair_nonfinite_zero(raw, {"layer": (0, len(raw))}) |
| assert np.isfinite(result.values).all() |
| np.testing.assert_array_equal(result.values.view(np.uint16)[[0, 1, 5]], raw.view(np.uint16)[[0, 1, 5]]) |
| assert result.repaired_value_count == 3 |
|
|
|
|
| def test_jxl_failure_preserves_subprocess_diagnostics(monkeypatch: pytest.MonkeyPatch) -> None: |
| import pixelmodel_robustness.cross_codec as module |
|
|
| def failed_run(*args: object, **kwargs: object) -> object: |
| class Completed: |
| returncode = 7 |
| stdout = "ImageMagick 7.1.2" |
| stderr = "libjxl unavailable" |
|
|
| failed_run.args = args |
| failed_run.kwargs = kwargs |
| return Completed() |
|
|
| monkeypatch.setattr(module.subprocess, "run", failed_run) |
| with pytest.raises(JxlCodecError, match="returncode=7") as error: |
| module._jxl_run(["/tmp/in.png", "/tmp/out.jxl"]) |
| assert error.value.stderr == "libjxl unavailable" |
| assert error.value.argv == ("/etc/profiles/per-user/scratchbrulee/bin/magick", "/tmp/in.png", "/tmp/out.jxl") |
| assert failed_run.kwargs["shell"] is False |
|
|
|
|
| def test_jxl_preflight_reports_version_and_argv_without_silent_fallback() -> None: |
| result = {item.name: item for item in preflight_codecs()}["jxl_d1"] |
| assert isinstance(result.available, bool) |
| if result.available: |
| assert result.version |
| assert result.argv |
| assert result.metadata["quality"] == 90 |
| assert result.metadata["libjxl_distance"] == 1.0 |
| assert result.metadata["probe_q90_bytes"] > 0 |
| assert result.metadata["probe_q50_bytes"] > 0 |
| assert result.metadata["probe_q90_sha256"] != result.metadata["probe_q50_sha256"] |
| assert result.metadata["probe_dimensions"] == (8, 8) |
| assert result.metadata["probe_q50_dimensions"] == (8, 8) |
| assert "ImageMagick" in result.version |
| assert result.metadata["imagemagick_full_version"] == result.version |
| assert result.metadata["libjxl_delegate_version"] == "0.11.2" |
| assert result.metadata["argv_template"] == ( |
| "/etc/profiles/per-user/scratchbrulee/bin/magick", "-quality", "90", "<source>", "<target>" |
| ) |
| with pytest.raises(TypeError): |
| result.metadata["argv_template"][0] = "tampered" |
| else: |
| assert result.reason |
|
|
|
|
| def test_encoded_bytes_are_readable_without_temp_artifact() -> None: |
| payload = encode_rgb(rgb_image(), CODEC_SPECS["png"]) |
| with Image.open(BytesIO(payload)) as image: |
| assert image.format == "PNG" |
| assert isinstance(payload, bytes) |
|
|
|
|
| def test_zero_repair_does_not_mutate_and_preserves_all_finite_fp16_bits() -> None: |
| raw = np.array([0.0, -0.0, np.nextafter(np.float16(0), np.float16(1)), np.finfo(np.float16).max, np.nan], dtype=np.float16) |
| before = raw.view(np.uint16).copy() |
| result = repair_nonfinite_zero(raw, {"layer": (0, len(raw))}) |
| np.testing.assert_array_equal(raw.view(np.uint16), before) |
| np.testing.assert_array_equal(result.values.view(np.uint16)[:4], before[:4]) |
|
|
|
|
| def test_preflight_diagnostics_are_structured_for_jxl_failure(monkeypatch: pytest.MonkeyPatch) -> None: |
| import pixelmodel_robustness.cross_codec as module |
|
|
| def failed_run(*args: object, **kwargs: object) -> object: |
| return type("Completed", (), {"returncode": 9, "stdout": "version out", "stderr": "format err"})() |
|
|
| monkeypatch.setattr(module.subprocess, "run", failed_run) |
| result = module._jxl_version() |
| assert result.available is False |
| assert result.diagnostics == { |
| "argv": (module.MAGICK_PATH, "-version"), "stdout": "version out", "stderr": "format err", "returncode": 9, |
| } |
|
|
|
|
| def test_non_jxl_encode_does_not_run_global_or_jxl_preflight(monkeypatch: pytest.MonkeyPatch) -> None: |
| import pixelmodel_robustness.cross_codec as module |
|
|
| monkeypatch.setattr(module, "preflight_codecs", lambda: (_ for _ in ()).throw(AssertionError("global preflight"))) |
| monkeypatch.setattr(module, "_jxl_version", lambda: (_ for _ in ()).throw(AssertionError("JXL probe"))) |
| payload = encode_rgb(rgb_image(), CODEC_SPECS["png"]) |
| assert payload.startswith(b"\x89PNG") |
|
|
|
|
| def test_avif_raw_decode_is_rgb_when_available() -> None: |
| status = {item.name: item for item in preflight_codecs()}["avif_q70"] |
| if not status.available: |
| pytest.skip(status.reason or "AVIF unavailable") |
| decoded = decode_rgb(encode_rgb(rgb_image(), CODEC_SPECS["avif_q70"]), CODEC_SPECS["avif_q70"]) |
| assert decoded.mode == "RGB" |
|
|
|
|
| def test_jxl_preflight_rejects_unparseable_libjxl_version(monkeypatch: pytest.MonkeyPatch) -> None: |
| import pixelmodel_robustness.cross_codec as module |
|
|
| def fake_run(args: list[str]) -> object: |
| if args == ["-version"]: |
| return type("Completed", (), {"returncode": 0, "stdout": "ImageMagick Delegates: jxl", "stderr": ""})() |
| if args == ["-list", "format"]: |
| return type("Completed", (), {"returncode": 8, "stdout": "format out", "stderr": "format err"})() |
| raise AssertionError(args) |
|
|
| monkeypatch.setattr(module, "_jxl_run", fake_run) |
| result = module._jxl_version() |
| assert result.available is False |
| assert result.reason |
| assert result.diagnostics == { |
| "argv": (module.MAGICK_PATH, "-list", "format"), "stdout": "format out", "stderr": "format err", "returncode": 8, |
| } |
|
|
|
|
| def test_protected_high_rejects_l_mode_non_png_formats() -> None: |
| values = np.arange(6, dtype=np.float16) |
| artifact = encode_protected_high(values, (2, 3), CODEC_SPECS["jpeg_q80"]) |
| for image_format in ("BMP", "TIFF"): |
| stream = BytesIO() |
| Image.fromarray(np.zeros((2, 3), dtype=np.uint8), "L").save(stream, format=image_format) |
| tampered = ProtectedArtifact(stream.getvalue(), artifact.low_codec_bytes, artifact.low_codec) |
| with pytest.raises(ValueError, match="format"): |
| decode_protected_high(tampered, (2, 3), values) |
|
|
|
|
| def test_protected_low_codec_has_immutable_lossy_allowlist() -> None: |
| import pixelmodel_robustness.cross_codec as module |
|
|
| assert module.LOSSY_CODEC_NAMES == ("jpeg_q100", "jpeg_q80", "webp_q80", "avif_q70", "jxl_d1") |
| values = np.arange(6, dtype=np.float16) |
| for name in ("png", "webp_lossless"): |
| with pytest.raises(ValueError, match="lossy"): |
| encode_protected_high(values, (2, 3), CODEC_SPECS[name]) |
| artifact = encode_protected_high(values, (2, 3), CODEC_SPECS["jpeg_q80"]) |
| with pytest.raises(ValueError, match="lossy"): |
| decode_protected_high(replace(artifact, low_codec="png"), (2, 3), values) |
| with pytest.raises(TypeError): |
| module.LOSSY_CODEC_NAMES[0] = "png" |
|
|
|
|
| def test_jxl_probe_failure_preserves_probe_diagnostics(monkeypatch: pytest.MonkeyPatch) -> None: |
| import pixelmodel_robustness.cross_codec as module |
|
|
| def fake_run(args: list[str]) -> object: |
| if args == ["-version"]: |
| return type("Completed", (), {"returncode": 0, "stdout": "ImageMagick Delegates: jxl", "stderr": "version err"})() |
| if args == ["-list", "format"]: |
| return type("Completed", (), {"returncode": 0, "stdout": "JXL* rw+ JPEG XL (libjxl 0.11.2)", "stderr": "format err"})() |
| raise AssertionError(args) |
|
|
| expected = module.JxlCodecError( |
| "probe failed", argv=[module.MAGICK_PATH, "-quality", "90", "source", "target"], |
| stdout="probe out", stderr="probe err", returncode=13, |
| ) |
| monkeypatch.setattr(module, "_jxl_run", fake_run) |
| monkeypatch.setattr(module, "_jxl_encode", lambda *args, **kwargs: (_ for _ in ()).throw(expected)) |
| result = module._jxl_version() |
| assert result.available is False |
| assert result.diagnostics["probe"] == { |
| "argv": expected.argv, "stdout": expected.stdout, "stderr": expected.stderr, "returncode": expected.returncode, |
| } |
|
|
|
|
| def test_preflight_diagnostics_are_immutable_for_pillow_results() -> None: |
| result = preflight_codecs()[0] |
| with pytest.raises(TypeError): |
| result.diagnostics["changed"] = True |
|
|
|
|
| def test_condition_registry_has_exact_full_semantics() -> None: |
| expected = ( |
| ("png_baseline", "png", "raw", None, False), |
| ("webp_lossless", "webp_lossless", "raw", None, False), |
| ("jpeg_q100_raw", "jpeg_q100", "raw", None, False), |
| ("jpeg_q100_repair_zero", "jpeg_q100", "repair_zero", "zero", False), |
| ("jpeg_q100_protected_high", "jpeg_q100", "protected_high", None, True), |
| ("jpeg_q80_raw", "jpeg_q80", "raw", None, False), |
| ("jpeg_q80_repair_zero", "jpeg_q80", "repair_zero", "zero", False), |
| ("jpeg_q80_protected_high", "jpeg_q80", "protected_high", None, True), |
| ("webp_q80_raw", "webp_q80", "raw", None, False), |
| ("webp_q80_repair_zero", "webp_q80", "repair_zero", "zero", False), |
| ("webp_q80_protected_high", "webp_q80", "protected_high", None, True), |
| ("avif_q70_raw", "avif_q70", "raw", None, False), |
| ("avif_q70_repair_zero", "avif_q70", "repair_zero", "zero", False), |
| ("avif_q70_protected_high", "avif_q70", "protected_high", None, True), |
| ("jxl_d1_raw", "jxl_d1", "raw", None, False), |
| ("jxl_d1_repair_zero", "jxl_d1", "repair_zero", "zero", False), |
| ("jxl_d1_protected_high", "jxl_d1", "protected_high", None, True), |
| ) |
| assert tuple((e.condition_id, e.codec, e.mode, e.repair, e.protected) for e in CONDITION_REGISTRY.values()) == expected |
|
|
|
|
| def test_protected_encode_decode_do_not_mutate_source_bits() -> None: |
| values = np.array([0.0, -0.0, 1.5, -2.25, 3.125, -4.5], dtype=np.float16) |
| before = values.view(np.uint16).copy() |
| artifact = encode_protected_high(values, (2, 3), CODEC_SPECS["jpeg_q80"]) |
| decode_protected_high(artifact, (2, 3), values) |
| np.testing.assert_array_equal(values.view(np.uint16), before) |
|
|
|
|
| def test_expected_formats_and_forged_codec_spec_are_binding() -> None: |
| import pixelmodel_robustness.cross_codec as module |
|
|
| expected = { |
| "png": "PNG", "webp_lossless": "WEBP", "jpeg_q100": "JPEG", "jpeg_q80": "JPEG", |
| "webp_q80": "WEBP", "avif_q70": "AVIF", "jxl_d1": "JXL", |
| } |
| assert dict(module.EXPECTED_FORMAT_BY_CODEC) == expected |
| with pytest.raises(TypeError): |
| module.EXPECTED_FORMAT_BY_CODEC["png"] = "JPEG" |
| forged = replace(CODEC_SPECS["jpeg_q80"], format="PNG") |
| with pytest.raises(ValueError, match="semantic"): |
| encode_protected_high(np.arange(6, dtype=np.float16), (2, 3), forged) |
|
|
|
|
| @pytest.mark.parametrize("codec", ("jpeg_q100", "jpeg_q80", "webp_q80", "avif_q70")) |
| def test_non_jxl_decode_l_plane_rejects_payload_format_mismatch(codec: str) -> None: |
| png_payload = encode_l_plane(gray_plane(), CODEC_SPECS["png"]) |
| with pytest.raises(ValueError, match="format"): |
| decode_l_plane(png_payload, CODEC_SPECS[codec]) |
|
|
|
|
| @pytest.mark.parametrize("codec", ("jpeg_q100", "jpeg_q80", "webp_q80", "avif_q70", "jxl_d1")) |
| def test_protected_decode_rejects_png_bytes_for_every_lossy_codec(codec: str) -> None: |
| values = np.arange(6, dtype=np.float16) |
| artifact = encode_protected_high(values, (2, 3), CODEC_SPECS["jpeg_q80"]) |
| png_payload = encode_l_plane(np.zeros((2, 3), dtype=np.uint8), CODEC_SPECS["png"]) |
| tampered = replace(artifact, low_codec=codec, low_codec_bytes=png_payload) |
| with pytest.raises((ValueError, JxlCodecError)): |
| decode_protected_high(tampered, (2, 3), values) |
|
|
|
|
| @pytest.mark.parametrize("codec", ("jpeg_q100", "jpeg_q80", "webp_q80", "avif_q70", "jxl_d1")) |
| def test_decode_rgb_rejects_png_payload_for_every_lossy_codec(codec: str) -> None: |
| png_payload = encode_rgb(rgb_image(), CODEC_SPECS["png"]) |
| with pytest.raises((ValueError, JxlCodecError)): |
| decode_rgb(png_payload, CODEC_SPECS[codec]) |
|
|
|
|
| def test_verified_preflight_token_skips_jxl_probe(monkeypatch: pytest.MonkeyPatch) -> None: |
| import pixelmodel_robustness.cross_codec as module |
| token = {"jxl_d1": module.CodecPreflight("jxl_d1", True, "fixture", None, ("fixture",), {})} |
| monkeypatch.setattr(module, "_jxl_version", lambda: (_ for _ in ()).throw(AssertionError("probe bypassed"))) |
| monkeypatch.setattr(module, "_jxl_encode", lambda image: b"jxl-payload") |
| monkeypatch.setattr(module, "_jxl_decode", lambda payload: Image.new("RGB", (2, 2))) |
| assert module.encode_rgb(rgb_image(), CODEC_SPECS["jxl_d1"], verified_preflight=token) == b"jxl-payload" |
| assert module.decode_rgb(b"jxl-payload", CODEC_SPECS["jxl_d1"], verified_preflight=token).mode == "RGB" |
|
|
|
|
| def test_verified_preflight_token_rejects_missing_or_unavailable_codec() -> None: |
| import pixelmodel_robustness.cross_codec as module |
| with pytest.raises(RuntimeError, match="unavailable"): |
| module.encode_rgb(rgb_image(), CODEC_SPECS["jxl_d1"], verified_preflight={}) |
| token = {"jxl_d1": module.CodecPreflight("jxl_d1", False, None, "missing", (), {})} |
| with pytest.raises(RuntimeError, match="missing"): |
| module.encode_rgb(rgb_image(), CODEC_SPECS["jxl_d1"], verified_preflight=token) |
|
|
|
|
| def test_decode_uses_canonical_format_for_forged_and_unknown_specs() -> None: |
| png_payload = encode_rgb(rgb_image(), CODEC_SPECS["png"]) |
| forged = replace(CODEC_SPECS["jpeg_q80"], format="PNG") |
| unknown = CodecSpec("unknown_codec", "PNG", None, {}, {}) |
| for spec in (forged, unknown): |
| with pytest.raises(ValueError, match="canonical|unknown"): |
| decode_rgb(png_payload, spec) |
| with pytest.raises(ValueError, match="canonical|unknown"): |
| decode_l_plane(png_payload, spec) |
|
|