// Copyright 2024 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_sapphire/fuchsia/host/controllers/fidl_controller.h" #include #include "gmock/gmock.h" #include "pw_bluetooth_sapphire/fuchsia/host/fidl/fake_vendor_server.h" #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h" #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h" #include "pw_bluetooth_sapphire/internal/host/testing/gtest_helpers.h" #include "pw_bluetooth_sapphire/internal/host/testing/loop_fixture.h" namespace fhbt = fuchsia_hardware_bluetooth; namespace bt::controllers { namespace { constexpr hci_spec::ConnectionHandle kConnectionHandle = 0x0001; const StaticByteBuffer kSetAclPriorityNormalCommand(0x00); const StaticByteBuffer kSetAclPrioritySourceCommand(0x01); const StaticByteBuffer kSetAclPrioritySinkCommand(0x02); } // namespace class FidlControllerTest : public bt::testing::TestLoopFixture { public: void SetUp() override { auto [vendor_client_end, vendor_server_end] = ::fidl::Endpoints::Create(); fake_vendor_server_.emplace(std::move(vendor_server_end), dispatcher()); fidl_controller_.emplace(std::move(vendor_client_end), dispatcher()); } void InitializeController() { controller()->Initialize( [&](pw::Status cb_complete_status) { complete_status_ = cb_complete_status; }, [&](pw::Status cb_error) { controller_error_ = cb_error; }); ASSERT_FALSE(complete_status_.has_value()); ASSERT_FALSE(controller_error_.has_value()); } FidlController* controller() { return &fidl_controller_.value(); } fidl::testing::FakeHciTransportServer* hci_server() { return fake_vendor_server_->hci_server(); } fidl::testing::FakeVendorServer* vendor_server() { return &fake_vendor_server_.value(); } std::optional complete_status() const { return complete_status_; } std::optional controller_error() const { return controller_error_; } private: std::optional complete_status_; std::optional controller_error_; std::optional fake_vendor_server_; std::optional fidl_controller_; }; TEST_F(FidlControllerTest, SendAndReceiveAclPackets) { RETURN_IF_FATAL(InitializeController()); RunLoopUntilIdle(); ASSERT_THAT(complete_status(), ::testing::Optional(PW_STATUS_OK)); const StaticByteBuffer acl_packet_0(0x00, 0x01, 0x02, 0x03); controller()->SendAclData(acl_packet_0.subspan()); RunLoopUntilIdle(); ASSERT_EQ(hci_server()->acl_packets_received().size(), 1u); EXPECT_THAT(hci_server()->acl_packets_received()[0], BufferEq(acl_packet_0)); const StaticByteBuffer acl_packet_1(0x04, 0x05, 0x06, 0x07); controller()->SendAclData(acl_packet_1.subspan()); RunLoopUntilIdle(); ASSERT_EQ(hci_server()->acl_packets_received().size(), 2u); EXPECT_THAT(hci_server()->acl_packets_received()[1], BufferEq(acl_packet_1)); std::vector received_acl; controller()->SetReceiveAclFunction([&](pw::span buffer) { received_acl.emplace_back(BufferView(buffer.data(), buffer.size())); }); hci_server()->SendAcl(acl_packet_0.view()); RunLoopUntilIdle(); ASSERT_EQ(received_acl.size(), 1u); EXPECT_THAT(received_acl[0], BufferEq(acl_packet_0)); EXPECT_EQ(hci_server()->acks_received(), 1u); hci_server()->SendAcl(acl_packet_1.view()); RunLoopUntilIdle(); ASSERT_EQ(received_acl.size(), 2u); EXPECT_THAT(received_acl[1], BufferEq(acl_packet_1)); EXPECT_EQ(hci_server()->acks_received(), 2u); std::optional close_status; controller()->Close([&](pw::Status status) { close_status = status; }); ASSERT_TRUE(close_status.has_value()); EXPECT_EQ(close_status.value(), PW_STATUS_OK); } TEST_F(FidlControllerTest, SendCommandsAndReceiveEvents) { RETURN_IF_FATAL(InitializeController()); RunLoopUntilIdle(); ASSERT_THAT(complete_status(), ::testing::Optional(PW_STATUS_OK)); const StaticByteBuffer packet_0(0x00, 0x01, 0x02, 0x03); controller()->SendCommand(packet_0.subspan()); RunLoopUntilIdle(); ASSERT_EQ(hci_server()->commands_received().size(), 1u); EXPECT_THAT(hci_server()->commands_received()[0], BufferEq(packet_0)); const StaticByteBuffer packet_1(0x04, 0x05, 0x06, 0x07); controller()->SendCommand(packet_1.subspan()); RunLoopUntilIdle(); ASSERT_EQ(hci_server()->commands_received().size(), 2u); EXPECT_THAT(hci_server()->commands_received()[1], BufferEq(packet_1)); std::vector events; controller()->SetEventFunction([&](pw::span buffer) { events.emplace_back(BufferView(buffer.data(), buffer.size())); }); hci_server()->SendEvent(packet_1.view()); RunLoopUntilIdle(); ASSERT_EQ(events.size(), 1u); EXPECT_THAT(events[0], BufferEq(packet_1)); EXPECT_EQ(hci_server()->acks_received(), 1u); hci_server()->SendEvent(packet_1.view()); RunLoopUntilIdle(); ASSERT_EQ(events.size(), 2u); EXPECT_THAT(events[1], BufferEq(packet_1)); EXPECT_EQ(hci_server()->acks_received(), 2u); std::optional close_status; controller()->Close([&](pw::Status status) { close_status = status; }); ASSERT_TRUE(close_status.has_value()); EXPECT_EQ(close_status.value(), PW_STATUS_OK); } TEST_F(FidlControllerTest, SendScoWhenNotConfigured) { RETURN_IF_FATAL(InitializeController()); RunLoopUntilIdle(); ASSERT_THAT(complete_status(), ::testing::Optional(PW_STATUS_OK)); const StaticByteBuffer packet_0(0x00, 0x01, 0x02, 0x03); controller()->SendScoData(packet_0.subspan()); RunLoopUntilIdle(); ASSERT_EQ(hci_server()->sco_packets_received().size(), 0u); ASSERT_THAT(controller_error(), ::testing::Optional(PW_STATUS_UNKNOWN)); } TEST_F(FidlControllerTest, SendAndReceiveSco) { RETURN_IF_FATAL(InitializeController()); RunLoopUntilIdle(); ASSERT_THAT(complete_status(), ::testing::Optional(PW_STATUS_OK)); int controller_cb_count = 0; controller()->ConfigureSco(pw::bluetooth::Controller::ScoCodingFormat::kCvsd, pw::bluetooth::Controller::ScoEncoding::k8Bits, pw::bluetooth::Controller::ScoSampleRate::k8Khz, [&controller_cb_count](pw::Status status) { controller_cb_count++; EXPECT_EQ(status, PW_STATUS_OK); }); EXPECT_EQ(controller_cb_count, 1); const StaticByteBuffer packet_0(0x00, 0x01, 0x02, 0x03); controller()->SendScoData(packet_0.subspan()); RunLoopUntilIdle(); ASSERT_EQ(hci_server()->sco_packets_received().size(), 1u); EXPECT_THAT(hci_server()->sco_packets_received()[0], BufferEq(packet_0)); const StaticByteBuffer packet_1(0x04, 0x05, 0x06, 0x07); controller()->SendScoData(packet_1.subspan()); RunLoopUntilIdle(); ASSERT_EQ(hci_server()->sco_packets_received().size(), 2u); EXPECT_THAT(hci_server()->sco_packets_received()[1], BufferEq(packet_1)); std::vector received_sco; controller()->SetReceiveScoFunction([&](pw::span buffer) { received_sco.emplace_back(BufferView(buffer.data(), buffer.size())); }); hci_server()->SendSco(packet_1.view()); RunLoopUntilIdle(); ASSERT_EQ(received_sco.size(), 1u); EXPECT_THAT(received_sco[0], BufferEq(packet_1)); EXPECT_EQ(hci_server()->sco_acks_received(), 1u); EXPECT_EQ(hci_server()->acks_received(), 0u); hci_server()->SendSco(packet_1.view()); RunLoopUntilIdle(); ASSERT_EQ(received_sco.size(), 2u); EXPECT_THAT(received_sco[1], BufferEq(packet_1)); EXPECT_EQ(hci_server()->sco_acks_received(), 2u); std::optional close_status; controller()->Close([&](pw::Status status) { close_status = status; }); ASSERT_TRUE(close_status.has_value()); EXPECT_EQ(close_status.value(), PW_STATUS_OK); } TEST_F(FidlControllerTest, SendAndReceiveIso) { RETURN_IF_FATAL(InitializeController()); RunLoopUntilIdle(); ASSERT_THAT(complete_status(), ::testing::Optional(PW_STATUS_OK)); const StaticByteBuffer iso_packet_0(0x00, 0x01, 0x02, 0x03); controller()->SendIsoData(iso_packet_0.subspan()); RunLoopUntilIdle(); ASSERT_EQ(hci_server()->iso_packets_received().size(), 1u); EXPECT_THAT(hci_server()->iso_packets_received()[0], BufferEq(iso_packet_0)); const StaticByteBuffer iso_packet_1(0x04, 0x05, 0x06, 0x07); controller()->SendIsoData(iso_packet_1.subspan()); RunLoopUntilIdle(); ASSERT_EQ(hci_server()->iso_packets_received().size(), 2u); EXPECT_THAT(hci_server()->iso_packets_received()[1], BufferEq(iso_packet_1)); std::vector received_iso; controller()->SetReceiveIsoFunction([&](pw::span buffer) { received_iso.emplace_back(BufferView(buffer.data(), buffer.size())); }); hci_server()->SendIso(iso_packet_0.view()); RunLoopUntilIdle(); ASSERT_EQ(received_iso.size(), 1u); EXPECT_THAT(received_iso[0], BufferEq(iso_packet_0)); EXPECT_EQ(hci_server()->acks_received(), 1u); hci_server()->SendIso(iso_packet_1.view()); RunLoopUntilIdle(); ASSERT_EQ(received_iso.size(), 2u); EXPECT_THAT(received_iso[1], BufferEq(iso_packet_1)); EXPECT_EQ(hci_server()->acks_received(), 2u); std::optional close_status; controller()->Close([&](pw::Status status) { close_status = status; }); ASSERT_TRUE(close_status.has_value()); EXPECT_EQ(close_status.value(), PW_STATUS_OK); } TEST_F(FidlControllerTest, ConfigureScoWithFormatCvsdEncoding8BitsSampleRate8Khz) { RETURN_IF_FATAL(InitializeController()); RunLoopUntilIdle(); ASSERT_THAT(complete_status(), ::testing::Optional(PW_STATUS_OK)); int device_cb_count = 0; hci_server()->set_check_configure_sco( [&device_cb_count](fhbt::ScoCodingFormat format, fhbt::ScoEncoding encoding, fhbt::ScoSampleRate rate) mutable { device_cb_count++; EXPECT_EQ(format, fhbt::ScoCodingFormat::kCvsd); EXPECT_EQ(encoding, fhbt::ScoEncoding::kBits8); EXPECT_EQ(rate, fhbt::ScoSampleRate::kKhz8); }); int controller_cb_count = 0; controller()->ConfigureSco(pw::bluetooth::Controller::ScoCodingFormat::kCvsd, pw::bluetooth::Controller::ScoEncoding::k8Bits, pw::bluetooth::Controller::ScoSampleRate::k8Khz, [&controller_cb_count](pw::Status status) { controller_cb_count++; EXPECT_EQ(status, PW_STATUS_OK); }); EXPECT_EQ(device_cb_count, 0); EXPECT_EQ(controller_cb_count, 1); RunLoopUntilIdle(); EXPECT_EQ(controller_cb_count, 1); EXPECT_EQ(device_cb_count, 1); } TEST_F(FidlControllerTest, ConfigureScoWithFormatCvsdEncoding16BitsSampleRate8Khz) { RETURN_IF_FATAL(InitializeController()); RunLoopUntilIdle(); ASSERT_THAT(complete_status(), ::testing::Optional(PW_STATUS_OK)); hci_server()->set_check_configure_sco([](fhbt::ScoCodingFormat format, fhbt::ScoEncoding encoding, fhbt::ScoSampleRate rate) { EXPECT_EQ(format, fhbt::ScoCodingFormat::kCvsd); EXPECT_EQ(encoding, fhbt::ScoEncoding::kBits16); EXPECT_EQ(rate, fhbt::ScoSampleRate::kKhz8); }); int config_cb_count = 0; controller()->ConfigureSco(pw::bluetooth::Controller::ScoCodingFormat::kCvsd, pw::bluetooth::Controller::ScoEncoding::k16Bits, pw::bluetooth::Controller::ScoSampleRate::k8Khz, [&](pw::Status status) { config_cb_count++; EXPECT_EQ(status, PW_STATUS_OK); }); RunLoopUntilIdle(); EXPECT_EQ(config_cb_count, 1); } TEST_F(FidlControllerTest, ConfigureScoWithFormatCvsdEncoding16BitsSampleRate16Khz) { RETURN_IF_FATAL(InitializeController()); RunLoopUntilIdle(); ASSERT_THAT(complete_status(), ::testing::Optional(PW_STATUS_OK)); hci_server()->set_check_configure_sco([](fhbt::ScoCodingFormat format, fhbt::ScoEncoding encoding, fhbt::ScoSampleRate rate) { EXPECT_EQ(format, fhbt::ScoCodingFormat::kCvsd); EXPECT_EQ(encoding, fhbt::ScoEncoding::kBits16); EXPECT_EQ(rate, fhbt::ScoSampleRate::kKhz16); }); int config_cb_count = 0; controller()->ConfigureSco(pw::bluetooth::Controller::ScoCodingFormat::kCvsd, pw::bluetooth::Controller::ScoEncoding::k16Bits, pw::bluetooth::Controller::ScoSampleRate::k16Khz, [&](pw::Status status) { config_cb_count++; EXPECT_EQ(status, PW_STATUS_OK); }); RunLoopUntilIdle(); EXPECT_EQ(config_cb_count, 1); } TEST_F(FidlControllerTest, ConfigureScoWithFormatMsbcEncoding16BitsSampleRate16Khz) { RETURN_IF_FATAL(InitializeController()); RunLoopUntilIdle(); ASSERT_THAT(complete_status(), ::testing::Optional(PW_STATUS_OK)); hci_server()->set_check_configure_sco([](fhbt::ScoCodingFormat format, fhbt::ScoEncoding encoding, fhbt::ScoSampleRate rate) { EXPECT_EQ(format, fhbt::ScoCodingFormat::kMsbc); EXPECT_EQ(encoding, fhbt::ScoEncoding::kBits16); EXPECT_EQ(rate, fhbt::ScoSampleRate::kKhz16); }); int config_cb_count = 0; controller()->ConfigureSco(pw::bluetooth::Controller::ScoCodingFormat::kMsbc, pw::bluetooth::Controller::ScoEncoding::k16Bits, pw::bluetooth::Controller::ScoSampleRate::k16Khz, [&](pw::Status status) { config_cb_count++; EXPECT_EQ(status, PW_STATUS_OK); }); RunLoopUntilIdle(); EXPECT_EQ(config_cb_count, 1); } TEST_F(FidlControllerTest, ConfigureAndResetScoTwice) { RETURN_IF_FATAL(InitializeController()); RunLoopUntilIdle(); ASSERT_THAT(complete_status(), ::testing::Optional(PW_STATUS_OK)); int device_cb_count = 0; hci_server()->set_reset_sco_callback( [&device_cb_count]() { device_cb_count++; }); int config_cb_count_0 = 0; controller()->ConfigureSco(pw::bluetooth::Controller::ScoCodingFormat::kMsbc, pw::bluetooth::Controller::ScoEncoding::k16Bits, pw::bluetooth::Controller::ScoSampleRate::k16Khz, [&](pw::Status status) { config_cb_count_0++; EXPECT_EQ(status, PW_STATUS_OK); }); EXPECT_EQ(config_cb_count_0, 1); int reset_cb_count_0 = 0; controller()->ResetSco([&](pw::Status status) { reset_cb_count_0++; EXPECT_EQ(status, PW_STATUS_OK); }); EXPECT_EQ(device_cb_count, 0); EXPECT_EQ(reset_cb_count_0, 0); RunLoopUntilIdle(); EXPECT_EQ(device_cb_count, 1); EXPECT_EQ(reset_cb_count_0, 1); int config_cb_count_1 = 0; controller()->ConfigureSco(pw::bluetooth::Controller::ScoCodingFormat::kMsbc, pw::bluetooth::Controller::ScoEncoding::k16Bits, pw::bluetooth::Controller::ScoSampleRate::k16Khz, [&](pw::Status status) { config_cb_count_1++; EXPECT_EQ(status, PW_STATUS_OK) << status.str(); }); EXPECT_EQ(config_cb_count_1, 1); int reset_cb_count_1 = 0; controller()->ResetSco([&](pw::Status status) { reset_cb_count_1++; EXPECT_EQ(status, PW_STATUS_OK); }); EXPECT_EQ(reset_cb_count_1, 0); RunLoopUntilIdle(); EXPECT_EQ(device_cb_count, 2); EXPECT_EQ(reset_cb_count_0, 1); EXPECT_EQ(reset_cb_count_1, 1); } TEST_F(FidlControllerTest, CloseUnbindsHciProtocol) { RETURN_IF_FATAL(InitializeController()); RunLoopUntilIdle(); ASSERT_THAT(complete_status(), ::testing::Optional(PW_STATUS_OK)); std::optional close_status; controller()->Close([&](pw::Status status) { close_status = status; }); RunLoopUntilIdle(); ASSERT_TRUE(close_status.has_value()); EXPECT_EQ(close_status.value(), PW_STATUS_OK); EXPECT_FALSE(hci_server()->bound()); } TEST_F(FidlControllerTest, HciServerClosesProtocol) { RETURN_IF_FATAL(InitializeController()); RunLoopUntilIdle(); ASSERT_THAT(complete_status(), ::testing::Optional(PW_STATUS_OK)); hci_server()->Unbind(); RunLoopUntilIdle(); ASSERT_THAT(controller_error(), ::testing::Optional(pw::Status::Unavailable())); } TEST_F(FidlControllerTest, VendorGetFeatures) { RETURN_IF_FATAL(InitializeController()); RunLoopUntilIdle(); ASSERT_THAT(complete_status(), ::testing::Optional(PW_STATUS_OK)); std::optional features; controller()->GetFeatures( [&](FidlController::FeaturesBits bits) { features = bits; }); RunLoopUntilIdle(); ASSERT_TRUE(features.has_value()); EXPECT_TRUE(*features & FidlController::FeaturesBits::kSetAclPriorityCommand); std::optional close_status; controller()->Close([&](pw::Status status) { close_status = status; }); ASSERT_TRUE(close_status.has_value()); EXPECT_EQ(close_status.value(), PW_STATUS_OK); } TEST_F(FidlControllerTest, VendorEncodeSetAclPriorityCommandNormal) { RETURN_IF_FATAL(InitializeController()); RunLoopUntilIdle(); ASSERT_THAT(complete_status(), ::testing::Optional(PW_STATUS_OK)); pw::bluetooth::SetAclPriorityCommandParameters params; params.connection_handle = kConnectionHandle; params.priority = pw::bluetooth::AclPriority::kNormal; std::optional buffer; controller()->EncodeVendorCommand( params, [&](pw::Result> result) { ASSERT_TRUE(result.ok()); buffer.emplace( BufferView(result.value().data(), result.value().size())); }); RunLoopUntilIdle(); ASSERT_TRUE(buffer); EXPECT_THAT(*buffer, BufferEq(kSetAclPriorityNormalCommand)); } TEST_F(FidlControllerTest, VendorEncodeSetAclPriorityCommandSink) { RETURN_IF_FATAL(InitializeController()); RunLoopUntilIdle(); ASSERT_THAT(complete_status(), ::testing::Optional(PW_STATUS_OK)); pw::bluetooth::SetAclPriorityCommandParameters params; params.connection_handle = kConnectionHandle; params.priority = pw::bluetooth::AclPriority::kSink; std::optional buffer; controller()->EncodeVendorCommand( params, [&](pw::Result> result) { ASSERT_TRUE(result.ok()); buffer.emplace( BufferView(result.value().data(), result.value().size())); }); RunLoopUntilIdle(); ASSERT_TRUE(buffer); EXPECT_THAT(*buffer, BufferEq(kSetAclPrioritySinkCommand)); } TEST_F(FidlControllerTest, VendorEncodeSetAclPriorityCommandSource) { RETURN_IF_FATAL(InitializeController()); RunLoopUntilIdle(); ASSERT_THAT(complete_status(), ::testing::Optional(PW_STATUS_OK)); pw::bluetooth::SetAclPriorityCommandParameters params; params.connection_handle = kConnectionHandle; params.priority = pw::bluetooth::AclPriority::kSource; std::optional buffer; controller()->EncodeVendorCommand( params, [&](pw::Result> result) { ASSERT_TRUE(result.ok()); buffer.emplace( BufferView(result.value().data(), result.value().size())); }); RunLoopUntilIdle(); ASSERT_TRUE(buffer); EXPECT_THAT(*buffer, BufferEq(kSetAclPrioritySourceCommand)); } TEST_F(FidlControllerTest, VendorServerClosesChannelBeforeOpenHci) { RETURN_IF_FATAL(InitializeController()); ASSERT_THAT(complete_status(), std::nullopt); ASSERT_THAT(controller_error(), std::nullopt); vendor_server()->Unbind(); RunLoopUntilIdle(); ASSERT_THAT(complete_status(), ::testing::Optional(pw::Status::Unavailable())); ASSERT_THAT(controller_error(), std::nullopt); } TEST_F(FidlControllerTest, VendorServerClosesProtocolBeforeInitialize) { vendor_server()->Unbind(); RunLoopUntilIdle(); RETURN_IF_FATAL(InitializeController()); RunLoopUntilIdle(); ASSERT_THAT(complete_status(), ::testing::Optional(pw::Status::Unavailable())); ASSERT_THAT(controller_error(), std::nullopt); } TEST_F(FidlControllerTest, VendorOpenHciError) { // Make OpenHci() return error during controller initialization vendor_server()->set_open_hci_error(true); RETURN_IF_FATAL(InitializeController()); RunLoopUntilIdle(); ASSERT_THAT(complete_status(), ::testing::Optional(pw::Status::Internal())); ASSERT_THAT(controller_error(), std::nullopt); } TEST_F(FidlControllerTest, VendorServerClosesProtocol) { RETURN_IF_FATAL(InitializeController()); RunLoopUntilIdle(); ASSERT_THAT(complete_status(), ::testing::Optional(PW_STATUS_OK)); vendor_server()->Unbind(); RunLoopUntilIdle(); ASSERT_THAT(controller_error(), ::testing::Optional(pw::Status::Unavailable())); } // Attempting to send a command once the HciTransport protocol has been closed // from the server end may trigger the driver to terminate. Verify that a clean // shutdown will still occur. TEST_F(FidlControllerTest, EventClosesDriver) { RETURN_IF_FATAL(InitializeController()); RunLoopUntilIdle(); ASSERT_THAT(complete_status(), ::testing::Optional(PW_STATUS_OK)); const StaticByteBuffer kCommandPacket(0x00, 0x01, 0x02, 0x03); const StaticByteBuffer kEventPacket(0x04, 0x05, 0x06, 0x07); controller()->SetEventFunction([&](pw::span /* buffer */) { hci_server()->Unbind(); controller()->SendCommand(kCommandPacket.subspan()); }); hci_server()->SendEvent(kEventPacket.view()); RunLoopUntilIdle(); } TEST_F(FidlControllerTest, ScoServerClosesProtocolUnexpectedly) { RETURN_IF_FATAL(InitializeController()); RunLoopUntilIdle(); ASSERT_THAT(complete_status(), ::testing::Optional(PW_STATUS_OK)); int config_cb_count = 0; controller()->ConfigureSco(pw::bluetooth::Controller::ScoCodingFormat::kMsbc, pw::bluetooth::Controller::ScoEncoding::k16Bits, pw::bluetooth::Controller::ScoSampleRate::k16Khz, [&](pw::Status status) { config_cb_count++; EXPECT_EQ(status, PW_STATUS_OK); }); RunLoopUntilIdle(); EXPECT_EQ(config_cb_count, 1); EXPECT_TRUE(hci_server()->UnbindSco()); RunLoopUntilIdle(); EXPECT_THAT(controller_error(), ::testing::Optional(pw::Status::Unknown())); } TEST_F(FidlControllerTest, ConfigureScoAlreadyConfigured) { RETURN_IF_FATAL(InitializeController()); RunLoopUntilIdle(); ASSERT_THAT(complete_status(), ::testing::Optional(PW_STATUS_OK)); int config_cb_count_0 = 0; controller()->ConfigureSco(pw::bluetooth::Controller::ScoCodingFormat::kMsbc, pw::bluetooth::Controller::ScoEncoding::k16Bits, pw::bluetooth::Controller::ScoSampleRate::k16Khz, [&](pw::Status status) { config_cb_count_0++; EXPECT_EQ(status, PW_STATUS_OK); }); int config_cb_count_1 = 0; controller()->ConfigureSco(pw::bluetooth::Controller::ScoCodingFormat::kMsbc, pw::bluetooth::Controller::ScoEncoding::k16Bits, pw::bluetooth::Controller::ScoSampleRate::k16Khz, [&](pw::Status status) { config_cb_count_1++; EXPECT_EQ(status, PW_STATUS_ALREADY_EXISTS); }); EXPECT_EQ(config_cb_count_0, 1); EXPECT_EQ(config_cb_count_1, 1); RunLoopUntilIdle(); EXPECT_EQ(config_cb_count_0, 1); EXPECT_EQ(config_cb_count_1, 1); } TEST_F(FidlControllerTest, ResetScoWhenNotConfiguredFails) { RETURN_IF_FATAL(InitializeController()); RunLoopUntilIdle(); ASSERT_THAT(complete_status(), ::testing::Optional(PW_STATUS_OK)); int device_cb_count = 0; hci_server()->set_reset_sco_callback( [&device_cb_count]() { device_cb_count++; }); int reset_cb_count = 0; controller()->ResetSco([&](pw::Status status) { reset_cb_count++; EXPECT_EQ(status, PW_STATUS_FAILED_PRECONDITION); }); EXPECT_EQ(device_cb_count, 0); EXPECT_EQ(reset_cb_count, 1); RunLoopUntilIdle(); EXPECT_EQ(device_cb_count, 0); EXPECT_EQ(reset_cb_count, 1); } TEST_F(FidlControllerTest, ResetScoAlreadyPending) { RETURN_IF_FATAL(InitializeController()); RunLoopUntilIdle(); ASSERT_THAT(complete_status(), ::testing::Optional(PW_STATUS_OK)); int device_cb_count = 0; hci_server()->set_reset_sco_callback( [&device_cb_count]() { device_cb_count++; }); int config_cb_count_0 = 0; controller()->ConfigureSco(pw::bluetooth::Controller::ScoCodingFormat::kMsbc, pw::bluetooth::Controller::ScoEncoding::k16Bits, pw::bluetooth::Controller::ScoSampleRate::k16Khz, [&](pw::Status status) { config_cb_count_0++; EXPECT_EQ(status, PW_STATUS_OK); }); EXPECT_EQ(config_cb_count_0, 1); int reset_cb_count_0 = 0; controller()->ResetSco([&](pw::Status status) { reset_cb_count_0++; EXPECT_EQ(status, PW_STATUS_OK); }); int reset_cb_count_1 = 0; controller()->ResetSco([&](pw::Status status) { reset_cb_count_1++; EXPECT_EQ(status, PW_STATUS_ALREADY_EXISTS); }); EXPECT_EQ(device_cb_count, 0); EXPECT_EQ(reset_cb_count_0, 0); EXPECT_EQ(reset_cb_count_1, 1); RunLoopUntilIdle(); EXPECT_EQ(device_cb_count, 1); EXPECT_EQ(reset_cb_count_0, 1); EXPECT_EQ(reset_cb_count_1, 1); } } // namespace bt::controllers