• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/sequential_command_runner.h"
16 
17 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
18 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
19 #include "pw_bluetooth_sapphire/internal/host/testing/controller_test.h"
20 #include "pw_bluetooth_sapphire/internal/host/testing/mock_controller.h"
21 #include "pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
22 #include "pw_bluetooth_sapphire/internal/host/testing/test_packets.h"
23 
24 namespace bt::hci {
25 namespace {
26 
27 constexpr hci_spec::OpCode kTestOpCode = 0xFFFF;
28 constexpr hci_spec::OpCode kTestOpCode2 = 0xF00F;
29 
30 using bt::testing::CommandTransaction;
31 
32 using TestingBase =
33     bt::testing::FakeDispatcherControllerTest<bt::testing::MockController>;
34 
35 class SequentialCommandRunnerTest : public TestingBase {
36  public:
37   SequentialCommandRunnerTest() = default;
38   ~SequentialCommandRunnerTest() override = default;
39 };
40 
41 using HCI_SequentialCommandRunnerTest = SequentialCommandRunnerTest;
42 
TEST_F(SequentialCommandRunnerTest,SequentialCommandRunner)43 TEST_F(SequentialCommandRunnerTest, SequentialCommandRunner) {
44   // HCI command with custom opcode FFFF.
45   StaticByteBuffer command_bytes(0xFF, 0xFF, 0x00);
46 
47   StaticByteBuffer command_status_error_bytes(
48       hci_spec::kCommandStatusEventCode,
49       0x04,  // parameter_total_size (4 byte payload)
50       pw::bluetooth::emboss::StatusCode::HARDWARE_FAILURE,
51       1,
52       0xFF,
53       0xFF);
54 
55   StaticByteBuffer command_cmpl_error_bytes(
56       hci_spec::kCommandCompleteEventCode,
57       0x04,  // parameter_total_size (4 byte payload)
58       1,
59       0xFF,
60       0xFF,
61       pw::bluetooth::emboss::StatusCode::RESERVED_0);
62 
63   auto command_cmpl_success_bytes =
64       StaticByteBuffer(hci_spec::kCommandCompleteEventCode,
65                        0x04,  // parameter_total_size (4 byte payload)
66                        1,
67                        0xFF,
68                        0xFF,
69                        pw::bluetooth::emboss::StatusCode::SUCCESS);
70 
71   // Here we perform multiple test sequences where we queue up several  commands
72   // in each sequence. We expect each sequence to terminate differently after
73   // the following HCI transactions:
74   //
75   // Sequence 1 (HCI packets)
76   //    -> Command; <- error status
77   EXPECT_CMD_PACKET_OUT(
78       test_device(), command_bytes, &command_status_error_bytes);
79 
80   // Sequence 2 (HCI packets)
81   //    -> Command; <- error complete
82   EXPECT_CMD_PACKET_OUT(
83       test_device(), command_bytes, &command_cmpl_error_bytes);
84 
85   // Sequence 3 (HCI packets)
86   //    -> Command; <- success complete
87   //    -> Command; <- error complete
88   EXPECT_CMD_PACKET_OUT(
89       test_device(), command_bytes, &command_cmpl_success_bytes);
90   EXPECT_CMD_PACKET_OUT(
91       test_device(), command_bytes, &command_cmpl_error_bytes);
92 
93   // Sequence 4 (HCI packets)
94   //    -> Command; <- success complete
95   //    -> Command; <- success complete
96   EXPECT_CMD_PACKET_OUT(
97       test_device(), command_bytes, &command_cmpl_success_bytes);
98   EXPECT_CMD_PACKET_OUT(
99       test_device(), command_bytes, &command_cmpl_success_bytes);
100 
101   // Sequence 5 (HCI packets)
102   //    -> Command; <- success complete
103   //    -> Command; <- success complete
104   EXPECT_CMD_PACKET_OUT(
105       test_device(), command_bytes, &command_cmpl_success_bytes);
106   EXPECT_CMD_PACKET_OUT(
107       test_device(), command_bytes, &command_cmpl_success_bytes);
108 
109   Result<> status = fit::ok();
110   int status_cb_called = 0;
111   auto status_cb = [&](Result<> cb_status) {
112     status = cb_status;
113     status_cb_called++;
114   };
115 
116   int cb_called = 0;
117   auto cb = [&](const EventPacket& event) { cb_called++; };
118 
119   // Sequence 1 (test)
120   SequentialCommandRunner cmd_runner(cmd_channel()->AsWeakPtr());
121   EXPECT_FALSE(cmd_runner.HasQueuedCommands());
122 
123   cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode), cb);
124   cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode),
125                           cb);  // <-- Should not run
126 
127   EXPECT_TRUE(cmd_runner.IsReady());
128   EXPECT_TRUE(cmd_runner.HasQueuedCommands());
129 
130   cmd_runner.RunCommands(status_cb);
131   EXPECT_FALSE(cmd_runner.IsReady());
132   RunUntilIdle();
133   EXPECT_TRUE(cmd_runner.IsReady());
134   EXPECT_FALSE(cmd_runner.HasQueuedCommands());
135   EXPECT_EQ(1, cb_called);
136   EXPECT_EQ(1, status_cb_called);
137   EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::HARDWARE_FAILURE),
138             status);
139   cb_called = 0;
140 
141   // Sequence 2 (test)
142   cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode), cb);
143   cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode),
144                           cb);  // <-- Should not run
145 
146   EXPECT_TRUE(cmd_runner.IsReady());
147   EXPECT_TRUE(cmd_runner.HasQueuedCommands());
148 
149   cmd_runner.RunCommands(status_cb);
150   EXPECT_FALSE(cmd_runner.IsReady());
151   RunUntilIdle();
152   EXPECT_TRUE(cmd_runner.IsReady());
153   EXPECT_FALSE(cmd_runner.HasQueuedCommands());
154   EXPECT_EQ(1, cb_called);
155   EXPECT_EQ(2, status_cb_called);
156   EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::RESERVED_0), status);
157   cb_called = 0;
158 
159   // Sequence 3 (test)
160   cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode), cb);
161   cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode), cb);
162   cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode),
163                           cb);  // <-- Should not run
164 
165   EXPECT_TRUE(cmd_runner.IsReady());
166   EXPECT_TRUE(cmd_runner.HasQueuedCommands());
167 
168   cmd_runner.RunCommands(status_cb);
169   EXPECT_FALSE(cmd_runner.IsReady());
170   RunUntilIdle();
171   EXPECT_TRUE(cmd_runner.IsReady());
172   EXPECT_FALSE(cmd_runner.HasQueuedCommands());
173   EXPECT_EQ(2, cb_called);
174   EXPECT_EQ(3, status_cb_called);
175   EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::RESERVED_0), status);
176   cb_called = 0;
177 
178   // Sequence 4 (test)
179   cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode), cb);
180   cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode), cb);
181 
182   EXPECT_TRUE(cmd_runner.IsReady());
183   EXPECT_TRUE(cmd_runner.HasQueuedCommands());
184 
185   cmd_runner.RunCommands(status_cb);
186   EXPECT_FALSE(cmd_runner.IsReady());
187   RunUntilIdle();
188   EXPECT_TRUE(cmd_runner.IsReady());
189   EXPECT_FALSE(cmd_runner.HasQueuedCommands());
190   EXPECT_EQ(2, cb_called);
191   EXPECT_EQ(4, status_cb_called);
192   EXPECT_EQ(fit::ok(), status);
193   cb_called = 0;
194   status_cb_called = 0;
195 
196   // Sequence 5 (test) (no callback passed to QueueCommand)
197   cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode));
198   cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode));
199 
200   EXPECT_TRUE(cmd_runner.IsReady());
201   EXPECT_TRUE(cmd_runner.HasQueuedCommands());
202 
203   cmd_runner.RunCommands(status_cb);
204   EXPECT_FALSE(cmd_runner.IsReady());
205   RunUntilIdle();
206   EXPECT_TRUE(cmd_runner.IsReady());
207   EXPECT_FALSE(cmd_runner.HasQueuedCommands());
208   EXPECT_EQ(0, cb_called);
209   EXPECT_EQ(1, status_cb_called);
210   EXPECT_EQ(fit::ok(), status);
211 }
212 
TEST_F(SequentialCommandRunnerTest,SequentialCommandRunnerCancel)213 TEST_F(SequentialCommandRunnerTest, SequentialCommandRunnerCancel) {
214   StaticByteBuffer command_bytes(0xFF, 0xFF, 0x00);
215 
216   auto command_cmpl_error_bytes =
217       StaticByteBuffer(hci_spec::kCommandCompleteEventCode,
218                        0x04,  // parameter_total_size (4 byte payload)
219                        1,
220                        0xFF,
221                        0xFF,
222                        pw::bluetooth::emboss::StatusCode::HARDWARE_FAILURE);
223 
224   auto command_cmpl_success_bytes =
225       StaticByteBuffer(hci_spec::kCommandCompleteEventCode,
226                        0x04,  // parameter_total_size (4 byte payload)
227                        1,
228                        0xFF,
229                        0xFF,
230                        pw::bluetooth::emboss::StatusCode::SUCCESS);
231 
232   // Sequence 1
233   //   -> Command; <- success complete
234   EXPECT_CMD_PACKET_OUT(
235       test_device(), command_bytes, &command_cmpl_success_bytes);
236 
237   // Sequence 2
238   //   -> Command; <- success complete
239   EXPECT_CMD_PACKET_OUT(
240       test_device(), command_bytes, &command_cmpl_success_bytes);
241 
242   // Sequence 3
243   //   -> Command; <- success complete
244   //   -> Command; <- error complete
245   EXPECT_CMD_PACKET_OUT(
246       test_device(), command_bytes, &command_cmpl_success_bytes);
247   EXPECT_CMD_PACKET_OUT(
248       test_device(), command_bytes, &command_cmpl_error_bytes);
249 
250   Result<> status = fit::ok();
251   int status_cb_called = 0;
252   auto status_cb = [&](Result<> cb_status) {
253     status = cb_status;
254     status_cb_called++;
255   };
256 
257   int cb_called = 0;
258   auto cb = [&](const EventPacket& event) { cb_called++; };
259 
260   // Sequence 1: Sequence will be cancelled after the first command.
261   SequentialCommandRunner cmd_runner(cmd_channel()->AsWeakPtr());
262   cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode), cb);
263   cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode), cb);
264   EXPECT_TRUE(cmd_runner.IsReady());
265   EXPECT_TRUE(cmd_runner.HasQueuedCommands());
266 
267   // Call RunCommands() and cancel the sequence immediately. The
268   // first command will go out but no successive packets should be sent.
269   // The status callback should be invoked
270   // No command callbacks should be called.
271   cmd_runner.RunCommands(status_cb);
272   EXPECT_FALSE(cmd_runner.IsReady());
273   cmd_runner.Cancel();
274 
275   RunUntilIdle();
276   EXPECT_TRUE(cmd_runner.IsReady());
277   EXPECT_FALSE(cmd_runner.HasQueuedCommands());
278   EXPECT_EQ(0, cb_called);
279   EXPECT_EQ(1, status_cb_called);
280   EXPECT_EQ(ToResult(HostError::kCanceled), status);
281   cb_called = 0;
282   status_cb_called = 0;
283   status = fit::ok();
284 
285   // Sequence 2: Sequence will be cancelled after first command. This tests
286   // canceling a sequence from a CommandCompleteCallback.
287   cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode),
288                           [&](const EventPacket& event) {
289                             bt_log(TRACE, "hci-test", "callback called");
290                             cmd_runner.Cancel();
291                             EXPECT_TRUE(cmd_runner.IsReady());
292                             EXPECT_FALSE(cmd_runner.HasQueuedCommands());
293                           });
294   cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode),
295                           cb);  // <-- Should not run
296   EXPECT_TRUE(cmd_runner.IsReady());
297   EXPECT_TRUE(cmd_runner.HasQueuedCommands());
298 
299   cmd_runner.RunCommands(status_cb);
300   EXPECT_FALSE(cmd_runner.IsReady());
301 
302   // |status_cb| is expected to get called with kCanceled
303   RunUntilIdle();
304   EXPECT_TRUE(cmd_runner.IsReady());
305   EXPECT_FALSE(cmd_runner.HasQueuedCommands());
306 
307   EXPECT_EQ(0, cb_called);
308   EXPECT_EQ(1, status_cb_called);
309   EXPECT_EQ(ToResult(HostError::kCanceled), status);
310   cb_called = 0;
311   status_cb_called = 0;
312   status = fit::ok();
313 
314   // Sequence 3: Sequence will be cancelled after first command and immediately
315   // followed by a second command which will fail. This tests canceling a
316   // sequence and initiating a new one from a CommandCompleteCallback.
317   cmd_runner.QueueCommand(
318       CommandPacket::New(kTestOpCode), [&](const EventPacket& event) {
319         cmd_runner.Cancel();
320         EXPECT_TRUE(cmd_runner.IsReady());
321         EXPECT_FALSE(cmd_runner.HasQueuedCommands());
322 
323         EXPECT_EQ(1, status_cb_called);
324         EXPECT_EQ(ToResult(HostError::kCanceled), status);
325 
326         // Queue multiple commands (only one will execute since MockController
327         // will send back an error status).
328         cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode), cb);
329         cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode),
330                                 cb);  // <-- Should not run
331         cmd_runner.RunCommands(status_cb);
332       });
333   cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode),
334                           cb);  // <-- Should not run
335   EXPECT_TRUE(cmd_runner.IsReady());
336   EXPECT_TRUE(cmd_runner.HasQueuedCommands());
337 
338   cmd_runner.RunCommands(status_cb);
339   EXPECT_FALSE(cmd_runner.IsReady());
340 
341   RunUntilIdle();
342 
343   EXPECT_TRUE(cmd_runner.IsReady());
344   EXPECT_FALSE(cmd_runner.HasQueuedCommands());
345 
346   // The cb queued from inside the first callback should have been called.
347   EXPECT_EQ(1, cb_called);
348   // The result callback should have been called with the failure result.
349   EXPECT_EQ(2, status_cb_called);
350   EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::HARDWARE_FAILURE),
351             status);
352 }
353 
TEST_F(SequentialCommandRunnerTest,ParallelCommands)354 TEST_F(SequentialCommandRunnerTest, ParallelCommands) {
355   // Need to signal to the queue that we can run more than one command at once.
356   auto command_status_queue_increase =
357       StaticByteBuffer(hci_spec::kCommandStatusEventCode,
358                        0x04,  // parameter_total_size (4 byte payload)
359                        pw::bluetooth::emboss::StatusCode::SUCCESS,
360                        250,
361                        0x00,
362                        0x00);
363   // HCI command with custom opcode FFFF.
364   StaticByteBuffer command_bytes(0xFF, 0xFF, 0x00);
365   auto command_status_error_bytes =
366       StaticByteBuffer(hci_spec::kCommandStatusEventCode,
367                        0x04,  // parameter_total_size (4 byte payload)
368                        pw::bluetooth::emboss::StatusCode::HARDWARE_FAILURE,
369                        2,
370                        0xFF,
371                        0xFF);
372 
373   auto command_cmpl_error_bytes =
374       StaticByteBuffer(hci_spec::kCommandCompleteEventCode,
375                        0x04,  // parameter_total_size (4 byte payload)
376                        2,
377                        0xFF,
378                        0xFF,
379                        pw::bluetooth::emboss::StatusCode::RESERVED_0);
380 
381   auto command_cmpl_success_bytes =
382       StaticByteBuffer(hci_spec::kCommandCompleteEventCode,
383                        0x04,  // parameter_total_size (4 byte payload)
384                        2,
385                        0xFF,
386                        0xFF,
387                        pw::bluetooth::emboss::StatusCode::SUCCESS);
388 
389   // HCI command with custom opcode F00F.
390   StaticByteBuffer command2_bytes(0x0F, 0xF0, 0x00);
391   auto command2_status_error_bytes =
392       StaticByteBuffer(hci_spec::kCommandStatusEventCode,
393                        0x04,  // parameter_total_size (4 byte payload)
394                        pw::bluetooth::emboss::StatusCode::HARDWARE_FAILURE,
395                        2,
396                        0x0F,
397                        0xF0);
398 
399   auto command2_cmpl_error_bytes =
400       StaticByteBuffer(hci_spec::kCommandCompleteEventCode,
401                        0x04,  // parameter_total_size (4 byte payload)
402                        2,
403                        0x0F,
404                        0xF0,
405                        pw::bluetooth::emboss::StatusCode::RESERVED_0);
406 
407   auto command2_cmpl_success_bytes =
408       StaticByteBuffer(hci_spec::kCommandCompleteEventCode,
409                        0x04,  // parameter_total_size (4 byte payload)
410                        2,
411                        0x0F,
412                        0xF0,
413                        pw::bluetooth::emboss::StatusCode::SUCCESS);
414 
415   test_device()->SendCommandChannelPacket(command_status_queue_increase);
416 
417   // Parallel commands should all run before commands that require success.
418   // command and command2 are answered in opposite order because they should be
419   // sent simultaneously.
420   EXPECT_CMD_PACKET_OUT(test_device(), command_bytes, );
421   EXPECT_CMD_PACKET_OUT(test_device(), command2_bytes, );
422   EXPECT_CMD_PACKET_OUT(
423       test_device(), command_bytes, &command_cmpl_success_bytes);
424   EXPECT_CMD_PACKET_OUT(
425       test_device(), command_bytes, &command_cmpl_success_bytes);
426   EXPECT_CMD_PACKET_OUT(
427       test_device(), command_bytes, &command_cmpl_success_bytes);
428 
429   int cb_called = 0;
430   auto cb = [&](const hci::EventPacket&) { cb_called++; };
431 
432   int status_cb_called = 0;
433   Result<> status = ToResult(HostError::kFailed);
434   auto status_cb = [&](Result<> cb_status) {
435     status = cb_status;
436     status_cb_called++;
437   };
438 
439   SequentialCommandRunner cmd_runner(cmd_channel()->AsWeakPtr());
440 
441   cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode), cb, /*wait=*/false);
442   cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode2), cb, /*wait=*/false);
443   cmd_runner.QueueCommand(
444       CommandPacket::New(kTestOpCode),
445       [&](const hci::EventPacket&) {
446         EXPECT_EQ(2, cb_called);
447         cb_called++;
448       },
449       /*wait=*/true);
450   // We can also queue to the end of the queue without the last one being a
451   // wait.
452   cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode), cb, /*wait=*/false);
453   cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode), cb, /*wait=*/false);
454   cmd_runner.RunCommands(status_cb);
455   EXPECT_FALSE(cmd_runner.IsReady());
456 
457   RunUntilIdle();
458   // The first two commands should have been sent but no responses are back yet.
459   EXPECT_EQ(0, cb_called);
460 
461   // It should not matter if they get answered in opposite order.
462   test_device()->SendCommandChannelPacket(command2_cmpl_success_bytes);
463   test_device()->SendCommandChannelPacket(command_cmpl_success_bytes);
464   RunUntilIdle();
465 
466   EXPECT_EQ(5, cb_called);
467   EXPECT_EQ(fit::ok(), status);
468   EXPECT_EQ(1, status_cb_called);
469   cb_called = 0;
470   status_cb_called = 0;
471 
472   // If any simultaneous commands fail, the sequence fails and the command
473   // sequence is terminated.
474   EXPECT_CMD_PACKET_OUT(test_device(), command_bytes, );
475   EXPECT_CMD_PACKET_OUT(test_device(), command2_bytes, );
476 
477   int cb_0_called = 0;
478   auto cb_0 = [&](const hci::EventPacket&) { cb_0_called++; };
479   int cb_1_called = 0;
480   auto cb_1 = [&](const hci::EventPacket&) { cb_1_called++; };
481   int cb_2_called = 0;
482   auto cb_2 = [&](const hci::EventPacket&) { cb_2_called++; };
483   cmd_runner.QueueCommand(
484       CommandPacket::New(kTestOpCode), cb_0, /*wait=*/false);
485   cmd_runner.QueueCommand(
486       CommandPacket::New(kTestOpCode2), cb_1, /*wait=*/false);
487   cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode),
488                           cb_2);  // shouldn't run
489 
490   cmd_runner.RunCommands(status_cb);
491   EXPECT_FALSE(cmd_runner.IsReady());
492 
493   RunUntilIdle();
494   // The first two commands should have been sent but no responses are back yet.
495   EXPECT_EQ(0, cb_0_called);
496   EXPECT_EQ(0, cb_1_called);
497   EXPECT_EQ(0, cb_2_called);
498 
499   test_device()->SendCommandChannelPacket(command_status_error_bytes);
500   test_device()->SendCommandChannelPacket(command2_cmpl_success_bytes);
501   RunUntilIdle();
502 
503   // Only the first command's callback should be called, as further callbacks
504   // will be canceled due to the error status.
505   EXPECT_EQ(1, cb_0_called);
506   EXPECT_EQ(0, cb_1_called);
507   EXPECT_EQ(0, cb_2_called);
508   EXPECT_EQ(1, status_cb_called);
509   EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::HARDWARE_FAILURE),
510             status);
511 }
512 
TEST_F(SequentialCommandRunnerTest,CommandCompletesOnStatusEvent)513 TEST_F(SequentialCommandRunnerTest, CommandCompletesOnStatusEvent) {
514   auto command = bt::testing::EmptyCommandPacket(kTestOpCode);
515   auto command0_status_event = bt::testing::CommandStatusPacket(
516       kTestOpCode, pw::bluetooth::emboss::StatusCode::SUCCESS);
517 
518   auto command1 = bt::testing::EmptyCommandPacket(kTestOpCode2);
519   auto command1_cmpl_event = bt::testing::CommandCompletePacket(
520       kTestOpCode2, pw::bluetooth::emboss::StatusCode::SUCCESS);
521 
522   Result<> status = fit::ok();
523   int status_cb_called = 0;
524   auto status_cb = [&](Result<> cb_status) {
525     status = cb_status;
526     status_cb_called++;
527   };
528 
529   int cb_called = 0;
530   auto cb = [&](const EventPacket& event) { cb_called++; };
531 
532   SequentialCommandRunner cmd_runner(cmd_channel()->AsWeakPtr());
533   EXPECT_FALSE(cmd_runner.HasQueuedCommands());
534 
535   EXPECT_CMD_PACKET_OUT(test_device(), command, &command0_status_event);
536   cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode),
537                           cb,
538                           /*wait=*/false,
539                           hci_spec::kCommandStatusEventCode);
540 
541   EXPECT_CMD_PACKET_OUT(test_device(), command1, &command1_cmpl_event);
542   cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode2), cb, /*wait=*/true);
543 
544   EXPECT_TRUE(cmd_runner.IsReady());
545   EXPECT_TRUE(cmd_runner.HasQueuedCommands());
546 
547   cmd_runner.RunCommands(status_cb);
548   EXPECT_FALSE(cmd_runner.IsReady());
549   RunUntilIdle();
550   EXPECT_TRUE(cmd_runner.IsReady());
551   EXPECT_FALSE(cmd_runner.HasQueuedCommands());
552   EXPECT_EQ(2, cb_called);
553   EXPECT_EQ(1, status_cb_called);
554   EXPECT_EQ(fit::ok(), status);
555 }
556 
TEST_F(SequentialCommandRunnerTest,AsyncCommands)557 TEST_F(SequentialCommandRunnerTest, AsyncCommands) {
558   auto command = bt::testing::EmptyCommandPacket(hci_spec::kRemoteNameRequest);
559   auto command0_status_event = bt::testing::CommandStatusPacket(
560       hci_spec::kRemoteNameRequest, pw::bluetooth::emboss::StatusCode::SUCCESS);
561   auto command0_cmpl_event =
562       bt::testing::RemoteNameRequestCompletePacket(DeviceAddress());
563 
564   auto command1 =
565       bt::testing::EmptyCommandPacket(hci_spec::kLEReadRemoteFeatures);
566   auto command1_status_event = bt::testing::CommandStatusPacket(
567       hci_spec::kLEReadRemoteFeatures,
568       pw::bluetooth::emboss::StatusCode::SUCCESS);
569   auto command1_cmpl_event = bt::testing::LEReadRemoteFeaturesCompletePacket(
570       /*conn=*/0x0000, hci_spec::LESupportedFeatures{0});
571 
572   auto command2 =
573       bt::testing::EmptyCommandPacket(hci_spec::kReadRemoteVersionInfo);
574   auto command2_status_event = bt::testing::CommandStatusPacket(
575       hci_spec::kReadRemoteVersionInfo,
576       pw::bluetooth::emboss::StatusCode::SUCCESS);
577   auto command2_cmpl_event =
578       bt::testing::ReadRemoteVersionInfoCompletePacket(/*conn=*/0x0000);
579 
580   Result<> status = fit::ok();
581   int status_cb_called = 0;
582   auto status_cb = [&](Result<> cb_status) {
583     status = cb_status;
584     status_cb_called++;
585   };
586 
587   int cb_called = 0;
588   auto cb = [&](const EventPacket& event) { cb_called++; };
589 
590   SequentialCommandRunner cmd_runner(cmd_channel()->AsWeakPtr());
591   EXPECT_FALSE(cmd_runner.HasQueuedCommands());
592 
593   EXPECT_CMD_PACKET_OUT(test_device(), command, &command0_status_event);
594   cmd_runner.QueueCommand(CommandPacket::New(hci_spec::kRemoteNameRequest),
595                           cb,
596                           /*wait=*/false,
597                           hci_spec::kRemoteNameRequestCompleteEventCode);
598 
599   EXPECT_CMD_PACKET_OUT(test_device(), command1, &command1_status_event);
600   cmd_runner.QueueLeAsyncCommand(
601       EmbossCommandPacket::New<pw::bluetooth::emboss::CommandHeaderView>(
602           hci_spec::kLEReadRemoteFeatures),
603       hci_spec::kLEReadRemoteFeaturesCompleteSubeventCode,
604       cb,
605       /*wait=*/false);
606 
607   cmd_runner.QueueCommand(CommandPacket::New(hci_spec::kReadRemoteVersionInfo),
608                           cb,
609                           /*wait=*/true,
610                           hci_spec::kReadRemoteVersionInfoCompleteEventCode);
611 
612   EXPECT_TRUE(cmd_runner.IsReady());
613   EXPECT_TRUE(cmd_runner.HasQueuedCommands());
614 
615   cmd_runner.RunCommands(status_cb);
616   EXPECT_FALSE(cmd_runner.IsReady());
617 
618   RunUntilIdle();
619   // Command 2 should wait on command 0 & command 1 complete events.
620   EXPECT_FALSE(cmd_runner.IsReady());
621   EXPECT_TRUE(cmd_runner.HasQueuedCommands());
622   // Completing the commands out of order shouldn't matter.
623   test_device()->SendCommandChannelPacket(command1_cmpl_event);
624   test_device()->SendCommandChannelPacket(command0_cmpl_event);
625 
626   EXPECT_CMD_PACKET_OUT(
627       test_device(), command2, &command2_status_event, &command2_cmpl_event);
628   RunUntilIdle();
629   EXPECT_TRUE(cmd_runner.IsReady());
630   EXPECT_FALSE(cmd_runner.HasQueuedCommands());
631   EXPECT_EQ(3, cb_called);
632   EXPECT_EQ(1, status_cb_called);
633   EXPECT_EQ(fit::ok(), status);
634 }
635 
TEST_F(SequentialCommandRunnerTest,ExclusiveAsyncCommands)636 TEST_F(SequentialCommandRunnerTest, ExclusiveAsyncCommands) {
637   auto command = bt::testing::EmptyCommandPacket(hci_spec::kRemoteNameRequest);
638   auto command0_status_event = bt::testing::CommandStatusPacket(
639       hci_spec::kRemoteNameRequest, pw::bluetooth::emboss::StatusCode::SUCCESS);
640   auto command0_cmpl_event =
641       bt::testing::RemoteNameRequestCompletePacket(DeviceAddress());
642 
643   auto command1 =
644       bt::testing::EmptyCommandPacket(hci_spec::kReadRemoteVersionInfo);
645   auto command1_status_event = bt::testing::CommandStatusPacket(
646       hci_spec::kReadRemoteVersionInfo,
647       pw::bluetooth::emboss::StatusCode::SUCCESS);
648   auto command1_cmpl_event =
649       bt::testing::ReadRemoteVersionInfoCompletePacket(/*conn=*/0x0000);
650 
651   Result<> status = fit::ok();
652   int status_cb_called = 0;
653   auto status_cb = [&](Result<> cb_status) {
654     status = cb_status;
655     status_cb_called++;
656   };
657 
658   int cb_called = 0;
659   auto cb = [&](const EventPacket& event) { cb_called++; };
660 
661   SequentialCommandRunner cmd_runner(cmd_channel()->AsWeakPtr());
662   EXPECT_FALSE(cmd_runner.HasQueuedCommands());
663 
664   EXPECT_CMD_PACKET_OUT(test_device(), command, &command0_status_event);
665   cmd_runner.QueueCommand(CommandPacket::New(hci_spec::kRemoteNameRequest),
666                           cb,
667                           /*wait=*/false,
668                           hci_spec::kRemoteNameRequestCompleteEventCode);
669 
670   // Even though command 1 is not waiting on command 0, it should remain queued
671   // due to the exclusion list.
672   cmd_runner.QueueCommand(CommandPacket::New(hci_spec::kReadRemoteVersionInfo),
673                           cb,
674                           /*wait=*/false,
675                           hci_spec::kReadRemoteVersionInfoCompleteEventCode,
676                           /*exclusions=*/{hci_spec::kRemoteNameRequest});
677 
678   EXPECT_TRUE(cmd_runner.IsReady());
679   EXPECT_TRUE(cmd_runner.HasQueuedCommands());
680 
681   cmd_runner.RunCommands(status_cb);
682 
683   RunUntilIdle();
684   EXPECT_FALSE(cmd_runner.IsReady());
685   // Command 1 is "sent" but queued in CommandChannel.
686   EXPECT_FALSE(cmd_runner.HasQueuedCommands());
687   // Completing command 0 should send command 1.
688   test_device()->SendCommandChannelPacket(command0_cmpl_event);
689 
690   EXPECT_CMD_PACKET_OUT(
691       test_device(), command1, &command1_status_event, &command1_cmpl_event);
692   RunUntilIdle();
693   EXPECT_TRUE(cmd_runner.IsReady());
694   EXPECT_EQ(2, cb_called);
695   EXPECT_EQ(1, status_cb_called);
696   EXPECT_EQ(fit::ok(), status);
697 }
698 
TEST_F(SequentialCommandRunnerTest,CommandRunnerDestroyedBeforeSecondEventCallbackCalled)699 TEST_F(SequentialCommandRunnerTest,
700        CommandRunnerDestroyedBeforeSecondEventCallbackCalled) {
701   auto command = bt::testing::EmptyCommandPacket(hci_spec::kRemoteNameRequest);
702   auto command0_status_event = bt::testing::CommandStatusPacket(
703       hci_spec::kRemoteNameRequest, pw::bluetooth::emboss::StatusCode::SUCCESS);
704   auto command0_cmpl_event =
705       bt::testing::RemoteNameRequestCompletePacket(DeviceAddress());
706 
707   auto command1 =
708       bt::testing::EmptyCommandPacket(hci_spec::kLEReadRemoteFeatures);
709   auto command1_status_event = bt::testing::CommandStatusPacket(
710       hci_spec::kLEReadRemoteFeatures,
711       pw::bluetooth::emboss::StatusCode::SUCCESS);
712   auto command1_cmpl_event = bt::testing::LEReadRemoteFeaturesCompletePacket(
713       /*conn=*/0x0000, hci_spec::LESupportedFeatures{0});
714 
715   std::optional<SequentialCommandRunner> cmd_runner;
716   cmd_runner.emplace(cmd_channel()->AsWeakPtr());
717 
718   Result<> status = fit::ok();
719   int status_cb_called = 0;
720   auto status_cb = [&](Result<> cb_status) {
721     status = cb_status;
722     status_cb_called++;
723   };
724 
725   int cb_called = 0;
726   auto cb = [&](const EventPacket& event) {
727     if (cb_called == 0) {
728       cmd_runner.reset();
729     }
730     cb_called++;
731   };
732 
733   EXPECT_FALSE(cmd_runner->HasQueuedCommands());
734 
735   EXPECT_CMD_PACKET_OUT(
736       test_device(), command, &command0_status_event, &command0_cmpl_event);
737   cmd_runner->QueueCommand(CommandPacket::New(hci_spec::kRemoteNameRequest),
738                            cb,
739                            /*wait=*/false,
740                            hci_spec::kRemoteNameRequestCompleteEventCode);
741 
742   EXPECT_CMD_PACKET_OUT(test_device(), command1, &command1_status_event);
743   cmd_runner->QueueLeAsyncCommand(
744       CommandPacket::New(hci_spec::kLEReadRemoteFeatures),
745       hci_spec::kLEReadRemoteFeaturesCompleteSubeventCode,
746       cb,
747       /*wait=*/false);
748 
749   EXPECT_TRUE(cmd_runner->IsReady());
750   EXPECT_TRUE(cmd_runner->HasQueuedCommands());
751 
752   cmd_runner->RunCommands(status_cb);
753   EXPECT_FALSE(cmd_runner->IsReady());
754 
755   RunUntilIdle();
756   EXPECT_FALSE(cmd_runner.has_value());
757   EXPECT_EQ(1, cb_called);
758   EXPECT_EQ(0, status_cb_called);
759 }
760 
TEST_F(SequentialCommandRunnerTest,SequentialCommandRunnerDestroyedInCancelStatusCallbackDoesNotCrash)761 TEST_F(SequentialCommandRunnerTest,
762        SequentialCommandRunnerDestroyedInCancelStatusCallbackDoesNotCrash) {
763   std::optional<SequentialCommandRunner> cmd_runner;
764   cmd_runner.emplace(cmd_channel()->AsWeakPtr());
765 
766   Result<> status = fit::ok();
767   int status_cb_called = 0;
768   auto status_cb = [&](Result<> cb_status) {
769     status = cb_status;
770     status_cb_called++;
771     cmd_runner.reset();
772   };
773 
774   int cb_called = 0;
775   auto cb = [&](const EventPacket& event) { cb_called++; };
776 
777   auto command = bt::testing::EmptyCommandPacket(kTestOpCode);
778   cmd_runner->QueueCommand(CommandPacket::New(kTestOpCode), cb);
779   EXPECT_CMD_PACKET_OUT(test_device(), command);
780   cmd_runner->RunCommands(status_cb);
781   EXPECT_FALSE(cmd_runner->IsReady());
782   cmd_runner->Cancel();
783 
784   RunUntilIdle();
785   EXPECT_FALSE(cmd_runner);
786   EXPECT_EQ(0, cb_called);
787   EXPECT_EQ(1, status_cb_called);
788   EXPECT_EQ(ToResult(HostError::kCanceled), status);
789 }
790 
TEST_F(SequentialCommandRunnerTest,QueueCommandsWhileAlreadyRunning)791 TEST_F(SequentialCommandRunnerTest, QueueCommandsWhileAlreadyRunning) {
792   auto command = bt::testing::EmptyCommandPacket(hci_spec::kRemoteNameRequest);
793   auto command0_status_event = bt::testing::CommandStatusPacket(
794       hci_spec::kRemoteNameRequest, pw::bluetooth::emboss::StatusCode::SUCCESS);
795   auto command0_cmpl_event =
796       bt::testing::RemoteNameRequestCompletePacket(DeviceAddress());
797 
798   auto command1 =
799       bt::testing::EmptyCommandPacket(hci_spec::kLEReadRemoteFeatures);
800   auto command1_status_event = bt::testing::CommandStatusPacket(
801       hci_spec::kLEReadRemoteFeatures,
802       pw::bluetooth::emboss::StatusCode::SUCCESS);
803   auto command1_cmpl_event = bt::testing::LEReadRemoteFeaturesCompletePacket(
804       /*conn=*/0x0000, hci_spec::LESupportedFeatures{0});
805 
806   auto command2 =
807       bt::testing::EmptyCommandPacket(hci_spec::kReadRemoteVersionInfo);
808   auto command2_status_event = bt::testing::CommandStatusPacket(
809       hci_spec::kReadRemoteVersionInfo,
810       pw::bluetooth::emboss::StatusCode::SUCCESS);
811   auto command2_cmpl_event =
812       bt::testing::ReadRemoteVersionInfoCompletePacket(/*conn=*/0x0000);
813 
814   SequentialCommandRunner cmd_runner(cmd_channel()->AsWeakPtr());
815 
816   Result<> status = fit::ok();
817   int status_cb_called = 0;
818   auto status_cb = [&](Result<> cb_status) {
819     status = cb_status;
820     status_cb_called++;
821   };
822 
823   int cb_called = 0;
824   auto cb = [&](const EventPacket& event) { cb_called++; };
825 
826   int name_cb_called = 0;
827   auto name_request_callback = [&](const EventPacket& event) {
828     name_cb_called++;
829 
830     EXPECT_FALSE(cmd_runner.IsReady());
831     EXPECT_FALSE(cmd_runner.HasQueuedCommands());
832 
833     EXPECT_CMD_PACKET_OUT(
834         test_device(), command1, &command1_status_event, &command1_cmpl_event);
835     cmd_runner.QueueLeAsyncCommand(
836         CommandPacket::New(hci_spec::kLEReadRemoteFeatures),
837         hci_spec::kLEReadRemoteFeaturesCompleteSubeventCode,
838         cb,
839         /*wait=*/false);
840 
841     EXPECT_CMD_PACKET_OUT(
842         test_device(), command2, &command2_status_event, &command2_cmpl_event);
843     cmd_runner.QueueCommand(
844         CommandPacket::New(hci_spec::kReadRemoteVersionInfo),
845         cb,
846         /*wait=*/false,
847         hci_spec::kReadRemoteVersionInfoCompleteEventCode);
848   };
849 
850   EXPECT_CMD_PACKET_OUT(
851       test_device(), command, &command0_status_event, &command0_cmpl_event);
852   cmd_runner.QueueCommand(CommandPacket::New(hci_spec::kRemoteNameRequest),
853                           name_request_callback,
854                           /*wait=*/false,
855                           hci_spec::kRemoteNameRequestCompleteEventCode);
856   EXPECT_TRUE(cmd_runner.IsReady());
857   EXPECT_TRUE(cmd_runner.HasQueuedCommands());
858 
859   cmd_runner.RunCommands(status_cb);
860   EXPECT_FALSE(cmd_runner.IsReady());
861 
862   RunUntilIdle();
863   EXPECT_TRUE(cmd_runner.IsReady());
864   EXPECT_FALSE(cmd_runner.HasQueuedCommands());
865   EXPECT_EQ(1, name_cb_called);
866   EXPECT_EQ(2, cb_called);
867   EXPECT_EQ(1, status_cb_called);
868   EXPECT_EQ(fit::ok(), status);
869 }
870 
TEST_F(SequentialCommandRunnerTest,EmbossEventHandler)871 TEST_F(SequentialCommandRunnerTest, EmbossEventHandler) {
872   SequentialCommandRunner cmd_runner(cmd_channel()->AsWeakPtr());
873   EXPECT_FALSE(cmd_runner.HasQueuedCommands());
874 
875   // HCI command with custom opcode FFFF.
876   StaticByteBuffer command_bytes(0xFF, 0xFF, 0x00);
877   StaticByteBuffer command_cmpl_success_bytes(
878       hci_spec::kCommandCompleteEventCode,
879       0x04,  // parameter_total_size (4 byte payload)
880       1,
881       0xFF,
882       0xFF,
883       pw::bluetooth::emboss::StatusCode::SUCCESS);
884 
885   Result<> status = fit::ok();
886   int status_cb_called = 0;
887   auto status_cb = [&](Result<> cb_status) {
888     status = cb_status;
889     status_cb_called++;
890   };
891 
892   int cb_called = 0;
893   SequentialCommandRunner::EmbossCommandCompleteCallback cb =
894       [&](const EmbossEventPacket& event) {
895         cb_called++;
896         EXPECT_THAT(event.data(), BufferEq(command_cmpl_success_bytes));
897       };
898 
899   cmd_runner.QueueCommand(CommandPacket::New(kTestOpCode), std::move(cb));
900   EXPECT_TRUE(cmd_runner.IsReady());
901   EXPECT_TRUE(cmd_runner.HasQueuedCommands());
902 
903   EXPECT_CMD_PACKET_OUT(
904       test_device(), command_bytes, &command_cmpl_success_bytes);
905   cmd_runner.RunCommands(status_cb);
906   EXPECT_FALSE(cmd_runner.IsReady());
907   RunUntilIdle();
908   EXPECT_TRUE(cmd_runner.IsReady());
909   EXPECT_FALSE(cmd_runner.HasQueuedCommands());
910   EXPECT_EQ(1, cb_called);
911   EXPECT_EQ(1, status_cb_called);
912   EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::SUCCESS), status);
913 }
914 
915 }  // namespace
916 }  // namespace bt::hci
917