xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/l2cap/le_dynamic_channel_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2024 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include "pw_bluetooth_sapphire/internal/host/l2cap/le_dynamic_channel.h"
16 
17 #include <memory>
18 
19 #include "pw_async/fake_dispatcher_fixture.h"
20 #include "pw_bluetooth_sapphire/internal/host/l2cap/dynamic_channel_registry.h"
21 #include "pw_bluetooth_sapphire/internal/host/l2cap/fake_signaling_channel.h"
22 #include "pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h"
23 #include "pw_bluetooth_sapphire/internal/host/l2cap/signaling_channel.h"
24 #include "pw_bluetooth_sapphire/internal/host/l2cap/types.h"
25 #include "pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
26 #include "pw_unit_test/framework.h"
27 
28 namespace bt::l2cap::internal {
29 namespace {
DynamicCid(int16_t channel_number=0)30 constexpr ChannelId DynamicCid(int16_t channel_number = 0) {
31   return kFirstDynamicChannelId + channel_number;
32 }
33 
LeConnReq(const LECreditBasedConnectionRequestPayload & payload)34 auto LeConnReq(const LECreditBasedConnectionRequestPayload& payload) {
35   return StaticByteBuffer(LowerBits(payload.le_psm),
36                           UpperBits(payload.le_psm),
37                           LowerBits(payload.src_cid),
38                           UpperBits(payload.src_cid),
39                           LowerBits(payload.mtu),
40                           UpperBits(payload.mtu),
41                           LowerBits(payload.mps),
42                           UpperBits(payload.mps),
43                           LowerBits(payload.initial_credits),
44                           UpperBits(payload.initial_credits));
45 }
46 
LeConnRsp(const LECreditBasedConnectionResponsePayload & payload)47 auto LeConnRsp(const LECreditBasedConnectionResponsePayload& payload) {
48   return StaticByteBuffer(LowerBits(payload.dst_cid),
49                           UpperBits(payload.dst_cid),
50                           LowerBits(payload.mtu),
51                           UpperBits(payload.mtu),
52                           LowerBits(payload.mps),
53                           UpperBits(payload.mps),
54                           LowerBits(payload.initial_credits),
55                           UpperBits(payload.initial_credits),
56                           LowerBits(static_cast<uint16_t>(payload.result)),
57                           UpperBits(static_cast<uint16_t>(payload.result)));
58 }
59 
DisconnReq(const DisconnectionRequestPayload & payload)60 auto DisconnReq(const DisconnectionRequestPayload& payload) {
61   return StaticByteBuffer(LowerBits(payload.dst_cid),
62                           UpperBits(payload.dst_cid),
63                           LowerBits(payload.src_cid),
64                           UpperBits(payload.src_cid));
65 }
66 
DisconnRsp(const DisconnectionResponsePayload & payload)67 auto DisconnRsp(const DisconnectionResponsePayload& payload) {
68   return StaticByteBuffer(LowerBits(payload.dst_cid),
69                           UpperBits(payload.dst_cid),
70                           LowerBits(payload.src_cid),
71                           UpperBits(payload.src_cid));
72 }
73 
74 class LeDynamicChannelTest : public pw::async::test::FakeDispatcherFixture {
75  public:
76   LeDynamicChannelTest() = default;
77   ~LeDynamicChannelTest() override = default;
78 
79  protected:
80   // Import types for brevity.
81   using DynamicChannelCallback = DynamicChannelRegistry::DynamicChannelCallback;
82   using ServiceRequestCallback = DynamicChannelRegistry::ServiceRequestCallback;
83 
84   // TestLoopFixture overrides
SetUp()85   void SetUp() override {
86     channel_close_cb_ = nullptr;
87     inbound_open_cb_ = nullptr;
88     signaling_channel_ =
89         std::make_unique<testing::FakeSignalingChannel>(dispatcher());
90 
91     registry_ = std::make_unique<LeDynamicChannelRegistry>(
92         sig(),
93         fit::bind_member<&LeDynamicChannelTest::OnChannelClose>(this),
94         fit::bind_member<&LeDynamicChannelTest::OnServiceRequest>(this),
95         /*random_channel_ids=*/false);
96   }
97 
TearDown()98   void TearDown() override {
99     RunUntilIdle();
100     services_.clear();
101     registry_ = nullptr;
102     signaling_channel_ = nullptr;
103     inbound_open_cb_ = nullptr;
104     channel_close_cb_ = nullptr;
105   }
106 
DoOpenOutbound(const LECreditBasedConnectionRequestPayload & request,const LECreditBasedConnectionResponsePayload & response,ChannelParameters params,SignalingChannel::Status response_status=SignalingChannel::Status::kSuccess)107   const DynamicChannel* DoOpenOutbound(
108       const LECreditBasedConnectionRequestPayload& request,
109       const LECreditBasedConnectionResponsePayload& response,
110       ChannelParameters params,
111       SignalingChannel::Status response_status =
112           SignalingChannel::Status::kSuccess) {
113     auto req = LeConnReq(request);
114     auto rsp = LeConnRsp(response);
115     EXPECT_OUTBOUND_REQ(*sig(),
116                         kLECreditBasedConnectionRequest,
117                         req.view(),
118                         {response_status, rsp.view()});
119 
120     auto channel = std::make_shared<std::optional<const DynamicChannel*>>();
121     registry()->OpenOutbound(
122         request.le_psm, params, [weak = std::weak_ptr(channel)](auto chan) {
123           if (auto channel = weak.lock()) {
124             *channel = chan;
125           }
126         });
127 
128     RunUntilIdle();
129     if (HasFatalFailure()) {
130       return nullptr;
131     }
132 
133     // We should always get a result, whether it is a success or failure.
134     EXPECT_TRUE(*channel);
135     if (!*channel) {
136       return nullptr;
137     }
138 
139     return **channel;
140   }
141 
DoOpenInbound(const LECreditBasedConnectionRequestPayload & request,const LECreditBasedConnectionResponsePayload & expected_response)142   bool DoOpenInbound(
143       const LECreditBasedConnectionRequestPayload& request,
144       const LECreditBasedConnectionResponsePayload& expected_response) {
145     auto req = LeConnReq(request);
146     auto rsp = LeConnRsp(expected_response);
147 
148     sig()->ReceiveExpect(
149         kLECreditBasedConnectionRequest, req.view(), rsp.view());
150 
151     RunUntilIdle();
152     if (HasFatalFailure()) {
153       return false;
154     }
155 
156     return true;
157   }
158 
DoCloseOutbound(const DisconnectionRequestPayload & request,const DisconnectionResponsePayload & response)159   bool DoCloseOutbound(const DisconnectionRequestPayload& request,
160                        const DisconnectionResponsePayload& response) {
161     auto req = DisconnReq(request);
162     auto rsp = DisconnRsp(response);
163     EXPECT_OUTBOUND_REQ(*sig(),
164                         kDisconnectionRequest,
165                         req.view(),
166                         {SignalingChannel::Status::kSuccess, rsp.view()});
167     bool channel_close_cb_called = false;
168     registry()->CloseChannel(request.src_cid,
169                              [&] { channel_close_cb_called = true; });
170     RunUntilIdle();
171     if (HasFatalFailure()) {
172       return false;
173     }
174 
175     return channel_close_cb_called;
176   }
177 
DoCloseOutbound(const DisconnectionRequestPayload & request)178   bool DoCloseOutbound(const DisconnectionRequestPayload& request) {
179     DisconnectionResponsePayload response = {request.dst_cid, request.src_cid};
180     return DoCloseOutbound(request, response);
181   }
182 
sig() const183   testing::FakeSignalingChannel* sig() const {
184     return signaling_channel_.get();
185   }
186 
registry() const187   LeDynamicChannelRegistry* registry() const { return registry_.get(); }
188 
set_channel_close_cb(DynamicChannelCallback close_cb)189   void set_channel_close_cb(DynamicChannelCallback close_cb) {
190     channel_close_cb_ = std::move(close_cb);
191   }
192 
RegisterService(Psm psm,ChannelParameters params)193   bool RegisterService(Psm psm, ChannelParameters params) {
194     auto [_, result] = services_.insert(
195         {psm,
196          DynamicChannelRegistry::ServiceInfo{
197              params,
198              fit::bind_member(this, &LeDynamicChannelTest::OnInboundChannel)}});
199     return result;
200   }
201 
SetNextInboundChannel(std::optional<const DynamicChannel * > * out)202   void SetNextInboundChannel(std::optional<const DynamicChannel*>* out) {
203     ASSERT_EQ(inbound_open_cb_, nullptr);
204     inbound_open_cb_ = [out](const DynamicChannel* channel) { *out = channel; };
205   }
206 
207  private:
OnChannelClose(const DynamicChannel * channel)208   void OnChannelClose(const DynamicChannel* channel) {
209     if (channel_close_cb_) {
210       channel_close_cb_(channel);
211     }
212   }
213 
OnInboundChannel(const DynamicChannel * channel)214   void OnInboundChannel(const DynamicChannel* channel) {
215     if (inbound_open_cb_) {
216       std::move(inbound_open_cb_)(channel);
217     }
218   }
219 
OnServiceRequest(Psm psm)220   std::optional<DynamicChannelRegistry::ServiceInfo> OnServiceRequest(Psm psm) {
221     auto iter = services_.find(psm);
222     if (iter == services_.end()) {
223       return std::nullopt;
224     }
225 
226     return DynamicChannelRegistry::ServiceInfo(iter->second.channel_params,
227                                                iter->second.channel_cb.share());
228   }
229 
230   DynamicChannelCallback channel_close_cb_;
231   DynamicChannelCallback inbound_open_cb_;
232   std::unordered_map<Psm, DynamicChannelRegistry::ServiceInfo> services_;
233   std::unique_ptr<testing::FakeSignalingChannel> signaling_channel_;
234   std::unique_ptr<LeDynamicChannelRegistry> registry_;
235 
236   BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LeDynamicChannelTest);
237 };
238 
TEST_F(LeDynamicChannelTest,StateToString)239 TEST_F(LeDynamicChannelTest, StateToString) {
240   constexpr std::array<std::pair<LeDynamicChannel::State, const char*>, 4>
241       kCases{{
242           {{.exchanged_connection_request = false,
243             .exchanged_connection_response = false,
244             .exchanged_disconnect_request = false},
245            "{exchanged_connection_request: false, "
246            "exchanged_connection_response: false, "
247            "exchanged_disconnect_request: false}"},
248           {{.exchanged_connection_request = false,
249             .exchanged_connection_response = false,
250             .exchanged_disconnect_request = true},
251            "{exchanged_connection_request: false, "
252            "exchanged_connection_response: false, "
253            "exchanged_disconnect_request: true}"},
254           {{.exchanged_connection_request = false,
255             .exchanged_connection_response = true,
256             .exchanged_disconnect_request = false},
257            "{exchanged_connection_request: false, "
258            "exchanged_connection_response: true, "
259            "exchanged_disconnect_request: false}"},
260           {{.exchanged_connection_request = true,
261             .exchanged_connection_response = false,
262             .exchanged_disconnect_request = false},
263            "{exchanged_connection_request: true, "
264            "exchanged_connection_response: false, "
265            "exchanged_disconnect_request: false}"},
266       }};
267   for (const auto& [state, expected] : kCases) {
268     EXPECT_EQ(state.ToString(), expected);
269   }
270 }
271 
TEST_F(LeDynamicChannelTest,OpenOutboundDefaultParametersCloseOutbound)272 TEST_F(LeDynamicChannelTest, OpenOutboundDefaultParametersCloseOutbound) {
273   static constexpr auto kMode =
274       CreditBasedFlowControlMode::kLeCreditBasedFlowControl;
275   static constexpr ChannelParameters kChannelParams{
276       .mode = kMode,
277       .max_rx_sdu_size = std::nullopt,
278       .flush_timeout = std::nullopt,
279   };
280   static constexpr LECreditBasedConnectionRequestPayload kLeConnReqPayload{
281       .le_psm = 0x0015,
282       .src_cid = DynamicCid(),
283       .mtu = kDefaultMTU,
284       .mps = kMaxInboundPduPayloadSize,
285       .initial_credits = 0x0000,
286   };
287   static constexpr LECreditBasedConnectionResponsePayload kLeConnRspPayload{
288       .dst_cid = DynamicCid(),
289       .mtu = 0x0064,
290       .mps = 0x0032,
291       .initial_credits = 0x0050,
292       .result = LECreditBasedConnectionResult::kSuccess,
293   };
294   static constexpr DisconnectionRequestPayload kDisconnReqPayload{
295       .dst_cid = kLeConnRspPayload.dst_cid,
296       .src_cid = kLeConnReqPayload.src_cid,
297   };
298 
299   auto chan =
300       DoOpenOutbound(kLeConnReqPayload, kLeConnRspPayload, kChannelParams);
301   ASSERT_TRUE(chan);
302   EXPECT_TRUE(chan->IsOpen());
303   EXPECT_TRUE(chan->IsConnected());
304   EXPECT_EQ(kLeConnReqPayload.src_cid, chan->local_cid());
305   EXPECT_EQ(kLeConnRspPayload.dst_cid, chan->remote_cid());
306 
307   ChannelInfo expected_info = ChannelInfo::MakeCreditBasedFlowControlMode(
308       kMode,
309       kDefaultMTU,
310       kLeConnRspPayload.mtu,
311       kLeConnRspPayload.mps,
312       kLeConnRspPayload.initial_credits);
313   ChannelInfo actual_info = chan->info();
314 
315   EXPECT_EQ(expected_info.mode, actual_info.mode);
316   EXPECT_EQ(expected_info.max_rx_sdu_size, actual_info.max_rx_sdu_size);
317   EXPECT_EQ(expected_info.max_tx_sdu_size, actual_info.max_tx_sdu_size);
318   EXPECT_EQ(expected_info.n_frames_in_tx_window,
319             actual_info.n_frames_in_tx_window);
320   EXPECT_EQ(expected_info.max_transmissions, actual_info.max_transmissions);
321   EXPECT_EQ(expected_info.max_tx_pdu_payload_size,
322             actual_info.max_tx_pdu_payload_size);
323   EXPECT_EQ(expected_info.psm, actual_info.psm);
324   EXPECT_EQ(expected_info.flush_timeout, actual_info.flush_timeout);
325   EXPECT_EQ(expected_info.remote_initial_credits,
326             actual_info.remote_initial_credits);
327 
328   EXPECT_TRUE(DoCloseOutbound(kDisconnReqPayload));
329 }
330 
TEST_F(LeDynamicChannelTest,OpenOutboundSpecificParametersCloseOutbound)331 TEST_F(LeDynamicChannelTest, OpenOutboundSpecificParametersCloseOutbound) {
332   static constexpr auto kMode =
333       CreditBasedFlowControlMode::kLeCreditBasedFlowControl;
334   static constexpr ChannelParameters kChannelParams{
335       .mode = kMode,
336       .max_rx_sdu_size = 0x0023,
337       .flush_timeout = std::nullopt,
338   };
339   static constexpr LECreditBasedConnectionRequestPayload kLeConnReqPayload{
340       .le_psm = 0x0015,
341       .src_cid = DynamicCid(),
342       .mtu = kChannelParams.max_rx_sdu_size.value_or(0),
343       .mps = kMaxInboundPduPayloadSize,
344       .initial_credits = 0x0000,
345   };
346   static constexpr LECreditBasedConnectionResponsePayload kLeConnRspPayload{
347       .dst_cid = DynamicCid(),
348       .mtu = 0x0064,
349       .mps = 0x0032,
350       .initial_credits = 0x0050,
351       .result = LECreditBasedConnectionResult::kSuccess,
352   };
353   static constexpr DisconnectionRequestPayload kDisconnReqPayload{
354       .dst_cid = kLeConnRspPayload.dst_cid,
355       .src_cid = kLeConnReqPayload.src_cid,
356   };
357 
358   auto chan =
359       DoOpenOutbound(kLeConnReqPayload, kLeConnRspPayload, kChannelParams);
360   ASSERT_TRUE(chan);
361   EXPECT_TRUE(chan->IsOpen());
362   EXPECT_TRUE(chan->IsConnected());
363   EXPECT_EQ(kLeConnReqPayload.src_cid, chan->local_cid());
364   EXPECT_EQ(kLeConnRspPayload.dst_cid, chan->remote_cid());
365 
366   ChannelInfo expected_info = ChannelInfo::MakeCreditBasedFlowControlMode(
367       kMode,
368       *kChannelParams.max_rx_sdu_size,
369       kLeConnRspPayload.mtu,
370       kLeConnRspPayload.mps,
371       kLeConnRspPayload.initial_credits);
372   ChannelInfo actual_info = chan->info();
373 
374   EXPECT_EQ(expected_info.mode, actual_info.mode);
375   EXPECT_EQ(expected_info.max_rx_sdu_size, actual_info.max_rx_sdu_size);
376   EXPECT_EQ(expected_info.max_tx_sdu_size, actual_info.max_tx_sdu_size);
377   EXPECT_EQ(expected_info.n_frames_in_tx_window,
378             actual_info.n_frames_in_tx_window);
379   EXPECT_EQ(expected_info.max_transmissions, actual_info.max_transmissions);
380   EXPECT_EQ(expected_info.max_tx_pdu_payload_size,
381             actual_info.max_tx_pdu_payload_size);
382   EXPECT_EQ(expected_info.psm, actual_info.psm);
383   EXPECT_EQ(expected_info.flush_timeout, actual_info.flush_timeout);
384   EXPECT_EQ(expected_info.remote_initial_credits,
385             actual_info.remote_initial_credits);
386 
387   EXPECT_TRUE(DoCloseOutbound(kDisconnReqPayload));
388 }
389 
TEST_F(LeDynamicChannelTest,OpenOutboundBadChannel)390 TEST_F(LeDynamicChannelTest, OpenOutboundBadChannel) {
391   static constexpr auto kMode =
392       CreditBasedFlowControlMode::kLeCreditBasedFlowControl;
393   static constexpr ChannelParameters kChannelParams{
394       .mode = kMode,
395       .max_rx_sdu_size = std::nullopt,
396       .flush_timeout = std::nullopt,
397   };
398   static constexpr LECreditBasedConnectionRequestPayload kLeConnReqPayload{
399       .le_psm = 0x0015,
400       .src_cid = DynamicCid(),
401       .mtu = kDefaultMTU,
402       .mps = kMaxInboundPduPayloadSize,
403       .initial_credits = 0x0000,
404   };
405   static constexpr LECreditBasedConnectionResponsePayload kLeConnRspPayload{
406       .dst_cid = DynamicCid(-1),
407       .mtu = 0x0064,
408       .mps = 0x0032,
409       .initial_credits = 0x0050,
410       .result = LECreditBasedConnectionResult::kSuccess,
411   };
412 
413   auto chan =
414       DoOpenOutbound(kLeConnReqPayload, kLeConnRspPayload, kChannelParams);
415   EXPECT_FALSE(chan);
416 }
417 
TEST_F(LeDynamicChannelTest,OpenOutboundRejected)418 TEST_F(LeDynamicChannelTest, OpenOutboundRejected) {
419   static constexpr auto kMode =
420       CreditBasedFlowControlMode::kLeCreditBasedFlowControl;
421   static constexpr ChannelParameters kChannelParams{
422       .mode = kMode,
423       .max_rx_sdu_size = std::nullopt,
424       .flush_timeout = std::nullopt,
425   };
426   static constexpr LECreditBasedConnectionRequestPayload kLeConnReqPayload{
427       .le_psm = 0x0015,
428       .src_cid = DynamicCid(),
429       .mtu = kDefaultMTU,
430       .mps = kMaxInboundPduPayloadSize,
431       .initial_credits = 0x0000,
432   };
433   static constexpr LECreditBasedConnectionResponsePayload kLeConnRspPayload{
434       .dst_cid = DynamicCid(),
435       .mtu = 0x0064,
436       .mps = 0x0032,
437       .initial_credits = 0x0050,
438       .result = LECreditBasedConnectionResult::kSuccess,
439   };
440 
441   auto chan = DoOpenOutbound(kLeConnReqPayload,
442                              kLeConnRspPayload,
443                              kChannelParams,
444                              SignalingChannel::Status::kReject);
445   EXPECT_FALSE(chan);
446 }
447 
TEST_F(LeDynamicChannelTest,OpenOutboundPsmNotSupported)448 TEST_F(LeDynamicChannelTest, OpenOutboundPsmNotSupported) {
449   static constexpr auto kMode =
450       CreditBasedFlowControlMode::kLeCreditBasedFlowControl;
451   static constexpr ChannelParameters kChannelParams{
452       .mode = kMode,
453       .max_rx_sdu_size = std::nullopt,
454       .flush_timeout = std::nullopt,
455   };
456   static constexpr LECreditBasedConnectionRequestPayload kLeConnReqPayload{
457       .le_psm = 0x0015,
458       .src_cid = DynamicCid(),
459       .mtu = kDefaultMTU,
460       .mps = kMaxInboundPduPayloadSize,
461       .initial_credits = 0x0000,
462   };
463   static constexpr LECreditBasedConnectionResponsePayload kLeConnRspPayload{
464       .dst_cid = DynamicCid(),
465       .mtu = 0x0064,
466       .mps = 0x0032,
467       .initial_credits = 0x0050,
468       .result = LECreditBasedConnectionResult::kPsmNotSupported,
469   };
470 
471   auto chan =
472       DoOpenOutbound(kLeConnReqPayload, kLeConnRspPayload, kChannelParams);
473   EXPECT_FALSE(chan);
474 }
475 
TEST_F(LeDynamicChannelTest,OpenInboundDefaultParamsCloseInbound)476 TEST_F(LeDynamicChannelTest, OpenInboundDefaultParamsCloseInbound) {
477   static constexpr auto kMode =
478       CreditBasedFlowControlMode::kLeCreditBasedFlowControl;
479   static constexpr Psm kPsm = 0x0015;
480   static constexpr ChannelParameters kChannelParams{
481       .mode = kMode,
482       .max_rx_sdu_size = std::nullopt,
483       .flush_timeout = std::nullopt,
484   };
485   static constexpr LECreditBasedConnectionRequestPayload kLeConnReqPayload{
486       .le_psm = kPsm,
487       .src_cid = DynamicCid(),
488       .mtu = 0x0064,
489       .mps = 0x0032,
490       .initial_credits = 0x0032,
491   };
492   static constexpr LECreditBasedConnectionResponsePayload kLeConnRspPayload{
493       .dst_cid = DynamicCid(),
494       .mtu = kDefaultMTU,
495       .mps = kMaxInboundPduPayloadSize,
496       .initial_credits = 0x0000,
497       .result = LECreditBasedConnectionResult::kSuccess,
498   };
499   static constexpr DisconnectionRequestPayload kDisconnReqPayload{
500       .dst_cid = kLeConnRspPayload.dst_cid,
501       .src_cid = kLeConnReqPayload.src_cid,
502   };
503 
504   std::optional<const DynamicChannel*> channel;
505   SetNextInboundChannel(&channel);
506   ASSERT_TRUE(RegisterService(kPsm, kChannelParams));
507   EXPECT_TRUE(DoOpenInbound(kLeConnReqPayload, kLeConnRspPayload));
508   ASSERT_TRUE(channel);
509   ASSERT_TRUE(*channel);
510   EXPECT_TRUE((*channel)->IsOpen());
511   EXPECT_TRUE((*channel)->IsConnected());
512   EXPECT_EQ(kLeConnReqPayload.src_cid, (*channel)->remote_cid());
513   EXPECT_EQ(kLeConnRspPayload.dst_cid, (*channel)->local_cid());
514 
515   ChannelInfo expected_info = ChannelInfo::MakeCreditBasedFlowControlMode(
516       kMode,
517       kLeConnRspPayload.mtu,
518       kLeConnReqPayload.mtu,
519       kLeConnReqPayload.mps,
520       kLeConnReqPayload.initial_credits);
521   ChannelInfo actual_info = (*channel)->info();
522 
523   EXPECT_EQ(expected_info.mode, actual_info.mode);
524   EXPECT_EQ(expected_info.max_rx_sdu_size, actual_info.max_rx_sdu_size);
525   EXPECT_EQ(expected_info.max_tx_sdu_size, actual_info.max_tx_sdu_size);
526   EXPECT_EQ(expected_info.n_frames_in_tx_window,
527             actual_info.n_frames_in_tx_window);
528   EXPECT_EQ(expected_info.max_transmissions, actual_info.max_transmissions);
529   EXPECT_EQ(expected_info.max_tx_pdu_payload_size,
530             actual_info.max_tx_pdu_payload_size);
531   EXPECT_EQ(expected_info.psm, actual_info.psm);
532   EXPECT_EQ(expected_info.flush_timeout, actual_info.flush_timeout);
533   EXPECT_EQ(expected_info.remote_initial_credits,
534             actual_info.remote_initial_credits);
535 
536   EXPECT_TRUE(DoCloseOutbound(kDisconnReqPayload));
537 }
538 
TEST_F(LeDynamicChannelTest,OpenInboundUnsupportedPsm)539 TEST_F(LeDynamicChannelTest, OpenInboundUnsupportedPsm) {
540   static constexpr Psm kPsm = 0x0015;
541   static constexpr LECreditBasedConnectionRequestPayload kLeConnReqPayload{
542       .le_psm = kPsm,
543       .src_cid = DynamicCid(),
544       .mtu = 0x0064,
545       .mps = 0x0032,
546       .initial_credits = 0x0032,
547   };
548   static constexpr LECreditBasedConnectionResponsePayload kLeConnRspPayload{
549       .dst_cid = kInvalidChannelId,
550       .mtu = 0,
551       .mps = 0,
552       .initial_credits = 0,
553       .result = LECreditBasedConnectionResult::kPsmNotSupported,
554   };
555 
556   std::optional<const DynamicChannel*> channel;
557   SetNextInboundChannel(&channel);
558   EXPECT_TRUE(DoOpenInbound(kLeConnReqPayload, kLeConnRspPayload));
559   EXPECT_FALSE(channel);
560 }
561 
TEST_F(LeDynamicChannelTest,OpenInboundBadChannel)562 TEST_F(LeDynamicChannelTest, OpenInboundBadChannel) {
563   static constexpr LECreditBasedConnectionRequestPayload kLeConnReqPayload{
564       .le_psm = 0x0015,
565       .src_cid = DynamicCid(-1),
566       .mtu = 0x0064,
567       .mps = 0x0032,
568       .initial_credits = 0x0050,
569   };
570   static constexpr LECreditBasedConnectionResponsePayload kLeConnRspPayload{
571       .dst_cid = kInvalidChannelId,
572       .mtu = 0,
573       .mps = 0,
574       .initial_credits = 0,
575       .result = LECreditBasedConnectionResult::kInvalidSourceCID,
576   };
577 
578   std::optional<const DynamicChannel*> channel;
579   SetNextInboundChannel(&channel);
580   EXPECT_TRUE(DoOpenInbound(kLeConnReqPayload, kLeConnRspPayload));
581   EXPECT_FALSE(channel);
582 }
583 
584 }  // namespace
585 }  // namespace bt::l2cap::internal
586