Enhance transports

* Support IPv6 schema
* Add transport integration tests
* Add UNIX socket server
This commit is contained in:
Josh Wu
2025-08-18 16:12:48 +08:00
parent 3b8dd6f3cf
commit bb2aa8229d
10 changed files with 238 additions and 45 deletions
+13 -19
View File
@@ -16,8 +16,7 @@ import asyncio
import os
import pytest
import socket
import unittest
from unittest.mock import ANY, patch
from unittest import mock
from bumble.transport.tcp_server import (
open_tcp_server_transport,
@@ -25,28 +24,23 @@ from bumble.transport.tcp_server import (
)
class OpenTcpServerTransportTests(unittest.TestCase):
def setUp(self):
self.patcher = patch('bumble.transport.tcp_server._create_server')
self.mock_create_server = self.patcher.start()
async def test_open_with_spec():
with mock.patch.object(asyncio.get_running_loop(), 'create_server') as m:
await open_tcp_server_transport('localhost:32100')
m.assert_awaited_once_with(mock.ANY, host='localhost', port=32100)
def tearDown(self):
self.patcher.stop()
def test_open_with_spec(self):
asyncio.run(open_tcp_server_transport('localhost:32100'))
self.mock_create_server.assert_awaited_once_with(
ANY, host='localhost', port=32100
)
async def test_open_with_port_only_spec():
with mock.patch.object(asyncio.get_running_loop(), 'create_server') as m:
await open_tcp_server_transport('_:32100')
m.assert_awaited_once_with(mock.ANY, host=None, port=32100)
def test_open_with_port_only_spec(self):
asyncio.run(open_tcp_server_transport('_:32100'))
self.mock_create_server.assert_awaited_once_with(ANY, host=None, port=32100)
def test_open_with_socket(self):
async def test_open_with_socket():
with mock.patch.object(asyncio.get_running_loop(), 'create_server') as m:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
asyncio.run(open_tcp_server_transport_with_socket(sock=sock))
self.mock_create_server.assert_awaited_once_with(ANY, sock=sock)
await open_tcp_server_transport_with_socket(sock=sock)
m.assert_awaited_once_with(mock.ANY, sock=sock)
@pytest.mark.skipif(
+135
View File
@@ -17,9 +17,41 @@
# -----------------------------------------------------------------------------
import random
import os
import socket
import sys
import pytest
from bumble import controller
from bumble import device
from bumble import hci
from bumble import link
from bumble import transport
from bumble.transport.common import PacketParser
# -----------------------------------------------------------------------------
def _make_controller_from_transport(transport: transport.Transport):
return controller.Controller(
name="server",
host_sink=transport.sink,
host_source=transport.source,
link=link.LocalLink(),
)
# -----------------------------------------------------------------------------
def _make_device_from_transport(
transport: transport.Transport, address: str = "11:22:33:44:55:66"
):
return device.Device.with_hci(
name="client",
address=hci.Address(address),
hci_sink=transport.sink,
hci_source=transport.source,
)
# -----------------------------------------------------------------------------
class Sink:
def __init__(self):
@@ -71,6 +103,109 @@ def test_parser_extensions():
assert len(sink.packets) == 1
# -----------------------------------------------------------------------------
@pytest.mark.parametrize(
"address,",
("127.0.0.1", "::1"),
)
async def test_tcp_connection(address):
server_transport = await transport.open_transport(f"tcp-server:{address}:0")
port = server_transport.server.sockets[0].getsockname()[1]
_make_controller_from_transport(server_transport)
client_transport = await transport.open_transport(f"tcp-client:{address}:{port}")
client_device = _make_device_from_transport(client_transport)
await client_device.power_on()
await client_transport.close()
await server_transport.close()
# -----------------------------------------------------------------------------
@pytest.mark.parametrize(
"address, family",
(("127.0.0.1", socket.AF_INET), ("::1", socket.AF_INET6)),
)
async def test_udp_connection(address, family):
# Pick empty ports
ports = []
for _ in range(2):
sock = socket.socket(family=family, type=socket.SOCK_DGRAM)
sock.bind((address, 0))
ports.append(sock.getsockname()[1])
sock.close()
server_transport = await transport.open_transport(
f"udp:{address}:{ports[0]},{address}:{ports[1]}"
)
_make_controller_from_transport(server_transport)
client_transport = await transport.open_transport(
f"udp:{address}:{ports[1]},{address}:{ports[0]}"
)
client_device = _make_device_from_transport(client_transport)
await client_device.power_on()
await client_transport.close()
await server_transport.close()
# -----------------------------------------------------------------------------
@pytest.mark.parametrize(
"server_address, client_address",
(
("127.0.0.1", "ws://127.0.0.1"),
("::1", "ws://[::1]"),
),
)
async def test_ws_connection(server_address, client_address):
server_transport = await transport.open_transport(f"ws-server:{server_address}:0")
port = server_transport.server.sockets[0].getsockname()[1]
_make_controller_from_transport(server_transport)
client_transport = await transport.open_transport(
f"ws-client:{client_address}:{port}"
)
client_device = _make_device_from_transport(client_transport)
await client_device.power_on()
await client_transport.close()
await server_transport.close()
# -----------------------------------------------------------------------------
@pytest.mark.skipif(
sys.platform != 'linux', reason='Unix socket is only fully supported on Linux'
)
async def test_unix_connection_file(tmpdir):
path = str(tmpdir / 'bumble.sock')
server_transport = await transport.open_transport(f"unix-server:{path}")
_make_controller_from_transport(server_transport)
client_transport = await transport.open_transport(f"unix-client:{path}")
client_device = _make_device_from_transport(client_transport)
await client_device.power_on()
await client_transport.close()
await server_transport.close()
# -----------------------------------------------------------------------------
@pytest.mark.skipif(
sys.platform != 'linux', reason='Unix socket is only fully supported on Linux'
)
async def test_unix_connection_abstract():
server_transport = await transport.open_transport("unix-server:@bumble.test.sock")
_make_controller_from_transport(server_transport)
client_transport = await transport.open_transport("unix-client:@bumble.test.sock")
client_device = _make_device_from_transport(client_transport)
await client_device.power_on()
await client_transport.close()
await server_transport.close()
# -----------------------------------------------------------------------------
if __name__ == '__main__':
test_parser()