Files
proxmark3/tools/ePassport/tests/test_camera.py
T

139 lines
4.7 KiB
Python

"""Camera enumeration, selection, and the zero-camera path."""
from __future__ import annotations
import pytest
from epassport.ocr import camera as cam
def test_device_label_is_index_plus_name() -> None:
device = cam.CameraDevice(index=2, name="IPEVO Ziggi-HD Plus")
assert device.label == "2: IPEVO Ziggi-HD Plus"
def test_device_label_marks_a_node_that_delivers_nothing() -> None:
device = cam.CameraDevice(index=1, name="Webcam", works=False)
assert "(no frames)" in device.label
assert (
"(no frames)" not in cam.CameraDevice(index=0, name="Webcam", works=True).label
)
def test_listing_without_probe_opens_nothing(monkeypatch) -> None:
"""Enumerating must not disturb a camera another process is using."""
opened: list[int] = []
def boom(index): # pragma: no cover - must never run
opened.append(index)
raise AssertionError("listing must not open devices")
monkeypatch.setattr(cam, "probe_camera", boom)
cam.list_cameras(probe=False)
assert opened == []
def test_listing_sorts_working_devices_first(monkeypatch) -> None:
devices = [
cam.CameraDevice(index=0, name="Cam", node=0),
cam.CameraDevice(index=1, name="Cam", node=1),
]
monkeypatch.setattr(cam, "_sysfs_devices", lambda root=None: devices)
monkeypatch.setattr(cam, "opencv_available", lambda: True)
monkeypatch.setattr(cam, "probe_camera", lambda index: index == 1)
got = cam.list_cameras(probe=True)
assert got[0].index == 1 and got[0].works is True
def test_no_devices_at_all_yields_an_empty_list(monkeypatch) -> None:
monkeypatch.setattr(cam, "_sysfs_devices", lambda root=None: [])
monkeypatch.setattr(cam, "opencv_available", lambda: True)
monkeypatch.setattr(cam, "probe_camera", lambda index: False)
assert cam.list_cameras(probe=True, limit=3) == [] or all(
not d.works for d in cam.list_cameras(probe=True, limit=3)
)
def test_sysfs_parsing_reads_names_and_skips_junk(tmp_path) -> None:
root = tmp_path / "video4linux"
for name, index, label in (
("video0", "0", "IPEVO Ziggi-HD Plus"),
("video1", "1", "IPEVO Ziggi-HD Plus"),
):
node = root / name
node.mkdir(parents=True)
(node / "name").write_text(label + "\n")
(node / "index").write_text(index + "\n")
(root / "videoX").mkdir() # not a number: must be skipped
(root / "video2").mkdir() # no name/index files: must still be listed
devices = cam._sysfs_devices(root)
assert [d.index for d in devices] == [0, 1, 2]
assert devices[0].name == "IPEVO Ziggi-HD Plus"
assert devices[0].node == 0 and devices[1].node == 1
assert devices[0].path == "/dev/video0"
assert devices[2].name == "" # missing file degrades, does not raise
def test_missing_sysfs_falls_back_to_numbered_indices(tmp_path) -> None:
devices = cam.list_cameras(limit=3, root=tmp_path / "nope")
assert [d.index for d in devices] == [0, 1, 2]
def test_capture_node_sorts_before_metadata_node(tmp_path) -> None:
root = tmp_path / "video4linux"
for name, node in (("video0", "1"), ("video1", "0")):
entry = root / name
entry.mkdir(parents=True)
(entry / "name").write_text("Cam")
(entry / "index").write_text(node)
devices = cam.list_cameras(root=root)
assert devices[0].index == 1, "the node with V4L2 index 0 is the capture node"
class _FakeScreen:
"""The label/selection interplay, without importing Kivy."""
def __init__(self) -> None:
self.camera_label = ""
self._setting_label = False
self.selections: list[str] = []
def _set_label(self, label: str) -> None:
if label == self.camera_label:
return
self._setting_label = True
try:
self.camera_label = label
self.on_text(label) # the Spinner echoes every text change back
finally:
self._setting_label = False
def on_text(self, label: str) -> None:
self.select_camera(label)
def select_camera(self, label: str) -> None:
if self._setting_label or not label:
return
self.selections.append(label)
def test_setting_the_label_does_not_count_as_a_user_selection() -> None:
"""The dropdown writes back, which would restart the camera mid-open."""
screen = _FakeScreen()
screen._set_label("0: Cam")
assert screen.selections == []
def test_a_real_user_selection_is_honoured() -> None:
screen = _FakeScreen()
screen._set_label("0: Cam")
screen.on_text("1: Other Cam")
assert screen.selections == ["1: Other Cam"]
def test_empty_selection_is_ignored() -> None:
screen = _FakeScreen()
screen.on_text("")
assert screen.selections == []