blob: e352903dbae21566d98c2264d620e1d20665364c [file] [log] [blame]
// Copyright 2020 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 "sco_connection.h"
#include <gtest/gtest.h>
#include "lib/gtest/test_loop_fixture.h"
#include "src/connectivity/bluetooth/core/bt-host/common/test_helpers.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/fake_sco_connection.h"
#include "src/connectivity/bluetooth/core/bt-host/socket/socket_factory.h"
#include "src/connectivity/bluetooth/core/bt-host/testing/controller_test.h"
#include "src/connectivity/bluetooth/core/bt-host/testing/mock_controller.h"
#include "src/connectivity/bluetooth/core/bt-host/transport/fake_sco_data_channel.h"
namespace bt::sco {
namespace {
hci_spec::ConnectionHandle kConnectionHandle = 1u;
constexpr uint16_t kHciScoMtu = 1;
using TestingBase = testing::ControllerTest<testing::MockController>;
class ScoConnectionTest : public TestingBase {
public:
ScoConnectionTest() = default;
~ScoConnectionTest() override = default;
protected:
void SetUp() override {
TestingBase::SetUp();
InitializeACLDataChannel();
auto fake_conn = std::make_unique<hci::testing::FakeScoConnection>(
kConnectionHandle, DeviceAddress(), DeviceAddress(), transport()->WeakPtr());
hci_conn_ = fake_conn->GetWeakPtr();
fake_conn_ = fake_conn.get();
deactivated_cb_count_ = 0;
sco_conn_ = CreateScoConnection(std::move(fake_conn));
}
void TearDown() override {
sco_conn_ = nullptr;
TestingBase::TearDown();
}
virtual fbl::RefPtr<ScoConnection> CreateScoConnection(
std::unique_ptr<hci::Connection> hci_conn) {
return ScoConnection::Create(
std::move(hci_conn), [this] { OnDeactivated(); },
hci_spec::SynchronousConnectionParameters(), /*channel=*/nullptr);
}
void OnDeactivated() { deactivated_cb_count_++; }
auto sco_conn() { return sco_conn_; }
auto hci_conn() { return hci_conn_; }
auto fake_conn() { return fake_conn_; }
size_t deactivated_count() const { return deactivated_cb_count_; }
private:
size_t deactivated_cb_count_;
fbl::RefPtr<ScoConnection> sco_conn_;
fxl::WeakPtr<hci::ScoConnection> hci_conn_;
hci::testing::FakeScoConnection* fake_conn_;
};
class HciScoConnectionTest : public ScoConnectionTest {
public:
fbl::RefPtr<ScoConnection> CreateScoConnection(
std::unique_ptr<hci::Connection> hci_conn) override {
channel_ = std::make_unique<hci::FakeScoDataChannel>(/*mtu=*/kHciScoMtu);
constexpr hci_spec::SynchronousConnectionParameters hci_conn_params{
.input_data_path = hci_spec::ScoDataPath::kHci,
.output_data_path = hci_spec::ScoDataPath::kHci,
};
return ScoConnection::Create(
std::move(hci_conn), [this] { OnDeactivated(); }, hci_conn_params, channel_.get());
}
hci::FakeScoDataChannel* fake_sco_chan() { return channel_.get(); }
private:
std::unique_ptr<hci::FakeScoDataChannel> channel_;
};
TEST_F(ScoConnectionTest, Send) { EXPECT_FALSE(sco_conn()->Send(nullptr)); }
TEST_F(ScoConnectionTest, MaxTxSduSize) { EXPECT_EQ(sco_conn()->max_tx_sdu_size(), 0u); }
TEST_F(ScoConnectionTest, ActivateAndDeactivate) {
size_t close_count = 0;
auto closed_cb = [&] { close_count++; };
EXPECT_TRUE(sco_conn()->Activate(/*rx_callback=*/[]() {}, std::move(closed_cb)));
EXPECT_EQ(close_count, 0u);
EXPECT_TRUE(hci_conn());
sco_conn()->Deactivate();
EXPECT_EQ(close_count, 0u);
EXPECT_EQ(deactivated_count(), 1u);
EXPECT_FALSE(hci_conn());
// Deactivating should be idempotent.
sco_conn()->Deactivate();
EXPECT_EQ(close_count, 0u);
EXPECT_EQ(deactivated_count(), 1u);
}
TEST_F(HciScoConnectionTest, ActivateAndDeactivateRegistersAndUnregistersConnection) {
EXPECT_TRUE(fake_sco_chan()->connections().empty());
EXPECT_TRUE(sco_conn()->Activate(/*rx_callback=*/[]() {}, /*closed_callback=*/[] {}));
ASSERT_EQ(fake_sco_chan()->connections().size(), 1u);
EXPECT_EQ(fake_sco_chan()->connections().begin()->first, sco_conn()->handle());
sco_conn()->Deactivate();
EXPECT_TRUE(fake_sco_chan()->connections().empty());
}
TEST_F(ScoConnectionTest, ActivateAndClose) {
size_t close_count = 0;
auto closed_cb = [&] { close_count++; };
EXPECT_TRUE(sco_conn()->Activate(/*rx_callback=*/[]() {}, std::move(closed_cb)));
EXPECT_EQ(close_count, 0u);
EXPECT_TRUE(hci_conn());
sco_conn()->Close();
EXPECT_EQ(close_count, 1u);
EXPECT_EQ(deactivated_count(), 0u);
EXPECT_FALSE(hci_conn());
// Closing should be idempotent.
sco_conn()->Close();
EXPECT_EQ(close_count, 1u);
EXPECT_EQ(deactivated_count(), 0u);
}
TEST_F(HciScoConnectionTest, ActivateAndCloseRegistersAndUnregistersConnection) {
EXPECT_TRUE(fake_sco_chan()->connections().empty());
EXPECT_TRUE(sco_conn()->Activate(/*rx_callback=*/[]() {}, /*closed_callback=*/[] {}));
ASSERT_EQ(fake_sco_chan()->connections().size(), 1u);
EXPECT_EQ(fake_sco_chan()->connections().begin()->first, sco_conn()->handle());
sco_conn()->Close();
EXPECT_TRUE(fake_sco_chan()->connections().empty());
}
TEST_F(ScoConnectionTest, UniqueId) { EXPECT_EQ(sco_conn()->unique_id(), kConnectionHandle); }
TEST_F(ScoConnectionTest, CloseWithoutActivating) {
EXPECT_TRUE(hci_conn());
sco_conn()->Close();
EXPECT_EQ(deactivated_count(), 0u);
EXPECT_FALSE(hci_conn());
}
TEST_F(HciScoConnectionTest, CloseWithoutActivatingDoesNotUnregister) { sco_conn()->Close(); }
TEST_F(ScoConnectionTest, ActivateAndPeerDisconnectDeactivates) {
size_t close_count = 0;
auto closed_cb = [&] { close_count++; };
EXPECT_TRUE(sco_conn()->Activate(/*rx_callback=*/[]() {}, std::move(closed_cb)));
EXPECT_EQ(close_count, 0u);
ASSERT_TRUE(hci_conn());
fake_conn()->TriggerPeerDisconnectCallback();
EXPECT_EQ(close_count, 1u);
EXPECT_EQ(deactivated_count(), 0u);
EXPECT_FALSE(hci_conn());
}
TEST_F(HciScoConnectionTest, ReceiveTwoPackets) {
size_t close_count = 0;
auto closed_cb = [&] { close_count++; };
std::vector<std::unique_ptr<hci::ScoDataPacket>> packets;
auto rx_callback = [&packets, sco_conn = sco_conn().get()]() {
std::unique_ptr<hci::ScoDataPacket> packet = sco_conn->Read();
ASSERT_TRUE(packet);
packets.push_back(std::move(packet));
};
EXPECT_TRUE(sco_conn()->Activate(std::move(rx_callback), std::move(closed_cb)));
ASSERT_EQ(fake_sco_chan()->connections().size(), 1u);
EXPECT_FALSE(sco_conn()->Read());
StaticByteBuffer packet_buffer_0(
LowerBits(kConnectionHandle),
UpperBits(kConnectionHandle) | 0x30, // handle + packet status flag: kDataPartiallyLost
0x01, // payload length
0x00 // payload
);
std::unique_ptr<hci::ScoDataPacket> packet_0 = hci::ScoDataPacket::New(/*payload_size=*/1);
packet_0->mutable_view()->mutable_data().Write(packet_buffer_0);
packet_0->InitializeFromBuffer();
sco_conn()->ReceiveInboundPacket(std::move(packet_0));
ASSERT_EQ(packets.size(), 1u);
EXPECT_FALSE(sco_conn()->Read());
StaticByteBuffer payload_buffer_0(0x00);
EXPECT_TRUE(ContainersEqual(packets[0]->view().payload_data(), payload_buffer_0));
EXPECT_EQ(packets[0]->packet_status_flag(),
hci_spec::SynchronousDataPacketStatusFlag::kDataPartiallyLost);
StaticByteBuffer packet_buffer_1(
LowerBits(kConnectionHandle),
UpperBits(kConnectionHandle), // handle + packet status flag: kCorrectlyReceived
0x01, // payload length
0x01 // payload
);
std::unique_ptr<hci::ScoDataPacket> packet_1 = hci::ScoDataPacket::New(/*payload_size=*/1);
packet_1->mutable_view()->mutable_data().Write(packet_buffer_1);
packet_1->InitializeFromBuffer();
sco_conn()->ReceiveInboundPacket(std::move(packet_1));
ASSERT_EQ(packets.size(), 2u);
EXPECT_FALSE(sco_conn()->Read());
auto payload_buffer_1 = StaticByteBuffer(0x01);
EXPECT_TRUE(ContainersEqual(packets[1]->view().payload_data(), payload_buffer_1));
EXPECT_EQ(packets[1]->packet_status_flag(),
hci_spec::SynchronousDataPacketStatusFlag::kCorrectlyReceived);
}
TEST_F(HciScoConnectionTest, SendPackets) {
EXPECT_TRUE(sco_conn()->Activate(/*rx_callback=*/[]() {}, /*closed_callback=*/[] {}));
ASSERT_EQ(fake_sco_chan()->connections().size(), 1u);
const auto packet_buffer_0 = StaticByteBuffer(LowerBits(kConnectionHandle),
UpperBits(kConnectionHandle), // handle
0x01, // payload length
0x00 // payload
);
const BufferView payload_view_0 = packet_buffer_0.view(sizeof(hci_spec::SynchronousDataHeader));
sco_conn()->Send(std::make_unique<DynamicByteBuffer>(payload_view_0));
auto packet_buffer_1 = StaticByteBuffer(LowerBits(kConnectionHandle),
UpperBits(kConnectionHandle), // handle
0x01, // payload length
0x01 // payload
);
BufferView payload_view_1 = packet_buffer_1.view(sizeof(hci_spec::SynchronousDataHeader));
sco_conn()->Send(std::make_unique<DynamicByteBuffer>(payload_view_1));
EXPECT_EQ(fake_sco_chan()->readable_count(), 1u);
std::unique_ptr<hci::ScoDataPacket> sent_packet = sco_conn()->GetNextOutboundPacket();
ASSERT_TRUE(sent_packet);
EXPECT_TRUE(ContainersEqual(packet_buffer_0, sent_packet->view().data()));
sent_packet = sco_conn()->GetNextOutboundPacket();
ASSERT_TRUE(sent_packet);
EXPECT_TRUE(ContainersEqual(packet_buffer_1, sent_packet->view().data()));
EXPECT_FALSE(sco_conn()->GetNextOutboundPacket());
}
TEST_F(HciScoConnectionTest, SendPacketLargerThanMtuGetsDropped) {
EXPECT_TRUE(sco_conn()->Activate(/*rx_callback=*/[]() {}, /*closed_callback=*/[] {}));
ASSERT_EQ(fake_sco_chan()->connections().size(), 1u);
const auto packet_buffer = StaticByteBuffer(LowerBits(kConnectionHandle),
UpperBits(kConnectionHandle), // handle
0x02, // payload length
0x00, 0x01 // payload
);
const BufferView payload_view = packet_buffer.view(sizeof(hci_spec::SynchronousDataHeader));
EXPECT_GT(payload_view.size(), sco_conn()->max_tx_sdu_size());
sco_conn()->Send(std::make_unique<DynamicByteBuffer>(payload_view));
EXPECT_EQ(fake_sco_chan()->readable_count(), 0u);
EXPECT_FALSE(sco_conn()->GetNextOutboundPacket());
}
TEST_F(HciScoConnectionTest, OnHciError) {
int closed_cb_count = 0;
EXPECT_TRUE(sco_conn()->Activate(/*rx_callback=*/[]() {}, /*closed_callback=*/
[&] {
closed_cb_count++;
sco_conn()->Deactivate();
}));
ASSERT_EQ(fake_sco_chan()->connections().size(), 1u);
sco_conn()->OnHciError();
EXPECT_EQ(closed_cb_count, 1);
}
} // namespace
} // namespace bt::sco