"""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"] # type: ignore[index] 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 # type: ignore[misc] 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 # type: ignore[index] with pytest.raises(TypeError): spec.metadata["changed"] = True # type: ignore[index] entry = CONDITION_REGISTRY["png_baseline"] assert isinstance(entry, ConditionSpec) with pytest.raises(FrozenInstanceError): entry.mode = "tampered" # type: ignore[misc] @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 # type: ignore[attr-defined] failed_run.kwargs = kwargs # type: ignore[attr-defined] 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 # type: ignore[attr-defined] 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", "", "" ) with pytest.raises(TypeError): result.metadata["argv_template"][0] = "tampered" # type: ignore[index] 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" # type: ignore[index] 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 # type: ignore[index] 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" # type: ignore[index] 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)