// 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 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(kframe.writer)) { emboss::FirstKFrameWriter first_kframe_writer = std::get(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(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