test_events.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. # -*- coding: utf-8 -*-
  2. """
  3. Unit-Tests für trixy_core.events.
  4. """
  5. import pytest
  6. from unittest.mock import MagicMock, AsyncMock
  7. from datetime import datetime
  8. from trixy_core.events.enums import EventPriority
  9. from trixy_core.events.event_data.base import EventData
  10. from trixy_core.events.event_data.basic import (
  11. SatelliteConnected, SatelliteDisconnected, MusicSocketConnected,
  12. WakewordReceived, RawAudioInputReceived,
  13. SpeechRecognized, IntentReceived,
  14. ConversationStarted, ConversationEnded,
  15. PluginLoaded, PluginUnloaded, SystemShutdown
  16. )
  17. from trixy_core.events.decorators import TrixyEvent, event_handler, get_event_handlers
  18. from trixy_core.events.eventmanager import EventManager
  19. class TestEventPriority:
  20. """Tests für EventPriority."""
  21. def test_priority_ordering(self):
  22. """Testet die Prioritätsreihenfolge."""
  23. assert EventPriority.SYSTEM < EventPriority.HIGHEST
  24. assert EventPriority.HIGHEST < EventPriority.HIGH
  25. assert EventPriority.HIGH < EventPriority.NORMAL
  26. assert EventPriority.NORMAL < EventPriority.LOW
  27. assert EventPriority.LOW < EventPriority.LOWEST
  28. assert EventPriority.LOWEST < EventPriority.MONITOR
  29. def test_priority_values(self):
  30. """Testet die Prioritätswerte."""
  31. assert EventPriority.SYSTEM == 0
  32. assert EventPriority.NORMAL == 30
  33. assert EventPriority.MONITOR == 100
  34. class TestEventData:
  35. """Tests für EventData-Basisklasse."""
  36. def test_event_data_creation(self):
  37. """Testet die Event-Data-Erstellung."""
  38. event = EventData()
  39. assert event.event_id is not None
  40. assert isinstance(event.timestamp, datetime)
  41. assert event.source == ""
  42. assert event.cancelled is False
  43. def test_event_data_cancel(self):
  44. """Testet das Abbrechen eines Events."""
  45. event = EventData()
  46. assert event.is_cancelled() is False
  47. event.cancel()
  48. assert event.is_cancelled() is True
  49. def test_event_name_snake_case(self):
  50. """Testet die Event-Name-Konvertierung."""
  51. assert EventData.event_name() == "event_data"
  52. def test_custom_source(self):
  53. """Testet benutzerdefinierte Quelle."""
  54. event = EventData(source="TestSource")
  55. assert event.source == "TestSource"
  56. def test_metadata(self):
  57. """Testet Metadaten."""
  58. event = EventData(metadata={"key": "value"})
  59. assert event.metadata == {"key": "value"}
  60. class TestBasicEventData:
  61. """Tests für grundlegende Event-Datenklassen."""
  62. def test_satellite_connected(self):
  63. """Testet SatelliteConnected."""
  64. event = SatelliteConnected(
  65. satellite_id="sat-1",
  66. room="wohnzimmer",
  67. alias="Echo 1",
  68. mac_address="AA:BB:CC:DD:EE:FF",
  69. ip_address="192.168.1.100"
  70. )
  71. assert event.satellite_id == "sat-1"
  72. assert event.room == "wohnzimmer"
  73. assert event.alias == "Echo 1"
  74. assert SatelliteConnected.event_name() == "satellite_connected"
  75. def test_satellite_disconnected(self):
  76. """Testet SatelliteDisconnected."""
  77. event = SatelliteDisconnected(
  78. satellite_id="sat-1",
  79. reason="Connection lost"
  80. )
  81. assert event.satellite_id == "sat-1"
  82. assert event.reason == "Connection lost"
  83. def test_music_socket_connected(self):
  84. """Testet MusicSocketConnected."""
  85. event = MusicSocketConnected(
  86. satellite_id="sat-1",
  87. alias="Echo 1",
  88. room="wohnzimmer"
  89. )
  90. assert event.satellite_id == "sat-1"
  91. assert event.alias == "Echo 1"
  92. assert event.room == "wohnzimmer"
  93. assert MusicSocketConnected.event_name() == "music_socket_connected"
  94. def test_wakeword_received(self):
  95. """Testet WakewordReceived."""
  96. event = WakewordReceived(
  97. satellite_id="sat-1",
  98. wakeword_model="custom",
  99. confidence=0.95,
  100. audio_level=0.7
  101. )
  102. assert event.confidence == 0.95
  103. assert event.audio_level == 0.7
  104. assert WakewordReceived.event_name() == "wakeword_received"
  105. def test_raw_audio_input_received(self):
  106. """Testet RawAudioInputReceived."""
  107. audio = b"\x00\x00" * 100
  108. event = RawAudioInputReceived(
  109. audio_data=audio,
  110. sample_rate=16000,
  111. duration_seconds=1.0
  112. )
  113. assert event.audio_data == audio
  114. assert event.sample_rate == 16000
  115. def test_speech_recognized(self):
  116. """Testet SpeechRecognized."""
  117. event = SpeechRecognized(
  118. text="Hallo Trixy",
  119. confidence=0.98,
  120. language="de"
  121. )
  122. assert event.text == "Hallo Trixy"
  123. assert event.language == "de"
  124. def test_intent_received(self):
  125. """Testet IntentReceived."""
  126. event = IntentReceived(
  127. intent="turn_on_light",
  128. confidence=0.9,
  129. slots={"room": "wohnzimmer"}
  130. )
  131. assert event.intent == "turn_on_light"
  132. assert event.slots["room"] == "wohnzimmer"
  133. def test_conversation_started(self):
  134. """Testet ConversationStarted."""
  135. event = ConversationStarted(
  136. satellite_id="sat-1",
  137. conversation_id="conv-123"
  138. )
  139. assert event.conversation_id == "conv-123"
  140. def test_conversation_ended(self):
  141. """Testet ConversationEnded."""
  142. event = ConversationEnded(
  143. conversation_id="conv-123",
  144. reason="completed"
  145. )
  146. assert event.reason == "completed"
  147. def test_plugin_loaded(self):
  148. """Testet PluginLoaded."""
  149. event = PluginLoaded(
  150. plugin_name="TestPlugin",
  151. plugin_version="1.0.0"
  152. )
  153. assert event.plugin_name == "TestPlugin"
  154. def test_system_shutdown(self):
  155. """Testet SystemShutdown."""
  156. event = SystemShutdown(
  157. reason="User request",
  158. force=True
  159. )
  160. assert event.reason == "User request"
  161. assert event.force is True
  162. class TestEventDecorators:
  163. """Tests für Event-Dekoratoren."""
  164. def test_trixy_event_decorator(self):
  165. """Testet den TrixyEvent-Dekorator."""
  166. class TestHandler:
  167. @TrixyEvent(["wakeword_received", "satellite_connected"])
  168. def on_events(self, event_name, event_data):
  169. pass
  170. handlers = get_event_handlers(TestHandler())
  171. assert len(handlers) == 2
  172. event_names = [h["event_name"] for h in handlers]
  173. assert "wakeword_received" in event_names
  174. assert "satellite_connected" in event_names
  175. def test_trixy_event_with_priority(self):
  176. """Testet TrixyEvent mit Priorität."""
  177. class TestHandler:
  178. @TrixyEvent(["test_event"], priority=EventPriority.HIGH)
  179. def on_event(self, event_name, event_data):
  180. pass
  181. handlers = get_event_handlers(TestHandler())
  182. assert len(handlers) == 1
  183. assert handlers[0]["priority"] == EventPriority.HIGH
  184. def test_event_handler_decorator(self):
  185. """Testet den event_handler-Dekorator."""
  186. class TestHandler:
  187. @event_handler(WakewordReceived)
  188. async def on_wakeword(self, event):
  189. pass
  190. handlers = get_event_handlers(TestHandler())
  191. assert len(handlers) == 1
  192. assert handlers[0]["event_name"] == "wakeword_received"
  193. assert handlers[0]["event_class"] == WakewordReceived
  194. class TestEventManager:
  195. """Tests für EventManager."""
  196. @pytest.fixture
  197. def mock_application(self):
  198. """Erstellt eine Mock-Anwendung."""
  199. return MagicMock()
  200. @pytest.fixture
  201. def manager(self, mock_application):
  202. """Erstellt einen EventManager."""
  203. return EventManager(mock_application)
  204. def test_manager_initialization(self, manager):
  205. """Testet die Manager-Initialisierung."""
  206. assert manager._handlers == {}
  207. assert manager._global_handlers == []
  208. def test_register_handler(self, manager):
  209. """Testet die Handler-Registrierung."""
  210. def handler(event_name, event_data):
  211. pass
  212. manager.register("test_event", handler)
  213. assert manager.has_handlers("test_event")
  214. def test_unregister_handler(self, manager):
  215. """Testet die Handler-Deregistrierung."""
  216. def handler(event_name, event_data):
  217. pass
  218. manager.register("test_event", handler)
  219. result = manager.unregister("test_event", handler)
  220. assert result is True
  221. assert not manager.has_handlers("test_event")
  222. def test_register_global_handler(self, manager):
  223. """Testet die globale Handler-Registrierung."""
  224. def handler(event_name, event_data):
  225. pass
  226. manager.register_global(handler)
  227. assert len(manager._global_handlers) == 1
  228. @pytest.mark.asyncio
  229. async def test_trigger_event(self, manager):
  230. """Testet das Auslösen eines Events."""
  231. called = []
  232. def handler(event_name, event_data):
  233. called.append((event_name, event_data))
  234. manager.register("test_event", handler)
  235. event_data = EventData()
  236. result = await manager.trigger("test_event", event_data)
  237. assert len(called) == 1
  238. assert called[0][0] == "test_event"
  239. assert called[0][1] == event_data
  240. @pytest.mark.asyncio
  241. async def test_trigger_async_handler(self, manager):
  242. """Testet das Auslösen mit async Handler."""
  243. called = []
  244. async def async_handler(event_name, event_data):
  245. called.append(event_name)
  246. manager.register("async_event", async_handler)
  247. await manager.trigger("async_event", EventData())
  248. assert "async_event" in called
  249. @pytest.mark.asyncio
  250. async def test_event_cancellation(self, manager):
  251. """Testet die Event-Abbruchfunktion."""
  252. order = []
  253. def first_handler(event_name, event_data):
  254. order.append("first")
  255. event_data.cancel()
  256. def second_handler(event_name, event_data):
  257. order.append("second")
  258. manager.register("cancel_test", first_handler, EventPriority.HIGH)
  259. manager.register("cancel_test", second_handler, EventPriority.NORMAL)
  260. event = EventData()
  261. await manager.trigger("cancel_test", event)
  262. assert "first" in order
  263. assert "second" not in order # Wurde abgebrochen
  264. @pytest.mark.asyncio
  265. async def test_monitor_receives_cancelled_events(self, manager):
  266. """Testet, dass MONITOR-Handler abgebrochene Events erhalten."""
  267. received = []
  268. def canceller(event_name, event_data):
  269. event_data.cancel()
  270. def monitor(event_name, event_data):
  271. received.append(event_name)
  272. manager.register("monitor_test", canceller, EventPriority.HIGH)
  273. manager.register("monitor_test", monitor, EventPriority.MONITOR)
  274. await manager.trigger("monitor_test", EventData())
  275. assert "monitor_test" in received
  276. @pytest.mark.asyncio
  277. async def test_priority_ordering(self, manager):
  278. """Testet die Prioritätsreihenfolge."""
  279. order = []
  280. def low_handler(event_name, event_data):
  281. order.append("low")
  282. def high_handler(event_name, event_data):
  283. order.append("high")
  284. manager.register("priority_test", low_handler, EventPriority.LOW)
  285. manager.register("priority_test", high_handler, EventPriority.HIGH)
  286. await manager.trigger("priority_test", EventData())
  287. assert order == ["high", "low"]
  288. def test_get_registered_events(self, manager):
  289. """Testet das Abrufen registrierter Events."""
  290. manager.register("event1", lambda *args: None)
  291. manager.register("event2", lambda *args: None)
  292. events = manager.get_registered_events()
  293. assert "event1" in events
  294. assert "event2" in events
  295. def test_clear_all(self, manager):
  296. """Testet das Löschen aller Handler."""
  297. manager.register("event1", lambda *args: None)
  298. manager.register_global(lambda *args: None)
  299. manager.clear_all()
  300. assert manager._handlers == {}
  301. assert manager._global_handlers == []
  302. def test_register_object(self, manager):
  303. """Testet die Registrierung eines Objekts."""
  304. class Handler:
  305. @TrixyEvent(["test_event"])
  306. def on_test(self, event_name, event_data):
  307. pass
  308. handler = Handler()
  309. manager.register_object(handler)
  310. assert manager.has_handlers("test_event")