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