chore(node): preserve pre-canonicalization experiment snapshot
Historical working copy retained for audit before consolidation into main. The canonicalized plugin architecture and later fixes already live in main; this snapshot is not a release or a request to restore obsolete source layout.
This commit is contained in:
+119
-8
@@ -1,5 +1,5 @@
|
||||
import asyncio
|
||||
from collections.abc import Iterator
|
||||
from collections.abc import Callable, Iterator
|
||||
from pathlib import Path
|
||||
from typing import Literal
|
||||
|
||||
@@ -36,6 +36,117 @@ from k1link.device_plugins.xgrids_k1.ble.scanner import (
|
||||
RecoveryOperationKind = Literal["status-read", "wifi-provision", "ap-enable"]
|
||||
|
||||
|
||||
def install_advertisement_source(
|
||||
monkeypatch: MonkeyPatch,
|
||||
observations: list[tuple[float, BLEDevice, str | None]],
|
||||
) -> dict[str, int]:
|
||||
"""A single native scanner that delivers advertisements while it is open."""
|
||||
lifecycle = {"started": 0, "stopped": 0}
|
||||
|
||||
class FakeScanner:
|
||||
def __init__(
|
||||
self, detection_callback: Callable[[BLEDevice, AdvertisementData], None]
|
||||
) -> None:
|
||||
self.callback = detection_callback
|
||||
self.scheduled: list[asyncio.TimerHandle] = []
|
||||
|
||||
async def __aenter__(self) -> "FakeScanner":
|
||||
lifecycle["started"] += 1
|
||||
for delay, device, name in observations:
|
||||
advertisement = AdvertisementData(
|
||||
local_name=name, manufacturer_data={}, service_data={},
|
||||
service_uuids=[], tx_power=None, rssi=-45, platform_data=(),
|
||||
)
|
||||
self.scheduled.append(asyncio.get_running_loop().call_later(
|
||||
delay, self.callback, device, advertisement
|
||||
))
|
||||
return self
|
||||
|
||||
async def __aexit__(self, *_args: object) -> None:
|
||||
lifecycle["stopped"] += 1
|
||||
for timer in self.scheduled:
|
||||
timer.cancel()
|
||||
|
||||
monkeypatch.setattr(scanner_module, "BleakScanner", FakeScanner)
|
||||
monkeypatch.setattr(scanner_module, "BLE_SCAN_INITIAL_WINDOW_SECONDS", 0.02)
|
||||
return lifecycle
|
||||
|
||||
|
||||
def test_discovery_keeps_one_scanner_open_for_a_late_k1_name(monkeypatch: MonkeyPatch) -> None:
|
||||
device = BLEDevice("LATE-K1", None, details=object())
|
||||
lifecycle = install_advertisement_source(monkeypatch, [
|
||||
(0, device, None), (0.05, device, "XGR-LATE"),
|
||||
])
|
||||
|
||||
async def scenario() -> None:
|
||||
result = await scan(0.4)
|
||||
timing = result["discovery_timing"]
|
||||
assert timing["scan_extended"] is True
|
||||
assert timing["first_candidate_ms"] >= timing["initial_window_ms"]
|
||||
assert timing["scan_elapsed_ms"] < 400
|
||||
assert len(result["devices"]) == 1
|
||||
assert result["devices"][0]["k1_name_candidate"] is True
|
||||
assert discovered_device(device.address) is device
|
||||
|
||||
asyncio.run(scenario())
|
||||
assert lifecycle == {"started": 1, "stopped": 1}
|
||||
|
||||
|
||||
def test_discovery_finishes_after_initial_window_when_k1_is_visible(
|
||||
monkeypatch: MonkeyPatch,
|
||||
) -> None:
|
||||
device = BLEDevice("EARLY-K1", "XGR-EARLY", details=object())
|
||||
lifecycle = install_advertisement_source(monkeypatch, [(0, device, "XGR-EARLY")])
|
||||
result = asyncio.run(scan(0.4))
|
||||
timing = result["discovery_timing"]
|
||||
assert timing["scan_extended"] is False
|
||||
assert timing["first_candidate_ms"] < timing["initial_window_ms"]
|
||||
assert 20 <= timing["scan_elapsed_ms"] < 400
|
||||
assert lifecycle == {"started": 1, "stopped": 1}
|
||||
|
||||
|
||||
def test_discovery_without_k1_stops_at_deadline_and_does_not_reuse_old_results(
|
||||
monkeypatch: MonkeyPatch,
|
||||
) -> None:
|
||||
device = BLEDevice("OTHER-DEVICE", "Headphones", details=object())
|
||||
lifecycle = install_advertisement_source(monkeypatch, [(0, device, "Headphones")])
|
||||
|
||||
async def scenario() -> None:
|
||||
result = await scan(0.06)
|
||||
timing = result["discovery_timing"]
|
||||
assert timing["scan_extended"] is True
|
||||
assert "first_candidate_ms" not in timing
|
||||
assert timing["scan_elapsed_ms"] >= 60
|
||||
assert result["devices"][0]["k1_name_candidate"] is False
|
||||
install_advertisement_source(monkeypatch, [])
|
||||
empty = await scan(0.01)
|
||||
assert empty["devices"] == []
|
||||
assert empty["discovery_timing"]["scan_extended"] is False
|
||||
assert discovered_device(device.address) is None
|
||||
|
||||
asyncio.run(scenario())
|
||||
assert lifecycle == {"started": 1, "stopped": 1}
|
||||
|
||||
|
||||
def test_cancelled_discovery_closes_scanner_without_publishing_partial_handles(
|
||||
monkeypatch: MonkeyPatch,
|
||||
) -> None:
|
||||
device = BLEDevice("PARTIAL", "Headphones", details=object())
|
||||
lifecycle = install_advertisement_source(monkeypatch, [(0, device, "Headphones")])
|
||||
|
||||
async def scenario() -> None:
|
||||
task = asyncio.create_task(scan(1))
|
||||
await asyncio.sleep(0.04)
|
||||
assert lifecycle["started"] == 1
|
||||
task.cancel()
|
||||
with pytest.raises(asyncio.CancelledError):
|
||||
await task
|
||||
assert discovered_device(device.address) is None
|
||||
|
||||
asyncio.run(scenario())
|
||||
assert lifecycle == {"started": 1, "stopped": 1}
|
||||
|
||||
|
||||
async def _retrieve_connected_inside_ble_lease(
|
||||
macos_uuid: str,
|
||||
*,
|
||||
@@ -175,7 +286,7 @@ def test_scan_retains_the_live_corebluetooth_handle(
|
||||
return {"LIVE-UUID": (device, advertisement)}
|
||||
|
||||
monkeypatch.setattr(
|
||||
"k1link.device_plugins.xgrids_k1.ble.scanner.BleakScanner.discover",
|
||||
"k1link.device_plugins.xgrids_k1.ble.scanner._discover_k1_advertisements",
|
||||
fake_discover,
|
||||
)
|
||||
|
||||
@@ -209,7 +320,7 @@ def test_scan_start_invalidates_previous_lease_and_failure_leaves_it_empty(
|
||||
) -> dict[str, tuple[BLEDevice, AdvertisementData]]:
|
||||
return {old_device.address: (old_device, old_advertisement)}
|
||||
|
||||
monkeypatch.setattr(scanner_module.BleakScanner, "discover", initial_discover)
|
||||
monkeypatch.setattr(scanner_module, "_discover_k1_advertisements", initial_discover)
|
||||
await scan(1.0)
|
||||
assert discovered_device(old_device.address) is old_device
|
||||
|
||||
@@ -221,7 +332,7 @@ def test_scan_start_invalidates_previous_lease_and_failure_leaves_it_empty(
|
||||
await release_failing_scan.wait()
|
||||
raise RuntimeError("synthetic BLE scan failure")
|
||||
|
||||
monkeypatch.setattr(scanner_module.BleakScanner, "discover", failing_discover)
|
||||
monkeypatch.setattr(scanner_module, "_discover_k1_advertisements", failing_discover)
|
||||
scan_task = asyncio.create_task(scan(1.0))
|
||||
await failing_scan_started.wait()
|
||||
|
||||
@@ -263,7 +374,7 @@ def test_process_arbiter_rejects_overlapping_scan(monkeypatch: MonkeyPatch) -> N
|
||||
await release_older_scan.wait()
|
||||
return {older_device.address: (older_device, older_advertisement)}
|
||||
|
||||
monkeypatch.setattr(scanner_module.BleakScanner, "discover", blocked_discover)
|
||||
monkeypatch.setattr(scanner_module, "_discover_k1_advertisements", blocked_discover)
|
||||
older_task = asyncio.create_task(scan(1.0))
|
||||
await older_scan_started.wait()
|
||||
|
||||
@@ -377,7 +488,7 @@ def test_connected_handle_is_session_scoped_and_survives_wall_clock_age(
|
||||
async def fake_discover(**_kwargs: object) -> dict[str, tuple[BLEDevice, AdvertisementData]]:
|
||||
return discoveries.pop(0)
|
||||
|
||||
monkeypatch.setattr(scanner_module.BleakScanner, "discover", fake_discover)
|
||||
monkeypatch.setattr(scanner_module, "_discover_k1_advertisements", fake_discover)
|
||||
monkeypatch.setattr(scanner_module, "monotonic", lambda: monotonic_clock[0])
|
||||
monkeypatch.setattr(
|
||||
scanner_module,
|
||||
@@ -487,7 +598,7 @@ def test_scan_and_retained_handle_survive_macos_sleep_until_explicit_invalidatio
|
||||
) -> dict[str, tuple[BLEDevice, AdvertisementData]]:
|
||||
return {device.address: (device, advertisement)}
|
||||
|
||||
monkeypatch.setattr(scanner_module.BleakScanner, "discover", fake_discover)
|
||||
monkeypatch.setattr(scanner_module, "_discover_k1_advertisements", fake_discover)
|
||||
monkeypatch.setattr(scanner_module, "monotonic", lambda: monotonic_clock[0])
|
||||
monkeypatch.setattr(
|
||||
scanner_module,
|
||||
@@ -1340,7 +1451,7 @@ def test_later_fresh_scan_does_not_replace_retained_corebluetooth_object(
|
||||
async def fake_discover(**_kwargs: object) -> dict[str, tuple[BLEDevice, AdvertisementData]]:
|
||||
return discoveries.pop(0)
|
||||
|
||||
monkeypatch.setattr(scanner_module.BleakScanner, "discover", fake_discover)
|
||||
monkeypatch.setattr(scanner_module, "_discover_k1_advertisements", fake_discover)
|
||||
|
||||
async def scenario() -> None:
|
||||
await scan(1.0)
|
||||
|
||||
Reference in New Issue
Block a user