From: "Michael D Kinney" <michael.d.kinney@intel.com>
To: devel@edk2.groups.io
Cc: Sean Brogan <sean.brogan@microsoft.com>,
Bret Barkelew <Bret.Barkelew@microsoft.com>
Subject: [Patch 05/11] UnitTestFrameworkPkg/Library: Add library instances
Date: Thu, 23 Jan 2020 18:10:26 -0800 [thread overview]
Message-ID: <20200124021032.13808-6-michael.d.kinney@intel.com> (raw)
In-Reply-To: <20200124021032.13808-1-michael.d.kinney@intel.com>
Add the following library instances that are used to
build unit tests for host and target environments.
* CmockaLib with cmocka submodule to:
https://git.cryptomilk.org/projects/cmocka.git
* DebugLibPosix - Instance of DebugLib based on POSIX
APIs (e.g. printf).
* MemoryAllocationLibPosix - Instance of MemoryAllocationLib
based on POSIX APIs (e.g. malloc/free).
* UnitTestBootLibNull - Null instance of the UnitTestBootLib
* UnitTestBootLibUsbClass - UnitTestBootLib instances that
supports setting boot next to a USB device.
* UnitTestLib - UnitTestLib instance that is designed to work
with PEI, DXE, SMM, and UEFI Shell target environments.
* UnitTestLibCmocka - UintTestLib instance that uses cmocka
APIs and can only be use in a host environment.
* UnitTestPersistenceLibNull - Null instance of the
UnitTestPersistenceLib
* UnitTestPersistenceLibSimpleFileSystem - UnitTestPersistenceLib
instance that can safe the unit test framework state to a
media device that supports the UEFI Simple File System
Protocol.
* UnitTestResultReportLibConOut - UnitTestResultReportLib
instance that sends report results to the UEFI standard
output console.
* UnitTestResultReportLibDebugLib - UnitTestResultReportLib
instance that sends report results to a DebugLib using
DEBUG() macros.
Cc: Sean Brogan <sean.brogan@microsoft.com>
Cc: Bret Barkelew <Bret.Barkelew@microsoft.com>
Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com>
---
.gitmodules | 3 +
.../Library/CmockaLib/CmockaLib.inf | 35 +
.../Library/CmockaLib/CmockaLib.uni | 14 +
UnitTestFrameworkPkg/Library/CmockaLib/cmocka | 1 +
.../Posix/DebugLibPosix/DebugLibPosix.c | 279 ++++++
.../Posix/DebugLibPosix/DebugLibPosix.inf | 35 +
.../Posix/DebugLibPosix/DebugLibPosix.uni | 14 +
.../MemoryAllocationLibPosix.c | 631 +++++++++++++
.../MemoryAllocationLibPosix.inf | 27 +
.../MemoryAllocationLibPosix.uni | 14 +
.../UnitTestBootLibNull/UnitTestBootLibNull.c | 26 +
.../UnitTestBootLibNull.inf | 23 +
.../UnitTestBootLibNull.uni | 11 +
.../UnitTestBootLibUsbClass.c | 127 +++
.../UnitTestBootLibUsbClass.inf | 34 +
.../UnitTestBootLibUsbClass.uni | 12 +
.../Library/UnitTestLib/Assert.c | 491 ++++++++++
.../Library/UnitTestLib/AssertCmocka.c | 335 +++++++
.../Library/UnitTestLib/Log.c | 200 ++++
.../Library/UnitTestLib/RunTests.c | 171 ++++
.../Library/UnitTestLib/RunTestsCmocka.c | 278 ++++++
.../Library/UnitTestLib/UnitTestLib.c | 853 ++++++++++++++++++
.../Library/UnitTestLib/UnitTestLib.inf | 37 +
.../Library/UnitTestLib/UnitTestLib.uni | 11 +
.../Library/UnitTestLib/UnitTestLibCmocka.inf | 38 +
.../Library/UnitTestLib/UnitTestLibCmocka.uni | 11 +
.../UnitTestPersistenceLibNull.c | 75 ++
.../UnitTestPersistenceLibNull.inf | 28 +
.../UnitTestPersistenceLibNull.uni | 11 +
.../UnitTestPersistenceLibSimpleFileSystem.c | 416 +++++++++
...UnitTestPersistenceLibSimpleFileSystem.inf | 47 +
...UnitTestPersistenceLibSimpleFileSystem.uni | 15 +
.../UnitTestResultReportLib.c | 216 +++++
.../UnitTestResultReportLibConOut.c | 48 +
.../UnitTestResultReportLibConOut.inf | 29 +
.../UnitTestResultReportLibConOut.uni | 11 +
.../UnitTestResultReportLibDebugLib.c | 47 +
.../UnitTestResultReportLibDebugLib.inf | 28 +
.../UnitTestResultReportLibDebugLib.uni | 11 +
39 files changed, 4693 insertions(+)
create mode 100644 UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.inf
create mode 100644 UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.uni
create mode 160000 UnitTestFrameworkPkg/Library/CmockaLib/cmocka
create mode 100644 UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.c
create mode 100644 UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.inf
create mode 100644 UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.uni
create mode 100644 UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.c
create mode 100644 UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.inf
create mode 100644 UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.uni
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootLibNull.c
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootLibNull.inf
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootLibNull.uni
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibUsbClass.c
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibUsbClass.inf
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibUsbClass.uni
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/Assert.c
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/AssertCmocka.c
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/Log.c
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/RunTests.c
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/RunTestsCmocka.c
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.c
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.inf
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.uni
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLibCmocka.inf
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLibCmocka.uni
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.c
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.inf
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.uni
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.c
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.inf
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.uni
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLib.c
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.c
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.inf
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.uni
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.c
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.inf
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.uni
diff --git a/.gitmodules b/.gitmodules
index 508f0c1828..b30f5bf136 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -4,3 +4,6 @@
[submodule "SoftFloat"]
path = ArmPkg/Library/ArmSoftFloatLib/berkeley-softfloat-3
url = https://github.com/ucb-bar/berkeley-softfloat-3.git
+[submodule "UnitTestFrameworkPkg/Library/CmockaLib/cmocka"]
+ path = UnitTestFrameworkPkg/Library/CmockaLib/cmocka
+ url = https://git.cryptomilk.org/projects/cmocka.git
diff --git a/UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.inf b/UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.inf
new file mode 100644
index 0000000000..07da7a88e9
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.inf
@@ -0,0 +1,35 @@
+## @file
+# This module provides Cmocka Library implementation.
+#
+# Copyright (c) 2019 - 2020, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = CmockaLib
+ MODULE_UNI_FILE = CmockaLib.uni
+ FILE_GUID = F1662152-3399-49AC-BE44-CAA97575FACE
+ MODULE_TYPE = BASE
+ VERSION_STRING = 0.1
+ LIBRARY_CLASS = CmockaLib|HOST_APPLICATION
+
+#
+# VALID_ARCHITECTURES = IA32 X64 ARM AARCH64
+#
+
+[Sources]
+ cmocka/src/cmocka.c
+
+[Packages]
+ UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec
+
+[BuildOptions]
+ MSFT:*_*_*_CC_FLAGS == /c -DHAVE_VSNPRINTF -DHAVE_SNPRINTF
+ MSFT:NOOPT_*_*_CC_FLAGS = /Od
+
+ GCC:*_*_*_CC_FLAGS == -g -DHAVE_SIGNAL_H
+ GCC:NOOPT_*_*_CC_FLAGS = -O0
+ GCC:*_*_IA32_CC_FLAGS = -m32
+ GCC:*_*_X64_CC_FLAGS = -m64
diff --git a/UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.uni b/UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.uni
new file mode 100644
index 0000000000..acdb72d075
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.uni
@@ -0,0 +1,14 @@
+// /** @file
+// This module provides Cmocka Library implementation.
+//
+// This module provides Cmocka Library implementation.
+//
+// Copyright (c) 2019 - 2020, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT #language en-US "Cmocka Library implementation"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module provides Cmocka Library implementation."
diff --git a/UnitTestFrameworkPkg/Library/CmockaLib/cmocka b/UnitTestFrameworkPkg/Library/CmockaLib/cmocka
new file mode 160000
index 0000000000..1cc9cde344
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/CmockaLib/cmocka
@@ -0,0 +1 @@
+Subproject commit 1cc9cde3448cdd2e000886a26acf1caac2db7cf1
diff --git a/UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.c b/UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.c
new file mode 100644
index 0000000000..0daea00728
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.c
@@ -0,0 +1,279 @@
+/** @file
+ Instance of Debug Library based on POSIX APIs
+
+ Uses Print Library to produce formatted output strings sent to printf().
+
+ Copyright (c) 2018 - 2020, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <stdio.h>
+
+#include <Base.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseLib.h>
+#include <Library/PrintLib.h>
+#include <Library/BaseMemoryLib.h>
+
+///
+/// Define the maximum debug and assert message length that this library supports
+///
+#define MAX_DEBUG_MESSAGE_LENGTH 0x100
+
+/**
+ Prints a debug message to the debug output device if the specified error level is enabled.
+
+ If any bit in ErrorLevel is also set in DebugPrintErrorLevelLib function
+ GetDebugPrintErrorLevel (), then print the message specified by Format and the
+ associated variable argument list to the debug output device.
+
+ If Format is NULL, then ASSERT().
+
+ @param ErrorLevel The error level of the debug message.
+ @param Format The format string for the debug message to print.
+ @param ... The variable argument list whose contents are accessed
+ based on the format string specified by Format.
+
+**/
+VOID
+EFIAPI
+DebugPrint (
+ IN UINTN ErrorLevel,
+ IN CONST CHAR8 *Format,
+ ...
+ )
+{
+ VA_LIST Marker;
+
+ VA_START (Marker, Format);
+ DebugVPrint (ErrorLevel, Format, Marker);
+ VA_END (Marker);
+}
+
+/**
+ Prints a debug message to the debug output device if the specified
+ error level is enabled.
+
+ If any bit in ErrorLevel is also set in DebugPrintErrorLevelLib function
+ GetDebugPrintErrorLevel (), then print the message specified by Format and
+ the associated variable argument list to the debug output device.
+
+ If Format is NULL, then ASSERT().
+
+ @param ErrorLevel The error level of the debug message.
+ @param Format Format string for the debug message to print.
+ @param VaListMarker VA_LIST marker for the variable argument list.
+
+**/
+VOID
+EFIAPI
+DebugVPrint (
+ IN UINTN ErrorLevel,
+ IN CONST CHAR8 *Format,
+ IN VA_LIST VaListMarker
+ )
+{
+ CHAR8 Buffer[MAX_DEBUG_MESSAGE_LENGTH];
+
+ AsciiVSPrint (Buffer, sizeof (Buffer), Format, VaListMarker);
+ printf ("%s", Buffer);
+}
+
+/**
+ Prints a debug message to the debug output device if the specified
+ error level is enabled.
+ This function use BASE_LIST which would provide a more compatible
+ service than VA_LIST.
+
+ If any bit in ErrorLevel is also set in DebugPrintErrorLevelLib function
+ GetDebugPrintErrorLevel (), then print the message specified by Format and
+ the associated variable argument list to the debug output device.
+
+ If Format is NULL, then ASSERT().
+
+ @param ErrorLevel The error level of the debug message.
+ @param Format Format string for the debug message to print.
+ @param BaseListMarker BASE_LIST marker for the variable argument list.
+
+**/
+VOID
+EFIAPI
+DebugBPrint (
+ IN UINTN ErrorLevel,
+ IN CONST CHAR8 *Format,
+ IN BASE_LIST BaseListMarker
+ )
+{
+ CHAR8 Buffer[MAX_DEBUG_MESSAGE_LENGTH];
+
+ AsciiBSPrint (Buffer, sizeof (Buffer), Format, BaseListMarker);
+ printf ("%s", Buffer);
+}
+
+/**
+ Prints an assert message containing a filename, line number, and description.
+ This may be followed by a breakpoint or a dead loop.
+
+ Print a message of the form "ASSERT <FileName>(<LineNumber>): <Description>\n"
+ to the debug output device. If DEBUG_PROPERTY_ASSERT_BREAKPOINT_ENABLED bit of
+ PcdDebugPropertyMask is set then CpuBreakpoint() is called. Otherwise, if
+ DEBUG_PROPERTY_ASSERT_DEADLOOP_ENABLED bit of PcdDebugPropertyMask is set then
+ CpuDeadLoop() is called. If neither of these bits are set, then this function
+ returns immediately after the message is printed to the debug output device.
+ DebugAssert() must actively prevent recursion. If DebugAssert() is called while
+ processing another DebugAssert(), then DebugAssert() must return immediately.
+
+ If FileName is NULL, then a <FileName> string of "(NULL) Filename" is printed.
+ If Description is NULL, then a <Description> string of "(NULL) Description" is printed.
+
+ @param FileName The pointer to the name of the source file that generated the assert condition.
+ @param LineNumber The line number in the source file that generated the assert condition
+ @param Description The pointer to the description of the assert condition.
+
+**/
+VOID
+EFIAPI
+DebugAssert (
+ IN CONST CHAR8 *FileName,
+ IN UINTN LineNumber,
+ IN CONST CHAR8 *Description
+ )
+{
+ printf ("ASSERT: %s(%d): %s\n", FileName, (INT32)(UINT32)LineNumber, Description);
+
+ //
+ // Generate a Breakpoint, DeadLoop, or NOP based on PCD settings
+ //
+ if ((PcdGet8(PcdDebugPropertyMask) & DEBUG_PROPERTY_ASSERT_BREAKPOINT_ENABLED) != 0) {
+ CpuBreakpoint ();
+ } else if ((PcdGet8(PcdDebugPropertyMask) & DEBUG_PROPERTY_ASSERT_DEADLOOP_ENABLED) != 0) {
+ CpuDeadLoop ();
+ }
+}
+
+/**
+ Fills a target buffer with PcdDebugClearMemoryValue, and returns the target buffer.
+
+ This function fills Length bytes of Buffer with the value specified by
+ PcdDebugClearMemoryValue, and returns Buffer.
+
+ If Buffer is NULL, then ASSERT().
+ If Length is greater than (MAX_ADDRESS - Buffer + 1), then ASSERT().
+
+ @param Buffer The pointer to the target buffer to be filled with PcdDebugClearMemoryValue.
+ @param Length The number of bytes in Buffer to fill with zeros PcdDebugClearMemoryValue.
+
+ @return Buffer The pointer to the target buffer filled with PcdDebugClearMemoryValue.
+
+**/
+VOID *
+EFIAPI
+DebugClearMemory (
+ OUT VOID *Buffer,
+ IN UINTN Length
+ )
+{
+ //
+ // If Buffer is NULL, then ASSERT().
+ //
+ ASSERT (Buffer != NULL);
+
+ //
+ // SetMem() checks for the the ASSERT() condition on Length and returns Buffer
+ //
+ return SetMem (Buffer, Length, PcdGet8(PcdDebugClearMemoryValue));
+}
+
+/**
+ Returns TRUE if ASSERT() macros are enabled.
+
+ This function returns TRUE if the DEBUG_PROPERTY_DEBUG_ASSERT_ENABLED bit of
+ PcdDebugPropertyMask is set. Otherwise FALSE is returned.
+
+ @retval TRUE The DEBUG_PROPERTY_DEBUG_ASSERT_ENABLED bit of PcdDebugPropertyMask is set.
+ @retval FALSE The DEBUG_PROPERTY_DEBUG_ASSERT_ENABLED bit of PcdDebugPropertyMask is clear.
+
+**/
+BOOLEAN
+EFIAPI
+DebugAssertEnabled (
+ VOID
+ )
+{
+ return (BOOLEAN) ((PcdGet8(PcdDebugPropertyMask) & DEBUG_PROPERTY_DEBUG_ASSERT_ENABLED) != 0);
+}
+
+/**
+ Returns TRUE if DEBUG() macros are enabled.
+
+ This function returns TRUE if the DEBUG_PROPERTY_DEBUG_PRINT_ENABLED bit of
+ PcdDebugPropertyMask is set. Otherwise FALSE is returned.
+
+ @retval TRUE The DEBUG_PROPERTY_DEBUG_PRINT_ENABLED bit of PcdDebugPropertyMask is set.
+ @retval FALSE The DEBUG_PROPERTY_DEBUG_PRINT_ENABLED bit of PcdDebugPropertyMask is clear.
+
+**/
+BOOLEAN
+EFIAPI
+DebugPrintEnabled (
+ VOID
+ )
+{
+ return (BOOLEAN) ((PcdGet8(PcdDebugPropertyMask) & DEBUG_PROPERTY_DEBUG_PRINT_ENABLED) != 0);
+}
+
+/**
+ Returns TRUE if DEBUG_CODE() macros are enabled.
+
+ This function returns TRUE if the DEBUG_PROPERTY_DEBUG_CODE_ENABLED bit of
+ PcdDebugPropertyMask is set. Otherwise FALSE is returned.
+
+ @retval TRUE The DEBUG_PROPERTY_DEBUG_CODE_ENABLED bit of PcdDebugPropertyMask is set.
+ @retval FALSE The DEBUG_PROPERTY_DEBUG_CODE_ENABLED bit of PcdDebugPropertyMask is clear.
+
+**/
+BOOLEAN
+EFIAPI
+DebugCodeEnabled (
+ VOID
+ )
+{
+ return (BOOLEAN) ((PcdGet8(PcdDebugPropertyMask) & DEBUG_PROPERTY_DEBUG_CODE_ENABLED) != 0);
+}
+
+/**
+ Returns TRUE if DEBUG_CLEAR_MEMORY() macro is enabled.
+
+ This function returns TRUE if the DEBUG_PROPERTY_CLEAR_MEMORY_ENABLED bit of
+ PcdDebugPropertyMask is set. Otherwise FALSE is returned.
+
+ @retval TRUE The DEBUG_PROPERTY_CLEAR_MEMORY_ENABLED bit of PcdDebugPropertyMask is set.
+ @retval FALSE The DEBUG_PROPERTY_CLEAR_MEMORY_ENABLED bit of PcdDebugPropertyMask is clear.
+
+**/
+BOOLEAN
+EFIAPI
+DebugClearMemoryEnabled (
+ VOID
+ )
+{
+ return (BOOLEAN) ((PcdGet8(PcdDebugPropertyMask) & DEBUG_PROPERTY_CLEAR_MEMORY_ENABLED) != 0);
+}
+
+/**
+ Returns TRUE if any one of the bit is set both in ErrorLevel and PcdFixedDebugPrintErrorLevel.
+
+ This function compares the bit mask of ErrorLevel and PcdFixedDebugPrintErrorLevel.
+
+ @retval TRUE Current ErrorLevel is supported.
+ @retval FALSE Current ErrorLevel is not supported.
+
+**/
+BOOLEAN
+EFIAPI
+DebugPrintLevelEnabled (
+ IN CONST UINTN ErrorLevel
+ )
+{
+ return (BOOLEAN) ((ErrorLevel & PcdGet32(PcdFixedDebugPrintErrorLevel)) != 0);
+}
diff --git a/UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.inf b/UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.inf
new file mode 100644
index 0000000000..5babbca3b0
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.inf
@@ -0,0 +1,35 @@
+## @file
+# Instance of Debug Library based on POSIX APIs
+#
+# Uses Print Library to produce formatted output strings sent to printf().
+#
+# Copyright (c) 2018 - 2020, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = DebugLibPosix
+ MODULE_UNI_FILE = DebugLibPosix.uni
+ FILE_GUID = 6A77CE89-C1B6-4A6B-9561-07D7127514A7
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = DebugLib|HOST_APPLICATION
+
+[Sources]
+ DebugLibPosix.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ BaseMemoryLib
+ PcdLib
+ PrintLib
+ BaseLib
+
+[Pcd]
+ gEfiMdePkgTokenSpaceGuid.PcdDebugClearMemoryValue ## SOMETIMES_CONSUMES
+ gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask ## CONSUMES
+ gEfiMdePkgTokenSpaceGuid.PcdFixedDebugPrintErrorLevel ## CONSUMES
diff --git a/UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.uni b/UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.uni
new file mode 100644
index 0000000000..d34f1a05be
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.uni
@@ -0,0 +1,14 @@
+// /** @file
+// Instance of Debug Library based on POSIX APIs
+//
+// Uses Print Library to produce formatted output strings sent to printf().
+//
+// Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT #language en-US "Instance of Debug Library based on POSIX APIs"
+
+#string STR_MODULE_DESCRIPTION #language en-US "Uses Print Library to produce formatted output strings sent to printf()."
diff --git a/UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.c b/UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.c
new file mode 100644
index 0000000000..1f590524d8
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.c
@@ -0,0 +1,631 @@
+/** @file
+ Instance of Memory Allocation Library based on POSIX APIs
+
+ Uses POSIX APIs malloc() and free() to allocate and free memory.
+
+ Copyright (c) 2018 - 2020, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <Uefi.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DebugLib.h>
+
+///
+/// Signature for PAGE_HEAD structure
+/// Used to verify that buffer being freed was allocated by this library.
+///
+#define PAGE_HEAD_PRIVATE_SIGNATURE SIGNATURE_32 ('P', 'H', 'D', 'R')
+
+///
+/// Structure placed immediately before an aligned allocation to store the
+/// information required to free the entire buffer allocated to support then
+/// aligned allocation.
+///
+typedef struct {
+ UINT32 Signature;
+ VOID *AllocatedBufffer;
+ UINTN TotalPages;
+ VOID *AlignedBuffer;
+ UINTN AlignedPages;
+} PAGE_HEAD;
+
+/**
+ Allocates one or more 4KB pages of type EfiBootServicesData.
+
+ Allocates the number of 4KB pages of type EfiBootServicesData and returns a pointer to the
+ allocated buffer. The buffer returned is aligned on a 4KB boundary. If Pages is 0, then NULL
+ is returned. If there is not enough memory remaining to satisfy the request, then NULL is
+ returned.
+
+ @param Pages The number of 4 KB pages to allocate.
+
+ @return A pointer to the allocated buffer or NULL if allocation fails.
+
+**/
+VOID *
+EFIAPI
+AllocatePages (
+ IN UINTN Pages
+ )
+{
+ return AllocateAlignedPages (Pages, SIZE_4KB);
+}
+
+/**
+ Allocates one or more 4KB pages of type EfiRuntimeServicesData.
+
+ Allocates the number of 4KB pages of type EfiRuntimeServicesData and returns a pointer to the
+ allocated buffer. The buffer returned is aligned on a 4KB boundary. If Pages is 0, then NULL
+ is returned. If there is not enough memory remaining to satisfy the request, then NULL is
+ returned.
+
+ @param Pages The number of 4 KB pages to allocate.
+
+ @return A pointer to the allocated buffer or NULL if allocation fails.
+
+**/
+VOID *
+EFIAPI
+AllocateRuntimePages (
+ IN UINTN Pages
+ )
+{
+ return AllocatePages (Pages);
+}
+
+/**
+ Allocates one or more 4KB pages of type EfiReservedMemoryType.
+
+ Allocates the number of 4KB pages of type EfiReservedMemoryType and returns a pointer to the
+ allocated buffer. The buffer returned is aligned on a 4KB boundary. If Pages is 0, then NULL
+ is returned. If there is not enough memory remaining to satisfy the request, then NULL is
+ returned.
+
+ @param Pages The number of 4 KB pages to allocate.
+
+ @return A pointer to the allocated buffer or NULL if allocation fails.
+
+**/
+VOID *
+EFIAPI
+AllocateReservedPages (
+ IN UINTN Pages
+ )
+{
+ return AllocatePages (Pages);
+}
+
+/**
+ Frees one or more 4KB pages that were previously allocated with one of the page allocation
+ functions in the Memory Allocation Library.
+
+ Frees the number of 4KB pages specified by Pages from the buffer specified by Buffer. Buffer
+ must have been allocated on a previous call to the page allocation services of the Memory
+ Allocation Library. If it is not possible to free allocated pages, then this function will
+ perform no actions.
+
+ If Buffer was not allocated with a page allocation function in the Memory Allocation Library,
+ then ASSERT().
+ If Pages is zero, then ASSERT().
+
+ @param Buffer The pointer to the buffer of pages to free.
+ @param Pages The number of 4 KB pages to free.
+
+**/
+VOID
+EFIAPI
+FreePages (
+ IN VOID *Buffer,
+ IN UINTN Pages
+ )
+{
+ FreeAlignedPages (Buffer, Pages);
+}
+
+/**
+ Allocates one or more 4KB pages of type EfiBootServicesData at a specified alignment.
+
+ Allocates the number of 4KB pages specified by Pages of type EfiBootServicesData with an
+ alignment specified by Alignment. The allocated buffer is returned. If Pages is 0, then NULL is
+ returned. If there is not enough memory at the specified alignment remaining to satisfy the
+ request, then NULL is returned.
+
+ If Alignment is not a power of two and Alignment is not zero, then ASSERT().
+ If Pages plus EFI_SIZE_TO_PAGES (Alignment) overflows, then ASSERT().
+
+ @param Pages The number of 4 KB pages to allocate.
+ @param Alignment The requested alignment of the allocation. Must be a power of two.
+ If Alignment is zero, then byte alignment is used.
+
+ @return A pointer to the allocated buffer or NULL if allocation fails.
+
+**/VOID *
+EFIAPI
+AllocateAlignedPages (
+ IN UINTN Pages,
+ IN UINTN Alignment
+ )
+{
+ PAGE_HEAD PageHead;
+ PAGE_HEAD *PageHeadPtr;
+ UINTN AlignmentMask;
+
+ ASSERT ((Alignment & (Alignment - 1)) == 0);
+
+ if (Alignment < SIZE_4KB) {
+ Alignment = SIZE_4KB;
+ }
+ AlignmentMask = Alignment - 1;
+
+ //
+ // We need reserve Alignment pages for PAGE_HEAD, as meta data.
+ //
+ PageHead.Signature = PAGE_HEAD_PRIVATE_SIGNATURE;
+ PageHead.TotalPages = Pages + EFI_SIZE_TO_PAGES (Alignment) * 2;
+ PageHead.AlignedPages = Pages;
+ PageHead.AllocatedBufffer = malloc (EFI_PAGES_TO_SIZE (PageHead.TotalPages));
+ if (PageHead.AllocatedBufffer == NULL) {
+ return NULL;
+ }
+ PageHead.AlignedBuffer = (VOID *)(((UINTN) PageHead.AllocatedBufffer + AlignmentMask) & ~AlignmentMask);
+ if ((UINTN)PageHead.AlignedBuffer - (UINTN)PageHead.AllocatedBufffer < sizeof(PAGE_HEAD)) {
+ PageHead.AlignedBuffer = (VOID *)((UINTN)PageHead.AlignedBuffer + Alignment);
+ }
+
+ PageHeadPtr = (VOID *)((UINTN)PageHead.AlignedBuffer - sizeof(PAGE_HEAD));
+ memcpy (PageHeadPtr, &PageHead, sizeof(PAGE_HEAD));
+
+ return PageHead.AlignedBuffer;
+}
+
+/**
+ Allocates one or more 4KB pages of type EfiRuntimeServicesData at a specified alignment.
+
+ Allocates the number of 4KB pages specified by Pages of type EfiRuntimeServicesData with an
+ alignment specified by Alignment. The allocated buffer is returned. If Pages is 0, then NULL is
+ returned. If there is not enough memory at the specified alignment remaining to satisfy the
+ request, then NULL is returned.
+
+ If Alignment is not a power of two and Alignment is not zero, then ASSERT().
+ If Pages plus EFI_SIZE_TO_PAGES (Alignment) overflows, then ASSERT().
+
+ @param Pages The number of 4 KB pages to allocate.
+ @param Alignment The requested alignment of the allocation. Must be a power of two.
+ If Alignment is zero, then byte alignment is used.
+
+ @return A pointer to the allocated buffer or NULL if allocation fails.
+
+**/
+VOID *
+EFIAPI
+AllocateAlignedRuntimePages (
+ IN UINTN Pages,
+ IN UINTN Alignment
+ )
+{
+ return AllocateAlignedPages (Pages, Alignment);
+}
+
+/**
+ Allocates one or more 4KB pages of type EfiReservedMemoryType at a specified alignment.
+
+ Allocates the number of 4KB pages specified by Pages of type EfiReservedMemoryType with an
+ alignment specified by Alignment. The allocated buffer is returned. If Pages is 0, then NULL is
+ returned. If there is not enough memory at the specified alignment remaining to satisfy the
+ request, then NULL is returned.
+
+ If Alignment is not a power of two and Alignment is not zero, then ASSERT().
+ If Pages plus EFI_SIZE_TO_PAGES (Alignment) overflows, then ASSERT().
+
+ @param Pages The number of 4 KB pages to allocate.
+ @param Alignment The requested alignment of the allocation. Must be a power of two.
+ If Alignment is zero, then byte alignment is used.
+
+ @return A pointer to the allocated buffer or NULL if allocation fails.
+
+**/
+VOID *
+EFIAPI
+AllocateAlignedReservedPages (
+ IN UINTN Pages,
+ IN UINTN Alignment
+ )
+{
+ return AllocateAlignedPages (Pages, Alignment);
+}
+
+/**
+ Frees one or more 4KB pages that were previously allocated with one of the aligned page
+ allocation functions in the Memory Allocation Library.
+
+ Frees the number of 4KB pages specified by Pages from the buffer specified by Buffer. Buffer
+ must have been allocated on a previous call to the aligned page allocation services of the Memory
+ Allocation Library. If it is not possible to free allocated pages, then this function will
+ perform no actions.
+
+ If Buffer was not allocated with an aligned page allocation function in the Memory Allocation
+ Library, then ASSERT().
+ If Pages is zero, then ASSERT().
+
+ @param Buffer The pointer to the buffer of pages to free.
+ @param Pages The number of 4 KB pages to free.
+
+**/
+VOID
+EFIAPI
+FreeAlignedPages (
+ IN VOID *Buffer,
+ IN UINTN Pages
+ )
+{
+ PAGE_HEAD *PageHeadPtr;
+
+ //
+ // NOTE: Partial free is not supported. Just keep it.
+ //
+ PageHeadPtr = (VOID *)((UINTN)Buffer - sizeof(PAGE_HEAD));
+ if (PageHeadPtr->Signature != PAGE_HEAD_PRIVATE_SIGNATURE) {
+ return;
+ }
+ if (PageHeadPtr->AlignedPages != Pages) {
+ return;
+ }
+
+ PageHeadPtr->Signature = 0;
+ free (PageHeadPtr->AllocatedBufffer);
+}
+
+/**
+ Allocates a buffer of type EfiBootServicesData.
+
+ Allocates the number bytes specified by AllocationSize of type EfiBootServicesData and returns a
+ pointer to the allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is
+ returned. If there is not enough memory remaining to satisfy the request, then NULL is returned.
+
+ @param AllocationSize The number of bytes to allocate.
+
+ @return A pointer to the allocated buffer or NULL if allocation fails.
+
+**/VOID *
+EFIAPI
+AllocatePool (
+ IN UINTN AllocationSize
+ )
+{
+ return malloc (AllocationSize);
+}
+
+/**
+ Allocates a buffer of type EfiRuntimeServicesData.
+
+ Allocates the number bytes specified by AllocationSize of type EfiRuntimeServicesData and returns
+ a pointer to the allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is
+ returned. If there is not enough memory remaining to satisfy the request, then NULL is returned.
+
+ @param AllocationSize The number of bytes to allocate.
+
+ @return A pointer to the allocated buffer or NULL if allocation fails.
+
+**/
+VOID *
+EFIAPI
+AllocateRuntimePool (
+ IN UINTN AllocationSize
+ )
+{
+ return AllocatePool (AllocationSize);
+}
+
+/**
+ Allocates a buffer of type EfiReservedMemoryType.
+
+ Allocates the number bytes specified by AllocationSize of type EfiReservedMemoryType and returns
+ a pointer to the allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is
+ returned. If there is not enough memory remaining to satisfy the request, then NULL is returned.
+
+ @param AllocationSize The number of bytes to allocate.
+
+ @return A pointer to the allocated buffer or NULL if allocation fails.
+
+**/
+VOID *
+EFIAPI
+AllocateReservedPool (
+ IN UINTN AllocationSize
+ )
+{
+ return AllocatePool (AllocationSize);
+}
+
+/**
+ Allocates and zeros a buffer of type EfiBootServicesData.
+
+ Allocates the number bytes specified by AllocationSize of type EfiBootServicesData, clears the
+ buffer with zeros, and returns a pointer to the allocated buffer. If AllocationSize is 0, then a
+ valid buffer of 0 size is returned. If there is not enough memory remaining to satisfy the
+ request, then NULL is returned.
+
+ @param AllocationSize The number of bytes to allocate and zero.
+
+ @return A pointer to the allocated buffer or NULL if allocation fails.
+
+**/
+VOID *
+EFIAPI
+AllocateZeroPool (
+ IN UINTN AllocationSize
+ )
+{
+ VOID *Buffer;
+
+ Buffer = malloc (AllocationSize);
+ if (Buffer == NULL) {
+ return NULL;
+ }
+ memset (Buffer, 0, AllocationSize);
+ return Buffer;
+}
+
+/**
+ Allocates and zeros a buffer of type EfiRuntimeServicesData.
+
+ Allocates the number bytes specified by AllocationSize of type EfiRuntimeServicesData, clears the
+ buffer with zeros, and returns a pointer to the allocated buffer. If AllocationSize is 0, then a
+ valid buffer of 0 size is returned. If there is not enough memory remaining to satisfy the
+ request, then NULL is returned.
+
+ @param AllocationSize The number of bytes to allocate and zero.
+
+ @return A pointer to the allocated buffer or NULL if allocation fails.
+
+**/
+VOID *
+EFIAPI
+AllocateRuntimeZeroPool (
+ IN UINTN AllocationSize
+ )
+{
+ return AllocateZeroPool (AllocationSize);
+}
+
+/**
+ Allocates and zeros a buffer of type EfiReservedMemoryType.
+
+ Allocates the number bytes specified by AllocationSize of type EfiReservedMemoryType, clears the
+ buffer with zeros, and returns a pointer to the allocated buffer. If AllocationSize is 0, then a
+ valid buffer of 0 size is returned. If there is not enough memory remaining to satisfy the
+ request, then NULL is returned.
+
+ @param AllocationSize The number of bytes to allocate and zero.
+
+ @return A pointer to the allocated buffer or NULL if allocation fails.
+
+**/
+VOID *
+EFIAPI
+AllocateReservedZeroPool (
+ IN UINTN AllocationSize
+ )
+{
+ return AllocateZeroPool (AllocationSize);
+}
+
+/**
+ Copies a buffer to an allocated buffer of type EfiBootServicesData.
+
+ Allocates the number bytes specified by AllocationSize of type EfiBootServicesData, copies
+ AllocationSize bytes from Buffer to the newly allocated buffer, and returns a pointer to the
+ allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is returned. If there
+ is not enough memory remaining to satisfy the request, then NULL is returned.
+
+ If Buffer is NULL, then ASSERT().
+ If AllocationSize is greater than (MAX_ADDRESS - Buffer + 1), then ASSERT().
+
+ @param AllocationSize The number of bytes to allocate and zero.
+ @param Buffer The buffer to copy to the allocated buffer.
+
+ @return A pointer to the allocated buffer or NULL if allocation fails.
+
+**/
+VOID *
+EFIAPI
+AllocateCopyPool (
+ IN UINTN AllocationSize,
+ IN CONST VOID *Buffer
+ )
+{
+ VOID *Memory;
+
+ Memory = malloc (AllocationSize);
+ if (Memory == NULL) {
+ return NULL;
+ }
+ memcpy (Memory, Buffer, AllocationSize);
+ return Memory;
+}
+
+/**
+ Copies a buffer to an allocated buffer of type EfiRuntimeServicesData.
+
+ Allocates the number bytes specified by AllocationSize of type EfiRuntimeServicesData, copies
+ AllocationSize bytes from Buffer to the newly allocated buffer, and returns a pointer to the
+ allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is returned. If there
+ is not enough memory remaining to satisfy the request, then NULL is returned.
+
+ If Buffer is NULL, then ASSERT().
+ If AllocationSize is greater than (MAX_ADDRESS - Buffer + 1), then ASSERT().
+
+ @param AllocationSize The number of bytes to allocate and zero.
+ @param Buffer The buffer to copy to the allocated buffer.
+
+ @return A pointer to the allocated buffer or NULL if allocation fails.
+
+**/
+VOID *
+EFIAPI
+AllocateRuntimeCopyPool (
+ IN UINTN AllocationSize,
+ IN CONST VOID *Buffer
+ )
+{
+ return AllocateCopyPool (AllocationSize, Buffer);
+}
+
+/**
+ Copies a buffer to an allocated buffer of type EfiReservedMemoryType.
+
+ Allocates the number bytes specified by AllocationSize of type EfiReservedMemoryType, copies
+ AllocationSize bytes from Buffer to the newly allocated buffer, and returns a pointer to the
+ allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is returned. If there
+ is not enough memory remaining to satisfy the request, then NULL is returned.
+
+ If Buffer is NULL, then ASSERT().
+ If AllocationSize is greater than (MAX_ADDRESS - Buffer + 1), then ASSERT().
+
+ @param AllocationSize The number of bytes to allocate and zero.
+ @param Buffer The buffer to copy to the allocated buffer.
+
+ @return A pointer to the allocated buffer or NULL if allocation fails.
+
+**/
+VOID *
+EFIAPI
+AllocateReservedCopyPool (
+ IN UINTN AllocationSize,
+ IN CONST VOID *Buffer
+ )
+{
+ return AllocateCopyPool (AllocationSize, Buffer);
+}
+
+/**
+ Reallocates a buffer of type EfiBootServicesData.
+
+ Allocates and zeros the number bytes specified by NewSize from memory of type
+ EfiBootServicesData. If OldBuffer is not NULL, then the smaller of OldSize and
+ NewSize bytes are copied from OldBuffer to the newly allocated buffer, and
+ OldBuffer is freed. A pointer to the newly allocated buffer is returned.
+ If NewSize is 0, then a valid buffer of 0 size is returned. If there is not
+ enough memory remaining to satisfy the request, then NULL is returned.
+
+ If the allocation of the new buffer is successful and the smaller of NewSize and OldSize
+ is greater than (MAX_ADDRESS - OldBuffer + 1), then ASSERT().
+
+ @param OldSize The size, in bytes, of OldBuffer.
+ @param NewSize The size, in bytes, of the buffer to reallocate.
+ @param OldBuffer The buffer to copy to the allocated buffer. This is an optional
+ parameter that may be NULL.
+
+ @return A pointer to the allocated buffer or NULL if allocation fails.
+
+**/
+VOID *
+EFIAPI
+ReallocatePool (
+ IN UINTN OldSize,
+ IN UINTN NewSize,
+ IN VOID *OldBuffer OPTIONAL
+ )
+{
+ VOID *NewBuffer;
+
+ NewBuffer = malloc (NewSize);
+ if (NewBuffer != NULL && OldBuffer != NULL) {
+ memcpy (NewBuffer, OldBuffer, MIN (OldSize, NewSize));
+ }
+ if (OldBuffer != NULL) {
+ FreePool(OldBuffer);
+ }
+ return NewBuffer;
+}
+
+/**
+ Reallocates a buffer of type EfiRuntimeServicesData.
+
+ Allocates and zeros the number bytes specified by NewSize from memory of type
+ EfiRuntimeServicesData. If OldBuffer is not NULL, then the smaller of OldSize and
+ NewSize bytes are copied from OldBuffer to the newly allocated buffer, and
+ OldBuffer is freed. A pointer to the newly allocated buffer is returned.
+ If NewSize is 0, then a valid buffer of 0 size is returned. If there is not
+ enough memory remaining to satisfy the request, then NULL is returned.
+
+ If the allocation of the new buffer is successful and the smaller of NewSize and OldSize
+ is greater than (MAX_ADDRESS - OldBuffer + 1), then ASSERT().
+
+ @param OldSize The size, in bytes, of OldBuffer.
+ @param NewSize The size, in bytes, of the buffer to reallocate.
+ @param OldBuffer The buffer to copy to the allocated buffer. This is an optional
+ parameter that may be NULL.
+
+ @return A pointer to the allocated buffer or NULL if allocation fails.
+
+**/
+VOID *
+EFIAPI
+ReallocateRuntimePool (
+ IN UINTN OldSize,
+ IN UINTN NewSize,
+ IN VOID *OldBuffer OPTIONAL
+ )
+{
+ return ReallocatePool (OldSize, NewSize, OldBuffer);
+}
+
+/**
+ Reallocates a buffer of type EfiReservedMemoryType.
+
+ Allocates and zeros the number bytes specified by NewSize from memory of type
+ EfiReservedMemoryType. If OldBuffer is not NULL, then the smaller of OldSize and
+ NewSize bytes are copied from OldBuffer to the newly allocated buffer, and
+ OldBuffer is freed. A pointer to the newly allocated buffer is returned.
+ If NewSize is 0, then a valid buffer of 0 size is returned. If there is not
+ enough memory remaining to satisfy the request, then NULL is returned.
+
+ If the allocation of the new buffer is successful and the smaller of NewSize and OldSize
+ is greater than (MAX_ADDRESS - OldBuffer + 1), then ASSERT().
+
+ @param OldSize The size, in bytes, of OldBuffer.
+ @param NewSize The size, in bytes, of the buffer to reallocate.
+ @param OldBuffer The buffer to copy to the allocated buffer. This is an optional
+ parameter that may be NULL.
+
+ @return A pointer to the allocated buffer or NULL if allocation fails.
+
+**/
+VOID *
+EFIAPI
+ReallocateReservedPool (
+ IN UINTN OldSize,
+ IN UINTN NewSize,
+ IN VOID *OldBuffer OPTIONAL
+ )
+{
+ return ReallocatePool (OldSize, NewSize, OldBuffer);
+}
+
+/**
+ Frees a buffer that was previously allocated with one of the pool allocation functions in the
+ Memory Allocation Library.
+
+ Frees the buffer specified by Buffer. Buffer must have been allocated on a previous call to the
+ pool allocation services of the Memory Allocation Library. If it is not possible to free pool
+ resources, then this function will perform no actions.
+
+ If Buffer was not allocated with a pool allocation function in the Memory Allocation Library,
+ then ASSERT().
+
+ @param Buffer The pointer to the buffer to free.
+
+**/
+VOID
+EFIAPI
+FreePool (
+ IN VOID *Buffer
+ )
+{
+ free (Buffer);
+}
diff --git a/UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.inf b/UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.inf
new file mode 100644
index 0000000000..44ec3fd517
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.inf
@@ -0,0 +1,27 @@
+## @file
+# Instance of Memory Allocation Library based on POSIX APIs
+#
+# Uses POSIX APIs malloc() and free() to allocate and free memory.
+#
+# Copyright (c) 2018 - 2020, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = MemoryAllocationLibPosix
+ MODULE_UNI_FILE = MemoryAllocationLibPosix.uni
+ FILE_GUID = A1672454-A3D3-4AAC-A86B-8D63132BBB91
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = MemoryAllocationLib|HOST_APPLICATION
+
+[Sources]
+ MemoryAllocationLibPosix.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ BaseLib
diff --git a/UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.uni b/UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.uni
new file mode 100644
index 0000000000..854b427976
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.uni
@@ -0,0 +1,14 @@
+// /** @file
+// Instance of Memory Allocation Library based on POSIX APIs
+//
+// Uses POSIX APIs malloc() and free() to allocate and free memory.
+//
+// Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT #language en-US "Instance of Memory Allocation Library based on POSIX APIs"
+
+#string STR_MODULE_DESCRIPTION #language en-US "Uses POSIX APIs malloc() and free() to allocate and free memory."
diff --git a/UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootLibNull.c b/UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootLibNull.c
new file mode 100644
index 0000000000..c5a5162c58
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootLibNull.c
@@ -0,0 +1,26 @@
+/**
+ NULL implementation for UnitTestBootLib to allow simple compilation
+
+ Copyright (c) Microsoft Corporation.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <PiDxe.h>
+
+/**
+ Set the boot manager to boot from a specific device on the next boot. This
+ should be set only for the next boot and shouldn't require any manual clean up
+
+ @retval EFI_SUCCESS Boot device for next boot was set.
+ @retval EFI_UNSUPPORTED Setting the boot device for the next boot is not
+ supportted.
+ @retval Other Boot device for next boot can not be set.
+**/
+EFI_STATUS
+EFIAPI
+SetBootNextDevice(
+ VOID
+ )
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootLibNull.inf b/UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootLibNull.inf
new file mode 100644
index 0000000000..a4a907b65b
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootLibNull.inf
@@ -0,0 +1,23 @@
+## @file
+# NULL library for UnitTestBootUsb
+#
+# Copyright (c) Microsoft Corporation.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010017
+ BASE_NAME = UnitTestBootLibNull
+ MODULE_UNI_FILE = UnitTestBootLibNull.uni
+ FILE_GUID = f143e75d-76e1-4040-b134-8f4f0bd5e3bd
+ VERSION_STRING = 1.0
+ MODULE_TYPE = DXE_DRIVER
+ LIBRARY_CLASS = UnitTestBootLib
+
+[Sources]
+ UnitTestBootLibNull.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+
diff --git a/UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootLibNull.uni b/UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootLibNull.uni
new file mode 100644
index 0000000000..1ed3b20544
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootLibNull.uni
@@ -0,0 +1,11 @@
+// /** @file
+// NULL library for UnitTestBootUsb
+//
+// Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT #language en-US "NULL library for UnitTestBootUsb"
+
+#string STR_MODULE_DESCRIPTION #language en-US "NULL library for UnitTestBootUsb."
diff --git a/UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibUsbClass.c b/UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibUsbClass.c
new file mode 100644
index 0000000000..4ce48bd233
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibUsbClass.c
@@ -0,0 +1,127 @@
+/**
+ Implement UnitTestBootLib using USB Class Boot option. This should be
+ industry standard and should work on all platforms
+
+ Copyright (c) Microsoft Corporation.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <PiDxe.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiBootManagerLib.h>
+#include <Library/DevicePathLib.h>
+#include <Protocol/DevicePath.h>
+#include <Library/MemoryAllocationLib.h>
+
+/**
+ Set the boot manager to boot from a specific device on the next boot. This
+ should be set only for the next boot and shouldn't require any manual clean up
+
+ @retval EFI_SUCCESS Boot device for next boot was set.
+ @retval EFI_UNSUPPORTED Setting the boot device for the next boot is not
+ supportted.
+ @retval Other Boot device for next boot can not be set.
+**/
+EFI_STATUS
+EFIAPI
+SetBootNextDevice (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_BOOT_MANAGER_LOAD_OPTION NewOption;
+ UINT32 Attributes;
+ UINT8 *OptionalData;
+ UINT32 OptionalDataSize;
+ UINT16 BootNextValue;
+ USB_CLASS_DEVICE_PATH UsbDp;
+ EFI_DEVICE_PATH_PROTOCOL *DpEnd;
+ EFI_DEVICE_PATH_PROTOCOL *Dp;
+ BOOLEAN NewOptionValid;
+
+ OptionalData = NULL;
+ OptionalDataSize = 0;
+ BootNextValue = 0xABCD; // this should be a safe number...
+ DpEnd = NULL;
+ Dp = NULL;
+ NewOptionValid = FALSE;
+
+ UsbDp.Header.Length[0] = (UINT8)(sizeof(USB_CLASS_DEVICE_PATH) & 0xff);
+ UsbDp.Header.Length[1] = (UINT8)(sizeof(USB_CLASS_DEVICE_PATH) >> 8);
+ UsbDp.Header.Type = MESSAGING_DEVICE_PATH;
+ UsbDp.Header.SubType = MSG_USB_CLASS_DP;
+ UsbDp.VendorId = 0xFFFF;
+ UsbDp.ProductId = 0xFFFF;
+ UsbDp.DeviceClass = 0xFF;
+ UsbDp.DeviceSubClass = 0xFF;
+ UsbDp.DeviceProtocol = 0xFF;
+
+ Attributes = LOAD_OPTION_ACTIVE;
+
+ DpEnd = AppendDevicePathNode (NULL, NULL);
+ if (DpEnd == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a: Unable to create device path. DpEnd is NULL.\n", __FUNCTION__));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto CLEANUP;
+ }
+
+ //@MRT --- Is this memory leak because we lose the old Dp memory
+ Dp = AppendDevicePathNode (
+ DpEnd,
+ (EFI_DEVICE_PATH_PROTOCOL *)&UsbDp
+ );
+ if (Dp == NULL) {
+ DEBUG((DEBUG_ERROR, "%a: Unable to create device path. Dp is NULL.\n", __FUNCTION__));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto CLEANUP;
+ }
+
+ Status = EfiBootManagerInitializeLoadOption (
+ &NewOption,
+ (UINTN) BootNextValue,
+ LoadOptionTypeBoot,
+ Attributes,
+ L"Generic USB Class Device",
+ Dp,
+ OptionalData,
+ OptionalDataSize
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Error creating load option. Status = %r\n", __FUNCTION__, Status));
+ goto CLEANUP;
+ }
+
+ NewOptionValid = TRUE;
+ DEBUG ((DEBUG_VERBOSE, "%a: Generic USB Class Device boot option created.\n", __FUNCTION__));
+ Status = EfiBootManagerLoadOptionToVariable (&NewOption);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Error Saving boot option NV variable. Status = %r\n", __FUNCTION__, Status));
+ goto CLEANUP;
+ }
+
+ //
+ // Set Boot Next
+ //
+ Status = gRT->SetVariable (
+ L"BootNext",
+ &gEfiGlobalVariableGuid,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof(BootNextValue),
+ &(BootNextValue)
+ );
+
+ DEBUG((DEBUG_VERBOSE, "%a - Set BootNext Status (%r)\n", __FUNCTION__, Status));
+
+CLEANUP:
+ if (Dp != NULL) {
+ FreePool (Dp);
+ }
+ if (DpEnd != NULL) {
+ FreePool (DpEnd);
+ }
+ if (NewOptionValid) {
+ EfiBootManagerFreeLoadOption (&NewOption);
+ }
+ return Status;
+}
diff --git a/UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibUsbClass.inf b/UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibUsbClass.inf
new file mode 100644
index 0000000000..80c4e4f111
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibUsbClass.inf
@@ -0,0 +1,34 @@
+## @file
+# Library to support booting to USB on the next boot
+# This instance uses the industry standard usb class boot option.
+#
+# Copyright (c) Microsoft Corporation.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x00010017
+ BASE_NAME = UnitTestBootLibUsbClass
+ MODULE_UNI_FILE = UnitTestBootLibUsbClass.uni
+ FILE_GUID = DFADE2A2-DB69-47DE-A37A-40FB6D52E844
+ VERSION_STRING = 1.0
+ MODULE_TYPE = UEFI_APPLICATION
+ LIBRARY_CLASS = UnitTestBootLib
+
+[Sources]
+ UnitTestBootLibUsbClass.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec
+
+[LibraryClasses]
+ DebugLib
+ UefiRuntimeServicesTableLib
+ MemoryAllocationLib
+ DevicePathLib
+ UefiBootManagerLib
+
+[Guids]
+ gEfiGlobalVariableGuid ## CONSUMES ## Used to probe boot options and set BootNext.
diff --git a/UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibUsbClass.uni b/UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibUsbClass.uni
new file mode 100644
index 0000000000..8468b3537c
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibUsbClass.uni
@@ -0,0 +1,12 @@
+// /** @file
+// Library to support booting to USB on the next boot
+// This instance uses the industry standard usb class boot option.
+//
+// Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT #language en-US "Library to support booting to USB on the next boot"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This instance uses the industry standard usb class boot option.."
diff --git a/UnitTestFrameworkPkg/Library/UnitTestLib/Assert.c b/UnitTestFrameworkPkg/Library/UnitTestLib/Assert.c
new file mode 100644
index 0000000000..dd85b84b08
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestLib/Assert.c
@@ -0,0 +1,491 @@
+/**
+ Implement UnitTestLib assert services
+
+ Copyright (c) Microsoft Corporation.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+#include <UnitTestFrameworkTypes.h>
+#include <Library/UnitTestLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PrintLib.h>
+
+STATIC
+EFI_STATUS
+AddUnitTestFailure (
+ IN OUT UNIT_TEST *UnitTest,
+ IN CONST CHAR8 *FailureMessage,
+ IN FAILURE_TYPE FailureType
+ )
+{
+ //
+ // Make sure that you're cooking with gas.
+ //
+ if (UnitTest == NULL || FailureMessage == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ UnitTest->FailureType = FailureType;
+ AsciiStrCpyS (
+ &UnitTest->FailureMessage[0],
+ UNIT_TEST_TESTFAILUREMSG_LENGTH,
+ FailureMessage
+ );
+
+ return EFI_SUCCESS;
+}
+
+STATIC
+VOID
+UnitTestLogFailure (
+ IN FAILURE_TYPE FailureType,
+ IN CONST CHAR8 *Format,
+ ...
+ )
+{
+ UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle;
+ CHAR8 LogString[UNIT_TEST_TESTFAILUREMSG_LENGTH];
+ VA_LIST Marker;
+
+ //
+ // Get active Framework handle
+ //
+ FrameworkHandle = GetActiveFrameworkHandle ();
+
+ //
+ // Convert the message to an ASCII String
+ //
+ VA_START (Marker, Format);
+ AsciiVSPrint (LogString, sizeof (LogString), Format, Marker);
+ VA_END (Marker);
+
+ //
+ // Finally, add the string to the log.
+ //
+ AddUnitTestFailure (
+ ((UNIT_TEST_FRAMEWORK *)FrameworkHandle)->CurrentTest,
+ LogString,
+ FailureType
+ );
+
+ return;
+}
+
+/**
+ If Expression is TRUE, then TRUE is returned.
+ If Expression is FALSE, then an assert is triggered and the location of the
+ assert provided by FunctionName, LineNumber, FileName, and Description are
+ recorded and FALSE is returned.
+
+ @param[in] Expression The BOOLEAN result of the expression evaluation.
+ @param[in] FunctionName Null-terminated ASCII string of the function
+ executing the assert macro.
+ @param[in] LineNumber The source file line number of the assert macro.
+ @param[in] FileName Null-terminated ASCII string of the filename
+ executing the assert macro.
+ @param[in] Description Null-terminated ASCII string of the expression being
+ evaluated.
+
+ @retval TRUE Expression is TRUE.
+ @retval FALSE Expression is FALSE.
+**/
+BOOLEAN
+EFIAPI
+UnitTestAssertTrue (
+ IN BOOLEAN Expression,
+ IN CONST CHAR8 *FunctionName,
+ IN UINTN LineNumber,
+ IN CONST CHAR8 *FileName,
+ IN CONST CHAR8 *Description
+ )
+{
+ if (!Expression) {
+ UnitTestLogFailure (
+ FAILURETYPE_ASSERTTRUE,
+ "%a::%d Expression (%a) is not TRUE!\n",
+ FunctionName,
+ LineNumber,
+ Description
+ );
+ UT_LOG_ERROR (
+ "[ASSERT FAIL] %a::%d Expression (%a) is not TRUE!\n",
+ FunctionName,
+ LineNumber,
+ Description
+ );
+ }
+ return Expression;
+}
+
+/**
+ If Expression is FALSE, then TRUE is returned.
+ If Expression is TRUE, then an assert is triggered and the location of the
+ assert provided by FunctionName, LineNumber, FileName, and Description are
+ recorded and FALSE is returned.
+
+ @param[in] Expression The BOOLEAN result of the expression evaluation.
+ @param[in] FunctionName Null-terminated ASCII string of the function
+ executing the assert macro.
+ @param[in] LineNumber The source file line number of the assert macro.
+ @param[in] FileName Null-terminated ASCII string of the filename
+ executing the assert macro.
+ @param[in] Description Null-terminated ASCII string of the expression being
+ evaluated.
+
+ @retval TRUE Expression is FALSE.
+ @retval FALSE Expression is TRUE.
+**/
+BOOLEAN
+EFIAPI
+UnitTestAssertFalse (
+ IN BOOLEAN Expression,
+ IN CONST CHAR8 *FunctionName,
+ IN UINTN LineNumber,
+ IN CONST CHAR8 *FileName,
+ IN CONST CHAR8 *Description
+ )
+{
+ if (Expression) {
+ UnitTestLogFailure (
+ FAILURETYPE_ASSERTFALSE,
+ "%a::%d Expression(%a) is not FALSE!\n",
+ FunctionName,
+ LineNumber,
+ Description
+ );
+ UT_LOG_ERROR (
+ "[ASSERT FAIL] %a::%d Expression (%a) is not FALSE!\n",
+ FunctionName,
+ LineNumber,
+ Description
+ );
+ }
+ return !Expression;
+}
+
+/**
+ If Status is not an EFI_ERROR(), then TRUE is returned.
+ If Status is an EFI_ERROR(), then an assert is triggered and the location of
+ the assert provided by FunctionName, LineNumber, FileName, and Description are
+ recorded and FALSE is returned.
+
+ @param[in] Status The EFI_STATUS value to evaluate.
+ @param[in] FunctionName Null-terminated ASCII string of the function
+ executing the assert macro.
+ @param[in] LineNumber The source file line number of the assert macro.
+ @param[in] FileName Null-terminated ASCII string of the filename
+ executing the assert macro.
+ @param[in] Description Null-terminated ASCII string of the status
+ expression being evaluated.
+
+ @retval TRUE Status is not an EFI_ERROR().
+ @retval FALSE Status is an EFI_ERROR().
+**/
+BOOLEAN
+EFIAPI
+UnitTestAssertNotEfiError (
+ IN EFI_STATUS Status,
+ IN CONST CHAR8 *FunctionName,
+ IN UINTN LineNumber,
+ IN CONST CHAR8 *FileName,
+ IN CONST CHAR8 *Description
+ )
+{
+ if (EFI_ERROR (Status)) {
+ UnitTestLogFailure (
+ FAILURETYPE_ASSERTNOTEFIERROR,
+ "%a::%d Status '%a' is EFI_ERROR (%r)!\n",
+ FunctionName,
+ LineNumber,
+ Description,
+ Status
+ );
+ UT_LOG_ERROR (
+ "[ASSERT FAIL] %a::%d Status '%a' is EFI_ERROR (%r)!\n",
+ FunctionName,
+ LineNumber,
+ Description,
+ Status
+ );
+ }
+ return !EFI_ERROR( Status );
+}
+
+/**
+ If ValueA is equal ValueB, then TRUE is returned.
+ If ValueA is not equal to ValueB, then an assert is triggered and the location
+ of the assert provided by FunctionName, LineNumber, FileName, DescriptionA,
+ and DescriptionB are recorded and FALSE is returned.
+
+ @param[in] ValueA 64-bit value.
+ @param[in] ValueB 64-bit value.
+ @param[in] FunctionName Null-terminated ASCII string of the function
+ executing the assert macro.
+ @param[in] LineNumber The source file line number of the assert macro.
+ @param[in] FileName Null-terminated ASCII string of the filename
+ executing the assert macro.
+ @param[in] DescriptionA Null-terminated ASCII string that is a description
+ of ValueA.
+ @param[in] DescriptionB Null-terminated ASCII string that is a description
+ of ValueB.
+
+ @retval TRUE ValueA is equal to ValueB.
+ @retval FALSE ValueA is not equal to ValueB.
+**/
+BOOLEAN
+EFIAPI
+UnitTestAssertEqual (
+ IN UINT64 ValueA,
+ IN UINT64 ValueB,
+ IN CONST CHAR8 *FunctionName,
+ IN UINTN LineNumber,
+ IN CONST CHAR8 *FileName,
+ IN CONST CHAR8 *DescriptionA,
+ IN CONST CHAR8 *DescriptionB
+ )
+{
+ if ((ValueA != ValueB)) {
+ UnitTestLogFailure (
+ FAILURETYPE_ASSERTEQUAL,
+ "%a::%d Value %a != %a (%d != %d)!\n",
+ FunctionName,
+ LineNumber,
+ DescriptionA,
+ DescriptionB,
+ ValueA,
+ ValueB
+ );
+ UT_LOG_ERROR (
+ "[ASSERT FAIL] %a::%d Value %a != %a (%d != %d)!\n",
+ FunctionName,
+ LineNumber,
+ DescriptionA,
+ DescriptionB,
+ ValueA,
+ ValueB
+ );
+ }
+ return (ValueA == ValueB);
+}
+
+/**
+ If the contents of BufferA are identical to the contents of BufferB, then TRUE
+ is returned. If the contents of BufferA are not identical to the contents of
+ BufferB, then an assert is triggered and the location of the assert provided
+ by FunctionName, LineNumber, FileName, DescriptionA, and DescriptionB are
+ recorded and FALSE is returned.
+
+ @param[in] BufferA Pointer to a buffer for comparison.
+ @param[in] BufferB Pointer to a buffer for comparison.
+ @param[in] Length Number of bytes to compare in BufferA and BufferB.
+ @param[in] FunctionName Null-terminated ASCII string of the function
+ executing the assert macro.
+ @param[in] LineNumber The source file line number of the assert macro.
+ @param[in] FileName Null-terminated ASCII string of the filename
+ executing the assert macro.
+ @param[in] DescriptionA Null-terminated ASCII string that is a description
+ of BufferA.
+ @param[in] DescriptionB Null-terminated ASCII string that is a description
+ of BufferB.
+
+ @retval TRUE The contents of BufferA are identical to the contents of
+ BufferB.
+ @retval FALSE The contents of BufferA are not identical to the contents of
+ BufferB.
+**/
+BOOLEAN
+EFIAPI
+UnitTestAssertMemEqual (
+ IN VOID *BufferA,
+ IN VOID *BufferB,
+ IN UINTN Length,
+ IN CONST CHAR8 *FunctionName,
+ IN UINTN LineNumber,
+ IN CONST CHAR8 *FileName,
+ IN CONST CHAR8 *DescriptionA,
+ IN CONST CHAR8 *DescriptionB
+ )
+{
+ if (CompareMem(BufferA, BufferB, Length) != 0) {
+ UnitTestLogFailure (
+ FAILURETYPE_ASSERTEQUAL,
+ "%a::%d Memory at %a != %a for length %d bytes!\n",
+ FunctionName,
+ LineNumber,
+ DescriptionA,
+ DescriptionB,
+ Length
+ );
+ UT_LOG_ERROR (
+ "[ASSERT FAIL] %a::%d Value %a != %a for length %d bytes!\n",
+ FunctionName,
+ LineNumber,
+ DescriptionA,
+ DescriptionB,
+ Length
+ );
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ If ValueA is not equal ValueB, then TRUE is returned.
+ If ValueA is equal to ValueB, then an assert is triggered and the location
+ of the assert provided by FunctionName, LineNumber, FileName, DescriptionA
+ and DescriptionB are recorded and FALSE is returned.
+
+ @param[in] ValueA 64-bit value.
+ @param[in] ValueB 64-bit value.
+ @param[in] FunctionName Null-terminated ASCII string of the function
+ executing the assert macro.
+ @param[in] LineNumber The source file line number of the assert macro.
+ @param[in] FileName Null-terminated ASCII string of the filename
+ executing the assert macro.
+ @param[in] DescriptionA Null-terminated ASCII string that is a description
+ of ValueA.
+ @param[in] DescriptionB Null-terminated ASCII string that is a description
+ of ValueB.
+
+ @retval TRUE ValueA is not equal to ValueB.
+ @retval FALSE ValueA is equal to ValueB.
+**/
+BOOLEAN
+EFIAPI
+UnitTestAssertNotEqual (
+ IN UINT64 ValueA,
+ IN UINT64 ValueB,
+ IN CONST CHAR8 *FunctionName,
+ IN UINTN LineNumber,
+ IN CONST CHAR8 *FileName,
+ IN CONST CHAR8 *DescriptionA,
+ IN CONST CHAR8 *DescriptionB
+ )
+{
+ if ((ValueA == ValueB)) {
+ UnitTestLogFailure (
+ FAILURETYPE_ASSERTNOTEQUAL,
+ "%a::%d Value %a == %a (%d == %d)!\n",
+ FunctionName,
+ LineNumber,
+ DescriptionA,
+ DescriptionB,
+ ValueA,
+ ValueB
+ );
+ UT_LOG_ERROR (
+ "[ASSERT FAIL] %a::%d Value %a == %a (%d == %d)!\n",
+ FunctionName,
+ LineNumber,
+ DescriptionA,
+ DescriptionB,
+ ValueA,
+ ValueB
+ );
+ }
+ return (ValueA != ValueB);
+}
+
+/**
+ If Status is equal to Expected, then TRUE is returned.
+ If Status is not equal to Expected, then an assert is triggered and the
+ location of the assert provided by FunctionName, LineNumber, FileName, and
+ Description are recorded and FALSE is returned.
+
+ @param[in] Status EFI_STATUS value returned from an API under test.
+ @param[in] Expected The expected EFI_STATUS return value from an API
+ under test.
+ @param[in] FunctionName Null-terminated ASCII string of the function
+ executing the assert macro.
+ @param[in] LineNumber The source file line number of the assert macro.
+ @param[in] FileName Null-terminated ASCII string of the filename
+ executing the assert macro.
+ @param[in] Description Null-terminated ASCII string that is a description
+ of Status.
+
+ @retval TRUE Status is equal to Expected.
+ @retval FALSE Status is not equal to Expected.
+**/
+BOOLEAN
+EFIAPI
+UnitTestAssertStatusEqual (
+ IN EFI_STATUS Status,
+ IN EFI_STATUS Expected,
+ IN CONST CHAR8 *FunctionName,
+ IN UINTN LineNumber,
+ IN CONST CHAR8 *FileName,
+ IN CONST CHAR8 *Description
+ )
+{
+ if ((Status != Expected)) {
+ UnitTestLogFailure (
+ FAILURETYPE_ASSERTSTATUSEQUAL,
+ "%a::%d Status '%a' is %r, should be %r!\n",
+ FunctionName,
+ LineNumber,
+ Description,
+ Status,
+ Expected
+ );
+ UT_LOG_ERROR (
+ "[ASSERT FAIL] %a::%d Status '%a' is %r, should be %r!\n",
+ FunctionName,
+ LineNumber,
+ Description,
+ Status,
+ Expected
+ );
+ }
+ return (Status == Expected);
+}
+
+/**
+ If Pointer is not equal to NULL, then TRUE is returned.
+ If Pointer is equal to NULL, then an assert is triggered and the location of
+ the assert provided by FunctionName, LineNumber, FileName, and PointerName
+ are recorded and FALSE is returned.
+
+ @param[in] Pointer Pointer value to be checked against NULL.
+ @param[in] Expected The expected EFI_STATUS return value from a function
+ under test.
+ @param[in] FunctionName Null-terminated ASCII string of the function
+ executing the assert macro.
+ @param[in] LineNumber The source file line number of the assert macro.
+ @param[in] FileName Null-terminated ASCII string of the filename
+ executing the assert macro.
+ @param[in] PointerName Null-terminated ASCII string that is a description
+ of Pointer.
+
+ @retval TRUE Pointer is not equal to NULL.
+ @retval FALSE Pointer is equal to NULL.
+**/
+BOOLEAN
+EFIAPI
+UnitTestAssertNotNull (
+ IN VOID *Pointer,
+ IN CONST CHAR8 *FunctionName,
+ IN UINTN LineNumber,
+ IN CONST CHAR8 *FileName,
+ IN CONST CHAR8 *PointerName
+ )
+{
+ if (Pointer == NULL) {
+ UnitTestLogFailure (
+ FAILURETYPE_ASSERTNOTNULL,
+ "%a::%d Pointer (%a) is NULL!\n",
+ FunctionName,
+ LineNumber,
+ PointerName
+ );
+ UT_LOG_ERROR (
+ "[ASSERT FAIL] %a::%d Pointer (%a) is NULL!\n",
+ FunctionName,
+ LineNumber,
+ PointerName
+ );
+ }
+ return (Pointer != NULL);
+}
diff --git a/UnitTestFrameworkPkg/Library/UnitTestLib/AssertCmocka.c b/UnitTestFrameworkPkg/Library/UnitTestLib/AssertCmocka.c
new file mode 100644
index 0000000000..e48d614976
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestLib/AssertCmocka.c
@@ -0,0 +1,335 @@
+/** @file
+ Implement UnitTestLib assert services using cmocka services
+
+ Copyright (c) 2019 - 2020, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include <Uefi.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UnitTestLib.h>
+
+#define MAX_STRING_SIZE 1025
+
+/**
+ If Expression is TRUE, then TRUE is returned.
+ If Expression is FALSE, then an assert is triggered and the location of the
+ assert provided by FunctionName, LineNumber, FileName, and Description are
+ recorded and FALSE is returned.
+
+ @param[in] Expression The BOOLEAN result of the expression evaluation.
+ @param[in] FunctionName Null-terminated ASCII string of the function
+ executing the assert macro.
+ @param[in] LineNumber The source file line number of the assert macro.
+ @param[in] FileName Null-terminated ASCII string of the filename
+ executing the assert macro.
+ @param[in] Description Null-terminated ASCII string of the expression being
+ evaluated.
+
+ @retval TRUE Expression is TRUE.
+ @retval FALSE Expression is FALSE.
+**/
+BOOLEAN
+EFIAPI
+UnitTestAssertTrue (
+ IN BOOLEAN Expression,
+ IN CONST CHAR8 *FunctionName,
+ IN UINTN LineNumber,
+ IN CONST CHAR8 *FileName,
+ IN CONST CHAR8 *Description
+ )
+{
+ CHAR8 TempStr[MAX_STRING_SIZE];
+
+ snprintf (TempStr, sizeof(TempStr), "UT_ASSERT_TRUE(%s:%x)", Description, Expression);
+ _assert_true (Expression, TempStr, FileName, (INT32)LineNumber);
+
+ return Expression;
+}
+
+/**
+ If Expression is FALSE, then TRUE is returned.
+ If Expression is TRUE, then an assert is triggered and the location of the
+ assert provided by FunctionName, LineNumber, FileName, and Description are
+ recorded and FALSE is returned.
+
+ @param[in] Expression The BOOLEAN result of the expression evaluation.
+ @param[in] FunctionName Null-terminated ASCII string of the function
+ executing the assert macro.
+ @param[in] LineNumber The source file line number of the assert macro.
+ @param[in] FileName Null-terminated ASCII string of the filename
+ executing the assert macro.
+ @param[in] Description Null-terminated ASCII string of the expression being
+ evaluated.
+
+ @retval TRUE Expression is FALSE.
+ @retval FALSE Expression is TRUE.
+**/
+BOOLEAN
+EFIAPI
+UnitTestAssertFalse (
+ IN BOOLEAN Expression,
+ IN CONST CHAR8 *FunctionName,
+ IN UINTN LineNumber,
+ IN CONST CHAR8 *FileName,
+ IN CONST CHAR8 *Description
+ )
+{
+ CHAR8 TempStr[MAX_STRING_SIZE];
+
+ snprintf (TempStr, sizeof(TempStr), "UT_ASSERT_FALSE(%s:%x)", Description, Expression);
+ _assert_true (!Expression, TempStr, FileName, (INT32)LineNumber);
+
+ return !Expression;
+}
+
+/**
+ If Status is not an EFI_ERROR(), then TRUE is returned.
+ If Status is an EFI_ERROR(), then an assert is triggered and the location of
+ the assert provided by FunctionName, LineNumber, FileName, and Description are
+ recorded and FALSE is returned.
+
+ @param[in] Status The EFI_STATUS value to evaluate.
+ @param[in] FunctionName Null-terminated ASCII string of the function
+ executing the assert macro.
+ @param[in] LineNumber The source file line number of the assert macro.
+ @param[in] FileName Null-terminated ASCII string of the filename
+ executing the assert macro.
+ @param[in] Description Null-terminated ASCII string of the status
+ expression being evaluated.
+
+ @retval TRUE Status is not an EFI_ERROR().
+ @retval FALSE Status is an EFI_ERROR().
+**/
+BOOLEAN
+EFIAPI
+UnitTestAssertNotEfiError (
+ IN EFI_STATUS Status,
+ IN CONST CHAR8 *FunctionName,
+ IN UINTN LineNumber,
+ IN CONST CHAR8 *FileName,
+ IN CONST CHAR8 *Description
+ )
+{
+ CHAR8 TempStr[MAX_STRING_SIZE];
+
+ snprintf (TempStr, sizeof(TempStr), "UT_ASSERT_NOT_EFI_ERROR(%s:%p)", Description, (void *)Status);
+ _assert_true (!EFI_ERROR (Status), TempStr, FileName, (INT32)LineNumber);
+
+ return !EFI_ERROR (Status);
+}
+
+/**
+ If ValueA is equal ValueB, then TRUE is returned.
+ If ValueA is not equal to ValueB, then an assert is triggered and the location
+ of the assert provided by FunctionName, LineNumber, FileName, DescriptionA,
+ and DescriptionB are recorded and FALSE is returned.
+
+ @param[in] ValueA 64-bit value.
+ @param[in] ValueB 64-bit value.
+ @param[in] FunctionName Null-terminated ASCII string of the function
+ executing the assert macro.
+ @param[in] LineNumber The source file line number of the assert macro.
+ @param[in] FileName Null-terminated ASCII string of the filename
+ executing the assert macro.
+ @param[in] DescriptionA Null-terminated ASCII string that is a description
+ of ValueA.
+ @param[in] DescriptionB Null-terminated ASCII string that is a description
+ of ValueB.
+
+ @retval TRUE ValueA is equal to ValueB.
+ @retval FALSE ValueA is not equal to ValueB.
+**/
+BOOLEAN
+EFIAPI
+UnitTestAssertEqual (
+ IN UINT64 ValueA,
+ IN UINT64 ValueB,
+ IN CONST CHAR8 *FunctionName,
+ IN UINTN LineNumber,
+ IN CONST CHAR8 *FileName,
+ IN CONST CHAR8 *DescriptionA,
+ IN CONST CHAR8 *DescriptionB
+ )
+{
+ CHAR8 TempStr[MAX_STRING_SIZE];
+
+ snprintf (TempStr, sizeof(TempStr), "UT_ASSERT_EQUAL(%s:%llx, %s:%llx)", DescriptionA, ValueA, DescriptionB, ValueB);
+ _assert_true ((ValueA == ValueB), TempStr, FileName, (INT32)LineNumber);
+
+ return (ValueA == ValueB);
+}
+
+/**
+ If the contents of BufferA are identical to the contents of BufferB, then TRUE
+ is returned. If the contents of BufferA are not identical to the contents of
+ BufferB, then an assert is triggered and the location of the assert provided
+ by FunctionName, LineNumber, FileName, DescriptionA, and DescriptionB are
+ recorded and FALSE is returned.
+
+ @param[in] BufferA Pointer to a buffer for comparison.
+ @param[in] BufferB Pointer to a buffer for comparison.
+ @param[in] Length Number of bytes to compare in BufferA and BufferB.
+ @param[in] FunctionName Null-terminated ASCII string of the function
+ executing the assert macro.
+ @param[in] LineNumber The source file line number of the assert macro.
+ @param[in] FileName Null-terminated ASCII string of the filename
+ executing the assert macro.
+ @param[in] DescriptionA Null-terminated ASCII string that is a description
+ of BufferA.
+ @param[in] DescriptionB Null-terminated ASCII string that is a description
+ of BufferB.
+
+ @retval TRUE The contents of BufferA are identical to the contents of
+ BufferB.
+ @retval FALSE The contents of BufferA are not identical to the contents of
+ BufferB.
+**/
+BOOLEAN
+EFIAPI
+UnitTestAssertMemEqual (
+ IN VOID *BufferA,
+ IN VOID *BufferB,
+ IN UINTN Length,
+ IN CONST CHAR8 *FunctionName,
+ IN UINTN LineNumber,
+ IN CONST CHAR8 *FileName,
+ IN CONST CHAR8 *DescriptionA,
+ IN CONST CHAR8 *DescriptionB
+ )
+{
+ CHAR8 TempStr[MAX_STRING_SIZE];
+ BOOLEAN Result;
+
+ Result = (CompareMem(BufferA, BufferB, Length) == 0);
+
+ snprintf (TempStr, sizeof(TempStr), "UT_ASSERT_MEM_EQUAL(%s:%p, %s:%p)", DescriptionA, BufferA, DescriptionB, BufferB);
+ _assert_true (Result, TempStr, FileName, (INT32)LineNumber);
+
+ return Result;
+}
+
+/**
+ If ValueA is not equal ValueB, then TRUE is returned.
+ If ValueA is equal to ValueB, then an assert is triggered and the location
+ of the assert provided by FunctionName, LineNumber, FileName, DescriptionA
+ and DescriptionB are recorded and FALSE is returned.
+
+ @param[in] ValueA 64-bit value.
+ @param[in] ValueB 64-bit value.
+ @param[in] FunctionName Null-terminated ASCII string of the function
+ executing the assert macro.
+ @param[in] LineNumber The source file line number of the assert macro.
+ @param[in] FileName Null-terminated ASCII string of the filename
+ executing the assert macro.
+ @param[in] DescriptionA Null-terminated ASCII string that is a description
+ of ValueA.
+ @param[in] DescriptionB Null-terminated ASCII string that is a description
+ of ValueB.
+
+ @retval TRUE ValueA is not equal to ValueB.
+ @retval FALSE ValueA is equal to ValueB.
+**/
+BOOLEAN
+EFIAPI
+UnitTestAssertNotEqual (
+ IN UINT64 ValueA,
+ IN UINT64 ValueB,
+ IN CONST CHAR8 *FunctionName,
+ IN UINTN LineNumber,
+ IN CONST CHAR8 *FileName,
+ IN CONST CHAR8 *DescriptionA,
+ IN CONST CHAR8 *DescriptionB
+ )
+{
+ CHAR8 TempStr[MAX_STRING_SIZE];
+
+ snprintf (TempStr, sizeof(TempStr), "UT_ASSERT_NOT_EQUAL(%s:%llx, %s:%llx)", DescriptionA, ValueA, DescriptionB, ValueB);
+ _assert_true ((ValueA != ValueB), TempStr, FileName, (INT32)LineNumber);
+
+ return (ValueA != ValueB);
+}
+
+/**
+ If Status is equal to Expected, then TRUE is returned.
+ If Status is not equal to Expected, then an assert is triggered and the
+ location of the assert provided by FunctionName, LineNumber, FileName, and
+ Description are recorded and FALSE is returned.
+
+ @param[in] Status EFI_STATUS value returned from an API under test.
+ @param[in] Expected The expected EFI_STATUS return value from an API
+ under test.
+ @param[in] FunctionName Null-terminated ASCII string of the function
+ executing the assert macro.
+ @param[in] LineNumber The source file line number of the assert macro.
+ @param[in] FileName Null-terminated ASCII string of the filename
+ executing the assert macro.
+ @param[in] Description Null-terminated ASCII string that is a description
+ of Status.
+
+ @retval TRUE Status is equal to Expected.
+ @retval FALSE Status is not equal to Expected.
+**/
+BOOLEAN
+EFIAPI
+UnitTestAssertStatusEqual (
+ IN EFI_STATUS Status,
+ IN EFI_STATUS Expected,
+ IN CONST CHAR8 *FunctionName,
+ IN UINTN LineNumber,
+ IN CONST CHAR8 *FileName,
+ IN CONST CHAR8 *Description
+ )
+{
+ CHAR8 TempStr[MAX_STRING_SIZE];
+
+ snprintf (TempStr, sizeof(TempStr), "UT_ASSERT_STATUS_EQUAL(%s:%p)", Description, (VOID *)Status);
+ _assert_true ((Status == Expected), TempStr, FileName, (INT32)LineNumber);
+
+ return (Status == Expected);
+}
+
+/**
+ If Pointer is not equal to NULL, then TRUE is returned.
+ If Pointer is equal to NULL, then an assert is triggered and the location of
+ the assert provided by FunctionName, LineNumber, FileName, and PointerName
+ are recorded and FALSE is returned.
+
+ @param[in] Pointer Pointer value to be checked against NULL.
+ @param[in] Expected The expected EFI_STATUS return value from a function
+ under test.
+ @param[in] FunctionName Null-terminated ASCII string of the function
+ executing the assert macro.
+ @param[in] LineNumber The source file line number of the assert macro.
+ @param[in] FileName Null-terminated ASCII string of the filename
+ executing the assert macro.
+ @param[in] PointerName Null-terminated ASCII string that is a description
+ of Pointer.
+
+ @retval TRUE Pointer is not equal to NULL.
+ @retval FALSE Pointer is equal to NULL.
+**/
+BOOLEAN
+EFIAPI
+UnitTestAssertNotNull (
+ IN VOID *Pointer,
+ IN CONST CHAR8 *FunctionName,
+ IN UINTN LineNumber,
+ IN CONST CHAR8 *FileName,
+ IN CONST CHAR8 *PointerName
+ )
+{
+ CHAR8 TempStr[MAX_STRING_SIZE];
+
+ snprintf (TempStr, sizeof(TempStr), "UT_ASSERT_NOT_NULL(%s:%p)", PointerName, Pointer);
+ _assert_true ((Pointer != NULL), TempStr, FileName, (INT32)LineNumber);
+
+ return (Pointer != NULL);
+}
diff --git a/UnitTestFrameworkPkg/Library/UnitTestLib/Log.c b/UnitTestFrameworkPkg/Library/UnitTestLib/Log.c
new file mode 100644
index 0000000000..78df086a28
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestLib/Log.c
@@ -0,0 +1,200 @@
+/**
+ Implemnet UnitTestLib log services
+
+ Copyright (c) Microsoft Corporation.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <PiDxe.h>
+#include <UnitTestFrameworkTypes.h>
+#include <Library/UnitTestLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PrintLib.h>
+#include <Library/PcdLib.h>
+
+#define UNIT_TEST_MAX_SINGLE_LOG_STRING_LENGTH (512)
+#define UNIT_TEST_MAX_LOG_BUFFER SIZE_16KB
+
+struct _UNIT_TEST_LOG_PREFIX_STRING {
+ UNIT_TEST_STATUS LogLevel;
+ CHAR8 *String;
+};
+
+struct _UNIT_TEST_LOG_PREFIX_STRING mLogPrefixStrings[] = {
+ { UNIT_TEST_LOG_LEVEL_ERROR, "[ERROR] " },
+ { UNIT_TEST_LOG_LEVEL_WARN, "[WARNING] " },
+ { UNIT_TEST_LOG_LEVEL_INFO, "[INFO] " },
+ { UNIT_TEST_LOG_LEVEL_VERBOSE, "[VERBOSE] " }
+};
+
+//
+// Unit-Test Log helper functions
+//
+
+STATIC
+CONST CHAR8*
+GetStringForStatusLogPrefix (
+ IN UINTN LogLevel
+ )
+{
+ UINTN Index;
+ CHAR8 *Result;
+
+ Result = NULL;
+ for (Index = 0; Index < ARRAY_SIZE (mLogPrefixStrings); Index++) {
+ if (mLogPrefixStrings[Index].LogLevel == LogLevel) {
+ Result = mLogPrefixStrings[Index].String;
+ break;
+ }
+ }
+ return Result;
+}
+
+STATIC
+EFI_STATUS
+AddStringToUnitTestLog (
+ IN OUT UNIT_TEST *UnitTest,
+ IN CONST CHAR8 *String
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Make sure that you're cooking with gas.
+ //
+ if (UnitTest == NULL || String == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // If this is the first log for the test allocate log space
+ if (UnitTest->Log == NULL) {
+ UnitTestLogInit (UnitTest, NULL, 0);
+ }
+
+ if (UnitTest->Log == NULL) {
+ DEBUG ((DEBUG_ERROR, "Failed to allocate space for unit test log\n"));
+ ASSERT (UnitTest->Log != NULL);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = AsciiStrnCatS (
+ UnitTest->Log,
+ UNIT_TEST_MAX_LOG_BUFFER / sizeof (CHAR8),
+ String,
+ UNIT_TEST_MAX_SINGLE_LOG_STRING_LENGTH
+ );
+ if(EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed to add unit test log string. Status = %r\n", Status));
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function is responsible for initializing the log buffer for a single test. It can
+ be used internally, but may also be consumed by the test framework to add pre-existing
+ data to a log before it's used.
+
+ @param[in,out] TestHandle A handle to the test being initialized.
+ @param[in] Buffer [Optional] A pointer to pre-existing log data that should
+ be used to initialize the log. Should include a NULL terminator.
+ @param[in] BufferSize [Optional] The size of the pre-existing log data.
+
+**/
+VOID
+EFIAPI
+UnitTestLogInit (
+ IN OUT UNIT_TEST *Test,
+ IN UINT8 *Buffer, OPTIONAL
+ IN UINTN BufferSize OPTIONAL
+ )
+{
+ //
+ // Make sure that you're cooking with gas.
+ //
+ if (Test == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a called with invalid Test parameter\n", __FUNCTION__));
+ return;
+ }
+
+ //
+ // If this is the first log for the test allocate log space
+ //
+ if (Test->Log == NULL) {
+ Test->Log = AllocateZeroPool (UNIT_TEST_MAX_LOG_BUFFER);
+ }
+
+ //
+ //check again to make sure allocate worked
+ //
+ if(Test->Log == NULL) {
+ DEBUG ((DEBUG_ERROR, "Failed to allocate memory for the log\n"));
+ return;
+ }
+
+ if((Buffer != NULL) && (BufferSize > 0) && ((BufferSize <= UNIT_TEST_MAX_LOG_BUFFER))) {
+ CopyMem (Test->Log, Buffer, BufferSize);
+ }
+}
+
+/**
+ Test logging function that records a messages in the test framework log.
+ Record is associated with the currently executing test case.
+
+ @param[in] ErrorLevel The error level of the unit test log message.
+ @param[in] Format Formatting string following the format defined in the
+ MdePkg/Include/Library/PrintLib.h.
+ @param[in] ... Print args.
+**/
+VOID
+EFIAPI
+UnitTestLog (
+ IN UINTN ErrorLevel,
+ IN CONST CHAR8 *Format,
+ ...
+ )
+{
+ UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle;
+ CHAR8 NewFormatString[UNIT_TEST_MAX_SINGLE_LOG_STRING_LENGTH];
+ CHAR8 LogString[UNIT_TEST_MAX_SINGLE_LOG_STRING_LENGTH];
+ CONST CHAR8 *LogTypePrefix;
+ VA_LIST Marker;
+
+ FrameworkHandle = GetActiveFrameworkHandle ();
+
+ LogTypePrefix = NULL;
+
+ //
+ // Make sure that this unit test log level is enabled.
+ //
+ if ((ErrorLevel & (UINTN)PcdGet32 (PcdUnitTestLogLevel)) == 0) {
+ return;
+ }
+
+ //
+ // If we need to define a new format string...
+ // well... get to it.
+ //
+ LogTypePrefix = GetStringForStatusLogPrefix (ErrorLevel);
+ if (LogTypePrefix != NULL) {
+ AsciiSPrint (NewFormatString, sizeof (NewFormatString), "%a%a", LogTypePrefix, Format);
+ } else {
+ AsciiStrCpyS (NewFormatString, sizeof (NewFormatString), Format);
+ }
+
+ //
+ // Convert the message to an ASCII String
+ //
+ VA_START (Marker, Format);
+ AsciiVSPrint (LogString, sizeof (LogString), NewFormatString, Marker);
+ VA_END (Marker);
+
+ //
+ // Finally, add the string to the log.
+ //
+ AddStringToUnitTestLog (((UNIT_TEST_FRAMEWORK *)FrameworkHandle)->CurrentTest, LogString);
+}
diff --git a/UnitTestFrameworkPkg/Library/UnitTestLib/RunTests.c b/UnitTestFrameworkPkg/Library/UnitTestLib/RunTests.c
new file mode 100644
index 0000000000..d66382e2d3
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestLib/RunTests.c
@@ -0,0 +1,171 @@
+/**
+ UnitTestLib APIs to run unit tests
+
+ Copyright (c) Microsoft Corporation.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+#include <Library/UnitTestLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UnitTestResultReportLib.h>
+
+STATIC UNIT_TEST_FRAMEWORK_HANDLE mFrameworkHandle = NULL;
+
+UNIT_TEST_FRAMEWORK_HANDLE
+GetActiveFrameworkHandle (
+ VOID
+ )
+{
+ ASSERT (mFrameworkHandle != NULL);
+ return mFrameworkHandle;
+}
+
+STATIC
+EFI_STATUS
+RunTestSuite (
+ IN UNIT_TEST_SUITE *Suite
+ )
+{
+ UNIT_TEST_LIST_ENTRY *TestEntry;
+ UNIT_TEST *Test;
+ UNIT_TEST_FRAMEWORK *ParentFramework;
+
+ TestEntry = NULL;
+ ParentFramework = (UNIT_TEST_FRAMEWORK *)Suite->ParentFramework;
+
+ if (Suite == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DEBUG ((DEBUG_VERBOSE, "---------------------------------------------------------\n"));
+ DEBUG ((DEBUG_VERBOSE, "RUNNING TEST SUITE: %a\n", Suite->Title));
+ DEBUG ((DEBUG_VERBOSE, "---------------------------------------------------------\n"));
+
+ if (Suite->Setup != NULL) {
+ Suite->Setup ();
+ }
+
+ //
+ // Iterate all tests within the suite
+ //
+ for (TestEntry = (UNIT_TEST_LIST_ENTRY *)GetFirstNode (&(Suite->TestCaseList));
+ (LIST_ENTRY*)TestEntry != &(Suite->TestCaseList);
+ TestEntry = (UNIT_TEST_LIST_ENTRY *)GetNextNode (&(Suite->TestCaseList), (LIST_ENTRY *)TestEntry)) {
+ Test = &TestEntry->UT;
+ ParentFramework->CurrentTest = Test;
+
+ DEBUG ((DEBUG_VERBOSE, "*********************************************************\n"));
+ DEBUG ((DEBUG_VERBOSE, " RUNNING TEST: %a:\n", Test->Description));
+ DEBUG ((DEBUG_VERBOSE, "**********************************************************\n"));
+
+ //
+ // First, check to see whether the test has already been run.
+ // NOTE: This would generally only be the case if a saved state was detected and loaded.
+ //
+ if (Test->Result != UNIT_TEST_PENDING && Test->Result != UNIT_TEST_RUNNING) {
+ DEBUG ((DEBUG_VERBOSE, "Test was run on a previous pass. Skipping.\n"));
+ ParentFramework->CurrentTest = NULL;
+ continue;
+ }
+
+ //
+ // Next, if we're still running, make sure that our test prerequisites are in place.
+ if (Test->Result == UNIT_TEST_PENDING && Test->Prerequisite != NULL) {
+ DEBUG ((DEBUG_VERBOSE, "PREREQ\n"));
+ if (Test->Prerequisite (Test->Context) != UNIT_TEST_PASSED) {
+ DEBUG ((DEBUG_ERROR, "Prerequisite Not Met\n"));
+ Test->Result = UNIT_TEST_ERROR_PREREQUISITE_NOT_MET;
+ ParentFramework->CurrentTest = NULL;
+ continue;
+ }
+ }
+
+ //
+ // Now we should be ready to call the actual test.
+ // We set the status to UNIT_TEST_RUNNING in case the test needs to reboot
+ // or quit. The UNIT_TEST_RUNNING state will allow the test to resume
+ // but will prevent the Prerequisite from being dispatched a second time.
+ Test->Result = UNIT_TEST_RUNNING;
+ Test->Result = Test->RunTest (Test->Context);
+
+ //
+ // Finally, clean everything up, if need be.
+ if (Test->CleanUp != NULL) {
+ DEBUG ((DEBUG_VERBOSE, "CLEANUP\n"));
+ Test->CleanUp (Test->Context);
+ }
+
+ //
+ // End the test.
+ //
+ ParentFramework->CurrentTest = NULL;
+ }
+
+ if (Suite->Teardown != NULL) {
+ Suite->Teardown ();
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Execute all unit test cases in all unit test suites added to a Framework.
+
+ Once a unit test framework is initialized and all unit test suites and unit
+ test cases are registered, this function will cause the unit test framework to
+ dispatch all unit test cases in sequence and record the results for reporting.
+
+ @param[in] FrameworkHandle A handle to the current running framework that
+ dispatched the test. Necessary for recording
+ certain test events with the framework.
+
+ @retval EFI_SUCCESS All test cases were dispached.
+ @retval EFI_INVALID_PARAMETER FrameworkHandle is NULL.
+**/
+EFI_STATUS
+EFIAPI
+RunAllTestSuites (
+ IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle
+ )
+{
+ UNIT_TEST_FRAMEWORK *Framework;
+ UNIT_TEST_SUITE_LIST_ENTRY *Suite;
+ EFI_STATUS Status;
+
+ Framework = (UNIT_TEST_FRAMEWORK *)FrameworkHandle;
+ Suite = NULL;
+
+ if (Framework == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DEBUG ((DEBUG_VERBOSE, "---------------------------------------------------------\n"));
+ DEBUG ((DEBUG_VERBOSE, "------------ RUNNING ALL TEST SUITES --------------\n"));
+ DEBUG ((DEBUG_VERBOSE, "---------------------------------------------------------\n"));
+ mFrameworkHandle = FrameworkHandle;
+
+ //
+ // Iterate all suites
+ //
+ for (Suite = (UNIT_TEST_SUITE_LIST_ENTRY *)GetFirstNode (&Framework->TestSuiteList);
+ (LIST_ENTRY *)Suite != &Framework->TestSuiteList;
+ Suite = (UNIT_TEST_SUITE_LIST_ENTRY *)GetNextNode (&Framework->TestSuiteList, (LIST_ENTRY *)Suite)) {
+ Status = RunTestSuite (&(Suite->UTS));
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Test Suite Failed with Error. %r\n", Status));
+ }
+ }
+
+ //
+ // Save current state so if test is started again it doesn't have to run. It will just report
+ //
+ SaveFrameworkState (FrameworkHandle, NULL, 0);
+ OutputUnitTestFrameworkReport (FrameworkHandle);
+
+ mFrameworkHandle = NULL;
+
+ return EFI_SUCCESS;
+}
diff --git a/UnitTestFrameworkPkg/Library/UnitTestLib/RunTestsCmocka.c b/UnitTestFrameworkPkg/Library/UnitTestLib/RunTestsCmocka.c
new file mode 100644
index 0000000000..cb9c881723
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestLib/RunTestsCmocka.c
@@ -0,0 +1,278 @@
+/** @file
+ UnitTestLib APIs to run unit tests using cmocka
+
+ Copyright (c) 2019 - 2020, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include <Uefi.h>
+#include <UnitTestFrameworkTypes.h>
+#include <Library/UnitTestLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DebugLib.h>
+
+STATIC UNIT_TEST_FRAMEWORK_HANDLE mFrameworkHandle = NULL;
+
+UNIT_TEST_FRAMEWORK_HANDLE
+GetActiveFrameworkHandle (
+ VOID
+ )
+{
+ ASSERT (mFrameworkHandle != NULL);
+ return mFrameworkHandle;
+}
+
+//
+// The currently active test suite
+//
+UNIT_TEST_SUITE *mActiveUnitTestSuite = NULL;
+
+void
+CmockaUnitTestFunctionRunner (
+ void **state
+ )
+{
+ UNIT_TEST *UnitTest;
+ UNIT_TEST_SUITE *Suite;
+ UNIT_TEST_FRAMEWORK *Framework;
+
+ UnitTest = (UNIT_TEST *)(*state);
+ Suite = (UNIT_TEST_SUITE *)(UnitTest->ParentSuite);
+ Framework = (UNIT_TEST_FRAMEWORK *)(Suite->ParentFramework);
+
+ if (UnitTest->RunTest == NULL) {
+ UnitTest->Result = UNIT_TEST_SKIPPED;
+ } else {
+ UnitTest->Result = UNIT_TEST_RUNNING;
+
+ Framework->CurrentTest = UnitTest;
+ UnitTest->Result = UnitTest->RunTest (UnitTest->Context);
+ Framework->CurrentTest = NULL;
+
+ // Print out the log messages - This is a partial solution as it
+ // does not get the log into the XML. Need cmocka changes to support
+ // stdout and stderr in their xml format
+ //
+ if (UnitTest->Log != NULL) {
+ print_message("UnitTest: %s - %s\n", UnitTest->Name, UnitTest->Description);
+ print_message("Log Output Start\n");
+ print_message("%s", UnitTest->Log);
+ print_message("Log Output End\n");
+ }
+ }
+}
+
+int
+CmockaUnitTestSetupFunctionRunner (
+ void **state
+ )
+{
+ UNIT_TEST *UnitTest;
+ UNIT_TEST_SUITE *Suite;
+ UNIT_TEST_FRAMEWORK *Framework;
+ UNIT_TEST_STATUS Result;
+
+ UnitTest = (UNIT_TEST *)(*state);
+ Suite = (UNIT_TEST_SUITE *)(UnitTest->ParentSuite);
+ Framework = (UNIT_TEST_FRAMEWORK *)(Suite->ParentFramework);
+
+ if (UnitTest->Prerequisite == NULL) {
+ return 0;
+ }
+
+ Framework->CurrentTest = UnitTest;
+ Result = UnitTest->Prerequisite (UnitTest->Context);
+ Framework->CurrentTest = NULL;
+
+ //
+ // Return 0 for success. Non-zero for error.
+ //
+ return (int)Result;
+}
+
+int
+CmockaUnitTestTeardownFunctionRunner (
+ void **state
+ )
+{
+ UNIT_TEST *UnitTest;
+ UNIT_TEST_SUITE *Suite;
+ UNIT_TEST_FRAMEWORK *Framework;
+
+ UnitTest = (UNIT_TEST *)(*state);
+ Suite = (UNIT_TEST_SUITE *)(UnitTest->ParentSuite);
+ Framework = (UNIT_TEST_FRAMEWORK *)(Suite->ParentFramework);
+
+ if (UnitTest->CleanUp == NULL) {
+ return 0;
+ }
+
+ Framework->CurrentTest = UnitTest;
+ UnitTest->CleanUp (UnitTest->Context);
+ Framework->CurrentTest = NULL;
+ //
+ // Return 0 for success. Non-zero for error.
+ //
+ return 0;
+}
+
+int
+CmockaUnitTestSuiteSetupFunctionRunner (
+ void **state
+ )
+{
+ if (mActiveUnitTestSuite == NULL) {
+ return -1;
+ }
+ if (mActiveUnitTestSuite->Setup == NULL) {
+ return 0;
+ }
+
+ mActiveUnitTestSuite->Setup ();
+ //
+ // Always succeed
+ //
+ return 0;
+}
+
+int
+CmockaUnitTestSuiteTeardownFunctionRunner (
+ void **state
+ )
+{
+ if (mActiveUnitTestSuite == NULL) {
+ return -1;
+ }
+ if (mActiveUnitTestSuite->Teardown == NULL) {
+ return 0;
+ }
+
+ mActiveUnitTestSuite->Teardown ();
+ //
+ // Always succeed
+ //
+ return 0;
+}
+
+STATIC
+EFI_STATUS
+RunTestSuite (
+ IN UNIT_TEST_SUITE *Suite
+ )
+{
+ UNIT_TEST_LIST_ENTRY *TestEntry;
+ UNIT_TEST *UnitTest;
+ struct CMUnitTest *Tests;
+ UINTN Index;
+
+ TestEntry = NULL;
+
+ if (Suite == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DEBUG ((DEBUG_VERBOSE, "---------------------------------------------------------\n"));
+ DEBUG ((DEBUG_VERBOSE, "RUNNING TEST SUITE: %a\n", Suite->Title));
+ DEBUG ((DEBUG_VERBOSE, "---------------------------------------------------------\n"));
+
+ //
+ // Allocate buffer of CMUnitTest entries
+ //
+ Tests = AllocateZeroPool (Suite->NumTests * sizeof (struct CMUnitTest));
+ ASSERT (Tests != NULL);
+
+ //
+ // Populate buffer of CMUnitTest entries
+ //
+ Index = 0;
+ for (TestEntry = (UNIT_TEST_LIST_ENTRY *)GetFirstNode (&(Suite->TestCaseList));
+ (LIST_ENTRY *)TestEntry != &(Suite->TestCaseList);
+ TestEntry = (UNIT_TEST_LIST_ENTRY *)GetNextNode (&(Suite->TestCaseList), (LIST_ENTRY *)TestEntry)) {
+ UnitTest = &TestEntry->UT;
+ Tests[Index].name = UnitTest->Description;
+ Tests[Index].test_func = CmockaUnitTestFunctionRunner;
+ Tests[Index].setup_func = CmockaUnitTestSetupFunctionRunner;
+ Tests[Index].teardown_func = CmockaUnitTestTeardownFunctionRunner;
+ Tests[Index].initial_state = UnitTest;
+ Index++;
+ }
+ ASSERT (Index == Suite->NumTests);
+
+ //
+ // Run all unit tests in a test suite
+ //
+ mActiveUnitTestSuite = Suite;
+ _cmocka_run_group_tests (
+ Suite->Title,
+ Tests,
+ Suite->NumTests,
+ CmockaUnitTestSuiteSetupFunctionRunner,
+ CmockaUnitTestSuiteTeardownFunctionRunner
+ );
+ mActiveUnitTestSuite = NULL;
+ FreePool (Tests);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Execute all unit test cases in all unit test suites added to a Framework.
+
+ Once a unit test framework is initialized and all unit test suites and unit
+ test cases are registered, this function will cause the unit test framework to
+ dispatch all unit test cases in sequence and record the results for reporting.
+
+ @param[in] FrameworkHandle A handle to the current running framework that
+ dispatched the test. Necessary for recording
+ certain test events with the framework.
+
+ @retval EFI_SUCCESS All test cases were dispached.
+ @retval EFI_INVALID_PARAMETER FrameworkHandle is NULL.
+**/
+EFI_STATUS
+EFIAPI
+RunAllTestSuites (
+ IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle
+ )
+{
+ UNIT_TEST_FRAMEWORK *Framework;
+ UNIT_TEST_SUITE_LIST_ENTRY *Suite;
+ EFI_STATUS Status;
+
+ Framework = (UNIT_TEST_FRAMEWORK *)FrameworkHandle;
+ Suite = NULL;
+
+ if (Framework == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DEBUG((DEBUG_VERBOSE, "---------------------------------------------------------\n"));
+ DEBUG((DEBUG_VERBOSE, "------------ RUNNING ALL TEST SUITES --------------\n"));
+ DEBUG((DEBUG_VERBOSE, "---------------------------------------------------------\n"));
+ mFrameworkHandle = FrameworkHandle;
+
+ //
+ // Iterate all suites
+ //
+ for (Suite = (UNIT_TEST_SUITE_LIST_ENTRY *)GetFirstNode (&Framework->TestSuiteList);
+ (LIST_ENTRY *)Suite != &Framework->TestSuiteList;
+ Suite = (UNIT_TEST_SUITE_LIST_ENTRY *)GetNextNode (&Framework->TestSuiteList, (LIST_ENTRY *)Suite)) {
+ Status = RunTestSuite (&(Suite->UTS));
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Test Suite Failed with Error. %r\n", Status));
+ }
+ }
+
+ mFrameworkHandle = NULL;
+
+ return EFI_SUCCESS;
+}
diff --git a/UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.c b/UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.c
new file mode 100644
index 0000000000..e37c78a41d
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.c
@@ -0,0 +1,853 @@
+/**
+ Implement UnitTestLib
+
+ Copyright (c) Microsoft Corporation.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+#include <Library/UnitTestLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UnitTestPersistenceLib.h>
+#include <Library/UnitTestResultReportLib.h>
+
+///
+/// Forward declaration of prototype
+///
+STATIC
+VOID
+UpdateTestFromSave (
+ IN OUT UNIT_TEST *Test,
+ IN UNIT_TEST_SAVE_HEADER *SavedState
+ );
+
+/**
+ This function will determine whether the short name violates any rules that would
+ prevent it from being used as a reporting name or as a serialization name.
+
+ Example: If the name cannot be serialized to a filesystem file name.
+
+ @param[in] ShortTitleString A pointer to the short title string to be evaluated.
+
+ @retval TRUE The string is acceptable.
+ @retval FALSE The string should not be used.
+
+**/
+STATIC
+BOOLEAN
+IsFrameworkShortNameValid (
+ IN CHAR8 *ShortTitleString
+ )
+{
+ // TODO: Finish this function.
+ return TRUE;
+}
+
+STATIC
+CHAR8*
+AllocateAndCopyString (
+ IN CHAR8 *StringToCopy
+ )
+{
+ CHAR8 *NewString;
+ UINTN NewStringLength;
+
+ NewString = NULL;
+ NewStringLength = AsciiStrnLenS (StringToCopy, UNIT_TEST_MAX_STRING_LENGTH) + 1;
+ NewString = AllocatePool (NewStringLength * sizeof( CHAR8 ));
+ if (NewString != NULL) {
+ AsciiStrCpyS (NewString, NewStringLength, StringToCopy);
+ }
+ return NewString;
+}
+
+STATIC
+VOID
+SetFrameworkFingerprint (
+ OUT UINT8 *Fingerprint,
+ IN UNIT_TEST_FRAMEWORK *Framework
+ )
+{
+ UINT32 NewFingerprint;
+
+ // For this one we'll just use the title and version as the unique fingerprint.
+ NewFingerprint = CalculateCrc32( Framework->Title, (AsciiStrLen( Framework->Title ) * sizeof( CHAR8 )) );
+ NewFingerprint = (NewFingerprint >> 8) ^ CalculateCrc32( Framework->VersionString, (AsciiStrLen( Framework->VersionString ) * sizeof( CHAR8 )) );
+
+ CopyMem( Fingerprint, &NewFingerprint, UNIT_TEST_FINGERPRINT_SIZE );
+ return;
+}
+
+STATIC
+VOID
+SetSuiteFingerprint (
+ OUT UINT8 *Fingerprint,
+ IN UNIT_TEST_FRAMEWORK *Framework,
+ IN UNIT_TEST_SUITE *Suite
+ )
+{
+ UINT32 NewFingerprint;
+
+ // For this one, we'll use the fingerprint from the framework, and the title of the suite.
+ NewFingerprint = CalculateCrc32( &Framework->Fingerprint[0], UNIT_TEST_FINGERPRINT_SIZE );
+ NewFingerprint = (NewFingerprint >> 8) ^ CalculateCrc32( Suite->Title, (AsciiStrLen( Suite->Title ) * sizeof( CHAR8 )) );
+ NewFingerprint = (NewFingerprint >> 8) ^ CalculateCrc32( Suite->Name, (AsciiStrLen(Suite->Name) * sizeof(CHAR8)) );
+
+ CopyMem( Fingerprint, &NewFingerprint, UNIT_TEST_FINGERPRINT_SIZE );
+ return;
+}
+
+STATIC
+VOID
+SetTestFingerprint (
+ OUT UINT8 *Fingerprint,
+ IN UNIT_TEST_SUITE *Suite,
+ IN UNIT_TEST *Test
+ )
+{
+ UINT32 NewFingerprint;
+
+ // For this one, we'll use the fingerprint from the suite, and the description and classname of the test.
+ NewFingerprint = CalculateCrc32( &Suite->Fingerprint[0], UNIT_TEST_FINGERPRINT_SIZE );
+ NewFingerprint = (NewFingerprint >> 8) ^ CalculateCrc32( Test->Description, (AsciiStrLen( Test->Description ) * sizeof( CHAR8 )) );
+ NewFingerprint = (NewFingerprint >> 8) ^ CalculateCrc32( Test->Name, (AsciiStrLen(Test->Name) * sizeof(CHAR8)) );
+
+ CopyMem( Fingerprint, &NewFingerprint, UNIT_TEST_FINGERPRINT_SIZE );
+ return;
+}
+
+STATIC
+BOOLEAN
+CompareFingerprints (
+ IN UINT8 *FingerprintA,
+ IN UINT8 *FingerprintB
+ )
+{
+ return (CompareMem( FingerprintA, FingerprintB, UNIT_TEST_FINGERPRINT_SIZE ) == 0);
+}
+
+/**
+ Cleanup a test framework.
+
+ After tests are run, this will teardown the entire framework and free all
+ allocated data within.
+
+ @param[in] FrameworkHandle A handle to the current running framework that
+ dispatched the test. Necessary for recording
+ certain test events with the framework.
+
+ @retval EFI_SUCCESS All resources associated with framework were
+ freed.
+ @retval EFI_INVALID_PARAMETER FrameworkHandle is NULL.
+**/
+EFI_STATUS
+EFIAPI
+FreeUnitTestFramework (
+ IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle
+ )
+{
+ // TODO: Finish this function.
+ return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+FreeUnitTestSuiteEntry (
+ IN UNIT_TEST_SUITE_LIST_ENTRY *SuiteEntry
+ )
+{
+ // TODO: Finish this function.
+ return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+FreeUnitTestTestEntry (
+ IN UNIT_TEST_LIST_ENTRY *TestEntry
+ )
+{
+ // TODO: Finish this function.
+ return EFI_SUCCESS;
+}
+
+/**
+ Method to Initialize the Unit Test framework. This function registers the
+ test name and also initializes the internal state of the test framework to
+ receive any new suites and tests.
+
+ @param[out] FrameworkHandle Unit test framework to be created.
+ @param[in] Title Null-terminated ASCII string that is the user
+ friendly name of the framework. String is
+ copied.
+ @param[in] ShortTitle Null-terminaled ASCII short string that is the
+ short name of the framework with no spaces.
+ String is copied.
+ @param[in] VersionString Null-terminaled ASCII version string for the
+ framework. String is copied.
+
+ @retval EFI_SUCCESS The unit test framework was initialized.
+ @retval EFI_INVALID_PARAMETER FrameworkHandle is NULL.
+ @retval EFI_INVALID_PARAMETER Title is NULL.
+ @retval EFI_INVALID_PARAMETER ShortTitle is NULL.
+ @retval EFI_INVALID_PARAMETER VersionString is NULL.
+ @retval EFI_INVALID_PARAMETER ShortTitle is invalid.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources available to
+ initialize the unit test framework.
+**/
+EFI_STATUS
+EFIAPI
+InitUnitTestFramework (
+ OUT UNIT_TEST_FRAMEWORK_HANDLE *FrameworkHandle,
+ IN CHAR8 *Title,
+ IN CHAR8 *ShortTitle,
+ IN CHAR8 *VersionString
+ )
+{
+ EFI_STATUS Status;
+ UNIT_TEST_FRAMEWORK_HANDLE NewFrameworkHandle;
+ UNIT_TEST_FRAMEWORK *NewFramework;
+ UNIT_TEST_SAVE_HEADER *SavedState;
+
+ Status = EFI_SUCCESS;
+ NewFramework = NULL;
+
+ //
+ // First, check all pointers and make sure nothing's broked.
+ //
+ if (FrameworkHandle == NULL || Title == NULL ||
+ ShortTitle == NULL || VersionString == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Next, determine whether all of the strings are good to use.
+ //
+ if (!IsFrameworkShortNameValid (ShortTitle)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Next, set aside some space to start messing with the framework.
+ //
+ NewFramework = AllocateZeroPool (sizeof (UNIT_TEST_FRAMEWORK));
+ if (NewFramework == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Next, set up all the test data.
+ //
+ NewFrameworkHandle = (UNIT_TEST_FRAMEWORK_HANDLE)NewFramework;
+ NewFramework->Title = AllocateAndCopyString (Title);
+ NewFramework->ShortTitle = AllocateAndCopyString (ShortTitle);
+ NewFramework->VersionString = AllocateAndCopyString (VersionString);
+ NewFramework->Log = NULL;
+ NewFramework->CurrentTest = NULL;
+ NewFramework->SavedState = NULL;
+ if (NewFramework->Title == NULL ||
+ NewFramework->ShortTitle == NULL ||
+ NewFramework->VersionString == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+ InitializeListHead (&(NewFramework->TestSuiteList));
+
+ //
+ // Create the framework fingerprint.
+ //
+ SetFrameworkFingerprint (&NewFramework->Fingerprint[0], NewFramework);
+
+ //
+ // If there is a persisted context, load it now.
+ //
+ if (DoesCacheExist (NewFrameworkHandle)) {
+ SavedState = (UNIT_TEST_SAVE_HEADER *)NewFramework->SavedState;
+ Status = LoadUnitTestCache (NewFrameworkHandle, &SavedState);
+ if (EFI_ERROR (Status)) {
+ //
+ // Don't actually report it as an error, but emit a warning.
+ //
+ DEBUG (( DEBUG_ERROR, "%a - Cache was detected, but failed to load.\n", __FUNCTION__ ));
+ Status = EFI_SUCCESS;
+ }
+ }
+
+Exit:
+ //
+ // If we're good, then let's copy the framework.
+ //
+ if (!EFI_ERROR (Status)) {
+ *FrameworkHandle = NewFrameworkHandle;
+ } else {
+ //
+ // Otherwise, we need to undo this horrible thing that we've done.
+ //
+ FreeUnitTestFramework (NewFrameworkHandle);
+ }
+
+ return Status;
+}
+
+/**
+ Registers a Unit Test Suite in the Unit Test Framework.
+ At least one test suite must be registered, because all test cases must be
+ within a unit test suite.
+
+ @param[out] SuiteHandle Unit test suite to create
+ @param[in] FrameworkHandle Unit test framework to add unit test suite to
+ @param[in] Title Null-terminated ASCII string that is the user
+ friendly name of the test suite. String is
+ copied.
+ @param[in] Name Null-terminated ASCII string that is the short
+ name of the test suite with no spaces. String
+ is copied.
+ @param[in] Setup Setup function, runs before suite. This is an
+ optional parameter that may be NULL.
+ @param[in] Teardown Teardown function, runs after suite. This is an
+ optional parameter that may be NULL.
+
+ @retval EFI_SUCCESS The unit test suite was created.
+ @retval EFI_INVALID_PARAMETER SuiteHandle is NULL.
+ @retval EFI_INVALID_PARAMETER FrameworkHandle is NULL.
+ @retval EFI_INVALID_PARAMETER Title is NULL.
+ @retval EFI_INVALID_PARAMETER Name is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources available to
+ initialize the unit test suite.
+**/
+EFI_STATUS
+EFIAPI
+CreateUnitTestSuite (
+ OUT UNIT_TEST_SUITE_HANDLE *SuiteHandle,
+ IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle,
+ IN CHAR8 *Title,
+ IN CHAR8 *Name,
+ IN UNIT_TEST_SUITE_SETUP Setup OPTIONAL,
+ IN UNIT_TEST_SUITE_TEARDOWN Teardown OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ UNIT_TEST_SUITE_LIST_ENTRY *NewSuiteEntry;
+ UNIT_TEST_FRAMEWORK *Framework;
+
+ Status = EFI_SUCCESS;
+ Framework = (UNIT_TEST_FRAMEWORK *)FrameworkHandle;
+
+ //
+ // First, let's check to make sure that our parameters look good.
+ //
+ if ((SuiteHandle == NULL) || (Framework == NULL) || (Title == NULL) || (Name == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Create the new entry.
+ //
+ NewSuiteEntry = AllocateZeroPool (sizeof (UNIT_TEST_SUITE_LIST_ENTRY));
+ if (NewSuiteEntry == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Copy the fields we think we need.
+ //
+ NewSuiteEntry->UTS.NumTests = 0;
+ NewSuiteEntry->UTS.Title = AllocateAndCopyString (Title);
+ NewSuiteEntry->UTS.Name = AllocateAndCopyString (Name);
+ NewSuiteEntry->UTS.Setup = Setup;
+ NewSuiteEntry->UTS.Teardown = Teardown;
+ NewSuiteEntry->UTS.ParentFramework = FrameworkHandle;
+ InitializeListHead (&(NewSuiteEntry->Entry)); // List entry for sibling suites.
+ InitializeListHead (&(NewSuiteEntry->UTS.TestCaseList)); // List entry for child tests.
+ if (NewSuiteEntry->UTS.Title == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+
+ if (NewSuiteEntry->UTS.Name == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+
+ //
+ // Create the suite fingerprint.
+ //
+ SetSuiteFingerprint( &NewSuiteEntry->UTS.Fingerprint[0], Framework, &NewSuiteEntry->UTS );
+
+Exit:
+ //
+ // If everything is going well, add the new suite to the tail list for the framework.
+ //
+ if (!EFI_ERROR( Status )) {
+ InsertTailList (&(Framework->TestSuiteList), (LIST_ENTRY *)NewSuiteEntry);
+ *SuiteHandle = (UNIT_TEST_SUITE_HANDLE)(&NewSuiteEntry->UTS);
+ } else {
+ //
+ // Otherwise, make with the destruction.
+ //
+ FreeUnitTestSuiteEntry (NewSuiteEntry);
+ }
+
+ return Status;
+}
+
+/**
+ Adds test case to Suite
+
+ @param[in] SuiteHandle Unit test suite to add test to.
+ @param[in] Description Null-terminated ASCII string that is the user
+ friendly description of a test. String is copied.
+ @param[in] Name Null-terminated ASCII string that is the short name
+ of the test with no spaces. String is copied.
+ @param[in] Function Unit test function.
+ @param[in] Prerequisite Prerequisite function, runs before test. This is
+ an optional parameter that may be NULL.
+ @param[in] CleanUp Clean up function, runs after test. This is an
+ optional parameter that may be NULL.
+ @param[in] Context Pointer to context. This is an optional parameter
+ that may be NULL.
+
+ @retval EFI_SUCCESS The unit test case was added to Suite.
+ @retval EFI_INVALID_PARAMETER SuiteHandle is NULL.
+ @retval EFI_INVALID_PARAMETER Description is NULL.
+ @retval EFI_INVALID_PARAMETER Name is NULL.
+ @retval EFI_INVALID_PARAMETER Function is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources available to
+ add the unit test case to Suite.
+**/
+EFI_STATUS
+EFIAPI
+AddTestCase (
+ IN UNIT_TEST_SUITE_HANDLE SuiteHandle,
+ IN CHAR8 *Description,
+ IN CHAR8 *Name,
+ IN UNIT_TEST_FUNCTION Function,
+ IN UNIT_TEST_PREREQUISITE Prerequisite OPTIONAL,
+ IN UNIT_TEST_CLEANUP CleanUp OPTIONAL,
+ IN UNIT_TEST_CONTEXT Context OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ UNIT_TEST_LIST_ENTRY *NewTestEntry;
+ UNIT_TEST_FRAMEWORK *ParentFramework;
+ UNIT_TEST_SUITE *Suite;
+
+ Status = EFI_SUCCESS;
+ Suite = (UNIT_TEST_SUITE *)SuiteHandle;
+ ParentFramework = (UNIT_TEST_FRAMEWORK *)Suite->ParentFramework;
+
+ //
+ // First, let's check to make sure that our parameters look good.
+ //
+ if ((Suite == NULL) || (Description == NULL) || (Name == NULL) || (Function == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Create the new entry.
+ NewTestEntry = AllocateZeroPool (sizeof( UNIT_TEST_LIST_ENTRY ));
+ if (NewTestEntry == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Copy the fields we think we need.
+ NewTestEntry->UT.Description = AllocateAndCopyString (Description);
+ NewTestEntry->UT.Name = AllocateAndCopyString (Name);
+ NewTestEntry->UT.FailureType = FAILURETYPE_NOFAILURE;
+ NewTestEntry->UT.FailureMessage[0] = '\0';
+ NewTestEntry->UT.Log = NULL;
+ NewTestEntry->UT.Prerequisite = Prerequisite;
+ NewTestEntry->UT.CleanUp = CleanUp;
+ NewTestEntry->UT.RunTest = Function;
+ NewTestEntry->UT.Context = Context;
+ NewTestEntry->UT.Result = UNIT_TEST_PENDING;
+ NewTestEntry->UT.ParentSuite = SuiteHandle;
+ InitializeListHead (&(NewTestEntry->Entry)); // List entry for sibling tests.
+ if (NewTestEntry->UT.Description == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+ if (NewTestEntry->UT.Name == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+
+ //
+ // Create the test fingerprint.
+ //
+ SetTestFingerprint (&NewTestEntry->UT.Fingerprint[0], Suite, &NewTestEntry->UT);
+
+ // TODO: Make sure that duplicate fingerprints cannot be created.
+
+ //
+ // If there is saved test data, update this record.
+ //
+ if (ParentFramework->SavedState != NULL) {
+ UpdateTestFromSave (&NewTestEntry->UT, ParentFramework->SavedState);
+ }
+
+Exit:
+ //
+ // If everything is going well, add the new suite to the tail list for the framework.
+ //
+ if (!EFI_ERROR (Status)) {
+ InsertTailList (&(Suite->TestCaseList), (LIST_ENTRY*)NewTestEntry);
+ Suite->NumTests++;
+ } else {
+ //
+ // Otherwise, make with the destruction.
+ //
+ FreeUnitTestTestEntry (NewTestEntry);
+ }
+
+ return Status;
+}
+
+STATIC
+VOID
+UpdateTestFromSave (
+ IN OUT UNIT_TEST *Test,
+ IN UNIT_TEST_SAVE_HEADER *SavedState
+ )
+{
+ UNIT_TEST_SAVE_TEST *CurrentTest;
+ UNIT_TEST_SAVE_TEST *MatchingTest;
+ UINT8 *FloatingPointer;
+ UNIT_TEST_SAVE_CONTEXT *SavedContext;
+ UINTN Index;
+
+ //
+ // First, evaluate the inputs.
+ //
+ if (Test == NULL || SavedState == NULL) {
+ return;
+ }
+ if (SavedState->TestCount == 0) {
+ return;
+ }
+
+ //
+ // Next, determine whether a matching test can be found.
+ // Start at the beginning.
+ //
+ MatchingTest = NULL;
+ FloatingPointer = (UINT8 *)SavedState + sizeof (*SavedState);
+ for (Index = 0; Index < SavedState->TestCount; Index++) {
+ CurrentTest = (UNIT_TEST_SAVE_TEST *)FloatingPointer;
+ if (CompareFingerprints (&Test->Fingerprint[0], &CurrentTest->Fingerprint[0])) {
+ MatchingTest = CurrentTest;
+ //
+ // If there's a saved context, it's important that we iterate through the entire list.
+ //
+ if (!SavedState->HasSavedContext) {
+ break;
+ }
+ }
+
+ //
+ // If we didn't find it, we have to increment to the next test.
+ //
+ FloatingPointer = (UINT8 *)CurrentTest + CurrentTest->Size;
+ }
+
+ //
+ // If a matching test was found, copy the status.
+ //
+ if (MatchingTest) {
+ //
+ // Override the test status with the saved status.
+ //
+ Test->Result = MatchingTest->Result;
+
+ Test->FailureType = MatchingTest->FailureType;
+ AsciiStrnCpyS (
+ &Test->FailureMessage[0],
+ UNIT_TEST_TESTFAILUREMSG_LENGTH,
+ &MatchingTest->FailureMessage[0],
+ UNIT_TEST_TESTFAILUREMSG_LENGTH
+ );
+
+ //
+ // If there is a log string associated, grab that.
+ // We can tell that there's a log string because the "size" will be larger than
+ // the structure size.
+ // IMPORTANT NOTE: There are security implications here.
+ // This data is user-supplied and we're about to play kinda
+ // fast and loose with data buffers.
+ //
+ if (MatchingTest->Size > sizeof (UNIT_TEST_SAVE_TEST)) {
+ UnitTestLogInit (Test, (UINT8 *)MatchingTest->Log, MatchingTest->Size - sizeof (UNIT_TEST_SAVE_TEST));
+ }
+ }
+
+ //
+ // If the saved context exists and matches this test, grab it, too.
+ //
+ if (SavedState->HasSavedContext) {
+ //
+ // If there was a saved context, the "matching test" loop will have placed the FloatingPointer
+ // at the beginning of the context structure.
+ //
+ SavedContext = (UNIT_TEST_SAVE_CONTEXT *)FloatingPointer;
+ if ((SavedContext->Size - sizeof (UNIT_TEST_SAVE_CONTEXT)) > 0 &&
+ CompareFingerprints (&Test->Fingerprint[0], &SavedContext->Fingerprint[0])) {
+ //
+ // Override the test context with the saved context.
+ //
+ Test->Context = (VOID*)SavedContext->Data;
+ }
+ }
+}
+
+STATIC
+UNIT_TEST_SAVE_HEADER*
+SerializeState (
+ IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle,
+ IN UNIT_TEST_CONTEXT ContextToSave, OPTIONAL
+ IN UINTN ContextToSaveSize
+ )
+{
+ UNIT_TEST_FRAMEWORK *Framework;
+ UNIT_TEST_SAVE_HEADER *Header;
+ LIST_ENTRY *SuiteListHead;
+ LIST_ENTRY *Suite;
+ LIST_ENTRY *TestListHead;
+ LIST_ENTRY *Test;
+ UINT32 TestCount;
+ UINT32 TotalSize;
+ UINTN LogSize;
+ UNIT_TEST_SAVE_TEST *TestSaveData;
+ UNIT_TEST_SAVE_CONTEXT *TestSaveContext;
+ UNIT_TEST *UnitTest;
+ UINT8 *FloatingPointer;
+
+ Framework = (UNIT_TEST_FRAMEWORK *)FrameworkHandle;
+ Header = NULL;
+
+ //
+ // First, let's not make assumptions about the parameters.
+ //
+ if (Framework == NULL ||
+ (ContextToSave != NULL && ContextToSaveSize == 0) ||
+ ContextToSaveSize > MAX_UINT32) {
+ return NULL;
+ }
+
+ //
+ // Next, we've gotta figure out the resources that will be required to serialize the
+ // the framework state so that we can persist it.
+ // To start with, we're gonna need a header.
+ //
+ TotalSize = sizeof (UNIT_TEST_SAVE_HEADER);
+ //
+ // Now we need to figure out how many tests there are.
+ //
+ TestCount = 0;
+ //
+ // Iterate all suites.
+ //
+ SuiteListHead = &Framework->TestSuiteList;
+ for (Suite = GetFirstNode (SuiteListHead); Suite != SuiteListHead; Suite = GetNextNode (SuiteListHead, Suite)) {
+ //
+ // Iterate all tests within the suite.
+ //
+ TestListHead = &((UNIT_TEST_SUITE_LIST_ENTRY *)Suite)->UTS.TestCaseList;
+ for (Test = GetFirstNode (TestListHead); Test != TestListHead; Test = GetNextNode (TestListHead, Test)) {
+ UnitTest = &((UNIT_TEST_LIST_ENTRY *)Test)->UT;
+ //
+ // Account for the size of a test structure.
+ //
+ TotalSize += sizeof( UNIT_TEST_SAVE_TEST );
+ //
+ // If there's a log, make sure to account for the log size.
+ //
+ if (UnitTest->Log != NULL) {
+ //
+ // The +1 is for the NULL character. Can't forget the NULL character.
+ //
+ LogSize = (AsciiStrLen (UnitTest->Log) + 1) * sizeof (CHAR8);
+ ASSERT (LogSize < MAX_UINT32);
+ TotalSize += (UINT32)LogSize;
+ }
+ //
+ // Increment the test count.
+ //
+ TestCount++;
+ }
+ }
+ //
+ // If there are no tests, we're done here.
+ //
+ if (TestCount == 0) {
+ return NULL;
+ }
+ //
+ // Add room for the context, if there is one.
+ //
+ if (ContextToSave != NULL) {
+ TotalSize += sizeof (UNIT_TEST_SAVE_CONTEXT) + (UINT32)ContextToSaveSize;
+ }
+
+ //
+ // Now that we know the size, we need to allocate space for the serialized output.
+ //
+ Header = AllocateZeroPool (TotalSize);
+ if (Header == NULL) {
+ return NULL;
+ }
+
+ //
+ // Alright, let's start setting up some data.
+ //
+ Header->Version = UNIT_TEST_PERSISTENCE_LIB_VERSION;
+ Header->SaveStateSize = TotalSize;
+ CopyMem (&Header->Fingerprint[0], &Framework->Fingerprint[0], UNIT_TEST_FINGERPRINT_SIZE);
+ CopyMem (&Header->StartTime, &Framework->StartTime, sizeof (EFI_TIME));
+ Header->TestCount = TestCount;
+ Header->HasSavedContext = FALSE;
+
+ //
+ // Start adding all of the test cases.
+ // Set the floating pointer to the start of the current test save buffer.
+ //
+ FloatingPointer = (UINT8*)Header + sizeof( UNIT_TEST_SAVE_HEADER );
+ //
+ // Iterate all suites.
+ //
+ SuiteListHead = &Framework->TestSuiteList;
+ for (Suite = GetFirstNode (SuiteListHead); Suite != SuiteListHead; Suite = GetNextNode (SuiteListHead, Suite)) {
+ //
+ // Iterate all tests within the suite.
+ //
+ TestListHead = &((UNIT_TEST_SUITE_LIST_ENTRY *)Suite)->UTS.TestCaseList;
+ for (Test = GetFirstNode (TestListHead); Test != TestListHead; Test = GetNextNode (TestListHead, Test)) {
+ TestSaveData = (UNIT_TEST_SAVE_TEST *)FloatingPointer;
+ UnitTest = &((UNIT_TEST_LIST_ENTRY *)Test)->UT;
+
+ //
+ // Save the fingerprint.
+ //
+ CopyMem (&TestSaveData->Fingerprint[0], &UnitTest->Fingerprint[0], UNIT_TEST_FINGERPRINT_SIZE);
+
+ //
+ // Save the result.
+ //
+ TestSaveData->Result = UnitTest->Result;
+ TestSaveData->FailureType = UnitTest->FailureType;
+ AsciiStrnCpyS (&TestSaveData->FailureMessage[0], UNIT_TEST_TESTFAILUREMSG_LENGTH, &UnitTest->FailureMessage[0], UNIT_TEST_TESTFAILUREMSG_LENGTH);
+
+
+ //
+ // If there is a log, save the log.
+ //
+ FloatingPointer += sizeof (UNIT_TEST_SAVE_TEST);
+ if (UnitTest->Log != NULL) {
+ //
+ // The +1 is for the NULL character. Can't forget the NULL character.
+ //
+ LogSize = (AsciiStrLen (UnitTest->Log) + 1) * sizeof (CHAR8);
+ CopyMem (FloatingPointer, UnitTest->Log, LogSize);
+ FloatingPointer += LogSize;
+ }
+
+ //
+ // Update the size once the structure is complete.
+ // NOTE: Should this be a straight cast without validation?
+ //
+ TestSaveData->Size = (UINT32)(FloatingPointer - (UINT8 *)TestSaveData);
+ }
+ }
+
+ //
+ // If there is a context to save, let's do that now.
+ //
+ if (ContextToSave != NULL && Framework->CurrentTest != NULL) {
+ TestSaveContext = (UNIT_TEST_SAVE_CONTEXT*)FloatingPointer;
+ TestSaveContext->Size = (UINT32)ContextToSaveSize + sizeof (UNIT_TEST_SAVE_CONTEXT);
+ CopyMem (&TestSaveContext->Fingerprint[0], &Framework->CurrentTest->Fingerprint[0], UNIT_TEST_FINGERPRINT_SIZE);
+ CopyMem (((UINT8 *)TestSaveContext + sizeof (UNIT_TEST_SAVE_CONTEXT)), ContextToSave, ContextToSaveSize);
+ Header->HasSavedContext = TRUE;
+ }
+
+ return Header;
+}
+
+/**
+ Leverages a framework-specific mechanism (see UnitTestPersistenceLib if you're
+ a framework author) to save the state of the executing framework along with
+ any allocated data so that the test may be resumed upon reentry. A test case
+ should pass any needed context (which, to prevent an infinite loop, should be
+ at least the current execution count) which will be saved by the framework and
+ passed to the test case upon resume.
+
+ Generally called from within a test case prior to quitting or rebooting.
+
+ @param[in] FrameworkHandle A handle to the current running framework that
+ dispatched the test. Necessary for recording
+ certain test events with the framework.
+ @param[in] ContextToSave A buffer of test case-specific data to be saved
+ along with framework state. Will be passed as
+ "Context" to the test case upon resume. This
+ is an optional parameter that may be NULL.
+ @param[in] ContextToSaveSize Size of the ContextToSave buffer.
+
+ @retval EFI_SUCCESS The framework state and context were saved.
+ @retval EFI_INVALID_PARAMETER FrameworkHandle is NULL.
+ @retval EFI_INVALID_PARAMETER ContextToSave is not NULL and
+ ContextToSaveSize is 0.
+ @retval EFI_INVALID_PARAMETER ContextToSave is >= 4GB.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources available to
+ save the framework and context state.
+ @retval EFI_DEVICE_ERROR The framework and context state could not be
+ saved to a persistent storage device due to a
+ device error.
+**/
+EFI_STATUS
+EFIAPI
+SaveFrameworkState (
+ IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle,
+ IN UNIT_TEST_CONTEXT ContextToSave OPTIONAL,
+ IN UINTN ContextToSaveSize
+ )
+{
+ EFI_STATUS Status;
+ UNIT_TEST_SAVE_HEADER *Header;
+
+ Header = NULL;
+
+ //
+ // First, let's not make assumptions about the parameters.
+ //
+ if (FrameworkHandle == NULL ||
+ (ContextToSave != NULL && ContextToSaveSize == 0) ||
+ ContextToSaveSize > MAX_UINT32) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Now, let's package up all the data for saving.
+ //
+ Header = SerializeState (FrameworkHandle, ContextToSave, ContextToSaveSize);
+ if (Header == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // All that should be left to do is save it using the associated persistence lib.
+ //
+ Status = SaveUnitTestCache (FrameworkHandle, Header);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a - Could not save state! %r\n", __FUNCTION__, Status));
+ Status = EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Free data that was used.
+ //
+ FreePool (Header);
+
+ return Status;
+}
diff --git a/UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.inf b/UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.inf
new file mode 100644
index 0000000000..96e40e973c
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.inf
@@ -0,0 +1,37 @@
+## @file
+# Library to support Unit Testing from PEI, DXE, SMM, and UEFI Applications.
+#
+# Copyright (c) Microsoft Corporation.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x00010017
+ BASE_NAME = UnitTestLib
+ MODULE_UNI_FILE = UnitTestLib.uni
+ FILE_GUID = 98CEF9CA-15CE-40A3-ADE8-C299953CD0F6
+ VERSION_STRING = 1.0
+ MODULE_TYPE = UEFI_DRIVER
+ LIBRARY_CLASS = UnitTestLib|PEIM DXE_DRIVER DXE_SMM_DRIVER UEFI_DRIVER UEFI_APPLICATION
+
+[Sources]
+ UnitTestLib.c
+ RunTests.c
+ Assert.c
+ Log.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ PcdLib
+ DebugLib
+ MemoryAllocationLib
+ UnitTestPersistenceLib
+ UnitTestResultReportLib
+
+[Pcd]
+ gUnitTestFrameworkPkgTokenSpaceGuid.PcdUnitTestLogLevel ## CONSUMES
diff --git a/UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.uni b/UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.uni
new file mode 100644
index 0000000000..fe7c9c7f71
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.uni
@@ -0,0 +1,11 @@
+// /** @file
+// Library to support Unit Testing from PEI, DXE, SMM, and UEFI Applications.
+//
+// Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT #language en-US "Library to support Unit Testing from PEI, DXE, SMM, and UEFI Applications"
+
+#string STR_MODULE_DESCRIPTION #language en-US "Library to support Unit Testing from PEI, DXE, SMM, and UEFI Applications."
diff --git a/UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLibCmocka.inf b/UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLibCmocka.inf
new file mode 100644
index 0000000000..b12af91576
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLibCmocka.inf
@@ -0,0 +1,38 @@
+## @file
+# Library to support Unit Testing from host environments using Cmocka services.
+#
+# Copyright (c) 2019 - 2020, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x00010017
+ BASE_NAME = UnitTestLibCmocka
+ MODULE_UNI_FILE = UnitTestLibCmocka.uni
+ FILE_GUID = C800595F-45A3-45A1-8B50-28F01C2A5A4F
+ VERSION_STRING = 1.0
+ MODULE_TYPE = UEFI_DRIVER
+ LIBRARY_CLASS = UnitTestLib|HOST_APPLICATION
+
+[Sources]
+ UnitTestLib.c
+ RunTestsCmocka.c
+ AssertCmocka.c
+ Log.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ PcdLib
+ DebugLib
+ MemoryAllocationLib
+ UnitTestPersistenceLib
+ UnitTestResultReportLib
+ CmockaLib
+
+[Pcd]
+ gUnitTestFrameworkPkgTokenSpaceGuid.PcdUnitTestLogLevel ## CONSUMES
diff --git a/UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLibCmocka.uni b/UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLibCmocka.uni
new file mode 100644
index 0000000000..aa25a44e35
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLibCmocka.uni
@@ -0,0 +1,11 @@
+// /** @file
+// Library to support Unit Testing from host environments using Cmocka services.
+//
+// Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT #language en-US "Library to support Unit Testing from host environments using Cmocka services"
+
+#string STR_MODULE_DESCRIPTION #language en-US "Library to support Unit Testing from host environments using Cmocka services."
diff --git a/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.c b/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.c
new file mode 100644
index 0000000000..e28327652e
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.c
@@ -0,0 +1,75 @@
+/** @file
+ This is an instance of the Unit Test Persistence Lib that does nothing.
+
+ Copyright (c) Microsoft Corporation.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+#include <Library/UnitTestPersistenceLib.h>
+
+/**
+ Determines whether a persistence cache already exists for
+ the given framework.
+
+ @param[in] FrameworkHandle A pointer to the framework that is being persisted.
+
+ @retval TRUE
+ @retval FALSE Cache doesn't exist or an error occurred.
+
+**/
+BOOLEAN
+EFIAPI
+DoesCacheExist (
+ IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle
+ )
+{
+ return FALSE;
+}
+
+/**
+ Will save the data associated with an internal Unit Test Framework
+ state in a manner that can persist a Unit Test Application quit or
+ even a system reboot.
+
+ @param[in] FrameworkHandle A pointer to the framework that is being persisted.
+ @param[in] SaveData A pointer to the buffer containing the serialized
+ framework internal state.
+
+ @retval EFI_SUCCESS Data is persisted and the test can be safely quit.
+ @retval Others Data is not persisted and test cannot be resumed upon exit.
+
+**/
+EFI_STATUS
+EFIAPI
+SaveUnitTestCache (
+ IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle,
+ IN UNIT_TEST_SAVE_HEADER *SaveData
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Will retrieve any cached state associated with the given framework.
+ Will allocate a buffer to hold the loaded data.
+
+ @param[in] FrameworkHandle A pointer to the framework that is being persisted.
+ @param[in] SaveData A pointer pointer that will be updated with the address
+ of the loaded data buffer.
+
+ @retval EFI_SUCCESS Data has been loaded successfully and SaveData is updated
+ with a pointer to the buffer.
+ @retval Others An error has occurred and no data has been loaded. SaveData
+ is set to NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+LoadUnitTestCache (
+ IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle,
+ OUT UNIT_TEST_SAVE_HEADER **SaveData
+ )
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.inf b/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.inf
new file mode 100644
index 0000000000..1175772662
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.inf
@@ -0,0 +1,28 @@
+## @file
+# This is an instance of the Unit Test Persistence Lib does nothing.
+#
+# Copyright (c) Microsoft Corporation.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x00010017
+ BASE_NAME = UnitTestPersistenceLibNull
+ MODULE_UNI_FILE = UnitTestPersistenceLibNull.uni
+ FILE_GUID = B8553C7A-0B0B-4BBD-9DF3-825804BF26AB
+ VERSION_STRING = 1.0
+ MODULE_TYPE = UEFI_DRIVER
+ LIBRARY_CLASS = UnitTestPersistenceLib
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Sources]
+ UnitTestPersistenceLibNull.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec
diff --git a/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.uni b/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.uni
new file mode 100644
index 0000000000..00f7d8d7f0
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.uni
@@ -0,0 +1,11 @@
+// /** @file
+// NULL library for Unit Test Persistence Lib.
+//
+// Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT #language en-US "NULL library for Unit Test Persistence Lib"
+
+#string STR_MODULE_DESCRIPTION #language en-US "NULL library for Unit Test Persistence Lib."
diff --git a/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.c b/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.c
new file mode 100644
index 0000000000..ccca9bfacb
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.c
@@ -0,0 +1,416 @@
+/** @file
+ This is an instance of the Unit Test Persistence Lib that will utilize
+ the filesystem that a test application is running from to save a serialized
+ version of the internal test state in case the test needs to quit and restore.
+
+ Copyright (c) Microsoft Corporation.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <PiDxe.h>
+#include <Library/UnitTestPersistenceLib.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/ShellLib.h>
+#include <Protocol/LoadedImage.h>
+
+#define CACHE_FILE_SUFFIX L"_Cache.dat"
+
+/**
+ Generate the device path to the cache file.
+
+ @param[in] FrameworkHandle A pointer to the framework that is being persisted.
+
+ @retval !NULL A pointer to the EFI_FILE protocol instance for the filesystem.
+ @retval NULL Filesystem could not be found or an error occurred.
+
+**/
+STATIC
+EFI_DEVICE_PATH_PROTOCOL*
+GetCacheFileDevicePath (
+ IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle
+ )
+{
+ EFI_STATUS Status;
+ UNIT_TEST_FRAMEWORK *Framework;
+ EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
+ CHAR16 *AppPath;
+ CHAR16 *CacheFilePath;
+ CHAR16 *TestName;
+ UINTN DirectorySlashOffset;
+ UINTN CacheFilePathLength;
+ EFI_DEVICE_PATH_PROTOCOL *CacheFileDevicePath;
+
+ Framework = (UNIT_TEST_FRAMEWORK*)FrameworkHandle;
+ AppPath = NULL;
+ CacheFilePath = NULL;
+ TestName = NULL;
+ CacheFileDevicePath = NULL;
+
+ //
+ // First, we need to get some information from the loaded image.
+ //
+ Status = gBS->HandleProtocol (
+ gImageHandle,
+ &gEfiLoadedImageProtocolGuid,
+ (VOID**)&LoadedImage
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_WARN, "%a - Failed to locate DevicePath for loaded image. %r\n", __FUNCTION__, Status));
+ return NULL;
+ }
+
+ //
+ // Before we can start, change test name from ASCII to Unicode.
+ //
+ CacheFilePathLength = AsciiStrLen (Framework->ShortTitle) + 1;
+ TestName = AllocatePool (CacheFilePathLength);
+ if (!TestName) {
+ goto Exit;
+ }
+ AsciiStrToUnicodeStrS (Framework->ShortTitle, TestName, CacheFilePathLength);
+
+ //
+ // Now we should have the device path of the root device and a file path for the rest.
+ // In order to target the directory for the test application, we must process
+ // the file path a little.
+ //
+ // NOTE: This may not be necessary... Path processing functions exist...
+ // PathCleanUpDirectories (FileNameCopy);
+ // if (PathRemoveLastItem (FileNameCopy)) {
+ //
+ AppPath = ConvertDevicePathToText (LoadedImage->FilePath, TRUE, TRUE); // NOTE: This must be freed.
+ DirectorySlashOffset = StrLen (AppPath);
+ //
+ // Make sure we didn't get any weird data.
+ //
+ if (DirectorySlashOffset == 0) {
+ DEBUG ((DEBUG_ERROR, "%a - Weird 0-length string when processing app path.\n", __FUNCTION__));
+ goto Exit;
+ }
+
+ //
+ // Now that we know we have a decent string, let's take a deeper look.
+ //
+ do {
+ if (AppPath[DirectorySlashOffset] == L'\\') {
+ break;
+ }
+ DirectorySlashOffset--;
+ } while (DirectorySlashOffset > 0);
+
+ //
+ // After that little maneuver, DirectorySlashOffset should be pointing at the last '\' in AppString.
+ // That would be the path to the parent directory that the test app is executing from.
+ // Let's check and make sure that's right.
+ //
+ if (AppPath[DirectorySlashOffset] != L'\\') {
+ DEBUG ((DEBUG_ERROR, "%a - Could not find a single directory separator in app path.\n", __FUNCTION__));
+ goto Exit;
+ }
+
+ //
+ // Now we know some things, we're ready to produce our output string, I think.
+ //
+ CacheFilePathLength = DirectorySlashOffset + 1;
+ CacheFilePathLength += StrLen (TestName);
+ CacheFilePathLength += StrLen (CACHE_FILE_SUFFIX);
+ CacheFilePathLength += 1; // Don't forget the NULL terminator.
+ CacheFilePath = AllocateZeroPool (CacheFilePathLength * sizeof (CHAR16));
+ if (!CacheFilePath) {
+ goto Exit;
+ }
+
+ //
+ // Let's produce our final path string, shall we?
+ //
+ StrnCpyS (CacheFilePath, CacheFilePathLength, AppPath, DirectorySlashOffset + 1); // Copy the path for the parent directory.
+ StrCatS (CacheFilePath, CacheFilePathLength, TestName); // Copy the base name for the test cache.
+ StrCatS (CacheFilePath, CacheFilePathLength, CACHE_FILE_SUFFIX); // Copy the file suffix.
+
+ //
+ // Finally, try to create the device path for the thing thing.
+ //
+ CacheFileDevicePath = FileDevicePath (LoadedImage->DeviceHandle, CacheFilePath);
+
+Exit:
+ //
+ // Free allocated buffers.
+ //
+ if (AppPath != NULL) {
+ FreePool (AppPath);
+ }
+ if (CacheFilePath != NULL) {
+ FreePool (CacheFilePath);
+ }
+ if (TestName != NULL) {
+ FreePool (TestName);
+ }
+
+ return CacheFileDevicePath;
+}
+
+/**
+ Determines whether a persistence cache already exists for
+ the given framework.
+
+ @param[in] FrameworkHandle A pointer to the framework that is being persisted.
+
+ @retval TRUE
+ @retval FALSE Cache doesn't exist or an error occurred.
+
+**/
+BOOLEAN
+EFIAPI
+DoesCacheExist (
+ IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *FileDevicePath;
+ EFI_STATUS Status;
+ SHELL_FILE_HANDLE FileHandle;
+
+ //
+ // NOTE: This devpath is allocated and must be freed.
+ //
+ FileDevicePath = GetCacheFileDevicePath (FrameworkHandle);
+
+ //
+ // Check to see whether the file exists. If the file can be opened for
+ // reading, it exists. Otherwise, probably not.
+ //
+ Status = ShellOpenFileByDevicePath (
+ &FileDevicePath,
+ &FileHandle,
+ EFI_FILE_MODE_READ,
+ 0
+ );
+ if (!EFI_ERROR (Status)) {
+ ShellCloseFile (&FileHandle);
+ }
+
+ if (FileDevicePath != NULL) {
+ FreePool (FileDevicePath);
+ }
+
+ DEBUG ((DEBUG_VERBOSE, "%a - Returning %d\n", __FUNCTION__, !EFI_ERROR (Status)));
+
+ return (!EFI_ERROR (Status));
+}
+
+/**
+ Will save the data associated with an internal Unit Test Framework
+ state in a manner that can persist a Unit Test Application quit or
+ even a system reboot.
+
+ @param[in] FrameworkHandle A pointer to the framework that is being persisted.
+ @param[in] SaveData A pointer to the buffer containing the serialized
+ framework internal state.
+
+ @retval EFI_SUCCESS Data is persisted and the test can be safely quit.
+ @retval Others Data is not persisted and test cannot be resumed upon exit.
+
+**/
+EFI_STATUS
+EFIAPI
+SaveUnitTestCache (
+ IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle,
+ IN UNIT_TEST_SAVE_HEADER *SaveData
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *FileDevicePath;
+ EFI_STATUS Status;
+ SHELL_FILE_HANDLE FileHandle;
+ UINTN WriteCount;
+
+ //
+ // Check the inputs for sanity.
+ //
+ if (FrameworkHandle == NULL || SaveData == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Determine the path for the cache file.
+ // NOTE: This devpath is allocated and must be freed.
+ //
+ FileDevicePath = GetCacheFileDevicePath (FrameworkHandle);
+
+ //
+ //First lets open the file if it exists so we can delete it...This is the work around for truncation
+ //
+ Status = ShellOpenFileByDevicePath (
+ &FileDevicePath,
+ &FileHandle,
+ (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE),
+ 0
+ );
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // If file handle above was opened it will be closed by the delete.
+ //
+ Status = ShellDeleteFile (&FileHandle);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a failed to delete file %r\n", __FUNCTION__, Status));
+ }
+ }
+
+ //
+ // Now that we know the path to the file... let's open it for writing.
+ //
+ Status = ShellOpenFileByDevicePath (
+ &FileDevicePath,
+ &FileHandle,
+ (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE),
+ 0
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a - Opening file for writing failed! %r\n", __FUNCTION__, Status));
+ goto Exit;
+ }
+
+ //
+ // Write the data to the file.
+ //
+ WriteCount = SaveData->SaveStateSize;
+ DEBUG ((DEBUG_INFO, "%a - Writing %d bytes to file...\n", __FUNCTION__, WriteCount));
+ Status = ShellWriteFile (
+ FileHandle,
+ &WriteCount,
+ SaveData
+ );
+
+ if (EFI_ERROR (Status) || WriteCount != SaveData->SaveStateSize) {
+ DEBUG ((DEBUG_ERROR, "%a - Writing to file failed! %r\n", __FUNCTION__, Status));
+ } else {
+ DEBUG ((DEBUG_INFO, "%a - SUCCESS!\n", __FUNCTION__));
+ }
+
+ //
+ // No matter what, we should probably close the file.
+ //
+ ShellCloseFile (&FileHandle);
+
+Exit:
+ if (FileDevicePath != NULL) {
+ FreePool (FileDevicePath);
+ }
+
+ return Status;
+}
+
+/**
+ Will retrieve any cached state associated with the given framework.
+ Will allocate a buffer to hold the loaded data.
+
+ @param[in] FrameworkHandle A pointer to the framework that is being persisted.
+ @param[in] SaveData A pointer pointer that will be updated with the address
+ of the loaded data buffer.
+
+ @retval EFI_SUCCESS Data has been loaded successfully and SaveData is updated
+ with a pointer to the buffer.
+ @retval Others An error has occurred and no data has been loaded. SaveData
+ is set to NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+LoadUnitTestCache (
+ IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle,
+ OUT UNIT_TEST_SAVE_HEADER **SaveData
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *FileDevicePath;
+ SHELL_FILE_HANDLE FileHandle;
+ BOOLEAN IsFileOpened;
+ UINT64 LargeFileSize;
+ UINTN FileSize;
+ UNIT_TEST_SAVE_HEADER *Buffer;
+
+ IsFileOpened = FALSE;
+ Buffer = NULL;
+
+ //
+ // Check the inputs for sanity.
+ //
+ if (FrameworkHandle == NULL || SaveData == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Determine the path for the cache file.
+ // NOTE: This devpath is allocated and must be freed.
+ //
+ FileDevicePath = GetCacheFileDevicePath (FrameworkHandle);
+
+ //
+ // Now that we know the path to the file... let's open it for writing.
+ //
+ Status = ShellOpenFileByDevicePath (
+ &FileDevicePath,
+ &FileHandle,
+ EFI_FILE_MODE_READ,
+ 0
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a - Opening file for writing failed! %r\n", __FUNCTION__, Status));
+ goto Exit;
+ } else {
+ IsFileOpened = TRUE;
+ }
+
+ //
+ // Now that the file is opened, we need to determine how large a buffer we need.
+ //
+ Status = ShellGetFileSize (FileHandle, &LargeFileSize);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a - Failed to determine file size! %r\n", __FUNCTION__, Status));
+ goto Exit;
+ }
+
+ //
+ // Now that we know the size, let's allocated a buffer to hold the contents.
+ //
+ FileSize = (UINTN)LargeFileSize; // You know what... if it's too large, this lib don't care.
+ Buffer = AllocatePool (FileSize);
+ if (Buffer == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a - Failed to allocate a pool to hold the file contents! %r\n", __FUNCTION__, Status));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+
+ //
+ // Finally, let's read the data.
+ //
+ Status = ShellReadFile (FileHandle, &FileSize, Buffer);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a - Failed to read the file contents! %r\n", __FUNCTION__, Status));
+ }
+
+Exit:
+ //
+ // Free allocated buffers
+ //
+ if (FileDevicePath != NULL) {
+ FreePool (FileDevicePath);
+ }
+ if (IsFileOpened) {
+ ShellCloseFile (&FileHandle);
+ }
+
+ //
+ // If we're returning an error, make sure
+ // the state is sane.
+ if (EFI_ERROR (Status) && Buffer != NULL) {
+ FreePool (Buffer);
+ Buffer = NULL;
+ }
+
+ *SaveData = Buffer;
+ return Status;
+}
diff --git a/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.inf b/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.inf
new file mode 100644
index 0000000000..c518c4e5ce
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.inf
@@ -0,0 +1,47 @@
+## @file
+# UEFI Simple File System based version of the Unit Test Persistence Lib
+#
+# Instance of the Unit Test Persistence Lib that utilizes the UEFI filesystem
+# that a test application is running from to save a serialized version of the
+# internal test state in case the test needs to quit and restore.
+#
+# Copyright (c) Microsoft Corporation.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x00010017
+ BASE_NAME = UnitTestPersistenceLibSimpleFileSystem
+ MODULE_UNI_FILE = UnitTestPersistenceLibSimpleFileSystem.uni
+ FILE_GUID = 9200844A-CDFD-4368-B4BD-106354702605
+ VERSION_STRING = 1.0
+ MODULE_TYPE = UEFI_APPLICATION
+ LIBRARY_CLASS = UnitTestPersistenceLib
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Sources]
+ UnitTestPersistenceLibSimpleFileSystem.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec
+ ShellPkg/ShellPkg.dec
+
+[LibraryClasses]
+ DebugLib
+ UefiBootServicesTableLib
+ BaseLib
+ ShellLib
+
+[Protocols]
+ gEfiLoadedImageProtocolGuid
+ gEfiSimpleFileSystemProtocolGuid
+
+[Guids]
+ gEfiFileInfoGuid
+ gEfiFileSystemInfoGuid
diff --git a/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.uni b/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.uni
new file mode 100644
index 0000000000..e6593be137
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.uni
@@ -0,0 +1,15 @@
+// /** @file
+// UEFI Simple File System based version of the Unit Test Persistence Lib
+//
+// Instance of the Unit Test Persistence Lib that utilizes the UEFI filesystem
+// that a test application is running from to save a serialized version of the
+// internal test state in case the test needs to quit and restore.
+//
+// Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT #language en-US "UEFI Simple File System based version of the Unit Test Persistence Lib"
+
+#string STR_MODULE_DESCRIPTION #language en-US "UEFI Simple File System based version of the Unit Test Persistence Lib."
diff --git a/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLib.c b/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLib.c
new file mode 100644
index 0000000000..687a04f55d
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLib.c
@@ -0,0 +1,216 @@
+/** @file
+ Implement UnitTestResultReportLib doing plain txt out to console
+
+ Copyright (c) Microsoft Corporation.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+#include <Library/UnitTestResultReportLib.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+
+VOID
+ReportPrint (
+ IN CONST CHAR8 *Format,
+ ...
+ );
+
+VOID
+ReportOutput (
+ IN CONST CHAR8 *Output
+ );
+
+struct _UNIT_TEST_STATUS_STRING {
+ UNIT_TEST_STATUS Status;
+ CHAR8 *String;
+};
+
+struct _UNIT_TEST_FAILURE_TYPE_STRING {
+ FAILURE_TYPE Type;
+ CHAR8 *String;
+};
+
+struct _UNIT_TEST_STATUS_STRING mStatusStrings[] = {
+ { UNIT_TEST_PASSED, "PASSED"},
+ { UNIT_TEST_ERROR_PREREQUISITE_NOT_MET, "NOT RUN - PREREQUISITE FAILED"},
+ { UNIT_TEST_ERROR_TEST_FAILED, "FAILED"},
+ { UNIT_TEST_RUNNING, "RUNNING"},
+ { UNIT_TEST_PENDING, "PENDING"},
+ { 0, "**UNKNOWN**"}
+};
+
+struct _UNIT_TEST_FAILURE_TYPE_STRING mFailureTypeStrings[] = {
+ { FAILURETYPE_NOFAILURE, "NO FAILURE"},
+ { FAILURETYPE_OTHER, "OTHER FAILURE"},
+ { FAILURETYPE_ASSERTTRUE, "ASSERT_TRUE FAILURE"},
+ { FAILURETYPE_ASSERTFALSE, "ASSERT_FALSE FAILURE"},
+ { FAILURETYPE_ASSERTEQUAL, "ASSERT_EQUAL FAILURE"},
+ { FAILURETYPE_ASSERTNOTEQUAL, "ASSERT_NOTEQUAL FAILURE"},
+ { FAILURETYPE_ASSERTNOTEFIERROR, "ASSERT_NOTEFIERROR FAILURE"},
+ { FAILURETYPE_ASSERTSTATUSEQUAL, "ASSERT_STATUSEQUAL FAILURE"},
+ { FAILURETYPE_ASSERTNOTNULL , "ASSERT_NOTNULL FAILURE"},
+ { 0, "*UNKNOWN* Failure"}
+};
+
+//
+// TEST REPORTING FUNCTIONS
+//
+
+STATIC
+CONST CHAR8*
+GetStringForUnitTestStatus (
+ IN UNIT_TEST_STATUS Status
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Index < ARRAY_SIZE (mStatusStrings); Index++) {
+ if (mStatusStrings[Index].Status == Status) {
+ //
+ // Return string from matching entry
+ //
+ return mStatusStrings[Index].String;
+ }
+ }
+ //
+ // Return last entry if no match found.
+ //
+ return mStatusStrings[Index].String;
+}
+
+STATIC
+CONST CHAR8*
+GetStringForFailureType (
+ IN FAILURE_TYPE Failure
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Index < ARRAY_SIZE (mFailureTypeStrings); Index++) {
+ if (mFailureTypeStrings[Index].Type == Failure) {
+ //
+ // Return string from matching entry
+ //
+ return mFailureTypeStrings[Index].String;
+ }
+ }
+ //
+ // Return last entry if no match found.
+ //
+ DEBUG((DEBUG_INFO, "%a Failure Type does not have string defined 0x%X\n", __FUNCTION__, (UINT32)Failure));
+ return mFailureTypeStrings[Index].String;
+}
+
+/*
+ Method to print the Unit Test run results
+
+ @retval Success
+*/
+EFI_STATUS
+EFIAPI
+OutputUnitTestFrameworkReport (
+ IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle
+ )
+{
+ UNIT_TEST_FRAMEWORK *Framework;
+ INTN Passed;
+ INTN Failed;
+ INTN NotRun;
+ UNIT_TEST_SUITE_LIST_ENTRY *Suite;
+ UNIT_TEST_LIST_ENTRY *Test;
+ INTN SPassed;
+ INTN SFailed;
+ INTN SNotRun;
+
+ Passed = 0;
+ Failed = 0;
+ NotRun = 0;
+ Suite = NULL;
+
+ Framework = (UNIT_TEST_FRAMEWORK *)FrameworkHandle;
+ if (Framework == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ReportPrint ("---------------------------------------------------------\n");
+ ReportPrint ("------------- UNIT TEST FRAMEWORK RESULTS ---------------\n");
+ ReportPrint ("---------------------------------------------------------\n");
+
+ //print the version and time
+
+ //
+ // Iterate all suites
+ //
+ for (Suite = (UNIT_TEST_SUITE_LIST_ENTRY*)GetFirstNode(&Framework->TestSuiteList);
+ (LIST_ENTRY*)Suite != &Framework->TestSuiteList;
+ Suite = (UNIT_TEST_SUITE_LIST_ENTRY*)GetNextNode(&Framework->TestSuiteList, (LIST_ENTRY*)Suite)) {
+
+ Test = NULL;
+ SPassed = 0;
+ SFailed = 0;
+ SNotRun = 0;
+
+ ReportPrint ("/////////////////////////////////////////////////////////\n");
+ ReportPrint (" SUITE: %a\n", Suite->UTS.Title);
+ ReportPrint (" PACKAGE: %a\n", Suite->UTS.Name);
+ ReportPrint ("/////////////////////////////////////////////////////////\n");
+
+ //
+ // Iterate all tests within the suite
+ //
+ for (Test = (UNIT_TEST_LIST_ENTRY*)GetFirstNode(&(Suite->UTS.TestCaseList));
+ (LIST_ENTRY*)Test != &(Suite->UTS.TestCaseList);
+ Test = (UNIT_TEST_LIST_ENTRY*)GetNextNode(&(Suite->UTS.TestCaseList), (LIST_ENTRY*)Test)) {
+
+ ReportPrint ("*********************************************************\n");
+ ReportPrint (" CLASS NAME: %a\n", Test->UT.Name);
+ ReportPrint (" TEST: %a\n", Test->UT.Description);
+ ReportPrint (" STATUS: %a\n", GetStringForUnitTestStatus (Test->UT.Result));
+ ReportPrint (" FAILURE: %a\n", GetStringForFailureType (Test->UT.FailureType));
+ ReportPrint (" FAILURE MESSAGE:\n%a\n", Test->UT.FailureMessage);
+
+ if (Test->UT.Log != NULL) {
+ ReportPrint (" LOG:\n");
+ ReportOutput (Test->UT.Log);
+ }
+
+ switch (Test->UT.Result) {
+ case UNIT_TEST_PASSED:
+ SPassed++;
+ break;
+ case UNIT_TEST_ERROR_TEST_FAILED:
+ SFailed++;
+ break;
+ case UNIT_TEST_PENDING: // Fall through...
+ case UNIT_TEST_RUNNING: // Fall through...
+ case UNIT_TEST_ERROR_PREREQUISITE_NOT_MET:
+ SNotRun++;
+ break;
+ default:
+ break;
+ }
+ ReportPrint ("**********************************************************\n");
+ } //End Test iteration
+
+ ReportPrint ("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
+ ReportPrint ("Suite Stats\n");
+ ReportPrint (" Passed: %d (%d%%)\n", SPassed, (SPassed * 100)/(SPassed+SFailed+SNotRun));
+ ReportPrint (" Failed: %d (%d%%)\n", SFailed, (SFailed * 100) / (SPassed + SFailed + SNotRun));
+ ReportPrint (" Not Run: %d (%d%%)\n", SNotRun, (SNotRun * 100) / (SPassed + SFailed + SNotRun));
+ ReportPrint ("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n" );
+
+ Passed += SPassed; //add to global counters
+ Failed += SFailed; //add to global counters
+ NotRun += SNotRun; //add to global counters
+ }//End Suite iteration
+
+ ReportPrint ("=========================================================\n");
+ ReportPrint ("Total Stats\n");
+ ReportPrint (" Passed: %d (%d%%)\n", Passed, (Passed * 100) / (Passed + Failed + NotRun));
+ ReportPrint (" Failed: %d (%d%%)\n", Failed, (Failed * 100) / (Passed + Failed + NotRun));
+ ReportPrint (" Not Run: %d (%d%%)\n", NotRun, (NotRun * 100) / (Passed + Failed + NotRun));
+ ReportPrint ("=========================================================\n" );
+
+ return EFI_SUCCESS;
+}
diff --git a/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.c b/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.c
new file mode 100644
index 0000000000..139360ee16
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.c
@@ -0,0 +1,48 @@
+/** @file
+ Implement UnitTestResultReportLib doing plain txt out to console
+
+ Copyright (c) Microsoft Corporation.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+#include <Library/BaseLib.h>
+#include <Library/PrintLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/DebugLib.h>
+
+VOID
+ReportPrint (
+ IN CONST CHAR8 *Format,
+ ...
+ )
+{
+ VA_LIST Marker;
+ CHAR16 String[256];
+ UINTN Length;
+
+ VA_START (Marker, Format);
+ Length = UnicodeVSPrintAsciiFormat (String, sizeof (String), Format, Marker);
+ if (Length == 0) {
+ DEBUG ((DEBUG_ERROR, "%a formatted string is too long\n", __FUNCTION__));
+ } else {
+ gST->ConOut->OutputString (gST->ConOut, String);
+ }
+ VA_END (Marker);
+}
+
+VOID
+ReportOutput (
+ IN CONST CHAR8 *Output
+ )
+{
+ CHAR8 AsciiString[128];
+ UINTN Length;
+ UINTN Index;
+
+ Length = AsciiStrLen (Output);
+ for (Index = 0; Index < Length; Index += (sizeof (AsciiString) - 1)) {
+ AsciiStrCpyS (AsciiString, sizeof (AsciiString), &Output[Index]);
+ ReportPrint ("%a", AsciiString);
+ }
+}
diff --git a/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.inf b/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.inf
new file mode 100644
index 0000000000..4382199fbc
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.inf
@@ -0,0 +1,29 @@
+## @file
+# Library to support printing out the unit test report to a UEFI console
+#
+# Copyright (c) Microsoft Corporation.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x00010017
+ BASE_NAME = UnitTestResultReportLibConOut
+ MODULE_UNI_FILE = UnitTestResultReportLibConOut.uni
+ FILE_GUID = C659641D-BA1F-4B58-946E-B1E1103903F9
+ VERSION_STRING = 1.0
+ MODULE_TYPE = UEFI_DRIVER
+ LIBRARY_CLASS = UnitTestResultReportLib
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ UefiBootServicesTableLib
+ PrintLib
+
+[Packages]
+ MdePkg/MdePkg.dec
+ UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec
+
+[Sources]
+ UnitTestResultReportLib.c
+ UnitTestResultReportLibConOut.c
diff --git a/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.uni b/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.uni
new file mode 100644
index 0000000000..92ba1b84da
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.uni
@@ -0,0 +1,11 @@
+// /** @file
+// Library to support printing out the unit test report to a UEFI console
+//
+// Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT #language en-US "Library to support printing out the unit test report to a UEFI console"
+
+#string STR_MODULE_DESCRIPTION #language en-US "Library to support printing out the unit test report to a UEFI console."
diff --git a/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.c b/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.c
new file mode 100644
index 0000000000..743aad2958
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.c
@@ -0,0 +1,47 @@
+/** @file
+ Implement UnitTestResultReportLib doing plain txt out to console
+
+ Copyright (c) Microsoft Corporation.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+#include <Library/BaseLib.h>
+#include <Library/PrintLib.h>
+#include <Library/DebugLib.h>
+
+VOID
+ReportPrint (
+ IN CONST CHAR8 *Format,
+ ...
+ )
+{
+ VA_LIST Marker;
+ CHAR8 String[256];
+ UINTN Length;
+
+ VA_START (Marker, Format);
+ Length = AsciiVSPrint (String, sizeof (String), Format, Marker);
+ if (Length == 0) {
+ DEBUG ((DEBUG_ERROR, "%a formatted string is too long\n", __FUNCTION__));
+ } else {
+ DEBUG ((DEBUG_INFO, String));
+ }
+ VA_END (Marker);
+}
+
+VOID
+ReportOutput (
+ IN CONST CHAR8 *Output
+ )
+{
+ CHAR8 AsciiString[128];
+ UINTN Length;
+ UINTN Index;
+
+ Length = AsciiStrLen (Output);
+ for (Index = 0; Index < Length; Index += (sizeof (AsciiString) - 1)) {
+ AsciiStrCpyS (AsciiString, sizeof (AsciiString), &Output[Index]);
+ DEBUG ((DEBUG_INFO, AsciiString));
+ }
+}
diff --git a/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.inf b/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.inf
new file mode 100644
index 0000000000..a1c786a700
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.inf
@@ -0,0 +1,28 @@
+## @file
+# Library to support printing out the unit test report using DEBUG() macros.
+#
+# Copyright (c) Microsoft Corporation.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x00010017
+ BASE_NAME = UnitTestResultReportLibDebugLib
+ MODULE_UNI_FILE = UnitTestResultReportLibDebugLib.uni
+ FILE_GUID = BED736D4-D197-475F-B7CE-0D828FF2C9A6
+ VERSION_STRING = 1.0
+ MODULE_TYPE = UEFI_DRIVER
+ LIBRARY_CLASS = UnitTestResultReportLib
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ PrintLib
+
+[Packages]
+ MdePkg/MdePkg.dec
+ UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec
+
+[Sources]
+ UnitTestResultReportLib.c
+ UnitTestResultReportLibDebugLib.c
diff --git a/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.uni b/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.uni
new file mode 100644
index 0000000000..4f1993417a
--- /dev/null
+++ b/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.uni
@@ -0,0 +1,11 @@
+// /** @file
+// Library to support printing out the unit test report using DEBUG() macros.
+//
+// Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT #language en-US "Library to support printing out the unit test report using DEBUG() macros"
+
+#string STR_MODULE_DESCRIPTION #language en-US "Library to support printing out the unit test report using DEBUG() macros."
--
2.21.0.windows.1
next prev parent reply other threads:[~2020-01-24 2:10 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-01-24 2:10 [Patch 00/11] Add Unit Test Framework Michael D Kinney
2020-01-24 2:10 ` [Patch 01/11] .pytool: Add CI support for host based unit tests with results Michael D Kinney
2020-01-27 23:28 ` [edk2-devel] " brbarkel
2020-01-24 2:10 ` [Patch 02/11] BaseTools/Plugin: Add HostBasedUnitTestRunner plugin Michael D Kinney
2020-01-27 23:29 ` [edk2-devel] " brbarkel
2020-02-07 2:32 ` Bob Feng
2020-01-24 2:10 ` [Patch 03/11] MdePkg/Include/Library: Add UnitTestLib class Michael D Kinney
2020-01-27 23:42 ` [edk2-devel] " brbarkel
2020-02-07 0:49 ` Michael D Kinney
2020-02-07 1:22 ` Wu, Hao A
2020-02-07 5:43 ` Bret Barkelew
2020-01-24 2:10 ` [Patch 04/11] UnitTestFrameworkPkg: Add public and private interfaces Michael D Kinney
2020-01-27 23:42 ` [edk2-devel] " brbarkel
2020-01-24 2:10 ` Michael D Kinney [this message]
2020-01-27 23:43 ` [edk2-devel] [Patch 05/11] UnitTestFrameworkPkg/Library: Add library instances brbarkel
2020-01-24 2:10 ` [Patch 06/11] UnitTestFrameworkPkg/Test: Add unit test samples Michael D Kinney
2020-01-27 23:43 ` [edk2-devel] " brbarkel
2020-01-24 2:10 ` [Patch 07/11] UnitTestFrameworkPkg: Add DSC, DSC INC, and YAML files Michael D Kinney
2020-01-27 23:43 ` [edk2-devel] " brbarkel
2020-01-24 2:10 ` [Patch 08/11] MdePkg/Test: Add SafeIntLib and BaseLib Base64 unit tests Michael D Kinney
2020-01-27 23:43 ` [edk2-devel] " brbarkel
2020-02-07 1:27 ` Wu, Hao A
2020-02-07 7:56 ` Liming Gao
2020-02-07 16:05 ` Michael D Kinney
2020-01-24 2:10 ` [Patch 09/11] MdeModulePkg: Add DxeResetSystemLib unit test Michael D Kinney
2020-01-27 23:43 ` [edk2-devel] " brbarkel
2020-02-07 1:25 ` Wu, Hao A
2020-01-24 2:10 ` [Patch 10/11] .azurepipelines: Enable CI for UnitTestFrameworkPkg and host tests Michael D Kinney
2020-01-27 23:44 ` [edk2-devel] " brbarkel
2020-01-24 2:10 ` [Patch 11/11] Maintainers.txt: Add UnitTestFrameworkPkg Michael D Kinney
2020-01-24 10:22 ` Laszlo Ersek
2020-01-27 23:44 ` [edk2-devel] " brbarkel
2020-01-27 23:28 ` [edk2-devel] [Patch 00/11] Add Unit Test Framework brbarkel
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20200124021032.13808-6-michael.d.kinney@intel.com \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox