[sw/silicon_creator] Make GlobalMock a base class

This change makes GlobalMock a base class for all mocks used in Mask ROM
unit tests to support multiple types of mocks, e.g. strict and nice, for
a Mask ROM module.

Signed-off-by: Alphan Ulusoy <alphan@google.com>
diff --git a/sw/device/lib/base/testing/mock_csr.h b/sw/device/lib/base/testing/mock_csr.h
index 2e119ec..f3c8282 100644
--- a/sw/device/lib/base/testing/mock_csr.h
+++ b/sw/device/lib/base/testing/mock_csr.h
@@ -14,18 +14,16 @@
 
 namespace internal {
 
-class MockCsr {
+class MockCsr : public ::mask_rom_test::GlobalMock<MockCsr> {
  public:
   MOCK_METHOD(uint32_t, Read, (uint32_t csr));
   MOCK_METHOD(void, Write, (uint32_t csr, uint32_t value));
   MOCK_METHOD(void, SetBits, (uint32_t csr, uint32_t mask));
   MOCK_METHOD(void, ClearBits, (uint32_t csr, uint32_t mask));
-  virtual ~MockCsr() {}
 };
 }  // namespace internal
 
-using MockCsr =
-    ::mask_rom_test::GlobalMock<testing::StrictMock<internal::MockCsr>>;
+using MockCsr = testing::StrictMock<internal::MockCsr>;
 
 /**
  * Conveninence fixture for creating CSR tests.
diff --git a/sw/device/lib/testing/mask_rom_test.h b/sw/device/lib/testing/mask_rom_test.h
index d7dab97..4d5e473 100644
--- a/sw/device/lib/testing/mask_rom_test.h
+++ b/sw/device/lib/testing/mask_rom_test.h
@@ -11,44 +11,50 @@
 namespace mask_rom_test {
 
 /**
- * Mixin for global mocks.
+ * Base class for mocks used in Mask ROM unit tests.
  *
- * `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.
+ * If a class `Mock` derives from `GlobalMock<Mock>`, `GlobalMock<Mock>`
+ * ensures that there is at most one instance of `Mock` at a time (checked at
+ * runtime) and makes this instance globally accessible via the static `Mock
+ * &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:
+ * Mock classes should be globally accessible so that mock functions can call
+ * their methods during tests. They can also be strict or nice depending on
+ * tests' needs. Mock classes that satisfy both requirements can be defined as
+ * follows:
  *
  *     namespace mask_rom_test {
  *     namespace internal {
- *     class MockFoo {
+ *     class MockFoo : public GlobalMock<MockFoo> {
  *       ...
  *     };
  *     }  // namespace internal
- *     // Type alias for making `internal::MockFoo` a global and strict mock.
- *     using MockFoo = GlobalMock<testing::StrictMock<internal::MockFoo>>;
+ *     // Type alias for making `internal::MockFoo` a strict mock.
+ *     using MockFoo = testing::StrictMock<internal::MockFoo>;
+ *     // Type alias for making `internal::MockFoo` a nice mock if needed.
+ *     using NiceMockFoo = testing::NiceMock<internal::MockFoo>;
  *     ...
  *     }  // namespace mask_rom_test
+ *
+ * This construction also ensures that we cannot have `MockFoo` and
+ * `NiceMockFoo` instantiated at the same time since they both derive from the
+ * same class, i.e. `GlobalMock<internal::MockFoo>`.
  */
-template <typename T>
-class GlobalMock : public T {
+template <typename Mock>
+class GlobalMock {
  public:
-  template <typename... Args>
-  GlobalMock(Args &&... args) : T(std::forward(args)...) {
+  GlobalMock() {
     if (instance_ != nullptr) {
       throw std::runtime_error("Mock is already instantiated.");
     }
-    instance_ = this;
+    instance_ = static_cast<Mock *>(this);
   }
 
-  static_assert(std::has_virtual_destructor<T>::value,
-                "Mock class must have a virtual destructor.");
+  // Note: Destructors of mock classes must be virtual for `testing::StrictMock`
+  // and `testing::NiceMock` to work correctly.
   virtual ~GlobalMock() { instance_ = nullptr; }
 
-  static GlobalMock<T> &Instance() {
+  static Mock &Instance() {
     if (instance_ == nullptr) {
       throw std::runtime_error("Mock is not instantiated yet.");
     }
@@ -61,10 +67,10 @@
   GlobalMock &operator=(GlobalMock &&) = delete;
 
  private:
-  static GlobalMock<T> *instance_;
+  static Mock *instance_;
 };
-template <typename T>
-GlobalMock<T> *GlobalMock<T>::instance_ = nullptr;
+template <typename Mock>
+Mock *GlobalMock<Mock>::instance_ = nullptr;
 
 /**
  * Test fixture for mask ROM tests.
diff --git a/sw/device/silicon_creator/lib/base/mock_abs_mmio.h b/sw/device/silicon_creator/lib/base/mock_abs_mmio.h
index a470e81..5b15152 100644
--- a/sw/device/silicon_creator/lib/base/mock_abs_mmio.h
+++ b/sw/device/silicon_creator/lib/base/mock_abs_mmio.h
@@ -14,18 +14,16 @@
 /**
  * Mock class for abs_mmio.c.
  */
-class MockAbsMmio {
+class MockAbsMmio : public GlobalMock<MockAbsMmio> {
  public:
   MOCK_METHOD(uint8_t, Read8, (uint32_t addr));
   MOCK_METHOD(void, Write8, (uint32_t addr, uint8_t value));
   MOCK_METHOD(uint32_t, Read32, (uint32_t addr));
   MOCK_METHOD(void, Write32, (uint32_t addr, uint32_t value));
-
-  virtual ~MockAbsMmio() {}
 };
 }  // namespace internal
 
-using MockAbsMmio = GlobalMock<testing::StrictMock<internal::MockAbsMmio>>;
+using MockAbsMmio = testing::StrictMock<internal::MockAbsMmio>;
 
 /**
  * Expect a read to the device `dev` at the given offset, returning the given
diff --git a/sw/device/silicon_creator/lib/base/mock_sec_mmio.h b/sw/device/silicon_creator/lib/base/mock_sec_mmio.h
index d04ca52..4582fec 100644
--- a/sw/device/silicon_creator/lib/base/mock_sec_mmio.h
+++ b/sw/device/silicon_creator/lib/base/mock_sec_mmio.h
@@ -14,7 +14,7 @@
 /**
  * Mock class for abs_mmio.c.
  */
-class MockSecMmio {
+class MockSecMmio : public GlobalMock<MockSecMmio> {
  public:
   MOCK_METHOD(void, Init, (sec_mmio_shutdown_handler callee));
   MOCK_METHOD(uint32_t, Read32, (uint32_t addr));
@@ -22,12 +22,10 @@
   MOCK_METHOD(void, WriteIncrement, (uint32_t value));
   MOCK_METHOD(void, CheckValues, (uint32_t rnd_offset));
   MOCK_METHOD(void, CheckCounters, (uint32_t expected_check_count));
-
-  virtual ~MockSecMmio() {}
 };
 }  // namespace internal
 
-using MockSecMmio = GlobalMock<testing::StrictMock<internal::MockSecMmio>>;
+using MockSecMmio = testing::StrictMock<internal::MockSecMmio>;
 
 /**
  * Expect a read to the device `dev` at the given offset, returning the given
diff --git a/sw/device/silicon_creator/lib/drivers/mock_alert.h b/sw/device/silicon_creator/lib/drivers/mock_alert.h
index 897346a..2934767 100644
--- a/sw/device/silicon_creator/lib/drivers/mock_alert.h
+++ b/sw/device/silicon_creator/lib/drivers/mock_alert.h
@@ -14,7 +14,7 @@
 /**
  * Mock class for alert.c.
  */
-class MockAlert {
+class MockAlert : public GlobalMock<MockAlert> {
  public:
   MOCK_METHOD(rom_error_t, alert_configure,
               (size_t, alert_class_t, alert_enable_t));
@@ -23,12 +23,11 @@
   MOCK_METHOD(rom_error_t, alert_class_configure,
               (alert_class_t, const alert_class_config_t *));
   MOCK_METHOD(rom_error_t, alert_ping_enable, ());
-  virtual ~MockAlert() {}
 };
 
 }  // namespace internal
 
-using MockAlert = GlobalMock<testing::StrictMock<internal::MockAlert>>;
+using MockAlert = testing::StrictMock<internal::MockAlert>;
 
 extern "C" {
 
diff --git a/sw/device/silicon_creator/lib/drivers/mock_hmac.h b/sw/device/silicon_creator/lib/drivers/mock_hmac.h
index a80ad0d..eeeba87 100644
--- a/sw/device/silicon_creator/lib/drivers/mock_hmac.h
+++ b/sw/device/silicon_creator/lib/drivers/mock_hmac.h
@@ -14,17 +14,16 @@
 /**
  * Mock class for hmac.c.
  */
-class MockHmac {
+class MockHmac : public GlobalMock<MockHmac> {
  public:
   MOCK_METHOD(void, sha256_init, ());
   MOCK_METHOD(rom_error_t, sha256_update, (const void *, size_t));
   MOCK_METHOD(rom_error_t, sha256_final, (hmac_digest_t *));
-  virtual ~MockHmac() {}
 };
 
 }  // namespace internal
 
-using MockHmac = GlobalMock<testing::StrictMock<internal::MockHmac>>;
+using MockHmac = testing::StrictMock<internal::MockHmac>;
 
 extern "C" {
 
diff --git a/sw/device/silicon_creator/lib/drivers/mock_otp.h b/sw/device/silicon_creator/lib/drivers/mock_otp.h
index 25348d6..11f5d6a 100644
--- a/sw/device/silicon_creator/lib/drivers/mock_otp.h
+++ b/sw/device/silicon_creator/lib/drivers/mock_otp.h
@@ -14,20 +14,27 @@
 /**
  * Mock class for otp.c.
  */
-class MockOtp {
+class MockOtp : public GlobalMock<MockOtp> {
  public:
   MOCK_METHOD(uint32_t, read32, (uint32_t address));
   MOCK_METHOD(uint32_t, read64, (uint32_t address));
   MOCK_METHOD(void, read, (uint32_t address, uint32_t *data, size_t num_words));
-  virtual ~MockOtp() {}
 };
 
 }  // namespace internal
 
-using MockOtp = GlobalMock<testing::StrictMock<internal::MockOtp>>;
+// Nice mock for shutdown unit tests.
+using NiceMockOtp = testing::NiceMock<internal::MockOtp>;
+// Strict mock for other unit tests.
+using MockOtp = testing::StrictMock<internal::MockOtp>;
 
 extern "C" {
 
+// Note: In the functions below, we use `MockOtp` only for conciseness. The
+// static `Instance()` method returns a reference to the same
+// `internal::MockOtp` instance regardless if we use `MockOtp`, `NiceMockOtp`,
+// or `internal::MockOtp`.
+
 uint32_t otp_read32(uint32_t address) {
   return MockOtp::Instance().read32(address);
 }
diff --git a/sw/device/silicon_creator/lib/mock_sigverify_mod_exp_ibex.h b/sw/device/silicon_creator/lib/mock_sigverify_mod_exp_ibex.h
index 624d8b1..0f1aa93 100644
--- a/sw/device/silicon_creator/lib/mock_sigverify_mod_exp_ibex.h
+++ b/sw/device/silicon_creator/lib/mock_sigverify_mod_exp_ibex.h
@@ -14,18 +14,17 @@
 /**
  * Mock class for sigverify_mod_exp_ibex.c
  */
-class MockSigverifyModExpIbex {
+class MockSigverifyModExpIbex : public GlobalMock<MockSigverifyModExpIbex> {
  public:
   MOCK_METHOD(rom_error_t, mod_exp,
               (const sigverify_rsa_key_t *, const sigverify_rsa_buffer_t *,
                sigverify_rsa_buffer_t *));
-  virtual ~MockSigverifyModExpIbex() {}
 };
 
 }  // namespace internal
 
 using MockSigverifyModExpIbex =
-    GlobalMock<testing::StrictMock<internal::MockSigverifyModExpIbex>>;
+    testing::StrictMock<internal::MockSigverifyModExpIbex>;
 
 extern "C" {
 
diff --git a/sw/device/silicon_creator/lib/mock_sigverify_mod_exp_otbn.h b/sw/device/silicon_creator/lib/mock_sigverify_mod_exp_otbn.h
index bda1832..848a869 100644
--- a/sw/device/silicon_creator/lib/mock_sigverify_mod_exp_otbn.h
+++ b/sw/device/silicon_creator/lib/mock_sigverify_mod_exp_otbn.h
@@ -14,18 +14,17 @@
 /**
  * Mock class for sigverify_mod_exp_otbn.c
  */
-class MockSigverifyModExpOtbn {
+class MockSigverifyModExpOtbn : public GlobalMock<MockSigverifyModExpOtbn> {
  public:
   MOCK_METHOD(rom_error_t, mod_exp,
               (const sigverify_rsa_key_t *, const sigverify_rsa_buffer_t *,
                sigverify_rsa_buffer_t *));
-  virtual ~MockSigverifyModExpOtbn() {}
 };
 
 }  // namespace internal
 
 using MockSigverifyModExpOtbn =
-    GlobalMock<testing::StrictMock<internal::MockSigverifyModExpOtbn>>;
+    testing::StrictMock<internal::MockSigverifyModExpOtbn>;
 
 extern "C" {
 
diff --git a/sw/device/silicon_creator/lib/shutdown_unittest.cc b/sw/device/silicon_creator/lib/shutdown_unittest.cc
index 7a0c96c..fcdb078 100644
--- a/sw/device/silicon_creator/lib/shutdown_unittest.cc
+++ b/sw/device/silicon_creator/lib/shutdown_unittest.cc
@@ -36,28 +36,26 @@
 // TODO(lowRISC/opentitan#7148): Refactor mocks into their own headers.
 namespace internal {
 // Create a mock for reading OTP words.
-class MockOtp {
+class MockOtp : public ::mask_rom_test::GlobalMock<MockOtp> {
  public:
   MOCK_METHOD(uint32_t, otp_read32, (uint32_t));
   virtual ~MockOtp() {}
 };
 
 // Create a mock for shutdown functions.
-class MockShutdown {
+class MockShutdown : public ::mask_rom_test::GlobalMock<MockShutdown> {
  public:
   MOCK_METHOD(void, shutdown_software_escalate, ());
   MOCK_METHOD(void, shutdown_keymgr_kill, ());
   MOCK_METHOD(void, shutdown_flash_kill, ());
   MOCK_METHOD(void, shutdown_hang, ());
-  virtual ~MockShutdown() {}
 };
 
 }  // namespace internal
 // Use NiceMock because we aren't interested in the specifics of OTP reads,
 // but we want to mock out calls to otp_read32.
-using MockOtp = mask_rom_test::GlobalMock<testing::NiceMock<internal::MockOtp>>;
-using MockShutdown =
-    mask_rom_test::GlobalMock<testing::StrictMock<internal::MockShutdown>>;
+using MockOtp = testing::NiceMock<internal::MockOtp>;
+using MockShutdown = testing::StrictMock<internal::MockShutdown>;
 extern "C" {
 uint32_t otp_read32(uint32_t address) {
   return MockOtp::Instance().otp_read32(address);
diff --git a/sw/device/silicon_creator/mask_rom/mock_romextimage_ptrs.h b/sw/device/silicon_creator/mask_rom/mock_romextimage_ptrs.h
index 0616427..5baca31 100644
--- a/sw/device/silicon_creator/mask_rom/mock_romextimage_ptrs.h
+++ b/sw/device/silicon_creator/mask_rom/mock_romextimage_ptrs.h
@@ -14,18 +14,15 @@
 /**
  * Mock class for romextimage_ptrs.h
  */
-class MockRomextimagePtrs {
+class MockRomextimagePtrs : public GlobalMock<MockRomextimagePtrs> {
  public:
   MOCK_METHOD(const manifest_t *, slot_a_manifest_ptr_get, ());
   MOCK_METHOD(const manifest_t *, slot_b_manifest_ptr_get, ());
-
-  virtual ~MockRomextimagePtrs() {}
 };
 
 }  // namespace internal
 
-using MockRomextimagePtrs =
-    GlobalMock<testing::StrictMock<internal::MockRomextimagePtrs>>;
+using MockRomextimagePtrs = testing::StrictMock<internal::MockRomextimagePtrs>;
 
 extern "C" {