blob: a37cc85d1e55b6be0dd817b7883ccc69b399fca2 [file] [log] [blame] [edit]
// 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