Spaces:
Running on Zero
Running on Zero
| """WP-0: Tests for the Gradio application — build_app constructs successfully.""" | |
| from __future__ import annotations | |
| import gradio as gr | |
| class TestBuildApp: | |
| def test_build_app_returns_blocks(self): | |
| from app.main import build_app | |
| demo = build_app() | |
| assert isinstance(demo, gr.Blocks) | |
| def test_build_app_has_expected_components(self): | |
| from app.main import build_app | |
| demo = build_app() | |
| # Blocks should be a non-empty layout tree | |
| assert demo is not None | |
| def test_build_app_idempotent(self): | |
| from app.main import build_app | |
| demo1 = build_app() | |
| demo2 = build_app() | |
| assert isinstance(demo1, gr.Blocks) | |
| assert isinstance(demo2, gr.Blocks) | |
| class TestProcessNegativeWP13: | |
| """WP-13 D5: process_negative runs green with affine/flat_guard wiring.""" | |
| def test_process_negative_on_fixture(self): | |
| import numpy as np | |
| from PIL import Image | |
| from app.main import process_negative | |
| data = np.load("synth/fixtures/case_000.npz") | |
| scan = data["scan"].astype(np.float32) | |
| upload = Image.fromarray((scan * 255.0).clip(0, 255).astype(np.uint8)) | |
| out = process_negative( | |
| upload, | |
| film_stock="Generic", | |
| physics_weight=1.0, | |
| perceptual_weight=0.5, | |
| num_candidates=3, | |
| include_deep_prior=False, | |
| full_res_export=False, | |
| scan_type="Positive", | |
| scan_calibration="auto-exposed", | |
| auto_trim=False, | |
| ) | |
| # 11-tuple return | |
| assert len(out) == 11 | |
| positive_pil, recombined_pil, img_a, img_b, status, best_state, ranked, gallery, conf, fa, fb = out | |
| assert img_a is not None and img_b is not None | |
| assert ranked is not None and len(ranked) >= 1 | |
| assert "Error" not in (status or "") | |
| # Affine ranking path should produce finite hybrid scores | |
| assert np.isfinite(ranked[0].score.total_loss) | |
| # Best must not be a flat solid pair when structured candidates exist | |
| from app.scoring import _is_flat_pair | |
| obs = np.asarray(positive_pil.convert("RGB"), dtype=np.float32) / 255.0 | |
| # Use preprocessed observed from best_state if available | |
| if best_state and best_state.get("observed_rgb") is not None: | |
| obs = best_state["observed_rgb"] | |
| a = ranked[0].separation.image_a | |
| b = ranked[0].separation.image_b | |
| # With guard on, if anything non-flat exists best should not be flat | |
| assert not _is_flat_pair(a, b, obs) or len(ranked) == 1 | |
| class TestAsymmetricWP14: | |
| """WP-14 D3: checkbox OFF byte-identical; ON offline asym_sub + note.""" | |
| def _upload(self): | |
| import numpy as np | |
| from PIL import Image | |
| data = np.load("synth/fixtures/case_000.npz") | |
| scan = data["scan"].astype(np.float32) | |
| return Image.fromarray((scan * 255.0).clip(0, 255).astype(np.uint8)) | |
| def test_checkbox_off_byte_identical_population(self): | |
| """Default path (asym OFF) matches explicit asymmetric_recovery=False.""" | |
| import os | |
| from app.main import process_negative | |
| upload = self._upload() | |
| kwargs = dict( | |
| film_stock="Generic", | |
| physics_weight=1.0, | |
| perceptual_weight=0.5, | |
| num_candidates=3, | |
| include_deep_prior=False, | |
| full_res_export=False, | |
| scan_type="Positive", | |
| scan_calibration="auto-exposed", | |
| auto_trim=False, | |
| ) | |
| # Ensure no accidental key influence | |
| old = os.environ.pop("REPLICATE_API_TOKEN", None) | |
| try: | |
| out_default = process_negative(upload, **kwargs) | |
| out_off = process_negative(upload, **kwargs, asymmetric_recovery=False) | |
| finally: | |
| if old is not None: | |
| os.environ["REPLICATE_API_TOKEN"] = old | |
| ranked_d = out_default[6] | |
| ranked_o = out_off[6] | |
| assert ranked_d is not None and ranked_o is not None | |
| ids_d = [r.candidate_id for r in ranked_d] | |
| ids_o = [r.candidate_id for r in ranked_o] | |
| assert ids_d == ids_o | |
| # No asym candidates when off | |
| assert "asym_sub" not in ids_d | |
| assert "asym_fill" not in ids_d | |
| # Scores byte-identical | |
| for a, b in zip(ranked_d, ranked_o): | |
| assert a.candidate_id == b.candidate_id | |
| assert abs(a.score.total_loss - b.score.total_loss) < 1e-12 | |
| def test_checkbox_on_offline_asym_sub_no_network(self): | |
| """ON without keys → asym_sub present; offline note; no network attempted.""" | |
| import os | |
| from unittest.mock import patch | |
| from app.main import process_negative, ASYM_CONSENT | |
| upload = self._upload() | |
| old_r = os.environ.pop("REPLICATE_API_TOKEN", None) | |
| old_a = os.environ.pop("ANTHROPIC_API_KEY", None) | |
| try: | |
| with patch("app.asymmetric._replicate_inpaint") as mock_inpaint: | |
| mock_inpaint.side_effect = AssertionError("network must not be called") | |
| out = process_negative( | |
| upload, | |
| film_stock="Generic", | |
| physics_weight=1.0, | |
| perceptual_weight=0.5, | |
| num_candidates=3, | |
| include_deep_prior=False, | |
| full_res_export=False, | |
| scan_type="Positive", | |
| scan_calibration="auto-exposed", | |
| auto_trim=False, | |
| asymmetric_recovery=True, | |
| asymmetric_anchor="Auto", | |
| ) | |
| mock_inpaint.assert_not_called() | |
| finally: | |
| if old_r is not None: | |
| os.environ["REPLICATE_API_TOKEN"] = old_r | |
| if old_a is not None: | |
| os.environ["ANTHROPIC_API_KEY"] = old_a | |
| status = out[4] or "" | |
| ranked = out[6] | |
| assert ranked is not None | |
| ids = [r.candidate_id for r in ranked] | |
| assert "asym_sub" in ids, f"asym_sub missing from {ids}" | |
| assert "asym_fill" not in ids | |
| assert "offline" in status.lower() or "no REPLICATE" in status or ASYM_CONSENT[:40] in status | |
| assert "Asymmetric recovery" in status | |