70 lines
2.9 KiB
Python
70 lines
2.9 KiB
Python
"""Discovery fixtures include the owner's actual unnamed FE40 advertisement."""
|
|
|
|
import asyncio
|
|
import queue
|
|
import sys
|
|
import types
|
|
import unittest
|
|
from unittest.mock import AsyncMock, patch
|
|
|
|
from controller.discovery import ScanWorker, Speaker, scan_speakers, selected_address
|
|
|
|
|
|
def advertisement(name, address, uuids=()):
|
|
return (
|
|
types.SimpleNamespace(name=name, address=address),
|
|
types.SimpleNamespace(local_name=name, service_uuids=list(uuids)),
|
|
)
|
|
|
|
|
|
class DiscoveryTests(unittest.TestCase):
|
|
def test_unnamed_model_advertisement_and_named_speaker_are_selectable(self):
|
|
found = {
|
|
"unnamed": advertisement(
|
|
None, "12:34:56:78:9A:BC", ["0000fe40-0000-1000-8000-00805f9b34fb"]
|
|
),
|
|
"named": advertisement("[AV] MX-T40", "12:34:56:78:9A:BD"),
|
|
"unrelated": advertisement(
|
|
"Headphones", "12:34:56:78:9A:BE", ["0000ffa0-0000-1000-8000-00805f9b34fb"]
|
|
),
|
|
}
|
|
discover = AsyncMock(return_value=found)
|
|
with patch.dict(
|
|
sys.modules,
|
|
{"bleak": types.SimpleNamespace(BleakScanner=types.SimpleNamespace(discover=discover))},
|
|
):
|
|
speakers = asyncio.run(scan_speakers(timeout=3))
|
|
self.assertEqual(len(speakers), 2)
|
|
self.assertEqual(
|
|
{speaker.address for speaker in speakers}, {"12:34:56:78:9A:BC", "12:34:56:78:9A:BD"}
|
|
)
|
|
unnamed = next(speaker for speaker in speakers if speaker.address.endswith("BC"))
|
|
self.assertEqual(unnamed.name, "MX-T40 (model advertisement)")
|
|
self.assertIn(unnamed.address, unnamed.label)
|
|
discover.assert_awaited_once_with(timeout=3, return_adv=True)
|
|
|
|
def test_choice_resolves_to_address_and_preserves_manual_input(self):
|
|
speaker = Speaker("12:34:56:78:9A:BC", "MX-T40")
|
|
self.assertEqual(selected_address(speaker.label, [speaker]), speaker.address)
|
|
self.assertEqual(selected_address(" 12:34:56:78:9A:BD ", [speaker]), "12:34:56:78:9A:BD")
|
|
|
|
def test_identical_names_remain_distinguishable(self):
|
|
speakers = [Speaker("12:34:56:78:9A:BC", "MX-T40"), Speaker("12:34:56:78:9A:BD", "MX-T40")]
|
|
self.assertNotEqual(speakers[0].label, speakers[1].label)
|
|
self.assertEqual(selected_address(speakers[1].label, speakers), speakers[1].address)
|
|
|
|
def test_scan_error_is_delivered_to_gui_queue(self):
|
|
events = queue.Queue()
|
|
with patch(
|
|
"controller.discovery.scan_speakers",
|
|
AsyncMock(side_effect=RuntimeError("Bluetooth is off")),
|
|
):
|
|
ScanWorker(events).run()
|
|
self.assertEqual(events.get_nowait(), ("scan_error", "Bluetooth is off"))
|
|
|
|
def test_empty_scan_has_a_result_event(self):
|
|
events = queue.Queue()
|
|
with patch("controller.discovery.scan_speakers", AsyncMock(return_value=[])):
|
|
ScanWorker(events).run()
|
|
self.assertEqual(events.get_nowait(), ("scan_results", []))
|