test_channels.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. # -*- coding: utf-8 -*-
  2. """
  3. Tests fuer den ChannelMapper und die CHANNELS-Definitionen.
  4. Testet Alias-Aufloesung, XMLTV-Mapping, Sender-Filterung und
  5. benutzerdefinierte Aliase.
  6. """
  7. from __future__ import annotations
  8. import pytest
  9. from plugins.tv_program.epg.channels import CHANNELS, ChannelMapper
  10. # ============================================================================
  11. # CHANNELS-Dict Struktur
  12. # ============================================================================
  13. class TestChannelsDictStruktur:
  14. """Tests fuer die CHANNELS-Konstantendefinition."""
  15. def test_channels_enthaelt_ard(self) -> None:
  16. """ARD muss im CHANNELS-Dict vorhanden sein."""
  17. assert "ard" in CHANNELS
  18. def test_channels_enthaelt_zdf(self) -> None:
  19. """ZDF muss im CHANNELS-Dict vorhanden sein."""
  20. assert "zdf" in CHANNELS
  21. def test_channels_enthaelt_rtl(self) -> None:
  22. """RTL muss im CHANNELS-Dict vorhanden sein."""
  23. assert "rtl" in CHANNELS
  24. def test_channels_enthaelt_sat1(self) -> None:
  25. """SAT.1 muss im CHANNELS-Dict vorhanden sein."""
  26. assert "sat1" in CHANNELS
  27. def test_channels_enthaelt_pro7(self) -> None:
  28. """ProSieben muss im CHANNELS-Dict vorhanden sein."""
  29. assert "pro7" in CHANNELS
  30. def test_channels_enthaelt_mindestens_35_sender(self) -> None:
  31. """Das CHANNELS-Dict muss mindestens 35 Sender enthalten."""
  32. assert len(CHANNELS) >= 35
  33. def test_channels_eintraege_haben_pflichtfelder(self) -> None:
  34. """Jeder Sender muss name, aliases, group und xmltv_ids haben."""
  35. for channel_id, info in CHANNELS.items():
  36. assert "name" in info, f"'{channel_id}' fehlt 'name'"
  37. assert "aliases" in info, f"'{channel_id}' fehlt 'aliases'"
  38. assert "group" in info, f"'{channel_id}' fehlt 'group'"
  39. assert "xmltv_ids" in info, f"'{channel_id}' fehlt 'xmltv_ids'"
  40. def test_channels_aliase_sind_listen(self) -> None:
  41. """Alle Alias-Felder muessen Listen sein."""
  42. for channel_id, info in CHANNELS.items():
  43. assert isinstance(info["aliases"], list), (
  44. f"'{channel_id}' aliases ist keine Liste"
  45. )
  46. def test_channels_xmltv_ids_sind_listen(self) -> None:
  47. """Alle xmltv_ids-Felder muessen Listen sein."""
  48. for channel_id, info in CHANNELS.items():
  49. assert isinstance(info["xmltv_ids"], list), (
  50. f"'{channel_id}' xmltv_ids ist keine Liste"
  51. )
  52. # ============================================================================
  53. # ChannelMapper - resolve()
  54. # ============================================================================
  55. class TestChannelMapperResolve:
  56. """Tests fuer die Namensaufloesung ueber resolve()."""
  57. @pytest.fixture
  58. def mapper(self) -> ChannelMapper:
  59. """Standard-Mapper ohne Filter oder benutzerdefinierte Aliase."""
  60. return ChannelMapper()
  61. def test_resolve_exakt_rtl2(self, mapper: ChannelMapper) -> None:
  62. """Exakter Match: 'RTL2' wird auf 'rtl2' aufgeloest."""
  63. assert mapper.resolve("RTL2") == "rtl2"
  64. def test_resolve_exakt_prosieben(self, mapper: ChannelMapper) -> None:
  65. """Exakter Match: 'ProSieben' wird auf 'pro7' aufgeloest."""
  66. assert mapper.resolve("ProSieben") == "pro7"
  67. def test_resolve_exakt_ard(self, mapper: ChannelMapper) -> None:
  68. """Exakter Match: 'ARD' wird auf 'ard' aufgeloest."""
  69. assert mapper.resolve("ARD") == "ard"
  70. def test_resolve_gross_kleinschreibung_rtl2(self, mapper: ChannelMapper) -> None:
  71. """Gross-/Kleinschreibung wird ignoriert: 'rtl2' -> 'rtl2'."""
  72. assert mapper.resolve("rtl2") == "rtl2"
  73. def test_resolve_gross_kleinschreibung_prosieben(self, mapper: ChannelMapper) -> None:
  74. """Gross-/Kleinschreibung wird ignoriert: 'PROSIEBEN' -> 'pro7'."""
  75. assert mapper.resolve("PROSIEBEN") == "pro7"
  76. def test_resolve_gross_kleinschreibung_zdf(self, mapper: ChannelMapper) -> None:
  77. """Gross-/Kleinschreibung wird ignoriert: 'zdf' -> 'zdf'."""
  78. assert mapper.resolve("zdf") == "zdf"
  79. def test_resolve_alias_pro7(self, mapper: ChannelMapper) -> None:
  80. """Alias-Match: 'Pro 7' wird auf 'pro7' aufgeloest."""
  81. assert mapper.resolve("Pro 7") == "pro7"
  82. def test_resolve_alias_sat_eins(self, mapper: ChannelMapper) -> None:
  83. """Alias-Match: 'Sat eins' wird auf 'sat1' aufgeloest."""
  84. assert mapper.resolve("Sat eins") == "sat1"
  85. def test_resolve_alias_rtl_zwei(self, mapper: ChannelMapper) -> None:
  86. """Alias-Match: 'RTL Zwei' wird auf 'rtl2' aufgeloest."""
  87. assert mapper.resolve("RTL Zwei") == "rtl2"
  88. def test_resolve_alias_rtl_ii(self, mapper: ChannelMapper) -> None:
  89. """Alias-Match: 'RTL II' wird auf 'rtl2' aufgeloest."""
  90. assert mapper.resolve("RTL II") == "rtl2"
  91. def test_resolve_teilstring_pro7_maxx(self, mapper: ChannelMapper) -> None:
  92. """Teilstring-Match: 'Pro7 Maxx' wird auf 'pro7_maxx' aufgeloest."""
  93. assert mapper.resolve("Pro7 Maxx") == "pro7_maxx"
  94. def test_resolve_unbekannter_sender_none(self, mapper: ChannelMapper) -> None:
  95. """Unbekannter Sendername gibt None zurueck."""
  96. assert mapper.resolve("Unbekannter Sender XYZ") is None
  97. def test_resolve_leerer_string_none(self, mapper: ChannelMapper) -> None:
  98. """Leerer String gibt None zurueck."""
  99. assert mapper.resolve("") is None
  100. def test_resolve_whitespace_wird_gestrippt(self, mapper: ChannelMapper) -> None:
  101. """Fuehrende und abschliessende Leerzeichen werden entfernt."""
  102. assert mapper.resolve(" RTL ") == "rtl"
  103. def test_resolve_channel_id_direkt(self, mapper: ChannelMapper) -> None:
  104. """Die Sender-ID selbst wird als Alias erkannt."""
  105. assert mapper.resolve("ard") == "ard"
  106. def test_resolve_display_name(self, mapper: ChannelMapper) -> None:
  107. """Der Display-Name wird als Alias erkannt."""
  108. assert mapper.resolve("Das Erste") == "ard"
  109. # ============================================================================
  110. # ChannelMapper - resolve_xmltv_id()
  111. # ============================================================================
  112. class TestChannelMapperResolveXmltvId:
  113. """Tests fuer die XMLTV-ID-Aufloesung."""
  114. @pytest.fixture
  115. def mapper(self) -> ChannelMapper:
  116. return ChannelMapper()
  117. def test_resolve_xmltv_id_das_erste(self, mapper: ChannelMapper) -> None:
  118. """XMLTV-ID 'DasErste.de' wird auf 'ard' aufgeloest."""
  119. assert mapper.resolve_xmltv_id("DasErste.de") == "ard"
  120. def test_resolve_xmltv_id_prosieben(self, mapper: ChannelMapper) -> None:
  121. """XMLTV-ID 'ProSieben.de' wird auf 'pro7' aufgeloest."""
  122. assert mapper.resolve_xmltv_id("ProSieben.de") == "pro7"
  123. def test_resolve_xmltv_id_zdf(self, mapper: ChannelMapper) -> None:
  124. """XMLTV-ID 'ZDF.de' wird auf 'zdf' aufgeloest."""
  125. assert mapper.resolve_xmltv_id("ZDF.de") == "zdf"
  126. def test_resolve_xmltv_id_unbekannt_none(self, mapper: ChannelMapper) -> None:
  127. """Unbekannte XMLTV-ID gibt None zurueck."""
  128. assert mapper.resolve_xmltv_id("UnbekannterSender.de") is None
  129. def test_resolve_xmltv_id_alternative_id(self, mapper: ChannelMapper) -> None:
  130. """Alternative XMLTV-ID 'ARD.de' wird ebenfalls auf 'ard' aufgeloest."""
  131. assert mapper.resolve_xmltv_id("ARD.de") == "ard"
  132. # ============================================================================
  133. # ChannelMapper - get_display_name()
  134. # ============================================================================
  135. class TestChannelMapperGetDisplayName:
  136. """Tests fuer die Display-Name-Abfrage."""
  137. @pytest.fixture
  138. def mapper(self) -> ChannelMapper:
  139. return ChannelMapper()
  140. def test_display_name_rtl2(self, mapper: ChannelMapper) -> None:
  141. """Display-Name fuer 'rtl2' ist 'RTLZWEI'."""
  142. assert mapper.get_display_name("rtl2") == "RTLZWEI"
  143. def test_display_name_ard(self, mapper: ChannelMapper) -> None:
  144. """Display-Name fuer 'ard' ist 'Das Erste'."""
  145. assert mapper.get_display_name("ard") == "Das Erste"
  146. def test_display_name_unbekannt_fallback(self, mapper: ChannelMapper) -> None:
  147. """Unbekannte Sender-ID gibt die ID selbst als Fallback zurueck."""
  148. assert mapper.get_display_name("unbekannt_123") == "unbekannt_123"
  149. # ============================================================================
  150. # ChannelMapper - get_all_channel_ids()
  151. # ============================================================================
  152. class TestChannelMapperGetAllChannelIds:
  153. """Tests fuer die Abfrage aller aktiven Sender-IDs."""
  154. def test_alle_sender_ohne_filter(self) -> None:
  155. """Ohne Filter werden alle Sender zurueckgegeben."""
  156. mapper = ChannelMapper()
  157. ids = mapper.get_all_channel_ids()
  158. assert len(ids) == len(CHANNELS)
  159. def test_alle_sender_ist_liste(self) -> None:
  160. """Die Rueckgabe ist eine Liste."""
  161. mapper = ChannelMapper()
  162. ids = mapper.get_all_channel_ids()
  163. assert isinstance(ids, list)
  164. def test_alle_sender_enthaelt_ard(self) -> None:
  165. """Die Liste enthaelt 'ard'."""
  166. mapper = ChannelMapper()
  167. ids = mapper.get_all_channel_ids()
  168. assert "ard" in ids
  169. # ============================================================================
  170. # ChannelMapper - channel_filter
  171. # ============================================================================
  172. class TestChannelMapperFilter:
  173. """Tests fuer die Sender-Filterung."""
  174. def test_filter_nur_ard_zdf(self) -> None:
  175. """Mit Filter ['ard', 'zdf'] werden nur 2 Sender aktiv."""
  176. mapper = ChannelMapper(channel_filter=["ard", "zdf"])
  177. ids = mapper.get_all_channel_ids()
  178. assert set(ids) == {"ard", "zdf"}
  179. def test_filter_resolve_aktiver_sender(self) -> None:
  180. """Aktive Sender koennen ueber resolve() gefunden werden."""
  181. mapper = ChannelMapper(channel_filter=["rtl"])
  182. assert mapper.resolve("RTL") == "rtl"
  183. def test_filter_resolve_inaktiver_sender_none(self) -> None:
  184. """Inaktive (gefilterte) Sender geben None zurueck."""
  185. mapper = ChannelMapper(channel_filter=["rtl"])
  186. assert mapper.resolve("ARD") is None
  187. def test_filter_xmltv_nur_aktive(self) -> None:
  188. """XMLTV-IDs gefilterter Sender sind nicht verfuegbar."""
  189. mapper = ChannelMapper(channel_filter=["rtl"])
  190. assert mapper.resolve_xmltv_id("DasErste.de") is None
  191. assert mapper.resolve_xmltv_id("RTL.de") == "rtl"
  192. # ============================================================================
  193. # ChannelMapper - custom_aliases
  194. # ============================================================================
  195. class TestChannelMapperCustomAliases:
  196. """Tests fuer benutzerdefinierte Aliase."""
  197. def test_custom_alias_ueberschreibt(self) -> None:
  198. """Benutzerdefinierter Alias wird korrekt aufgeloest."""
  199. mapper = ChannelMapper(custom_aliases={"Mein Sender": "rtl2"})
  200. assert mapper.resolve("Mein Sender") == "rtl2"
  201. def test_custom_alias_aktiviert_sender(self) -> None:
  202. """Benutzerdefinierter Alias aktiviert einen gefilterten Sender."""
  203. mapper = ChannelMapper(
  204. channel_filter=["ard"],
  205. custom_aliases={"Lieblingssender": "rtl"},
  206. )
  207. ids = mapper.get_all_channel_ids()
  208. assert "rtl" in ids
  209. def test_custom_alias_ungueltiger_sender_ignoriert(self) -> None:
  210. """Alias auf nicht existierenden Sender wird ignoriert."""
  211. mapper = ChannelMapper(custom_aliases={"Test": "existiert_nicht"})
  212. assert mapper.resolve("Test") is None
  213. class TestChannelMapperBlacklist:
  214. """Tests fuer die Blacklist-Funktion."""
  215. def test_blacklist_schliesst_sender_aus(self) -> None:
  216. """Sender auf der Blacklist werden nicht aufgenommen."""
  217. mapper = ChannelMapper(blacklist=["rtl", "rtl2"])
  218. ids = mapper.get_all_channel_ids()
  219. assert "rtl" not in ids
  220. assert "rtl2" not in ids
  221. assert "ard" in ids
  222. def test_blacklist_resolve_gibt_none(self) -> None:
  223. """Geblacklister Sender kann nicht aufgeloest werden."""
  224. mapper = ChannelMapper(blacklist=["vox"])
  225. assert mapper.resolve("VOX") is None
  226. assert mapper.resolve("Vox") is None
  227. def test_blacklist_case_insensitive(self) -> None:
  228. """Blacklist-Vergleich ist case-insensitive."""
  229. mapper = ChannelMapper(blacklist=["PRO7", "RTL2"])
  230. ids = mapper.get_all_channel_ids()
  231. assert "pro7" not in ids
  232. assert "rtl2" not in ids
  233. def test_blacklist_andere_sender_bleiben(self) -> None:
  234. """Nicht geblackliste Sender bleiben verfuegbar."""
  235. mapper = ChannelMapper(blacklist=["vox"])
  236. ids = mapper.get_all_channel_ids()
  237. assert "vox" not in ids
  238. assert len(ids) == len(CHANNELS) - 1
  239. def test_whitelist_hat_vorrang_vor_blacklist(self) -> None:
  240. """Wenn Whitelist gesetzt, wird Blacklist ignoriert."""
  241. mapper = ChannelMapper(
  242. channel_filter=["ard", "zdf", "rtl"],
  243. blacklist=["rtl"],
  244. )
  245. ids = mapper.get_all_channel_ids()
  246. # RTL ist auf Whitelist → Blacklist wird ignoriert
  247. assert "rtl" in ids
  248. assert len(ids) == 3
  249. def test_leere_blacklist_aendert_nichts(self) -> None:
  250. """Leere Blacklist hat keine Auswirkung."""
  251. mapper_normal = ChannelMapper()
  252. mapper_empty = ChannelMapper(blacklist=[])
  253. assert len(mapper_normal.get_all_channel_ids()) == len(mapper_empty.get_all_channel_ids())
  254. def test_blacklist_xmltv_ids_auch_ausgeschlossen(self) -> None:
  255. """XMLTV-IDs von geblacklisteten Sendern werden nicht aufgeloest."""
  256. mapper = ChannelMapper(blacklist=["rtl"])
  257. assert mapper.resolve_xmltv_id("RTL.de") is None