Skip to content

Commit f09da85

Browse files
committed
L2CAP cases 2
1 parent e64ebd5 commit f09da85

File tree

5 files changed

+111
-216
lines changed

5 files changed

+111
-216
lines changed

avatar/cases/l2cap_test.py

Lines changed: 75 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -19,25 +19,14 @@
1919
from avatar import BumblePandoraDevice
2020
from avatar import PandoraDevice
2121
from avatar import PandoraDevices
22-
from avatar.common import make_bredr_connection
23-
from avatar.common import make_le_connection
22+
from avatar import pandora_snippet
2423
from mobly import base_test
2524
from mobly import test_runner
2625
from mobly.asserts import assert_equal # type: ignore
2726
from mobly.asserts import assert_is_not_none # type: ignore
2827
from pandora import host_pb2
29-
from pandora import l2cap_pb2
30-
from typing import Any, Awaitable, Callable, Dict, Literal, Optional, Tuple, Union
31-
32-
CONNECTORS: Dict[
33-
str,
34-
Callable[[avatar.PandoraDevice, avatar.PandoraDevice], Awaitable[Tuple[host_pb2.Connection, host_pb2.Connection]]],
35-
] = {
36-
'Classic': make_bredr_connection,
37-
'LE': make_le_connection,
38-
}
39-
40-
FIXED_CHANNEL_CID = 0x3E
28+
from typing import Any, Dict, Optional
29+
4130
CLASSIC_PSM = 0xFEFF
4231
LE_SPSM = 0xF0
4332

@@ -49,6 +38,10 @@ class L2capTest(base_test.BaseTestClass): # type: ignore[misc]
4938
dut: PandoraDevice
5039
ref: PandoraDevice
5140

41+
# BR/EDR & Low-Energy connections.
42+
dut_ref: Dict[str, host_pb2.Connection] = {}
43+
ref_dut: Dict[str, host_pb2.Connection] = {}
44+
5245
def setup_class(self) -> None:
5346
self.devices = PandoraDevices(self)
5447
self.dut, self.ref, *_ = self.devices
@@ -66,160 +59,122 @@ def teardown_class(self) -> None:
6659
async def setup_test(self) -> None: # pytype: disable=wrong-arg-types
6760
await asyncio.gather(self.dut.reset(), self.ref.reset())
6861

62+
# Connect REF to DUT in both BR/EDR and Low-Energy.
63+
ref_dut_br, dut_ref_br = await pandora_snippet.connect(self.ref, self.dut)
64+
ref_dut_le, dut_ref_le = await pandora_snippet.connect_le_dummy(self.ref, self.dut)
65+
66+
self.dut_ref = dict(basic=dut_ref_br, le_credit_based=dut_ref_le)
67+
self.ref_dut = dict(basic=ref_dut_br, le_credit_based=ref_dut_le)
68+
6969
@avatar.parameterized(
70-
('Classic', dict(fixed=l2cap_pb2.FixedChannelRequest(cid=FIXED_CHANNEL_CID))),
71-
('LE', dict(fixed=l2cap_pb2.FixedChannelRequest(cid=FIXED_CHANNEL_CID))),
72-
('Classic', dict(basic=l2cap_pb2.ConnectionOrientedChannelRequest(psm=CLASSIC_PSM))),
73-
(
74-
'LE',
75-
dict(
76-
le_credit_based=l2cap_pb2.CreditBasedChannelRequest(
77-
spsm=LE_SPSM, mtu=2046, mps=2048, initial_credit=256
78-
)
79-
),
80-
),
70+
(dict(basic=dict(psm=CLASSIC_PSM)),),
71+
(dict(le_credit_based=dict(spsm=LE_SPSM, mtu=2046, mps=2048, initial_credit=256)),),
8172
) # type: ignore[misc]
8273
@avatar.asynchronous
8374
async def test_connect(
8475
self,
85-
transport: Union[Literal['Classic'], Literal['LE']],
8676
request: Dict[str, Any],
8777
) -> None:
88-
dut_ref_acl, ref_dut_acl = await CONNECTORS[transport](self.dut, self.ref)
89-
server = self.ref.aio.l2cap.OnConnection(connection=ref_dut_acl, **request)
90-
ref_dut_res, dut_ref_res = await asyncio.gather(
91-
anext(aiter(server)),
92-
self.dut.aio.l2cap.Connect(connection=dut_ref_acl, **request),
78+
transport = next(iter(request.keys()))
79+
ref_dut, dut_ref = await asyncio.gather(
80+
self.ref.aio.l2cap.WaitConnection(connection=self.ref_dut[transport], **request),
81+
self.dut.aio.l2cap.Connect(connection=self.dut_ref[transport], **request),
9382
)
94-
assert_is_not_none(ref_dut_res.channel)
95-
assert_is_not_none(dut_ref_res.channel)
83+
assert_is_not_none(ref_dut.channel)
84+
assert_is_not_none(dut_ref.channel)
9685

9786
@avatar.parameterized(
98-
('Classic', dict(fixed=l2cap_pb2.FixedChannelRequest(cid=FIXED_CHANNEL_CID))),
99-
('LE', dict(fixed=l2cap_pb2.FixedChannelRequest(cid=FIXED_CHANNEL_CID))),
100-
('Classic', dict(basic=l2cap_pb2.ConnectionOrientedChannelRequest(psm=CLASSIC_PSM))),
101-
(
102-
'LE',
103-
dict(
104-
le_credit_based=l2cap_pb2.CreditBasedChannelRequest(
105-
spsm=LE_SPSM, mtu=2046, mps=2048, initial_credit=256
106-
)
107-
),
108-
),
87+
(dict(basic=dict(psm=CLASSIC_PSM)),),
88+
(dict(le_credit_based=dict(spsm=LE_SPSM, mtu=2046, mps=2048, initial_credit=256)),),
10989
) # type: ignore[misc]
11090
@avatar.asynchronous
111-
async def test_on_connection(
91+
async def test_wait_connection(
11292
self,
113-
transport: Union[Literal['Classic'], Literal['LE']],
11493
request: Dict[str, Any],
11594
) -> None:
116-
dut_ref_acl, ref_dut_acl = await CONNECTORS[transport](self.dut, self.ref)
117-
server = self.dut.aio.l2cap.OnConnection(connection=dut_ref_acl, **request)
118-
ref_dut_res, dut_ref_res = await asyncio.gather(
119-
self.ref.aio.l2cap.Connect(connection=ref_dut_acl, **request),
120-
anext(aiter(server)),
95+
transport = next(iter(request.keys()))
96+
dut_ref, ref_dut = await asyncio.gather(
97+
self.ref.aio.l2cap.WaitConnection(connection=self.dut_ref[transport], **request),
98+
self.dut.aio.l2cap.Connect(connection=self.ref_dut[transport], **request),
12199
)
122-
assert_is_not_none(ref_dut_res.channel)
123-
assert_is_not_none(dut_ref_res.channel)
100+
assert_is_not_none(ref_dut.channel)
101+
assert_is_not_none(dut_ref.channel)
124102

125103
@avatar.parameterized(
126-
('Classic', dict(basic=l2cap_pb2.ConnectionOrientedChannelRequest(psm=CLASSIC_PSM))),
127-
(
128-
'LE',
129-
dict(
130-
le_credit_based=l2cap_pb2.CreditBasedChannelRequest(
131-
spsm=LE_SPSM, mtu=2046, mps=2048, initial_credit=256
132-
)
133-
),
134-
),
104+
(dict(basic=dict(psm=CLASSIC_PSM)),),
105+
(dict(le_credit_based=dict(spsm=LE_SPSM, mtu=2046, mps=2048, initial_credit=256)),),
135106
) # type: ignore[misc]
136107
@avatar.asynchronous
137108
async def test_disconnect(
138109
self,
139-
transport: Union[Literal['Classic'], Literal['LE']],
140110
request: Dict[str, Any],
141111
) -> None:
142-
dut_ref_acl, ref_dut_acl = await CONNECTORS[transport](self.dut, self.ref)
143-
server = self.ref.aio.l2cap.OnConnection(connection=ref_dut_acl, **request)
144-
ref_dut_res, dut_ref_res = await asyncio.gather(
145-
anext(aiter(server)),
146-
self.dut.aio.l2cap.Connect(connection=dut_ref_acl, **request),
112+
transport = next(iter(request.keys()))
113+
dut_ref, ref_dut = await asyncio.gather(
114+
self.ref.aio.l2cap.WaitConnection(connection=self.dut_ref[transport], **request),
115+
self.dut.aio.l2cap.Connect(connection=self.ref_dut[transport], **request),
147116
)
148-
assert dut_ref_res.channel and ref_dut_res.channel
117+
assert_is_not_none(ref_dut.channel)
118+
assert_is_not_none(dut_ref.channel)
119+
assert ref_dut.channel and dut_ref.channel
149120

150-
await asyncio.gather(
151-
self.dut.aio.l2cap.Disconnect(channel=dut_ref_res.channel),
152-
self.ref.aio.l2cap.WaitDisconnection(channel=ref_dut_res.channel),
121+
_, dis = await asyncio.gather(
122+
self.ref.aio.l2cap.WaitDisconnection(channel=ref_dut.channel),
123+
self.dut.aio.l2cap.Disconnect(channel=dut_ref.channel),
153124
)
154125

126+
assert_equal(dis.result_variant(), 'success')
127+
155128
@avatar.parameterized(
156-
('Classic', dict(basic=l2cap_pb2.ConnectionOrientedChannelRequest(psm=CLASSIC_PSM))),
157-
(
158-
'LE',
159-
dict(
160-
le_credit_based=l2cap_pb2.CreditBasedChannelRequest(
161-
spsm=LE_SPSM, mtu=2046, mps=2048, initial_credit=256
162-
)
163-
),
164-
),
129+
(dict(basic=dict(psm=CLASSIC_PSM)),),
130+
(dict(le_credit_based=dict(spsm=LE_SPSM, mtu=2046, mps=2048, initial_credit=256)),),
165131
) # type: ignore[misc]
166132
@avatar.asynchronous
167133
async def test_wait_disconnection(
168134
self,
169-
transport: Union[Literal['Classic'], Literal['LE']],
170135
request: Dict[str, Any],
171136
) -> None:
172-
dut_ref_acl, ref_dut_acl = await CONNECTORS[transport](self.dut, self.ref)
173-
server = self.ref.aio.l2cap.OnConnection(connection=ref_dut_acl, **request)
174-
ref_dut_res, dut_ref_res = await asyncio.gather(
175-
anext(aiter(server)),
176-
self.dut.aio.l2cap.Connect(connection=dut_ref_acl, **request),
137+
transport = next(iter(request.keys()))
138+
dut_ref, ref_dut = await asyncio.gather(
139+
self.ref.aio.l2cap.WaitConnection(connection=self.dut_ref[transport], **request),
140+
self.dut.aio.l2cap.Connect(connection=self.ref_dut[transport], **request),
177141
)
178-
assert dut_ref_res.channel and ref_dut_res.channel
142+
assert_is_not_none(ref_dut.channel)
143+
assert_is_not_none(dut_ref.channel)
144+
assert ref_dut.channel and dut_ref.channel
179145

180-
await asyncio.gather(
181-
self.ref.aio.l2cap.Disconnect(channel=ref_dut_res.channel),
182-
self.dut.aio.l2cap.WaitDisconnection(channel=dut_ref_res.channel),
146+
dis, _ = await asyncio.gather(
147+
self.dut.aio.l2cap.WaitDisconnection(channel=dut_ref.channel),
148+
self.ref.aio.l2cap.Disconnect(channel=ref_dut.channel),
183149
)
184150

151+
assert_equal(dis.result_variant(), 'success')
152+
185153
@avatar.parameterized(
186-
('Classic', dict(fixed=l2cap_pb2.FixedChannelRequest(cid=FIXED_CHANNEL_CID))),
187-
('LE', dict(fixed=l2cap_pb2.FixedChannelRequest(cid=FIXED_CHANNEL_CID))),
188-
('Classic', dict(basic=l2cap_pb2.ConnectionOrientedChannelRequest(psm=CLASSIC_PSM))),
189-
(
190-
'LE',
191-
dict(
192-
le_credit_based=l2cap_pb2.CreditBasedChannelRequest(
193-
spsm=LE_SPSM, mtu=2046, mps=2048, initial_credit=256
194-
)
195-
),
196-
),
154+
(dict(basic=dict(psm=CLASSIC_PSM)),),
155+
(dict(le_credit_based=dict(spsm=LE_SPSM, mtu=2046, mps=2048, initial_credit=256)),),
197156
) # type: ignore[misc]
198157
@avatar.asynchronous
199158
async def test_send(
200159
self,
201-
transport: Union[Literal['Classic'], Literal['LE']],
202160
request: Dict[str, Any],
203161
) -> None:
204-
dut_ref_acl, ref_dut_acl = await CONNECTORS[transport](self.dut, self.ref)
205-
server = self.dut.aio.l2cap.OnConnection(connection=dut_ref_acl, **request)
206-
ref_dut_res, dut_ref_res = await asyncio.gather(
207-
self.ref.aio.l2cap.Connect(connection=ref_dut_acl, **request),
208-
anext(aiter(server)),
162+
transport = next(iter(request.keys()))
163+
dut_ref, ref_dut = await asyncio.gather(
164+
self.ref.aio.l2cap.WaitConnection(connection=self.dut_ref[transport], **request),
165+
self.dut.aio.l2cap.Connect(connection=self.ref_dut[transport], **request),
209166
)
210-
ref_dut_channel = ref_dut_res.channel
211-
dut_ref_channel = dut_ref_res.channel
212-
assert_is_not_none(ref_dut_res.channel)
213-
assert_is_not_none(dut_ref_res.channel)
214-
assert ref_dut_channel and dut_ref_channel
215-
216-
dut_ref_stream = self.ref.aio.l2cap.Receive(channel=dut_ref_channel)
217-
_send_res, recv_res = await asyncio.gather(
218-
self.dut.aio.l2cap.Send(channel=ref_dut_channel, data=b"The quick brown fox jumps over the lazy dog"),
219-
anext(aiter(dut_ref_stream)),
167+
assert_is_not_none(ref_dut.channel)
168+
assert_is_not_none(dut_ref.channel)
169+
assert ref_dut.channel and dut_ref.channel
170+
171+
ref_source = self.ref.aio.l2cap.Receive(channel=dut_ref.channel)
172+
_, recv = await asyncio.gather(
173+
self.dut.aio.l2cap.Send(channel=ref_dut.channel, data=b"The quick brown fox jumps over the lazy dog"),
174+
anext(aiter(ref_source)),
220175
)
221-
assert recv_res.data
222-
assert_equal(recv_res.data, b"The quick brown fox jumps over the lazy dog")
176+
177+
assert_equal(recv.data, b"The quick brown fox jumps over the lazy dog")
223178

224179

225180
if __name__ == "__main__":

avatar/cases/le_security_test.py

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -203,38 +203,17 @@ async def connect_and_pair() -> Tuple[SecureResponse, WaitSecurityResponse]:
203203
nonlocal ref_dut
204204
nonlocal dut_ref
205205

206-
# Make LE connection task.
207-
async def connect_le(
208-
initiator: PandoraDevice,
209-
acceptor: PandoraDevice,
210-
initiator_addr_type: OwnAddressType,
211-
acceptor_addr_type: OwnAddressType,
212-
) -> Tuple[Connection, Connection]:
213-
# Acceptor - Advertise
214-
advertisement = acceptor.aio.host.Advertise(
215-
legacy=True,
216-
connectable=True,
217-
own_address_type=acceptor_addr_type,
218-
data=DataTypes(manufacturer_specific_data=b'pause cafe'),
219-
)
220-
221-
# Initiator - Scan and fetch the address
222-
scan = initiator.aio.host.Scan(own_address_type=initiator_addr_type)
223-
acceptor_scan = await anext(
224-
(x async for x in scan if b'pause cafe' in x.data.manufacturer_specific_data)
225-
) # pytype: disable=name-error
226-
scan.cancel()
227-
228-
# Initiator - LE connect
229-
return await pandora_snippet.connect_le(initiator, advertisement, acceptor_scan, initiator_addr_type)
230-
231206
# Make LE connection.
232207
if connect == 'incoming_connection':
233208
# DUT is acceptor
234-
ref_dut, dut_ref = await connect_le(self.ref, self.dut, ref_address_type, dut_address_type)
209+
ref_dut, dut_ref = await pandora_snippet.connect_le_dummy(
210+
self.ref, self.dut, ref_address_type, dut_address_type
211+
)
235212
else:
236213
# DUT is initiator
237-
dut_ref, ref_dut = await connect_le(self.dut, self.ref, dut_address_type, ref_address_type)
214+
dut_ref, ref_dut = await pandora_snippet.connect_le_dummy(
215+
self.dut, self.ref, dut_address_type, ref_address_type
216+
)
238217

239218
# Pairing.
240219

avatar/common.py

Lines changed: 0 additions & 67 deletions
This file was deleted.

0 commit comments

Comments
 (0)