| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108 |
- """Unit tests for camera profile registry.
- The registry decouples per-model camera tuning (probesize, analyzeduration,
- reconnect cadence) from the hard-coded constants that lived in
- ``camera.py`` until #1395 follow-up. Adding a new model's quirk should
- be a config edit, not a code change.
- """
- from dataclasses import FrozenInstanceError
- import pytest
- from backend.app.services.camera_profiles import (
- DEFAULT_PROFILE,
- CameraProfile,
- get_camera_profile,
- )
- class TestGetCameraProfile:
- def test_unknown_model_returns_default(self):
- """Models with no override fall through to DEFAULT_PROFILE so the
- camera path is never blocked on a missing entry."""
- assert get_camera_profile("UNKNOWN_MODEL") is DEFAULT_PROFILE
- assert get_camera_profile("Future_Bambu_Model_X42") is DEFAULT_PROFILE
- def test_none_model_returns_default(self):
- """`None` / empty model (very early in connect handshake) must not
- crash; the default profile is safe for any RTSP-capable printer."""
- assert get_camera_profile(None) is DEFAULT_PROFILE
- assert get_camera_profile("") is DEFAULT_PROFILE
- def test_default_profile_preserves_historical_fast_startup(self):
- """X1/H2 fast-startup tuning is the historical baseline. The first
- refactor must not regress it for the printers that already worked.
- """
- assert DEFAULT_PROFILE.probesize == 32
- assert DEFAULT_PROFILE.analyzeduration == 0
- assert DEFAULT_PROFILE.rtsp_reconnect_max == 30
- assert DEFAULT_PROFILE.rtsp_reconnect_delay == 0.2
- def test_p2s_has_relaxed_probe(self):
- """P2S firmware 01.02.00.00 needs more probe room — ffmpeg's own
- diagnostic says so. This is the first per-model override and the
- regression to guard."""
- profile = get_camera_profile("P2S")
- assert profile is not DEFAULT_PROFILE
- # Order of magnitude up from the default — enough to lock onto a
- # slow-keyframe stream without adding multi-second startup.
- assert profile.probesize >= 1_000_000
- assert profile.analyzeduration >= 500_000
- def test_p2s_regenerates_timestamps_from_wallclock(self):
- """P2S firmware 01.02.00.00 sends non-advancing RTP timestamps;
- ffmpeg's default CFR conversion (`-r 15`) then freezes the output
- clock after frame 1 and drops everything else (#1395). The profile
- must splice `-use_wallclock_as_timestamps 1` into the input args so
- ffmpeg rebuilds PTS from arrival time. Order matters — the flag and
- its value must be adjacent so they reach ffmpeg as a pair."""
- args = get_camera_profile("P2S").extra_ffmpeg_input_args
- assert "-use_wallclock_as_timestamps" in args
- idx = args.index("-use_wallclock_as_timestamps")
- assert args[idx + 1] == "1"
- def test_default_profile_has_no_timestamp_override(self):
- """X1/H2 send correct, advancing RTP timestamps — they must NOT get
- the wallclock override, which would needlessly re-stamp a healthy
- stream. Guards against the P2S fix leaking into the default."""
- assert DEFAULT_PROFILE.extra_ffmpeg_input_args == ()
- def test_p2s_internal_code_resolves_to_p2s_profile(self):
- """SSDP internal codes (e.g. `N7` for P2S) must resolve to the
- same profile as their display name. Otherwise printers freshly
- connected (before display-name lookup completes) would use the
- default profile and hit the same #1395 bug."""
- assert get_camera_profile("N7") is get_camera_profile("P2S")
- def test_lookup_is_case_insensitive(self):
- """Display-name capitalisation should not matter — callers may
- carry lowercase or mixed-case values straight from MQTT."""
- assert get_camera_profile("p2s") is get_camera_profile("P2S")
- assert get_camera_profile("P2s") is get_camera_profile("P2S")
- def test_known_rtsp_models_keep_default_unchanged(self):
- """X1, X1C, X1E, H2D, H2S, X2D — every other RTSP-capable model
- must use the default profile until proven otherwise. Anything
- else means we silently changed behaviour for a model the user
- hasn't reported a problem on."""
- for model in ("X1", "X1C", "X1E", "X2D", "H2C", "H2D", "H2D PRO", "H2S"):
- assert get_camera_profile(model) is DEFAULT_PROFILE, (
- f"{model} unexpectedly has a non-default profile — review "
- "whether the change is intentional before shipping."
- )
- class TestCameraProfileShape:
- def test_profile_is_frozen(self):
- """Profiles are immutable; mutating them at runtime would
- introduce action-at-a-distance for the camera generator."""
- with pytest.raises(FrozenInstanceError):
- DEFAULT_PROFILE.probesize = 999 # type: ignore[misc]
- def test_extra_ffmpeg_input_args_defaults_to_empty_tuple(self):
- """Profiles can declare extra `-flag value` pairs to splice into
- the ffmpeg input args without changing the dataclass shape.
- Default is empty so the historical command is unchanged."""
- p = CameraProfile()
- assert p.extra_ffmpeg_input_args == ()
|