| // Copyright 2024 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| |
| #include "pw_bluetooth_proxy_private/test_utils.h" |
| |
| namespace pw::bluetooth::proxy { |
| namespace { |
| |
| // ########## UtilsTest |
| |
| TEST(UtilsTest, SetupKFrameProperlySegments) { |
| std::array<uint8_t, 23> expected_payload = {1, 2, 3, 4, 5, 6, 7, 8, |
| 9, 10, 11, 12, 13, 14, 15, 16, |
| 17, 18, 19, 20, 21, 22, 23}; |
| ASSERT_GE(expected_payload.size(), 2ul); |
| constexpr uint16_t kHandle = 0x123; |
| constexpr uint16_t kCid = 0x456; |
| |
| // Validate the segmentation of `expected_payload` based on every MPS from 2 |
| // octets up to 5 octets greater than the length of `expected_payload`. |
| for (size_t mps = 2; mps < expected_payload.size() + 5; ++mps) { |
| size_t segment_no; |
| size_t pdu_total_length = expected_payload.size() + kSduLengthFieldSize; |
| size_t total_num_segments = 1; |
| size_t final_segment_payload_size = |
| std::min(mps, expected_payload.size() + kSduLengthFieldSize); |
| // Only if total L2CAP PDU payload bytes to be sent are greater than MPS |
| // will multiple segments be sent. |
| if (mps < pdu_total_length) { |
| total_num_segments = pdu_total_length / mps; |
| // Account for the final segment, which may be sub-MPS sized and not |
| // accounted for in the above int division operation. |
| if (pdu_total_length % mps != 0) { |
| ++total_num_segments; |
| final_segment_payload_size = pdu_total_length % mps; |
| } |
| } |
| |
| for (segment_no = 0; segment_no < total_num_segments; ++segment_no) { |
| PW_TEST_ASSERT_OK_AND_ASSIGN( |
| KFrameWithStorage kframe, |
| SetupKFrame(kHandle, kCid, mps, segment_no, span(expected_payload))); |
| |
| uint16_t pdu_length = mps; |
| if (segment_no == total_num_segments - 1) { |
| pdu_length = final_segment_payload_size; |
| } |
| |
| // Validate ACL header. |
| emboss::AclDataFrameView acl = kframe.acl.writer; |
| EXPECT_EQ(acl.header().handle().Read(), kHandle); |
| EXPECT_EQ(acl.data_total_length().Read(), |
| emboss::BasicL2capHeader::IntrinsicSizeInBytes() + pdu_length); |
| |
| uint8_t* kframe_payload_start; |
| if (std::holds_alternative<emboss::FirstKFrameWriter>(kframe.writer)) { |
| emboss::FirstKFrameWriter first_kframe_writer = |
| std::get<emboss::FirstKFrameWriter>(kframe.writer); |
| EXPECT_EQ(first_kframe_writer.pdu_length().Read(), pdu_length); |
| EXPECT_EQ(first_kframe_writer.channel_id().Read(), kCid); |
| EXPECT_EQ(first_kframe_writer.sdu_length().Read(), |
| expected_payload.size()); |
| kframe_payload_start = |
| first_kframe_writer.payload().BackingStorage().data(); |
| } else { |
| emboss::SubsequentKFrameWriter subsequent_kframe_writer = |
| std::get<emboss::SubsequentKFrameWriter>(kframe.writer); |
| EXPECT_EQ(subsequent_kframe_writer.pdu_length().Read(), pdu_length); |
| EXPECT_EQ(subsequent_kframe_writer.channel_id().Read(), kCid); |
| kframe_payload_start = |
| subsequent_kframe_writer.payload().BackingStorage().data(); |
| } |
| |
| // Validate payload of segment. |
| size_t payload_length = pdu_length; |
| uint8_t* expected_payload_start = |
| expected_payload.data() + segment_no * mps; |
| if (segment_no == 0) { |
| payload_length -= kSduLengthFieldSize; |
| } else { |
| expected_payload_start -= kSduLengthFieldSize; |
| } |
| EXPECT_EQ( |
| 0, |
| std::memcmp( |
| kframe_payload_start, expected_payload_start, payload_length)); |
| } |
| |
| // Confirm that requesting a segment one past the final expected segment |
| // results in an error. |
| EXPECT_EQ( |
| SetupKFrame(kHandle, kCid, mps, segment_no, span(expected_payload)) |
| .status(), |
| Status::OutOfRange()); |
| } |
| } |
| |
| } // namespace |
| } // namespace pw::bluetooth::proxy |