blob: ce542aef2fef5150256fa0d96cdea0c21e27659b [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "enhanced_retransmission_mode_rx_engine.h"
#include "gtest/gtest.h"
#include "src/connectivity/bluetooth/core/bt-host/common/test_helpers.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/fragmenter.h"
namespace bt {
namespace l2cap {
namespace internal {
namespace {
constexpr hci::ConnectionHandle kTestHandle = 0x0001;
constexpr ChannelId kTestChannelId = 0x0001;
using Engine = EnhancedRetransmissionModeRxEngine;
void NopTxCallback(ByteBufferPtr) {}
TEST(L2CAP_EnhancedRetransmissionModeRxEngineTest,
ProcessPduImmediatelyReturnsDataForUnsegmentedSdu) {
// See Core Spec, v5, Vol 3, Part A, Table 3.2 for the first two bytes.
const auto payload = CreateStaticByteBuffer(0, 0, 'h', 'e', 'l', 'l', 'o');
const ByteBufferPtr sdu =
Engine(NopTxCallback)
.ProcessPdu(
Fragmenter(kTestHandle).BuildBasicFrame(kTestChannelId, payload));
ASSERT_TRUE(sdu);
EXPECT_TRUE(
ContainersEqual(CreateStaticByteBuffer('h', 'e', 'l', 'l', 'o'), *sdu));
}
TEST(L2CAP_EnhancedRetransmissionModeRxEngineTest,
ProcessPduCanHandleZeroBytePayload) {
// See Core Spec, v5, Vol 3, Part A, Table 3.2 for the first two bytes.
const auto payload = CreateStaticByteBuffer(0, 0);
const ByteBufferPtr sdu =
Engine(NopTxCallback)
.ProcessPdu(
Fragmenter(kTestHandle).BuildBasicFrame(kTestChannelId, payload));
ASSERT_TRUE(sdu);
EXPECT_EQ(0u, sdu->size());
}
TEST(L2CAP_EnhancedRetransmissionModeRxEngineTest,
ProcessPduDoesNotGenerateSduForOutOfSequencePdu) {
// See Core Spec, v5, Vol 3, Part A, Table 3.2 for the first two bytes.
const auto payload = CreateStaticByteBuffer( //
1 << 1, // TxSeq = 1, R=0
0, // SAR and ReqSeq
'h', 'e', 'l', 'l', 'o');
EXPECT_FALSE(Engine(NopTxCallback)
.ProcessPdu(Fragmenter(kTestHandle)
.BuildBasicFrame(kTestChannelId, payload)));
}
TEST(L2CAP_EnhancedRetransmissionModeRxEngineTest,
ProcessPduAdvancesSequenceNumberOnInSequenceFrame) {
Engine rx_engine(NopTxCallback);
// Send with sequence 0.
{
const auto payload = CreateStaticByteBuffer( //
0 << 1, // TxSeq=0, R=0
0, // SAR and ReqSeq
'h', 'e', 'l', 'l', 'o');
ASSERT_TRUE(rx_engine.ProcessPdu(
Fragmenter(kTestHandle).BuildBasicFrame(kTestChannelId, payload)));
}
// Send with sequence 1.
{
const auto payload = CreateStaticByteBuffer( //
1 << 1, // TxSeq=1, R=0
0, // SAR and ReqSeq
'h', 'e', 'l', 'l', 'o');
ASSERT_TRUE(rx_engine.ProcessPdu(
Fragmenter(kTestHandle).BuildBasicFrame(kTestChannelId, payload)));
}
// Send with sequence 2.
{
const auto payload = CreateStaticByteBuffer( //
2 << 1, // TxSeq=2, R=0
0, // SAR and ReqSeq
'h', 'e', 'l', 'l', 'o');
EXPECT_TRUE(rx_engine.ProcessPdu(
Fragmenter(kTestHandle).BuildBasicFrame(kTestChannelId, payload)));
}
}
TEST(L2CAP_EnhancedRetransmissionModeRxEngineTest,
ProcessPduRollsOverSequenceNumber) {
Engine rx_engine(NopTxCallback);
auto payload = CreateStaticByteBuffer( //
0 << 1, // TxSeq=0, R=0
0, // SAR and ReqSeq
'h', 'e', 'l', 'l', 'o');
for (size_t i = 0; i < 64; ++i) {
payload[0] = i << 1; // Set TxSeq
ASSERT_TRUE(rx_engine.ProcessPdu(
Fragmenter(kTestHandle).BuildBasicFrame(kTestChannelId, payload)))
<< " (i=" << i << ")";
}
// Per Core Spec v5, Vol 3, Part A, Sec 8.3, the sequence number should now
// roll over to 0.
payload[0] = 0 << 1;
EXPECT_TRUE(rx_engine.ProcessPdu(
Fragmenter(kTestHandle).BuildBasicFrame(kTestChannelId, payload)));
}
TEST(L2CAP_EnhancedRetransmissionModeRxEngineTest,
ProcessPduDoesNotAdvanceSequenceNumberForOutOfSequencePdu) {
Engine rx_engine(NopTxCallback);
const auto out_of_seq = CreateStaticByteBuffer( //
1 << 1, // TxSeq=1, R=0
0, // SAR and ReqSeq
'h', 'e', 'l', 'l', 'o');
ASSERT_FALSE(rx_engine.ProcessPdu(
Fragmenter(kTestHandle).BuildBasicFrame(kTestChannelId, out_of_seq)));
const auto in_seq = CreateStaticByteBuffer( //
0 << 1, // TxSeq=0, R=0
0, // SAR and ReqSeq
'h', 'e', 'l', 'l', 'o');
EXPECT_TRUE(rx_engine.ProcessPdu(
Fragmenter(kTestHandle).BuildBasicFrame(kTestChannelId, in_seq)));
}
TEST(L2CAP_EnhancedRetransmissionModeRxEngineTest,
ProcessPduImmediatelyAcksUnsegmentedSdu) {
size_t n_acks = 0;
ByteBufferPtr outbound_ack;
auto tx_callback = [&](auto pdu) {
outbound_ack = std::move(pdu);
++n_acks;
};
// See Core Spec, v5, Vol 3, Part A, Table 3.2 for the first two bytes.
const auto payload = CreateStaticByteBuffer(0, 0, 'h', 'e', 'l', 'l', 'o');
ASSERT_TRUE(Engine(tx_callback)
.ProcessPdu(Fragmenter(kTestHandle)
.BuildBasicFrame(kTestChannelId, payload)));
EXPECT_EQ(1u, n_acks);
ASSERT_TRUE(outbound_ack);
ASSERT_EQ(sizeof(SimpleReceiverReadyFrame), outbound_ack->size());
auto ack_frame =
*reinterpret_cast<const SimpleReceiverReadyFrame *>(outbound_ack->data());
EXPECT_EQ(SupervisoryFunction::ReceiverReady, ack_frame.function());
EXPECT_EQ(1u, ack_frame.request_seq_num());
}
TEST(L2CAP_EnhancedRetransmissionModeRxEngineTest,
ProcessPduSendsCorrectReqSeqOnRollover) {
size_t n_acks = 0;
ByteBufferPtr last_ack;
auto tx_callback = [&](auto pdu) {
last_ack = std::move(pdu);
++n_acks;
};
Engine rx_engine(tx_callback);
// See Core Spec, v5, Vol 3, Part A, Table 3.2 for the first two bytes.
for (size_t i = 0; i < 64; ++i) {
const auto payload =
CreateStaticByteBuffer(i << 1, 0, 'h', 'e', 'l', 'l', 'o');
ASSERT_TRUE(rx_engine.ProcessPdu(
Fragmenter(kTestHandle).BuildBasicFrame(kTestChannelId, payload)))
<< " (i=" << i << ")";
}
EXPECT_EQ(64u, n_acks);
ASSERT_TRUE(last_ack);
ASSERT_EQ(sizeof(SimpleReceiverReadyFrame), last_ack->size());
auto ack_frame =
*reinterpret_cast<const SimpleReceiverReadyFrame *>(last_ack->data());
EXPECT_EQ(SupervisoryFunction::ReceiverReady, ack_frame.function());
EXPECT_EQ(0u, ack_frame.request_seq_num());
}
TEST(L2CAP_EnhancedRetransmissionModeRxEngineTest,
ProcessPduDoesNotAckOutOfSequenceFrame) {
size_t n_acks = 0;
ByteBufferPtr outbound_ack;
auto tx_callback = [&](auto pdu) {
outbound_ack = std::move(pdu);
++n_acks;
};
// See Core Spec, v5, Vol 3, Part A, Table 3.2 for the first two bytes.
const auto payload = CreateStaticByteBuffer(1, 0, 'h', 'e', 'l', 'l', 'o');
// Per Core Spec, v5, Vol 3, Part A, Sec 8.4.7.1, receipt of an
// out-of-sequence frame should cause us to transmit a Reject frame. We assume
// that we should _not_ also transmit a ReceiverReady frame.
//
// TODO(BT-448): Revise this test when we start sending Reject frames.
ASSERT_FALSE(Engine(tx_callback)
.ProcessPdu(Fragmenter(kTestHandle)
.BuildBasicFrame(kTestChannelId, payload)));
EXPECT_EQ(0u, n_acks);
}
TEST(L2CAP_EnhancedRetransmissionModeRxEngineTest,
ProcessPduRespondsToReceiverReadyPollRequest) {
size_t n_outbound_frames = 0;
ByteBufferPtr last_outbound_frame;
auto tx_callback = [&](auto pdu) {
last_outbound_frame = std::move(pdu);
++n_outbound_frames;
};
Engine rx_engine(tx_callback);
// Send an I-frame to advance the receiver's sequence number.
// See Core Spec, v5, Vol 3, Part A, Table 3.2 for the first two bytes.
const auto info_frame = CreateStaticByteBuffer(0, 0, 'h', 'e', 'l', 'l', 'o');
rx_engine.ProcessPdu(
Fragmenter(kTestHandle).BuildBasicFrame(kTestChannelId, info_frame));
ASSERT_EQ(1u, n_outbound_frames);
// Now send a ReceiverReady poll request. See Core Spec, v5, Vol 3, Part A,
// Table 3.2 and Table 3.5 for frame format.
const auto receiver_ready_poll_request = CreateStaticByteBuffer(0b1'0001, 0);
auto local_sdu = rx_engine.ProcessPdu(
Fragmenter(kTestHandle)
.BuildBasicFrame(kTestChannelId, receiver_ready_poll_request));
EXPECT_FALSE(local_sdu); // No payload in a ReceiverReady frame.
EXPECT_EQ(2u, n_outbound_frames);
ASSERT_TRUE(last_outbound_frame);
ASSERT_EQ(sizeof(SimpleSupervisoryFrame), last_outbound_frame->size());
auto sframe = *reinterpret_cast<const SimpleSupervisoryFrame *>(
last_outbound_frame->data());
EXPECT_EQ(SupervisoryFunction::ReceiverReady, sframe.function());
EXPECT_EQ(1u, sframe.request_seq_num());
EXPECT_TRUE(sframe.is_poll_response());
EXPECT_FALSE(sframe.is_poll_request());
}
} // namespace
} // namespace internal
} // namespace l2cap
} // namespace bt