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