| // Copyright 2021 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_hci/uart_transport.h" |
| |
| #include "gtest/gtest.h" |
| #include "pw_bluetooth_hci/packet.h" |
| #include "pw_bytes/byte_builder.h" |
| #include "pw_status/status.h" |
| |
| namespace pw::bluetooth_hci { |
| namespace { |
| |
| class UartTransportTest : public ::testing::Test { |
| protected: |
| constexpr static std::byte kInvalidPacketIndicator = std::byte{0x0}; |
| static_assert(kInvalidPacketIndicator != kUartCommandPacketIndicator); |
| static_assert(kInvalidPacketIndicator != kUartAsyncDataPacketIndicator); |
| static_assert(kInvalidPacketIndicator != kUartSyncDataPacketIndicator); |
| static_assert(kInvalidPacketIndicator != kUartEventPacketIndicator); |
| |
| constexpr static size_t kUartBufferSizeBytes = 256; |
| ByteBuffer<kUartBufferSizeBytes> uart_buffer_; |
| }; |
| |
| TEST_F(UartTransportTest, EmptyBuffer) { |
| const StatusWithSize status_with_size = |
| DecodeHciUartData(ConstByteSpan(), [](const Packet&) { FAIL(); }); |
| EXPECT_EQ(status_with_size.status(), OkStatus()); |
| EXPECT_EQ(status_with_size.size(), 0u); |
| } |
| |
| TEST_F(UartTransportTest, InvalidPacketIndicator) { |
| uart_buffer_.push_back(kInvalidPacketIndicator); |
| ASSERT_EQ(uart_buffer_.status(), OkStatus()); |
| |
| const StatusWithSize status_with_size = |
| DecodeHciUartData(uart_buffer_, [](const Packet&) { FAIL(); }); |
| EXPECT_EQ(status_with_size.status(), Status::DataLoss()); |
| EXPECT_EQ(status_with_size.size(), 1u); |
| } |
| |
| TEST_F(UartTransportTest, CommandPacketIndicatorOnly) { |
| uart_buffer_.push_back(kUartCommandPacketIndicator); |
| ASSERT_EQ(uart_buffer_.status(), OkStatus()); |
| |
| const StatusWithSize status_with_size = |
| DecodeHciUartData(uart_buffer_, [](const Packet&) { FAIL(); }); |
| EXPECT_EQ(status_with_size.status(), OkStatus()); |
| EXPECT_EQ(status_with_size.size(), 0u); |
| } |
| |
| TEST_F(UartTransportTest, CommandPacketPartialPacket) { |
| uart_buffer_.push_back(kUartCommandPacketIndicator); |
| |
| std::array<std::byte, CommandPacket::kHeaderSizeBytes> packet_buffer; |
| CommandPacket packet(0u, ConstByteSpan()); |
| const Result<ConstByteSpan> result = packet.Encode(packet_buffer); |
| ASSERT_EQ(result.status(), OkStatus()); |
| |
| uart_buffer_.append(result.value().first(result.value().size_bytes() - 1)); |
| ASSERT_EQ(uart_buffer_.status(), OkStatus()); |
| |
| const StatusWithSize status_with_size = |
| DecodeHciUartData(uart_buffer_, [](const Packet&) { FAIL(); }); |
| EXPECT_EQ(status_with_size.status(), OkStatus()); |
| EXPECT_EQ(status_with_size.size(), 0u); |
| } |
| |
| TEST_F(UartTransportTest, CommandPacket) { |
| uart_buffer_.push_back(kUartCommandPacketIndicator); |
| |
| std::array<std::byte, CommandPacket::kHeaderSizeBytes> packet_buffer; |
| CommandPacket packet(0u, ConstByteSpan()); |
| const Result<ConstByteSpan> result = packet.Encode(packet_buffer); |
| ASSERT_EQ(result.status(), OkStatus()); |
| |
| uart_buffer_.append(result.value()); |
| ASSERT_EQ(uart_buffer_.status(), OkStatus()); |
| |
| size_t command_packet_count = 0; |
| const StatusWithSize status_with_size = |
| DecodeHciUartData(uart_buffer_, [&](const Packet& decoded_packet) { |
| ASSERT_EQ(decoded_packet.type(), Packet::Type::kCommandPacket); |
| ++command_packet_count; |
| }); |
| EXPECT_EQ(status_with_size.status(), OkStatus()); |
| EXPECT_EQ(status_with_size.size(), uart_buffer_.size()); |
| EXPECT_EQ(command_packet_count, 1u); |
| } |
| |
| TEST_F(UartTransportTest, AsyncDataPacketIndicatorOnly) { |
| uart_buffer_.push_back(kUartAsyncDataPacketIndicator); |
| ASSERT_EQ(uart_buffer_.status(), OkStatus()); |
| |
| const StatusWithSize status_with_size = |
| DecodeHciUartData(uart_buffer_, [](const Packet&) { FAIL(); }); |
| EXPECT_EQ(status_with_size.status(), OkStatus()); |
| EXPECT_EQ(status_with_size.size(), 0u); |
| } |
| |
| TEST_F(UartTransportTest, AsyncDataPacketPartialPacket) { |
| uart_buffer_.push_back(kUartAsyncDataPacketIndicator); |
| |
| std::array<std::byte, AsyncDataPacket::kHeaderSizeBytes> packet_buffer; |
| AsyncDataPacket packet(0u, ConstByteSpan()); |
| const Result<ConstByteSpan> result = packet.Encode(packet_buffer); |
| ASSERT_EQ(result.status(), OkStatus()); |
| |
| uart_buffer_.append(result.value().first(result.value().size_bytes() - 1)); |
| ASSERT_EQ(uart_buffer_.status(), OkStatus()); |
| |
| const StatusWithSize status_with_size = |
| DecodeHciUartData(uart_buffer_, [](const Packet&) { FAIL(); }); |
| EXPECT_EQ(status_with_size.status(), OkStatus()); |
| EXPECT_EQ(status_with_size.size(), 0u); |
| } |
| |
| TEST_F(UartTransportTest, AsyncDataPacket) { |
| uart_buffer_.push_back(kUartAsyncDataPacketIndicator); |
| |
| std::array<std::byte, AsyncDataPacket::kHeaderSizeBytes> packet_buffer; |
| AsyncDataPacket packet(0u, ConstByteSpan()); |
| const Result<ConstByteSpan> result = packet.Encode(packet_buffer); |
| ASSERT_EQ(result.status(), OkStatus()); |
| |
| uart_buffer_.append(result.value()); |
| ASSERT_EQ(uart_buffer_.status(), OkStatus()); |
| |
| size_t async_data_packet_count = 0; |
| const StatusWithSize status_with_size = |
| DecodeHciUartData(uart_buffer_, [&](const Packet& decoded_packet) { |
| ASSERT_EQ(decoded_packet.type(), Packet::Type::kAsyncDataPacket); |
| ++async_data_packet_count; |
| }); |
| EXPECT_EQ(status_with_size.status(), OkStatus()); |
| EXPECT_EQ(status_with_size.size(), uart_buffer_.size()); |
| EXPECT_EQ(async_data_packet_count, 1u); |
| } |
| |
| TEST_F(UartTransportTest, SyncDataPacketIndicatorOnly) { |
| uart_buffer_.push_back(kUartSyncDataPacketIndicator); |
| ASSERT_EQ(uart_buffer_.status(), OkStatus()); |
| |
| const StatusWithSize status_with_size = |
| DecodeHciUartData(uart_buffer_, [](const Packet&) { FAIL(); }); |
| EXPECT_EQ(status_with_size.status(), OkStatus()); |
| EXPECT_EQ(status_with_size.size(), 0u); |
| } |
| |
| TEST_F(UartTransportTest, SyncDataPacketPartialPacket) { |
| uart_buffer_.push_back(kUartSyncDataPacketIndicator); |
| |
| std::array<std::byte, SyncDataPacket::kHeaderSizeBytes> packet_buffer; |
| SyncDataPacket packet(0u, ConstByteSpan()); |
| const Result<ConstByteSpan> result = packet.Encode(packet_buffer); |
| ASSERT_EQ(result.status(), OkStatus()); |
| |
| uart_buffer_.append(result.value().first(result.value().size_bytes() - 1)); |
| ASSERT_EQ(uart_buffer_.status(), OkStatus()); |
| |
| const StatusWithSize status_with_size = |
| DecodeHciUartData(uart_buffer_, [](const Packet&) { FAIL(); }); |
| EXPECT_EQ(status_with_size.status(), OkStatus()); |
| EXPECT_EQ(status_with_size.size(), 0u); |
| } |
| |
| TEST_F(UartTransportTest, SyncDataPacket) { |
| uart_buffer_.push_back(kUartSyncDataPacketIndicator); |
| |
| std::array<std::byte, SyncDataPacket::kHeaderSizeBytes> packet_buffer; |
| SyncDataPacket packet(0u, ConstByteSpan()); |
| const Result<ConstByteSpan> result = packet.Encode(packet_buffer); |
| ASSERT_EQ(result.status(), OkStatus()); |
| |
| uart_buffer_.append(result.value()); |
| ASSERT_EQ(uart_buffer_.status(), OkStatus()); |
| |
| size_t sync_data_packet_count = 0; |
| const StatusWithSize status_with_size = |
| DecodeHciUartData(uart_buffer_, [&](const Packet& decoded_packet) { |
| ASSERT_EQ(decoded_packet.type(), Packet::Type::kSyncDataPacket); |
| ++sync_data_packet_count; |
| }); |
| EXPECT_EQ(status_with_size.status(), OkStatus()); |
| EXPECT_EQ(status_with_size.size(), uart_buffer_.size()); |
| EXPECT_EQ(sync_data_packet_count, 1u); |
| } |
| |
| TEST_F(UartTransportTest, EventPacketIndicatorOnly) { |
| uart_buffer_.push_back(kUartEventPacketIndicator); |
| ASSERT_EQ(uart_buffer_.status(), OkStatus()); |
| |
| const StatusWithSize status_with_size = |
| DecodeHciUartData(uart_buffer_, [](const Packet&) { FAIL(); }); |
| EXPECT_EQ(status_with_size.status(), OkStatus()); |
| EXPECT_EQ(status_with_size.size(), 0u); |
| } |
| |
| TEST_F(UartTransportTest, EventPacketPartialPacket) { |
| uart_buffer_.push_back(kUartEventPacketIndicator); |
| |
| std::array<std::byte, EventPacket::kHeaderSizeBytes> packet_buffer; |
| EventPacket packet(0u, ConstByteSpan()); |
| const Result<ConstByteSpan> result = packet.Encode(packet_buffer); |
| ASSERT_EQ(result.status(), OkStatus()); |
| |
| uart_buffer_.append(result.value().first(result.value().size_bytes() - 1)); |
| ASSERT_EQ(uart_buffer_.status(), OkStatus()); |
| |
| const StatusWithSize status_with_size = |
| DecodeHciUartData(uart_buffer_, [](const Packet&) { FAIL(); }); |
| EXPECT_EQ(status_with_size.status(), OkStatus()); |
| EXPECT_EQ(status_with_size.size(), 0u); |
| } |
| |
| TEST_F(UartTransportTest, EventPacket) { |
| uart_buffer_.push_back(kUartEventPacketIndicator); |
| |
| std::array<std::byte, EventPacket::kHeaderSizeBytes> packet_buffer; |
| EventPacket packet(0u, ConstByteSpan()); |
| const Result<ConstByteSpan> result = packet.Encode(packet_buffer); |
| ASSERT_EQ(result.status(), OkStatus()); |
| |
| uart_buffer_.append(result.value()); |
| ASSERT_EQ(uart_buffer_.status(), OkStatus()); |
| |
| size_t event_packet_count = 0; |
| const StatusWithSize status_with_size = |
| DecodeHciUartData(uart_buffer_, [&](const Packet& decoded_packet) { |
| ASSERT_EQ(decoded_packet.type(), Packet::Type::kEventPacket); |
| ++event_packet_count; |
| }); |
| EXPECT_EQ(status_with_size.status(), OkStatus()); |
| EXPECT_EQ(status_with_size.size(), uart_buffer_.size()); |
| EXPECT_EQ(event_packet_count, 1u); |
| } |
| |
| TEST_F(UartTransportTest, BadIndicatorThenPacketSequence) { |
| // Invalid packet indicator. |
| uart_buffer_.push_back(kInvalidPacketIndicator); |
| |
| // Valid EventPacket w/ packet indicator. |
| uart_buffer_.push_back(kUartEventPacketIndicator); |
| std::array<std::byte, EventPacket::kHeaderSizeBytes> packet_buffer; |
| EventPacket packet(0u, ConstByteSpan()); |
| const Result<ConstByteSpan> result = packet.Encode(packet_buffer); |
| ASSERT_EQ(result.status(), OkStatus()); |
| uart_buffer_.append(result.value()); |
| ASSERT_EQ(uart_buffer_.status(), OkStatus()); |
| |
| // First decode should fail after consuming the invalid packet indicator. |
| StatusWithSize status_with_size = |
| DecodeHciUartData(uart_buffer_, [&](const Packet&) { FAIL(); }); |
| EXPECT_EQ(status_with_size.status(), Status::DataLoss()); |
| EXPECT_EQ(status_with_size.size(), 1u); |
| |
| const ConstByteSpan remaining_data = |
| ConstByteSpan(uart_buffer_).subspan(status_with_size.size()); |
| |
| // Second decode should work now that the invalid byte is gone. |
| size_t event_packet_count = 0; |
| status_with_size = |
| DecodeHciUartData(remaining_data, [&](const Packet& decoded_packet) { |
| ASSERT_EQ(decoded_packet.type(), Packet::Type::kEventPacket); |
| ++event_packet_count; |
| }); |
| EXPECT_EQ(status_with_size.status(), OkStatus()); |
| EXPECT_EQ(status_with_size.size(), remaining_data.size_bytes()); |
| EXPECT_EQ(event_packet_count, 1u); |
| } |
| |
| TEST_F(UartTransportTest, PacketThenBadIndicatorSequence) { |
| // Valid EventPacket w/ packet indicator. |
| uart_buffer_.push_back(kUartEventPacketIndicator); |
| std::array<std::byte, EventPacket::kHeaderSizeBytes> packet_buffer; |
| EventPacket packet(0u, ConstByteSpan()); |
| const Result<ConstByteSpan> result = packet.Encode(packet_buffer); |
| ASSERT_EQ(result.status(), OkStatus()); |
| uart_buffer_.append(result.value()); |
| ASSERT_EQ(uart_buffer_.status(), OkStatus()); |
| |
| // Invalid packet indicator. |
| uart_buffer_.push_back(kInvalidPacketIndicator); |
| |
| // First decode should fail, consuming all data. |
| size_t event_packet_count = 0; |
| const StatusWithSize status_with_size = |
| DecodeHciUartData(uart_buffer_, [&](const Packet& decoded_packet) { |
| ASSERT_EQ(decoded_packet.type(), Packet::Type::kEventPacket); |
| ++event_packet_count; |
| }); |
| EXPECT_EQ(status_with_size.status(), Status::DataLoss()); |
| EXPECT_EQ(status_with_size.size(), uart_buffer_.size()); |
| EXPECT_EQ(event_packet_count, 1u); |
| } |
| |
| TEST_F(UartTransportTest, AllPacketTypes) { |
| // Valid CommandPacket w/ packet indicator. |
| { |
| uart_buffer_.push_back(kUartCommandPacketIndicator); |
| std::array<std::byte, CommandPacket::kHeaderSizeBytes> packet_buffer; |
| CommandPacket packet(0u, ConstByteSpan()); |
| const Result<ConstByteSpan> result = packet.Encode(packet_buffer); |
| ASSERT_EQ(result.status(), OkStatus()); |
| uart_buffer_.append(result.value()); |
| ASSERT_EQ(uart_buffer_.status(), OkStatus()); |
| } |
| |
| // Valid AsyncDataPacket w/ packet indicator. |
| { |
| uart_buffer_.push_back(kUartAsyncDataPacketIndicator); |
| std::array<std::byte, AsyncDataPacket::kHeaderSizeBytes> packet_buffer; |
| AsyncDataPacket packet(0u, ConstByteSpan()); |
| const Result<ConstByteSpan> result = packet.Encode(packet_buffer); |
| ASSERT_EQ(result.status(), OkStatus()); |
| uart_buffer_.append(result.value()); |
| ASSERT_EQ(uart_buffer_.status(), OkStatus()); |
| } |
| |
| // Valid SyncDataPacket w/ packet indicator. |
| { |
| uart_buffer_.push_back(kUartSyncDataPacketIndicator); |
| std::array<std::byte, SyncDataPacket::kHeaderSizeBytes> packet_buffer; |
| SyncDataPacket packet(0u, ConstByteSpan()); |
| const Result<ConstByteSpan> result = packet.Encode(packet_buffer); |
| ASSERT_EQ(result.status(), OkStatus()); |
| uart_buffer_.append(result.value()); |
| ASSERT_EQ(uart_buffer_.status(), OkStatus()); |
| } |
| |
| // Valid EventPacket w/ packet indicator. |
| { |
| uart_buffer_.push_back(kUartEventPacketIndicator); |
| std::array<std::byte, EventPacket::kHeaderSizeBytes> packet_buffer; |
| EventPacket packet(0u, ConstByteSpan()); |
| const Result<ConstByteSpan> result = packet.Encode(packet_buffer); |
| ASSERT_EQ(result.status(), OkStatus()); |
| uart_buffer_.append(result.value()); |
| ASSERT_EQ(uart_buffer_.status(), OkStatus()); |
| } |
| |
| // First decode should succeed, consuming all data. |
| size_t packet_count = 0u; |
| const StatusWithSize status_with_size = |
| DecodeHciUartData(uart_buffer_, [&](const Packet& decoded_packet) { |
| ++packet_count; |
| switch (packet_count) { |
| case 1u: |
| EXPECT_EQ(decoded_packet.type(), Packet::Type::kCommandPacket); |
| break; |
| |
| case 2u: |
| EXPECT_EQ(decoded_packet.type(), Packet::Type::kAsyncDataPacket); |
| break; |
| |
| case 3u: |
| EXPECT_EQ(decoded_packet.type(), Packet::Type::kSyncDataPacket); |
| break; |
| |
| case 4u: |
| EXPECT_EQ(decoded_packet.type(), Packet::Type::kEventPacket); |
| break; |
| } |
| }); |
| EXPECT_EQ(status_with_size.status(), OkStatus()); |
| EXPECT_EQ(status_with_size.size(), uart_buffer_.size()); |
| EXPECT_EQ(packet_count, 4u); |
| } |
| |
| TEST_F(UartTransportTest, LotsOfEventPackets) { |
| std::array<std::byte, EventPacket::kHeaderSizeBytes> packet_buffer; |
| EventPacket packet(0u, ConstByteSpan()); |
| const Result<ConstByteSpan> result = packet.Encode(packet_buffer); |
| ASSERT_EQ(result.status(), OkStatus()); |
| |
| size_t expected_packet_count = 0; |
| while ((uart_buffer_.max_size() - uart_buffer_.size()) > |
| packet.size_bytes()) { |
| ++expected_packet_count; |
| uart_buffer_.push_back(kUartEventPacketIndicator); |
| uart_buffer_.append(result.value()); |
| ASSERT_EQ(uart_buffer_.status(), OkStatus()); |
| } |
| |
| size_t event_packet_count = 0; |
| const StatusWithSize status_with_size = |
| DecodeHciUartData(uart_buffer_, [&](const Packet& decoded_packet) { |
| ASSERT_EQ(decoded_packet.type(), Packet::Type::kEventPacket); |
| ++event_packet_count; |
| }); |
| EXPECT_EQ(status_with_size.status(), OkStatus()); |
| EXPECT_EQ(status_with_size.size(), uart_buffer_.size()); |
| EXPECT_EQ(event_packet_count, expected_packet_count); |
| } |
| |
| } // namespace |
| } // namespace pw::bluetooth_hci |