blob: e8761c24571a1cb9641ee9d59e99fde90c340d7d [file] [log] [blame]
// Copyright 2020 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 HWREG_MOCK_H_
#define HWREG_MOCK_H_
#include <lib/mock-function/mock-function.h>
#include <optional>
#include <type_traits>
#include "internal.h"
namespace hwreg {
// The io() pointer can be passed to ReadFrom and WriteTo methods. The
// Mock should first be primed with ExpectRead() and ExpectWrite() calls.
class Mock {
private:
// Forward declarations.
struct DummyIo;
class MockRegisterIo;
public:
using RegisterIo = std::variant<DummyIo, MockRegisterIo>;
Mock() = default;
Mock(Mock&& other) : mock_(std::move(other.mock_)) {}
Mock& operator=(Mock&& other) {
mock_ = std::move(other.mock_);
return *this;
}
template <typename IntType>
Mock& ExpectWrite(IntType value, uint32_t offset) {
static_assert(internal::IsSupportedInt<IntType>::value, "unsupported register access width");
mock_.ExpectCall(0, ExpectedWrite{sizeof(value), value}, offset);
return *this;
}
template <typename IntType>
Mock& ExpectRead(IntType value, uint32_t offset) {
static_assert(internal::IsSupportedInt<IntType>::value, "unsupported register access width");
mock_.ExpectCall(static_cast<uint64_t>(value), ExpectedRead{sizeof(value)}, offset);
return *this;
}
Mock& ExpectNoIo() {
mock_.ExpectNoCall();
return *this;
}
void VerifyAndClear() { mock_.VerifyAndClear(); }
RegisterIo* io() { return &io_; }
private:
struct ExpectedWrite {
size_t size;
uint64_t value;
bool operator==(const ExpectedWrite& other) const {
return other.size == size && other.value == value;
}
};
struct ExpectedRead {
size_t size;
bool operator==(const ExpectedRead& other) const { return other.size == size; }
};
using ExpectedIo = std::variant<ExpectedWrite, ExpectedRead>;
class MockRegisterIo {
public:
MockRegisterIo(const MockRegisterIo& other) : mock_(other.mock_) {}
template <typename IntType>
void Write(IntType value, uint32_t offset) const {
static_assert(internal::IsSupportedInt<IntType>::value, "unsupported register access width");
mock_.mock_.Call(ExpectedWrite{sizeof(value), value}, offset);
}
template <typename IntType>
IntType Read(uint32_t offset) const {
static_assert(internal::IsSupportedInt<IntType>::value, "unsupported register access width");
return static_cast<IntType>(mock_.mock_.Call(ExpectedRead{sizeof(IntType)}, offset));
}
private:
Mock& mock_;
friend class Mock;
explicit MockRegisterIo(Mock& mock) : mock_(mock) {}
};
// This is the default-constructed state of a hwreg::Mock::RegisterIo object.
// Such members can't be used until they're copied from a Mock::io() result.
struct DummyIo {
template <typename IntType>
void Write(IntType value, uint32_t offset) const {
static_assert(internal::IsSupportedInt<IntType>::value, "unsupported register access width");
ZX_PANIC("hwreg::Mock::RegisterIo used in default-constructed state");
}
template <typename IntType>
IntType Read(uint32_t offset) const {
static_assert(internal::IsSupportedInt<IntType>::value, "unsupported register access width");
ZX_PANIC("hwreg::Mock::RegisterIo used in default-constructed state");
return 0;
}
};
RegisterIo io_{MockRegisterIo{*this}};
mock_function::MockFunction<uint64_t, ExpectedIo, uint32_t> mock_;
};
} // namespace hwreg
#endif // HWREG_MOCK_H_