|  | // 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_ |