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