/
/
/
1"""Tests for ``Player.supported_sample_rates`` and ``get_supported_sample_rates``."""
2
3from __future__ import annotations
4
5from unittest.mock import MagicMock
6
7from music_assistant_models.config_entries import MULTI_VALUE_SPLITTER
8
9from music_assistant.constants import CONF_SAMPLE_RATES
10from music_assistant.models.player import Player
11
12
13def test_supported_sample_rates_property_reads_attr() -> None:
14 """The default property just exposes _attr_supported_sample_rates."""
15 player = _bare_player(attr_supported=[(96000, 24)])
16 assert player.supported_sample_rates == [(96000, 24)]
17
18
19def test_supported_sample_rates_property_returns_none_when_unset() -> None:
20 """A player that hasn't declared its rates returns None from the property."""
21 player = _bare_player(attr_supported=None)
22 assert player.supported_sample_rates is None
23
24
25def test_get_supported_sample_rates_uses_declared_value_when_set() -> None:
26 """``get_supported_sample_rates`` returns the declared list verbatim."""
27 player = _bare_player(attr_supported=[(96000, 24), (192000, 24)])
28 assert player.get_supported_sample_rates() == [(96000, 24), (192000, 24)]
29
30
31def test_get_supported_sample_rates_falls_back_to_config() -> None:
32 """When the property returns None, the user's CONF_SAMPLE_RATES selection is used."""
33 conf = [
34 f"44100{MULTI_VALUE_SPLITTER}16",
35 f"48000{MULTI_VALUE_SPLITTER}24",
36 ]
37 player = _bare_player(attr_supported=None, config_value=conf)
38 assert player.get_supported_sample_rates() == [(44100, 16), (48000, 24)]
39
40
41def test_get_supported_sample_rates_falls_back_to_default_when_no_config() -> None:
42 """Without any config value, the safe default [(44100, 16)] is returned."""
43 player = _bare_player(attr_supported=None, config_value=None)
44 assert player.get_supported_sample_rates() == [(44100, 16)]
45
46
47def test_declares_supported_sample_rates_true_when_attr_set() -> None:
48 """``declares_supported_sample_rates`` is True once _attr_* is populated."""
49 player = _bare_player(attr_supported=[(44100, 16)])
50 assert player.declares_supported_sample_rates is True
51
52
53def test_declares_supported_sample_rates_false_for_default_player() -> None:
54 """A player that hasn't set _attr_* and doesn't override the property returns False."""
55 player = _bare_player(attr_supported=None)
56 assert player.declares_supported_sample_rates is False
57
58
59def test_declares_supported_sample_rates_true_when_property_overridden() -> None:
60 """A subclass that overrides ``supported_sample_rates`` also counts as declared."""
61
62 class _OverridingPlayer(Player):
63 @property
64 def supported_sample_rates(self) -> list[tuple[int, int]] | None:
65 return [(48000, 24)]
66
67 player = object.__new__(_OverridingPlayer)
68 player._cache = {}
69 player._attr_supported_sample_rates = None
70 assert player.declares_supported_sample_rates is True
71 assert player.get_supported_sample_rates() == [(48000, 24)]
72
73
74def _bare_player(
75 *,
76 attr_supported: list[tuple[int, int]] | None,
77 config_value: list[str] | None = None,
78) -> Player:
79 """Construct a Player instance without invoking the real __init__."""
80 # the Player ABC's __init__ depends on a full provider/mass wiring;
81 # for these unit tests we only need the attributes that supported_sample_rates
82 # and get_supported_sample_rates actually read.
83 player = object.__new__(Player)
84 player._cache = {}
85 player._attr_supported_sample_rates = attr_supported
86 config = MagicMock()
87 config.get_value = MagicMock(
88 side_effect=lambda key, default=None: config_value if key == CONF_SAMPLE_RATES else default
89 )
90 player._config = config
91 return player
92