blob: cac2454cab91b917b6cbd6f8690c5fe52fdf00d4 [file] [log] [blame]
// Copyright 2019 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_tx_engine.h"
#include "gtest/gtest.h"
#include "lib/gtest/test_loop_fixture.h"
#include "src/connectivity/bluetooth/core/bt-host/common/byte_buffer.h"
#include "src/connectivity/bluetooth/core/bt-host/common/test_helpers.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/frame_headers.h"
namespace bt {
namespace l2cap {
namespace internal {
namespace {
constexpr ChannelId kTestChannelId = 0x0001;
using TxEngine = EnhancedRetransmissionModeTxEngine;
class L2CAP_EnhancedRetransmissionModeTxEngineTest
: public ::gtest::TestLoopFixture {
public:
L2CAP_EnhancedRetransmissionModeTxEngineTest()
: kDefaultPayload('h', 'e', 'l', 'l', 'o') {}
protected:
// The default values are provided for use by tests which don't depend on the
// specific value a given parameter. This should make the tests easier to
// read, because the reader can focus on only the non-defaulted parameter
// values.
static constexpr auto kDefaultMTU = bt::l2cap::kDefaultMTU;
static constexpr size_t kDefaultMaxTransmissions = 1;
static constexpr size_t kDefaultTxWindow = 63;
const StaticByteBuffer<5> kDefaultPayload;
void VerifyIsReceiverReadyPollFrame(ByteBuffer* buf) {
ASSERT_TRUE(buf);
ASSERT_EQ(sizeof(SimpleSupervisoryFrame), buf->size());
const auto sframe = buf->As<SimpleSupervisoryFrame>();
EXPECT_EQ(SupervisoryFunction::ReceiverReady, sframe.function());
EXPECT_TRUE(sframe.is_poll_request());
}
};
void NoOpFailureCallback(){};
TEST_F(L2CAP_EnhancedRetransmissionModeTxEngineTest,
QueueSduTransmitsMinimalSizedSdu) {
ByteBufferPtr last_pdu;
size_t n_pdus = 0;
auto tx_callback = [&](auto pdu) {
++n_pdus;
last_pdu = std::move(pdu);
};
constexpr size_t kMtu = 10;
const auto payload = CreateStaticByteBuffer(1);
TxEngine(kTestChannelId, kMtu, kDefaultMaxTransmissions, kDefaultTxWindow,
tx_callback, NoOpFailureCallback)
.QueueSdu(std::make_unique<DynamicByteBuffer>(payload));
EXPECT_EQ(1u, n_pdus);
ASSERT_TRUE(last_pdu);
// See Core Spec v5.0, Volume 3, Part A, Table 3.2.
const auto expected_pdu =
CreateStaticByteBuffer(0, // Final Bit, TxSeq, MustBeZeroBit
0, // SAR bits, ReqSeq
1); // Payload
EXPECT_TRUE(ContainersEqual(expected_pdu, *last_pdu));
}
TEST_F(L2CAP_EnhancedRetransmissionModeTxEngineTest,
QueueSduTransmitsMaximalSizedSdu) {
ByteBufferPtr last_pdu;
size_t n_pdus = 0;
auto tx_callback = [&](auto pdu) {
++n_pdus;
last_pdu = std::move(pdu);
};
constexpr size_t kMtu = 1;
const auto payload = CreateStaticByteBuffer(1);
TxEngine(kTestChannelId, kMtu, kDefaultMaxTransmissions, kDefaultTxWindow,
tx_callback, NoOpFailureCallback)
.QueueSdu(std::make_unique<DynamicByteBuffer>(payload));
EXPECT_EQ(1u, n_pdus);
ASSERT_TRUE(last_pdu);
// See Core Spec v5.0, Volume 3, Part A, Table 3.2.
const auto expected_pdu =
CreateStaticByteBuffer(0, // Final Bit, TxSeq, MustBeZeroBit
0, // SAR bits, ReqSeq
1); // Payload
EXPECT_TRUE(ContainersEqual(expected_pdu, *last_pdu));
}
TEST_F(L2CAP_EnhancedRetransmissionModeTxEngineTest,
QueueSduSurvivesOversizedSdu) {
// TODO(BT-440): Update this test when we add support for segmentation.
constexpr size_t kMtu = 1;
TxEngine(
kTestChannelId, kMtu, kDefaultMaxTransmissions, kDefaultTxWindow,
[](auto pdu) {}, NoOpFailureCallback)
.QueueSdu(
std::make_unique<DynamicByteBuffer>(CreateStaticByteBuffer(1, 2)));
}
TEST_F(L2CAP_EnhancedRetransmissionModeTxEngineTest,
QueueSduSurvivesZeroByteSdu) {
TxEngine(
kTestChannelId, kDefaultMTU, kDefaultMaxTransmissions, kDefaultTxWindow,
[](auto pdu) {}, NoOpFailureCallback)
.QueueSdu(std::make_unique<DynamicByteBuffer>());
}
TEST_F(L2CAP_EnhancedRetransmissionModeTxEngineTest,
QueueSduAdvancesSequenceNumber) {
const auto payload = CreateStaticByteBuffer(1);
ByteBufferPtr last_pdu;
auto tx_callback = [&](auto pdu) { last_pdu = std::move(pdu); };
TxEngine tx_engine(kTestChannelId, kDefaultMTU, kDefaultMaxTransmissions,
kDefaultTxWindow, tx_callback, NoOpFailureCallback);
{
// See Core Spec v5.0, Volume 3, Part A, Table 3.2.
const auto expected_pdu =
CreateStaticByteBuffer(0, // Final Bit, TxSeq, MustBeZeroBit
0, // SAR bits, ReqSeq
1); // Payload
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(payload));
ASSERT_TRUE(last_pdu);
EXPECT_TRUE(ContainersEqual(expected_pdu, *last_pdu));
}
{
// See Core Spec v5.0, Volume 3, Part A, Table 3.2.
const auto expected_pdu =
CreateStaticByteBuffer(1 << 1, // Final Bit, TxSeq=1, MustBeZeroBit
0, // SAR bits, ReqSeq
1); // Payload
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(payload));
ASSERT_TRUE(last_pdu);
EXPECT_TRUE(ContainersEqual(expected_pdu, *last_pdu));
}
{
// See Core Spec v5.0, Volume 3, Part A, Table 3.2.
const auto expected_pdu =
CreateStaticByteBuffer(2 << 1, // Final Bit, TxSeq=2, MustBeZeroBit
0, // SAR bits, ReqSeq
1); // Payload
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(payload));
ASSERT_TRUE(last_pdu);
EXPECT_TRUE(ContainersEqual(expected_pdu, *last_pdu));
}
}
TEST_F(L2CAP_EnhancedRetransmissionModeTxEngineTest,
QueueSduRollsOverSequenceNumber) {
const auto payload = CreateStaticByteBuffer(1);
ByteBufferPtr last_pdu;
auto tx_callback = [&](auto pdu) { last_pdu = std::move(pdu); };
TxEngine tx_engine(kTestChannelId, kDefaultMTU, kDefaultMaxTransmissions,
kDefaultTxWindow, tx_callback, NoOpFailureCallback);
constexpr size_t kMaxSeq = 64;
for (size_t i = 0; i < kMaxSeq; ++i) {
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(payload));
}
// See Core Spec v5.0, Volume 3, Part A, Table 3.2.
const auto expected_pdu = CreateStaticByteBuffer(
0, // Final Bit, TxSeq (rolls over from 63 to 0), MustBeZeroBit
0, // SAR bits, ReqSeq
1); // Payload
last_pdu = nullptr;
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(payload));
ASSERT_TRUE(last_pdu);
EXPECT_TRUE(ContainersEqual(expected_pdu, *last_pdu));
}
TEST_F(L2CAP_EnhancedRetransmissionModeTxEngineTest,
EngineTransmitsReceiverReadyPollAfterTimeout) {
ByteBufferPtr last_pdu;
auto tx_callback = [&](auto pdu) { last_pdu = std::move(pdu); };
TxEngine tx_engine(kTestChannelId, kDefaultMTU, kDefaultMaxTransmissions,
kDefaultTxWindow, tx_callback, NoOpFailureCallback);
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(kDefaultPayload));
RunLoopUntilIdle();
ASSERT_TRUE(last_pdu);
last_pdu = nullptr;
ASSERT_TRUE(RunLoopFor(zx::sec(2)));
SCOPED_TRACE("");
VerifyIsReceiverReadyPollFrame(last_pdu.get());
}
TEST_F(L2CAP_EnhancedRetransmissionModeTxEngineTest,
EngineTransmitsReceiverReadyPollOnlyOnceAfterTimeout) {
ByteBufferPtr last_pdu;
auto tx_callback = [&](auto pdu) { last_pdu = std::move(pdu); };
TxEngine tx_engine(kTestChannelId, kDefaultMTU, kDefaultMaxTransmissions,
kDefaultTxWindow, tx_callback, NoOpFailureCallback);
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(kDefaultPayload));
RunLoopUntilIdle();
ASSERT_TRUE(last_pdu);
last_pdu = nullptr;
ASSERT_TRUE(RunLoopFor(zx::sec(2)));
SCOPED_TRACE("");
RETURN_IF_FATAL(VerifyIsReceiverReadyPollFrame(last_pdu.get()));
last_pdu = nullptr;
// Note: This value is chosen to be at least as long as
// kReceiverReadyPollTimerDuration, but shorter than kMonitorTimerDuration.
EXPECT_FALSE(RunLoopFor(zx::sec(2))); // No tasks were run.
EXPECT_FALSE(last_pdu);
}
TEST_F(L2CAP_EnhancedRetransmissionModeTxEngineTest,
EngineAdvancesReceiverReadyPollTimeoutOnNewTransmission) {
ByteBufferPtr last_pdu;
auto tx_callback = [&](auto pdu) { last_pdu = std::move(pdu); };
TxEngine tx_engine(kTestChannelId, kDefaultMTU, kDefaultMaxTransmissions,
kDefaultTxWindow, tx_callback, NoOpFailureCallback);
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(kDefaultPayload));
RunLoopUntilIdle();
ASSERT_TRUE(last_pdu);
last_pdu = nullptr;
ASSERT_FALSE(RunLoopFor(zx::sec(1))); // No events should fire.
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(kDefaultPayload));
RunLoopUntilIdle();
last_pdu = nullptr;
ASSERT_FALSE(RunLoopFor(zx::sec(1))); // Original timeout should not fire.
ASSERT_TRUE(RunLoopFor(zx::sec(1))); // New timeout should fire.
SCOPED_TRACE("");
VerifyIsReceiverReadyPollFrame(last_pdu.get());
}
TEST_F(L2CAP_EnhancedRetransmissionModeTxEngineTest,
ReceiverReadyPollIncludesRequestSequenceNumber) {
ByteBufferPtr last_pdu;
auto tx_callback = [&](auto pdu) { last_pdu = std::move(pdu); };
TxEngine tx_engine(kTestChannelId, kDefaultMTU, kDefaultMaxTransmissions,
kDefaultTxWindow, tx_callback, NoOpFailureCallback);
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(kDefaultPayload));
RunLoopUntilIdle();
tx_engine.UpdateReqSeq(1);
RunLoopUntilIdle();
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(kDefaultPayload));
last_pdu = nullptr;
SCOPED_TRACE("");
EXPECT_TRUE(RunLoopFor(zx::sec(2)));
ASSERT_NO_FATAL_FAILURE(VerifyIsReceiverReadyPollFrame(last_pdu.get()));
EXPECT_EQ(1u, last_pdu->As<SimpleSupervisoryFrame>().request_seq_num());
}
TEST_F(L2CAP_EnhancedRetransmissionModeTxEngineTest,
AckOfOnlyOutstandingFrameCancelsReceiverReadyPollTimeout) {
ByteBufferPtr last_pdu;
auto tx_callback = [&](auto pdu) { last_pdu = std::move(pdu); };
TxEngine tx_engine(kTestChannelId, kDefaultMTU, kDefaultMaxTransmissions,
kDefaultTxWindow, tx_callback, NoOpFailureCallback);
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(kDefaultPayload));
RunLoopUntilIdle();
ASSERT_TRUE(last_pdu);
last_pdu = nullptr;
tx_engine.UpdateAckSeq(1, false);
RunLoopUntilIdle();
EXPECT_FALSE(RunLoopFor(zx::sec(2))); // No tasks were run.
EXPECT_FALSE(last_pdu);
}
TEST_F(L2CAP_EnhancedRetransmissionModeTxEngineTest,
AckOfAllOutstandingFramesCancelsReceiverReadyPollTimeout) {
ByteBufferPtr last_pdu;
auto tx_callback = [&](auto pdu) { last_pdu = std::move(pdu); };
TxEngine tx_engine(kTestChannelId, kDefaultMTU, kDefaultMaxTransmissions,
kDefaultTxWindow, tx_callback, NoOpFailureCallback);
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(kDefaultPayload));
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(kDefaultPayload));
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(kDefaultPayload));
RunLoopUntilIdle();
ASSERT_TRUE(last_pdu);
last_pdu = nullptr;
tx_engine.UpdateAckSeq(3, false);
RunLoopUntilIdle();
EXPECT_FALSE(RunLoopFor(zx::sec(2))); // No tasks were run.
EXPECT_FALSE(last_pdu);
}
TEST_F(L2CAP_EnhancedRetransmissionModeTxEngineTest,
PartialAckDoesNotCancelReceiverReadyPollTimeout) {
ByteBufferPtr last_pdu;
auto tx_callback = [&](auto pdu) { last_pdu = std::move(pdu); };
TxEngine tx_engine(kTestChannelId, kDefaultMTU, kDefaultMaxTransmissions,
kDefaultTxWindow, tx_callback, NoOpFailureCallback);
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(kDefaultPayload));
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(kDefaultPayload));
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(kDefaultPayload));
RunLoopUntilIdle();
ASSERT_TRUE(last_pdu);
last_pdu = nullptr;
tx_engine.UpdateAckSeq(1, false);
RunLoopUntilIdle();
// See Core Spec v5.0, Volume 3, Part A, Sec 8.6.5.6, under heading
// Process-ReqSeq. We should only Stop-RetransTimer if UnackedFrames is 0.
SCOPED_TRACE("");
EXPECT_TRUE(RunLoopFor(zx::sec(2)));
VerifyIsReceiverReadyPollFrame(last_pdu.get());
}
TEST_F(L2CAP_EnhancedRetransmissionModeTxEngineTest,
NewTransmissionAfterAckedFrameReArmsReceiverReadyPollTimeout) {
ByteBufferPtr last_pdu;
auto tx_callback = [&](auto pdu) { last_pdu = std::move(pdu); };
TxEngine tx_engine(kTestChannelId, kDefaultMTU, kDefaultMaxTransmissions,
kDefaultTxWindow, tx_callback, NoOpFailureCallback);
// Send a frame, and get the ACK.
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(kDefaultPayload));
RunLoopUntilIdle();
tx_engine.UpdateAckSeq(1, false);
RunLoopUntilIdle();
// Send a new frame.
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(kDefaultPayload));
last_pdu = nullptr;
// Having earlier received an ACK for the previous frame should not have left
// around any state that would prevent us from sending a receiver-ready poll
// for the second frame.
SCOPED_TRACE("");
EXPECT_TRUE(RunLoopFor(zx::sec(2)));
VerifyIsReceiverReadyPollFrame(last_pdu.get());
}
TEST_F(L2CAP_EnhancedRetransmissionModeTxEngineTest,
EngineRetransmitsReceiverReadyPollAfterMonitorTimeout) {
constexpr size_t kMaxTransmissions = 2; // Allow retransmission
ByteBufferPtr last_pdu;
auto tx_callback = [&](auto pdu) { last_pdu = std::move(pdu); };
TxEngine tx_engine(kTestChannelId, kDefaultMTU, kMaxTransmissions,
kDefaultTxWindow, tx_callback, NoOpFailureCallback);
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(kDefaultPayload));
RunLoopUntilIdle();
// First the receiver_ready_poll_task_ fires.
ASSERT_TRUE(RunLoopFor(zx::sec(2)));
ASSERT_TRUE(last_pdu);
last_pdu = nullptr;
// Then the monitor_task_ fires.
EXPECT_TRUE(RunLoopFor(zx::sec(12)));
VerifyIsReceiverReadyPollFrame(last_pdu.get());
}
TEST_F(
L2CAP_EnhancedRetransmissionModeTxEngineTest,
EngineDoesNotRetransmitReceiverReadyPollAfterMonitorTimeoutWhenRetransmissionsAreDisabled) {
constexpr size_t kMaxTransmissions = 1;
ByteBufferPtr last_pdu;
auto tx_callback = [&](auto pdu) { last_pdu = std::move(pdu); };
TxEngine tx_engine(kTestChannelId, kDefaultMTU, kMaxTransmissions,
kDefaultTxWindow, tx_callback, NoOpFailureCallback);
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(kDefaultPayload));
RunLoopUntilIdle();
// First the receiver_ready_poll_task_ fires.
ASSERT_TRUE(RunLoopFor(zx::sec(2)));
ASSERT_TRUE(last_pdu);
last_pdu = nullptr;
// Run the event loop long enough for the monitor task to fire again. Because
// kMaxTransmissions == 1, the ReceiverReadyPoll should not be retransmitted.
RunLoopFor(zx::sec(13));
EXPECT_FALSE(last_pdu);
}
// See Core Spec v5.0, Volume 3, Part A, Sec 5.4, Table 8.6.5.8, for the row
// with "Recv ReqSeqAndFbit" and "F = 1".
TEST_F(
L2CAP_EnhancedRetransmissionModeTxEngineTest,
EngineStopsPollingReceiverReadyFromMonitorTaskAfterReceivingFinalUpdateForAckSeq) {
constexpr size_t kMaxTransmissions = 3; // Allow multiple retransmissions
ByteBufferPtr last_pdu;
TxEngine tx_engine(
kTestChannelId, kDefaultMTU, kMaxTransmissions, kDefaultTxWindow,
[](auto) {}, NoOpFailureCallback);
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(kDefaultPayload));
RunLoopUntilIdle();
ASSERT_TRUE(RunLoopFor(zx::sec(2))); // receiver_ready_poll_task_
ASSERT_TRUE(RunLoopFor(zx::sec(12))); // monitor_task_
tx_engine.UpdateAckSeq(1, true);
EXPECT_FALSE(RunLoopFor(zx::sec(13))); // No other tasks.
}
// See Core Spec v5.0, Volume 3, Part A, Sec 5.4, Table 8.6.5.8, for the row
// with "Recv ReqSeqAndFbit" and "F = 0".
TEST_F(
L2CAP_EnhancedRetransmissionModeTxEngineTest,
EngineContinuesPollingReceiverReadyFromMonitorTaskAfterReceivingNonFinalUpdateForAckSeq) {
constexpr size_t kMaxTransmissions = 2; // Allow retransmissions
ByteBufferPtr last_pdu;
TxEngine tx_engine(
kTestChannelId, kDefaultMTU, kMaxTransmissions, kDefaultTxWindow,
[&](auto pdu) { last_pdu = std::move(pdu); }, NoOpFailureCallback);
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(kDefaultPayload));
RunLoopUntilIdle();
ASSERT_TRUE(RunLoopFor(zx::sec(2))); // receiver_ready_poll_task_
ASSERT_TRUE(RunLoopFor(zx::sec(12))); // monitor_task_
tx_engine.UpdateAckSeq(1, false);
EXPECT_TRUE(RunLoopFor(zx::sec(12))); // monitor_task_
VerifyIsReceiverReadyPollFrame(last_pdu.get());
}
TEST_F(L2CAP_EnhancedRetransmissionModeTxEngineTest,
EngineRetransmitsReceiverReadyPollAfterMultipleMonitorTimeouts) {
constexpr size_t kMaxTransmissions = 3; // Allow multiple retransmissions
ByteBufferPtr last_pdu;
TxEngine tx_engine(
kTestChannelId, kDefaultMTU, kMaxTransmissions, kDefaultTxWindow,
[&](auto pdu) { last_pdu = std::move(pdu); }, NoOpFailureCallback);
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(kDefaultPayload));
RunLoopUntilIdle();
ASSERT_TRUE(RunLoopFor(zx::sec(2))); // receiver_ready_poll_task_
ASSERT_TRUE(RunLoopFor(zx::sec(12))); // monitor_task_
ASSERT_FALSE(RunLoopFor(zx::sec(2))); // RR-poll task does _not_ fire
last_pdu = nullptr;
EXPECT_TRUE(RunLoopFor(zx::sec(10))); // monitor_task_ again
VerifyIsReceiverReadyPollFrame(last_pdu.get());
}
TEST_F(
L2CAP_EnhancedRetransmissionModeTxEngineTest,
EngineRetransmitsReceiverReadyPollIndefinitelyAfterMonitorTimeoutWhenMaxTransmitsIsZero) {
constexpr size_t kMaxTransmissions = 0;
ByteBufferPtr last_pdu;
auto tx_callback = [&](auto pdu) { last_pdu = std::move(pdu); };
TxEngine tx_engine(kTestChannelId, kDefaultMTU, kMaxTransmissions,
kDefaultTxWindow, tx_callback, NoOpFailureCallback);
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(kDefaultPayload));
RunLoopUntilIdle();
// First the receiver_ready_poll_task_ fires.
ASSERT_TRUE(RunLoopFor(zx::sec(2)));
EXPECT_TRUE(last_pdu);
last_pdu = nullptr;
// Then the monitor_task_ fires.
EXPECT_TRUE(RunLoopFor(zx::sec(12)));
EXPECT_TRUE(last_pdu);
last_pdu = nullptr;
// And the monitor_task_ fires again.
EXPECT_TRUE(RunLoopFor(zx::sec(12)));
EXPECT_TRUE(last_pdu);
last_pdu = nullptr;
// And the monitor_task_ fires yet again.
EXPECT_TRUE(RunLoopFor(zx::sec(12)));
EXPECT_TRUE(last_pdu);
}
TEST_F(L2CAP_EnhancedRetransmissionModeTxEngineTest,
EngineStopsTransmittingReceiverReadyPollAfterMaxTransmits) {
constexpr size_t kMaxTransmissions = 2;
ByteBufferPtr last_pdu;
TxEngine tx_engine(
kTestChannelId, kDefaultMTU, kMaxTransmissions, kDefaultTxWindow,
[&](auto pdu) { last_pdu = std::move(pdu); }, NoOpFailureCallback);
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(kDefaultPayload));
RunLoopUntilIdle();
ASSERT_TRUE(RunLoopFor(zx::sec(2))); // receiver_ready_poll_task_
ASSERT_TRUE(RunLoopFor(zx::sec(12))); // monitor_task_
ASSERT_TRUE(RunLoopFor(zx::sec(12))); // monitor_task_
last_pdu = nullptr;
EXPECT_FALSE(RunLoopFor(zx::sec(13)));
EXPECT_FALSE(last_pdu);
}
TEST_F(L2CAP_EnhancedRetransmissionModeTxEngineTest,
EngineClosesChannelAfterMaxTransmitsOfReceiverReadyPoll) {
constexpr size_t kMaxTransmissions = 2;
bool connection_failed = false;
TxEngine tx_engine(
kTestChannelId, kDefaultMTU, kMaxTransmissions, kDefaultTxWindow,
[](auto) {}, [&] { connection_failed = true; });
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(kDefaultPayload));
RunLoopUntilIdle();
ASSERT_TRUE(RunLoopFor(zx::sec(2))); // receiver_ready_poll_task_
ASSERT_TRUE(RunLoopFor(zx::sec(12))); // monitor_task_
ASSERT_TRUE(RunLoopFor(zx::sec(12))); // monitor_task_
EXPECT_TRUE(connection_failed);
}
TEST_F(
L2CAP_EnhancedRetransmissionModeTxEngineTest,
EngineClosesChannelAfterMaxTransmitsOfReceiverReadyPollEvenIfRetransmissionsAreDisabled) {
constexpr size_t kMaxTransmissions = 1;
bool connection_failed = false;
TxEngine tx_engine(
kTestChannelId, kDefaultMTU, kMaxTransmissions, kDefaultTxWindow,
[](auto) {}, [&] { connection_failed = true; });
tx_engine.QueueSdu(std::make_unique<DynamicByteBuffer>(kDefaultPayload));
RunLoopUntilIdle();
ASSERT_TRUE(RunLoopFor(zx::sec(2))); // receiver_ready_poll_task_
ASSERT_TRUE(RunLoopFor(zx::sec(12))); // monitor_task_
EXPECT_TRUE(connection_failed);
}
} // namespace
} // namespace internal
} // namespace l2cap
} // namespace bt