blob: 69a9f839a80a89ad531f39db0ad0409ddce27ac3 [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 "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci/command_handler.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/error.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/controller_test.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/mock_controller.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/test_packets.h"
#include <pw_bluetooth/hci_common.emb.h>
#include <pw_bluetooth/hci_test.emb.h>
namespace bt::hci {
namespace {
constexpr hci_spec::OpCode kOpCode(hci_spec::kInquiry);
constexpr uint8_t kTestEventParam = 3u;
template <bool DecodeSucceeds>
struct TestEvent {
uint8_t test_param;
static fit::result<bt::Error<>, TestEvent> Decode(const EventPacket& packet) {
if (!DecodeSucceeds) {
return fit::error(bt::Error(HostError::kPacketMalformed));
}
return fit::ok(TestEvent{.test_param = kTestEventParam});
}
static constexpr hci_spec::EventCode kEventCode =
hci_spec::kInquiryCompleteEventCode;
};
using DecodableEvent = TestEvent<true>;
using UndecodableEvent = TestEvent<false>;
DynamicByteBuffer MakeTestEventPacket(
pw::bluetooth::emboss::StatusCode status =
pw::bluetooth::emboss::StatusCode::SUCCESS) {
return DynamicByteBuffer(StaticByteBuffer(DecodableEvent::kEventCode,
0x01, // parameters_total_size
status));
}
template <bool DecodeSucceeds>
struct TestCommandCompleteEvent {
uint8_t test_param;
static fit::result<bt::Error<>, TestCommandCompleteEvent> Decode(
const EventPacket& packet) {
if (!DecodeSucceeds) {
return fit::error(bt::Error(HostError::kPacketMalformed));
}
return fit::ok(TestCommandCompleteEvent{.test_param = kTestEventParam});
}
static constexpr hci_spec::EventCode kEventCode =
hci_spec::kCommandCompleteEventCode;
};
using DecodableCommandCompleteEvent = TestCommandCompleteEvent<true>;
using UndecodableCommandCompleteEvent = TestCommandCompleteEvent<false>;
constexpr uint8_t kEncodedTestCommandParam = 2u;
template <typename CompleteEventT>
struct TestCommand {
uint8_t test_param;
EmbossCommandPacket Encode() {
auto packet = EmbossCommandPacket::New<
pw::bluetooth::emboss::TestCommandPacketWriter>(kOpCode);
packet.view_t().payload().Write(kEncodedTestCommandParam);
return packet;
}
static hci_spec::OpCode opcode() { return kOpCode; }
using EventT = CompleteEventT;
};
const TestCommand<DecodableEvent> kTestCommandWithAsyncEvent{.test_param = 1u};
const TestCommand<DecodableCommandCompleteEvent>
kTestCommandWithCommandCompleteEvent{.test_param = 1u};
const TestCommand<UndecodableCommandCompleteEvent>
kTestCommandWithUndecodableCommandCompleteEvent{.test_param = 1u};
const StaticByteBuffer kTestCommandPacket(LowerBits(kOpCode),
UpperBits(kOpCode),
0x01, // param length
kEncodedTestCommandParam);
using TestingBase =
bt::testing::FakeDispatcherControllerTest<bt::testing::MockController>;
class CommandHandlerTest : public TestingBase {
public:
void SetUp() override {
TestingBase::SetUp();
handler_.emplace(cmd_channel()->AsWeakPtr());
}
CommandHandler& handler() { return handler_.value(); }
private:
std::optional<CommandHandler> handler_;
};
TEST_F(CommandHandlerTest, SuccessfulSendCommandWithSyncEvent) {
const auto kEventPacket = bt::testing::CommandCompletePacket(
kOpCode, pw::bluetooth::emboss::StatusCode::SUCCESS);
EXPECT_CMD_PACKET_OUT(test_device(), kTestCommandPacket, &kEventPacket);
std::optional<DecodableCommandCompleteEvent> event;
handler().SendCommand(kTestCommandWithCommandCompleteEvent,
[&event](auto result) {
ASSERT_EQ(fit::ok(), result);
event = result.value();
});
RunUntilIdle();
ASSERT_TRUE(event.has_value());
EXPECT_EQ(event->test_param, kTestEventParam);
}
TEST_F(CommandHandlerTest, SendCommandReceiveFailEvent) {
const auto kEventPacket = bt::testing::CommandCompletePacket(
kOpCode, pw::bluetooth::emboss::StatusCode::COMMAND_DISALLOWED);
EXPECT_CMD_PACKET_OUT(test_device(), kTestCommandPacket, &kEventPacket);
std::optional<hci::Error> error;
handler().SendCommand(kTestCommandWithCommandCompleteEvent,
[&error](auto result) {
ASSERT_TRUE(result.is_error());
error = std::move(result).error_value();
});
RunUntilIdle();
ASSERT_TRUE(error.has_value());
EXPECT_TRUE(error->is(pw::bluetooth::emboss::StatusCode::COMMAND_DISALLOWED));
}
TEST_F(CommandHandlerTest, SendCommandWithSyncEventFailsToDecode) {
const auto kEventPacket = bt::testing::CommandCompletePacket(
kOpCode, pw::bluetooth::emboss::StatusCode::SUCCESS);
EXPECT_CMD_PACKET_OUT(test_device(), kTestCommandPacket, &kEventPacket);
std::optional<hci::Error> error;
handler().SendCommand(kTestCommandWithUndecodableCommandCompleteEvent,
[&error](auto result) {
ASSERT_TRUE(result.is_error());
error = std::move(result).error_value();
});
RunUntilIdle();
ASSERT_TRUE(error.has_value());
EXPECT_TRUE(error->is(HostError::kPacketMalformed));
}
TEST_F(CommandHandlerTest, SuccessfulSendCommandWithAsyncEvent) {
const auto kTestEventPacket = MakeTestEventPacket();
const auto kStatusEventPacket = bt::testing::CommandStatusPacket(
kOpCode, pw::bluetooth::emboss::StatusCode::SUCCESS);
EXPECT_CMD_PACKET_OUT(test_device(),
kTestCommandPacket,
&kStatusEventPacket,
&kTestEventPacket);
std::optional<DecodableEvent> event;
size_t cb_count = 0;
handler().SendCommand(kTestCommandWithAsyncEvent,
[&event, &cb_count](auto result) {
ASSERT_EQ(fit::ok(), result);
event = result.value();
cb_count++;
});
RunUntilIdle();
ASSERT_EQ(cb_count, 1u);
ASSERT_TRUE(event.has_value());
EXPECT_EQ(event->test_param, kTestEventParam);
}
TEST_F(CommandHandlerTest, AddEventHandlerSuccess) {
std::optional<DecodableEvent> event;
size_t cb_count = 0;
handler().AddEventHandler<DecodableEvent>([&event, &cb_count](auto cb_event) {
cb_count++;
event = cb_event;
return CommandChannel::EventCallbackResult::kContinue;
});
test_device()->SendCommandChannelPacket(MakeTestEventPacket());
test_device()->SendCommandChannelPacket(MakeTestEventPacket());
RunUntilIdle();
EXPECT_EQ(cb_count, 2u);
ASSERT_TRUE(event.has_value());
EXPECT_EQ(event->test_param, kTestEventParam);
}
TEST_F(CommandHandlerTest, AddEventHandlerDecodeError) {
size_t cb_count = 0;
handler().AddEventHandler<UndecodableEvent>([&cb_count](auto cb_event) {
cb_count++;
return CommandChannel::EventCallbackResult::kContinue;
});
test_device()->SendCommandChannelPacket(MakeTestEventPacket());
test_device()->SendCommandChannelPacket(MakeTestEventPacket());
RunUntilIdle();
EXPECT_EQ(cb_count, 0u);
}
TEST_F(CommandHandlerTest, SendCommandFinishOnStatus) {
const auto kStatusEventPacket = bt::testing::CommandStatusPacket(
kOpCode, pw::bluetooth::emboss::StatusCode::SUCCESS);
EXPECT_CMD_PACKET_OUT(test_device(), kTestCommandPacket, &kStatusEventPacket);
size_t cb_count = 0;
handler().SendCommandFinishOnStatus(kTestCommandWithAsyncEvent,
[&cb_count](auto result) {
ASSERT_EQ(fit::ok(), result);
cb_count++;
});
RunUntilIdle();
ASSERT_EQ(cb_count, 1u);
}
} // namespace
} // namespace bt::hci