0af6692786
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
117 lines
3.5 KiB
Python
117 lines
3.5 KiB
Python
"""Test di scripts/camera_service.py (#72).
|
|
|
|
Uso: vendor/visionsuite/.venv/bin/python -m pytest scripts/tests/test_camera_service.py -q
|
|
Dipende da: venv di VisionSuite (vs-camera, fastapi, httpx, opencv, pytest).
|
|
"""
|
|
import importlib.util
|
|
from pathlib import Path
|
|
|
|
import cv2
|
|
import numpy as np
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
|
|
MODULO = Path(__file__).resolve().parents[1] / "camera_service.py"
|
|
|
|
|
|
def _carica():
|
|
spec = importlib.util.spec_from_file_location("camera_service", MODULO)
|
|
mod = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(mod)
|
|
return mod
|
|
|
|
|
|
@pytest.fixture
|
|
def cartella(tmp_path):
|
|
for nome, (w, h) in {"a.png": (64, 48), "b.png": (64, 48)}.items():
|
|
cv2.imwrite(str(tmp_path / nome), np.full((h, w, 3), 128, np.uint8))
|
|
return tmp_path
|
|
|
|
|
|
@pytest.fixture
|
|
def client(cartella):
|
|
mod = _carica()
|
|
with TestClient(mod.create_app(cartella)) as c:
|
|
c.mod = mod
|
|
yield c
|
|
|
|
|
|
def test_sources_contiene_folder(client):
|
|
r = client.get("/camera/sources")
|
|
assert r.status_code == 200
|
|
nomi = [s["name"] for s in r.json()["sources"]]
|
|
assert "camera:folder" in nomi
|
|
voce = next(s for s in r.json()["sources"] if s["name"] == "camera:folder")
|
|
assert {"name", "kind", "label"} <= set(voce)
|
|
|
|
|
|
def test_grab_folder_restituisce_png(client):
|
|
r = client.post("/camera/grab", json={"source": "camera:folder"})
|
|
assert r.status_code == 200
|
|
assert r.headers["content-type"] == "image/png"
|
|
img = cv2.imdecode(np.frombuffer(r.content, np.uint8), cv2.IMREAD_COLOR)
|
|
assert img.shape[:2] == (48, 64)
|
|
|
|
|
|
def test_grab_ripetuto_in_loop(client):
|
|
# due immagini in cartella, tre grab: il loop non deve esaurirsi
|
|
for _ in range(3):
|
|
assert client.post("/camera/grab", json={"source": "camera:folder"}).status_code == 200
|
|
|
|
|
|
def test_source_mancante_400(client):
|
|
assert client.post("/camera/grab", json={}).status_code == 400
|
|
assert client.post("/camera/grab", json={"source": " "}).status_code == 400
|
|
|
|
|
|
def test_source_inesistente_404(client):
|
|
assert client.post("/camera/grab", json={"source": "camera:non-esiste"}).status_code == 404
|
|
|
|
|
|
def test_timeout_non_valido_400(client):
|
|
r = client.post("/camera/grab", json={"source": "camera:folder", "timeout_ms": 0})
|
|
assert r.status_code == 400
|
|
|
|
|
|
def test_errore_camera_503_tradotto(client, monkeypatch):
|
|
from visionsuite.camera.camera_interface import CameraGrabError
|
|
|
|
class Rotta:
|
|
def grab(self, timeout):
|
|
raise CameraGrabError("Camera is not open")
|
|
|
|
def close(self):
|
|
pass
|
|
|
|
monkeypatch.setattr(client.mod, "open_source", lambda *a, **k: Rotta())
|
|
r = client.post("/camera/grab", json={"source": "camera:finta"})
|
|
assert r.status_code == 503
|
|
assert r.json()["detail"] == "La camera non è aperta"
|
|
|
|
|
|
def test_timeout_504(client, monkeypatch):
|
|
class Lenta:
|
|
def grab(self, timeout):
|
|
raise TimeoutError("scaduto")
|
|
|
|
def close(self):
|
|
pass
|
|
|
|
monkeypatch.setattr(client.mod, "open_source", lambda *a, **k: Lenta())
|
|
r = client.post("/camera/grab", json={"source": "camera:lenta"})
|
|
assert r.status_code == 504
|
|
|
|
|
|
def test_sorgente_aperta_una_volta(client, monkeypatch):
|
|
aperture = []
|
|
reale = client.mod.open_source
|
|
|
|
def conta(*a, **k):
|
|
aperture.append(a[0])
|
|
return reale(*a, **k)
|
|
|
|
monkeypatch.setattr(client.mod, "open_source", conta)
|
|
for _ in range(3):
|
|
client.post("/camera/grab", json={"source": "camera:folder"})
|
|
assert aperture == ["camera:folder"]
|