blob: 0e53d2a47d53b36e6dba63a4fcca15a29f4e9739 [file] [log] [blame]
/*
*
* Copyright (c) 2024 Project CHIP Authors
* Copyright (c) 2018 Google LLC.
* Copyright (c) 2018 Nest Labs, Inc.
*
* 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
*
* http://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 <cstdint>
#include <numeric>
#include <pw_unit_test/framework.h>
#include <lib/core/StringBuilderAdapters.h>
#include <lib/support/CHIPMem.h>
#include <lib/support/logging/CHIPLogging.h>
#define _CHIP_BLE_BLE_H
#include <ble/BleLayer.h>
#include <ble/BtpEngine.h>
using namespace chip;
using namespace chip::Ble;
namespace {
class TestBtpEngine : public ::testing::Test
{
public:
static void SetUpTestSuite() { ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR); }
static void TearDownTestSuite() { chip::Platform::MemoryShutdown(); }
void SetUp()
{
ASSERT_EQ(mBtpEngine.Init(nullptr, false), CHIP_NO_ERROR);
ChipLogDetail(Test, "### Initial BTP Engine State:");
mBtpEngine.LogState();
}
void TearDown()
{
ChipLogDetail(Test, "### Final BTP Engine State:");
mBtpEngine.LogState();
}
Ble::BtpEngine mBtpEngine;
};
TEST_F(TestBtpEngine, HandleCharacteristicReceivedOnePacket)
{
constexpr uint8_t packetData0[] = {
to_underlying(BtpEngine::HeaderFlags::kStartMessage) | to_underlying(BtpEngine::HeaderFlags::kEndMessage),
0x01,
0x01,
0x00,
0xff, // payload
};
auto packet0 = System::PacketBufferHandle::NewWithData(packetData0, sizeof(packetData0));
EXPECT_EQ(packet0->DataLength(), static_cast<size_t>(5));
SequenceNumber_t receivedAck;
bool didReceiveAck;
EXPECT_EQ(mBtpEngine.HandleCharacteristicReceived(std::move(packet0), receivedAck, didReceiveAck), CHIP_NO_ERROR);
EXPECT_EQ(mBtpEngine.RxState(), BtpEngine::kState_Complete);
}
TEST_F(TestBtpEngine, HandleCharacteristicReceivedTwoPacket)
{
constexpr uint8_t packetData0[] = { to_underlying(BtpEngine::HeaderFlags::kStartMessage), 0x01, 0x02, 0x00, 0xfe };
constexpr uint8_t packetData1[] = { to_underlying(BtpEngine::HeaderFlags::kEndMessage), 0x02, 0xff };
auto packet0 = System::PacketBufferHandle::NewWithData(packetData0, sizeof(packetData0));
EXPECT_EQ(packet0->DataLength(), static_cast<size_t>(5));
SequenceNumber_t receivedAck;
bool didReceiveAck;
EXPECT_EQ(mBtpEngine.HandleCharacteristicReceived(std::move(packet0), receivedAck, didReceiveAck), CHIP_NO_ERROR);
EXPECT_EQ(mBtpEngine.RxState(), BtpEngine::kState_InProgress);
auto packet1 = System::PacketBufferHandle::NewWithData(packetData1, sizeof(packetData1));
EXPECT_EQ(packet1->DataLength(), static_cast<size_t>(3));
EXPECT_EQ(mBtpEngine.HandleCharacteristicReceived(std::move(packet1), receivedAck, didReceiveAck), CHIP_NO_ERROR);
EXPECT_EQ(mBtpEngine.RxState(), BtpEngine::kState_Complete);
}
TEST_F(TestBtpEngine, HandleCharacteristicReceivedThreePacket)
{
constexpr uint8_t packetData0[] = { to_underlying(BtpEngine::HeaderFlags::kStartMessage), 0x01, 0x03, 0x00, 0xfd };
constexpr uint8_t packetData1[] = { to_underlying(BtpEngine::HeaderFlags::kContinueMessage), 0x02, 0xfe };
constexpr uint8_t packetData2[] = { to_underlying(BtpEngine::HeaderFlags::kEndMessage), 0x03, 0xff };
auto packet0 = System::PacketBufferHandle::NewWithData(packetData0, sizeof(packetData0));
EXPECT_EQ(packet0->DataLength(), static_cast<size_t>(5));
SequenceNumber_t receivedAck;
bool didReceiveAck;
EXPECT_EQ(mBtpEngine.HandleCharacteristicReceived(std::move(packet0), receivedAck, didReceiveAck), CHIP_NO_ERROR);
EXPECT_EQ(mBtpEngine.RxState(), BtpEngine::kState_InProgress);
auto packet1 = System::PacketBufferHandle::NewWithData(packetData1, sizeof(packetData1));
EXPECT_EQ(packet1->DataLength(), static_cast<size_t>(3));
EXPECT_EQ(mBtpEngine.HandleCharacteristicReceived(std::move(packet1), receivedAck, didReceiveAck), CHIP_NO_ERROR);
EXPECT_EQ(mBtpEngine.RxState(), BtpEngine::kState_InProgress);
auto packet2 = System::PacketBufferHandle::NewWithData(packetData2, sizeof(packetData2));
EXPECT_EQ(packet2->DataLength(), static_cast<size_t>(3));
EXPECT_EQ(mBtpEngine.HandleCharacteristicReceived(std::move(packet2), receivedAck, didReceiveAck), CHIP_NO_ERROR);
EXPECT_EQ(mBtpEngine.RxState(), BtpEngine::kState_Complete);
}
TEST_F(TestBtpEngine, HandleCharacteristicSendOnePacket)
{
auto packet0 = System::PacketBufferHandle::New(10);
packet0->SetDataLength(1);
auto data0 = packet0->Start();
ASSERT_NE(data0, nullptr);
std::iota(data0, data0 + 1, 0);
EXPECT_TRUE(mBtpEngine.HandleCharacteristicSend(packet0.Retain(), false));
EXPECT_EQ(mBtpEngine.TxState(), BtpEngine::kState_Complete);
EXPECT_EQ(packet0->DataLength(), static_cast<size_t>(5));
}
TEST_F(TestBtpEngine, HandleCharacteristicSendTwoPacket)
{
auto packet0 = System::PacketBufferHandle::New(30);
packet0->SetDataLength(30);
auto data0 = packet0->Start();
ASSERT_NE(data0, nullptr);
std::iota(data0, data0 + 30, 0);
EXPECT_TRUE(mBtpEngine.HandleCharacteristicSend(packet0.Retain(), false));
EXPECT_EQ(mBtpEngine.TxState(), BtpEngine::kState_InProgress);
EXPECT_EQ(packet0->DataLength(), static_cast<size_t>(20));
EXPECT_TRUE(mBtpEngine.HandleCharacteristicSend(nullptr, false));
EXPECT_EQ(mBtpEngine.TxState(), BtpEngine::kState_Complete);
EXPECT_EQ(packet0->DataLength(), static_cast<size_t>(16));
}
// Send 40-byte payload.
// Packet0: 4 byte header + 16 byte payload
// Packet1: 2 byte header + 18 byte payload
// Packet2: 2 byte header + 6 byte payload
TEST_F(TestBtpEngine, HandleCharacteristicSendThreePacket)
{
auto packet0 = System::PacketBufferHandle::New(40);
packet0->SetDataLength(40);
auto data0 = packet0->Start();
ASSERT_NE(data0, nullptr);
std::iota(data0, data0 + 40, 0);
EXPECT_TRUE(mBtpEngine.HandleCharacteristicSend(packet0.Retain(), false));
EXPECT_EQ(mBtpEngine.TxState(), BtpEngine::kState_InProgress);
EXPECT_EQ(packet0->DataLength(), static_cast<size_t>(20));
EXPECT_TRUE(mBtpEngine.HandleCharacteristicSend(nullptr, false));
EXPECT_EQ(mBtpEngine.TxState(), BtpEngine::kState_InProgress);
EXPECT_EQ(packet0->DataLength(), static_cast<size_t>(20));
EXPECT_TRUE(mBtpEngine.HandleCharacteristicSend(nullptr, false));
EXPECT_EQ(mBtpEngine.TxState(), BtpEngine::kState_Complete);
EXPECT_EQ(packet0->DataLength(), static_cast<size_t>(8));
}
// Test sending a 15-byte payload in a single packet with acknowledgment required.
TEST_F(TestBtpEngine, HandleCharacteristicSendOnePacketWithAck)
{
// Create a new packet buffer with space for a 15-byte payload and set its data length to 15.
size_t dataLength = 15;
auto packet0 = System::PacketBufferHandle::New(dataLength);
packet0->SetDataLength(dataLength);
// Send the packet with acknowledgment required, checking the transmission state and the packet buffer.
EXPECT_TRUE(mBtpEngine.HandleCharacteristicSend(packet0.Retain(), true));
EXPECT_EQ(mBtpEngine.TxState(), BtpEngine::kState_Complete);
EXPECT_EQ(packet0->DataLength(), static_cast<size_t>(dataLength + 5)); // protocol header (5 bytes) + payload (15 bytes)
}
// Test sending a 30-byte payload with acknowledgment required, but providing incorrect ack sequence.
TEST_F(TestBtpEngine, HandleCharacteristicSendTwoPacketWithIncorrectAck)
{
// Create a new packet buffer with space for a 30-byte payload and set its data length to 30.
size_t dataLength = 30;
auto packet0 = System::PacketBufferHandle::New(dataLength);
packet0->SetDataLength(dataLength);
// Start sending the payload with acknowledgment required, checking the transmission state and the packet buffer.
auto data0MaxLength = BtpEngine::sDefaultFragmentSize;
EXPECT_TRUE(mBtpEngine.HandleCharacteristicSend(packet0.Retain(), true));
EXPECT_EQ(mBtpEngine.TxState(), BtpEngine::kState_InProgress);
EXPECT_EQ(packet0->DataLength(), static_cast<size_t>(data0MaxLength));
// Try to continue sending the next fragment, also attempting to send an ack
// when the sequence is incorrect and check the transmission state.
EXPECT_FALSE(mBtpEngine.HandleCharacteristicSend(nullptr, true));
EXPECT_EQ(mBtpEngine.TxState(), BtpEngine::kState_InProgress);
// Continue sending without acknowledgment, checking the transmission state and the packet buffer.
EXPECT_TRUE(mBtpEngine.HandleCharacteristicSend(nullptr, false));
EXPECT_EQ(mBtpEngine.TxState(), BtpEngine::kState_Complete);
EXPECT_EQ(packet0->DataLength(),
static_cast<size_t>(dataLength - (data0MaxLength - (kTransferProtocolMaxHeaderSize - kTransferProtocolAckSize)) +
kTransferProtocolMidFragmentMaxHeaderSize));
}
// Test sending a 30-byte payload with correct acknowledgment sequence.
TEST_F(TestBtpEngine, HandleCharacteristicSendTwoPacketWithCorrectAck)
{
// Create a new packet buffer with space for a 30-byte payload and set its data length to 30.
size_t dataLength = 30;
auto packet0 = System::PacketBufferHandle::New(dataLength);
packet0->SetDataLength(dataLength);
// Initialize the payload data with sequential values for testing.
auto data0 = packet0->Start();
ASSERT_NE(data0, nullptr);
std::iota(data0, data0 + dataLength, 0);
// Start sending the payload without requiring an acknowledgment for the first fragment.
auto data0MaxLength = BtpEngine::sDefaultFragmentSize;
EXPECT_TRUE(mBtpEngine.HandleCharacteristicSend(packet0.Retain(), false));
EXPECT_EQ(mBtpEngine.TxState(), BtpEngine::kState_InProgress);
EXPECT_EQ(packet0->DataLength(), static_cast<size_t>(data0MaxLength));
// Continue sending second fragment with acknowledgment required.
EXPECT_TRUE(mBtpEngine.HandleCharacteristicSend(nullptr, true));
EXPECT_EQ(mBtpEngine.TxState(), BtpEngine::kState_Complete);
EXPECT_EQ(packet0->DataLength(),
static_cast<size_t>(dataLength - (data0MaxLength - (kTransferProtocolMaxHeaderSize - kTransferProtocolAckSize)) +
kTransferProtocolMidFragmentMaxHeaderSize));
}
// Test that the engine prevents sending a new payload until the previous payload has been acknowledged.
TEST_F(TestBtpEngine, HandleCharacteristicSendRejectsNewSendUntilPreviousAcked)
{
// Create a new packet buffer with space for a 30-byte payload and set its data length to 30.
size_t dataLength = 30;
auto packet0 = System::PacketBufferHandle::New(dataLength);
packet0->SetDataLength(dataLength);
// Initialize the payload data with sequential values for testing.
auto data0 = packet0->Start();
ASSERT_NE(data0, nullptr);
std::iota(data0, data0 + dataLength, 0);
// Start sending the payload without requiring an acknowledgment for the first fragment.
auto data0MaxLength = BtpEngine::sDefaultFragmentSize;
EXPECT_TRUE(mBtpEngine.HandleCharacteristicSend(packet0.Retain(), false));
EXPECT_EQ(mBtpEngine.TxState(), BtpEngine::kState_InProgress);
EXPECT_EQ(packet0->DataLength(), static_cast<size_t>(data0MaxLength));
// Attempt to send another payload while the first one is still in progress.
auto packet1 = System::PacketBufferHandle::New(dataLength);
packet1->SetDataLength(dataLength);
EXPECT_FALSE(mBtpEngine.HandleCharacteristicSend(packet1.Retain(), false));
// Send the second fragment of the first payload with acknowledgment required.
EXPECT_TRUE(mBtpEngine.HandleCharacteristicSend(nullptr, true));
EXPECT_EQ(mBtpEngine.TxState(), BtpEngine::kState_Complete);
EXPECT_EQ(packet0->DataLength(),
static_cast<size_t>(dataLength - (data0MaxLength - (kTransferProtocolMaxHeaderSize - kTransferProtocolAckSize)) +
kTransferProtocolMidFragmentMaxHeaderSize));
}
// Test sending a payload when there is not enough headroom in the packet buffer.
TEST_F(TestBtpEngine, HandleCharacteristicSendInsufficientHeadroom)
{
// Create a new packet buffer with maximum size and set its data length to the maximum.
auto packet0 = PacketBufferHandle::New(chip::System::PacketBuffer::kMaxSize, 0);
packet0->SetDataLength(packet0->MaxDataLength());
// Attempt to send the packet with acknowledgment required, which should fail due to insufficient headroom.
EXPECT_FALSE(mBtpEngine.HandleCharacteristicSend(packet0.Retain(), true));
EXPECT_EQ(mBtpEngine.TxState(), BtpEngine::kState_Error);
EXPECT_EQ(packet0->DataLength(), packet0->MaxDataLength());
// Create a new packet buffer with space for a 15-byte payload and zero headroom, then set its data length to 15.
size_t dataLength = 15;
auto packet1 = System::PacketBufferHandle::New(dataLength, 0);
packet1->SetDataLength(dataLength);
// Attempt to send another packet with insufficient headroom, which should also fail.
EXPECT_FALSE(mBtpEngine.HandleCharacteristicSend(packet1.Retain(), true));
EXPECT_EQ(mBtpEngine.TxState(), BtpEngine::kState_Error);
EXPECT_EQ(packet1->DataLength(), packet1->MaxDataLength());
}
// Test that the BTP engine correctly encodes a standalone ACK packet when there is unacked data.
TEST_F(TestBtpEngine, EncodeStandAloneAckOnePacket)
{
// Create a packet for receiving a single message.
constexpr uint8_t packetData0[] = {
to_underlying(BtpEngine::HeaderFlags::kStartMessage) | to_underlying(BtpEngine::HeaderFlags::kEndMessage), // Header flags
0x01, // Sequence number
0x01, // Payload length, Least Significant Byte
0x00, // Payload length, Most Significant Byte
0xff, // Payload
};
// Create a packet buffer with the data for the message and check that it is created successfully.
auto packet0 = System::PacketBufferHandle::NewWithData(packetData0, sizeof(packetData0));
EXPECT_FALSE(packet0.IsNull());
// Handle the received packet and check the state of the BTP engine.
SequenceNumber_t receivedAck;
bool didReceiveAck;
EXPECT_EQ(mBtpEngine.HandleCharacteristicReceived(std::move(packet0), receivedAck, didReceiveAck), CHIP_NO_ERROR);
EXPECT_EQ(mBtpEngine.RxState(), BtpEngine::kState_Complete);
EXPECT_TRUE(mBtpEngine.HasUnackedData());
// Create a new packet buffer for the standalone ACK and check that it is created successfully.
auto ackPacket = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize);
EXPECT_FALSE(ackPacket.IsNull());
// Encode the standalone ACK and check that it is successful and the packet length is correct.
EXPECT_EQ(mBtpEngine.EncodeStandAloneAck(ackPacket), CHIP_NO_ERROR);
EXPECT_EQ(ackPacket->DataLength(), kTransferProtocolStandaloneAckHeaderSize);
// Check that the ACK packet has the correct header flags and sequence number.
EXPECT_EQ(ackPacket->Start()[0], to_underlying(BtpEngine::HeaderFlags::kFragmentAck));
EXPECT_EQ(ackPacket->Start()[1], 0x01); // Ack number for the received packet
EXPECT_EQ(ackPacket->Start()[2], 0x00); // Sequence number of the Ack packet itself
}
// Test that the BTP engine correctly encodes a standalone ACK packet when there is no unacked data.
TEST_F(TestBtpEngine, EncodeStandAloneAckNoUnackedData)
{
// Create a new packet buffer for the standalone ACK and check that it is created successfully.
auto ackPacket = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize);
EXPECT_FALSE(ackPacket.IsNull());
// Encode the standalone ACK and check that it is successful and the packet length is correct.
EXPECT_EQ(mBtpEngine.EncodeStandAloneAck(ackPacket), CHIP_NO_ERROR);
EXPECT_EQ(ackPacket->DataLength(), kTransferProtocolStandaloneAckHeaderSize);
// Check that the ACK packet has the correct header flags and sequence number.
EXPECT_EQ(ackPacket->Start()[0], to_underlying(BtpEngine::HeaderFlags::kFragmentAck));
EXPECT_EQ(ackPacket->Start()[1], 0x00); // Ack number (no previous packets to acknowledge)
EXPECT_EQ(ackPacket->Start()[2], 0x00); // Sequence number of the Ack packet itself
}
// Test EncodeStandAloneAck when the packet buffer is too small to hold the header.
TEST_F(TestBtpEngine, EncodeStandAloneAckInsufficientBuffer)
{
// Create a new packet buffer with size smaller than the header size.
auto ackPacket0 = System::PacketBufferHandle::New(kTransferProtocolStandaloneAckHeaderSize - 1);
EXPECT_FALSE(ackPacket0.IsNull());
// Attempt to encode the standalone ACK and check that it fails.
EXPECT_EQ(mBtpEngine.EncodeStandAloneAck(ackPacket0), CHIP_ERROR_NO_MEMORY);
EXPECT_EQ(ackPacket0->DataLength(), static_cast<size_t>(0));
}
// Test encoding a standalone ACK packet after receiving a complete multi-fragment message.
TEST_F(TestBtpEngine, EncodeStandAloneAckMultiFragmentMessage)
{
// Create packet buffers for a multi-fragment message.
constexpr uint8_t packetData0[] = {
to_underlying(BtpEngine::HeaderFlags::kStartMessage), // Header flags
0x01, // Sequence number
0x03, // Payload length, Least Significant Byte
0x00, // Payload length, Most Significant Byte
0xfd, // Payload
};
constexpr uint8_t packetData1[] = {
to_underlying(BtpEngine::HeaderFlags::kContinueMessage), // Header flags
0x02, // Sequence number
0xfe, // Payload
};
constexpr uint8_t packetData2[] = {
to_underlying(BtpEngine::HeaderFlags::kEndMessage), // Header flags
0x03, // Sequence number
0xff, // Payload
};
// Create the first packet and check its data length.
auto packet0 = System::PacketBufferHandle::NewWithData(packetData0, sizeof(packetData0));
EXPECT_EQ(packet0->DataLength(), static_cast<size_t>(sizeof(packetData0)));
// Receive the first packet and check the state of the BTP engine.
SequenceNumber_t receivedAck;
bool didReceiveAck;
EXPECT_EQ(mBtpEngine.HandleCharacteristicReceived(std::move(packet0), receivedAck, didReceiveAck), CHIP_NO_ERROR);
EXPECT_EQ(mBtpEngine.RxState(), BtpEngine::kState_InProgress);
// Create and receive the second packet, check the state of the BTP engine.
auto packet1 = System::PacketBufferHandle::NewWithData(packetData1, sizeof(packetData1));
EXPECT_EQ(packet1->DataLength(), static_cast<size_t>(sizeof(packetData1)));
EXPECT_EQ(mBtpEngine.HandleCharacteristicReceived(std::move(packet1), receivedAck, didReceiveAck), CHIP_NO_ERROR);
EXPECT_EQ(mBtpEngine.RxState(), BtpEngine::kState_InProgress);
// Create and receive the third packet, check the state of the BTP engine.
auto packet2 = System::PacketBufferHandle::NewWithData(packetData2, sizeof(packetData2));
EXPECT_EQ(packet2->DataLength(), static_cast<size_t>(sizeof(packetData2)));
EXPECT_EQ(mBtpEngine.HandleCharacteristicReceived(std::move(packet2), receivedAck, didReceiveAck), CHIP_NO_ERROR);
EXPECT_EQ(mBtpEngine.RxState(), BtpEngine::kState_Complete);
// Create a new packet buffer for the standalone ACK and check that it is created successfully.
auto ackPacket = System::PacketBufferHandle::New(kTransferProtocolStandaloneAckHeaderSize);
EXPECT_FALSE(ackPacket.IsNull());
// Encode the standalone ACK and check that it is successful and the packet length is correct.
EXPECT_EQ(mBtpEngine.EncodeStandAloneAck(ackPacket), CHIP_NO_ERROR);
EXPECT_EQ(ackPacket->DataLength(), kTransferProtocolStandaloneAckHeaderSize);
// Check that the ACK packet has the correct header flags and sequence number.
EXPECT_EQ(ackPacket->Start()[0], to_underlying(BtpEngine::HeaderFlags::kFragmentAck));
EXPECT_EQ(ackPacket->Start()[1], 0x03); // Ack number for the last received packet
EXPECT_EQ(ackPacket->Start()[2], 0x00); // Sequence number of the Ack packet itself
}
// Test handling a characteristic received with an incorrect sequence number.
TEST_F(TestBtpEngine, HandleCharacteristicReceivedIncorrectSequence)
{
// Create a packet buffer with a single message and check that it is created successfully.
uint8_t packetData0[] = {
to_underlying(BtpEngine::HeaderFlags::kStartMessage) | to_underlying(BtpEngine::HeaderFlags::kEndMessage), // Header flags
0x01, // Sequence number
0x01, // Payload length, Least Significant Byte
0x00, // Payload length, Most Significant Byte
0xff, // Payload
};
auto packet0 = System::PacketBufferHandle::NewWithData(packetData0, sizeof(packetData0));
EXPECT_FALSE(packet0.IsNull());
// Handle the received packet and check the state of the BTP engine.
SequenceNumber_t receivedAck;
bool didReceiveAck;
EXPECT_EQ(mBtpEngine.HandleCharacteristicReceived(std::move(packet0), receivedAck, didReceiveAck), CHIP_NO_ERROR);
EXPECT_EQ(mBtpEngine.RxState(), BtpEngine::kState_Complete);
// Create a second packet to test handling when the previous message hasn't been acknowledged.
uint8_t packetData1[] = {
to_underlying(BtpEngine::HeaderFlags::kStartMessage) | to_underlying(BtpEngine::HeaderFlags::kEndMessage), // Header flags
0x02, // Sequence number
0x01, // Payload length, Least Significant Byte
0x00, // Payload length, Most Significant Byte
0xff, // Payload
};
auto packet1 = System::PacketBufferHandle::NewWithData(packetData1, sizeof(packetData1));
EXPECT_FALSE(packet1.IsNull());
// Handle the second packet - this should fail due to engine state (unacked previous message), not sequence number.
EXPECT_EQ(mBtpEngine.HandleCharacteristicReceived(std::move(packet1), receivedAck, didReceiveAck),
BLE_ERROR_REASSEMBLER_INCORRECT_STATE);
EXPECT_EQ(mBtpEngine.RxState(), BtpEngine::kState_Error);
}
// Test handling a standalone ACK packet that is received unexpectedly.
TEST_F(TestBtpEngine, HandleCharacteristicReceivedUnexpectedStandaloneAck)
{
// Create an ACK packet for a packet that has not been sent yet and check that it is created successfully.
constexpr uint8_t ackPacketData0[] = {
to_underlying(BtpEngine::HeaderFlags::kFragmentAck), // Header flags for ACK
0x00, // Ack number for the received packet
0x01, // Sequence number of the Ack packet itself
};
auto ackPacket0 = System::PacketBufferHandle::NewWithData(ackPacketData0, sizeof(ackPacketData0));
EXPECT_FALSE(ackPacket0.IsNull());
// Handle the received ACK packet and check state of the BTP engine.
SequenceNumber_t receivedAck;
bool didReceiveAck;
EXPECT_EQ(mBtpEngine.HandleCharacteristicReceived(std::move(ackPacket0), receivedAck, didReceiveAck), BLE_ERROR_INVALID_ACK);
EXPECT_TRUE(didReceiveAck);
EXPECT_EQ(receivedAck, 0x00);
EXPECT_EQ(mBtpEngine.RxState(), BtpEngine::kState_Error);
}
// Test handling acknowledgment received with an incorrect sequence number.
TEST_F(TestBtpEngine, HandleAckReceivedIncorrectSequence)
{
// Create a packet buffer with a 30-byte payload and set its data length to 30.
size_t dataLength = 30;
auto packet0 = System::PacketBufferHandle::New(dataLength);
packet0->SetDataLength(dataLength);
// Start sending the packet, checking the transmission state.
EXPECT_TRUE(mBtpEngine.HandleCharacteristicSend(packet0.Retain(), false));
EXPECT_EQ(mBtpEngine.TxState(), BtpEngine::kState_InProgress);
// Continue sending the next fragment, checking the transmission state.
EXPECT_TRUE(mBtpEngine.HandleCharacteristicSend(nullptr, false));
EXPECT_EQ(mBtpEngine.TxState(), BtpEngine::kState_Complete);
// Create an ACK packet with an incorrect ACK sequence number and check that it is created successfully.
constexpr uint8_t ackPacketData0[] = {
to_underlying(BtpEngine::HeaderFlags::kFragmentAck), // Header flags for ACK
0x00, // Ack number (correct)
0x02, // Sequence number of the ACK packet itself (incorrect - should be 0x01)
};
auto ackPacket0 = System::PacketBufferHandle::NewWithData(ackPacketData0, sizeof(ackPacketData0));
EXPECT_FALSE(ackPacket0.IsNull());
// Handle the received ACK packet with an incorrect ACK sequence number and check the error code and state of the BTP engine.
SequenceNumber_t receivedAck;
bool didReceiveAck;
EXPECT_EQ(mBtpEngine.HandleCharacteristicReceived(std::move(ackPacket0), receivedAck, didReceiveAck),
BLE_ERROR_INVALID_BTP_SEQUENCE_NUMBER);
EXPECT_TRUE(didReceiveAck);
EXPECT_EQ(receivedAck, 0x00);
EXPECT_EQ(mBtpEngine.ExpectingAck(), 1);
}
// Test handling a packet that contains more data than the declared payload length (simulating BLE packet padding).
TEST_F(TestBtpEngine, HandleCharacteristicReceivedWithPadding)
{
// Create a packet buffer with a single message and padding bytes.
// The payload length in the header is 1 byte, but the actual packet has padding.
constexpr uint8_t packetData0[] = {
to_underlying(BtpEngine::HeaderFlags::kStartMessage) | to_underlying(BtpEngine::HeaderFlags::kEndMessage), // Header flags
0x01, // Sequence number
0x01, // Payload length, Least Significant Byte (only 1 byte of actual payload)
0x00, // Payload length, Most Significant Byte
0xff, // Payload
0x00, // Padding bytes (should be ignored by BTP engine)
0x00,
0x00,
0x00,
0x00,
};
// Create a packet with the full data including padding.
auto packet0 = System::PacketBufferHandle::NewWithData(packetData0, sizeof(packetData0));
// Handle the received packet and check the state of the BTP engine.
SequenceNumber_t receivedAck;
bool didReceiveAck;
EXPECT_EQ(mBtpEngine.HandleCharacteristicReceived(std::move(packet0), receivedAck, didReceiveAck), CHIP_NO_ERROR);
EXPECT_EQ(mBtpEngine.RxState(), BtpEngine::kState_Complete);
// Check that the received packet has been processed correctly, ignoring the padding.
EXPECT_EQ(mBtpEngine.TakeRxPacket()->DataLength(), static_cast<size_t>(1));
}
// Test that the BTP engine correctly handles an ACK packet with a sequence wraparound.
TEST_F(TestBtpEngine, IsValidAckOnSequenceWraparound)
{
const uint8_t invalidAckValue = 95;
// Create a packet buffer with a large payload that will result in 257 fragments (to test sequence number wraparound).
size_t packetLength = mBtpEngine.sDefaultFragmentSize - kTransferProtocolMaxHeaderSize + kTransferProtocolAckSize +
256 * (mBtpEngine.sDefaultFragmentSize - kTransferProtocolMidFragmentMaxHeaderSize + kTransferProtocolAckSize);
auto packet0 = System::PacketBufferHandle::New(packetLength);
packet0->SetDataLength(packetLength);
// Send the first packet to start transmission.
EXPECT_TRUE(mBtpEngine.HandleCharacteristicSend(packet0.Retain(), false));
int count = 0;
while (count < 256)
{
// Continue sending fragments until we reach the expected number.
EXPECT_TRUE(mBtpEngine.HandleCharacteristicSend(nullptr, false));
count++;
// Every 10 packets, simulate receiving an ACK to test ACK validation during transmission.
if (count % 10 == 0)
{
// Create an ACK packet with proper sequence number that BTP engine expects.
uint8_t ackData0[] = {
to_underlying(BtpEngine::HeaderFlags::kFragmentAck), // Header flags for ACK
static_cast<uint8_t>(count % 256), // Acknowledgment number for the received packet
static_cast<uint8_t>(count / 10), // Sequence number of the ACK packet itself
};
auto ackPacket0 = System::PacketBufferHandle::NewWithData(ackData0, sizeof(ackData0));
// Handle the ACK packet - this should succeed in normal flow.
SequenceNumber_t receivedAck;
bool didReceiveAck;
EXPECT_EQ(mBtpEngine.HandleCharacteristicReceived(std::move(ackPacket0), receivedAck, didReceiveAck), CHIP_NO_ERROR);
EXPECT_TRUE(didReceiveAck);
EXPECT_EQ(receivedAck, count % 256);
}
}
// After sending all fragments, transmission should be complete.
EXPECT_EQ(mBtpEngine.TxState(), BtpEngine::kState_Complete);
// Create an ACK packet with an invalid ACK number and check that it is created successfully.
uint8_t ackPacketData0[] = {
to_underlying(BtpEngine::HeaderFlags::kFragmentAck),
invalidAckValue, // Invalid ACK number
static_cast<uint8_t>(count / 10 + 1), // Sequence number of the ACK packet itself
};
auto ackPacket0 = System::PacketBufferHandle::NewWithData(ackPacketData0, sizeof(ackPacketData0));
EXPECT_FALSE(ackPacket0.IsNull());
// Handle the received ACK packet with an invalid ACK number and check the error code and state of the BTP engine.
SequenceNumber_t receivedAck;
bool didReceiveAck;
EXPECT_EQ(mBtpEngine.HandleCharacteristicReceived(std::move(ackPacket0), receivedAck, didReceiveAck), BLE_ERROR_INVALID_ACK);
EXPECT_EQ(mBtpEngine.RxState(), BtpEngine::kState_Error);
EXPECT_TRUE(didReceiveAck);
EXPECT_EQ(receivedAck, invalidAckValue);
}
// Test that the initial sequence numbers are set correctly when the BtpEngine is initialized.
TEST_F(TestBtpEngine, InitialSequenceNumbers)
{
// inline function completely omitted from the LCOV report, no function symbols are generated.
EXPECT_EQ(mBtpEngine.GetLastReceivedSequenceNumber(), 0);
// inline function appears in the LCOV report as uncovered (red), even though is actually covered/executed in the test cases,
// function symbols are generated.
EXPECT_EQ(mBtpEngine.GetNewestUnackedSentSequenceNumber(), 0);
}
// Test that sending a packet updates the unacknowledged sequence number correctly,
// and that receiving an acknowledgment updates the state as expected.
TEST_F(TestBtpEngine, NewestUnackedSentSequenceNumberSend)
{
// Create a 1-byte packet and fill it with data.
auto packet0 = System::PacketBufferHandle::New(10);
ASSERT_FALSE(packet0.IsNull());
packet0->SetDataLength(1);
auto * data0 = packet0->Start();
ASSERT_NE(data0, nullptr);
data0[0] = 0;
// Send the packet and check that the newest unacked sent sequence number is 0.
EXPECT_TRUE(mBtpEngine.HandleCharacteristicSend(packet0.Retain(), false));
EXPECT_EQ(mBtpEngine.GetNewestUnackedSentSequenceNumber(), 0);
// Confirm that there is unacknowledged data.
EXPECT_TRUE(mBtpEngine.ExpectingAck());
// Prepare an acknowledgment packet for sequence number 0.
uint8_t ackData[] = {
to_underlying(BtpEngine::HeaderFlags::kFragmentAck),
0x00,
0x01,
};
auto ackPacket = System::PacketBufferHandle::NewWithData(ackData, sizeof(ackData));
SequenceNumber_t seqNum;
bool didRecieveAck;
// Handle the acknowledgment and verify the result.
EXPECT_EQ(mBtpEngine.HandleCharacteristicReceived(std::move(ackPacket), seqNum, didRecieveAck), CHIP_NO_ERROR);
EXPECT_TRUE(didRecieveAck);
EXPECT_EQ(seqNum, 0);
// After acknowledgment, we are no longer expecting an ack
EXPECT_FALSE(mBtpEngine.ExpectingAck());
// After acknowledgment, the newest unacked sent sequence number should still be 0 (no new sends).
EXPECT_EQ(mBtpEngine.GetNewestUnackedSentSequenceNumber(), 0);
}
} // namespace