mirror of https://github.com/acidanthera/audk.git
132 lines
7.1 KiB
C++
132 lines
7.1 KiB
C++
/** @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_
|