blob: d7dab97f963618388a1703cb917f780c18bd7d65 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#ifndef OPENTITAN_SW_DEVICE_LIB_TESTING_MASK_ROM_TEST_H_
#define OPENTITAN_SW_DEVICE_LIB_TESTING_MASK_ROM_TEST_H_
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace mask_rom_test {
/**
* Mixin for global mocks.
*
* `GlobalMock<T>` is a derived class of `T` that can have at most one instance
* at a time (checked at runtime) and makes this instance globally accessible
* via the static `GlobalMock<T> &Instance()` method.
*
* Mock classes used in tests should be of this type so that mock functions can
* access their instances during tests. Since we prefer using strict mocks,
* mock classes should also be of type `testing::StrictMock`. Mock classes that
* satisfy both requirements can be defined using a type alias as follows:
*
* namespace mask_rom_test {
* namespace internal {
* class MockFoo {
* ...
* };
* } // namespace internal
* // Type alias for making `internal::MockFoo` a global and strict mock.
* using MockFoo = GlobalMock<testing::StrictMock<internal::MockFoo>>;
* ...
* } // namespace mask_rom_test
*/
template <typename T>
class GlobalMock : public T {
public:
template <typename... Args>
GlobalMock(Args &&... args) : T(std::forward(args)...) {
if (instance_ != nullptr) {
throw std::runtime_error("Mock is already instantiated.");
}
instance_ = this;
}
static_assert(std::has_virtual_destructor<T>::value,
"Mock class must have a virtual destructor.");
virtual ~GlobalMock() { instance_ = nullptr; }
static GlobalMock<T> &Instance() {
if (instance_ == nullptr) {
throw std::runtime_error("Mock is not instantiated yet.");
}
return *instance_;
}
GlobalMock(const GlobalMock &) = delete;
GlobalMock &operator=(const GlobalMock &) = delete;
GlobalMock(GlobalMock &&) = delete;
GlobalMock &operator=(GlobalMock &&) = delete;
private:
static GlobalMock<T> *instance_;
};
template <typename T>
GlobalMock<T> *GlobalMock<T>::instance_ = nullptr;
/**
* Test fixture for mask ROM tests.
*
* Test suites should derive their test fixtures from this class. This class
* enforces that mock methods are called in the order `EXPECT_CALL` statements
* are written. In cases where this behavior is not desired, test fixtures can
* derive from `Unordered<MaskRomTest>` instead to opt-out.
*/
class MaskRomTest : public testing::Test {
protected:
std::unique_ptr<testing::InSequence> seq_ =
std::make_unique<testing::InSequence>();
};
/**
* Mixin for unordered calls.
*
* @see MaskRomTest.
*/
template <typename T>
class Unordered : public T {
protected:
Unordered() : T() { T::seq_.reset(); }
};
} // namespace mask_rom_test
#endif // OPENTITAN_SW_DEVICE_LIB_TESTING_MASK_ROM_TEST_H_