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