diff options
12 files changed, 297 insertions, 18 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_
diff --git a/UnitTestFrameworkPkg/Include/Library/GoogleTestLib.h b/UnitTestFrameworkPkg/Include/Library/GoogleTestLib.h index ebec766d4c..c0a3d8e660 100644 --- a/UnitTestFrameworkPkg/Include/Library/GoogleTestLib.h +++ b/UnitTestFrameworkPkg/Include/Library/GoogleTestLib.h @@ -10,5 +10,101 @@ #define GOOGLE_TEST_LIB_H_
#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+#include <cstring>
+
+extern "C" {
+#include <Uefi.h>
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Below are the action extensions to GoogleTest and gmock for EDK2 types.
+// These actions are intended to be used in EXPECT_CALL (and related gmock
+// macros) to support assignments to output arguments in the expected call.
+//
+
+// Action to support pointer types to a buffer (such as UINT8* or VOID*)
+ACTION_TEMPLATE (
+ SetArgBuffer,
+ HAS_1_TEMPLATE_PARAMS (size_t, ArgNum),
+ AND_2_VALUE_PARAMS (Buffer, ByteSize)
+ ) {
+ auto ArgBuffer = std::get<ArgNum>(args);
+
+ std::memcpy (ArgBuffer, Buffer, ByteSize);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Below are the matcher extensions to GoogleTest and gmock for EDK2 types.
+// These matchers are intended to be used in EXPECT_CALL (and related gmock
+// macros) to support comparisons to input arguments in the expected call.
+//
+// Note that these matchers can also be used in the EXPECT_THAT or ASSERT_THAT
+// macros to compare whether two values are equal.
+//
+
+// Matcher to support pointer types to a buffer (such as UINT8* or VOID* or
+// any structure pointer)
+MATCHER_P2 (
+ BufferEq,
+ Buffer,
+ ByteSize,
+ std::string ("buffer data to ") + (negation ? "not " : "") + "be the same"
+ ) {
+ UINT8 *Actual = (UINT8 *)arg;
+ UINT8 *Expected = (UINT8 *)Buffer;
+
+ for (size_t i = 0; i < ByteSize; i++) {
+ if (Actual[i] != Expected[i]) {
+ *result_listener << "byte at offset " << i
+ << " does not match expected. [" << std::hex
+ << "Actual: 0x" << std::setw (2) << std::setfill ('0')
+ << (unsigned int)Actual[i] << ", "
+ << "Expected: 0x" << std::setw (2) << std::setfill ('0')
+ << (unsigned int)Expected[i] << "]";
+ return false;
+ }
+ }
+
+ *result_listener << "all bytes match";
+ return true;
+}
+
+// Matcher to support CHAR16* type
+MATCHER_P (
+ Char16StrEq,
+ String,
+ std::string ("strings to ") + (negation ? "not " : "") + "be the same"
+ ) {
+ CHAR16 *Actual = (CHAR16 *)arg;
+ CHAR16 *Expected = (CHAR16 *)String;
+
+ for (size_t i = 0; Actual[i] != 0; i++) {
+ if (Actual[i] != Expected[i]) {
+ *result_listener << "character at offset " << i
+ << " does not match expected. [" << std::hex
+ << "Actual: 0x" << std::setw (4) << std::setfill ('0')
+ << Actual[i];
+
+ if (std::isprint (Actual[i])) {
+ *result_listener << " ('" << (char)Actual[i] << "')";
+ }
+
+ *result_listener << ", Expected: 0x" << std::setw (4) << std::setfill ('0')
+ << Expected[i];
+
+ if (std::isprint (Expected[i])) {
+ *result_listener << " ('" << (char)Expected[i] << "')";
+ }
+
+ *result_listener << "]";
+
+ return false;
+ }
+ }
+
+ *result_listener << "strings match";
+ return true;
+}
#endif
diff --git a/UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.inf b/UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.inf index 052c7f5572..eeee6bc2b3 100644 --- a/UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.inf +++ b/UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.inf @@ -16,7 +16,7 @@ LIBRARY_CLASS = CmockaLib|HOST_APPLICATION
#
-# VALID_ARCHITECTURES = IA32 X64 ARM AARCH64
+# VALID_ARCHITECTURES = IA32 X64
#
[Sources]
diff --git a/UnitTestFrameworkPkg/Library/FunctionMockLib/FunctionMockLib.c b/UnitTestFrameworkPkg/Library/FunctionMockLib/FunctionMockLib.c new file mode 100644 index 0000000000..b7bb23f186 --- /dev/null +++ b/UnitTestFrameworkPkg/Library/FunctionMockLib/FunctionMockLib.c @@ -0,0 +1,7 @@ +/** @file
+ Macro-only FunctionMockLib library instance with no services.
+
+ Copyright (c) 2023, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
diff --git a/UnitTestFrameworkPkg/Library/FunctionMockLib/FunctionMockLib.inf b/UnitTestFrameworkPkg/Library/FunctionMockLib/FunctionMockLib.inf new file mode 100644 index 0000000000..44c5946be5 --- /dev/null +++ b/UnitTestFrameworkPkg/Library/FunctionMockLib/FunctionMockLib.inf @@ -0,0 +1,31 @@ +## @file
+# This module provides FunctionMockLib Library implementation.
+#
+# Copyright (c) 2023, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010018
+ BASE_NAME = FunctionMockLib
+ MODULE_UNI_FILE = FunctionMockLib.uni
+ FILE_GUID = DF1CAF2F-D584-4EC1-9ABF-07E8B10AD560
+ MODULE_TYPE = HOST_APPLICATION
+ VERSION_STRING = 0.1
+ LIBRARY_CLASS = FunctionMockLib
+
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Sources]
+ FunctionMockLib.c
+
+[LibraryClasses]
+ GoogleTestLib
+ SubhookLib
+
+[Packages]
+ MdePkg/MdePkg.dec
+ UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec
diff --git a/UnitTestFrameworkPkg/Library/FunctionMockLib/FunctionMockLib.uni b/UnitTestFrameworkPkg/Library/FunctionMockLib/FunctionMockLib.uni new file mode 100644 index 0000000000..13e5308ce0 --- /dev/null +++ b/UnitTestFrameworkPkg/Library/FunctionMockLib/FunctionMockLib.uni @@ -0,0 +1,11 @@ +// /** @file
+// This module provides FunctionMockLib Library implementation.
+//
+// Copyright (c) 2023, Intel Corporation. All rights reserved.<BR>
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT #language en-US "FunctionMockLib Library implementation"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module provides FunctionMockLib Library implementation."
diff --git a/UnitTestFrameworkPkg/Library/GoogleTestLib/GoogleTestLib.inf b/UnitTestFrameworkPkg/Library/GoogleTestLib/GoogleTestLib.inf index 68db75d702..0104384953 100644 --- a/UnitTestFrameworkPkg/Library/GoogleTestLib/GoogleTestLib.inf +++ b/UnitTestFrameworkPkg/Library/GoogleTestLib/GoogleTestLib.inf @@ -7,30 +7,27 @@ ##
[Defines]
- INF_VERSION = 0x00010005
+ INF_VERSION = 0x00010018
BASE_NAME = GoogleTestLib
MODULE_UNI_FILE = GoogleTestLib.uni
FILE_GUID = A90E4751-AD30-43CC-980B-01E356B49ADF
- MODULE_TYPE = BASE
+ MODULE_TYPE = HOST_APPLICATION
VERSION_STRING = 0.1
- LIBRARY_CLASS = GoogleTestLib|HOST_APPLICATION
+ LIBRARY_CLASS = GoogleTestLib
#
-# VALID_ARCHITECTURES = IA32 X64 ARM AARCH64
+# VALID_ARCHITECTURES = IA32 X64
#
[Sources]
googletest/googletest/src/gtest-all.cc
+ googletest/googlemock/src/gmock-all.cc
[Packages]
+ MdePkg/MdePkg.dec
UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec
[BuildOptions]
- MSFT:*_*_*_CC_FLAGS == /c /EHsc /Zi
- MSFT:NOOPT_*_*_CC_FLAGS = /Od
-
- GCC:*_*_*_CC_FLAGS == -g -c
-
- GCC:NOOPT_*_*_CC_FLAGS = -O0
- GCC:*_*_IA32_CC_FLAGS = -m32
- GCC:*_*_X64_CC_FLAGS = -m64
+ MSFT:*_*_*_CC_FLAGS == /c /EHsc /Zi /Od
+ GCC:*_*_IA32_CC_FLAGS == -g -c -fshort-wchar -O0 -m32
+ GCC:*_*_X64_CC_FLAGS == -g -c -fshort-wchar -O0 -m64
diff --git a/UnitTestFrameworkPkg/Library/GoogleTestLib/GoogleTestLib.uni b/UnitTestFrameworkPkg/Library/GoogleTestLib/GoogleTestLib.uni index 14c862a237..695588ce3f 100644 --- a/UnitTestFrameworkPkg/Library/GoogleTestLib/GoogleTestLib.uni +++ b/UnitTestFrameworkPkg/Library/GoogleTestLib/GoogleTestLib.uni @@ -1,10 +1,7 @@ // /** @file
// This module provides GoogleTest Library implementation.
//
-// This module provides GoogleTest Library implementation.
-//
// Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
-//
// SPDX-License-Identifier: BSD-2-Clause-Patent
//
// **/
diff --git a/UnitTestFrameworkPkg/Test/UnitTestFrameworkPkgHostTest.dsc b/UnitTestFrameworkPkg/Test/UnitTestFrameworkPkgHostTest.dsc index 722509c8f2..dbb429faae 100644 --- a/UnitTestFrameworkPkg/Test/UnitTestFrameworkPkgHostTest.dsc +++ b/UnitTestFrameworkPkg/Test/UnitTestFrameworkPkgHostTest.dsc @@ -32,8 +32,9 @@ # Build HOST_APPLICATION Libraries
#
UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.inf
+ UnitTestFrameworkPkg/Library/FunctionMockLib/FunctionMockLib.inf
UnitTestFrameworkPkg/Library/GoogleTestLib/GoogleTestLib.inf
- UnitTestFrameworkPkg/Library/SubhookLib/SubhookLib.inf
UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.inf
UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.inf
+ UnitTestFrameworkPkg/Library/SubhookLib/SubhookLib.inf
UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLibCmocka.inf
diff --git a/UnitTestFrameworkPkg/UnitTestFrameworkPkg.ci.yaml b/UnitTestFrameworkPkg/UnitTestFrameworkPkg.ci.yaml index d8f8e024c4..be839d1af0 100644 --- a/UnitTestFrameworkPkg/UnitTestFrameworkPkg.ci.yaml +++ b/UnitTestFrameworkPkg/UnitTestFrameworkPkg.ci.yaml @@ -79,9 +79,14 @@ "AuditOnly": False, # Fails test but run in AuditOnly mode to collect log
"IgnoreFiles": [ # use gitignore syntax to ignore errors in matching files
"Library/CmockaLib/cmocka/**/*.*", # not going to spell check a submodule
- "Library/GoogleTestLib/googletest/**/*.*" # not going to spell check a submodule
+ "Library/GoogleTestLib/googletest/**/*.*", # not going to spell check a submodule
+ "Library/SubhookLib/subhook/**/*.*" # not going to spell check a submodule
],
"ExtendWords": [ # words to extend to the dictionary for this package
+ "Pointee",
+ "gmock",
+ "GMOCK",
+ "DSUBHOOK",
"testcase",
"testsuites",
"cmocka",
diff --git a/UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec b/UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec index 30b489915d..ef0a148d48 100644 --- a/UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec +++ b/UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec @@ -26,6 +26,7 @@ PrivateInclude
Library/CmockaLib/cmocka/include/cmockery
Library/GoogleTestLib/googletest/googletest
+ Library/GoogleTestLib/googletest/googlemock
[LibraryClasses]
## @libraryclass Allows save and restore unit test internal state
@@ -36,6 +37,7 @@ #
GoogleTestLib|Include/Library/GoogleTestLib.h
SubhookLib|Include/Library/SubhookLib.h
+ FunctionMockLib|Include/Library/FunctionMockLib.h
[LibraryClasses.Common.Private]
## @libraryclass Provides a unit test result report
diff --git a/UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc b/UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc index e77897bd32..7866c36e66 100644 --- a/UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc +++ b/UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc @@ -16,6 +16,7 @@ CmockaLib|UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.inf
GoogleTestLib|UnitTestFrameworkPkg/Library/GoogleTestLib/GoogleTestLib.inf
SubhookLib|UnitTestFrameworkPkg/Library/SubhookLib/SubhookLib.inf
+ FunctionMockLib|UnitTestFrameworkPkg/Library/FunctionMockLib/FunctionMockLib.inf
UnitTestLib|UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLibCmocka.inf
DebugLib|UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.inf
MemoryAllocationLib|UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.inf
|