blob: 444ab3c3097818ea3f0a953bd8cf3132ec4ec5de [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 "command_handler.h"
#include <gtest/gtest.h>
#include "src/connectivity/bluetooth/core/bt-host/common/test_helpers.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/testing/test_packets.h"
namespace bt::hci {
namespace {
constexpr OpCode kOpCode(kInquiry);
constexpr uint8_t kTestEventParam = 3u;
template <bool DecodeSucceeds>
struct TestEvent {
uint8_t test_param;
static fit::result<TestEvent, HostError> Decode(const EventPacket& packet) {
if (!DecodeSucceeds) {
return fit::error(HostError::kPacketMalformed);
}
return fit::ok(TestEvent{.test_param = kTestEventParam});
}
static constexpr EventCode kEventCode = kInquiryCompleteEventCode;
};
using DecodableEvent = TestEvent<true>;
using UndecodableEvent = TestEvent<false>;
DynamicByteBuffer MakeTestEventPacket(StatusCode status = StatusCode::kSuccess) {
return DynamicByteBuffer(StaticByteBuffer(DecodableEvent::kEventCode,
0x01, // parameters_total_size
status));
}
template <bool DecodeSucceeds>
struct TestCommandCompleteEvent {
uint8_t test_param;
static fit::result<TestCommandCompleteEvent, HostError> Decode(const EventPacket& packet) {
if (!DecodeSucceeds) {
return fit::error(HostError::kPacketMalformed);
}
return fit::ok(TestCommandCompleteEvent{.test_param = kTestEventParam});
}
static constexpr EventCode kEventCode = kCommandCompleteEventCode;
};
using DecodableCommandCompleteEvent = TestCommandCompleteEvent<true>;
using UndecodableCommandCompleteEvent = TestCommandCompleteEvent<false>;
constexpr uint8_t kEncodedTestCommandParam = 2u;
template <typename CompleteEventT>
struct TestCommand {
uint8_t test_param;
std::unique_ptr<CommandPacket> Encode() {
auto packet = CommandPacket::New(kOpCode, sizeof(test_param));
auto* payload = packet->mutable_payload<decltype(test_param)>();
*payload = kEncodedTestCommandParam;
return packet;
}
static 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 auto kTestCommandPacket = StaticByteBuffer(LowerBits(kOpCode), UpperBits(kOpCode),
0x01, // param length
kEncodedTestCommandParam);
using TestingBase = bt::testing::ControllerTest<bt::testing::MockController>;
class HCI_CommandHandlerTest : public TestingBase {
public:
void SetUp() override {
TestingBase::SetUp();
StartTestDevice();
handler_.emplace(cmd_channel()->AsWeakPtr());
}
CommandHandler& handler() { return handler_.value(); }
private:
std::optional<CommandHandler> handler_;
};
TEST_F(HCI_CommandHandlerTest, SuccessfulSendCommandWithSyncEvent) {
const auto kEventPacket = testing::CommandCompletePacket(kOpCode, StatusCode::kSuccess);
EXPECT_CMD_PACKET_OUT(test_device(), kTestCommandPacket, &kEventPacket);
std::optional<DecodableCommandCompleteEvent> event;
handler().SendCommand(kTestCommandWithCommandCompleteEvent, [&event](auto result) {
ASSERT_TRUE(result.is_ok());
event = result.value();
});
RunLoopUntilIdle();
ASSERT_TRUE(event.has_value());
EXPECT_EQ(event->test_param, kTestEventParam);
}
TEST_F(HCI_CommandHandlerTest, SendCommandReceiveFailEvent) {
const auto kEventPacket = testing::CommandCompletePacket(kOpCode, StatusCode::kCommandDisallowed);
EXPECT_CMD_PACKET_OUT(test_device(), kTestCommandPacket, &kEventPacket);
std::optional<Status> status;
handler().SendCommand(kTestCommandWithCommandCompleteEvent, [&status](auto result) {
ASSERT_TRUE(result.is_error());
status = result.error();
});
RunLoopUntilIdle();
ASSERT_TRUE(status.has_value());
EXPECT_EQ(status.value(), Status(StatusCode::kCommandDisallowed));
}
TEST_F(HCI_CommandHandlerTest, SendCommandWithSyncEventFailsToDecode) {
const auto kEventPacket = testing::CommandCompletePacket(kOpCode, StatusCode::kSuccess);
EXPECT_CMD_PACKET_OUT(test_device(), kTestCommandPacket, &kEventPacket);
std::optional<Status> status;
handler().SendCommand(kTestCommandWithUndecodableCommandCompleteEvent, [&status](auto result) {
ASSERT_TRUE(result.is_error());
status = result.error();
});
RunLoopUntilIdle();
ASSERT_TRUE(status.has_value());
EXPECT_EQ(status.value(), Status(HostError::kPacketMalformed));
}
TEST_F(HCI_CommandHandlerTest, SuccessfulSendCommandWithAsyncEvent) {
const auto kTestEventPacket = MakeTestEventPacket();
const auto kStatusEventPacket = testing::CommandStatusPacket(kOpCode, StatusCode::kSuccess);
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_TRUE(result.is_ok());
event = result.value();
cb_count++;
});
RunLoopUntilIdle();
ASSERT_EQ(cb_count, 1u);
ASSERT_TRUE(event.has_value());
EXPECT_EQ(event->test_param, kTestEventParam);
}
TEST_F(HCI_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());
RunLoopUntilIdle();
EXPECT_EQ(cb_count, 2u);
ASSERT_TRUE(event.has_value());
EXPECT_EQ(event->test_param, kTestEventParam);
}
TEST_F(HCI_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());
RunLoopUntilIdle();
EXPECT_EQ(cb_count, 0u);
}
TEST_F(HCI_CommandHandlerTest, SendCommandFinishOnStatus) {
const auto kStatusEventPacket = testing::CommandStatusPacket(kOpCode, StatusCode::kSuccess);
EXPECT_CMD_PACKET_OUT(test_device(), kTestCommandPacket, &kStatusEventPacket);
size_t cb_count = 0;
handler().SendCommandFinishOnStatus(kTestCommandWithAsyncEvent, [&cb_count](auto result) {
ASSERT_TRUE(result.is_ok());
cb_count++;
});
RunLoopUntilIdle();
ASSERT_EQ(cb_count, 1u);
}
} // namespace
} // namespace bt::hci