Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b96b24ea authored by Chienyuan's avatar Chienyuan
Browse files

gd HCI: add test for RoundRobinScheduler

Bug: 152342190
Test: ./cert/run --host; ./bluetooth_test_gd
Change-Id: I658c711684b2f29b76ad7311f28bdc12a2994acc
parent a760c7fa
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ filegroup {
        "hci_packets_test.cc",
        "le_advertising_manager_test.cc",
        "le_scanning_manager_test.cc",
        "round_robin_scheduler_test.cc",
    ],
}

+31 −18
Original line number Diff line number Diff line
@@ -31,11 +31,11 @@ RoundRobinScheduler::RoundRobinScheduler(os::Handler* handler, Controller* contr
  le_acl_packet_credits_ = le_max_acl_packet_credits_;
  le_hci_mtu_ = le_buffer_size.le_data_packet_length_;
  controller_->RegisterCompletedAclPacketsCallback(
      common::Bind(&RoundRobinScheduler::IncomingAclCredits, common::Unretained(this)), handler_);
      common::Bind(&RoundRobinScheduler::incoming_acl_credits, common::Unretained(this)), handler_);
}

RoundRobinScheduler::~RoundRobinScheduler() {
  UnregisterAllConnections();
  unregister_all_connections();
  controller_->UnregisterCompletedAclPacketsCallback();
}

@@ -44,7 +44,7 @@ void RoundRobinScheduler::Register(ConnectionType connection_type, uint16_t hand
  acl_queue_handler acl_queue_handler = {connection_type, queue_down_end, false, 0, false};
  acl_queue_handlers_.insert(std::pair<uint16_t, RoundRobinScheduler::acl_queue_handler>(handle, acl_queue_handler));
  if (fragments_to_send_.size() == 0) {
    StartRoundRobin();
    start_round_robin();
  }
}

@@ -71,12 +71,20 @@ void RoundRobinScheduler::SetDisconnect(uint16_t handle) {
  acl_queue_handler.number_of_sent_packets_ = 0;
}

void RoundRobinScheduler::StartRoundRobin() {
uint16_t RoundRobinScheduler::GetCredits() {
  return acl_packet_credits_;
}

uint16_t RoundRobinScheduler::GetLeCredits() {
  return le_acl_packet_credits_;
}

void RoundRobinScheduler::start_round_robin() {
  if (acl_packet_credits_ == 0 && le_acl_packet_credits_ == 0) {
    return;
  }
  if (!fragments_to_send_.empty()) {
    SendNextFragment();
    send_next_fragment();
    return;
  }

@@ -86,10 +94,15 @@ void RoundRobinScheduler::StartRoundRobin() {
  size_t count = acl_queue_handlers_.size();

  for (auto acl_queue_handler = starting_point_; count > 0; count--) {
    if (!acl_queue_handler->second.dequeue_is_registered_) {
    // Prevent registration when credits is zero
    bool classic_buffer_full =
        acl_packet_credits_ == 0 && acl_queue_handler->second.connection_type_ == ConnectionType::CLASSIC;
    bool le_buffer_full =
        le_acl_packet_credits_ == 0 && acl_queue_handler->second.connection_type_ == ConnectionType::LE;
    if (!acl_queue_handler->second.dequeue_is_registered_ && !classic_buffer_full && !le_buffer_full) {
      acl_queue_handler->second.dequeue_is_registered_ = true;
      acl_queue_handler->second.queue_down_end_->RegisterDequeue(
          handler_, common::Bind(&RoundRobinScheduler::BufferPacket, common::Unretained(this), acl_queue_handler));
          handler_, common::Bind(&RoundRobinScheduler::buffer_packet, common::Unretained(this), acl_queue_handler));
    }
    acl_queue_handler = std::next(acl_queue_handler);
    if (acl_queue_handler == acl_queue_handlers_.end()) {
@@ -100,7 +113,7 @@ void RoundRobinScheduler::StartRoundRobin() {
  starting_point_ = std::next(starting_point_);
}

void RoundRobinScheduler::BufferPacket(std::map<uint16_t, acl_queue_handler>::iterator acl_queue_handler) {
void RoundRobinScheduler::buffer_packet(std::map<uint16_t, acl_queue_handler>::iterator acl_queue_handler) {
  BroadcastFlag broadcast_flag = BroadcastFlag::POINT_TO_POINT;
  // Wrap packet and enqueue it
  uint16_t handle = acl_queue_handler->first;
@@ -124,13 +137,13 @@ void RoundRobinScheduler::BufferPacket(std::map<uint16_t, acl_queue_handler>::it
    }
  }
  ASSERT(fragments_to_send_.size() > 0);
  UnregisterAllConnections();
  unregister_all_connections();

  acl_queue_handler->second.number_of_sent_packets_ += fragments_to_send_.size();
  SendNextFragment();
  send_next_fragment();
}

void RoundRobinScheduler::UnregisterAllConnections() {
void RoundRobinScheduler::unregister_all_connections() {
  for (auto acl_queue_handler = acl_queue_handlers_.begin(); acl_queue_handler != acl_queue_handlers_.end();
       acl_queue_handler = std::next(acl_queue_handler)) {
    if (acl_queue_handler->second.dequeue_is_registered_) {
@@ -140,15 +153,15 @@ void RoundRobinScheduler::UnregisterAllConnections() {
  }
}

void RoundRobinScheduler::SendNextFragment() {
void RoundRobinScheduler::send_next_fragment() {
  if (!enqueue_registered_.exchange(true)) {
    hci_queue_end_->RegisterEnqueue(
        handler_, common::Bind(&RoundRobinScheduler::HandleEnqueueNextFragment, common::Unretained(this)));
        handler_, common::Bind(&RoundRobinScheduler::handle_enqueue_next_fragment, common::Unretained(this)));
  }
}

// Invoked from some external Queue Reactable context 1
std::unique_ptr<AclPacketBuilder> RoundRobinScheduler::HandleEnqueueNextFragment() {
std::unique_ptr<AclPacketBuilder> RoundRobinScheduler::handle_enqueue_next_fragment() {
  ConnectionType connection_type = fragments_to_send_.front().first;
  if (connection_type == ConnectionType::CLASSIC) {
    ASSERT(acl_packet_credits_ > 0);
@@ -164,7 +177,7 @@ std::unique_ptr<AclPacketBuilder> RoundRobinScheduler::HandleEnqueueNextFragment
    if (enqueue_registered_.exchange(false)) {
      hci_queue_end_->UnregisterEnqueue();
    }
    handler_->Post(common::BindOnce(&RoundRobinScheduler::StartRoundRobin, common::Unretained(this)));
    handler_->Post(common::BindOnce(&RoundRobinScheduler::start_round_robin, common::Unretained(this)));
  } else {
    ConnectionType next_connection_type = fragments_to_send_.front().first;
    bool classic_buffer_full = next_connection_type == ConnectionType::CLASSIC && acl_packet_credits_ == 0;
@@ -176,7 +189,7 @@ std::unique_ptr<AclPacketBuilder> RoundRobinScheduler::HandleEnqueueNextFragment
  return std::unique_ptr<AclPacketBuilder>(raw_pointer);
}

void RoundRobinScheduler::IncomingAclCredits(uint16_t handle, uint16_t credits) {
void RoundRobinScheduler::incoming_acl_credits(uint16_t handle, uint16_t credits) {
  auto acl_queue_handler = acl_queue_handlers_.find(handle);
  if (acl_queue_handler == acl_queue_handlers_.end()) {
    LOG_INFO("Dropping %hx received credits to unknown connection 0x%0hx", credits, handle);
@@ -195,7 +208,7 @@ void RoundRobinScheduler::IncomingAclCredits(uint16_t handle, uint16_t credits)
  ASSERT(acl_packet_credits_ <= max_acl_packet_credits_);
  ASSERT(le_acl_packet_credits_ <= le_max_acl_packet_credits_);
  if (acl_packet_credits_ == credits || le_acl_packet_credits_ == credits) {
    StartRoundRobin();
    start_round_robin();
  }
}

+8 −6
Original line number Diff line number Diff line
@@ -46,14 +46,16 @@ class RoundRobinScheduler {
  void Register(ConnectionType connection_type, uint16_t handle, AclConnection::QueueDownEnd* queue_down_end);
  void Unregister(uint16_t handle);
  void SetDisconnect(uint16_t handle);
  uint16_t GetCredits();
  uint16_t GetLeCredits();

 private:
  void StartRoundRobin();
  void BufferPacket(std::map<uint16_t, acl_queue_handler>::iterator acl_queue_handler);
  void UnregisterAllConnections();
  void SendNextFragment();
  std::unique_ptr<AclPacketBuilder> HandleEnqueueNextFragment();
  void IncomingAclCredits(uint16_t handle, uint16_t credits);
  void start_round_robin();
  void buffer_packet(std::map<uint16_t, acl_queue_handler>::iterator acl_queue_handler);
  void unregister_all_connections();
  void send_next_fragment();
  std::unique_ptr<AclPacketBuilder> handle_enqueue_next_fragment();
  void incoming_acl_credits(uint16_t handle, uint16_t credits);

  os::Handler* handler_ = nullptr;
  Controller* controller_ = nullptr;
+382 −0
Original line number Diff line number Diff line
/*
 * Copyright 2020 The Android Open Source Project
 *
 * 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 "hci/round_robin_scheduler.h"
#include <gtest/gtest.h>
#include "common/bidi_queue.h"
#include "common/callback.h"
#include "hci/controller.h"
#include "hci/hci_packets.h"
#include "os/handler.h"
#include "os/log.h"
#include "packet/raw_builder.h"

using ::bluetooth::common::BidiQueue;
using ::bluetooth::common::Callback;
using ::bluetooth::os::Handler;
using ::bluetooth::os::Thread;

namespace bluetooth {
namespace hci {

class TestController : public Controller {
 public:
  uint16_t GetControllerNumAclPacketBuffers() const {
    return max_acl_packet_credits_;
  }

  uint16_t GetControllerAclPacketLength() const {
    return hci_mtu_;
  }

  LeBufferSize GetControllerLeBufferSize() const {
    LeBufferSize le_buffer_size;
    le_buffer_size.le_data_packet_length_ = le_hci_mtu_;
    le_buffer_size.total_num_le_packets_ = le_max_acl_packet_credits_;
    return le_buffer_size;
  }

  void RegisterCompletedAclPacketsCallback(common::Callback<void(uint16_t /* handle */, uint16_t /* num_packets */)> cb,
                                           os::Handler* handler) {
    acl_credits_handler_ = handler;
    acl_credits_callback_ = cb;
  }

  std::future<void> SendCompletedAclPacketsCallback(uint16_t handle, uint16_t credits) {
    auto promise = std::make_unique<std::promise<void>>();
    auto future = promise->get_future();
    acl_credits_handler_->Post(Bind(acl_credits_callback_, handle, credits));
    acl_credits_handler_->Post(common::BindOnce(
        [](std::unique_ptr<std::promise<void>> promise) mutable { promise->set_value(); }, std::move(promise)));
    return future;
  }

  void UnregisterCompletedAclPacketsCallback() {
    acl_credits_handler_ = nullptr;
    acl_credits_callback_ = {};
  }

  const uint16_t max_acl_packet_credits_ = 10;
  const uint16_t hci_mtu_ = 1024;
  const uint16_t le_max_acl_packet_credits_ = 15;
  const uint16_t le_hci_mtu_ = 27;

 private:
  Handler* acl_credits_handler_;
  Callback<void(uint16_t, uint16_t)> acl_credits_callback_;
};

class RoundRobinSchedulerTest : public ::testing::Test {
 public:
  void SetUp() override {
    thread_ = new Thread("thread", Thread::Priority::NORMAL);
    handler_ = new Handler(thread_);
    controller_ = new TestController();
    round_robin_scheduler_ = new RoundRobinScheduler(handler_, controller_, hci_queue_.GetUpEnd());
    hci_queue_.GetDownEnd()->RegisterDequeue(
        handler_, common::Bind(&RoundRobinSchedulerTest::HciDownEndDequeue, common::Unretained(this)));
  }

  void TearDown() override {
    hci_queue_.GetDownEnd()->UnregisterDequeue();
    delete round_robin_scheduler_;
    delete controller_;
    handler_->Clear();
    delete handler_;
    delete thread_;
  }

  void EnqueueAclUpEnd(AclConnection::QueueUpEnd* queue_up_end, std::vector<uint8_t> packet) {
    if (enqueue_promise_ != nullptr) {
      enqueue_future_->wait();
    }
    enqueue_promise_ = std::make_unique<std::promise<void>>();
    enqueue_future_ = std::make_unique<std::future<void>>(enqueue_promise_->get_future());
    queue_up_end->RegisterEnqueue(handler_, common::Bind(&RoundRobinSchedulerTest::enqueue_callback,
                                                         common::Unretained(this), queue_up_end, packet));
  }

  std::unique_ptr<packet::BasePacketBuilder> enqueue_callback(AclConnection::QueueUpEnd* queue_up_end,
                                                              std::vector<uint8_t> packet) {
    auto packet_one = std::make_unique<packet::RawBuilder>(2000);
    packet_one->AddOctets(packet);
    queue_up_end->UnregisterEnqueue();
    enqueue_promise_->set_value();
    return packet_one;
  };

  void HciDownEndDequeue() {
    auto packet = hci_queue_.GetDownEnd()->TryDequeue();
    // Convert from a Builder to a View
    auto bytes = std::make_shared<std::vector<uint8_t>>();
    bluetooth::packet::BitInserter i(*bytes);
    bytes->reserve(packet->size());
    packet->Serialize(i);
    auto packet_view = bluetooth::packet::PacketView<bluetooth::packet::kLittleEndian>(bytes);
    AclPacketView acl_packet_view = AclPacketView::Create(packet_view);
    ASSERT(acl_packet_view.IsValid());
    PacketView<true> count_view = acl_packet_view.GetPayload();
    sent_acl_packets_.push(acl_packet_view);

    packet_count_--;
    if (packet_count_ == 0) {
      packet_promise_->set_value();
      packet_promise_ = nullptr;
    }
  }

  void VerifyPacket(uint16_t handle, std::vector<uint8_t> packet) {
    auto acl_packet_view = sent_acl_packets_.front();
    ASSERT_EQ(handle, acl_packet_view.GetHandle());
    auto payload = acl_packet_view.GetPayload();
    for (size_t i = 0; i < payload.size(); i++) {
      ASSERT_EQ(payload[i], packet[i]);
    }
    sent_acl_packets_.pop();
  }

  void SetPacketFuture(uint16_t count) {
    ASSERT_LOG(packet_promise_ == nullptr, "Promises, Promises, ... Only one at a time.");
    packet_count_ = count;
    packet_promise_ = std::make_unique<std::promise<void>>();
    packet_future_ = std::make_unique<std::future<void>>(packet_promise_->get_future());
  }

  BidiQueue<AclPacketView, AclPacketBuilder> hci_queue_{3};
  Thread* thread_;
  Handler* handler_;
  TestController* controller_;
  RoundRobinScheduler* round_robin_scheduler_;
  std::queue<AclPacketView> sent_acl_packets_;
  uint16_t packet_count_;
  std::unique_ptr<std::promise<void>> packet_promise_;
  std::unique_ptr<std::future<void>> packet_future_;
  std::unique_ptr<std::promise<void>> enqueue_promise_;
  std::unique_ptr<std::future<void>> enqueue_future_;
};

TEST_F(RoundRobinSchedulerTest, startup_teardown) {}

TEST_F(RoundRobinSchedulerTest, register_unregister_connection) {
  uint16_t handle = 0x01;
  BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> connection_queue{10};
  round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, connection_queue.GetDownEnd());
  round_robin_scheduler_->Unregister(handle);
}

TEST_F(RoundRobinSchedulerTest, buffer_packet) {
  uint16_t handle = 0x01;
  BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> connection_queue{10};

  AclConnection::QueueDownEnd* queue_down_end = connection_queue.GetDownEnd();
  round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, queue_down_end);

  SetPacketFuture(2);
  AclConnection::QueueUpEnd* queue_up_end = connection_queue.GetUpEnd();
  std::vector<uint8_t> packet1 = {0x01, 0x02, 0x03};
  std::vector<uint8_t> packet2 = {0x04, 0x05, 0x06};
  EnqueueAclUpEnd(queue_up_end, packet1);
  EnqueueAclUpEnd(queue_up_end, packet2);

  packet_future_->wait();
  VerifyPacket(handle, packet1);
  VerifyPacket(handle, packet2);
  ASSERT_EQ(round_robin_scheduler_->GetCredits(), controller_->max_acl_packet_credits_ - 2);

  round_robin_scheduler_->Unregister(handle);
}

TEST_F(RoundRobinSchedulerTest, buffer_packet_from_two_connections) {
  uint16_t handle = 0x01;
  uint16_t le_handle = 0x02;
  BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> connection_queue{10};
  BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> le_connection_queue{10};

  AclConnection::QueueDownEnd* queue_down_end = connection_queue.GetDownEnd();
  AclConnection::QueueDownEnd* le_queue_down_end = le_connection_queue.GetDownEnd();
  round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, queue_down_end);
  round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, le_handle, le_queue_down_end);

  SetPacketFuture(2);
  AclConnection::QueueUpEnd* queue_up_end = connection_queue.GetUpEnd();
  AclConnection::QueueUpEnd* le_queue_up_end = le_connection_queue.GetUpEnd();
  std::vector<uint8_t> packet = {0x01, 0x02, 0x03};
  std::vector<uint8_t> le_packet = {0x04, 0x05, 0x06};
  EnqueueAclUpEnd(le_queue_up_end, le_packet);
  EnqueueAclUpEnd(queue_up_end, packet);

  packet_future_->wait();
  VerifyPacket(le_handle, le_packet);
  VerifyPacket(handle, packet);
  ASSERT_EQ(round_robin_scheduler_->GetCredits(), controller_->max_acl_packet_credits_ - 1);
  ASSERT_EQ(round_robin_scheduler_->GetLeCredits(), controller_->le_max_acl_packet_credits_ - 1);

  round_robin_scheduler_->Unregister(handle);
  round_robin_scheduler_->Unregister(le_handle);
}

TEST_F(RoundRobinSchedulerTest, do_not_register_when_credits_is_zero) {
  uint16_t handle = 0x01;
  BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> connection_queue{15};
  AclConnection::QueueDownEnd* queue_down_end = connection_queue.GetDownEnd();
  round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, queue_down_end);

  SetPacketFuture(10);
  AclConnection::QueueUpEnd* queue_up_end = connection_queue.GetUpEnd();
  for (uint8_t i = 0; i < 15; i++) {
    std::vector<uint8_t> packet = {0x01, 0x02, 0x03, i};
    EnqueueAclUpEnd(queue_up_end, packet);
  }

  packet_future_->wait();
  for (uint8_t i = 0; i < 10; i++) {
    std::vector<uint8_t> packet = {0x01, 0x02, 0x03, i};
    VerifyPacket(handle, packet);
  }
  ASSERT_EQ(round_robin_scheduler_->GetCredits(), 0);

  SetPacketFuture(5);
  auto future = controller_->SendCompletedAclPacketsCallback(0x01, 10);
  future.wait();
  packet_future_->wait();
  for (uint8_t i = 10; i < 15; i++) {
    std::vector<uint8_t> packet = {0x01, 0x02, 0x03, i};
    VerifyPacket(handle, packet);
  }
  ASSERT_EQ(round_robin_scheduler_->GetCredits(), 5);

  round_robin_scheduler_->Unregister(handle);
}

TEST_F(RoundRobinSchedulerTest, reveived_completed_callback_with_unknown_handle) {
  auto future = controller_->SendCompletedAclPacketsCallback(0x00, 1);
  future.wait();
  EXPECT_EQ(round_robin_scheduler_->GetCredits(), controller_->max_acl_packet_credits_);
  EXPECT_EQ(round_robin_scheduler_->GetLeCredits(), controller_->le_max_acl_packet_credits_);
}

TEST_F(RoundRobinSchedulerTest, buffer_packet_intervally) {
  uint16_t handle1 = 0x01;
  uint16_t handle2 = 0x02;
  uint16_t le_handle1 = 0x03;
  uint16_t le_handle2 = 0x04;
  BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> connection_queue1{10};
  BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> connection_queue2{10};
  BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> le_connection_queue1{10};
  BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> le_connection_queue2{10};
  AclConnection::QueueDownEnd* queue_down_end1 = connection_queue1.GetDownEnd();
  AclConnection::QueueDownEnd* queue_down_end2 = connection_queue2.GetDownEnd();
  AclConnection::QueueDownEnd* le_queue_down_end1 = le_connection_queue1.GetDownEnd();
  AclConnection::QueueDownEnd* le_queue_down_end2 = le_connection_queue2.GetDownEnd();

  SetPacketFuture(18);
  AclConnection::QueueUpEnd* queue_up_end1 = connection_queue1.GetUpEnd();
  AclConnection::QueueUpEnd* queue_up_end2 = connection_queue2.GetUpEnd();
  AclConnection::QueueUpEnd* le_queue_up_end1 = le_connection_queue1.GetUpEnd();
  AclConnection::QueueUpEnd* le_queue_up_end2 = le_connection_queue2.GetUpEnd();

  round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle1, queue_down_end1);
  round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle2, queue_down_end2);
  round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, le_handle1, le_queue_down_end1);
  round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, le_handle2, le_queue_down_end2);

  std::vector<uint8_t> packet = {0x01, 0x02, 0x03};
  EnqueueAclUpEnd(queue_up_end1, packet);
  EnqueueAclUpEnd(le_queue_up_end2, packet);
  for (uint8_t i = 0; i < 4; i++) {
    std::vector<uint8_t> packet1 = {0x01, 0x02, 0x03, i};
    std::vector<uint8_t> packet2 = {0x02, 0x02, 0x03, i};
    std::vector<uint8_t> le_packet1 = {0x04, 0x05, 0x06, i};
    std::vector<uint8_t> le_packet2 = {0x05, 0x05, 0x06, i};
    EnqueueAclUpEnd(queue_up_end1, packet1);
    EnqueueAclUpEnd(queue_up_end2, packet2);
    EnqueueAclUpEnd(le_queue_up_end1, le_packet1);
    EnqueueAclUpEnd(le_queue_up_end2, le_packet2);
  }

  packet_future_->wait();
  VerifyPacket(handle1, packet);
  VerifyPacket(le_handle2, packet);
  for (uint8_t i = 0; i < 4; i++) {
    std::vector<uint8_t> packet1 = {0x01, 0x02, 0x03, i};
    std::vector<uint8_t> packet2 = {0x02, 0x02, 0x03, i};
    std::vector<uint8_t> le_packet1 = {0x04, 0x05, 0x06, i};
    std::vector<uint8_t> le_packet2 = {0x05, 0x05, 0x06, i};
    VerifyPacket(handle1, packet1);
    VerifyPacket(handle2, packet2);
    VerifyPacket(le_handle1, le_packet1);
    VerifyPacket(le_handle2, le_packet2);
  }

  ASSERT_EQ(round_robin_scheduler_->GetCredits(), controller_->max_acl_packet_credits_ - 9);
  ASSERT_EQ(round_robin_scheduler_->GetLeCredits(), controller_->le_max_acl_packet_credits_ - 9);

  round_robin_scheduler_->Unregister(handle1);
  round_robin_scheduler_->Unregister(handle2);
  round_robin_scheduler_->Unregister(le_handle1);
  round_robin_scheduler_->Unregister(le_handle2);
}

TEST_F(RoundRobinSchedulerTest, send_fragments_without_interval) {
  uint16_t handle = 0x01;
  uint16_t le_handle = 0x02;
  BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> connection_queue{10};
  BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> le_connection_queue{10};

  AclConnection::QueueDownEnd* queue_down_end = connection_queue.GetDownEnd();
  AclConnection::QueueDownEnd* le_queue_down_end = le_connection_queue.GetDownEnd();
  round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, queue_down_end);
  round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, le_handle, le_queue_down_end);

  SetPacketFuture(5);
  AclConnection::QueueUpEnd* queue_up_end = connection_queue.GetUpEnd();
  AclConnection::QueueUpEnd* le_queue_up_end = le_connection_queue.GetUpEnd();
  std::vector<uint8_t> packet(controller_->hci_mtu_, 0xff);
  std::vector<uint8_t> packet_part1(controller_->hci_mtu_, 0xff);
  std::vector<uint8_t> packet_part2 = {0x03, 0x02, 0x01};
  packet.insert(packet.end(), packet_part2.begin(), packet_part2.end());

  std::vector<uint8_t> le_packet;
  std::vector<uint8_t> le_packet_part1;
  std::vector<uint8_t> le_packet_part2;
  std::vector<uint8_t> le_packet_part3;
  for (uint8_t i = 0; i < controller_->le_hci_mtu_; i++) {
    le_packet.push_back(i);
    le_packet_part1.push_back(i);
    le_packet_part2.push_back(i * 2);
    le_packet_part3.push_back(i * 3);
  }
  le_packet.insert(le_packet.end(), le_packet_part2.begin(), le_packet_part2.end());
  le_packet.insert(le_packet.end(), le_packet_part3.begin(), le_packet_part3.end());

  EnqueueAclUpEnd(le_queue_up_end, le_packet);
  EnqueueAclUpEnd(queue_up_end, packet);

  packet_future_->wait();
  VerifyPacket(le_handle, le_packet_part1);
  VerifyPacket(le_handle, le_packet_part2);
  VerifyPacket(le_handle, le_packet_part3);
  VerifyPacket(handle, packet_part1);
  VerifyPacket(handle, packet_part2);
  ASSERT_EQ(round_robin_scheduler_->GetCredits(), controller_->max_acl_packet_credits_ - 2);
  ASSERT_EQ(round_robin_scheduler_->GetLeCredits(), controller_->le_max_acl_packet_credits_ - 3);

  round_robin_scheduler_->Unregister(handle);
  round_robin_scheduler_->Unregister(le_handle);
}

}  // namespace hci
}  // namespace bluetooth
 No newline at end of file