Files
lda Verified 038e152bbc
Checks / tests (ubuntu-latest, 3.11) (push) Canceled after 0s
Checks / tests (ubuntu-latest, 3.14) (push) Canceled after 0s
Checks / tests (windows-latest, 3.11) (push) Canceled after 0s
Checks / tests (windows-latest, 3.14) (push) Canceled after 0s
Checks / package (push) Canceled after 0s
Add MX-T40 BLE desktop controller and protocol documentation
2026-10-25 04:43:47 +07:00

96 lines
3.7 KiB
Python

"""Replay the captured reply through asynchronous FFA1/FFA2 operations."""
import queue
import sys
import types
import unittest
from unittest.mock import AsyncMock, patch
from controller.ble_link import NOTIFY, SERVICE, WRITE, BLELink
class BLELinkTests(unittest.TestCase):
def run_peer(self, write_error=False):
operations = []
closed = []
write = types.SimpleNamespace(uuid=WRITE, properties=["write"])
notify = types.SimpleNamespace(uuid=NOTIFY, properties=["notify"])
service = types.SimpleNamespace(
get_characteristic=lambda uuid: {WRITE: write, NOTIFY: notify}.get(uuid)
)
class Events(queue.Queue):
def put(self, event, *args, **kwargs):
super().put(event, *args, **kwargs)
if event[0] == "ready":
link.stop()
events = Events()
link = BLELink("12:34:56:78:9A:BC", events)
class Peer:
services = types.SimpleNamespace(
get_service=lambda uuid: service if uuid == SERVICE else None
)
is_connected = True
async def __aenter__(self):
return self
async def __aexit__(self, *args):
closed.append(True)
async def start_notify(self, characteristic, callback):
operations.append(("subscribe", characteristic.uuid))
self.callback = callback
async def write_gatt_char(self, characteristic, data, response):
operations.append(("write", characteristic.uuid, data, response))
if write_error:
raise OSError("GATT write failed")
if data[6:-1] == b"\x01":
self.callback(notify, bytes.fromhex("00 bb 00 01 00 00 01"))
reply = bytes.fromhex("00 bb 01 1d 00 0b 02 10 01 34 30 04 01 02 04 05 01 b0")
self.callback(notify, reply[:8])
self.callback(notify, reply[8:])
elif data[6:-1] == b"\x04":
self.callback(notify, bytes.fromhex("00 bb 00 02 00 00 02"))
scanner = types.SimpleNamespace(find_device_by_address=AsyncMock(return_value=object()))
with patch.dict(
sys.modules,
{
"bleak": types.SimpleNamespace(
BleakScanner=scanner, BleakClient=lambda *a, **k: Peer()
)
},
):
link.run()
return operations, list(events.queue), closed
def test_subscribe_before_handshake_and_reassemble_notifications(self):
operations, events, closed = self.run_peer()
self.assertEqual(operations[0], ("subscribe", NOTIFY))
writes = operations[1:]
self.assertEqual([op[1] for op in writes], [WRITE, WRITE])
self.assertEqual(
[op[2] for op in writes],
[
bytes.fromhex("00 bb 01 01 00 01 01 04"),
bytes.fromhex("00 bb 01 02 00 01 04 08"),
],
)
self.assertTrue(all(op[3] is True for op in writes))
self.assertEqual(sum(kind == "ready" for kind, _ in events), 1)
self.assertFalse(any(kind == "error" for kind, _ in events))
self.assertEqual(closed, [True])
def test_write_error_disconnects_without_enabling_controls(self):
_, events, closed = self.run_peer(write_error=True)
self.assertFalse(any(kind == "ready" for kind, _ in events))
self.assertTrue(
any(kind == "error" and "GATT write failed" in value for kind, value in events)
)
self.assertEqual(events[-1], ("closed", None))
self.assertEqual(closed, [True])