REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4389 * Add gmock support to GoogleTestLib * Add FunctionMockLib library class and library instance * Add GoogleTest extension to GoogleTestLib.h for CHAR16 type * Add GoogleTest extension to GoogleTestLib.h for buffer types * HOST_APPLICATION only supports IA32/X64 Cc: Sean Brogan <sean.brogan@microsoft.com> Cc: Michael Kubacki <mikuback@linux.microsoft.com> Cc: Michael D Kinney <michael.d.kinney@intel.com> Signed-off-by: Chris Johnson <chris.n.johnson@intel.com> Reviewed-by: Michael Kubacki <michael.kubacki@microsoft.com> Reviewed-by: Oliver Smith-Denny <osde@linux.microsoft.com> Reviewed-by: Michael D Kinney <michael.d.kinney@intel.com>
		
			
				
	
	
		
			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_
 |