blob: 3b4151b4bab3a978e6e1319dbfcc1fd3052dae04 [file] [log] [blame]
// 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_