summaryrefslogtreecommitdiffstats
path: root/UnitTestFrameworkPkg/Include/Library/FunctionMockLib.h
diff options
context:
space:
mode:
Diffstat (limited to 'UnitTestFrameworkPkg/Include/Library/FunctionMockLib.h')
-rw-r--r--UnitTestFrameworkPkg/Include/Library/FunctionMockLib.h131
1 files changed, 131 insertions, 0 deletions
diff --git a/UnitTestFrameworkPkg/Include/Library/FunctionMockLib.h b/UnitTestFrameworkPkg/Include/Library/FunctionMockLib.h
new file mode 100644
index 0000000000..bf7a706656
--- /dev/null
+++ b/UnitTestFrameworkPkg/Include/Library/FunctionMockLib.h
@@ -0,0 +1,131 @@
+/** @file
+ This header allows the mocking of free (C style) functions using gmock.
+
+ Copyright (c) 2023, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef FUNCTION_MOCK_LIB_H_
+#define FUNCTION_MOCK_LIB_H_
+
+#include <Library/GoogleTestLib.h>
+#include <Library/SubhookLib.h>
+#include <type_traits>
+
+//////////////////////////////////////////////////////////////////////////////
+// The below macros are the public function mock interface that are intended
+// to be used outside this file.
+
+#define MOCK_INTERFACE_DECLARATION(MOCK) \
+ static MOCK * Instance; \
+ MOCK (); \
+ ~MOCK ();
+
+#define MOCK_INTERFACE_DEFINITION(MOCK) \
+ MOCK_STATIC_INSTANCE_DEFINITION (MOCK) \
+ MOCK_INTERFACE_CONSTRUCTOR (MOCK) \
+ MOCK_INTERFACE_DECONSTRUCTOR (MOCK)
+
+// Mock function declaration for external functions (i.e. functions to
+// mock that do not exist in the compilation unit).
+#define MOCK_FUNCTION_DECLARATION(RET_TYPE, FUNC, ARGS) \
+ MOCK_FUNCTION_TYPE_DEFINITIONS(RET_TYPE, FUNC, ARGS) \
+ MOCK_METHOD (RET_TYPE, FUNC, ARGS);
+
+// Mock function definition for external functions (i.e. functions to
+// mock that do not exist in the compilation unit).
+#define MOCK_FUNCTION_DEFINITION(MOCK, FUNC, NUM_ARGS, CALL_TYPE) \
+ FUNCTION_DEFINITION_TO_CALL_MOCK(MOCK, FUNC, FUNC, NUM_ARGS, CALL_TYPE)
+
+// Mock function declaration for internal functions (i.e. functions to
+// mock that already exist in the compilation unit).
+#define MOCK_FUNCTION_INTERNAL_DECLARATION(RET_TYPE, FUNC, ARGS) \
+ MOCK_FUNCTION_DECLARATION(RET_TYPE, FUNC, ARGS) \
+ MOCK_FUNCTION_HOOK_DECLARATIONS(FUNC)
+
+// Mock function definition for internal functions (i.e. functions to
+// mock that already exist in the compilation unit). This definition also
+// implements MOCK_FUNC() which is later hooked as FUNC().
+#define MOCK_FUNCTION_INTERNAL_DEFINITION(MOCK, FUNC, NUM_ARGS, CALL_TYPE) \
+ FUNCTION_DEFINITION_TO_CALL_MOCK(MOCK, FUNC, MOCK##_##FUNC, NUM_ARGS, CALL_TYPE) \
+ MOCK_FUNCTION_HOOK_DEFINITIONS(MOCK, FUNC)
+
+//////////////////////////////////////////////////////////////////////////////
+// The below macros are private and should not be used outside this file.
+
+#define MOCK_FUNCTION_HOOK_DECLARATIONS(FUNC) \
+ static subhook::Hook Hook##FUNC; \
+ struct MockContainer_##FUNC { \
+ MockContainer_##FUNC (); \
+ ~MockContainer_##FUNC (); \
+ }; \
+ MockContainer_##FUNC MockContainerInst_##FUNC;
+
+// This definition implements a constructor and destructor inside a nested
+// class to enable automatic installation of the hooks to the associated
+// MOCK_FUNC() when the mock object is instantiated in scope and automatic
+// removal when the instantiated mock object goes out of scope.
+#define MOCK_FUNCTION_HOOK_DEFINITIONS(MOCK, FUNC) \
+ subhook :: Hook MOCK :: Hook##FUNC; \
+ MOCK :: MockContainer_##FUNC :: MockContainer_##FUNC () { \
+ if (MOCK :: Instance == NULL) \
+ MOCK :: Hook##FUNC .Install( \
+ (FUNC##_ret_type *) ::FUNC, \
+ (FUNC##_ret_type *) MOCK##_##FUNC); \
+ } \
+ MOCK :: MockContainer_##FUNC :: ~MockContainer_##FUNC () { \
+ MOCK :: Hook##FUNC .Remove(); \
+ } \
+ static_assert( \
+ std::is_same<decltype(&::FUNC), decltype(&MOCK##_##FUNC)>::value, \
+ "Function signature from 'MOCK_FUNCTION_INTERNAL_DEFINITION' macro " \
+ "invocation for '" #FUNC "' does not match the function signature " \
+ "of '" #FUNC "' function it is mocking. Mismatch could be due to " \
+ "different return type, arguments, or calling convention. See " \
+ "associated 'MOCK_FUNCTION_INTERNAL_DECLARATION' macro invocation " \
+ "for more details.");
+
+#define MOCK_FUNCTION_TYPE_DEFINITIONS(RET_TYPE, FUNC, ARGS) \
+ using FUNC##_ret_type = RET_TYPE; \
+ using FUNC##_type = FUNC##_ret_type ARGS;
+
+// This function definition simply calls MOCK::Instance->FUNC() which is the
+// mocked counterpart of the original function. This allows using gmock with
+// C free functions (since by default gmock only works with object methods).
+#define FUNCTION_DEFINITION_TO_CALL_MOCK(MOCK, FUNC, FUNC_DEF_NAME, NUM_ARGS, CALL_TYPE) \
+ extern "C" { \
+ typename MOCK :: FUNC##_ret_type CALL_TYPE FUNC_DEF_NAME( \
+ GMOCK_PP_REPEAT(GMOCK_INTERNAL_PARAMETER, \
+ (MOCK :: FUNC##_type), \
+ NUM_ARGS)) \
+ { \
+ EXPECT_TRUE(MOCK :: Instance != NULL) \
+ << "Called '" #FUNC "' in '" #MOCK "' function mock object before " \
+ << "an instance of '" #MOCK "' was created in test '" \
+ << ::testing::UnitTest::GetInstance()->current_test_info()->name() \
+ << "'."; \
+ return MOCK :: Instance->FUNC( \
+ GMOCK_PP_REPEAT(GMOCK_INTERNAL_FORWARD_ARG, \
+ (MOCK :: FUNC##_type), \
+ NUM_ARGS)); \
+ } \
+ }
+
+#define MOCK_STATIC_INSTANCE_DEFINITION(MOCK) MOCK * MOCK :: Instance = NULL;
+
+#define MOCK_INTERFACE_CONSTRUCTOR(MOCK) \
+ MOCK :: MOCK () { \
+ EXPECT_TRUE(MOCK :: Instance == NULL) \
+ << "Multiple instances of '" #MOCK "' function mock object were " \
+ << "created and only one instance is allowed in test '" \
+ << ::testing::UnitTest::GetInstance()->current_test_info()->name() \
+ << "'."; \
+ MOCK :: Instance = this; \
+ }
+
+#define MOCK_INTERFACE_DECONSTRUCTOR(MOCK) \
+ MOCK :: ~ MOCK () { \
+ MOCK :: Instance = NULL; \
+ }
+
+#endif // FUNCTION_MOCK_LIB_H_