]> git.proxmox.com Git - mirror_edk2.git/commitdiff
UnitTestFrameworkPkg/Library: Add library instances
authorMichael D Kinney <michael.d.kinney@intel.com>
Wed, 22 Jan 2020 18:07:17 +0000 (10:07 -0800)
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Fri, 7 Feb 2020 19:18:53 +0000 (19:18 +0000)
https://bugzilla.tianocore.org/show_bug.cgi?id=2505

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>
Reviewed-by: Bret Barkelew <Bret.Barkelew@microsoft.com>
39 files changed:
.gitmodules
UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.inf [new file with mode: 0644]
UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.uni [new file with mode: 0644]
UnitTestFrameworkPkg/Library/CmockaLib/cmocka [new submodule]
UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.c [new file with mode: 0644]
UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.inf [new file with mode: 0644]
UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.uni [new file with mode: 0644]
UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.c [new file with mode: 0644]
UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.inf [new file with mode: 0644]
UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.uni [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootLibNull.c [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootLibNull.inf [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootLibNull.uni [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibUsbClass.c [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibUsbClass.inf [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibUsbClass.uni [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestLib/Assert.c [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestLib/AssertCmocka.c [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestLib/Log.c [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestLib/RunTests.c [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestLib/RunTestsCmocka.c [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.c [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.inf [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.uni [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLibCmocka.inf [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLibCmocka.uni [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.c [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.inf [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.uni [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.c [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.inf [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.uni [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLib.c [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.c [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.inf [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.uni [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.c [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.inf [new file with mode: 0644]
UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.uni [new file with mode: 0644]

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