test_usb_camera_listing.py 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
  1. """The USB camera list the camera settings pick a device from.
  2. Only devices a USB camera can stream from are listed (streaming accepts
  3. /dev/video0 to /dev/video99), and the listing, which runs v4l2-ctl for each
  4. device, does not hold up the server while it runs.
  5. """
  6. import threading
  7. from pathlib import Path
  8. from unittest.mock import patch
  9. import pytest
  10. from httpx import AsyncClient
  11. from backend.app.services.external_camera import list_usb_cameras
  12. def _devices(tmp_path: Path, *names: str) -> Path:
  13. for name in names:
  14. (tmp_path / name).touch()
  15. return tmp_path
  16. def test_only_device_paths_streaming_accepts_are_listed(tmp_path):
  17. dev = _devices(tmp_path, "video0", "video12", "video100", "video05", "video-meta", "videoX")
  18. with patch("backend.app.services.external_camera.shutil.which", return_value=None):
  19. cameras = list_usb_cameras(dev_dir=dev)
  20. assert [camera["device"] for camera in cameras] == ["/dev/video0", "/dev/video12"]
  21. def test_paths_are_listed_as_dev_paths(tmp_path):
  22. """The entry names the device as the camera settings store it."""
  23. dev = _devices(tmp_path, "video2")
  24. with patch("backend.app.services.external_camera.shutil.which", return_value=None):
  25. cameras = list_usb_cameras(dev_dir=dev)
  26. assert cameras == [{"device": "/dev/video2", "name": "video2", "capabilities": []}]
  27. @pytest.mark.asyncio
  28. async def test_the_listing_runs_off_the_event_loop(async_client: AsyncClient):
  29. seen: list[threading.Thread] = []
  30. def listing():
  31. seen.append(threading.current_thread())
  32. return [{"device": "/dev/video0", "name": "Webcam", "capabilities": ["capture"]}]
  33. with patch("backend.app.services.external_camera.list_usb_cameras", side_effect=listing):
  34. response = await async_client.get("/api/v1/printers/usb-cameras")
  35. assert response.status_code == 200
  36. assert response.json()["cameras"][0]["device"] == "/dev/video0"
  37. assert seen and seen[0] is not threading.main_thread()