"""Test di #94: «Blocca immagine» (scripts/camera_service.py) e riquadro vivo nella tab Calibrazione (PAGINA). Uso: uv run --python 3.13 --extra vision-worker --extra dev \ --with-editable vendor/visionsuite/packages/vs-camera pytest scripts/tests -q Sorgente finta che conta i grab: niente camera vera, niente ProgramData. """ import re import numpy as np import pytest from test_calibrazione import _png, cartella, client, una_scacchiera # fixture e helper della calibrazione from test_immagini_affiancate import _anteprima, _carica, _modulo from test_pagina_acquisizione import PAGINA __all__ = ["cartella", "client"] # le fixture importate restano usate FINTA = "finta" CAMPI = {"cols": "9", "rows": "6", "square_mm": "2"} @pytest.fixture def finta(client, monkeypatch): """Sorgente «finta»: il primo grab da' la scacchiera, i successivi un frame bianco (non calibrabile).""" letture = [] scacchiera = una_scacchiera() bianco = np.full(scacchiera.shape, 255, np.uint8) class Finta: def grab(self, timeout=1000): letture.append(1) return scacchiera.copy() if len(letture) == 1 else bianco.copy() def close(self): pass monkeypatch.setitem(_modulo(client), "open_source", lambda *a, **k: Finta()) client.letture = letture return client def _blocca(c, nome=FINTA): return c.post("/camera/blocca", json={"source": nome}) def _calcola(c, **extra): return c.post("/camera/calibrate", data={"source": FINTA, **CAMPI, **extra}) # ---- servizio def test_blocca_senza_frame_404(finta): r = _blocca(finta) assert r.status_code == 404 and r.json()["detail"] == "nessun frame: avviare l'anteprima o scattare" def test_blocca_senza_source_400(finta): assert finta.post("/camera/blocca", json={}).status_code == 400 assert finta.post("/camera/blocca").status_code == 400 def test_anteprima_poi_blocca_200_con_misure(finta): assert _anteprima(finta, FINTA).status_code == 200 r = _blocca(finta) alt, larg = una_scacchiera().shape[:2] assert r.status_code == 200 and r.json() == {"source": FINTA, "larghezza": larg, "altezza": alt} def test_calibrate_bloccata_usa_la_copia_senza_grab(finta): assert _anteprima(finta, FINTA).status_code == 200 # frame 1: la scacchiera assert _blocca(finta).status_code == 200 assert _anteprima(finta, FINTA).status_code == 200 # frame 2: bianco, il grezzo cambia ma la copia no prima = len(finta.letture) r = _calcola(finta, bloccata="true") assert r.status_code == 200, r.text assert r.json()["bloccata"] is True and r.json()["origine"] == FINTA assert len(finta.letture) == prima # nessun grab per il calcolo # senza il blocco il calcolo legge un frame nuovo (bianco): non calibrabile assert _calcola(finta).status_code == 422 assert len(finta.letture) == prima + 1 def test_calibrate_normale_dice_bloccata_false(client): r = client.post("/camera/calibrate", data={"source": "camera:folder", **CAMPI}) assert r.status_code == 200, r.text assert r.json()["bloccata"] is False def test_calibrate_bloccata_senza_copia_409(finta): assert _anteprima(finta, FINTA).status_code == 200 # il grezzo c'e', ma non e' bloccato r = _calcola(finta, bloccata="true") assert r.status_code == 409 and r.json()["detail"] == "nessuna immagine bloccata su questa sorgente" def test_sblocca_204_idempotente_e_toglie_la_copia(finta): assert finta.delete("/camera/blocca", params={"source": FINTA}).status_code == 204 # niente da togliere assert _anteprima(finta, FINTA).status_code == 200 assert _blocca(finta).status_code == 200 assert finta.delete("/camera/blocca", params={"source": FINTA}).status_code == 204 assert _calcola(finta, bloccata="true").status_code == 409 assert finta.delete("/camera/blocca", params={"source": FINTA}).status_code == 204 def test_rimuovere_l_immagine_toglie_la_copia_bloccata(client): assert _carica(client, "foto.png", una_scacchiera()).status_code == 201 assert _anteprima(client, "foto.png").status_code == 200 assert _blocca(client, "foto.png").status_code == 200 assert client.delete("/camera/immagini", params={"nome": "foto.png"}).status_code == 204 assert _carica(client, "foto.png", una_scacchiera()).status_code == 201 r = client.post("/camera/calibrate", data={"source": "foto.png", "bloccata": "true", **CAMPI}) assert r.status_code == 409 # ---- pagina (contenuto statico) def test_pagina_riquadro_e_casella_nella_tab_calibrazione(): ids = set(re.findall(r'id="([^"]+)"', PAGINA)) assert {"immagineCalTab", "vuotoCalTab", "scattaCal", "bloccaImg", "msgBlocco"} <= ids assert "Nessuna immagine: «Scatta» o avviare l'anteprima nella tab Acquisizione" in PAGINA assert "Blocca immagine" in PAGINA tab = PAGINA.split('id="pannelloCal"')[1].split('id="sezioneSalvate"')[0] assert "immagineCalTab" in tab and "bloccaImg" in tab # nella sezione Calibrazione, non altrove def test_pagina_scegli_tab_non_ferma_piu_il_ciclo(): assert "A.riprendi" not in PAGINA scegli = PAGINA.split("function scegliTab")[1].split("$(\"tabAcq\").onclick")[0] assert "fermaAnteprima" not in scegli and "avviaAnteprima" not in scegli def test_pagina_calcola_manda_bloccata_e_non_ferma_il_ciclo(): calcola = PAGINA.split("async function calcola")[1].split("async function salvaNome")[0] assert 'f.append("bloccata", "true")' in calcola assert "if (bloccata)" in calcola and "else fermaAnteprima();" in calcola assert "immagine bloccata" in calcola assert "l'anteprima resta sospesa" not in PAGINA and "si può bloccare l'immagine" in PAGINA def test_pagina_blocco_rotte_e_cambio_sorgente(): assert '"/camera/blocca"' in PAGINA and '"/camera/blocca?source="' in PAGINA assert "method: \"DELETE\"" in PAGINA cambio = PAGINA.split("function cambioSorgente")[1].split("// ---- Anteprima")[0] assert "rilasciaBlocco()" in cambio assert cambio.index("rilasciaBlocco()") < cambio.index("svuotaRiquadri()")