xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/testing/mock_controller.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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/testing/mock_controller.h"
16 
17 #include <pw_bytes/endian.h>
18 
19 #include <cstdint>
20 
21 #include "pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
22 #include "pw_unit_test/framework.h"
23 
24 namespace bt::testing {
25 
Transaction(const ByteBuffer & expected,const std::vector<const ByteBuffer * > & replies,ExpectationMetadata meta)26 Transaction::Transaction(const ByteBuffer& expected,
27                          const std::vector<const ByteBuffer*>& replies,
28                          ExpectationMetadata meta)
29     : expected_({DynamicByteBuffer(expected), meta}) {
30   for (const auto* buffer : replies) {
31     replies_.push(DynamicByteBuffer(*buffer));
32   }
33 }
34 
Match(const ByteBuffer & packet)35 bool Transaction::Match(const ByteBuffer& packet) {
36   return ContainersEqual(expected_.data, packet);
37 }
38 
CommandTransaction(hci_spec::OpCode expected_opcode,const std::vector<const ByteBuffer * > & replies,ExpectationMetadata meta)39 CommandTransaction::CommandTransaction(
40     hci_spec::OpCode expected_opcode,
41     const std::vector<const ByteBuffer*>& replies,
42     ExpectationMetadata meta)
43     : Transaction(DynamicByteBuffer(), replies, meta), prefix_(true) {
44   hci_spec::OpCode le_opcode =
45       pw::bytes::ConvertOrderTo(cpp20::endian::little, expected_opcode);
46   const BufferView expected(&le_opcode, sizeof(expected_opcode));
47   set_expected({DynamicByteBuffer(expected), meta});
48 }
49 
Match(const ByteBuffer & cmd)50 bool CommandTransaction::Match(const ByteBuffer& cmd) {
51   return ContainersEqual(
52       expected().data,
53       (prefix_ ? cmd.view(0, expected().data.size()) : cmd.view()));
54 }
55 
MockController(pw::async::Dispatcher & pw_dispatcher)56 MockController::MockController(pw::async::Dispatcher& pw_dispatcher)
57     : ControllerTestDoubleBase(pw_dispatcher), WeakSelf(this) {}
58 
~MockController()59 MockController::~MockController() {
60   while (!cmd_transactions_.empty()) {
61     auto& transaction = cmd_transactions_.front();
62     auto meta = transaction.expected().meta;
63     ADD_FAILURE_AT(meta.file, meta.line)
64         << "Didn't receive expected outbound command packet ("
65         << meta.expectation << ") {"
66         << ByteContainerToString(transaction.expected().data) << "}";
67     cmd_transactions_.pop();
68   }
69 
70   while (!data_transactions_.empty()) {
71     auto& transaction = data_transactions_.front();
72     auto meta = transaction.expected().meta;
73     ADD_FAILURE_AT(meta.file, meta.line)
74         << "Didn't receive expected outbound data packet (" << meta.expectation
75         << ") {" << ByteContainerToString(transaction.expected().data) << "}";
76     data_transactions_.pop();
77   }
78 
79   while (!sco_transactions_.empty()) {
80     auto& transaction = sco_transactions_.front();
81     auto meta = transaction.expected().meta;
82     ADD_FAILURE_AT(meta.file, meta.line)
83         << "Didn't receive expected outbound SCO packet (" << meta.expectation
84         << ") {" << ByteContainerToString(transaction.expected().data) << "}";
85     sco_transactions_.pop();
86   }
87 }
88 
QueueCommandTransaction(CommandTransaction transaction)89 void MockController::QueueCommandTransaction(CommandTransaction transaction) {
90   cmd_transactions_.push(std::move(transaction));
91 }
92 
QueueCommandTransaction(const ByteBuffer & expected,const std::vector<const ByteBuffer * > & replies,ExpectationMetadata meta)93 void MockController::QueueCommandTransaction(
94     const ByteBuffer& expected,
95     const std::vector<const ByteBuffer*>& replies,
96     ExpectationMetadata meta) {
97   QueueCommandTransaction(
98       CommandTransaction(DynamicByteBuffer(expected), replies, meta));
99 }
100 
QueueCommandTransaction(hci_spec::OpCode expected_opcode,const std::vector<const ByteBuffer * > & replies,ExpectationMetadata meta)101 void MockController::QueueCommandTransaction(
102     hci_spec::OpCode expected_opcode,
103     const std::vector<const ByteBuffer*>& replies,
104     ExpectationMetadata meta) {
105   QueueCommandTransaction(CommandTransaction(expected_opcode, replies, meta));
106 }
107 
QueueDataTransaction(DataTransaction transaction)108 void MockController::QueueDataTransaction(DataTransaction transaction) {
109   data_transactions_.push(std::move(transaction));
110 }
111 
QueueDataTransaction(const ByteBuffer & expected,const std::vector<const ByteBuffer * > & replies,ExpectationMetadata meta)112 void MockController::QueueDataTransaction(
113     const ByteBuffer& expected,
114     const std::vector<const ByteBuffer*>& replies,
115     ExpectationMetadata meta) {
116   QueueDataTransaction(
117       DataTransaction(DynamicByteBuffer(expected), replies, meta));
118 }
119 
QueueScoTransaction(const ByteBuffer & expected,ExpectationMetadata meta)120 void MockController::QueueScoTransaction(const ByteBuffer& expected,
121                                          ExpectationMetadata meta) {
122   sco_transactions_.push(ScoTransaction(DynamicByteBuffer(expected), meta));
123 }
124 
AllExpectedScoPacketsSent() const125 bool MockController::AllExpectedScoPacketsSent() const {
126   return sco_transactions_.empty();
127 }
128 
AllExpectedDataPacketsSent() const129 bool MockController::AllExpectedDataPacketsSent() const {
130   return data_transactions_.empty();
131 }
132 
AllExpectedCommandPacketsSent() const133 bool MockController::AllExpectedCommandPacketsSent() const {
134   return cmd_transactions_.empty();
135 }
136 
SetDataCallback(DataCallback callback)137 void MockController::SetDataCallback(DataCallback callback) {
138   PW_DCHECK(callback);
139   PW_DCHECK(!data_callback_);
140 
141   data_callback_ = std::move(callback);
142 }
143 
ClearDataCallback()144 void MockController::ClearDataCallback() {
145   // Leave dispatcher set (if already set) to preserve its write-once-ness.
146   data_callback_ = nullptr;
147 }
148 
SetTransactionCallback(fit::closure callback)149 void MockController::SetTransactionCallback(fit::closure callback) {
150   SetTransactionCallback([f = std::move(callback)](const auto&) { f(); });
151 }
152 
SetTransactionCallback(TransactionCallback callback)153 void MockController::SetTransactionCallback(TransactionCallback callback) {
154   PW_DCHECK(callback);
155   PW_DCHECK(!transaction_callback_);
156   transaction_callback_ = std::move(callback);
157 }
158 
ClearTransactionCallback()159 void MockController::ClearTransactionCallback() {
160   // Leave dispatcher set (if already set) to preserve its write-once-ness.
161   transaction_callback_ = nullptr;
162 }
163 
OnCommandReceived(const ByteBuffer & data)164 void MockController::OnCommandReceived(const ByteBuffer& data) {
165   ASSERT_GE(data.size(), sizeof(hci_spec::OpCode));
166   const hci_spec::OpCode opcode = pw::bytes::ConvertOrderFrom(
167       cpp20::endian::little, data.To<hci_spec::OpCode>());
168   const uint8_t ogf = hci_spec::GetOGF(opcode);
169   const uint16_t ocf = hci_spec::GetOCF(opcode);
170 
171   // Note: we upcast ogf to uint16_t so that it does not get interpreted as a
172   // char for printing
173   ASSERT_FALSE(cmd_transactions_.empty())
174       << "Received unexpected command packet with OGF: 0x" << std::hex
175       << static_cast<uint16_t>(ogf) << ", OCF: 0x" << ocf;
176 
177   auto& transaction = cmd_transactions_.front();
178   const hci_spec::OpCode expected_opcode = pw::bytes::ConvertOrderFrom(
179       cpp20::endian::little,
180       transaction.expected().data.To<hci_spec::OpCode>());
181   const uint8_t expected_ogf = hci_spec::GetOGF(expected_opcode);
182   const uint16_t expected_ocf = hci_spec::GetOCF(expected_opcode);
183 
184   if (!transaction.Match(data)) {
185     auto meta = transaction.expected().meta;
186     GTEST_FAIL_AT(meta.file, meta.line)
187         << " Expected command packet (" << meta.expectation << ") with OGF: 0x"
188         << std::hex << static_cast<uint16_t>(expected_ogf) << ", OCF: 0x"
189         << expected_ocf << ". Received command packet with OGF: 0x"
190         << static_cast<uint16_t>(ogf) << ", OCF: 0x" << ocf;
191   }
192 
193   while (!transaction.replies().empty()) {
194     DynamicByteBuffer& reply = transaction.replies().front();
195     ASSERT_TRUE(SendCommandChannelPacket(reply)) << "Failed to send reply";
196     transaction.replies().pop();
197   }
198   cmd_transactions_.pop();
199 
200   if (transaction_callback_) {
201     DynamicByteBuffer rx(data);
202     (void)heap_dispatcher().Post(
203         [rx = std::move(rx), f = transaction_callback_.share()](
204             auto, pw::Status status) {
205           if (status.ok()) {
206             f(rx);
207           }
208         });
209   }
210 }
211 
OnACLDataPacketReceived(const ByteBuffer & acl_data_packet)212 void MockController::OnACLDataPacketReceived(
213     const ByteBuffer& acl_data_packet) {
214   ASSERT_FALSE(data_transactions_.empty())
215       << "Received unexpected acl data packet: { "
216       << ByteContainerToString(acl_data_packet) << "}";
217 
218   auto& expected = data_transactions_.front();
219   if (!expected.Match(acl_data_packet.view())) {
220     auto meta = expected.expected().meta;
221     GTEST_FAIL_AT(meta.file, meta.line)
222         << "Expected data packet (" << meta.expectation << ")";
223   }
224 
225   while (!expected.replies().empty()) {
226     auto& reply = expected.replies().front();
227     ASSERT_TRUE(SendACLDataChannelPacket(reply)) << "Failed to send reply";
228     expected.replies().pop();
229   }
230   data_transactions_.pop();
231 
232   if (data_callback_) {
233     DynamicByteBuffer packet_copy(acl_data_packet);
234     (void)heap_dispatcher().Post(
235         [packet_copy = std::move(packet_copy), cb = data_callback_.share()](
236             auto, pw::Status status) mutable {
237           if (status.ok()) {
238             cb(packet_copy);
239           }
240         });
241   }
242 }
243 
OnScoDataPacketReceived(const ByteBuffer & sco_data_packet)244 void MockController::OnScoDataPacketReceived(
245     const ByteBuffer& sco_data_packet) {
246   ASSERT_FALSE(sco_transactions_.empty())
247       << "Received unexpected SCO data packet: { "
248       << ByteContainerToString(sco_data_packet) << "}";
249 
250   auto& expected = sco_transactions_.front();
251   if (!expected.Match(sco_data_packet.view())) {
252     auto meta = expected.expected().meta;
253     GTEST_FAIL_AT(meta.file, meta.line)
254         << "Expected SCO packet (" << meta.expectation << ")";
255   }
256 
257   sco_transactions_.pop();
258 }
259 
OnIsoDataPacketReceived(const ByteBuffer & iso_data_packet)260 void MockController::OnIsoDataPacketReceived(
261     const ByteBuffer& iso_data_packet) {
262   ASSERT_FALSE(iso_transactions_.empty())
263       << "Received unexpected ISO data packet: { "
264       << ByteContainerToString(iso_data_packet) << "}";
265 
266   auto& expected = iso_transactions_.front();
267   if (!expected.Match(iso_data_packet.view())) {
268     auto meta = expected.expected().meta;
269     GTEST_FAIL_AT(meta.file, meta.line)
270         << "Expected ISO packet (" << meta.expectation << ")";
271   }
272 
273   iso_transactions_.pop();
274 }
275 
SendCommand(pw::span<const std::byte> data)276 void MockController::SendCommand(pw::span<const std::byte> data) {
277   // Post task to simulate async
278   DynamicByteBuffer buffer(BufferView(data.data(), data.size()));
279   (void)heap_dispatcher().Post(
280       [this, buffer = std::move(buffer)](pw::async::Context /*ctx*/,
281                                          pw::Status status) {
282         if (status.ok()) {
283           OnCommandReceived(buffer);
284         }
285       });
286 }
SendAclData(pw::span<const std::byte> data)287 void MockController::SendAclData(pw::span<const std::byte> data) {
288   // Post task to simulate async
289   DynamicByteBuffer buffer(BufferView(data.data(), data.size()));
290   (void)heap_dispatcher().Post(
291       [this, buffer = std::move(buffer)](pw::async::Context /*ctx*/,
292                                          pw::Status status) {
293         if (status.ok()) {
294           OnACLDataPacketReceived(buffer);
295         }
296       });
297 }
SendScoData(pw::span<const std::byte> data)298 void MockController::SendScoData(pw::span<const std::byte> data) {
299   // Post task to simulate async
300   DynamicByteBuffer buffer(BufferView(data.data(), data.size()));
301   (void)heap_dispatcher().Post(
302       [this, buffer = std::move(buffer)](pw::async::Context /*ctx*/,
303                                          pw::Status status) {
304         if (status.ok()) {
305           OnScoDataPacketReceived(buffer);
306         }
307       });
308 }
SendIsoData(pw::span<const std::byte> data)309 void MockController::SendIsoData(pw::span<const std::byte> data) {
310   // Post task to simulate async
311   DynamicByteBuffer buffer(BufferView(data.data(), data.size()));
312   (void)heap_dispatcher().Post(
313       [this, buffer = std::move(buffer)](pw::async::Context /*ctx*/,
314                                          pw::Status status) {
315         if (status.ok()) {
316           OnIsoDataPacketReceived(buffer);
317         }
318       });
319 }
320 
321 }  // namespace bt::testing
322