blob: 71dc72956a5442438a9538bb86bdd6fcdfad6ee2 [file] [log] [blame]
// Copyright 2018 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.
#pragma once
#include <ddk/mmio-buffer.h>
#include <fbl/vector.h>
#include <unittest/unittest.h>
void mmio_fake_read(uintptr_t base, size_t size, zx_off_t off, void* value);
void mmio_fake_write(uintptr_t base, size_t size, const void* value, zx_off_t off);
namespace ddk_mock {
// Mocks a single MMIO register. This class is intended to be used with a ddk::MmioBuffer;
// operations on an instance of that class will be directed to the mock through mmio_fake_read and
// mmio_fake_write if TEST_MMIO_FAKE is defined. The base address used by the MmioBuffer should be
// an array of MockMmioReg objects. See the following example test:
//
// ddk_mock::MockMmioReg register_array[number_of_registers];
// ddk_mock::MockMmioRegRegion mock_registers(register_array, register_size, number_of_registers);
// ddk::MmioBuffer mmio_buffer(mock_registers.GetMmioBuffer());
//
// SomeDriver dut(mmio_buffer);
// mock_registers[0]
// .ExpectRead()
// .ExpectWrite(0xdeadbeef)
// .ExpectRead(0xcafecafe)
// .ExpectWrite()
// .ExpectRead();
// mock_registers[5]
// .ExpectWrite(0)
// .ExpectWrite(1024)
// .ReadReturns(0);
//
// EXPECT_EQ(dut.SomeMethod(), ZX_OK);
// mock_registers.VerifyAll();
class MockMmioReg {
public:
// Reads from the mocked register. Returns the value set by the next expectation, or the default
// value. The default is initially zero and can be set by calling ReadReturns() or Write(). This
// method is expected to be called (indirectly) by the code under test.
uint64_t Read() {
if (read_expectations_index_ >= read_expectations_.size()) {
return last_value_;
}
MmioExpectation& exp = read_expectations_[read_expectations_index_++];
if (exp.match == MmioExpectation::Match::kAny) {
return last_value_;
}
return last_value_ = exp.value;
}
// Writes to the mocked register. This method is expected to be called (indirectly) by the code
// under test.
bool Write(uint64_t value) {
BEGIN_HELPER;
last_value_ = value;
if (write_expectations_index_ >= write_expectations_.size()) {
return true;
}
MmioExpectation& exp = write_expectations_[write_expectations_index_++];
if (exp.match == MmioExpectation::Match::kAny) {
return true;
}
EXPECT_EQ(exp.value, value);
END_HELPER;
}
// Matches a register read and returns the specified value.
MockMmioReg& ExpectRead(uint64_t value) {
read_expectations_.push_back(MmioExpectation{
.match = MmioExpectation::Match::kEqual,
.value = value
});
return *this;
}
// Matches a register read and returns the default value.
MockMmioReg& ExpectRead() {
read_expectations_.push_back(MmioExpectation{
.match = MmioExpectation::Match::kAny,
.value = 0
});
return *this;
}
// Sets the default register read value.
MockMmioReg& ReadReturns(uint64_t value) {
last_value_ = value;
return *this;
}
// Matches a register write with the specified value.
MockMmioReg& ExpectWrite(uint64_t value) {
write_expectations_.push_back(MmioExpectation{
.match = MmioExpectation::Match::kEqual,
.value = value
});
return *this;
}
// Matches any register write.
MockMmioReg& ExpectWrite() {
write_expectations_.push_back(MmioExpectation{
.match = MmioExpectation::Match::kAny,
.value = 0
});
return *this;
}
// Removes and ignores all expectations and resets the default read value.
void Clear() {
last_value_ = 0;
read_expectations_index_ = 0;
while (read_expectations_.size() > 0) {
read_expectations_.pop_back();
}
write_expectations_index_ = 0;
while (write_expectations_.size() > 0) {
write_expectations_.pop_back();
}
}
// Removes all expectations and resets the default value. The presence of any outstanding
// expectations causes a test failure.
bool VerifyAndClear() {
BEGIN_HELPER;
EXPECT_GE(read_expectations_index_, read_expectations_.size());
EXPECT_GE(write_expectations_index_, write_expectations_.size());
Clear();
END_HELPER;
}
private:
struct MmioExpectation {
enum class Match { kEqual, kAny } match;
uint64_t value;
};
uint64_t last_value_ = 0;
size_t read_expectations_index_ = 0;
fbl::Vector<MmioExpectation> read_expectations_;
size_t write_expectations_index_ = 0;
fbl::Vector<MmioExpectation> write_expectations_;
};
// Represents an array of MockMmioReg objects.
class MockMmioRegRegion {
public:
// Constructs a MockMmioRegRegion backed by the given array. reg_size is the size of each
// register in bytes, reg_count is the total size of the region in bytes. Ownership of mock_regs
// is not transferred.
MockMmioRegRegion(MockMmioReg* mock_regs, size_t reg_size, size_t reg_count)
: mock_regs_(mock_regs), reg_size_(reg_size), reg_count_(reg_count) {
ZX_ASSERT(reg_size_ > 0);
}
// Accesses the MockMmioReg at the given offset. Note that this is the _offset_, not the
// _index_.
MockMmioReg& operator[](size_t offset) {
ZX_ASSERT(offset / reg_size_ < reg_count_);
return mock_regs_[offset / reg_size_];
}
// Calls VerifyAndClear() on all MockMmioReg objects.
void VerifyAll() {
for (size_t i = 0; i < reg_count_; i++) {
mock_regs_[i].VerifyAndClear();
}
}
// Returns an mmio_buffer_t that can be used for constructing a ddk::MmioBuffer object.
mmio_buffer_t GetMmioBuffer() {
return mmio_buffer_t{
.vaddr = this,
.offset = 0,
.size = reg_size_ * reg_count_,
.vmo = ZX_HANDLE_INVALID
};
}
private:
MockMmioReg* mock_regs_;
const size_t reg_size_;
const size_t reg_count_;
};
} // namespace ddk_mock