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_proxy_private/test_utils.h"
16
17 namespace pw::bluetooth::proxy {
18 namespace {
19
20 // ########## UtilsTest
21
TEST(UtilsTest,SetupKFrameProperlySegments)22 TEST(UtilsTest, SetupKFrameProperlySegments) {
23 std::array<uint8_t, 23> expected_payload = {1, 2, 3, 4, 5, 6, 7, 8,
24 9, 10, 11, 12, 13, 14, 15, 16,
25 17, 18, 19, 20, 21, 22, 23};
26 ASSERT_GE(expected_payload.size(), 2ul);
27 constexpr uint16_t kHandle = 0x123;
28 constexpr uint16_t kCid = 0x456;
29
30 // Validate the segmentation of `expected_payload` based on every MPS from 2
31 // octets up to 5 octets greater than the length of `expected_payload`.
32 for (size_t mps = 2; mps < expected_payload.size() + 5; ++mps) {
33 size_t segment_no;
34 size_t pdu_total_length = expected_payload.size() + kSduLengthFieldSize;
35 size_t total_num_segments = 1;
36 size_t final_segment_payload_size =
37 std::min(mps, expected_payload.size() + kSduLengthFieldSize);
38 // Only if total L2CAP PDU payload bytes to be sent are greater than MPS
39 // will multiple segments be sent.
40 if (mps < pdu_total_length) {
41 total_num_segments = pdu_total_length / mps;
42 // Account for the final segment, which may be sub-MPS sized and not
43 // accounted for in the above int division operation.
44 if (pdu_total_length % mps != 0) {
45 ++total_num_segments;
46 final_segment_payload_size = pdu_total_length % mps;
47 }
48 }
49
50 for (segment_no = 0; segment_no < total_num_segments; ++segment_no) {
51 PW_TEST_ASSERT_OK_AND_ASSIGN(
52 KFrameWithStorage kframe,
53 SetupKFrame(kHandle, kCid, mps, segment_no, span(expected_payload)));
54
55 uint16_t pdu_length = mps;
56 if (segment_no == total_num_segments - 1) {
57 pdu_length = final_segment_payload_size;
58 }
59
60 // Validate ACL header.
61 emboss::AclDataFrameView acl = kframe.acl.writer;
62 EXPECT_EQ(acl.header().handle().Read(), kHandle);
63 EXPECT_EQ(acl.data_total_length().Read(),
64 emboss::BasicL2capHeader::IntrinsicSizeInBytes() + pdu_length);
65
66 uint8_t* kframe_payload_start;
67 if (std::holds_alternative<emboss::FirstKFrameWriter>(kframe.writer)) {
68 emboss::FirstKFrameWriter first_kframe_writer =
69 std::get<emboss::FirstKFrameWriter>(kframe.writer);
70 EXPECT_EQ(first_kframe_writer.pdu_length().Read(), pdu_length);
71 EXPECT_EQ(first_kframe_writer.channel_id().Read(), kCid);
72 EXPECT_EQ(first_kframe_writer.sdu_length().Read(),
73 expected_payload.size());
74 kframe_payload_start =
75 first_kframe_writer.payload().BackingStorage().data();
76 } else {
77 emboss::SubsequentKFrameWriter subsequent_kframe_writer =
78 std::get<emboss::SubsequentKFrameWriter>(kframe.writer);
79 EXPECT_EQ(subsequent_kframe_writer.pdu_length().Read(), pdu_length);
80 EXPECT_EQ(subsequent_kframe_writer.channel_id().Read(), kCid);
81 kframe_payload_start =
82 subsequent_kframe_writer.payload().BackingStorage().data();
83 }
84
85 // Validate payload of segment.
86 size_t payload_length = pdu_length;
87 uint8_t* expected_payload_start =
88 expected_payload.data() + segment_no * mps;
89 if (segment_no == 0) {
90 payload_length -= kSduLengthFieldSize;
91 } else {
92 expected_payload_start -= kSduLengthFieldSize;
93 }
94 EXPECT_EQ(
95 0,
96 std::memcmp(
97 kframe_payload_start, expected_payload_start, payload_length));
98 }
99
100 // Confirm that requesting a segment one past the final expected segment
101 // results in an error.
102 EXPECT_EQ(
103 SetupKFrame(kHandle, kCid, mps, segment_no, span(expected_payload))
104 .status(),
105 Status::OutOfRange());
106 }
107 }
108
109 } // namespace
110 } // namespace pw::bluetooth::proxy
111