/** @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 #include #include ////////////////////////////////////////////////////////////////////////////// // 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::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_