1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_bluetooth_sapphire/internal/host/hci/command_handler.h"
16
17 #include <pw_bluetooth/hci_common.emb.h>
18 #include <pw_bluetooth/hci_test.emb.h>
19
20 #include "pw_bluetooth_sapphire/internal/host/common/error.h"
21 #include "pw_bluetooth_sapphire/internal/host/testing/controller_test.h"
22 #include "pw_bluetooth_sapphire/internal/host/testing/mock_controller.h"
23 #include "pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
24 #include "pw_bluetooth_sapphire/internal/host/testing/test_packets.h"
25
26 namespace bt::hci {
27
28 namespace {
29
30 constexpr hci_spec::OpCode kOpCode(hci_spec::kInquiry);
31 constexpr uint8_t kTestEventParam = 3u;
32
33 template <bool DecodeSucceeds>
34 struct TestEvent {
35 uint8_t test_param;
Decodebt::hci::__anon28f14fde0111::TestEvent36 static fit::result<bt::Error<>, TestEvent> Decode(const EventPacket& packet) {
37 if (!DecodeSucceeds) {
38 return fit::error(bt::Error(HostError::kPacketMalformed));
39 }
40
41 return fit::ok(TestEvent{.test_param = kTestEventParam});
42 }
43
44 static constexpr hci_spec::EventCode kEventCode =
45 hci_spec::kInquiryCompleteEventCode;
46 };
47 using DecodableEvent = TestEvent<true>;
48 using UndecodableEvent = TestEvent<false>;
49
MakeTestEventPacket(pw::bluetooth::emboss::StatusCode status=pw::bluetooth::emboss::StatusCode::SUCCESS)50 DynamicByteBuffer MakeTestEventPacket(
51 pw::bluetooth::emboss::StatusCode status =
52 pw::bluetooth::emboss::StatusCode::SUCCESS) {
53 return DynamicByteBuffer(StaticByteBuffer(DecodableEvent::kEventCode,
54 0x01, // parameters_total_size
55 status));
56 }
57
58 template <bool DecodeSucceeds>
59 struct TestCommandCompleteEvent {
60 uint8_t test_param;
61
Decodebt::hci::__anon28f14fde0111::TestCommandCompleteEvent62 static fit::result<bt::Error<>, TestCommandCompleteEvent> Decode(
63 const EventPacket& packet) {
64 if (!DecodeSucceeds) {
65 return fit::error(bt::Error(HostError::kPacketMalformed));
66 }
67
68 return fit::ok(TestCommandCompleteEvent{.test_param = kTestEventParam});
69 }
70
71 static constexpr hci_spec::EventCode kEventCode =
72 hci_spec::kCommandCompleteEventCode;
73 };
74 using DecodableCommandCompleteEvent = TestCommandCompleteEvent<true>;
75 using UndecodableCommandCompleteEvent = TestCommandCompleteEvent<false>;
76
77 constexpr uint8_t kEncodedTestCommandParam = 2u;
78
79 template <typename CompleteEventT>
80 struct TestCommand {
81 uint8_t test_param;
82
Encodebt::hci::__anon28f14fde0111::TestCommand83 EmbossCommandPacket Encode() {
84 auto packet = EmbossCommandPacket::New<
85 pw::bluetooth::emboss::TestCommandPacketWriter>(kOpCode);
86 packet.view_t().payload().Write(kEncodedTestCommandParam);
87 return packet;
88 }
89
opcodebt::hci::__anon28f14fde0111::TestCommand90 static hci_spec::OpCode opcode() { return kOpCode; }
91
92 using EventT = CompleteEventT;
93 };
94
95 const TestCommand<DecodableEvent> kTestCommandWithAsyncEvent{.test_param = 1u};
96 const TestCommand<DecodableCommandCompleteEvent>
97 kTestCommandWithCommandCompleteEvent{.test_param = 1u};
98 const TestCommand<UndecodableCommandCompleteEvent>
99 kTestCommandWithUndecodableCommandCompleteEvent{.test_param = 1u};
100
101 const StaticByteBuffer kTestCommandPacket(LowerBits(kOpCode),
102 UpperBits(kOpCode),
103 0x01, // param length
104 kEncodedTestCommandParam);
105
106 using TestingBase =
107 bt::testing::FakeDispatcherControllerTest<bt::testing::MockController>;
108 class CommandHandlerTest : public TestingBase {
109 public:
SetUp()110 void SetUp() override {
111 TestingBase::SetUp();
112
113 handler_.emplace(cmd_channel()->AsWeakPtr());
114 }
115
handler()116 CommandHandler& handler() { return handler_.value(); }
117
118 private:
119 std::optional<CommandHandler> handler_;
120 };
121
TEST_F(CommandHandlerTest,SuccessfulSendCommandWithSyncEvent)122 TEST_F(CommandHandlerTest, SuccessfulSendCommandWithSyncEvent) {
123 const auto kEventPacket = bt::testing::CommandCompletePacket(
124 kOpCode, pw::bluetooth::emboss::StatusCode::SUCCESS);
125 EXPECT_CMD_PACKET_OUT(test_device(), kTestCommandPacket, &kEventPacket);
126
127 std::optional<DecodableCommandCompleteEvent> event;
128 handler().SendCommand(kTestCommandWithCommandCompleteEvent,
129 [&event](auto result) {
130 ASSERT_EQ(fit::ok(), result);
131 event = result.value();
132 });
133
134 RunUntilIdle();
135 ASSERT_TRUE(event.has_value());
136 EXPECT_EQ(event->test_param, kTestEventParam);
137 }
138
TEST_F(CommandHandlerTest,SendCommandReceiveFailEvent)139 TEST_F(CommandHandlerTest, SendCommandReceiveFailEvent) {
140 const auto kEventPacket = bt::testing::CommandCompletePacket(
141 kOpCode, pw::bluetooth::emboss::StatusCode::COMMAND_DISALLOWED);
142 EXPECT_CMD_PACKET_OUT(test_device(), kTestCommandPacket, &kEventPacket);
143
144 std::optional<hci::Error> error;
145 handler().SendCommand(kTestCommandWithCommandCompleteEvent,
146 [&error](auto result) {
147 ASSERT_TRUE(result.is_error());
148 error = std::move(result).error_value();
149 });
150
151 RunUntilIdle();
152 ASSERT_TRUE(error.has_value());
153 EXPECT_TRUE(error->is(pw::bluetooth::emboss::StatusCode::COMMAND_DISALLOWED));
154 }
155
TEST_F(CommandHandlerTest,SendCommandWithSyncEventFailsToDecode)156 TEST_F(CommandHandlerTest, SendCommandWithSyncEventFailsToDecode) {
157 const auto kEventPacket = bt::testing::CommandCompletePacket(
158 kOpCode, pw::bluetooth::emboss::StatusCode::SUCCESS);
159 EXPECT_CMD_PACKET_OUT(test_device(), kTestCommandPacket, &kEventPacket);
160
161 std::optional<hci::Error> error;
162 handler().SendCommand(kTestCommandWithUndecodableCommandCompleteEvent,
163 [&error](auto result) {
164 ASSERT_TRUE(result.is_error());
165 error = std::move(result).error_value();
166 });
167
168 RunUntilIdle();
169 ASSERT_TRUE(error.has_value());
170 EXPECT_TRUE(error->is(HostError::kPacketMalformed));
171 }
172
TEST_F(CommandHandlerTest,SuccessfulSendCommandWithAsyncEvent)173 TEST_F(CommandHandlerTest, SuccessfulSendCommandWithAsyncEvent) {
174 const auto kTestEventPacket = MakeTestEventPacket();
175 const auto kStatusEventPacket = bt::testing::CommandStatusPacket(
176 kOpCode, pw::bluetooth::emboss::StatusCode::SUCCESS);
177 EXPECT_CMD_PACKET_OUT(test_device(),
178 kTestCommandPacket,
179 &kStatusEventPacket,
180 &kTestEventPacket);
181
182 std::optional<DecodableEvent> event;
183 size_t cb_count = 0;
184 handler().SendCommand(kTestCommandWithAsyncEvent,
185 [&event, &cb_count](auto result) {
186 ASSERT_EQ(fit::ok(), result);
187 event = result.value();
188 cb_count++;
189 });
190
191 RunUntilIdle();
192 ASSERT_EQ(cb_count, 1u);
193 ASSERT_TRUE(event.has_value());
194 EXPECT_EQ(event->test_param, kTestEventParam);
195 }
196
TEST_F(CommandHandlerTest,AddEventHandlerSuccess)197 TEST_F(CommandHandlerTest, AddEventHandlerSuccess) {
198 std::optional<DecodableEvent> event;
199 size_t cb_count = 0;
200 handler().AddEventHandler<DecodableEvent>([&event, &cb_count](auto cb_event) {
201 cb_count++;
202 event = cb_event;
203 return CommandChannel::EventCallbackResult::kContinue;
204 });
205 test_device()->SendCommandChannelPacket(MakeTestEventPacket());
206 test_device()->SendCommandChannelPacket(MakeTestEventPacket());
207 RunUntilIdle();
208 EXPECT_EQ(cb_count, 2u);
209 ASSERT_TRUE(event.has_value());
210 EXPECT_EQ(event->test_param, kTestEventParam);
211 }
212
TEST_F(CommandHandlerTest,AddEventHandlerDecodeError)213 TEST_F(CommandHandlerTest, AddEventHandlerDecodeError) {
214 size_t cb_count = 0;
215 handler().AddEventHandler<UndecodableEvent>([&cb_count](auto cb_event) {
216 cb_count++;
217 return CommandChannel::EventCallbackResult::kContinue;
218 });
219 test_device()->SendCommandChannelPacket(MakeTestEventPacket());
220 test_device()->SendCommandChannelPacket(MakeTestEventPacket());
221 RunUntilIdle();
222 EXPECT_EQ(cb_count, 0u);
223 }
224
TEST_F(CommandHandlerTest,SendCommandFinishOnStatus)225 TEST_F(CommandHandlerTest, SendCommandFinishOnStatus) {
226 const auto kStatusEventPacket = bt::testing::CommandStatusPacket(
227 kOpCode, pw::bluetooth::emboss::StatusCode::SUCCESS);
228 EXPECT_CMD_PACKET_OUT(test_device(), kTestCommandPacket, &kStatusEventPacket);
229
230 size_t cb_count = 0;
231 handler().SendCommandFinishOnStatus(kTestCommandWithAsyncEvent,
232 [&cb_count](auto result) {
233 ASSERT_EQ(fit::ok(), result);
234 cb_count++;
235 });
236
237 RunUntilIdle();
238 ASSERT_EQ(cb_count, 1u);
239 }
240
241 } // namespace
242 } // namespace bt::hci
243