forked from BasedHardware/omi
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_ble_callbacks.py
More file actions
188 lines (142 loc) · 6.28 KB
/
Copy pathtest_ble_callbacks.py
File metadata and controls
188 lines (142 loc) · 6.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
from __future__ import annotations
import asyncio
from unittest.mock import patch
import pytest
from omi.ble import listen, listen_payload
from omi.constants import PACKET_HEADER_BYTES
class CustomAwaitable:
def __init__(self):
self.awaited = False
def __await__(self):
async def _run():
self.awaited = True
return _run().__await__()
class MockBleakClient:
def __init__(self, device_id: str):
self.device_id = device_id
self.notify_handler = None
async def __aenter__(self):
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
pass
async def start_notify(self, char_uuid, handler):
self.notify_handler = handler
def test_listen_awaits_futures_and_custom_awaitables():
async def _test():
client_instance = MockBleakClient("fake-device")
loop = asyncio.get_running_loop()
# 1. Direct Future: created inside callback and completed via call_soon.
# If notify_handler awaits the future, fut.done() is True immediately after it returns.
active_fut: asyncio.Future | None = None
def future_handler(data: bytes):
nonlocal active_fut
active_fut = loop.create_future()
loop.call_soon(active_fut.set_result, None)
return active_fut
# 2. Custom awaitable: must have __await__ executed.
active_custom: CustomAwaitable | None = None
def custom_handler(data: bytes):
nonlocal active_custom
active_custom = CustomAwaitable()
return active_custom
# 3. Direct asyncio.Task: must finish upon await.
active_task: asyncio.Task | None = None
def task_handler(data: bytes):
nonlocal active_task
async def _task_work():
pass
active_task = asyncio.create_task(_task_work())
return active_task
# 4. Standard coroutine
coro_awaited = False
async def coro_handler(data: bytes):
nonlocal coro_awaited
coro_awaited = True
# 5. Sync callback
sync_called = False
def sync_handler(data: bytes):
nonlocal sync_called
sync_called = True
handlers = [
("future", future_handler),
("custom", custom_handler),
("task", task_handler),
("coro", coro_handler),
("sync", sync_handler),
]
with patch("omi.ble.BleakClient", return_value=client_instance):
for name, handler in handlers:
async def fake_sleep(_sec):
# Trigger notification handler
await client_instance.notify_handler("sender", bytearray(b"\x00\x01\x02\x03\x04"))
# If handler was an awaitable and not awaited, pending items won't be done yet
if name == "future":
assert active_fut is not None and active_fut.done(), "Future was not awaited"
elif name == "custom":
assert active_custom is not None and active_custom.awaited, "Custom awaitable was not awaited"
elif name == "task":
assert active_task is not None and active_task.done(), "Task was not awaited"
elif name == "coro":
assert coro_awaited, "Coroutine was not awaited"
elif name == "sync":
assert sync_called, "Sync handler was not called"
raise asyncio.CancelledError()
with patch("asyncio.sleep", side_effect=fake_sleep):
try:
await listen("fake-device", handler)
except asyncio.CancelledError:
pass
asyncio.run(_test())
def test_listen_payload_awaits_futures_and_custom_awaitables():
async def _test():
client_instance = MockBleakClient("fake-device")
loop = asyncio.get_running_loop()
# Future for payload
active_fut: asyncio.Future | None = None
def future_payload_handler(payload: bytes):
assert payload == b"\x03\x04"
nonlocal active_fut
active_fut = loop.create_future()
loop.call_soon(active_fut.set_result, None)
return active_fut
# Custom awaitable for payload
active_custom: CustomAwaitable | None = None
def custom_payload_handler(payload: bytes):
assert payload == b"\x03\x04"
nonlocal active_custom
active_custom = CustomAwaitable()
return active_custom
# Task for payload
active_task: asyncio.Task | None = None
def task_payload_handler(payload: bytes):
assert payload == b"\x03\x04"
nonlocal active_task
async def _task_work():
pass
active_task = asyncio.create_task(_task_work())
return active_task
handlers = [
("future", future_payload_handler),
("custom", custom_payload_handler),
("task", task_payload_handler),
]
with patch("omi.ble.BleakClient", return_value=client_instance):
for name, handler in handlers:
async def fake_sleep(_sec):
packet = b"\x00" * PACKET_HEADER_BYTES + b"\x03\x04"
await client_instance.notify_handler("sender", bytearray(packet))
if name == "future":
assert active_fut is not None and active_fut.done(), "Payload Future was not awaited"
elif name == "custom":
assert (
active_custom is not None and active_custom.awaited
), "Payload Custom awaitable was not awaited"
elif name == "task":
assert active_task is not None and active_task.done(), "Payload Task was not awaited"
raise asyncio.CancelledError()
with patch("asyncio.sleep", side_effect=fake_sleep):
try:
await listen_payload("fake-device", handler)
except asyncio.CancelledError:
pass
asyncio.run(_test())