| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_TESTING_MOCK_CONTROLLER_H_ |
| #define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_TESTING_MOCK_CONTROLLER_H_ |
| |
| #include <lib/async/dispatcher.h> |
| #include <lib/fit/function.h> |
| |
| #include <queue> |
| #include <vector> |
| |
| #include <fbl/macros.h> |
| |
| #include "src/connectivity/bluetooth/core/bt-host/common/byte_buffer.h" |
| #include "src/connectivity/bluetooth/core/bt-host/hci/hci.h" |
| #include "src/connectivity/bluetooth/core/bt-host/testing/controller_test_double_base.h" |
| |
| namespace bt::testing { |
| |
| struct ExpectationMetadata { |
| ExpectationMetadata(const char* file, int line, const char* expectation) |
| : file(file), line(line), expectation(expectation) {} |
| const char* file; |
| int line; |
| // String inside of the expectation expression: EXPECT_ACL_PACKET_OUT(expectation, ...). |
| const char* expectation; |
| }; |
| |
| struct PacketExpectation { |
| DynamicByteBuffer data; |
| ExpectationMetadata meta; |
| }; |
| |
| class Transaction { |
| public: |
| // The |expected| buffer and the buffers in |replies| will be copied, so their lifetime does not |
| // need to extend past Transaction construction. |
| Transaction(const ByteBuffer& expected, const std::vector<const ByteBuffer*>& replies, |
| ExpectationMetadata meta); |
| virtual ~Transaction() = default; |
| Transaction(Transaction&& other) = default; |
| Transaction& operator=(Transaction&& other) = default; |
| |
| // Returns true if the transaction matches the given HCI packet. |
| virtual bool Match(const ByteBuffer& packet); |
| |
| const PacketExpectation& expected() { return expected_; } |
| void set_expected(const PacketExpectation& expected) { |
| expected_ = PacketExpectation{.data = DynamicByteBuffer(expected.data), .meta = expected.meta}; |
| } |
| |
| std::queue<DynamicByteBuffer>& replies() { return replies_; } |
| |
| private: |
| PacketExpectation expected_; |
| std::queue<DynamicByteBuffer> replies_; |
| |
| DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(Transaction); |
| }; |
| |
| // A CommandTransaction is used to set up an expectation for a command channel |
| // packet and the events that should be sent back in response to it. |
| class CommandTransaction final : public Transaction { |
| public: |
| CommandTransaction(const ByteBuffer& expected, const std::vector<const ByteBuffer*>& replies, |
| ExpectationMetadata meta) |
| : Transaction(expected, replies, meta), prefix_(false) {} |
| |
| // Match by opcode only. |
| CommandTransaction(hci::OpCode expected_opcode, const std::vector<const ByteBuffer*>& replies, |
| ExpectationMetadata meta); |
| |
| // Move constructor and assignment operator. |
| CommandTransaction(CommandTransaction&& other) = default; |
| CommandTransaction& operator=(CommandTransaction&& other) = default; |
| |
| // Returns true if the transaction matches the given HCI command packet. |
| bool Match(const ByteBuffer& cmd) override; |
| |
| private: |
| bool prefix_ = false; |
| }; |
| |
| // A DataTransaction is used to set up an expectation for an acl data channel |
| class DataTransaction final : public Transaction { |
| public: |
| DataTransaction(const ByteBuffer& expected, const std::vector<const ByteBuffer*>& replies, |
| ExpectationMetadata meta) |
| : Transaction(expected, replies, meta) {} |
| DataTransaction(DataTransaction&& other) = default; |
| DataTransaction& operator=(DataTransaction&& other) = default; |
| }; |
| |
| // Helper macro for expecting a data packet and specifying a variable number of responses that the |
| // MockController should send in response to the expected packet. |
| #define EXPECT_ACL_PACKET_OUT(device, expected, ...) \ |
| (device)->QueueDataTransaction((expected), {__VA_ARGS__}, \ |
| bt::testing::ExpectationMetadata(__FILE__, __LINE__, #expected)) |
| |
| // Helper macro for expecting a command packet and receiving a variable number of responses. |
| #define EXPECT_CMD_PACKET_OUT(device, expected, ...) \ |
| (device)->QueueCommandTransaction( \ |
| (expected), {__VA_ARGS__}, bt::testing::ExpectationMetadata(__FILE__, __LINE__, #expected)) |
| |
| // MockController allows unit tests to set up an expected sequence of HCI |
| // command packets and ACL data packets and any packets that should be sent back in response. The |
| // code internally verifies each received packet using gtest ASSERT_* macros. |
| class MockController : public ControllerTestDoubleBase { |
| public: |
| MockController(); |
| ~MockController() override; |
| |
| // Queues a transaction into the MockController's expected command queue. Each |
| // packet received through the command channel endpoint will be verified |
| // against the next expected transaction in the queue. A mismatch will cause a |
| // fatal assertion. On a match, MockController will send back the replies |
| // provided in the transaction. |
| void QueueCommandTransaction(CommandTransaction transaction); |
| void QueueCommandTransaction(const ByteBuffer& expected, |
| const std::vector<const ByteBuffer*>& replies, ExpectationMetadata); |
| void QueueCommandTransaction(hci::OpCode expected_opcode, |
| const std::vector<const ByteBuffer*>& replies, |
| ExpectationMetadata meta); |
| |
| // Queues a transaction into the MockController's expected ACL data queue. Each |
| // packet received through the ACL data channel endpoint will be verified |
| // against the next expected transaction in the queue. A mismatch will cause a |
| // fatal assertion. On a match, MockController will send back the replies |
| // provided in the transaction. |
| void QueueDataTransaction(DataTransaction transaction); |
| void QueueDataTransaction(const ByteBuffer& expected, |
| const std::vector<const ByteBuffer*>& replies, |
| ExpectationMetadata meta); |
| |
| // Returns true iff all transactions queued with QueueDataTransaction() have been received. |
| bool AllExpectedDataPacketsSent() const; |
| |
| // TODO(benlawson): remove after all MockController tests have been refactored to use data |
| // expectations |
| void set_data_expectations_enabled(bool enabled) { data_expectations_enabled_ = enabled; } |
| |
| // Callback to invoke when a packet is received over the data channel. Care |
| // should be taken to ensure that a callback with a reference to test case |
| // variables is not invoked when tearing down. |
| using DataCallback = fit::function<void(const ByteBuffer& packet)>; |
| void SetDataCallback(DataCallback callback, async_dispatcher_t* dispatcher); |
| void ClearDataCallback(); |
| |
| // Callback invoked when a transaction completes. Care should be taken to |
| // ensure that a callback with a reference to test case variables is not |
| // invoked when tearing down. |
| using TransactionCallback = fit::function<void(const ByteBuffer& rx)>; |
| void SetTransactionCallback(TransactionCallback callback, async_dispatcher_t* dispatcher); |
| void SetTransactionCallback(fit::closure callback, async_dispatcher_t* dispatcher); |
| void ClearTransactionCallback(); |
| |
| private: |
| // ControllerTestDoubleBase overrides: |
| void OnCommandPacketReceived(const PacketView<hci::CommandHeader>& command_packet) override; |
| void OnACLDataPacketReceived(const ByteBuffer& acl_data_packet) override; |
| |
| std::queue<CommandTransaction> cmd_transactions_; |
| std::queue<DataTransaction> data_transactions_; |
| bool data_expectations_enabled_; |
| DataCallback data_callback_; |
| async_dispatcher_t* data_dispatcher_; |
| TransactionCallback transaction_callback_; |
| async_dispatcher_t* transaction_dispatcher_; |
| |
| DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(MockController); |
| }; |
| |
| } // namespace bt::testing |
| |
| #endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_TESTING_MOCK_CONTROLLER_H_ |