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