forked from auracaster/bumble_mirror
Refactor LE emulation with LL and Air Interface
This commit is contained in:
@@ -285,10 +285,12 @@ async def test_legacy_advertising():
|
||||
async def test_legacy_advertising_disconnection(auto_restart):
|
||||
devices = TwoDevices()
|
||||
for controller in devices.controllers:
|
||||
controller.le_features = bytes.fromhex('ffffffffffffffff')
|
||||
controller.le_features |= hci.LeFeatureMask.LE_EXTENDED_ADVERTISING
|
||||
for dev in devices:
|
||||
await dev.power_on()
|
||||
await devices[0].start_advertising(auto_restart=auto_restart)
|
||||
await devices[0].start_advertising(
|
||||
auto_restart=auto_restart, advertising_interval_min=1.0
|
||||
)
|
||||
connecion = await devices[1].connect(devices[0].random_address)
|
||||
|
||||
await connecion.disconnect()
|
||||
@@ -343,12 +345,15 @@ async def test_extended_advertising_connection(own_address_type):
|
||||
for dev in devices:
|
||||
await dev.power_on()
|
||||
advertising_set = await devices[0].create_advertising_set(
|
||||
advertising_parameters=AdvertisingParameters(own_address_type=own_address_type)
|
||||
advertising_parameters=AdvertisingParameters(
|
||||
own_address_type=own_address_type, primary_advertising_interval_min=1.0
|
||||
)
|
||||
)
|
||||
await asyncio.wait_for(
|
||||
devices[1].connect(advertising_set.random_address or devices[0].public_address),
|
||||
_TIMEOUT,
|
||||
)
|
||||
await async_barrier()
|
||||
|
||||
# Advertising set should be terminated after connected.
|
||||
assert not advertising_set.enabled
|
||||
@@ -376,7 +381,7 @@ async def test_extended_advertising_connection(own_address_type):
|
||||
async def test_extended_advertising_connection_out_of_order(own_address_type):
|
||||
devices = TwoDevices()
|
||||
device = devices[0]
|
||||
devices.controllers[0].le_features = bytes.fromhex('ffffffffffffffff')
|
||||
devices.controllers[0].le_features |= hci.LeFeatureMask.LE_EXTENDED_ADVERTISING
|
||||
await device.power_on()
|
||||
advertising_set = await device.create_advertising_set(
|
||||
advertising_parameters=AdvertisingParameters(own_address_type=own_address_type)
|
||||
|
||||
@@ -69,7 +69,7 @@ from bumble.host import Host
|
||||
from bumble.link import LocalLink
|
||||
from bumble.transport.common import AsyncPipeSink
|
||||
|
||||
from .test_utils import async_barrier
|
||||
from .test_utils import Devices, TwoDevices, async_barrier
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -160,7 +160,8 @@ async def test_characteristic_encoding():
|
||||
def decode_value(self, value_bytes):
|
||||
return value_bytes[0]
|
||||
|
||||
[client, server] = LinkedDevices().devices[:2]
|
||||
devices = await TwoDevices.create_with_connection()
|
||||
[client, server] = devices
|
||||
|
||||
characteristic = Characteristic(
|
||||
'FDB159DB-036C-49E3-B3DB-6325AC750806',
|
||||
@@ -189,9 +190,7 @@ async def test_characteristic_encoding():
|
||||
)
|
||||
server.add_service(service)
|
||||
|
||||
await client.power_on()
|
||||
await server.power_on()
|
||||
connection = await client.connect(server.random_address)
|
||||
connection = devices.connections[0]
|
||||
peer = Peer(connection)
|
||||
|
||||
await peer.discover_services()
|
||||
@@ -279,7 +278,8 @@ async def test_characteristic_encoding():
|
||||
# -----------------------------------------------------------------------------
|
||||
@pytest.mark.asyncio
|
||||
async def test_attribute_getters():
|
||||
[client, server] = LinkedDevices().devices[:2]
|
||||
devices = await TwoDevices.create_with_connection()
|
||||
[client, server] = devices
|
||||
|
||||
characteristic_uuid = UUID('FDB159DB-036C-49E3-B3DB-6325AC750806')
|
||||
characteristic = Characteristic(
|
||||
@@ -629,39 +629,11 @@ async def test_CharacteristicValue_async():
|
||||
m.assert_called_once_with(z, b)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
class LinkedDevices:
|
||||
def __init__(self):
|
||||
self.connections = [None, None, None]
|
||||
|
||||
self.link = LocalLink()
|
||||
self.controllers = [
|
||||
Controller('C1', link=self.link),
|
||||
Controller('C2', link=self.link),
|
||||
Controller('C3', link=self.link),
|
||||
]
|
||||
self.devices = [
|
||||
Device(
|
||||
address='F0:F1:F2:F3:F4:F5',
|
||||
host=Host(self.controllers[0], AsyncPipeSink(self.controllers[0])),
|
||||
),
|
||||
Device(
|
||||
address='F1:F2:F3:F4:F5:F6',
|
||||
host=Host(self.controllers[1], AsyncPipeSink(self.controllers[1])),
|
||||
),
|
||||
Device(
|
||||
address='F2:F3:F4:F5:F6:F7',
|
||||
host=Host(self.controllers[2], AsyncPipeSink(self.controllers[2])),
|
||||
),
|
||||
]
|
||||
|
||||
self.paired = [None, None, None]
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@pytest.mark.asyncio
|
||||
async def test_read_write():
|
||||
[client, server] = LinkedDevices().devices[:2]
|
||||
devices = await TwoDevices.create_with_connection()
|
||||
[client, server] = devices
|
||||
|
||||
characteristic1 = Characteristic(
|
||||
'FDB159DB-036C-49E3-B3DB-6325AC750806',
|
||||
@@ -694,9 +666,7 @@ async def test_read_write():
|
||||
)
|
||||
server.add_services([service1])
|
||||
|
||||
await client.power_on()
|
||||
await server.power_on()
|
||||
connection = await client.connect(server.random_address)
|
||||
connection = devices.connections[0]
|
||||
peer = Peer(connection)
|
||||
|
||||
await peer.discover_services()
|
||||
@@ -740,7 +710,8 @@ async def test_read_write():
|
||||
# -----------------------------------------------------------------------------
|
||||
@pytest.mark.asyncio
|
||||
async def test_read_write2():
|
||||
[client, server] = LinkedDevices().devices[:2]
|
||||
devices = await TwoDevices.create_with_connection()
|
||||
[client, server] = devices
|
||||
|
||||
v = bytes([0x11, 0x22, 0x33, 0x44])
|
||||
characteristic1 = Characteristic(
|
||||
@@ -753,9 +724,7 @@ async def test_read_write2():
|
||||
service1 = Service('3A657F47-D34F-46B3-B1EC-698E29B6B829', [characteristic1])
|
||||
server.add_services([service1])
|
||||
|
||||
await client.power_on()
|
||||
await server.power_on()
|
||||
connection = await client.connect(server.random_address)
|
||||
connection = devices.connections[0]
|
||||
peer = Peer(connection)
|
||||
|
||||
await peer.discover_services()
|
||||
@@ -785,7 +754,8 @@ async def test_read_write2():
|
||||
# -----------------------------------------------------------------------------
|
||||
@pytest.mark.asyncio
|
||||
async def test_subscribe_notify():
|
||||
[client, server] = LinkedDevices().devices[:2]
|
||||
devices = await TwoDevices.create_with_connection()
|
||||
[client, server] = devices
|
||||
|
||||
characteristic1 = Characteristic(
|
||||
'FDB159DB-036C-49E3-B3DB-6325AC750806',
|
||||
@@ -855,9 +825,7 @@ async def test_subscribe_notify():
|
||||
|
||||
server.on('characteristic_subscription', on_characteristic_subscription)
|
||||
|
||||
await client.power_on()
|
||||
await server.power_on()
|
||||
connection = await client.connect(server.random_address)
|
||||
connection = devices.connections[0]
|
||||
peer = Peer(connection)
|
||||
|
||||
await peer.discover_services()
|
||||
@@ -1006,7 +974,8 @@ async def test_subscribe_notify():
|
||||
# -----------------------------------------------------------------------------
|
||||
@pytest.mark.asyncio
|
||||
async def test_unsubscribe():
|
||||
[client, server] = LinkedDevices().devices[:2]
|
||||
devices = await TwoDevices.create_with_connection()
|
||||
[client, server] = devices
|
||||
|
||||
characteristic1 = Characteristic(
|
||||
'FDB159DB-036C-49E3-B3DB-6325AC750806',
|
||||
@@ -1032,9 +1001,7 @@ async def test_unsubscribe():
|
||||
mock2 = Mock()
|
||||
characteristic2.on('subscription', mock2)
|
||||
|
||||
await client.power_on()
|
||||
await server.power_on()
|
||||
connection = await client.connect(server.random_address)
|
||||
connection = devices.connections[0]
|
||||
peer = Peer(connection)
|
||||
|
||||
await peer.discover_services()
|
||||
@@ -1094,7 +1061,8 @@ async def test_unsubscribe():
|
||||
# -----------------------------------------------------------------------------
|
||||
@pytest.mark.asyncio
|
||||
async def test_discover_all():
|
||||
[client, server] = LinkedDevices().devices[:2]
|
||||
devices = await TwoDevices.create_with_connection()
|
||||
[client, server] = devices
|
||||
|
||||
characteristic1 = Characteristic(
|
||||
'FDB159DB-036C-49E3-B3DB-6325AC750806',
|
||||
@@ -1120,9 +1088,7 @@ async def test_discover_all():
|
||||
service2 = Service('1111', [])
|
||||
server.add_services([service1, service2])
|
||||
|
||||
await client.power_on()
|
||||
await server.power_on()
|
||||
connection = await client.connect(server.random_address)
|
||||
connection = devices.connections[0]
|
||||
peer = Peer(connection)
|
||||
|
||||
await peer.discover_all()
|
||||
@@ -1146,7 +1112,10 @@ async def test_discover_all():
|
||||
# -----------------------------------------------------------------------------
|
||||
@pytest.mark.asyncio
|
||||
async def test_mtu_exchange():
|
||||
[d1, d2, d3] = LinkedDevices().devices[:3]
|
||||
devices = Devices(3)
|
||||
for dev in devices:
|
||||
await dev.power_on()
|
||||
[d1, d2, d3] = devices
|
||||
|
||||
d3.gatt_server.max_mtu = 100
|
||||
|
||||
@@ -1160,11 +1129,15 @@ async def test_mtu_exchange():
|
||||
await d2.power_on()
|
||||
await d3.power_on()
|
||||
|
||||
await d3.start_advertising(advertising_interval_min=1.0)
|
||||
d1_connection = await d1.connect(d3.random_address)
|
||||
await async_barrier()
|
||||
assert len(d3_connections) == 1
|
||||
assert d3_connections[0] is not None
|
||||
|
||||
await d3.start_advertising(advertising_interval_min=1.0)
|
||||
d2_connection = await d2.connect(d3.random_address)
|
||||
await async_barrier()
|
||||
assert len(d3_connections) == 2
|
||||
assert d3_connections[1] is not None
|
||||
|
||||
@@ -1233,7 +1206,8 @@ Got: BROADCAST,HELLO"""
|
||||
# -----------------------------------------------------------------------------
|
||||
@pytest.mark.asyncio
|
||||
async def test_server_string():
|
||||
[_, server] = LinkedDevices().devices[:2]
|
||||
devices = await TwoDevices.create_with_connection()
|
||||
[_, server] = devices
|
||||
|
||||
characteristic = Characteristic(
|
||||
'FDB159DB-036C-49E3-B3DB-6325AC750806',
|
||||
@@ -1422,7 +1396,8 @@ def test_get_attribute_group():
|
||||
# -----------------------------------------------------------------------------
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_characteristics_by_uuid():
|
||||
[client, server] = LinkedDevices().devices[:2]
|
||||
devices = await TwoDevices.create_with_connection()
|
||||
[client, server] = devices
|
||||
|
||||
characteristic1 = Characteristic(
|
||||
'1234',
|
||||
@@ -1447,9 +1422,7 @@ async def test_get_characteristics_by_uuid():
|
||||
|
||||
server.add_services([service1, service2])
|
||||
|
||||
await client.power_on()
|
||||
await server.power_on()
|
||||
connection = await client.connect(server.random_address)
|
||||
connection = devices.connections[0]
|
||||
peer = Peer(connection)
|
||||
|
||||
await peer.discover_services()
|
||||
@@ -1472,7 +1445,8 @@ async def test_get_characteristics_by_uuid():
|
||||
# -----------------------------------------------------------------------------
|
||||
@pytest.mark.asyncio
|
||||
async def test_write_return_error():
|
||||
[client, server] = LinkedDevices().devices[:2]
|
||||
devices = await TwoDevices.create_with_connection()
|
||||
[client, server] = devices
|
||||
|
||||
on_write = Mock(side_effect=ATT_Error(error_code=ErrorCode.VALUE_NOT_ALLOWED))
|
||||
characteristic = Characteristic(
|
||||
@@ -1484,9 +1458,7 @@ async def test_write_return_error():
|
||||
service = Service('ABCD', [characteristic])
|
||||
server.add_service(service)
|
||||
|
||||
await client.power_on()
|
||||
await server.power_on()
|
||||
connection = await client.connect(server.random_address)
|
||||
connection = devices.connections[0]
|
||||
|
||||
async with Peer(connection) as peer:
|
||||
c = peer.get_characteristics_by_uuid(uuid=UUID('1234'))[0]
|
||||
|
||||
@@ -35,7 +35,7 @@ from bumble.smp import (
|
||||
OobLegacyContext,
|
||||
)
|
||||
|
||||
from .test_utils import TwoDevices
|
||||
from .test_utils import TwoDevices, async_barrier
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Logging
|
||||
@@ -56,12 +56,14 @@ async def test_self_disconnection():
|
||||
two_devices = TwoDevices()
|
||||
await two_devices.setup_connection()
|
||||
await two_devices.connections[0].disconnect()
|
||||
await async_barrier()
|
||||
assert two_devices.connections[0] is None
|
||||
assert two_devices.connections[1] is None
|
||||
|
||||
two_devices = TwoDevices()
|
||||
await two_devices.setup_connection()
|
||||
await two_devices.connections[1].disconnect()
|
||||
await async_barrier()
|
||||
assert two_devices.connections[0] is None
|
||||
assert two_devices.connections[1] is None
|
||||
|
||||
@@ -80,7 +82,8 @@ async def test_self_classic_connection(responder_role):
|
||||
two_devices.devices[1].classic_enabled = True
|
||||
|
||||
# Start
|
||||
await two_devices.setup_connection()
|
||||
for dev in two_devices.devices:
|
||||
await dev.power_on()
|
||||
|
||||
# Connect the two devices
|
||||
await asyncio.gather(
|
||||
@@ -418,8 +421,9 @@ async def test_self_smp_over_classic():
|
||||
two_devices.devices[1].classic_enabled = True
|
||||
|
||||
# Connect the two devices
|
||||
await two_devices.devices[0].power_on()
|
||||
await two_devices.devices[1].power_on()
|
||||
for dev in two_devices.devices:
|
||||
await dev.power_on()
|
||||
|
||||
await asyncio.gather(
|
||||
two_devices.devices[0].connect(
|
||||
two_devices.devices[1].public_address, transport=PhysicalTransport.BR_EDR
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# Imports
|
||||
# -----------------------------------------------------------------------------
|
||||
import asyncio
|
||||
import functools
|
||||
from typing import Optional
|
||||
|
||||
from typing_extensions import Self
|
||||
@@ -30,39 +31,34 @@ from bumble.transport.common import AsyncPipeSink
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
class TwoDevices:
|
||||
class Devices:
|
||||
connections: list[Optional[Connection]]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.connections = [None, None]
|
||||
def __init__(self, num_devices: int) -> None:
|
||||
self.connections = [None for _ in range(num_devices)]
|
||||
|
||||
self.link = LocalLink()
|
||||
addresses = ['F0:F1:F2:F3:F4:F5', 'F5:F4:F3:F2:F1:F0']
|
||||
addresses = [":".join([f"F{i}"] * 6) for i in range(num_devices)]
|
||||
self.controllers = [
|
||||
Controller('C1', link=self.link, public_address=addresses[0]),
|
||||
Controller('C2', link=self.link, public_address=addresses[1]),
|
||||
Controller(f'C{i+i}', link=self.link, public_address=addresses[i])
|
||||
for i in range(num_devices)
|
||||
]
|
||||
self.devices = [
|
||||
Device(
|
||||
address=Address(addresses[0]),
|
||||
host=Host(self.controllers[0], AsyncPipeSink(self.controllers[0])),
|
||||
),
|
||||
Device(
|
||||
address=Address(addresses[1]),
|
||||
host=Host(self.controllers[1], AsyncPipeSink(self.controllers[1])),
|
||||
),
|
||||
address=Address(addresses[i]),
|
||||
host=Host(self.controllers[i], AsyncPipeSink(self.controllers[i])),
|
||||
)
|
||||
for i in range(num_devices)
|
||||
]
|
||||
|
||||
self.devices[0].on(
|
||||
'connection', lambda connection: self.on_connection(0, connection)
|
||||
)
|
||||
self.devices[1].on(
|
||||
'connection', lambda connection: self.on_connection(1, connection)
|
||||
)
|
||||
for i in range(num_devices):
|
||||
self.devices[i].on(
|
||||
self.devices[i].EVENT_CONNECTION,
|
||||
functools.partial(self.on_connection, i),
|
||||
)
|
||||
|
||||
self.paired = [
|
||||
asyncio.get_event_loop().create_future(),
|
||||
asyncio.get_event_loop().create_future(),
|
||||
asyncio.get_event_loop().create_future() for _ in range(num_devices)
|
||||
]
|
||||
|
||||
def on_connection(self, which, connection):
|
||||
@@ -77,19 +73,26 @@ class TwoDevices:
|
||||
|
||||
async def setup_connection(self) -> None:
|
||||
# Start
|
||||
await self.devices[0].power_on()
|
||||
await self.devices[1].power_on()
|
||||
for dev in self.devices:
|
||||
await dev.power_on()
|
||||
|
||||
# Connect the two devices
|
||||
await self.devices[0].connect(self.devices[1].random_address)
|
||||
|
||||
# Check the post conditions
|
||||
assert self.connections[0] is not None
|
||||
assert self.connections[1] is not None
|
||||
# Connect devices
|
||||
for dev in self.devices[1:]:
|
||||
connection_future = asyncio.get_running_loop().create_future()
|
||||
dev.once(dev.EVENT_CONNECTION, connection_future.set_result)
|
||||
await dev.start_advertising(advertising_interval_min=1.0)
|
||||
await self.devices[0].connect(dev.random_address)
|
||||
await connection_future
|
||||
|
||||
def __getitem__(self, index: int) -> Device:
|
||||
return self.devices[index]
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
class TwoDevices(Devices):
|
||||
def __init__(self) -> None:
|
||||
super().__init__(2)
|
||||
|
||||
@classmethod
|
||||
async def create_with_connection(cls: type[Self]) -> Self:
|
||||
devices = cls()
|
||||
|
||||
Reference in New Issue
Block a user