public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [Patch v2 00/11] Add Unit Test Framework
@ 2020-02-07 18:13 Michael D Kinney
  2020-02-07 18:13 ` [Patch v2 01/11] .pytool: Add CI support for host based unit tests with results Michael D Kinney
                   ` (10 more replies)
  0 siblings, 11 replies; 13+ messages in thread
From: Michael D Kinney @ 2020-02-07 18:13 UTC (permalink / raw)
  To: devel; +Cc: Sean Brogan, Bret Barkelew, Liming Gao, Bob Feng, Hao A Wu

New in V2
==========
* Fix spelling error in comments
* [Patch v2 07/11] Add -no-pie to HOST_APPLICATION GCC:*_*_IA32_DLINK_FLAGS
* [Patch v2 07/11] Add -no-pie to HOST_APPLICATION GCC:*_*_X64_DLINK_FLAGS

Branch for review:
  https://github.com/mdkinney/edk2/tree/edk2-host-test_v2_mdk_updates_v4

GitHub Pull Request for review (Personal Build):
  https://github.com/tianocore/edk2/pull/315

Add a Unit Test Framework to the edk2 repository that supports development
of unit tests for interfaces, libraries, and modules.  The unit tests support
being run on target platforms or in a host environment.  Unit tests that are
built for the host environment can be registered to be run as part of the 
EDK II CI pre-commit and post-commit proccess, to prevent regressions during
maintenance of components that provide unit test coverage.

The Unit Test Framework depends on the cmocka submodule that extends the unit
test environment to support mocking interfaces that the API under test may use
and also provide unit tests results in standard XML JUINT format.

  https://git.cryptomilk.org/projects/cmocka.git
  https://api.cmocka.org/
  https://api.cmocka.org/modules.html

Unit tests use the services of the UnitTestLib defined in the MdePkg.  Unit
tests may optionally use cmocka services.  The UnitTestLib provides the
following features.

* Create a Unit Test Framework
* Add a Unit Test Suite to a Unit Test Framework
  + Support optional step that executes before a Unit
    Test Suite is started.
  + Support optional step that executes after a Unit
    Test Suite is finished.
* Add a Unit Test to a Unit Test Suite
  + Support optional step that executes before a Unit
    Test is started.
  + Support optional step that executes after a Unit
    Test is finished.
* Run all unit tests added to a Unit Test Framework
* Save Unit Test Framework state to persistent storage
* Support assertion checks in a unit test for TRUE, FALSE,
  EQUAL, MEM_EQUAL, NOT_EFI_ERROR, STATUS_EQUAL, and NOT_NULL.
* Support generation of log messages at ERROR, WARN, INFO,
  and VERBOSE levels.

Example unit tests for both host environments and target environments can be 
found in the following locations:

* MdePkg/Test/UnitTest/Library/BaseSafeIntLib
* MdePkg/Test/UnitTest/Library/BaseLib
* MdeModulePkg/Library/DxeResetSystemLib/UnitTest
* UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest

Cc: Sean Brogan <sean.brogan@microsoft.com>
Cc: Bret Barkelew <Bret.Barkelew@microsoft.com>
Cc: Liming Gao <liming.gao@intel.com>
Cc: Bob Feng <bob.c.feng@intel.com>
Cc: Hao A Wu <hao.a.wu@intel.com>
Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com>

Bret Barkelew (1):
  MdePkg/Include/Library: Add UnitTestLib class

Michael D Kinney (10):
  .pytool: Add CI support for host based unit tests with results
  BaseTools/Plugin: Add HostBasedUnitTestRunner plugin
  UnitTestFrameworkPkg: Add public and private interfaces
  UnitTestFrameworkPkg/Library: Add library instances
  UnitTestFrameworkPkg/Test: Add unit test samples
  UnitTestFrameworkPkg: Add DSC, DSC INC, and YAML files
  MdePkg/Test: Add SafeIntLib and BaseLib Base64 unit tests
  MdeModulePkg: Add DxeResetSystemLib unit test
  .azurepipelines: Enable CI for UnitTestFrameworkPkg and host tests
  Maintainers.txt: Add UnitTestFrameworkPkg

 .../templates/pr-gate-build-job.yml           |   10 +-
 .gitmodules                                   |    3 +
 .pytool/CISettings.py                         |   22 +-
 .../CharEncodingCheck/CharEncodingCheck.py    |    2 +-
 .../Plugin/CompilerPlugin/CompilerPlugin.py   |    4 +-
 .../Plugin/DependencyCheck/DependencyCheck.py |    2 +-
 .../DscCompleteCheck/DscCompleteCheck.py      |   35 +-
 .pytool/Plugin/DscCompleteCheck/Readme.md     |    7 +-
 .pytool/Plugin/GuidCheck/GuidCheck.py         |    2 +-
 .../HostUnitTestCompilerPlugin.py}            |   71 +-
 .../HostUnitTestCompiler_plug_in.yaml         |   12 +
 .../HostUnitTestCompilerPlugin/Readme.md      |   24 +
 .../HostUnitTestDscCompleteCheck.py}          |   58 +-
 .../HostUnitTestDscCompleteCheck_plug_in.yaml |   12 +
 .../HostUnitTestDscCompleteCheck/Readme.md    |   32 +
 .../LibraryClassCheck/LibraryClassCheck.py    |    2 +-
 .../HostBasedUnitTestRunner.py                |  115 +
 .../HostBasedUnitTestRunner_plug_in.yaml      |   12 +
 Maintainers.txt                               |    7 +
 .../UnitTest/DxeResetSystemLibUnitTest.c      |  312 ++
 .../DxeResetSystemLibUnitTestHost.inf         |   34 +
 .../MockUefiRuntimeServicesTableLib.c         |   13 +
 .../MockUefiRuntimeServicesTableLib.inf       |   25 +
 MdeModulePkg/MdeModulePkg.ci.yaml             |   13 +-
 MdeModulePkg/Test/MdeModulePkgHostTest.dsc    |   32 +
 MdePkg/Include/Library/UnitTestLib.h          |  757 ++++
 MdePkg/MdePkg.ci.yaml                         |   19 +-
 MdePkg/MdePkg.dec                             |    4 +
 MdePkg/MdePkg.dsc                             |   18 +
 MdePkg/Test/MdePkgHostTest.dsc                |   30 +
 .../UnitTest/Library/BaseLib/Base64UnitTest.c |  404 +++
 .../Library/BaseLib/BaseLibUnitTestsHost.inf  |   32 +
 .../Library/BaseLib/BaseLibUnitTestsUefi.inf  |   33 +
 .../SafeIntLibUintnIntnUnitTests32.c          |  540 +++
 .../SafeIntLibUintnIntnUnitTests64.c          |  544 +++
 .../BaseSafeIntLib/TestBaseSafeIntLib.c       | 3064 +++++++++++++++++
 .../BaseSafeIntLib/TestBaseSafeIntLib.h       |  123 +
 .../BaseSafeIntLib/TestBaseSafeIntLib.uni     |   13 +
 .../BaseSafeIntLib/TestBaseSafeIntLibDxe.inf  |   45 +
 .../BaseSafeIntLib/TestBaseSafeIntLibHost.inf |   40 +
 .../BaseSafeIntLib/TestBaseSafeIntLibPei.inf  |   45 +
 .../BaseSafeIntLib/TestBaseSafeIntLibSmm.inf  |   45 +
 .../TestBaseSafeIntLibUefiShell.inf           |   42 +
 .../Library/CmockaLib/CmockaLib.inf           |   35 +
 .../Library/CmockaLib/CmockaLib.uni           |   14 +
 UnitTestFrameworkPkg/Library/CmockaLib/cmocka |    1 +
 .../Posix/DebugLibPosix/DebugLibPosix.c       |  279 ++
 .../Posix/DebugLibPosix/DebugLibPosix.inf     |   35 +
 .../Posix/DebugLibPosix/DebugLibPosix.uni     |   14 +
 .../MemoryAllocationLibPosix.c                |  631 ++++
 .../MemoryAllocationLibPosix.inf              |   27 +
 .../MemoryAllocationLibPosix.uni              |   14 +
 .../UnitTestBootLibNull/UnitTestBootLibNull.c |   26 +
 .../UnitTestBootLibNull.inf                   |   23 +
 .../UnitTestBootLibNull.uni                   |   11 +
 .../UnitTestBootLibUsbClass.c                 |  127 +
 .../UnitTestBootLibUsbClass.inf               |   34 +
 .../UnitTestBootLibUsbClass.uni               |   12 +
 .../Library/UnitTestLib/Assert.c              |  491 +++
 .../Library/UnitTestLib/AssertCmocka.c        |  335 ++
 .../Library/UnitTestLib/Log.c                 |  200 ++
 .../Library/UnitTestLib/RunTests.c            |  171 +
 .../Library/UnitTestLib/RunTestsCmocka.c      |  278 ++
 .../Library/UnitTestLib/UnitTestLib.c         |  853 +++++
 .../Library/UnitTestLib/UnitTestLib.inf       |   37 +
 .../Library/UnitTestLib/UnitTestLib.uni       |   11 +
 .../Library/UnitTestLib/UnitTestLibCmocka.inf |   38 +
 .../Library/UnitTestLib/UnitTestLibCmocka.uni |   11 +
 .../UnitTestPersistenceLibNull.c              |   75 +
 .../UnitTestPersistenceLibNull.inf            |   28 +
 .../UnitTestPersistenceLibNull.uni            |   11 +
 .../UnitTestPersistenceLibSimpleFileSystem.c  |  416 +++
 ...UnitTestPersistenceLibSimpleFileSystem.inf |   47 +
 ...UnitTestPersistenceLibSimpleFileSystem.uni |   15 +
 .../UnitTestResultReportLib.c                 |  216 ++
 .../UnitTestResultReportLibConOut.c           |   48 +
 .../UnitTestResultReportLibConOut.inf         |   29 +
 .../UnitTestResultReportLibConOut.uni         |   11 +
 .../UnitTestResultReportLibDebugLib.c         |   47 +
 .../UnitTestResultReportLibDebugLib.inf       |   28 +
 .../UnitTestResultReportLibDebugLib.uni       |   11 +
 .../PrivateInclude/Library/UnitTestBootLib.h  |   31 +
 .../Library/UnitTestPersistenceLib.h          |   76 +
 .../Library/UnitTestResultReportLib.h         |   27 +
 .../PrivateInclude/UnitTestFrameworkTypes.h   |  183 +
 UnitTestFrameworkPkg/ReadMe.md                |  257 ++
 .../Sample/SampleUnitTest/SampleUnitTest.c    |  289 ++
 .../SampleUnitTest/SampleUnitTestDxe.inf      |   36 +
 .../SampleUnitTest/SampleUnitTestHost.inf     |   30 +
 .../SampleUnitTest/SampleUnitTestPei.inf      |   36 +
 .../SampleUnitTest/SampleUnitTestSmm.inf      |   37 +
 .../SampleUnitTestUefiShell.inf               |   33 +
 .../Test/UnitTestFrameworkPkgHostTest.dsc     |   33 +
 .../UnitTestFrameworkPkg.ci.yaml              |   76 +
 UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec |   50 +
 UnitTestFrameworkPkg/UnitTestFrameworkPkg.dsc |   34 +
 UnitTestFrameworkPkg/UnitTestFrameworkPkg.uni |   21 +
 .../UnitTestFrameworkPkgHost.dsc.inc          |   56 +
 .../UnitTestFrameworkPkgTarget.dsc.inc        |   58 +
 99 files changed, 12649 insertions(+), 63 deletions(-)
 copy .pytool/Plugin/{CompilerPlugin/CompilerPlugin.py => HostUnitTestCompilerPlugin/HostUnitTestCompilerPlugin.py} (54%)
 create mode 100644 .pytool/Plugin/HostUnitTestCompilerPlugin/HostUnitTestCompiler_plug_in.yaml
 create mode 100644 .pytool/Plugin/HostUnitTestCompilerPlugin/Readme.md
 copy .pytool/Plugin/{DscCompleteCheck/DscCompleteCheck.py => HostUnitTestDscCompleteCheck/HostUnitTestDscCompleteCheck.py} (62%)
 create mode 100644 .pytool/Plugin/HostUnitTestDscCompleteCheck/HostUnitTestDscCompleteCheck_plug_in.yaml
 create mode 100644 .pytool/Plugin/HostUnitTestDscCompleteCheck/Readme.md
 create mode 100644 BaseTools/Plugin/HostBasedUnitTestRunner/HostBasedUnitTestRunner.py
 create mode 100644 BaseTools/Plugin/HostBasedUnitTestRunner/HostBasedUnitTestRunner_plug_in.yaml
 create mode 100644 MdeModulePkg/Library/DxeResetSystemLib/UnitTest/DxeResetSystemLibUnitTest.c
 create mode 100644 MdeModulePkg/Library/DxeResetSystemLib/UnitTest/DxeResetSystemLibUnitTestHost.inf
 create mode 100644 MdeModulePkg/Library/DxeResetSystemLib/UnitTest/MockUefiRuntimeServicesTableLib.c
 create mode 100644 MdeModulePkg/Library/DxeResetSystemLib/UnitTest/MockUefiRuntimeServicesTableLib.inf
 create mode 100644 MdeModulePkg/Test/MdeModulePkgHostTest.dsc
 create mode 100644 MdePkg/Include/Library/UnitTestLib.h
 create mode 100644 MdePkg/Test/MdePkgHostTest.dsc
 create mode 100644 MdePkg/Test/UnitTest/Library/BaseLib/Base64UnitTest.c
 create mode 100644 MdePkg/Test/UnitTest/Library/BaseLib/BaseLibUnitTestsHost.inf
 create mode 100644 MdePkg/Test/UnitTest/Library/BaseLib/BaseLibUnitTestsUefi.inf
 create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/SafeIntLibUintnIntnUnitTests32.c
 create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/SafeIntLibUintnIntnUnitTests64.c
 create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLib.c
 create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLib.h
 create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLib.uni
 create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibDxe.inf
 create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibHost.inf
 create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibPei.inf
 create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibSmm.inf
 create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibUefiShell.inf
 create mode 100644 UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.inf
 create mode 100644 UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.uni
 create mode 160000 UnitTestFrameworkPkg/Library/CmockaLib/cmocka
 create mode 100644 UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.c
 create mode 100644 UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.inf
 create mode 100644 UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.uni
 create mode 100644 UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.c
 create mode 100644 UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.inf
 create mode 100644 UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.uni
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootLibNull.c
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootLibNull.inf
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootLibNull.uni
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibUsbClass.c
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibUsbClass.inf
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibUsbClass.uni
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/Assert.c
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/AssertCmocka.c
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/Log.c
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/RunTests.c
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/RunTestsCmocka.c
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.c
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.inf
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.uni
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLibCmocka.inf
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLibCmocka.uni
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.c
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.inf
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.uni
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.c
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.inf
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.uni
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLib.c
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.c
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.inf
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.uni
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.c
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.inf
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.uni
 create mode 100644 UnitTestFrameworkPkg/PrivateInclude/Library/UnitTestBootLib.h
 create mode 100644 UnitTestFrameworkPkg/PrivateInclude/Library/UnitTestPersistenceLib.h
 create mode 100644 UnitTestFrameworkPkg/PrivateInclude/Library/UnitTestResultReportLib.h
 create mode 100644 UnitTestFrameworkPkg/PrivateInclude/UnitTestFrameworkTypes.h
 create mode 100644 UnitTestFrameworkPkg/ReadMe.md
 create mode 100644 UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTest.c
 create mode 100644 UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestDxe.inf
 create mode 100644 UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestHost.inf
 create mode 100644 UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestPei.inf
 create mode 100644 UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestSmm.inf
 create mode 100644 UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestUefiShell.inf
 create mode 100644 UnitTestFrameworkPkg/Test/UnitTestFrameworkPkgHostTest.dsc
 create mode 100644 UnitTestFrameworkPkg/UnitTestFrameworkPkg.ci.yaml
 create mode 100644 UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec
 create mode 100644 UnitTestFrameworkPkg/UnitTestFrameworkPkg.dsc
 create mode 100644 UnitTestFrameworkPkg/UnitTestFrameworkPkg.uni
 create mode 100644 UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc
 create mode 100644 UnitTestFrameworkPkg/UnitTestFrameworkPkgTarget.dsc.inc

-- 
2.21.0.windows.1


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [Patch v2 01/11] .pytool: Add CI support for host based unit tests with results
  2020-02-07 18:13 [Patch v2 00/11] Add Unit Test Framework Michael D Kinney
@ 2020-02-07 18:13 ` Michael D Kinney
  2020-02-07 18:13 ` [Patch v2 02/11] BaseTools/Plugin: Add HostBasedUnitTestRunner plugin Michael D Kinney
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Michael D Kinney @ 2020-02-07 18:13 UTC (permalink / raw)
  To: devel; +Cc: Sean Brogan, Bret Barkelew, Liming Gao

https://bugzilla.tianocore.org/show_bug.cgi?id=2505

* Add plugin to build and run host based unit tests
* Add plugin that performs a DSC complete check DSC files
  used to build host based tests
* Update DscCompleteCheck plugin to ignore module INFs with
  a MODULE_TYPE of HOST_APPLICATION and library INFs that
  only support a module type of HOST_APPLICATION.
* Fix issues in XML reports from checkers.

Cc: Sean Brogan <sean.brogan@microsoft.com>
Cc: Bret Barkelew <Bret.Barkelew@microsoft.com>
Cc: Liming Gao <liming.gao@intel.com>
Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com>
Reviewed-by: Bret Barkelew <Bret.Barkelew@microsoft.com>
---
 .pytool/CISettings.py                         | 22 ++++--
 .../CharEncodingCheck/CharEncodingCheck.py    |  2 +-
 .../Plugin/CompilerPlugin/CompilerPlugin.py   |  4 +-
 .../Plugin/DependencyCheck/DependencyCheck.py |  2 +-
 .../DscCompleteCheck/DscCompleteCheck.py      | 35 ++++++---
 .pytool/Plugin/DscCompleteCheck/Readme.md     |  7 +-
 .pytool/Plugin/GuidCheck/GuidCheck.py         |  2 +-
 .../HostUnitTestCompilerPlugin.py}            | 71 +++++++++++++++----
 .../HostUnitTestCompiler_plug_in.yaml         | 12 ++++
 .../HostUnitTestCompilerPlugin/Readme.md      | 24 +++++++
 .../HostUnitTestDscCompleteCheck.py}          | 58 ++++++++++-----
 .../HostUnitTestDscCompleteCheck_plug_in.yaml | 12 ++++
 .../HostUnitTestDscCompleteCheck/Readme.md    | 32 +++++++++
 .../LibraryClassCheck/LibraryClassCheck.py    |  2 +-
 14 files changed, 231 insertions(+), 54 deletions(-)
 copy .pytool/Plugin/{CompilerPlugin/CompilerPlugin.py => HostUnitTestCompilerPlugin/HostUnitTestCompilerPlugin.py} (54%)
 create mode 100644 .pytool/Plugin/HostUnitTestCompilerPlugin/HostUnitTestCompiler_plug_in.yaml
 create mode 100644 .pytool/Plugin/HostUnitTestCompilerPlugin/Readme.md
 copy .pytool/Plugin/{DscCompleteCheck/DscCompleteCheck.py => HostUnitTestDscCompleteCheck/HostUnitTestDscCompleteCheck.py} (62%)
 create mode 100644 .pytool/Plugin/HostUnitTestDscCompleteCheck/HostUnitTestDscCompleteCheck_plug_in.yaml
 create mode 100644 .pytool/Plugin/HostUnitTestDscCompleteCheck/Readme.md

diff --git a/.pytool/CISettings.py b/.pytool/CISettings.py
index ce177937e1..79593d9dc5 100644
--- a/.pytool/CISettings.py
+++ b/.pytool/CISettings.py
@@ -48,7 +48,8 @@ class Settings(CiBuildSettingsManager, UpdateSettingsManager, SetupSettingsManag
                 "FmpDevicePkg",
                 "ShellPkg",
                 "FatPkg",
-                "CryptoPkg"
+                "CryptoPkg",
+                "UnitTestFrameworkPkg"
                 )
 
     def GetArchitecturesSupported(self):
@@ -117,10 +118,13 @@ class Settings(CiBuildSettingsManager, UpdateSettingsManager, SetupSettingsManag
 
     def GetActiveScopes(self):
         ''' return tuple containing scopes that should be active for this process '''
-        scopes = ("cibuild","edk2-build")
+        scopes = ("cibuild", "edk2-build", "host-based-test")
 
         self.ActualToolChainTag = shell_environment.GetBuildVars().GetValue("TOOL_CHAIN_TAG", "")
 
+        if GetHostInfo().os.upper() == "WINDOWS":
+            scopes += ('host-test-win',)
+
         if GetHostInfo().os.upper() == "LINUX" and self.ActualToolChainTag.upper().startswith("GCC"):
             if "AARCH64" in self.ActualArchitectures:
                 scopes += ("gcc_aarch64_linux",)
@@ -133,18 +137,21 @@ class Settings(CiBuildSettingsManager, UpdateSettingsManager, SetupSettingsManag
         ''' return iterable containing RequiredSubmodule objects.
         If no RequiredSubmodules return an empty iterable
         '''
-        rs=[]
+        rs = []
         rs.append(RequiredSubmodule(
             "ArmPkg/Library/ArmSoftFloatLib/berkeley-softfloat-3", False))
         rs.append(RequiredSubmodule(
             "CryptoPkg/Library/OpensslLib/openssl", False))
+        rs.append(RequiredSubmodule(
+            "UnitTestFrameworkPkg/Library/CmockaLib/cmocka", False))
         return rs
 
     def GetName(self):
         return "Edk2"
 
     def GetDependencies(self):
-        return []
+        return [
+        ]
 
     def GetPackagesPath(self):
         return ()
@@ -155,10 +162,11 @@ class Settings(CiBuildSettingsManager, UpdateSettingsManager, SetupSettingsManag
 
     def FilterPackagesToTest(self, changedFilesList: list, potentialPackagesList: list) -> list:
         ''' Filter potential packages to test based on changed files. '''
-        build_these_packages=[]
-        possible_packages=potentialPackagesList.copy()
+        build_these_packages = []
+        possible_packages = potentialPackagesList.copy()
         for f in changedFilesList:
-            nodes=f.split("/")  # split each part of path for comparison later
+            # split each part of path for comparison later
+            nodes = f.split("/")
 
             # python file change in .pytool folder causes building all
             if f.endswith(".py") and ".pytool" in nodes:
diff --git a/.pytool/Plugin/CharEncodingCheck/CharEncodingCheck.py b/.pytool/Plugin/CharEncodingCheck/CharEncodingCheck.py
index 02f25ab19f..1496e1f249 100644
--- a/.pytool/Plugin/CharEncodingCheck/CharEncodingCheck.py
+++ b/.pytool/Plugin/CharEncodingCheck/CharEncodingCheck.py
@@ -100,7 +100,7 @@ class CharEncodingCheck(ICiBuildPlugin):
                     overall_status += 1
 
         tc.LogStdOut("Tested Encoding on {0} files".format(files_tested))
-        if overall_status is not 0:
+        if overall_status != 0:
             tc.SetFailed("CharEncoding {0} Failed.  Errors {1}".format(packagename, overall_status), "CHAR_ENCODING_CHECK_FAILED")
         else:
             tc.SetSuccess()
diff --git a/.pytool/Plugin/CompilerPlugin/CompilerPlugin.py b/.pytool/Plugin/CompilerPlugin/CompilerPlugin.py
index 3b6f7c7698..e8657940d7 100644
--- a/.pytool/Plugin/CompilerPlugin/CompilerPlugin.py
+++ b/.pytool/Plugin/CompilerPlugin/CompilerPlugin.py
@@ -1,4 +1,4 @@
-# @file HostUnitTestCompiler_plugin.py
+# @file CompilerPlugin.py
 ##
 # Copyright (c) Microsoft Corporation.
 # SPDX-License-Identifier: BSD-2-Clause-Patent
@@ -42,7 +42,7 @@ class CompilerPlugin(ICiBuildPlugin):
         return ["DEBUG", "RELEASE"]
 
     ##
-    # External function of plugin.  This function is used to perform the task of the MuBuild Plugin
+    # External function of plugin.  This function is used to perform the task of the ICiBuildPlugin Plugin
     #
     #   - package is the edk2 path to package.  This means workspace/packagepath relative.
     #   - edk2path object configured with workspace and packages path
diff --git a/.pytool/Plugin/DependencyCheck/DependencyCheck.py b/.pytool/Plugin/DependencyCheck/DependencyCheck.py
index 2c3d8baf69..db154d769a 100644
--- a/.pytool/Plugin/DependencyCheck/DependencyCheck.py
+++ b/.pytool/Plugin/DependencyCheck/DependencyCheck.py
@@ -113,7 +113,7 @@ class DependencyCheck(ICiBuildPlugin):
                     overall_status += 1
 
         # If XML object exists, add results
-        if overall_status is not 0:
+        if overall_status != 0:
             tc.SetFailed("Failed with {0} errors".format(overall_status), "DEPENDENCYCHECK_FAILED")
         else:
             tc.SetSuccess()
diff --git a/.pytool/Plugin/DscCompleteCheck/DscCompleteCheck.py b/.pytool/Plugin/DscCompleteCheck/DscCompleteCheck.py
index 9af4f72c8d..c613cd5233 100644
--- a/.pytool/Plugin/DscCompleteCheck/DscCompleteCheck.py
+++ b/.pytool/Plugin/DscCompleteCheck/DscCompleteCheck.py
@@ -54,12 +54,15 @@ class DscCompleteCheck(ICiBuildPlugin):
         # Parse the config for required DscPath element
         if "DscPath" not in pkgconfig:
             tc.SetSkipped()
-            tc.LogStdError("DscPath not found in config file.  Nothing to check.")
+            tc.LogStdError(
+                "DscPath not found in config file.  Nothing to check.")
             return -1
 
-        abs_pkg_path = Edk2pathObj.GetAbsolutePathOnThisSytemFromEdk2RelativePath(packagename)
+        abs_pkg_path = Edk2pathObj.GetAbsolutePathOnThisSytemFromEdk2RelativePath(
+            packagename)
         abs_dsc_path = os.path.join(abs_pkg_path, pkgconfig["DscPath"].strip())
-        wsr_dsc_path = Edk2pathObj.GetEdk2RelativePathFromAbsolutePath(abs_dsc_path)
+        wsr_dsc_path = Edk2pathObj.GetEdk2RelativePathFromAbsolutePath(
+            abs_dsc_path)
 
         if abs_dsc_path is None or wsr_dsc_path == "" or not os.path.isfile(abs_dsc_path):
             tc.SetSkipped()
@@ -68,7 +71,8 @@ class DscCompleteCheck(ICiBuildPlugin):
 
         # Get INF Files
         INFFiles = self.WalkDirectoryForExtension([".inf"], abs_pkg_path)
-        INFFiles = [Edk2pathObj.GetEdk2RelativePathFromAbsolutePath(x) for x in INFFiles]  # make edk2relative path so can compare with DSC
+        INFFiles = [Edk2pathObj.GetEdk2RelativePathFromAbsolutePath(
+            x) for x in INFFiles]  # make edk2relative path so can compare with DSC
 
         # remove ignores
 
@@ -79,8 +83,10 @@ class DscCompleteCheck(ICiBuildPlugin):
                     tc.LogStdOut("Ignoring INF {0}".format(a))
                     INFFiles.remove(a)
                 except:
-                    tc.LogStdError("DscCompleteCheck.IgnoreInf -> {0} not found in filesystem.  Invalid ignore file".format(a))
-                    logging.info("DscCompleteCheck.IgnoreInf -> {0} not found in filesystem.  Invalid ignore file".format(a))
+                    tc.LogStdError(
+                        "DscCompleteCheck.IgnoreInf -> {0} not found in filesystem.  Invalid ignore file".format(a))
+                    logging.info(
+                        "DscCompleteCheck.IgnoreInf -> {0} not found in filesystem.  Invalid ignore file".format(a))
 
         # DSC Parser
         dp = DscParser()
@@ -99,11 +105,19 @@ class DscCompleteCheck(ICiBuildPlugin):
                 infp.SetPackagePaths(Edk2pathObj.PackagePathList)
                 infp.ParseFile(INF)
                 if("MODULE_TYPE" not in infp.Dict):
-                    tc.LogStdOut("Ignoring INF. Missing key for MODULE_TYPE {0}".format(INF))
+                    tc.LogStdOut(
+                        "Ignoring INF. Missing key for MODULE_TYPE {0}".format(INF))
                     continue
 
                 if(infp.Dict["MODULE_TYPE"] == "HOST_APPLICATION"):
-                    tc.LogStdOut("Ignoring INF.  Module type is HOST_APPLICATION {0}".format(INF))
+                    tc.LogStdOut(
+                        "Ignoring INF.  Module type is HOST_APPLICATION {0}".format(INF))
+                    continue
+
+                if len(infp.SupportedPhases) == 1 and \
+                   "HOST_APPLICATION" in infp.SupportedPhases:
+                    tc.LogStdOut(
+                        "Ignoring Library INF due to only supporting type HOST_APPLICATION {0}".format(INF))
                     continue
 
                 logging.critical(INF + " not in " + wsr_dsc_path)
@@ -111,8 +125,9 @@ class DscCompleteCheck(ICiBuildPlugin):
                 overall_status = overall_status + 1
 
         # If XML object exists, add result
-        if overall_status is not 0:
-            tc.SetFailed("DscCompleteCheck {0} Failed.  Errors {1}".format(wsr_dsc_path, overall_status), "CHECK_FAILED")
+        if overall_status != 0:
+            tc.SetFailed("DscCompleteCheck {0} Failed.  Errors {1}".format(
+                wsr_dsc_path, overall_status), "CHECK_FAILED")
         else:
             tc.SetSuccess()
         return overall_status
diff --git a/.pytool/Plugin/DscCompleteCheck/Readme.md b/.pytool/Plugin/DscCompleteCheck/Readme.md
index eefbb9894d..8aaa4f76ee 100644
--- a/.pytool/Plugin/DscCompleteCheck/Readme.md
+++ b/.pytool/Plugin/DscCompleteCheck/Readme.md
@@ -7,6 +7,11 @@ that it would not be built if the package were built). This is critical because
 much of the CI infrastructure assumes that all modules will be listed in the DSC
 and compiled.
 
+This test will ignore INFs in the following cases:
+
+1. When MODULE_TYPE = HOST_APPLICATION
+2. When a Library instance **only** supports the HOST_APPLICATION environment
+
 ## Configuration
 
 The plugin has a few configuration options to support the UEFI codebase.
@@ -14,7 +19,7 @@ The plugin has a few configuration options to support the UEFI codebase.
 ``` yaml
 "DscCompleteCheck": {
         "DscPath": "",   # Path to dsc from root of package
-        "IgnoreInf": []  # Ignore INF if found in filesystem by not dsc
+        "IgnoreInf": []  # Ignore INF if found in filesystem but not dsc
     }
 ```
 
diff --git a/.pytool/Plugin/GuidCheck/GuidCheck.py b/.pytool/Plugin/GuidCheck/GuidCheck.py
index f0b10beb1e..61fdc77911 100644
--- a/.pytool/Plugin/GuidCheck/GuidCheck.py
+++ b/.pytool/Plugin/GuidCheck/GuidCheck.py
@@ -221,7 +221,7 @@ class GuidCheck(ICiBuildPlugin):
 
         # add result to test case
         overall_status = len(Errors)
-        if overall_status is not 0:
+        if overall_status != 0:
             tc.SetFailed("GuidCheck {0} Failed.  Errors {1}".format(
                 packagename, overall_status), "CHECK_FAILED")
         else:
diff --git a/.pytool/Plugin/CompilerPlugin/CompilerPlugin.py b/.pytool/Plugin/HostUnitTestCompilerPlugin/HostUnitTestCompilerPlugin.py
similarity index 54%
copy from .pytool/Plugin/CompilerPlugin/CompilerPlugin.py
copy to .pytool/Plugin/HostUnitTestCompilerPlugin/HostUnitTestCompilerPlugin.py
index 3b6f7c7698..f21b40caf2 100644
--- a/.pytool/Plugin/CompilerPlugin/CompilerPlugin.py
+++ b/.pytool/Plugin/HostUnitTestCompilerPlugin/HostUnitTestCompilerPlugin.py
@@ -1,4 +1,4 @@
-# @file HostUnitTestCompiler_plugin.py
+# @file HostUnitTestCompilerPlugin.py
 ##
 # Copyright (c) Microsoft Corporation.
 # SPDX-License-Identifier: BSD-2-Clause-Patent
@@ -12,21 +12,25 @@ from edk2toolext.environment.plugintypes.ci_build_plugin import ICiBuildPlugin
 from edk2toolext.environment.uefi_build import UefiBuilder
 from edk2toolext import edk2_logging
 from edk2toolext.environment.var_dict import VarDict
+from edk2toollib.utility_functions import GetHostInfo
 
 
-class CompilerPlugin(ICiBuildPlugin):
+class HostUnitTestCompilerPlugin(ICiBuildPlugin):
     """
-    A CiBuildPlugin that compiles the package dsc
-    from the package being tested.
+    A CiBuildPlugin that compiles the dsc for host based unit test apps.
+    An IUefiBuildPlugin may be attached to this plugin that will run the
+    unit tests and collect the results after successful compilation.
 
     Configuration options:
-    "CompilerPlugin": {
+    "HostUnitTestCompilerPlugin": {
         "DscPath": "<path to dsc from root of pkg>"
     }
     """
 
     def GetTestName(self, packagename: str, environment: VarDict) -> tuple:
         """ Provide the testcase name and classname for use in reporting
+            testclassname: a descriptive string for the testcase can include whitespace
+            classname: should be patterned <packagename>.<plugin>.<optionally any unique condition>
 
             Args:
               packagename: string containing name of package to build
@@ -35,14 +39,41 @@ class CompilerPlugin(ICiBuildPlugin):
                 a tuple containing the testcase name and the classname
                 (testcasename, classname)
         """
-        target = environment.GetValue("TARGET")
-        return ("Compile " + packagename + " " + target, packagename + ".Compiler." + target)
+        num,types = self.__GetHostUnitTestArch(environment)
+        types = types.replace(" ", "_")
+
+        return ("Compile and Run Host-Based UnitTests for " + packagename + " on arch " + types,
+                packagename + ".HostUnitTestCompiler." + types)
 
     def RunsOnTargetList(self):
-        return ["DEBUG", "RELEASE"]
+        return ["NOOPT"]
+
+    #
+    # Find the intersection of application types that can run on this host
+    # and the TARGET_ARCH being build in this request.
+    #
+    # return tuple with (number of UEFI arch types, space separated string)
+    def __GetHostUnitTestArch(self, environment):
+        requested = environment.GetValue("TARGET_ARCH").split(' ')
+        host = []
+        if GetHostInfo().arch == 'x86':
+            #assume 64bit can handle 64 and 32
+            #assume 32bit can only handle 32
+            ## change once IA32 issues resolved host.append("IA32")
+            if GetHostInfo().bit == '64':
+                host.append("X64")
+        elif GetHostInfo().arch == 'ARM':
+            if GetHostInfo().bit == '64':
+                host.append("AARCH64")
+            elif GetHostInfo().bit == '32':
+                host.append("ARM")
+
+        willrun = set(requested) & set(host)
+        return (len(willrun), " ".join(willrun))
+
 
     ##
-    # External function of plugin.  This function is used to perform the task of the MuBuild Plugin
+    # External function of plugin.  This function is used to perform the task of the ICiBuildPlugin Plugin
     #
     #   - package is the edk2 path to package.  This means workspace/packagepath relative.
     #   - edk2path object configured with workspace and packages path
@@ -54,11 +85,12 @@ class CompilerPlugin(ICiBuildPlugin):
     #   - output_stream the StringIO output stream from this plugin via logging
     def RunBuildPlugin(self, packagename, Edk2pathObj, pkgconfig, environment, PLM, PLMHelper, tc, output_stream=None):
         self._env = environment
+        environment.SetValue("CI_BUILD_TYPE", "host_unit_test", "Set in HostUnitTestCompilerPlugin")
 
         # Parse the config for required DscPath element
         if "DscPath" not in pkgconfig:
             tc.SetSkipped()
-            tc.LogStdError("DscPath not found in config file.  Nothing to compile.")
+            tc.LogStdError("DscPath not found in config file.  Nothing to compile for HostBasedUnitTests.")
             return -1
 
         AP = Edk2pathObj.GetAbsolutePathOnThisSytemFromEdk2RelativePath(packagename)
@@ -67,11 +99,26 @@ class CompilerPlugin(ICiBuildPlugin):
         AP_Path = Edk2pathObj.GetEdk2RelativePathFromAbsolutePath(APDSC)
         if AP is None or AP_Path is None or not os.path.isfile(APDSC):
             tc.SetSkipped()
-            tc.LogStdError("Package Dsc not found.")
+            tc.LogStdError("Package HostBasedUnitTest Dsc not found.")
             return -1
 
         logging.info("Building {0}".format(AP_Path))
         self._env.SetValue("ACTIVE_PLATFORM", AP_Path, "Set in Compiler Plugin")
+        num, RUNNABLE_ARCHITECTURES = self.__GetHostUnitTestArch(environment)
+        if(num == 0):
+            tc.SetSkipped()
+            tc.LogStdError("No host architecture compatibility")
+            return -1
+
+        if not environment.SetValue("TARGET_ARCH",
+                                    RUNNABLE_ARCHITECTURES,
+                                    "Update Target Arch based on Host Support"):
+            #use AllowOverride function since this is a controlled attempt to change
+            environment.AllowOverride("TARGET_ARCH")
+            if not environment.SetValue("TARGET_ARCH",
+                                        RUNNABLE_ARCHITECTURES,
+                                        "Update Target Arch based on Host Support"):
+                raise RuntimeError("Can't Change TARGET_ARCH as required")
 
         # Parse DSC to check for SUPPORTED_ARCHITECTURES
         dp = DscParser()
@@ -85,7 +132,7 @@ class CompilerPlugin(ICiBuildPlugin):
             # Skip if there is no intersection between SUPPORTED_ARCHITECTURES and TARGET_ARCHITECTURES
             if len(set(SUPPORTED_ARCHITECTURES) & set(TARGET_ARCHITECTURES)) == 0:
                 tc.SetSkipped()
-                tc.LogStdError("No supported architecutres to build")
+                tc.LogStdError("No supported architecutres to build for host unit tests")
                 return -1
 
         uefiBuilder = UefiBuilder()
diff --git a/.pytool/Plugin/HostUnitTestCompilerPlugin/HostUnitTestCompiler_plug_in.yaml b/.pytool/Plugin/HostUnitTestCompilerPlugin/HostUnitTestCompiler_plug_in.yaml
new file mode 100644
index 0000000000..3cecf0af9a
--- /dev/null
+++ b/.pytool/Plugin/HostUnitTestCompilerPlugin/HostUnitTestCompiler_plug_in.yaml
@@ -0,0 +1,12 @@
+##
+# CiBuildPlugin used to build anything that identifies
+# as a unit test.
+#
+# Copyright (c) Microsoft Corporation.
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+{
+  "scope": "host-based-test",
+  "name": "Host Unit Test Compiler Plugin",
+  "module": "HostUnitTestCompilerPlugin"
+}
diff --git a/.pytool/Plugin/HostUnitTestCompilerPlugin/Readme.md b/.pytool/Plugin/HostUnitTestCompilerPlugin/Readme.md
new file mode 100644
index 0000000000..3eeebb4b16
--- /dev/null
+++ b/.pytool/Plugin/HostUnitTestCompilerPlugin/Readme.md
@@ -0,0 +1,24 @@
+# Host UnitTest Compiler Plugin
+
+A CiBuildPlugin that compiles the dsc for host based unit test apps.
+An IUefiBuildPlugin may be attached to this plugin that will run the unit tests and collect the results after successful compilation.
+
+## Configuration
+
+The package relative path of the DSC file to build.
+
+``` yaml
+"HostUnitTestCompilerPlugin": {
+    "DscPath": "<path to dsc from root of pkg>"
+}
+```
+
+### DscPath
+
+Package relative path to the DSC file to build.
+
+## Copyright
+
+Copyright (c) Microsoft Corporation.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
diff --git a/.pytool/Plugin/DscCompleteCheck/DscCompleteCheck.py b/.pytool/Plugin/HostUnitTestDscCompleteCheck/HostUnitTestDscCompleteCheck.py
similarity index 62%
copy from .pytool/Plugin/DscCompleteCheck/DscCompleteCheck.py
copy to .pytool/Plugin/HostUnitTestDscCompleteCheck/HostUnitTestDscCompleteCheck.py
index 9af4f72c8d..66bdecacfb 100644
--- a/.pytool/Plugin/DscCompleteCheck/DscCompleteCheck.py
+++ b/.pytool/Plugin/HostUnitTestDscCompleteCheck/HostUnitTestDscCompleteCheck.py
@@ -1,4 +1,7 @@
-# @file DscCompleteCheck.py
+# @file HostUnitTestDscCompleteCheck.py
+#
+# This is a copy of DscCompleteCheck with different filtering logic.
+# It should be discussed if this should be one plugin
 #
 # Copyright (c) Microsoft Corporation.
 # SPDX-License-Identifier: BSD-2-Clause-Patent
@@ -11,15 +14,15 @@ from edk2toollib.uefi.edk2.parsers.inf_parser import InfParser
 from edk2toolext.environment.var_dict import VarDict
 
 
-class DscCompleteCheck(ICiBuildPlugin):
+class HostUnitTestDscCompleteCheck(ICiBuildPlugin):
     """
-    A CiBuildPlugin that scans the package dsc file and confirms all modules (inf files) are
+    A CiBuildPlugin that scans the package Host Unit Test dsc file and confirms all Host application modules (inf files) are
     listed in the components sections.
 
     Configuration options:
-    "DscCompleteCheck": {
-        "DscPath": "<path to dsc from root of pkg>"
-        "IgnoreInf": []  # Ignore INF if found in filesystem by not dsc
+    "HostUnitTestDscCompleteCheck": {
+        "DscPath": "", # Path to Host based unit test DSC file
+        "IgnoreInf": []  # Ignore INF if found in filesystem but not dsc
     }
     """
 
@@ -35,7 +38,7 @@ class DscCompleteCheck(ICiBuildPlugin):
                 testclassname: a descriptive string for the testcase can include whitespace
                 classname: should be patterned <packagename>.<plugin>.<optionally any unique condition>
         """
-        return ("Check the " + packagename + " DSC for a being complete", packagename + ".DscCompleteCheck")
+        return ("Check the " + packagename + " Host Unit Test DSC for a being complete", packagename + ".HostUnitTestDscCompleteCheck")
 
     ##
     # External function of plugin.  This function is used to perform the task of the MuBuild Plugin
@@ -54,21 +57,25 @@ class DscCompleteCheck(ICiBuildPlugin):
         # Parse the config for required DscPath element
         if "DscPath" not in pkgconfig:
             tc.SetSkipped()
-            tc.LogStdError("DscPath not found in config file.  Nothing to check.")
+            tc.LogStdError(
+                "DscPath not found in config file.  Nothing to check.")
             return -1
 
-        abs_pkg_path = Edk2pathObj.GetAbsolutePathOnThisSytemFromEdk2RelativePath(packagename)
+        abs_pkg_path = Edk2pathObj.GetAbsolutePathOnThisSytemFromEdk2RelativePath(
+            packagename)
         abs_dsc_path = os.path.join(abs_pkg_path, pkgconfig["DscPath"].strip())
-        wsr_dsc_path = Edk2pathObj.GetEdk2RelativePathFromAbsolutePath(abs_dsc_path)
+        wsr_dsc_path = Edk2pathObj.GetEdk2RelativePathFromAbsolutePath(
+            abs_dsc_path)
 
         if abs_dsc_path is None or wsr_dsc_path == "" or not os.path.isfile(abs_dsc_path):
             tc.SetSkipped()
-            tc.LogStdError("Package Dsc not found")
+            tc.LogStdError("Package Host Unit Test Dsc not found")
             return 0
 
         # Get INF Files
         INFFiles = self.WalkDirectoryForExtension([".inf"], abs_pkg_path)
-        INFFiles = [Edk2pathObj.GetEdk2RelativePathFromAbsolutePath(x) for x in INFFiles]  # make edk2relative path so can compare with DSC
+        INFFiles = [Edk2pathObj.GetEdk2RelativePathFromAbsolutePath(
+            x) for x in INFFiles]  # make edk2relative path so can compare with DSC
 
         # remove ignores
 
@@ -79,8 +86,10 @@ class DscCompleteCheck(ICiBuildPlugin):
                     tc.LogStdOut("Ignoring INF {0}".format(a))
                     INFFiles.remove(a)
                 except:
-                    tc.LogStdError("DscCompleteCheck.IgnoreInf -> {0} not found in filesystem.  Invalid ignore file".format(a))
-                    logging.info("DscCompleteCheck.IgnoreInf -> {0} not found in filesystem.  Invalid ignore file".format(a))
+                    tc.LogStdError(
+                        "HostUnitTestDscCompleteCheck.IgnoreInf -> {0} not found in filesystem.  Invalid ignore file".format(a))
+                    logging.info(
+                        "HostUnitTestDscCompleteCheck.IgnoreInf -> {0} not found in filesystem.  Invalid ignore file".format(a))
 
         # DSC Parser
         dp = DscParser()
@@ -99,11 +108,23 @@ class DscCompleteCheck(ICiBuildPlugin):
                 infp.SetPackagePaths(Edk2pathObj.PackagePathList)
                 infp.ParseFile(INF)
                 if("MODULE_TYPE" not in infp.Dict):
-                    tc.LogStdOut("Ignoring INF. Missing key for MODULE_TYPE {0}".format(INF))
+                    tc.LogStdOut(
+                        "Ignoring INF. Missing key for MODULE_TYPE {0}".format(INF))
                     continue
 
                 if(infp.Dict["MODULE_TYPE"] == "HOST_APPLICATION"):
-                    tc.LogStdOut("Ignoring INF.  Module type is HOST_APPLICATION {0}".format(INF))
+                    # should compile test a library that is declared type HOST_APPLICATION
+                    pass
+
+                elif len(infp.SupportedPhases) > 0 and \
+                        "HOST_APPLICATION" in infp.SupportedPhases:
+                    # should compile test a library that supports HOST_APPLICATION but
+                    # require it to be an explicit opt-in
+                    pass
+
+                else:
+                    tc.LogStdOut(
+                        "Ignoring INF. MODULE_TYPE or suppored phases not HOST_APPLICATION {0}".format(INF))
                     continue
 
                 logging.critical(INF + " not in " + wsr_dsc_path)
@@ -111,8 +132,9 @@ class DscCompleteCheck(ICiBuildPlugin):
                 overall_status = overall_status + 1
 
         # If XML object exists, add result
-        if overall_status is not 0:
-            tc.SetFailed("DscCompleteCheck {0} Failed.  Errors {1}".format(wsr_dsc_path, overall_status), "CHECK_FAILED")
+        if overall_status != 0:
+            tc.SetFailed("HostUnitTestDscCompleteCheck {0} Failed.  Errors {1}".format(
+                wsr_dsc_path, overall_status), "CHECK_FAILED")
         else:
             tc.SetSuccess()
         return overall_status
diff --git a/.pytool/Plugin/HostUnitTestDscCompleteCheck/HostUnitTestDscCompleteCheck_plug_in.yaml b/.pytool/Plugin/HostUnitTestDscCompleteCheck/HostUnitTestDscCompleteCheck_plug_in.yaml
new file mode 100644
index 0000000000..82cebd7667
--- /dev/null
+++ b/.pytool/Plugin/HostUnitTestDscCompleteCheck/HostUnitTestDscCompleteCheck_plug_in.yaml
@@ -0,0 +1,12 @@
+##
+# CiBuildPlugin used to confirm all INFs are listed in
+# the components section of package dsc
+#
+# Copyright (c) Microsoft Corporation.
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+{
+    "scope": "host-based-test",
+    "name": "Host Unit Test Dsc Complete Check Test",
+    "module": "HostUnitTestDscCompleteCheck"
+  }
diff --git a/.pytool/Plugin/HostUnitTestDscCompleteCheck/Readme.md b/.pytool/Plugin/HostUnitTestDscCompleteCheck/Readme.md
new file mode 100644
index 0000000000..d77a1f2af1
--- /dev/null
+++ b/.pytool/Plugin/HostUnitTestDscCompleteCheck/Readme.md
@@ -0,0 +1,32 @@
+# Host Unit Test Dsc Complete Check Plugin
+
+This CiBuildPlugin scans all INF files from a package for those related to host
+based unit tests confirms they are listed in the unit test DSC file for the package.
+The test considers it an error if any INF meeting the requirements does not appear
+in the `Components` section of the unit test DSC. This is critical because
+much of the CI infrastructure assumes that  modules will be listed in the DSC
+and compiled.
+
+This test will only require INFs in the following cases:
+
+1. When MODULE_TYPE = HOST_APPLICATION
+2. When a Library instance supports the HOST_APPLICATION environment
+
+## Configuration
+
+The plugin has a few configuration options to support the UEFI codebase.
+
+``` yaml
+"HostUnitTestDscCompleteCheck": {
+    "DscPath": "", # Path to Host based unit test DSC file
+    "IgnoreInf": []  # Ignore INF if found in filesystem but not dsc
+}
+```
+
+### DscPath
+
+Path to DSC to consider platform dsc
+
+### IgnoreInf
+
+Ignore error if Inf file is not listed in DSC file
diff --git a/.pytool/Plugin/LibraryClassCheck/LibraryClassCheck.py b/.pytool/Plugin/LibraryClassCheck/LibraryClassCheck.py
index a62a7e912b..20d87f13f5 100644
--- a/.pytool/Plugin/LibraryClassCheck/LibraryClassCheck.py
+++ b/.pytool/Plugin/LibraryClassCheck/LibraryClassCheck.py
@@ -146,7 +146,7 @@ class LibraryClassCheck(ICiBuildPlugin):
 
 
         # If XML object exists, add result
-        if overall_status is not 0:
+        if overall_status != 0:
             tc.SetFailed("LibraryClassCheck {0} Failed.  Errors {1}".format(wsr_dec_path, overall_status), "CHECK_FAILED")
         else:
             tc.SetSuccess()
-- 
2.21.0.windows.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [Patch v2 02/11] BaseTools/Plugin: Add HostBasedUnitTestRunner plugin
  2020-02-07 18:13 [Patch v2 00/11] Add Unit Test Framework Michael D Kinney
  2020-02-07 18:13 ` [Patch v2 01/11] .pytool: Add CI support for host based unit tests with results Michael D Kinney
@ 2020-02-07 18:13 ` Michael D Kinney
  2020-02-07 18:13 ` [Patch v2 03/11] MdePkg/Include/Library: Add UnitTestLib class Michael D Kinney
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Michael D Kinney @ 2020-02-07 18:13 UTC (permalink / raw)
  To: devel; +Cc: Sean Brogan, Bret Barkelew, Bob Feng, Liming Gao

https://bugzilla.tianocore.org/show_bug.cgi?id=2505

Add plugin to BaseTools to run host based unit tests.

Cc: Sean Brogan <sean.brogan@microsoft.com>
Cc: Bret Barkelew <Bret.Barkelew@microsoft.com>
Cc: Bob Feng <bob.c.feng@intel.com>
Cc: Liming Gao <liming.gao@intel.com>
Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com>
Reviewed-by: Bret Barkelew <Bret.Barkelew@microsoft.com>
Acked-by: Bob Feng <bob.c.feng@intel.com>
---
 .../HostBasedUnitTestRunner.py                | 115 ++++++++++++++++++
 .../HostBasedUnitTestRunner_plug_in.yaml      |  12 ++
 2 files changed, 127 insertions(+)
 create mode 100644 BaseTools/Plugin/HostBasedUnitTestRunner/HostBasedUnitTestRunner.py
 create mode 100644 BaseTools/Plugin/HostBasedUnitTestRunner/HostBasedUnitTestRunner_plug_in.yaml

diff --git a/BaseTools/Plugin/HostBasedUnitTestRunner/HostBasedUnitTestRunner.py b/BaseTools/Plugin/HostBasedUnitTestRunner/HostBasedUnitTestRunner.py
new file mode 100644
index 0000000000..92426760ae
--- /dev/null
+++ b/BaseTools/Plugin/HostBasedUnitTestRunner/HostBasedUnitTestRunner.py
@@ -0,0 +1,115 @@
+# @file HostBasedUnitTestRunner.py
+# Plugin to located any host-based unit tests in the output directory and execute them.
+##
+# Copyright (c) Microsoft Corporation.
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+import os
+import logging
+import glob
+import xml.etree.ElementTree
+from edk2toolext.environment.plugintypes.uefi_build_plugin import IUefiBuildPlugin
+from edk2toolext import edk2_logging
+import edk2toollib.windows.locate_tools as locate_tools
+from edk2toolext.environment import shell_environment
+from edk2toollib.utility_functions import RunCmd
+
+
+class HostBasedUnitTestRunner(IUefiBuildPlugin):
+
+    def do_pre_build(self, thebuilder):
+        '''
+        Works with the compiler (either the HostBasedCompilerPlugin or an other Builder) to set
+        up the environment that will be needed to build host-based unit tests.
+
+        EXPECTS:
+        - Build Var 'CI_BUILD_TYPE' - If not set to 'host_unit_test', will not do anything.
+
+        UPDATES:
+        - Shell Var (Several) - Updates the shell with all vars listed in interesting_keys.
+        - Shell Path - Updated from QueryVcVariables()
+        - Shell Var 'CMOCKA_MESSAGE_OUTPUT'
+        '''
+        ci_type = thebuilder.env.GetValue('CI_BUILD_TYPE')
+        if ci_type != 'host_unit_test':
+            return 0
+
+        shell_env = shell_environment.GetEnvironment()
+        # Use the tools lib to determine the correct values for the vars that interest us.
+        interesting_keys = ["ExtensionSdkDir", "INCLUDE", "LIB", "LIBPATH", "UniversalCRTSdkDir",
+                            "UCRTVersion", "WindowsLibPath", "WindowsSdkBinPath", "WindowsSdkDir", "WindowsSdkVerBinPath",
+                            "WindowsSDKVersion", "VCToolsInstallDir"]
+        vs_vars = locate_tools.QueryVcVariables(interesting_keys, "amd64")
+        for (k, v) in vs_vars.items():
+            if k.upper() == "PATH":
+                shell_env.append_path(v)
+            else:
+                shell_env.set_shell_var(k, v)
+
+        # Set up the reporting type for Cmocka.
+        shell_env.set_shell_var('CMOCKA_MESSAGE_OUTPUT', 'xml')
+        return 0
+
+    def do_post_build(self, thebuilder):
+        '''
+        After a build, will automatically locate and run all host-based unit tests. Logs any
+        failures with Warning severity and will return a count of the failures as the return code.
+
+        EXPECTS:
+        - Build Var 'CI_BUILD_TYPE' - If not set to 'host_unit_test', will not do anything.
+
+        UPDATES:
+        - Shell Var 'CMOCKA_XML_FILE'
+        '''
+        ci_type = thebuilder.env.GetValue('CI_BUILD_TYPE')
+        if ci_type != 'host_unit_test':
+            return 0
+
+        shell_env = shell_environment.GetEnvironment()
+        logging.log(edk2_logging.get_section_level(),
+                    "Run Host based Unit Tests")
+        path = thebuilder.env.GetValue("BUILD_OUTPUT_BASE")
+
+        failure_count = 0
+
+        for arch in thebuilder.env.GetValue("TARGET_ARCH").split():
+            logging.log(edk2_logging.get_subsection_level(),
+                        "Testing for architecture: " + arch)
+            cp = os.path.join(path, arch)
+
+            # If any old results XML files exist, clean them up.
+            for old_result in glob.iglob(os.path.join(cp, "*.result.xml")):
+                os.remove(old_result)
+
+            # Determine whether any tests exist.
+            testList = glob.glob(os.path.join(cp, "*Test*.exe"))
+            for test in testList:
+                # Configure output name.
+                shell_env.set_shell_var(
+                    'CMOCKA_XML_FILE', test + ".%g." + arch + ".result.xml")
+
+                # Run the test.
+                ret = RunCmd('"' + test + '"', "", workingdir=cp)
+                if(ret != 0):
+                    logging.error("UnitTest Execution Error: " +
+                                  os.path.basename(test))
+                else:
+                    logging.info("UnitTest Completed: " +
+                                 os.path.basename(test))
+                    file_match_pattern = test + ".*." + arch + ".result.xml"
+                    xml_results_list = glob.glob(file_match_pattern)
+                    for xml_result_file in xml_results_list:
+                        root = xml.etree.ElementTree.parse(
+                            xml_result_file).getroot()
+                        for suite in root:
+                            for case in suite:
+                                for result in case:
+                                    if result.tag == 'failure':
+                                        logging.warning(
+                                            "%s Test Failed" % os.path.basename(test))
+                                        logging.warning(
+                                            "  %s - %s" % (case.attrib['name'], result.text))
+                                        failure_count += 1
+
+        return failure_count
diff --git a/BaseTools/Plugin/HostBasedUnitTestRunner/HostBasedUnitTestRunner_plug_in.yaml b/BaseTools/Plugin/HostBasedUnitTestRunner/HostBasedUnitTestRunner_plug_in.yaml
new file mode 100644
index 0000000000..d9eb852e97
--- /dev/null
+++ b/BaseTools/Plugin/HostBasedUnitTestRunner/HostBasedUnitTestRunner_plug_in.yaml
@@ -0,0 +1,12 @@
+##
+# IUefiBuildPlugin used to run any unittests that
+# were built on this build.
+#
+# Copyright (c) Microsoft Corporation.
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+{
+  "scope": "host-test-win",
+  "name": "Windows Host-Based Unit Test Runner",
+  "module": "HostBasedUnitTestRunner"
+}
-- 
2.21.0.windows.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [Patch v2 03/11] MdePkg/Include/Library: Add UnitTestLib class
  2020-02-07 18:13 [Patch v2 00/11] Add Unit Test Framework Michael D Kinney
  2020-02-07 18:13 ` [Patch v2 01/11] .pytool: Add CI support for host based unit tests with results Michael D Kinney
  2020-02-07 18:13 ` [Patch v2 02/11] BaseTools/Plugin: Add HostBasedUnitTestRunner plugin Michael D Kinney
@ 2020-02-07 18:13 ` Michael D Kinney
  2020-02-07 18:13 ` [Patch v2 04/11] UnitTestFrameworkPkg: Add public and private interfaces Michael D Kinney
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Michael D Kinney @ 2020-02-07 18:13 UTC (permalink / raw)
  To: devel; +Cc: Bret Barkelew, Sean Brogan, Bret Barkelew, Liming Gao, Hao A Wu

From: Bret Barkelew <brbarkel@microsoft.com>

https://bugzilla.tianocore.org/show_bug.cgi?id=2505

Add UnitTestLib class to MdePkg that provides services
and macros to implement unit tests.  These services and
macros support the following features:

* Create a Unit Test Framework
* Add a Unit Test Suite to a Unit Test Framework
  + Support optional step that executes before a Unit
    Test Suite is started.
  + Support optional step that executes after a Unit
    Test Suite is finished.
* Add a Unit Test to a Unit Test Suite
  + Support optional step that executes before a Unit
    Test is started.
  + Support optional step that executes after a Unit
    Test is finished.
* Run all unit tests added to a Unit Test Framework
* Save Unit Test Framework state to persistent storage
* Support assertion checks in a unit test for TRUE, FALSE,
  EQUAL, MEM_EQUAL, NOT_EFI_ERROR, STATUS_EQUAL, and NOT_NULL.
* Support generation of log messages at ERROR, WARN, INFO,
  and VERBOSE levels.

Cc: Sean Brogan <sean.brogan@microsoft.com>
Cc: Bret Barkelew <Bret.Barkelew@microsoft.com>
Cc: Liming Gao <liming.gao@intel.com>
Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com>
Reviewed-by: Bret Barkelew <Bret.Barkelew@microsoft.com>
Reviewed-by: Michael D Kinney <michael.d.kinney@intel.com>
Reviewed-by: Hao A Wu <hao.a.wu@intel.com>
---
 MdePkg/Include/Library/UnitTestLib.h | 757 +++++++++++++++++++++++++++
 MdePkg/MdePkg.dec                    |   4 +
 2 files changed, 761 insertions(+)
 create mode 100644 MdePkg/Include/Library/UnitTestLib.h

diff --git a/MdePkg/Include/Library/UnitTestLib.h b/MdePkg/Include/Library/UnitTestLib.h
new file mode 100644
index 0000000000..c06c36bea5
--- /dev/null
+++ b/MdePkg/Include/Library/UnitTestLib.h
@@ -0,0 +1,757 @@
+/** @file
+  Provides a unit test framework.  This allows tests to focus on testing logic
+  and the framework to focus on runnings, reporting, statistics, etc.
+
+  Copyright (c) Microsoft Corporation.<BR>
+  Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef __UNIT_TEST_LIB_H__
+#define __UNIT_TEST_LIB_H__
+
+///
+/// Unit Test Status
+///
+typedef UINT32  UNIT_TEST_STATUS;
+#define UNIT_TEST_PASSED                      (0)
+#define UNIT_TEST_ERROR_PREREQUISITE_NOT_MET  (1)
+#define UNIT_TEST_ERROR_TEST_FAILED           (2)
+#define UNIT_TEST_ERROR_CLEANUP_FAILED        (3)
+#define UNIT_TEST_SKIPPED                     (0xFFFFFFFD)
+#define UNIT_TEST_RUNNING                     (0xFFFFFFFE)
+#define UNIT_TEST_PENDING                     (0xFFFFFFFF)
+
+///
+/// Declare PcdUnitTestLogLevel bits and UnitTestLog() ErrorLevel parameter.
+///
+#define UNIT_TEST_LOG_LEVEL_ERROR    BIT0
+#define UNIT_TEST_LOG_LEVEL_WARN     BIT1
+#define UNIT_TEST_LOG_LEVEL_INFO     BIT2
+#define UNIT_TEST_LOG_LEVEL_VERBOSE  BIT3
+
+///
+/// Unit Test Framework Handle
+///
+struct UNIT_TEST_FRAMEWORK_OBJECT;
+typedef struct UNIT_TEST_FRAMEWORK_OBJECT  *UNIT_TEST_FRAMEWORK_HANDLE;
+
+///
+/// Unit Test Suite Handle
+///
+struct UNIT_TEST_SUITE_OBJECT;
+typedef struct UNIT_TEST_SUITE_OBJECT  *UNIT_TEST_SUITE_HANDLE;
+
+///
+/// Unit Test Handle
+///
+struct UNIT_TEST_OBJECT;
+typedef struct UNIT_TEST_OBJECT  *UNIT_TEST_HANDLE;
+
+///
+/// Unit Test Context
+///
+typedef VOID*  UNIT_TEST_CONTEXT;
+
+/**
+  The prototype for a single UnitTest case function.
+
+  Functions with this prototype are registered to be dispatched by the
+  UnitTest framework, and results are recorded as test Pass or Fail.
+
+  @param[in]  Context    [Optional] An optional parameter that enables:
+                         1) test-case reuse with varied parameters and
+                         2) test-case re-entry for Target tests that need a
+                         reboot.  This parameter is a VOID* and it is the
+                         responsibility of the test author to ensure that the
+                         contents are well understood by all test cases that may
+                         consume it.
+
+  @retval  UNIT_TEST_PASSED             The Unit test has completed and the test
+                                        case was successful.
+  @retval  UNIT_TEST_ERROR_TEST_FAILED  A test case assertion has failed.
+
+**/
+typedef
+UNIT_TEST_STATUS
+(EFIAPI *UNIT_TEST_FUNCTION)(
+  IN UNIT_TEST_CONTEXT  Context
+  );
+
+/**
+  Unit-Test Prerequisite Function pointer type.
+
+  Functions with this prototype are registered to be dispatched by the unit test
+  framework prior to a given test case. If this prereq function returns
+  UNIT_TEST_ERROR_PREREQUISITE_NOT_MET, the test case will be skipped.
+
+  @param[in]  Context    [Optional] An optional parameter that enables:
+                         1) test-case reuse with varied parameters and
+                         2) test-case re-entry for Target tests that need a
+                         reboot.  This parameter is a VOID* and it is the
+                         responsibility of the test author to ensure that the
+                         contents are well understood by all test cases that may
+                         consume it.
+
+  @retval  UNIT_TEST_PASSED                      Unit test case prerequisites
+                                                 are met.
+  @retval  UNIT_TEST_ERROR_PREREQUISITE_NOT_MET  Test case should be skipped.
+
+**/
+typedef
+UNIT_TEST_STATUS
+(EFIAPI *UNIT_TEST_PREREQUISITE)(
+  IN UNIT_TEST_CONTEXT  Context
+  );
+
+/**
+  Unit-Test Cleanup (after) function pointer type.
+
+  Functions with this prototype are registered to be dispatched by the
+  unit test framework after a given test case. This will be called even if the
+  test case returns an error, but not if the prerequisite fails and the test is
+  skipped.  The purpose of this function is to clean up any global state or
+  test data.
+
+  @param[in]  Context    [Optional] An optional parameter that enables:
+                         1) test-case reuse with varied parameters and
+                         2) test-case re-entry for Target tests that need a
+                         reboot.  This parameter is a VOID* and it is the
+                         responsibility of the test author to ensure that the
+                         contents are well understood by all test cases that may
+                         consume it.
+
+  @retval  UNIT_TEST_PASSED                Test case cleanup succeeded.
+  @retval  UNIT_TEST_ERROR_CLEANUP_FAILED  Test case cleanup failed.
+
+**/
+typedef
+VOID
+(EFIAPI *UNIT_TEST_CLEANUP)(
+  IN UNIT_TEST_CONTEXT  Context
+  );
+
+/**
+  Unit-Test Test Suite Setup (before) function pointer type. Functions with this
+  prototype are registered to be dispatched by the UnitTest framework prior to
+  running any of the test cases in a test suite.  It will only be run once at
+  the beginning of the suite (not prior to each case).
+
+  The purpose of this function is to set up any global state or test data.
+**/
+typedef
+VOID
+(EFIAPI *UNIT_TEST_SUITE_SETUP)(
+  VOID
+  );
+
+/**
+  Unit-Test Test Suite Teardown (after) function pointer type.  Functions with
+  this prototype are registered to be dispatched by the UnitTest framework after
+  running all of the test cases in a test suite.  It will only be run once at
+  the end of the suite.
+
+  The purpose of this function is to clean up any global state or test data.
+**/
+typedef
+VOID
+(EFIAPI *UNIT_TEST_SUITE_TEARDOWN)(
+  VOID
+  );
+
+/**
+  Method to Initialize the Unit Test framework.  This function registers the
+  test name and also initializes the internal state of the test framework to
+  receive any new suites and tests.
+
+  @param[out]  FrameworkHandle  Unit test framework to be created.
+  @param[in]   Title            Null-terminated ASCII string that is the user
+                                friendly name of the framework. String is
+                                copied.
+  @param[in]   ShortTitle       Null-terminated ASCII short string that is the
+                                short name of the framework with no spaces.
+                                String is copied.
+  @param[in]   VersionString    Null-terminated ASCII version string for the
+                                framework. String is copied.
+
+  @retval  EFI_SUCCESS            The unit test framework was initialized.
+  @retval  EFI_INVALID_PARAMETER  FrameworkHandle is NULL.
+  @retval  EFI_INVALID_PARAMETER  Title is NULL.
+  @retval  EFI_INVALID_PARAMETER  ShortTitle is NULL.
+  @retval  EFI_INVALID_PARAMETER  VersionString is NULL.
+  @retval  EFI_INVALID_PARAMETER  ShortTitle is invalid.
+  @retval  EFI_OUT_OF_RESOURCES   There are not enough resources available to
+                                  initialize the unit test framework.
+**/
+EFI_STATUS
+EFIAPI
+InitUnitTestFramework (
+  OUT UNIT_TEST_FRAMEWORK_HANDLE  *FrameworkHandle,
+  IN  CHAR8                       *Title,
+  IN  CHAR8                       *ShortTitle,
+  IN  CHAR8                       *VersionString
+  );
+
+/**
+  Registers a Unit Test Suite in the Unit Test Framework.
+  At least one test suite must be registered, because all test cases must be
+  within a unit test suite.
+
+  @param[out]  SuiteHandle      Unit test suite to create
+  @param[in]   FrameworkHandle  Unit test framework to add unit test suite to
+  @param[in]   Title            Null-terminated ASCII string that is the user
+                                friendly name of the test suite.  String is
+                                copied.
+  @param[in]   Name             Null-terminated ASCII string that is the short
+                                name of the test suite with no spaces.  String
+                                is copied.
+  @param[in]   Setup            Setup function, runs before suite.  This is an
+                                optional parameter that may be NULL.
+  @param[in]   Teardown         Teardown function, runs after suite.  This is an
+                                optional parameter that may be NULL.
+
+  @retval  EFI_SUCCESS            The unit test suite was created.
+  @retval  EFI_INVALID_PARAMETER  SuiteHandle is NULL.
+  @retval  EFI_INVALID_PARAMETER  FrameworkHandle is NULL.
+  @retval  EFI_INVALID_PARAMETER  Title is NULL.
+  @retval  EFI_INVALID_PARAMETER  Name is NULL.
+  @retval  EFI_OUT_OF_RESOURCES   There are not enough resources available to
+                                  initialize the unit test suite.
+**/
+EFI_STATUS
+EFIAPI
+CreateUnitTestSuite (
+  OUT UNIT_TEST_SUITE_HANDLE      *SuiteHandle,
+  IN  UNIT_TEST_FRAMEWORK_HANDLE  FrameworkHandle,
+  IN  CHAR8                       *Title,
+  IN  CHAR8                       *Name,
+  IN  UNIT_TEST_SUITE_SETUP       Setup     OPTIONAL,
+  IN  UNIT_TEST_SUITE_TEARDOWN    Teardown  OPTIONAL
+  );
+
+/**
+  Adds test case to Suite
+
+  @param[in]  SuiteHandle   Unit test suite to add test to.
+  @param[in]  Description   Null-terminated ASCII string that is the user
+                            friendly description of a test.  String is copied.
+  @param[in]  Name          Null-terminated ASCII string that is the short name
+                            of the test with no spaces.  String is copied.
+  @param[in]  Function      Unit test function.
+  @param[in]  Prerequisite  Prerequisite function, runs before test.  This is
+                            an optional parameter that may be NULL.
+  @param[in]  CleanUp       Clean up function, runs after test.  This is an
+                            optional parameter that may be NULL.
+  @param[in]  Context       Pointer to context.    This is an optional parameter
+                            that may be NULL.
+
+  @retval  EFI_SUCCESS            The unit test case was added to Suite.
+  @retval  EFI_INVALID_PARAMETER  SuiteHandle is NULL.
+  @retval  EFI_INVALID_PARAMETER  Description is NULL.
+  @retval  EFI_INVALID_PARAMETER  Name is NULL.
+  @retval  EFI_INVALID_PARAMETER  Function is NULL.
+  @retval  EFI_OUT_OF_RESOURCES   There are not enough resources available to
+                                  add the unit test case to Suite.
+**/
+EFI_STATUS
+EFIAPI
+AddTestCase (
+  IN UNIT_TEST_SUITE_HANDLE  SuiteHandle,
+  IN CHAR8                   *Description,
+  IN CHAR8                   *Name,
+  IN UNIT_TEST_FUNCTION      Function,
+  IN UNIT_TEST_PREREQUISITE  Prerequisite  OPTIONAL,
+  IN UNIT_TEST_CLEANUP       CleanUp       OPTIONAL,
+  IN UNIT_TEST_CONTEXT       Context       OPTIONAL
+  );
+
+/**
+  Execute all unit test cases in all unit test suites added to a Framework.
+
+  Once a unit test framework is initialized and all unit test suites and unit
+  test cases are registered, this function will cause the unit test framework to
+  dispatch all unit test cases in sequence and record the results for reporting.
+
+  @param[in]  FrameworkHandle  A handle to the current running framework that
+                               dispatched the test.  Necessary for recording
+                               certain test events with the framework.
+
+  @retval  EFI_SUCCESS            All test cases were dispatched.
+  @retval  EFI_INVALID_PARAMETER  FrameworkHandle is NULL.
+**/
+EFI_STATUS
+EFIAPI
+RunAllTestSuites (
+  IN UNIT_TEST_FRAMEWORK_HANDLE  FrameworkHandle
+  );
+
+/**
+  Cleanup a test framework.
+
+  After tests are run, this will teardown the entire framework and free all
+  allocated data within.
+
+  @param[in]  FrameworkHandle  A handle to the current running framework that
+                               dispatched the test.  Necessary for recording
+                               certain test events with the framework.
+
+  @retval  EFI_SUCCESS            All resources associated with framework were
+                                  freed.
+  @retval  EFI_INVALID_PARAMETER  FrameworkHandle is NULL.
+**/
+EFI_STATUS
+EFIAPI
+FreeUnitTestFramework (
+  IN UNIT_TEST_FRAMEWORK_HANDLE  FrameworkHandle
+  );
+
+/**
+  Leverages a framework-specific mechanism (see UnitTestPersistenceLib if you're
+  a framework author) to save the state of the executing framework along with
+  any allocated data so that the test may be resumed upon reentry. A test case
+  should pass any needed context (which, to prevent an infinite loop, should be
+  at least the current execution count) which will be saved by the framework and
+  passed to the test case upon resume.
+
+  Generally called from within a test case prior to quitting or rebooting.
+
+  @param[in]  FrameworkHandle    A handle to the current running framework that
+                                 dispatched the test.  Necessary for recording
+                                 certain test events with the framework.
+  @param[in]  ContextToSave      A buffer of test case-specific data to be saved
+                                 along with framework state.  Will be passed as
+                                 "Context" to the test case upon resume.  This
+                                 is an optional parameter that may be NULL.
+  @param[in]  ContextToSaveSize  Size of the ContextToSave buffer.
+
+  @retval  EFI_SUCCESS            The framework state and context were saved.
+  @retval  EFI_INVALID_PARAMETER  FrameworkHandle is NULL.
+  @retval  EFI_INVALID_PARAMETER  ContextToSave is not NULL and
+                                  ContextToSaveSize is 0.
+  @retval  EFI_INVALID_PARAMETER  ContextToSave is >= 4GB.
+  @retval  EFI_OUT_OF_RESOURCES   There are not enough resources available to
+                                  save the framework and context state.
+  @retval  EFI_DEVICE_ERROR       The framework and context state could not be
+                                  saved to a persistent storage device due to a
+                                  device error.
+**/
+EFI_STATUS
+EFIAPI
+SaveFrameworkState (
+  IN UNIT_TEST_FRAMEWORK_HANDLE  FrameworkHandle,
+  IN UNIT_TEST_CONTEXT           ContextToSave     OPTIONAL,
+  IN UINTN                       ContextToSaveSize
+  );
+
+/**
+  This macro uses the framework assertion logic to check an expression for
+  "TRUE". If the expression evaluates to TRUE, execution continues.
+  Otherwise, the test case immediately returns UNIT_TEST_ERROR_TEST_FAILED.
+
+  @param[in]  Expression  Expression to be evaluated for TRUE.
+**/
+#define UT_ASSERT_TRUE(Expression)                                                        \
+  if(!UnitTestAssertTrue ((Expression), __FUNCTION__, __LINE__, __FILE__, #Expression)) { \
+    return UNIT_TEST_ERROR_TEST_FAILED;                                                   \
+  }
+
+/**
+  This macro uses the framework assertion logic to check an expression for
+  "FALSE". If the expression evaluates to FALSE, execution continues.
+  Otherwise, the test case immediately returns UNIT_TEST_ERROR_TEST_FAILED.
+
+  @param[in]  Expression  Expression to be evaluated for FALSE.
+**/
+#define UT_ASSERT_FALSE(Expression)                                                        \
+  if(!UnitTestAssertFalse ((Expression), __FUNCTION__, __LINE__, __FILE__, #Expression)) { \
+    return UNIT_TEST_ERROR_TEST_FAILED;                                                    \
+  }
+
+/**
+  This macro uses the framework assertion logic to check whether two simple
+  values are equal.  If the values are equal, execution continues.
+  Otherwise, the test case immediately returns UNIT_TEST_ERROR_TEST_FAILED.
+
+  @param[in]  ValueA  Value to be compared for equality (64-bit comparison).
+  @param[in]  ValueB  Value to be compared for equality (64-bit comparison).
+**/
+#define UT_ASSERT_EQUAL(ValueA, ValueB)                                                                           \
+  if(!UnitTestAssertEqual ((UINT64)(ValueA), (UINT64)(ValueB), __FUNCTION__, __LINE__, __FILE__, #ValueA, #ValueB)) { \
+    return UNIT_TEST_ERROR_TEST_FAILED;                                                                           \
+  }
+
+/**
+  This macro uses the framework assertion logic to check whether two memory
+  buffers are equal.  If the buffers are equal, execution continues.
+  Otherwise, the test case immediately returns UNIT_TEST_ERROR_TEST_FAILED.
+
+  @param[in]  BufferA  Pointer to a buffer for comparison.
+  @param[in]  BufferB  Pointer to a buffer for comparison.
+  @param[in]  Length   Number of bytes to compare in BufferA and BufferB.
+**/
+#define UT_ASSERT_MEM_EQUAL(BufferA, BufferB, Length)                                                                               \
+  if(!UnitTestAssertMemEqual ((VOID *)(UINTN)(BufferA), (VOID *)(UINTN)(BufferB), (UINTN)Length, __FUNCTION__, __LINE__, __FILE__, #BufferA, #BufferB)) { \
+    return UNIT_TEST_ERROR_TEST_FAILED;                                                                                           \
+  }
+
+/**
+  This macro uses the framework assertion logic to check whether two simple
+  values are non-equal.  If the values are non-equal, execution continues.
+  Otherwise, the test case immediately returns UNIT_TEST_ERROR_TEST_FAILED.
+
+  @param[in]  ValueA  Value to be compared for inequality (64-bit comparison).
+  @param[in]  ValueB  Value to be compared for inequality (64-bit comparison).
+**/
+#define UT_ASSERT_NOT_EQUAL(ValueA, ValueB)                                                                          \
+  if(!UnitTestAssertNotEqual ((UINT64)(ValueA), (UINT64)(ValueB), __FUNCTION__, __LINE__, __FILE__, #ValueA, #ValueB)) { \
+    return UNIT_TEST_ERROR_TEST_FAILED;                                                                              \
+  }
+
+/**
+  This macro uses the framework assertion logic to check whether an EFI_STATUS
+  value is !EFI_ERROR().  If the status is !EFI_ERROR(), execution continues.
+  Otherwise, the test case immediately returns UNIT_TEST_ERROR_TEST_FAILED.
+
+  @param[in]  Status  EFI_STATUS value to check.
+**/
+#define UT_ASSERT_NOT_EFI_ERROR(Status)                                                \
+  if(!UnitTestAssertNotEfiError ((Status), __FUNCTION__, __LINE__, __FILE__, #Status)) { \
+    return UNIT_TEST_ERROR_TEST_FAILED;                                                \
+  }
+
+/**
+  This macro uses the framework assertion logic to check whether two EFI_STATUS
+  values are equal.  If the values are equal, execution continues.
+  Otherwise, the test case immediately returns UNIT_TEST_ERROR_TEST_FAILED.
+
+  @param[in]  Status    EFI_STATUS values to compare for equality.
+  @param[in]  Expected  EFI_STATUS values to compare for equality.
+**/
+#define UT_ASSERT_STATUS_EQUAL(Status, Expected)                                                 \
+  if(!UnitTestAssertStatusEqual ((Status), (Expected), __FUNCTION__, __LINE__, __FILE__, #Status)) { \
+    return UNIT_TEST_ERROR_TEST_FAILED;                                                          \
+  }
+
+/**
+  This macro uses the framework assertion logic to check whether a pointer is
+  not NULL.  If the pointer is not NULL, execution continues. Otherwise, the
+  test case immediately returns UNIT_TEST_ERROR_TEST_FAILED.
+
+  @param[in]  Pointer  Pointer to be checked against NULL.
+**/
+#define UT_ASSERT_NOT_NULL(Pointer)                                                  \
+  if(!UnitTestAssertNotNull ((Pointer), __FUNCTION__, __LINE__, __FILE__, #Pointer)) { \
+    return UNIT_TEST_ERROR_TEST_FAILED;                                              \
+  }
+
+/**
+  If Expression is TRUE, then TRUE is returned.
+  If Expression is FALSE, then an assert is triggered and the location of the
+  assert provided by FunctionName, LineNumber, FileName, and Description are
+  recorded and FALSE is returned.
+
+  @param[in]  Expression    The BOOLEAN result of the expression evaluation.
+  @param[in]  FunctionName  Null-terminated ASCII string of the function
+                            executing the assert macro.
+  @param[in]  LineNumber    The source file line number of the assert macro.
+  @param[in]  FileName      Null-terminated ASCII string of the filename
+                            executing the assert macro.
+  @param[in]  Description   Null-terminated ASCII string of the expression being
+                            evaluated.
+
+  @retval  TRUE   Expression is TRUE.
+  @retval  FALSE  Expression is FALSE.
+**/
+BOOLEAN
+EFIAPI
+UnitTestAssertTrue (
+  IN BOOLEAN      Expression,
+  IN CONST CHAR8  *FunctionName,
+  IN UINTN        LineNumber,
+  IN CONST CHAR8  *FileName,
+  IN CONST CHAR8  *Description
+  );
+
+/**
+  If Expression is FALSE, then TRUE is returned.
+  If Expression is TRUE, then an assert is triggered and the location of the
+  assert provided by FunctionName, LineNumber, FileName, and Description are
+  recorded and FALSE is returned.
+
+  @param[in]  Expression    The BOOLEAN result of the expression evaluation.
+  @param[in]  FunctionName  Null-terminated ASCII string of the function
+                            executing the assert macro.
+  @param[in]  LineNumber    The source file line number of the assert macro.
+  @param[in]  FileName      Null-terminated ASCII string of the filename
+                            executing the assert macro.
+  @param[in]  Description   Null-terminated ASCII string of the expression being
+                            evaluated.
+
+  @retval  TRUE   Expression is FALSE.
+  @retval  FALSE  Expression is TRUE.
+**/
+BOOLEAN
+EFIAPI
+UnitTestAssertFalse (
+  IN BOOLEAN      Expression,
+  IN CONST CHAR8  *FunctionName,
+  IN UINTN        LineNumber,
+  IN CONST CHAR8  *FileName,
+  IN CONST CHAR8  *Description
+  );
+
+/**
+  If Status is not an EFI_ERROR(), then TRUE is returned.
+  If Status is an EFI_ERROR(), then an assert is triggered and the location of
+  the assert provided by FunctionName, LineNumber, FileName, and Description are
+  recorded and FALSE is returned.
+
+  @param[in]  Status        The EFI_STATUS value to evaluate.
+  @param[in]  FunctionName  Null-terminated ASCII string of the function
+                            executing the assert macro.
+  @param[in]  LineNumber    The source file line number of the assert macro.
+  @param[in]  FileName      Null-terminated ASCII string of the filename
+                            executing the assert macro.
+  @param[in]  Description   Null-terminated ASCII string of the status
+                            expression being evaluated.
+
+  @retval  TRUE   Status is not an EFI_ERROR().
+  @retval  FALSE  Status is an EFI_ERROR().
+**/
+BOOLEAN
+EFIAPI
+UnitTestAssertNotEfiError (
+  IN EFI_STATUS   Status,
+  IN CONST CHAR8  *FunctionName,
+  IN UINTN        LineNumber,
+  IN CONST CHAR8  *FileName,
+  IN CONST CHAR8  *Description
+  );
+
+/**
+  If ValueA is equal ValueB, then TRUE is returned.
+  If ValueA is not equal to ValueB, then an assert is triggered and the location
+  of the assert provided by FunctionName, LineNumber, FileName, DescriptionA,
+  and DescriptionB are recorded and FALSE is returned.
+
+  @param[in]  ValueA        64-bit value.
+  @param[in]  ValueB        64-bit value.
+  @param[in]  FunctionName  Null-terminated ASCII string of the function
+                            executing the assert macro.
+  @param[in]  LineNumber    The source file line number of the assert macro.
+  @param[in]  FileName      Null-terminated ASCII string of the filename
+                            executing the assert macro.
+  @param[in]  DescriptionA  Null-terminated ASCII string that is a description
+                            of ValueA.
+  @param[in]  DescriptionB  Null-terminated ASCII string that is a description
+                            of ValueB.
+
+  @retval  TRUE   ValueA is equal to ValueB.
+  @retval  FALSE  ValueA is not equal to ValueB.
+**/
+BOOLEAN
+EFIAPI
+UnitTestAssertEqual (
+  IN UINT64       ValueA,
+  IN UINT64       ValueB,
+  IN CONST CHAR8  *FunctionName,
+  IN UINTN        LineNumber,
+  IN CONST CHAR8  *FileName,
+  IN CONST CHAR8  *DescriptionA,
+  IN CONST CHAR8  *DescriptionB
+  );
+
+/**
+  If the contents of BufferA are identical to the contents of BufferB, then TRUE
+  is returned.  If the contents of BufferA are not identical to the contents of
+  BufferB, then an assert is triggered and the location of the assert provided
+  by FunctionName, LineNumber, FileName, DescriptionA, and DescriptionB are
+  recorded and FALSE is returned.
+
+  @param[in]  BufferA       Pointer to a buffer for comparison.
+  @param[in]  BufferB       Pointer to a buffer for comparison.
+  @param[in]  Length        Number of bytes to compare in BufferA and BufferB.
+  @param[in]  FunctionName  Null-terminated ASCII string of the function
+                            executing the assert macro.
+  @param[in]  LineNumber    The source file line number of the assert macro.
+  @param[in]  FileName      Null-terminated ASCII string of the filename
+                            executing the assert macro.
+  @param[in]  DescriptionA  Null-terminated ASCII string that is a description
+                            of BufferA.
+  @param[in]  DescriptionB  Null-terminated ASCII string that is a description
+                            of BufferB.
+
+  @retval  TRUE   The contents of BufferA are identical to the contents of
+                  BufferB.
+  @retval  FALSE  The contents of BufferA are not identical to the contents of
+                  BufferB.
+**/
+BOOLEAN
+EFIAPI
+UnitTestAssertMemEqual (
+  IN VOID         *BufferA,
+  IN VOID         *BufferB,
+  IN UINTN        Length,
+  IN CONST CHAR8  *FunctionName,
+  IN UINTN        LineNumber,
+  IN CONST CHAR8  *FileName,
+  IN CONST CHAR8  *DescriptionA,
+  IN CONST CHAR8  *DescriptionB
+  );
+
+/**
+  If ValueA is not equal ValueB, then TRUE is returned.
+  If ValueA is equal to ValueB, then an assert is triggered and the location
+  of the assert provided by FunctionName, LineNumber, FileName, DescriptionA
+  and DescriptionB are recorded and FALSE is returned.
+
+  @param[in]  ValueA        64-bit value.
+  @param[in]  ValueB        64-bit value.
+  @param[in]  FunctionName  Null-terminated ASCII string of the function
+                            executing the assert macro.
+  @param[in]  LineNumber    The source file line number of the assert macro.
+  @param[in]  FileName      Null-terminated ASCII string of the filename
+                            executing the assert macro.
+  @param[in]  DescriptionA  Null-terminated ASCII string that is a description
+                            of ValueA.
+  @param[in]  DescriptionB  Null-terminated ASCII string that is a description
+                            of ValueB.
+
+  @retval  TRUE   ValueA is not equal to ValueB.
+  @retval  FALSE  ValueA is equal to ValueB.
+**/
+BOOLEAN
+EFIAPI
+UnitTestAssertNotEqual (
+  IN UINT64       ValueA,
+  IN UINT64       ValueB,
+  IN CONST CHAR8  *FunctionName,
+  IN UINTN        LineNumber,
+  IN CONST CHAR8  *FileName,
+  IN CONST CHAR8  *DescriptionA,
+  IN CONST CHAR8  *DescriptionB
+  );
+
+/**
+  If Status is equal to Expected, then TRUE is returned.
+  If Status is not equal to Expected, then an assert is triggered and the
+  location of the assert provided by FunctionName, LineNumber, FileName, and
+  Description are recorded and FALSE is returned.
+
+  @param[in]  Status        EFI_STATUS value returned from an API under test.
+  @param[in]  Expected      The expected EFI_STATUS return value from an API
+                            under test.
+  @param[in]  FunctionName  Null-terminated ASCII string of the function
+                            executing the assert macro.
+  @param[in]  LineNumber    The source file line number of the assert macro.
+  @param[in]  FileName      Null-terminated ASCII string of the filename
+                            executing the assert macro.
+  @param[in]  Description   Null-terminated ASCII string that is a description
+                            of Status.
+
+  @retval  TRUE   Status is equal to Expected.
+  @retval  FALSE  Status is not equal to Expected.
+**/
+BOOLEAN
+EFIAPI
+UnitTestAssertStatusEqual (
+  IN EFI_STATUS   Status,
+  IN EFI_STATUS   Expected,
+  IN CONST CHAR8  *FunctionName,
+  IN UINTN        LineNumber,
+  IN CONST CHAR8  *FileName,
+  IN CONST CHAR8  *Description
+  );
+
+/**
+  If Pointer is not equal to NULL, then TRUE is returned.
+  If Pointer is equal to NULL, then an assert is triggered and the location of
+  the assert provided by FunctionName, LineNumber, FileName, and PointerName
+  are recorded and FALSE is returned.
+
+  @param[in]  Pointer       Pointer value to be checked against NULL.
+  @param[in]  Expected      The expected EFI_STATUS return value from a function
+                            under test.
+  @param[in]  FunctionName  Null-terminated ASCII string of the function
+                            executing the assert macro.
+  @param[in]  LineNumber    The source file line number of the assert macro.
+  @param[in]  FileName      Null-terminated ASCII string of the filename
+                            executing the assert macro.
+  @param[in]  PointerName   Null-terminated ASCII string that is a description
+                            of Pointer.
+
+  @retval  TRUE   Pointer is not equal to NULL.
+  @retval  FALSE  Pointer is equal to NULL.
+**/
+BOOLEAN
+EFIAPI
+UnitTestAssertNotNull (
+  IN VOID         *Pointer,
+  IN CONST CHAR8  *FunctionName,
+  IN UINTN        LineNumber,
+  IN CONST CHAR8  *FileName,
+  IN CONST CHAR8  *PointerName
+  );
+
+/**
+  Test logging macro that records an ERROR message in the test framework log.
+  Record is associated with the currently executing test case.
+
+  @param[in]  Format  Formatting string following the format defined in
+                      MdePkg/Include/Library/PrintLib.h.
+  @param[in]  ...     Print args.
+**/
+#define UT_LOG_ERROR(Format, ...)  \
+  UnitTestLog (UNIT_TEST_LOG_LEVEL_ERROR, Format, ##__VA_ARGS__)
+
+/**
+  Test logging macro that records a WARNING message in the test framework log.
+  Record is associated with the currently executing test case.
+
+  @param[in]  Format  Formatting string following the format defined in
+                      MdePkg/Include/Library/PrintLib.h.
+  @param[in]  ...     Print args.
+**/
+#define UT_LOG_WARNING(Format, ...)  \
+  UnitTestLog (UNIT_TEST_LOG_LEVEL_WARN, Format, ##__VA_ARGS__)
+
+/**
+  Test logging macro that records an INFO message in the test framework log.
+  Record is associated with the currently executing test case.
+
+  @param[in]  Format  Formatting string following the format defined in
+                      MdePkg/Include/Library/PrintLib.h.
+  @param[in]  ...     Print args.
+**/
+#define UT_LOG_INFO(Format, ...)  \
+  UnitTestLog (UNIT_TEST_LOG_LEVEL_INFO, Format, ##__VA_ARGS__)
+
+/**
+  Test logging macro that records a VERBOSE message in the test framework log.
+  Record is associated with the currently executing test case.
+
+  @param[in]  Format  Formatting string following the format defined in
+                      MdePkg/Include/Library/PrintLib.h.
+  @param[in]  ...     Print args.
+**/
+#define UT_LOG_VERBOSE(Format, ...)  \
+  UnitTestLog (UNIT_TEST_LOG_LEVEL_VERBOSE, Format, ##__VA_ARGS__)
+
+/**
+  Test logging function that records a messages in the test framework log.
+  Record is associated with the currently executing test case.
+
+  @param[in]  ErrorLevel  The error level of the unit test log message.
+  @param[in]  Format      Formatting string following the format defined in the
+                          MdePkg/Include/Library/PrintLib.h.
+  @param[in]  ...         Print args.
+**/
+VOID
+EFIAPI
+UnitTestLog (
+  IN  UINTN        ErrorLevel,
+  IN  CONST CHAR8  *Format,
+  ...
+  );
+
+#endif
diff --git a/MdePkg/MdePkg.dec b/MdePkg/MdePkg.dec
index d022cc5e3e..ac1f5339af 100644
--- a/MdePkg/MdePkg.dec
+++ b/MdePkg/MdePkg.dec
@@ -244,6 +244,10 @@ [LibraryClasses]
   ##  @libraryclass  Module entry point library for standalone MM drivers.
   StandaloneMmDriverEntryPoint|Include/Library/StandaloneMmDriverEntryPoint.h
 
+  ## @libraryclass Provides a unit test framework
+  #
+  UnitTestLib|Include/Library/UnitTestLib.h
+
 [LibraryClasses.IA32, LibraryClasses.X64]
   ##  @libraryclass  Abstracts both S/W SMI generation and detection.
   ##
-- 
2.21.0.windows.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [Patch v2 04/11] UnitTestFrameworkPkg: Add public and private interfaces
  2020-02-07 18:13 [Patch v2 00/11] Add Unit Test Framework Michael D Kinney
                   ` (2 preceding siblings ...)
  2020-02-07 18:13 ` [Patch v2 03/11] MdePkg/Include/Library: Add UnitTestLib class Michael D Kinney
@ 2020-02-07 18:13 ` Michael D Kinney
  2020-02-07 18:13 ` [Patch v2 05/11] UnitTestFrameworkPkg/Library: Add library instances Michael D Kinney
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Michael D Kinney @ 2020-02-07 18:13 UTC (permalink / raw)
  To: devel; +Cc: Sean Brogan, Bret Barkelew

https://bugzilla.tianocore.org/show_bug.cgi?id=2505

Add public interfaces for use by unit test implementations.

* Include path to cmocka library interfaces.
* PcdUnitTestLogLevel to set the unit test logging message
  level to filter log messages.

Add private interfaces that are used by UnitTestLib
implementations.

* [Private] UnitTestBootLib - Set boot next to continue unit
  tests across a reboot.
* [Private] UnitTestPersistenceLib - Save unit test framework
  state to a persistent storage device.
* [Private] UnitTestResultLib - Output unit test results to a
  console device.
* [Private] UnitTestFrameworkTypes.h - Internal structures
  used by UnitTestLib implementations to keep track if unit
  test frameworks, unit test suites, and unit tests along with
  the serialized storage format to save a unit test framework
  state to persistent storage.

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>
---
 .../PrivateInclude/Library/UnitTestBootLib.h  |  31 +++
 .../Library/UnitTestPersistenceLib.h          |  76 ++++++
 .../Library/UnitTestResultReportLib.h         |  27 ++
 .../PrivateInclude/UnitTestFrameworkTypes.h   | 183 +++++++++++++
 UnitTestFrameworkPkg/ReadMe.md                | 257 ++++++++++++++++++
 UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec |  50 ++++
 UnitTestFrameworkPkg/UnitTestFrameworkPkg.uni |  21 ++
 7 files changed, 645 insertions(+)
 create mode 100644 UnitTestFrameworkPkg/PrivateInclude/Library/UnitTestBootLib.h
 create mode 100644 UnitTestFrameworkPkg/PrivateInclude/Library/UnitTestPersistenceLib.h
 create mode 100644 UnitTestFrameworkPkg/PrivateInclude/Library/UnitTestResultReportLib.h
 create mode 100644 UnitTestFrameworkPkg/PrivateInclude/UnitTestFrameworkTypes.h
 create mode 100644 UnitTestFrameworkPkg/ReadMe.md
 create mode 100644 UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec
 create mode 100644 UnitTestFrameworkPkg/UnitTestFrameworkPkg.uni

diff --git a/UnitTestFrameworkPkg/PrivateInclude/Library/UnitTestBootLib.h b/UnitTestFrameworkPkg/PrivateInclude/Library/UnitTestBootLib.h
new file mode 100644
index 0000000000..d90bff0e4c
--- /dev/null
+++ b/UnitTestFrameworkPkg/PrivateInclude/Library/UnitTestBootLib.h
@@ -0,0 +1,31 @@
+/** @file
+  Provides a library function that can be customized to set the platform to boot
+  from USB on the next boot.  This allows the test framework to reboot back to
+  USB.  Since boot managers are not all the same creating a lib to support
+  platform customization will make porting to new code base/platform easier.
+
+  Copyright (c) Microsoft Corporation.<BR>
+  Copyright (c) 2019 - 2020, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __UNIT_TEST_BOOT_LIB_H__
+#define __UNIT_TEST_BOOT_LIB_H__
+
+/**
+  Set the boot manager to boot from a specific device on the next boot. This
+  should be set only for the next boot and shouldn't require any manual clean up
+
+  @retval EFI_SUCCESS      Boot device for next boot was set.
+  @retval EFI_UNSUPPORTED  Setting the boot device for the next boot is not
+                           supportted.
+  @retval Other            Boot device for next boot can not be set.
+**/
+EFI_STATUS
+EFIAPI
+SetBootNextDevice (
+  VOID
+  );
+
+#endif
diff --git a/UnitTestFrameworkPkg/PrivateInclude/Library/UnitTestPersistenceLib.h b/UnitTestFrameworkPkg/PrivateInclude/Library/UnitTestPersistenceLib.h
new file mode 100644
index 0000000000..af19ba8f53
--- /dev/null
+++ b/UnitTestFrameworkPkg/PrivateInclude/Library/UnitTestPersistenceLib.h
@@ -0,0 +1,76 @@
+/** @file
+  This header file describes a library that contains functions to save and
+  restore unit test internal state, in case the test needs to pause and resume
+  (eg. a reboot-based test).
+
+  Copyright (c) Microsoft Corporation.<BR>
+  Copyright (c) 2019 - 2020, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _UNIT_TEST_PERSISTENCE_LIB_H_
+#define _UNIT_TEST_PERSISTENCE_LIB_H_
+
+#include <UnitTestFrameworkTypes.h>
+
+#define UNIT_TEST_PERSISTENCE_LIB_VERSION   1
+
+/**
+  Determines whether a persistence cache already exists for
+  the given framework.
+
+  @param[in]  FrameworkHandle   A pointer to the framework that is being persisted.
+
+  @retval     TRUE
+  @retval     FALSE   Cache doesn't exist or an error occurred.
+
+**/
+BOOLEAN
+EFIAPI
+DoesCacheExist (
+  IN UNIT_TEST_FRAMEWORK_HANDLE  FrameworkHandle
+  );
+
+/**
+  Will save the data associated with an internal Unit Test Framework
+  state in a manner that can persist a Unit Test Application quit or
+  even a system reboot.
+
+  @param[in]  FrameworkHandle   A pointer to the framework that is being persisted.
+  @param[in]  SaveData          A pointer to the buffer containing the serialized
+                                framework internal state.
+
+  @retval     EFI_SUCCESS   Data is persisted and the test can be safely quit.
+  @retval     Others        Data is not persisted and test cannot be resumed upon exit.
+
+**/
+EFI_STATUS
+EFIAPI
+SaveUnitTestCache (
+  IN UNIT_TEST_FRAMEWORK_HANDLE  FrameworkHandle,
+  IN UNIT_TEST_SAVE_HEADER       *SaveData
+  );
+
+/**
+  Will retrieve any cached state associated with the given framework.
+  Will allocate a buffer to hold the loaded data.
+
+  @param[in]  FrameworkHandle   A pointer to the framework that is being persisted.
+  @param[in]  SaveData          A pointer pointer that will be updated with the address
+                                of the loaded data buffer.
+
+  @retval     EFI_SUCCESS       Data has been loaded successfully and SaveData is updated
+                                with a pointer to the buffer.
+  @retval     Others            An error has occurred and no data has been loaded. SaveData
+                                is set to NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+LoadUnitTestCache (
+  IN  UNIT_TEST_FRAMEWORK_HANDLE  FrameworkHandle,
+  OUT UNIT_TEST_SAVE_HEADER       **SaveData
+  );
+
+#endif
diff --git a/UnitTestFrameworkPkg/PrivateInclude/Library/UnitTestResultReportLib.h b/UnitTestFrameworkPkg/PrivateInclude/Library/UnitTestResultReportLib.h
new file mode 100644
index 0000000000..a417f490dc
--- /dev/null
+++ b/UnitTestFrameworkPkg/PrivateInclude/Library/UnitTestResultReportLib.h
@@ -0,0 +1,27 @@
+/** @file
+  Provides a unit test result report.  This allows new result output formats to
+  be easily customized.
+
+  Copyright (c) Microsoft Corporation.<BR>
+  Copyright (c) 2019 - 2020, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __UNIT_TEST_RESULT_REPORT_LIB_H__
+#define __UNIT_TEST_RESULT_REPORT_LIB_H__
+
+#include <UnitTestFrameworkTypes.h>
+
+/**
+Method to produce the Unit Test run results
+
+@retval Success
+**/
+EFI_STATUS
+EFIAPI
+OutputUnitTestFrameworkReport (
+  IN UNIT_TEST_FRAMEWORK_HANDLE  FrameworkHandle
+  );
+
+#endif
diff --git a/UnitTestFrameworkPkg/PrivateInclude/UnitTestFrameworkTypes.h b/UnitTestFrameworkPkg/PrivateInclude/UnitTestFrameworkTypes.h
new file mode 100644
index 0000000000..e58b30093e
--- /dev/null
+++ b/UnitTestFrameworkPkg/PrivateInclude/UnitTestFrameworkTypes.h
@@ -0,0 +1,183 @@
+/** @file
+  Provides the basic types and common elements of the unit test framework
+
+  Copyright (c) Microsoft Corporation.<BR>
+  Copyright (c) 2019 - 2020, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __UNIT_TEST_TYPES_H__
+#define __UNIT_TEST_TYPES_H__
+
+#include <Library/UnitTestLib.h>
+
+///
+/// The maximum length of a string stored in the unit test framework
+///
+#define UNIT_TEST_MAX_STRING_LENGTH      (120)
+
+///
+/// The size of a firngerprint used to save/resume execution of a unit test
+/// framework.  This is the size of a CRC32 value which is 32-bit value.
+///
+///
+#define UNIT_TEST_FINGERPRINT_SIZE       (sizeof (UINT32))
+
+///
+/// The maximum length of a test failure message stored in the unit test
+/// framework
+///
+#define UNIT_TEST_TESTFAILUREMSG_LENGTH  (120)
+
+///
+/// FAILURE_TYPE used to record the type of assert that was triggered by a unit
+/// test.
+///
+typedef UINT32 FAILURE_TYPE;
+#define FAILURETYPE_NOFAILURE            (0)
+#define FAILURETYPE_OTHER                (1)
+#define FAILURETYPE_ASSERTTRUE           (2)
+#define FAILURETYPE_ASSERTFALSE          (3)
+#define FAILURETYPE_ASSERTEQUAL          (4)
+#define FAILURETYPE_ASSERTNOTEQUAL       (5)
+#define FAILURETYPE_ASSERTNOTEFIERROR    (6)
+#define FAILURETYPE_ASSERTSTATUSEQUAL    (7)
+#define FAILURETYPE_ASSERTNOTNULL        (8)
+
+///
+/// Unit Test context structure tracked by the unit test framework.
+///
+typedef struct {
+  CHAR8                   *Description;
+  CHAR8                   *Name;  //can't have spaces and should be short
+  CHAR8                   *Log;
+  FAILURE_TYPE            FailureType;
+  CHAR8                   FailureMessage[UNIT_TEST_TESTFAILUREMSG_LENGTH];
+  UINT8                   Fingerprint[UNIT_TEST_FINGERPRINT_SIZE];
+  UNIT_TEST_STATUS        Result;
+  UNIT_TEST_FUNCTION      RunTest;
+  UNIT_TEST_PREREQUISITE  Prerequisite;
+  UNIT_TEST_CLEANUP       CleanUp;
+  UNIT_TEST_CONTEXT       Context;
+  UNIT_TEST_SUITE_HANDLE  ParentSuite;
+} UNIT_TEST;
+
+///
+/// Structure used to store the set of unit tests in a unit test suite as a list.
+///
+typedef struct {
+  LIST_ENTRY  Entry;
+  UNIT_TEST   UT;
+} UNIT_TEST_LIST_ENTRY;
+
+///
+/// Unit Test Suite context structure tracked by the unit test framework.
+///
+typedef struct {
+  UINTN                       NumTests;
+  CHAR8                       *Title;
+  CHAR8                       *Name;
+  UINT8                       Fingerprint[UNIT_TEST_FINGERPRINT_SIZE];
+  UNIT_TEST_SUITE_SETUP       Setup;
+  UNIT_TEST_SUITE_TEARDOWN    Teardown;
+  LIST_ENTRY                  TestCaseList;     // UNIT_TEST_LIST_ENTRY
+  UNIT_TEST_FRAMEWORK_HANDLE  ParentFramework;
+} UNIT_TEST_SUITE;
+
+///
+/// Structure used to store the set of unit test suites in a unit test framework
+/// as a list.
+///
+typedef struct {
+  LIST_ENTRY       Entry;
+  UNIT_TEST_SUITE  UTS;
+} UNIT_TEST_SUITE_LIST_ENTRY;
+
+///
+/// Unit Test Framework context structure tracked by the unit test framework.
+///
+typedef struct {
+  CHAR8       *Title;
+  CHAR8       *ShortTitle;      // This title should contain NO spaces or non-filename characters. Is used in reporting and serialization.
+  CHAR8       *VersionString;
+  CHAR8       *Log;
+  UINT8       Fingerprint[UNIT_TEST_FINGERPRINT_SIZE];
+  LIST_ENTRY  TestSuiteList;    // UNIT_TEST_SUITE_LIST_ENTRY
+  EFI_TIME    StartTime;
+  EFI_TIME    EndTime;
+  UNIT_TEST   *CurrentTest;
+  VOID        *SavedState;      // This is an instance of UNIT_TEST_SAVE_HEADER*, if present.
+} UNIT_TEST_FRAMEWORK;
+
+///
+/// Serialized version of a unit test
+///
+typedef struct {
+  UINT32            Size;                                         // Size of the UNIT_TEST_SAVE_TEST including Log[]
+  UINT8             Fingerprint[UNIT_TEST_FINGERPRINT_SIZE];      // Fingerprint of the test itself.
+  CHAR8             FailureMessage[UNIT_TEST_TESTFAILUREMSG_LENGTH];
+  FAILURE_TYPE      FailureType;
+  UNIT_TEST_STATUS  Result;
+  CHAR8             Log[];
+} UNIT_TEST_SAVE_TEST;
+
+///
+/// Serialized version of a unit test context
+///
+typedef struct {
+  UINT32  Size;                                     // Size of the UNIT_TEST_SAVE_CONTEXT including Data[]
+  UINT8   Fingerprint[UNIT_TEST_FINGERPRINT_SIZE];  // Fingerprint of the corresponding test.
+  UINT8   Data[];                                   // Actual data of the context.
+} UNIT_TEST_SAVE_CONTEXT;
+
+///
+/// Serialized version of unit test framework
+///
+typedef struct {
+  UINT8     Version;
+  UINT32    SaveStateSize;                            // Size of the entire serialized buffer.
+  UINT8     Fingerprint[UNIT_TEST_FINGERPRINT_SIZE];  // Fingerprint of the framework that has been saved.
+  EFI_TIME  StartTime;
+  UINT32    TestCount;
+  BOOLEAN   HasSavedContext;
+  // UNIT_TEST_SAVE_TEST    Tests[];         // Array of structures starts here.
+  // UNIT_TEST_SAVE_CONTEXT SavedContext[];  // Saved context for the currently running test.
+  // CHAR8                  Log[];           // NOTE: Not yet implemented!!
+} UNIT_TEST_SAVE_HEADER;
+
+/**
+  This function is responsible for initializing the log buffer for a single test. It can
+  be used internally, but may also be consumed by the test framework to add pre-existing
+  data to a log before it's used.
+
+  @param[in,out]  TestHandle    A handle to the test being initialized.
+  @param[in]      Buffer        [Optional] A pointer to pre-existing log data that should
+                                be used to initialize the log. Should include a NULL terminator.
+  @param[in]      BufferSize    [Optional] The size of the pre-existing log data.
+
+**/
+VOID
+EFIAPI
+UnitTestLogInit (
+  IN OUT UNIT_TEST  *Test,
+  IN UINT8          *Buffer     OPTIONAL,
+  IN UINTN          BufferSize  OPTIONAL
+  );
+
+/**
+  Internal helper function to return a handle to the currently executing framework.
+  This function is generally used for communication within the UnitTest framework, but is
+  defined here so that it can be consumed by the Assertion and Logging macros.
+
+  There should be no need to consume as a test writer, but it's there if you need it.
+
+  @retval     Handle to the currently executing test framework.
+
+**/
+UNIT_TEST_FRAMEWORK_HANDLE
+GetActiveFrameworkHandle (
+  VOID
+  );
+
+#endif
diff --git a/UnitTestFrameworkPkg/ReadMe.md b/UnitTestFrameworkPkg/ReadMe.md
new file mode 100644
index 0000000000..7296f0a45c
--- /dev/null
+++ b/UnitTestFrameworkPkg/ReadMe.md
@@ -0,0 +1,257 @@
+# Unit Test Framework Package
+
+## About
+
+This package adds a unit test framework capable of building tests for multiple contexts including
+the UEFI shell environment and host-based environments. It allows for unit test development to focus
+on the tests and leave error logging, result formatting, context persistance, and test running to the framework.
+The unit test framework works well for low level unit tests as well as system level tests and
+fits easily in automation frameworks.
+
+### UnitTestLib
+
+The main "framework" library. The core of the framework is the Framework object, which can have any number
+of test cases and test suites registered with it. The Framework object is also what drives test execution.
+
+The Framework also provides helper macros and functions for checking test conditions and
+reporting errors. Status and error info will be logged into the test context. There are a number
+of Assert macros that make the unit test code friendly to view and easy to understand.
+
+Finally, the Framework also supports logging strings during the test execution. This data is logged
+to the test context and will be available in the test reporting phase. This should be used for
+logging test details and helpful messages to resolve test failures.
+
+### UnitTestPersistenceLib
+
+Persistence lib has the main job of saving and restoring test context to a storage medium so that for tests
+that require exiting the active process and then resuming state can be maintained. This is critical
+in supporting a system reboot in the middle of a test run.
+
+### UnitTestResultReportLib
+
+Library provides function to run at the end of a framework test run and handles formatting the report.
+This is a common customization point and allows the unit test framework to fit its output reports into
+other test infrastructure. In this package a simple library instances has been supplied to output test
+results to the console as plain text.
+
+## Samples
+
+There is a sample unit test provided as both an example of how to write a unit test and leverage
+many of the features of the framework. This sample can be found in the `Test/UnitTest/Sample/SampleUnitTest`
+directory.
+
+The sample is provided in PEI, SMM, DXE, and UEFI App flavors. It also has a flavor for the HOST_APPLICATION
+build type, which can be run on a host system without needing a target.
+
+## Usage
+
+This section is built a lot like a "Getting Started". We'll go through some of the components that are needed
+when constructing a unit test and some of the decisions that are made by the test writer. We'll also describe
+how to check for expected conditions in test cases and a bit of the logging characteristics.
+
+Most of these examples will refer to the SampleUnitTestUefiShell app found in this package.
+
+### Requirements - INF
+
+In our INF file, we'll need to bring in the `UnitTestLib` library. Conveniently, the interface
+header for the `UnitTestLib` is located in `MdePkg`, so you shouldn't need to depend on any other
+packages. As long as your DSC file knows where to find the lib implementation that you want to use,
+you should be good to go.
+
+See this example in 'SampleUnitTestApp.inf'...
+
+```
+[Packages]
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  UefiApplicationEntryPoint
+  BaseLib
+  DebugLib
+  UnitTestLib
+  PrintLib
+```
+
+### Requirements - Code
+
+Not to state the obvious, but let's make sure we have the following include before getting too far along...
+
+```c
+#include <Library/UnitTestLib.h>
+```
+
+Now that we've got that squared away, let's look at our 'Main()'' routine (or DriverEntryPoint() or whatever).
+
+### Configuring the Framework
+
+Everything in the UnitTestPkg framework is built around an object called -- conveniently -- the Framework.
+This Framework object will contain all the information about our test, the test suites and test cases associated
+with it, the current location within the test pass, and any results that have been recorded so far.
+
+To get started with a test, we must first create a Framework instance. The function for this is
+`InitUnitTestFramework`. It takes in `CHAR8` strings for the long name, short name, and test version.
+The long name and version strings are just for user presentation and relatively flexible. The short name
+will be used to name any cache files and/or test results, so should be a name that makes sense in that context.
+These strings are copied internally to the Framework, so using stack-allocated or literal strings is fine.
+
+In the 'SampleUnitTestUefiShell' app, the module name is used as the short name, so the init looks like this.
+
+```c
+DEBUG(( DEBUG_INFO, "%a v%a\n", UNIT_TEST_APP_NAME, UNIT_TEST_APP_VERSION ));
+
+//
+// Start setting up the test framework for running the tests.
+//
+Status = InitUnitTestFramework( &Framework, UNIT_TEST_APP_NAME, gEfiCallerBaseName, UNIT_TEST_APP_VERSION );
+```
+
+The `&Framework` returned here is the handle to the Framework. If it's successfully returned, we can start adding
+test suites and test cases.
+
+Test suites exist purely to help organize test cases and to differentiate the results in reports. If you're writing
+a small unit test, you can conceivably put all test cases into a single suite. However, if you end up with 20+ test
+cases, it may be beneficial to organize them according to purpose. You _must_ have at least one test suite, even if
+it's just a catch-all. The function to create a test suite is `CreateUnitTestSuite`. It takes in a handle to
+the Framework object, a `CHAR8` string for the suite title and package name, and optional function pointers for
+a setup function and a teardown function.
+
+The suite title is for user presentation. The package name is for xUnit type reporting and uses a '.'-separated
+hierarchical format (see 'SampleUnitTestApp' for example). If provided, the setup and teardown functions will be
+called once at the start of the suite (before _any_ tests have run) and once at the end of the suite (after _all_
+tests have run), respectively. If either or both of these are unneeded, pass `NULL`. The function prototypes are
+`UNIT_TEST_SUITE_SETUP` and `UNIT_TEST_SUITE_TEARDOWN`.
+
+Looking at 'SampleUnitTestUefiShell' app, you can see that the first test suite is created as below...
+
+```c
+//
+// Populate the SimpleMathTests Unit Test Suite.
+//
+Status = CreateUnitTestSuite( &SimpleMathTests, Fw, "Simple Math Tests", "Sample.Math", NULL, NULL );
+```
+
+This test suite has no setup or teardown functions. The `&SimpleMathTests` returned here is a handle to the suite and
+will be used when adding test cases.
+
+Great! Now we've finished some of the cruft, red tape, and busy work. We're ready to add some tests. Adding a test
+to a test suite is accomplished with the -- you guessed it -- `AddTestCase` function. It takes in the suite handle;
+a `CHAR8` string for the description and class name; a function pointer for the test case itself; additional, optional
+function pointers for prerequisite check and cleanup routines; and and optional pointer to a context structure.
+
+Okay, that's a lot. Let's take it one piece at a time. The description and class name strings are very similar in
+usage to the suite title and package name strings in the test suites. The former is for user presentation and the
+latter is for xUnit parsing. The test case function pointer is what is actually executed as the "test" and the
+prototype should be `UNIT_TEST_FUNCTION`. The last three parameters require a little bit more explaining.
+
+The prerequisite check function has a prototype of `UNIT_TEST_PREREQUISITE` and -- if provided -- will be called
+immediately before the test case. If this function returns any error, the test case will not be run and will be
+recorded as `UNIT_TEST_ERROR_PREREQUISITE_NOT_MET`. The cleanup function (prototype `UNIT_TEST_CLEANUP`) will be called
+immediately after the test case to provide an opportunity to reset any global state that may have been changed in the
+test case. In the event of a prerequisite failure, the cleanup function will also be skipped. If either of these
+functions is not needed, pass `NULL`.
+
+The context pointer is entirely case-specific. It will be passed to the test case upon execution. One of the purposes
+of the context pointer is to allow test case reuse with different input data. (Another use is for testing that wraps
+around a system reboot, but that's beyond the scope of this guide.) The test case must know how to interpret the context
+pointer, so it could be a simple value, or it could be a complex structure. If unneeded, pass `NULL`.
+
+In 'SampleUnitTestUefiShell' app, the first test case is added using the code below...
+
+```c
+AddTestCase( SimpleMathTests, "Adding 1 to 1 should produce 2", "Addition", OnePlusOneShouldEqualTwo, NULL, NULL, NULL );
+```
+
+This test case calls the function `OnePlusOneShouldEqualTwo` and has no prerequisite, cleanup, or context.
+
+Once all the suites and cases are added, it's time to run the Framework.
+
+```c
+//
+// Execute the tests.
+//
+Status = RunAllTestSuites( Framework );
+```
+
+### A Simple Test Case
+
+We'll take a look at the below test case from 'SampleUnitTestApp'...
+
+```c
+UNIT_TEST_STATUS
+EFIAPI
+OnePlusOneShouldEqualTwo (
+  IN UNIT_TEST_FRAMEWORK_HANDLE  Framework,
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  UINTN     A, B, C;
+
+  A = 1;
+  B = 1;
+  C = A + B;
+
+  UT_ASSERT_EQUAL(C, 2);
+  return UNIT_TEST_PASSED;
+} // OnePlusOneShouldEqualTwo()
+```
+
+The prototype for this function matches the `UNIT_TEST_FUNCTION` prototype. It takes in a handle to the Framework
+itself and the context pointer. The context pointer could be cast and interpreted as anything within this test case,
+which is why it's important to configure contexts carefully. The test case returns a value of `UNIT_TEST_STATUS`, which
+will be recorded in the Framework and reported at the end of all suites.
+
+In this test case, the `UT_ASSERT_EQUAL` assertion is being used to establish that the business logic has functioned
+correctly. There are several assertion macros, and you are encouraged to use one that matches as closely to your
+intended test criterium as possible, because the logging is specific to the macro and more specific macros have more
+detailed logs. When in doubt, there are always `UT_ASSERT_TRUE` and `UT_ASSERT_FALSE`. Assertion macros that fail their
+test criterium will immediately return from the test case with `UNIT_TEST_ERROR_TEST_FAILED` and log an error string.
+_Note_ that this early return can have implications for memory leakage.
+
+At the end, if all test criteria pass, you should return `UNIT_TEST_PASSED`.
+
+### More Complex Cases
+
+To write more advanced tests, first take a look at all the Assertion and Logging macros provided in the framework.
+
+Beyond that, if you're writing host-based tests and want to take a dependency on the UnitTestFrameworkPkg, you can
+leverage the `cmocka.h` interface and write tests with all the features of the Cmocka framework.
+
+Documentation for Cmocka can be found here:
+https://api.cmocka.org/
+
+## Development
+
+When using the EDK2 Pytools for CI testing, the host-based unit tests will be built and run on any build that includes the `NOOPT` build target.
+
+If you are trying to iterate on a single test, a convenient pattern is to build only that test module. For example, the following command will build only the SafeIntLib host-based test from the MdePkg...
+
+```bash
+stuart_ci_build -c .pytool/CISettings.py TOOL_CHAIN_TAG=VS2017 -p MdePkg -t NOOPT BUILDMODULE=MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLib.inf
+```
+
+## Known Limitations
+
+### PEI, DXE, SMM
+
+While sample tests have been provided for these execution environments, only cursory build validation
+has been performed. Care has been taken while designing the frameworks to allow for execution during
+boot phases, but only UEFI Shell and host-based tests have been thoroughly evaluated. Full support for
+PEI, DXE, and SMM is forthcoming, but should be considered beta/staging for now.
+
+### Host-Based Support vs Other Tests
+
+The host-based test framework is powered internally by the Cmocka framework. As such, it has abilities
+that the target-based tests don't (yet). It would be awesome if this meant that it was a super set of
+the target-based tests, and it worked just like the target-based tests but with more features. Unfortunately,
+this is not the case. While care has been taken to keep them as close a possible, there are a few known
+inconsistencies that we're still ironing out. For example, the logging messages in the target-based tests
+are cached internally and associated with the running test case. They can be saved later as part of the
+reporting lib. This isn't currently possible with host-based. Only the assertion failures are logged.
+
+We will continue trying to make these as similar as possible.
+
+## Copyright
+
+Copyright (c) Microsoft Corporation.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
diff --git a/UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec b/UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec
new file mode 100644
index 0000000000..069289f009
--- /dev/null
+++ b/UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec
@@ -0,0 +1,50 @@
+## @file
+# This Package provides all definitions (including functions, MACROs,
+# structures library classes, and PCDs) and libraries instances, which are used
+# to support unit testing and interface testing.
+#
+# Copyright (c) Microsoft Corporation.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  DEC_SPECIFICATION = 0x00010005
+  PACKAGE_NAME      = UnitTestFrameworkPkg
+  PACKAGE_UNI_FILE  = UnitTestFrameworkPkg.uni
+  PACKAGE_GUID      = 4A70C4A0-D72C-4D3F-9943-BE7C41C50BA3
+  PACKAGE_VERSION   = 1.00
+
+[Includes]
+  Library/CmockaLib/cmocka/include
+
+[Includes.Common.Private]
+  PrivateInclude
+  Library/CmockaLib/cmocka/include/cmockery
+
+[LibraryClasses.Common.Private]
+  ## @libraryclass Allows save and restore unit test internal state
+  #
+  UnitTestPersistenceLib|PrivateInclude/Library/UnitTestPersistenceLib.h
+
+  ## @libraryclass Provides a unit test result report
+  #
+  UnitTestResultReportLib|PrivateInclude/Library/UnitTestResultReportLib.h
+
+  ## @libraryclass Provides boot-option routines useful in shell-based tests.
+  #
+  UnitTestBootLib|PrivateInclude/Library/UnitTestBootLib.h
+
+[Guids]
+  gUnitTestFrameworkPkgTokenSpaceGuid = { 0x833d3aba, 0x39b4, 0x43a2, { 0xb9, 0x30, 0x7a, 0x34, 0x53, 0x39, 0x31, 0xb3 } }
+
+[PcdsFixedAtBuild]
+  ## This flag is used to control build time optimization based on unit test
+  #  log level.  The default value is 0xFFFFFFFF to enable all unit test log
+  #  messages.
+  #  BIT0 - Error unit test log messages.<BR>
+  #  BIT1 - Warning unit test log messages.<BR>
+  #  BIT2 - Informational unit test log messages.<BR>
+  #  BIT3 - Verbose unit test log messages.<BR>
+  # @Prompt  Unit Test Log Message Level
+  gUnitTestFrameworkPkgTokenSpaceGuid.PcdUnitTestLogLevel|0xFFFFFFFF|UINT32|0x00000001
diff --git a/UnitTestFrameworkPkg/UnitTestFrameworkPkg.uni b/UnitTestFrameworkPkg/UnitTestFrameworkPkg.uni
new file mode 100644
index 0000000000..180675ae1a
--- /dev/null
+++ b/UnitTestFrameworkPkg/UnitTestFrameworkPkg.uni
@@ -0,0 +1,21 @@
+// /** @file
+// This Package provides all definitions (including functions, MACROs,
+// structures library classes, and PCDs) and libraries instances, which are used
+// to support unit testing and interface testing.
+//
+// Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PACKAGE_ABSTRACT     #language en-US "This Package provides all definitions (including functions, MACROs, structures library classes, and PCDs) and libraries instances, which are used to support unit testing and interface testing."
+
+#string STR_PACKAGE_DESCRIPTION  #language en-US "This Package provides all definitions (including functions, MACROs, structures library classes, and PCDs) and libraries instances, which are used to support unit testing and interface testing."
+
+#string STR_gUnitTestFrameworkPkgTokenSpaceGuid_PcdUnitTestLogLevel_PROMPT  #language en-US "Unit Test Log Message Level"
+
+#string STR_gUnitTestFrameworkPkgTokenSpaceGuid_PcdUnitTestLogLevel_HELP    #language en-US "This flag is used to control build time optimization based on unit test log level.  The default value is 0xFFFFFFFF to enable all unit test log messages.<BR><BR>\n"
+                                                                                   "BIT0 - Error unit test log messages.<BR>\n"
+                                                                                   "BIT1 - Warning unit test log messages.<BR>\n"
+                                                                                   "BIT2 - Informational unit test log messages.<BR>\n"
+                                                                                   "BIT3 - Verbose unit test log messages.<BR>\n"
-- 
2.21.0.windows.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [Patch v2 05/11] UnitTestFrameworkPkg/Library: Add library instances
  2020-02-07 18:13 [Patch v2 00/11] Add Unit Test Framework Michael D Kinney
                   ` (3 preceding siblings ...)
  2020-02-07 18:13 ` [Patch v2 04/11] UnitTestFrameworkPkg: Add public and private interfaces Michael D Kinney
@ 2020-02-07 18:13 ` Michael D Kinney
  2020-02-07 18:13 ` [Patch v2 06/11] UnitTestFrameworkPkg/Test: Add unit test samples Michael D Kinney
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Michael D Kinney @ 2020-02-07 18:13 UTC (permalink / raw)
  To: devel; +Cc: Sean Brogan, Bret Barkelew

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>
---
 .gitmodules                                   |   3 +
 .../Library/CmockaLib/CmockaLib.inf           |  35 +
 .../Library/CmockaLib/CmockaLib.uni           |  14 +
 UnitTestFrameworkPkg/Library/CmockaLib/cmocka |   1 +
 .../Posix/DebugLibPosix/DebugLibPosix.c       | 279 ++++++
 .../Posix/DebugLibPosix/DebugLibPosix.inf     |  35 +
 .../Posix/DebugLibPosix/DebugLibPosix.uni     |  14 +
 .../MemoryAllocationLibPosix.c                | 631 +++++++++++++
 .../MemoryAllocationLibPosix.inf              |  27 +
 .../MemoryAllocationLibPosix.uni              |  14 +
 .../UnitTestBootLibNull/UnitTestBootLibNull.c |  26 +
 .../UnitTestBootLibNull.inf                   |  23 +
 .../UnitTestBootLibNull.uni                   |  11 +
 .../UnitTestBootLibUsbClass.c                 | 127 +++
 .../UnitTestBootLibUsbClass.inf               |  34 +
 .../UnitTestBootLibUsbClass.uni               |  12 +
 .../Library/UnitTestLib/Assert.c              | 491 ++++++++++
 .../Library/UnitTestLib/AssertCmocka.c        | 335 +++++++
 .../Library/UnitTestLib/Log.c                 | 200 ++++
 .../Library/UnitTestLib/RunTests.c            | 171 ++++
 .../Library/UnitTestLib/RunTestsCmocka.c      | 278 ++++++
 .../Library/UnitTestLib/UnitTestLib.c         | 853 ++++++++++++++++++
 .../Library/UnitTestLib/UnitTestLib.inf       |  37 +
 .../Library/UnitTestLib/UnitTestLib.uni       |  11 +
 .../Library/UnitTestLib/UnitTestLibCmocka.inf |  38 +
 .../Library/UnitTestLib/UnitTestLibCmocka.uni |  11 +
 .../UnitTestPersistenceLibNull.c              |  75 ++
 .../UnitTestPersistenceLibNull.inf            |  28 +
 .../UnitTestPersistenceLibNull.uni            |  11 +
 .../UnitTestPersistenceLibSimpleFileSystem.c  | 416 +++++++++
 ...UnitTestPersistenceLibSimpleFileSystem.inf |  47 +
 ...UnitTestPersistenceLibSimpleFileSystem.uni |  15 +
 .../UnitTestResultReportLib.c                 | 216 +++++
 .../UnitTestResultReportLibConOut.c           |  48 +
 .../UnitTestResultReportLibConOut.inf         |  29 +
 .../UnitTestResultReportLibConOut.uni         |  11 +
 .../UnitTestResultReportLibDebugLib.c         |  47 +
 .../UnitTestResultReportLibDebugLib.inf       |  28 +
 .../UnitTestResultReportLibDebugLib.uni       |  11 +
 39 files changed, 4693 insertions(+)
 create mode 100644 UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.inf
 create mode 100644 UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.uni
 create mode 160000 UnitTestFrameworkPkg/Library/CmockaLib/cmocka
 create mode 100644 UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.c
 create mode 100644 UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.inf
 create mode 100644 UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.uni
 create mode 100644 UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.c
 create mode 100644 UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.inf
 create mode 100644 UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.uni
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootLibNull.c
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootLibNull.inf
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootLibNull.uni
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibUsbClass.c
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibUsbClass.inf
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibUsbClass.uni
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/Assert.c
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/AssertCmocka.c
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/Log.c
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/RunTests.c
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/RunTestsCmocka.c
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.c
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.inf
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.uni
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLibCmocka.inf
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLibCmocka.uni
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.c
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.inf
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.uni
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.c
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.inf
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.uni
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLib.c
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.c
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.inf
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.uni
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.c
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.inf
 create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.uni

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


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [Patch v2 06/11] UnitTestFrameworkPkg/Test: Add unit test samples
  2020-02-07 18:13 [Patch v2 00/11] Add Unit Test Framework Michael D Kinney
                   ` (4 preceding siblings ...)
  2020-02-07 18:13 ` [Patch v2 05/11] UnitTestFrameworkPkg/Library: Add library instances Michael D Kinney
@ 2020-02-07 18:13 ` Michael D Kinney
  2020-02-07 18:13 ` [Patch v2 07/11] UnitTestFrameworkPkg: Add DSC, DSC INC, and YAML files Michael D Kinney
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Michael D Kinney @ 2020-02-07 18:13 UTC (permalink / raw)
  To: devel; +Cc: Sean Brogan, Bret Barkelew

https://bugzilla.tianocore.org/show_bug.cgi?id=2505

Add an implementation of a sample unit test that
demonstrates the use of the UnitTestLib services and
macros and supports being built for execution in a
host environment or for execution on a target in PEI,
DXE, SMM, or UEFI Shell.

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>
---
 .../Sample/SampleUnitTest/SampleUnitTest.c    | 289 ++++++++++++++++++
 .../SampleUnitTest/SampleUnitTestDxe.inf      |  36 +++
 .../SampleUnitTest/SampleUnitTestHost.inf     |  30 ++
 .../SampleUnitTest/SampleUnitTestPei.inf      |  36 +++
 .../SampleUnitTest/SampleUnitTestSmm.inf      |  37 +++
 .../SampleUnitTestUefiShell.inf               |  33 ++
 6 files changed, 461 insertions(+)
 create mode 100644 UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTest.c
 create mode 100644 UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestDxe.inf
 create mode 100644 UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestHost.inf
 create mode 100644 UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestPei.inf
 create mode 100644 UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestSmm.inf
 create mode 100644 UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestUefiShell.inf

diff --git a/UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTest.c b/UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTest.c
new file mode 100644
index 0000000000..37d5747bca
--- /dev/null
+++ b/UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTest.c
@@ -0,0 +1,289 @@
+/** @file
+  This is a sample to demostrate the usage of the Unit Test Library that
+  supports the PEI, DXE, SMM, UEFI SHell, and host execution environments.
+
+  Copyright (c) Microsoft Corporation.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#include <PiPei.h>
+#include <Uefi.h>
+#include <Library/UefiLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UnitTestLib.h>
+#include <Library/PrintLib.h>
+
+#define UNIT_TEST_NAME     "Sample Unit Test"
+#define UNIT_TEST_VERSION  "0.1"
+
+///
+/// Global variables used in unit tests
+///
+BOOLEAN  mSampleGlobalTestBoolean  = FALSE;
+VOID     *mSampleGlobalTestPointer = NULL;
+
+/**
+  Sample Unit-Test Prerequisite Function that checks to make sure the global
+  pointer used in the test is already set to NULL.
+
+  Functions with this prototype are registered to be dispatched by the unit test
+  framework prior to a given test case. If this prereq function returns
+  UNIT_TEST_ERROR_PREREQUISITE_NOT_MET, the test case will be skipped.
+
+  @param[in]  Context    [Optional] An optional parameter that enables:
+                         1) test-case reuse with varied parameters and
+                         2) test-case re-entry for Target tests that need a
+                         reboot.  This parameter is a VOID* and it is the
+                         responsibility of the test author to ensure that the
+                         contents are well understood by all test cases that may
+                         consume it.
+
+  @retval  UNIT_TEST_PASSED                      Unit test case prerequisites
+                                                 are met.
+  @retval  UNIT_TEST_ERROR_PREREQUISITE_NOT_MET  Test case should be skipped.
+
+**/
+UNIT_TEST_STATUS
+EFIAPI
+MakeSureThatPointerIsNull (
+  IN UNIT_TEST_CONTEXT  Context
+  )
+{
+  UT_ASSERT_EQUAL ((UINTN)mSampleGlobalTestPointer, (UINTN)NULL);
+  return UNIT_TEST_PASSED;
+}
+
+/**
+  Sample Unit-Test Cleanup (after) function that resets the global pointer to
+  NULL.
+
+  Functions with this prototype are registered to be dispatched by the
+  unit test framework after a given test case. This will be called even if the
+  test case returns an error, but not if the prerequisite fails and the test is
+  skipped.  The purpose of this function is to clean up any global state or
+  test data.
+
+  @param[in]  Context    [Optional] An optional parameter that enables:
+                         1) test-case reuse with varied parameters and
+                         2) test-case re-entry for Target tests that need a
+                         reboot.  This parameter is a VOID* and it is the
+                         responsibility of the test author to ensure that the
+                         contents are well understood by all test cases that may
+                         consume it.
+
+  @retval  UNIT_TEST_PASSED                Test case cleanup succeeded.
+  @retval  UNIT_TEST_ERROR_CLEANUP_FAILED  Test case cleanup failed.
+
+**/
+VOID
+EFIAPI
+ClearThePointer (
+  IN UNIT_TEST_CONTEXT  Context
+  )
+{
+  mSampleGlobalTestPointer = NULL;
+}
+
+/**
+  Sample unit test that verifies the expected result of an unsigned integer
+  addition operation.
+
+  @param[in]  Context    [Optional] An optional parameter that enables:
+                         1) test-case reuse with varied parameters and
+                         2) test-case re-entry for Target tests that need a
+                         reboot.  This parameter is a VOID* and it is the
+                         responsibility of the test author to ensure that the
+                         contents are well understood by all test cases that may
+                         consume it.
+
+  @retval  UNIT_TEST_PASSED             The Unit test has completed and the test
+                                        case was successful.
+  @retval  UNIT_TEST_ERROR_TEST_FAILED  A test case assertion has failed.
+**/
+UNIT_TEST_STATUS
+EFIAPI
+OnePlusOneShouldEqualTwo (
+  IN UNIT_TEST_CONTEXT  Context
+  )
+{
+  UINTN  A;
+  UINTN  B;
+  UINTN  C;
+
+  A = 1;
+  B = 1;
+  C = A + B;
+
+  UT_ASSERT_EQUAL (C, 2);
+
+  return UNIT_TEST_PASSED;
+}
+
+/**
+  Sample unit test that verifies that a global BOOLEAN is updatable.
+
+  @param[in]  Context    [Optional] An optional parameter that enables:
+                         1) test-case reuse with varied parameters and
+                         2) test-case re-entry for Target tests that need a
+                         reboot.  This parameter is a VOID* and it is the
+                         responsibility of the test author to ensure that the
+                         contents are well understood by all test cases that may
+                         consume it.
+
+  @retval  UNIT_TEST_PASSED             The Unit test has completed and the test
+                                        case was successful.
+  @retval  UNIT_TEST_ERROR_TEST_FAILED  A test case assertion has failed.
+**/
+UNIT_TEST_STATUS
+EFIAPI
+GlobalBooleanShouldBeChangeable (
+  IN UNIT_TEST_CONTEXT  Context
+  )
+{
+  mSampleGlobalTestBoolean = TRUE;
+  UT_ASSERT_TRUE (mSampleGlobalTestBoolean);
+
+  mSampleGlobalTestBoolean = FALSE;
+  UT_ASSERT_FALSE (mSampleGlobalTestBoolean);
+
+  return UNIT_TEST_PASSED;
+}
+
+/**
+  Sample unit test that logs a warning message and verifies that a global
+  pointer is updatable.
+
+  @param[in]  Context    [Optional] An optional parameter that enables:
+                         1) test-case reuse with varied parameters and
+                         2) test-case re-entry for Target tests that need a
+                         reboot.  This parameter is a VOID* and it is the
+                         responsibility of the test author to ensure that the
+                         contents are well understood by all test cases that may
+                         consume it.
+
+  @retval  UNIT_TEST_PASSED             The Unit test has completed and the test
+                                        case was successful.
+  @retval  UNIT_TEST_ERROR_TEST_FAILED  A test case assertion has failed.
+**/
+UNIT_TEST_STATUS
+EFIAPI
+GlobalPointerShouldBeChangeable (
+  IN UNIT_TEST_CONTEXT  Context
+  )
+{
+  //
+  // Example of logging.
+  //
+  UT_LOG_WARNING ("About to change a global pointer! Current value is 0x%X\n", mSampleGlobalTestPointer);
+
+  mSampleGlobalTestPointer = (VOID *)-1;
+  UT_ASSERT_EQUAL ((UINTN)mSampleGlobalTestPointer, (UINTN)((VOID *)-1));
+  return UNIT_TEST_PASSED;
+}
+
+/**
+  Initialize the unit test framework, suite, and unit tests for the
+  sample unit tests and run the unit tests.
+
+  @retval  EFI_SUCCESS           All test cases were dispatched.
+  @retval  EFI_OUT_OF_RESOURCES  There are not enough resources available to
+                                 initialize the unit tests.
+**/
+EFI_STATUS
+EFIAPI
+UefiTestMain (
+  VOID
+  )
+{
+  EFI_STATUS                  Status;
+  UNIT_TEST_FRAMEWORK_HANDLE  Framework;
+  UNIT_TEST_SUITE_HANDLE      SimpleMathTests;
+  UNIT_TEST_SUITE_HANDLE      GlobalVarTests;
+
+  Framework = NULL;
+
+  DEBUG(( DEBUG_INFO, "%a v%a\n", UNIT_TEST_NAME, UNIT_TEST_VERSION ));
+
+  //
+  // Start setting up the test framework for running the tests.
+  //
+  Status = InitUnitTestFramework (&Framework, UNIT_TEST_NAME, gEfiCallerBaseName, UNIT_TEST_VERSION);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "Failed in InitUnitTestFramework. Status = %r\n", Status));
+    goto EXIT;
+  }
+
+  //
+  // Populate the SimpleMathTests Unit Test Suite.
+  //
+  Status = CreateUnitTestSuite (&SimpleMathTests, Framework, "Simple Math Tests", "Sample.Math", NULL, NULL);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for SimpleMathTests\n"));
+    Status = EFI_OUT_OF_RESOURCES;
+    goto EXIT;
+  }
+  AddTestCase (SimpleMathTests, "Adding 1 to 1 should produce 2", "Addition", OnePlusOneShouldEqualTwo, NULL, NULL, NULL);
+
+  //
+  // Populate the GlobalVarTests Unit Test Suite.
+  //
+  Status = CreateUnitTestSuite (&GlobalVarTests, Framework, "Global Variable Tests", "Sample.Globals", NULL, NULL);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for GlobalVarTests\n"));
+    Status = EFI_OUT_OF_RESOURCES;
+    goto EXIT;
+  }
+  AddTestCase (GlobalVarTests, "You should be able to change a global BOOLEAN", "Boolean", GlobalBooleanShouldBeChangeable, NULL, NULL, NULL);
+  AddTestCase (GlobalVarTests, "You should be able to change a global pointer", "Pointer", GlobalPointerShouldBeChangeable, MakeSureThatPointerIsNull, ClearThePointer, NULL);
+
+  //
+  // Execute the tests.
+  //
+  Status = RunAllTestSuites (Framework);
+
+EXIT:
+  if (Framework) {
+    FreeUnitTestFramework (Framework);
+  }
+
+  return Status;
+}
+
+/**
+  Standard PEIM entry point for target based unit test execution from PEI.
+**/
+EFI_STATUS
+EFIAPI
+PeiEntryPoint (
+  IN EFI_PEI_FILE_HANDLE     FileHandle,
+  IN CONST EFI_PEI_SERVICES  **PeiServices
+  )
+{
+  return UefiTestMain ();
+}
+
+/**
+  Standard UEFI entry point for target based unit test execution from DXE, SMM,
+  UEFI Shell.
+**/
+EFI_STATUS
+EFIAPI
+DxeEntryPoint (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  return UefiTestMain ();
+}
+
+/**
+  Standard POSIX C entry point for host based unit test execution.
+**/
+int
+main (
+  int argc,
+  char *argv[]
+  )
+{
+  return UefiTestMain ();
+}
diff --git a/UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestDxe.inf b/UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestDxe.inf
new file mode 100644
index 0000000000..e253cf6e16
--- /dev/null
+++ b/UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestDxe.inf
@@ -0,0 +1,36 @@
+## @file
+# Sample UnitTest built for execution in DXE.
+#
+# Copyright (c) Microsoft Corporation.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+  INF_VERSION    = 0x00010006
+  BASE_NAME      = SampleUnitTestDxe
+  FILE_GUID      = 96BB18BD-FF2B-4B51-B683-0DC9A4BF12CF
+  MODULE_TYPE    = DXE_DRIVER
+  VERSION_STRING = 1.0
+  ENTRY_POINT    = DxeEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+
+[Sources]
+  SampleUnitTest.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  UefiDriverEntryPoint
+  BaseLib
+  DebugLib
+  UnitTestLib
+  PrintLib
+
+[Depex]
+  TRUE
diff --git a/UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestHost.inf b/UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestHost.inf
new file mode 100644
index 0000000000..59a367cc6e
--- /dev/null
+++ b/UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestHost.inf
@@ -0,0 +1,30 @@
+## @file
+# Sample UnitTest built for execution on a Host/Dev machine.
+#
+# Copyright (c) Microsoft Corporation.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+  INF_VERSION    = 0x00010005
+  BASE_NAME      = SampleUnitTestHost
+  FILE_GUID      = CC0EA77E-BF2D-4134-B419-0C02E15CE08E
+  MODULE_TYPE    = HOST_APPLICATION
+  VERSION_STRING = 1.0
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+
+[Sources]
+  SampleUnitTest.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  BaseLib
+  DebugLib
+  UnitTestLib
diff --git a/UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestPei.inf b/UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestPei.inf
new file mode 100644
index 0000000000..60f5252c34
--- /dev/null
+++ b/UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestPei.inf
@@ -0,0 +1,36 @@
+## @file
+# Sample UnitTest built for execution in PEI.
+#
+# Copyright (c) Microsoft Corporation.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+  INF_VERSION    = 0x00010006
+  BASE_NAME      = SampleUnitTestPei
+  FILE_GUID      = B9BD9451-3DC8-48EA-A6F0-55753BF186F1
+  MODULE_TYPE    = PEIM
+  VERSION_STRING = 1.0
+  ENTRY_POINT    = PeiEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+
+[Sources]
+  SampleUnitTest.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  PeimEntryPoint
+  BaseLib
+  DebugLib
+  UnitTestLib
+  PrintLib
+
+[Depex]
+  gEfiPeiMemoryDiscoveredPpiGuid
diff --git a/UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestSmm.inf b/UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestSmm.inf
new file mode 100644
index 0000000000..324ad2686e
--- /dev/null
+++ b/UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestSmm.inf
@@ -0,0 +1,37 @@
+## @file
+# Sample UnitTest built for execution in SMM.
+#
+# Copyright (c) Microsoft Corporation.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+  INF_VERSION              = 0x00010006
+  BASE_NAME                = SampleUnitTestSmm
+  FILE_GUID                = 389B16DB-F622-424C-9000-9E43C69CBF71
+  MODULE_TYPE              = DXE_SMM_DRIVER
+  VERSION_STRING           = 1.0
+  PI_SPECIFICATION_VERSION = 0x0001000A
+  ENTRY_POINT              = DxeEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+
+[Sources]
+  SampleUnitTest.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  UefiDriverEntryPoint
+  BaseLib
+  DebugLib
+  UnitTestLib
+  PrintLib
+
+[Depex]
+  gEfiSmmCpuProtocolGuid
diff --git a/UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestUefiShell.inf b/UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestUefiShell.inf
new file mode 100644
index 0000000000..6e39c229d4
--- /dev/null
+++ b/UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestUefiShell.inf
@@ -0,0 +1,33 @@
+## @file
+# Sample UnitTest built for execution in UEFI Shell.
+#
+# Copyright (c) Microsoft Corporation.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+  INF_VERSION    = 0x00010006
+  BASE_NAME      = SampleUnitTestUefiShell
+  FILE_GUID      = 9E8F461A-17E1-4312-B49C-E66F0A88EA8B
+  MODULE_TYPE    = UEFI_APPLICATION
+  VERSION_STRING = 1.0
+  ENTRY_POINT    = DxeEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+
+[Sources]
+  SampleUnitTest.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  UefiApplicationEntryPoint
+  BaseLib
+  DebugLib
+  UnitTestLib
+  PrintLib
-- 
2.21.0.windows.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [Patch v2 07/11] UnitTestFrameworkPkg: Add DSC, DSC INC, and YAML files
  2020-02-07 18:13 [Patch v2 00/11] Add Unit Test Framework Michael D Kinney
                   ` (5 preceding siblings ...)
  2020-02-07 18:13 ` [Patch v2 06/11] UnitTestFrameworkPkg/Test: Add unit test samples Michael D Kinney
@ 2020-02-07 18:13 ` Michael D Kinney
  2020-02-07 18:54   ` [edk2-devel] " Sean
  2020-02-07 18:13 ` [Patch v2 08/11] MdePkg/Test: Add SafeIntLib and BaseLib Base64 unit tests Michael D Kinney
                   ` (3 subsequent siblings)
  10 siblings, 1 reply; 13+ messages in thread
From: Michael D Kinney @ 2020-02-07 18:13 UTC (permalink / raw)
  To: devel; +Cc: Sean Brogan, Bret Barkelew

https://bugzilla.tianocore.org/show_bug.cgi?id=2505

* DSC in root of package file to perform a package
  build of the UnitTestFrameworkPkg and build sample
  unit test for all supported target environments.
* DSC file in Test directory to build the sample
  unit test for the host environment.
* UnitTestFrameworkPkgTarget.dsc.inc - DSC include
  file to !include when building unit tests for
  target environments.
* UnitTestFrameworkPkgHost.dsc.inc - DSC include
  file to !include when building unit tests for
  target environments.
* YAML file with set of CI checks to perform on UnitTestFrameworkPkg.

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>
---
 .../Test/UnitTestFrameworkPkgHostTest.dsc     | 33 ++++++++
 .../UnitTestFrameworkPkg.ci.yaml              | 76 +++++++++++++++++++
 UnitTestFrameworkPkg/UnitTestFrameworkPkg.dsc | 34 +++++++++
 .../UnitTestFrameworkPkgHost.dsc.inc          | 56 ++++++++++++++
 .../UnitTestFrameworkPkgTarget.dsc.inc        | 58 ++++++++++++++
 5 files changed, 257 insertions(+)
 create mode 100644 UnitTestFrameworkPkg/Test/UnitTestFrameworkPkgHostTest.dsc
 create mode 100644 UnitTestFrameworkPkg/UnitTestFrameworkPkg.ci.yaml
 create mode 100644 UnitTestFrameworkPkg/UnitTestFrameworkPkg.dsc
 create mode 100644 UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc
 create mode 100644 UnitTestFrameworkPkg/UnitTestFrameworkPkgTarget.dsc.inc

diff --git a/UnitTestFrameworkPkg/Test/UnitTestFrameworkPkgHostTest.dsc b/UnitTestFrameworkPkg/Test/UnitTestFrameworkPkgHostTest.dsc
new file mode 100644
index 0000000000..701e7299d7
--- /dev/null
+++ b/UnitTestFrameworkPkg/Test/UnitTestFrameworkPkgHostTest.dsc
@@ -0,0 +1,33 @@
+## @file
+# UnitTestFrameworkPkg DSC file used to build host-based unit tests.
+#
+# Copyright (c) Microsoft Corporation.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  PLATFORM_NAME           = UnitTestFrameworkPkgHostTest
+  PLATFORM_GUID           = C7F97D6D-54AC-45A9-8197-CC99B20CC7EC
+  PLATFORM_VERSION        = 0.1
+  DSC_SPECIFICATION       = 0x00010005
+  OUTPUT_DIRECTORY        = Build/UnitTestFrameworkPkg/HostTest
+  SUPPORTED_ARCHITECTURES = IA32|X64
+  BUILD_TARGETS           = NOOPT
+  SKUID_IDENTIFIER        = DEFAULT
+
+!include UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc
+
+[Components]
+  #
+  # Build HOST_APPLICATION that tests the SampleUnitTest
+  #
+  UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestHost.inf
+
+  #
+  # Build Libraries
+  #
+  UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.inf
+  UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.inf
+  UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.inf
+  UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLibCmocka.inf
diff --git a/UnitTestFrameworkPkg/UnitTestFrameworkPkg.ci.yaml b/UnitTestFrameworkPkg/UnitTestFrameworkPkg.ci.yaml
new file mode 100644
index 0000000000..0164859505
--- /dev/null
+++ b/UnitTestFrameworkPkg/UnitTestFrameworkPkg.ci.yaml
@@ -0,0 +1,76 @@
+## @file
+# CI configuration for UnitTestFrameworkPkg
+#
+# Copyright (c) Microsoft Corporation
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+{
+    ## options defined .pytool/Plugin/CompilerPlugin
+    "CompilerPlugin": {
+        "DscPath": "UnitTestFrameworkPkg.dsc"
+    },
+    ## options defined .pytool/Plugin/HostUnitTestCompilerPlugin
+    "HostUnitTestCompilerPlugin": {
+        "DscPath": "Test/UnitTestFrameworkPkgHostTest.dsc"
+    },
+    ## options defined .pytool/Plugin/CharEncodingCheck
+    "CharEncodingCheck": {
+        "IgnoreFiles": []
+    },
+
+    ## options defined .pytool/Plugin/DependencyCheck
+    "DependencyCheck": {
+        "AcceptableDependencies": [
+            "MdePkg/MdePkg.dec",
+            "UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec"
+        ],
+        # For host based unit tests
+        "AcceptableDependencies-HOST_APPLICATION":[],
+        # For UEFI shell based apps
+        "AcceptableDependencies-UEFI_APPLICATION":[
+            "MdeModulePkg/MdeModulePkg.dec",
+            "ShellPkg/ShellPkg.dec"
+        ],
+        "IgnoreInf": []
+    },
+    ## options defined .pytool/Plugin/DscCompleteCheck
+    "DscCompleteCheck": {
+        "DscPath": "UnitTestFrameworkPkg.dsc",
+        "IgnoreInf": []
+    },
+    ## options defined .pytool/Plugin/HostUnitTestDscCompleteCheck
+    "HostUnitTestDscCompleteCheck": {
+        "IgnoreInf": [],
+        "DscPath": "Test/UnitTestFrameworkPkgHostTest.dsc"
+    },
+    ## options defined .pytool/Plugin/GuidCheck
+    "GuidCheck": {
+        "IgnoreGuidName": [],
+        "IgnoreGuidValue": [],
+        "IgnoreFoldersAndFiles": [],
+        "IgnoreDuplicates": []
+    },
+    ## options defined .pytool/Plugin/LibraryClassCheck
+    "LibraryClassCheck": {
+        "IgnoreHeaderFile": []
+    },
+
+    ## options defined .pytool/Plugin/SpellCheck
+    "SpellCheck": {
+        "AuditOnly": False,           # Fails test but run in AuditOnly mode to collect log
+        "IgnoreFiles": [             # use gitignore syntax to ignore errors in matching files
+            "/Library/CmockaLib/cmocka/**/*.*"  # not going to spell check a submodule
+        ],
+        "ExtendWords": [             # words to extend to the dictionary for this package
+            "cmocka",
+            "buildmodule",
+            "criterium",
+            "pytool",
+            "pytools",
+            "NOFAILURE",
+            "DHAVE" # build flag for cmocka in the INF
+        ],
+        "IgnoreStandardPaths": [],   # Standard Plugin defined paths that should be ignore
+        "AdditionalIncludePaths": [] # Additional paths to spell check (wildcards supported)
+    }
+}
diff --git a/UnitTestFrameworkPkg/UnitTestFrameworkPkg.dsc b/UnitTestFrameworkPkg/UnitTestFrameworkPkg.dsc
new file mode 100644
index 0000000000..0ad4482273
--- /dev/null
+++ b/UnitTestFrameworkPkg/UnitTestFrameworkPkg.dsc
@@ -0,0 +1,34 @@
+## @file
+# UnitTestFrameworkPkg
+#
+# Copyright (c) 2019 - 2020, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  PLATFORM_NAME           = UnitTestFrameworkPkg
+  PLATFORM_GUID           = 7420CC7E-334E-4EFF-B974-A39613455168
+  PLATFORM_VERSION        = 1.00
+  DSC_SPECIFICATION       = 0x00010005
+  OUTPUT_DIRECTORY        = Build/UnitTestFrameworkPkg
+  SUPPORTED_ARCHITECTURES = IA32|X64|EBC|ARM|AARCH64
+  BUILD_TARGETS           = DEBUG|RELEASE|NOOPT
+  SKUID_IDENTIFIER        = DEFAULT
+
+!include UnitTestFrameworkPkg/UnitTestFrameworkPkgTarget.dsc.inc
+
+[Components]
+  UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.inf
+  UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.inf
+  UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.inf
+  UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootLibNull.inf
+  UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.inf
+  UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibUsbClass.inf
+  UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.inf
+
+  UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestDxe.inf
+  UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestPei.inf
+  UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestSmm.inf
+  UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestUefiShell.inf
diff --git a/UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc b/UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc
new file mode 100644
index 0000000000..e954968efc
--- /dev/null
+++ b/UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc
@@ -0,0 +1,56 @@
+## @file
+# UnitTestFrameworkPkg DSC include file for host based test DSC
+#
+# Copyright (c) 2019 - 2020, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+!include UnitTestFrameworkPkg/UnitTestFrameworkPkgTarget.dsc.inc
+
+[LibraryClasses.common.HOST_APPLICATION]
+  CmockaLib|UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.inf
+  UnitTestLib|UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLibCmocka.inf
+  DebugLib|UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.inf
+  MemoryAllocationLib|UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.inf
+
+[BuildOptions]
+  GCC:*_*_*_CC_FLAGS = -fno-pie
+
+[BuildOptions.common.EDKII.HOST_APPLICATION]
+  #
+  # MSFT
+  #
+  MSFT:*_*_*_DLINK_FLAGS            == /out:"$(BIN_DIR)\$(BASE_NAME).exe" /pdb:"$(BIN_DIR)\$(BASE_NAME).pdb" /IGNORE:4001 /NOLOGO /SUBSYSTEM:CONSOLE /DEBUG /NODEFAULTLIB:libcmt.lib libcmtd.lib
+  MSFT:*_*_IA32_DLINK_FLAGS         = /MACHINE:I386
+  MSFT:*_*_X64_DLINK_FLAGS          = /MACHINE:AMD64
+
+  MSFT:*_VS2015_IA32_DLINK_FLAGS    = /LIBPATH:"%VS2015_PREFIX%Lib" /LIBPATH:"%VS2015_PREFIX%VC\Lib" /LIBPATH:"%UniversalCRTSdkDir%lib\%UCRTVersion%\ucrt\x86" /LIBPATH:"%WindowsSdkDir%lib\%WindowsSDKLibVersion%\um\x86"
+  MSFT:*_VS2015x86_IA32_DLINK_FLAGS = /LIBPATH:"%VS2015_PREFIX%Lib" /LIBPATH:"%VS2015_PREFIX%VC\Lib" /LIBPATH:"%UniversalCRTSdkDir%lib\%UCRTVersion%\ucrt\x86" /LIBPATH:"%WindowsSdkDir%lib\%WindowsSDKLibVersion%\um\x86"
+  MSFT:*_VS2017_IA32_DLINK_FLAGS    = /LIBPATH:"%VCToolsInstallDir%lib\x86" /LIBPATH:"%UniversalCRTSdkDir%lib\%UCRTVersion%\ucrt\x86" /LIBPATH:"%WindowsSdkDir%lib\%WindowsSDKLibVersion%\um\x86"
+  MSFT:*_VS2019_IA32_DLINK_FLAGS    = /LIBPATH:"%VCToolsInstallDir%lib\x86" /LIBPATH:"%UniversalCRTSdkDir%lib\%UCRTVersion%\ucrt\x86" /LIBPATH:"%WindowsSdkDir%lib\%WindowsSDKLibVersion%\um\x86"
+
+  MSFT:*_VS2015_X64_DLINK_FLAGS     = /LIBPATH:"%VS2015_PREFIX%VC\Lib\AMD64" /LIBPATH:"%UniversalCRTSdkDir%lib\%UCRTVersion%\ucrt\x64" /LIBPATH:"%WindowsSdkDir%lib\%WindowsSDKLibVersion%\um\x64"
+  MSFT:*_VS2015x86_X64_DLINK_FLAGS  = /LIBPATH:"%VS2015_PREFIX%VC\Lib\AMD64" /LIBPATH:"%UniversalCRTSdkDir%lib\%UCRTVersion%\ucrt\x64" /LIBPATH:"%WindowsSdkDir%lib\%WindowsSDKLibVersion%\um\x64"
+  MSFT:*_VS2017_X64_DLINK_FLAGS     = /LIBPATH:"%VCToolsInstallDir%lib\x64" /LIBPATH:"%UniversalCRTSdkDir%lib\%UCRTVersion%\ucrt\x64" /LIBPATH:"%WindowsSdkDir%lib\%WindowsSDKLibVersion%\um\x64"
+  MSFT:*_VS2019_X64_DLINK_FLAGS     = /LIBPATH:"%VCToolsInstallDir%lib\x64" /LIBPATH:"%UniversalCRTSdkDir%lib\%UCRTVersion%\ucrt\x64" /LIBPATH:"%WindowsSdkDir%lib\%WindowsSDKLibVersion%\um\x64"
+
+  #
+  # GCC
+  #
+  GCC:*_*_IA32_DLINK_FLAGS == -o $(BIN_DIR)/$(BASE_NAME) -m32 -no-pie
+  GCC:*_*_X64_DLINK_FLAGS  == -o $(BIN_DIR)/$(BASE_NAME) -m64 -no-pie
+  GCC:*_*_*_DLINK2_FLAGS   == -lgcov
+
+  #
+  # Need to do this link via gcc and not ld as the pathing to libraries changes from OS version to OS version
+  #
+  XCODE:*_*_IA32_DLINK_PATH == gcc
+  XCODE:*_*_IA32_CC_FLAGS = -I$(WORKSPACE)/EmulatorPkg/Unix/Host/X11IncludeHack
+  XCODE:*_*_IA32_DLINK_FLAGS == -arch i386 -o $(BIN_DIR)/Host -L/usr/X11R6/lib -lXext -lX11 -framework Carbon
+  XCODE:*_*_IA32_ASM_FLAGS == -arch i386 -g
+
+  XCODE:*_*_X64_DLINK_PATH == gcc
+  XCODE:*_*_X64_DLINK_FLAGS == -o $(BIN_DIR)/Host -L/usr/X11R6/lib -lXext -lX11 -framework Carbon -Wl,-no_pie
+  XCODE:*_*_X64_ASM_FLAGS == -g
+  XCODE:*_*_X64_CC_FLAGS = -O0 -target x86_64-apple-darwin -I$(WORKSPACE)/EmulatorPkg/Unix/Host/X11IncludeHack "-DEFIAPI=__attribute__((ms_abi))"
diff --git a/UnitTestFrameworkPkg/UnitTestFrameworkPkgTarget.dsc.inc b/UnitTestFrameworkPkg/UnitTestFrameworkPkgTarget.dsc.inc
new file mode 100644
index 0000000000..c29b056fca
--- /dev/null
+++ b/UnitTestFrameworkPkg/UnitTestFrameworkPkgTarget.dsc.inc
@@ -0,0 +1,58 @@
+## @file
+# UnitTestFrameworkPkg DSC include file for target based test DSC
+#
+# Copyright (c) 2019 - 2020, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[LibraryClasses]
+  #
+  # Entry point
+  #
+  PeimEntryPoint|MdePkg/Library/PeimEntryPoint/PeimEntryPoint.inf
+  UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf
+  UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf
+
+  BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
+  BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
+  DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf
+  MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+  PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
+  PeiServicesLib|MdePkg/Library/PeiServicesLib/PeiServicesLib.inf
+  PerformanceLib|MdePkg/Library/BasePerformanceLibNull/BasePerformanceLibNull.inf
+  PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
+  UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf
+
+  UnitTestLib|UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.inf
+  UnitTestPersistenceLib|UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.inf
+  UnitTestResultReportLib|UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.inf
+
+[LibraryClasses.ARM, LibraryClasses.AARCH64]
+  #
+  # It is not possible to prevent ARM compiler calls to generic intrinsic functions.
+  # This library provides the instrinsic functions generated by a given compiler.
+  # [LibraryClasses.ARM] and NULL mean link this library into all ARM images.
+  #
+  NULL|ArmPkg/Library/CompilerIntrinsicsLib/CompilerIntrinsicsLib.inf
+
+  #
+  # Since software stack checking may be heuristically enabled by the compiler
+  # include BaseStackCheckLib unconditionally.
+  #
+  NULL|MdePkg/Library/BaseStackCheckLib/BaseStackCheckLib.inf
+
+[LibraryClasses.common.PEIM]
+  HobLib|MdePkg/Library/PeiHobLib/PeiHobLib.inf
+  MemoryAllocationLib|MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf
+  PeiServicesTablePointerLib|MdePkg/Library/PeiServicesTablePointerLib/PeiServicesTablePointerLib.inf
+
+[LibraryClasses.common.UEFI_APPLICATION]
+  UnitTestResultReportLib|UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.inf
+
+[PcdsFixedAtBuild]
+  gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x17
+
+[BuildOptions]
+  MSFT:*_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES
+  GCC:*_*_*_CC_FLAGS  = -D DISABLE_NEW_DEPRECATED_INTERFACES
-- 
2.21.0.windows.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [Patch v2 08/11] MdePkg/Test: Add SafeIntLib and BaseLib Base64 unit tests
  2020-02-07 18:13 [Patch v2 00/11] Add Unit Test Framework Michael D Kinney
                   ` (6 preceding siblings ...)
  2020-02-07 18:13 ` [Patch v2 07/11] UnitTestFrameworkPkg: Add DSC, DSC INC, and YAML files Michael D Kinney
@ 2020-02-07 18:13 ` Michael D Kinney
  2020-02-07 18:13 ` [Patch v2 09/11] MdeModulePkg: Add DxeResetSystemLib unit test Michael D Kinney
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Michael D Kinney @ 2020-02-07 18:13 UTC (permalink / raw)
  To: devel; +Cc: Sean Brogan, Bret Barkelew, Liming Gao, Hao A Wu

https://bugzilla.tianocore.org/show_bug.cgi?id=2505

* Add unit tests for SafeIntLib class
* Add unit tests for BaseLib Base64 conversion APIs.
* Add Test/MdePkgHostTest.dsc -to build host based unit
  tests
* Update MdePkg.dsc to build target based tests for
  SafeIntLib and BaseLib
* Update MdePkg.ci.yaml to build and run host based
  tests for SafeIntLib and BaseLib

Cc: Sean Brogan <sean.brogan@microsoft.com>
Cc: Bret Barkelew <Bret.Barkelew@microsoft.com>
Cc: Liming Gao <liming.gao@intel.com>
Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com>
Acked-by: Hao A Wu <hao.a.wu@intel.com>
Reviewed-by: Bret Barkelew <Bret.Barkelew@microsoft.com>
---
 MdePkg/MdePkg.ci.yaml                         |   19 +-
 MdePkg/MdePkg.dsc                             |   18 +
 MdePkg/Test/MdePkgHostTest.dsc                |   30 +
 .../UnitTest/Library/BaseLib/Base64UnitTest.c |  404 +++
 .../Library/BaseLib/BaseLibUnitTestsHost.inf  |   32 +
 .../Library/BaseLib/BaseLibUnitTestsUefi.inf  |   33 +
 .../SafeIntLibUintnIntnUnitTests32.c          |  540 +++
 .../SafeIntLibUintnIntnUnitTests64.c          |  544 +++
 .../BaseSafeIntLib/TestBaseSafeIntLib.c       | 3064 +++++++++++++++++
 .../BaseSafeIntLib/TestBaseSafeIntLib.h       |  123 +
 .../BaseSafeIntLib/TestBaseSafeIntLib.uni     |   13 +
 .../BaseSafeIntLib/TestBaseSafeIntLibDxe.inf  |   45 +
 .../BaseSafeIntLib/TestBaseSafeIntLibHost.inf |   40 +
 .../BaseSafeIntLib/TestBaseSafeIntLibPei.inf  |   45 +
 .../BaseSafeIntLib/TestBaseSafeIntLibSmm.inf  |   45 +
 .../TestBaseSafeIntLibUefiShell.inf           |   42 +
 16 files changed, 5034 insertions(+), 3 deletions(-)
 create mode 100644 MdePkg/Test/MdePkgHostTest.dsc
 create mode 100644 MdePkg/Test/UnitTest/Library/BaseLib/Base64UnitTest.c
 create mode 100644 MdePkg/Test/UnitTest/Library/BaseLib/BaseLibUnitTestsHost.inf
 create mode 100644 MdePkg/Test/UnitTest/Library/BaseLib/BaseLibUnitTestsUefi.inf
 create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/SafeIntLibUintnIntnUnitTests32.c
 create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/SafeIntLibUintnIntnUnitTests64.c
 create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLib.c
 create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLib.h
 create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLib.uni
 create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibDxe.inf
 create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibHost.inf
 create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibPei.inf
 create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibSmm.inf
 create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibUefiShell.inf

diff --git a/MdePkg/MdePkg.ci.yaml b/MdePkg/MdePkg.ci.yaml
index 65c4ec3bec..88e490fcb6 100644
--- a/MdePkg/MdePkg.ci.yaml
+++ b/MdePkg/MdePkg.ci.yaml
@@ -10,8 +10,13 @@
         "DscPath": "MdePkg.dsc"
     },
 
+    ## options defined ci/Plugin/HostUnitTestCompilerPlugin
+    "HostUnitTestCompilerPlugin": {
+        "DscPath": "Test/MdePkgHostTest.dsc"
+    },
+
     ## options defined ci/Plugin/CharEncodingCheck
-"CharEncodingCheck": {
+    "CharEncodingCheck": {
         "IgnoreFiles": []
     },
 
@@ -21,7 +26,9 @@
             "MdePkg/MdePkg.dec"
         ],
         # For host based unit tests
-        "AcceptableDependencies-HOST_APPLICATION":[],
+        "AcceptableDependencies-HOST_APPLICATION":[
+            "UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec"
+        ],
         # For UEFI shell based apps
         "AcceptableDependencies-UEFI_APPLICATION":[],
         "IgnoreInf": []
@@ -29,10 +36,16 @@
 
     ## options defined ci/Plugin/DscCompleteCheck
     "DscCompleteCheck": {
-        "IgnoreInf": [],
+        "IgnoreInf": [""],
         "DscPath": "MdePkg.dsc"
     },
 
+    ## options defined ci/Plugin/HostUnitTestDscCompleteCheck
+    "HostUnitTestDscCompleteCheck": {
+        "IgnoreInf": [""],
+        "DscPath": "Test/MdePkgHostTest.dsc"
+    },
+
     ## options defined ci/Plugin/GuidCheck
     "GuidCheck": {
         "IgnoreGuidName": [
diff --git a/MdePkg/MdePkg.dsc b/MdePkg/MdePkg.dsc
index 0aeafaaacc..87af740853 100644
--- a/MdePkg/MdePkg.dsc
+++ b/MdePkg/MdePkg.dsc
@@ -18,6 +18,8 @@ [Defines]
   BUILD_TARGETS                  = DEBUG|RELEASE|NOOPT
   SKUID_IDENTIFIER               = DEFAULT
 
+!include UnitTestFrameworkPkg/UnitTestFrameworkPkgTarget.dsc.inc
+
 [PcdsFeatureFlag]
   gEfiMdePkgTokenSpaceGuid.PcdUgaConsumeSupport|TRUE
 
@@ -26,6 +28,9 @@ [PcdsFixedAtBuild]
   gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x80000000
   gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress|0xE0000000
 
+[LibraryClasses]
+  SafeIntLib|MdePkg/Library/BaseSafeIntLib/BaseSafeIntLib.inf
+
 [Components]
   MdePkg/Library/UefiFileHandleLib/UefiFileHandleLib.inf
   MdePkg/Library/BaseCacheMaintenanceLib/BaseCacheMaintenanceLib.inf
@@ -115,6 +120,19 @@ [Components]
   MdePkg/Library/StandaloneMmDriverEntryPoint/StandaloneMmDriverEntryPoint.inf
   MdePkg/Library/StandaloneMmServicesTableLib/StandaloneMmServicesTableLib.inf
 
+  #
+  # Add UEFI Target Based Unit Tests
+  #
+  MdePkg/Test/UnitTest/Library/BaseLib/BaseLibUnitTestsUefi.inf
+
+  #
+  # Build PEIM, DXE_DRIVER, SMM_DRIVER, UEFI Shell components that test SafeIntLib
+  #
+  MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibPei.inf
+  MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibDxe.inf
+  MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibSmm.inf
+  MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibUefiShell.inf
+
 [Components.IA32, Components.X64]
   MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf
   MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicSev.inf
diff --git a/MdePkg/Test/MdePkgHostTest.dsc b/MdePkg/Test/MdePkgHostTest.dsc
new file mode 100644
index 0000000000..3d677ee75c
--- /dev/null
+++ b/MdePkg/Test/MdePkgHostTest.dsc
@@ -0,0 +1,30 @@
+## @file
+# MdePkg DSC file used to build host-based unit tests.
+#
+# Copyright (c) 2019 - 2020, Intel Corporation. All rights reserved.<BR>
+# Copyright (C) Microsoft Corporation.
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  PLATFORM_NAME           = MdePkgHostTest
+  PLATFORM_GUID           = 50652B4C-88CB-4481-96E8-37F2D0034440
+  PLATFORM_VERSION        = 0.1
+  DSC_SPECIFICATION       = 0x00010005
+  OUTPUT_DIRECTORY        = Build/MdePkg/HostTest
+  SUPPORTED_ARCHITECTURES = IA32|X64
+  BUILD_TARGETS           = NOOPT
+  SKUID_IDENTIFIER        = DEFAULT
+
+!include UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc
+
+[LibraryClasses]
+  SafeIntLib|MdePkg/Library/BaseSafeIntLib/BaseSafeIntLib.inf
+
+[Components]
+  #
+  # Build HOST_APPLICATION that tests the SafeIntLib
+  #
+  MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibHost.inf
+  MdePkg/Test/UnitTest/Library/BaseLib/BaseLibUnitTestsHost.inf
diff --git a/MdePkg/Test/UnitTest/Library/BaseLib/Base64UnitTest.c b/MdePkg/Test/UnitTest/Library/BaseLib/Base64UnitTest.c
new file mode 100644
index 0000000000..6f7c31cab4
--- /dev/null
+++ b/MdePkg/Test/UnitTest/Library/BaseLib/Base64UnitTest.c
@@ -0,0 +1,404 @@
+/** @file
+  Unit tests of Base64 conversion APIs in BaseLib.
+
+  Copyright (C) Microsoft Corporation.
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UnitTestLib.h>
+
+#define UNIT_TEST_APP_NAME     "BaseLib Unit Test Application"
+#define UNIT_TEST_APP_VERSION  "1.0"
+
+/**
+  RFC 4648  https://tools.ietf.org/html/rfc4648 test vectors
+
+  BASE64("") = ""
+  BASE64("f") = "Zg=="
+  BASE64("fo") = "Zm8="
+  BASE64("foo") = "Zm9v"
+  BASE64("foob") = "Zm9vYg=="
+  BASE64("fooba") = "Zm9vYmE="
+  BASE64("foobar") = "Zm9vYmFy"
+
+  The test vectors are using ascii strings for the binary data
+ */
+
+typedef struct {
+    CHAR8      *TestInput;
+    CHAR8      *TestOutput;
+    EFI_STATUS  ExpectedStatus;
+    VOID       *BufferToFree;
+    UINTN       ExpectedSize;
+} BASIC_TEST_CONTEXT;
+
+#define B64_TEST_1     ""
+#define BIN_TEST_1     ""
+
+#define B64_TEST_2     "Zg=="
+#define BIN_TEST_2     "f"
+
+#define B64_TEST_3     "Zm8="
+#define BIN_TEST_3     "fo"
+
+#define B64_TEST_4     "Zm9v"
+#define BIN_TEST_4     "foo"
+
+#define B64_TEST_5     "Zm9vYg=="
+#define BIN_TEST_5     "foob"
+
+#define B64_TEST_6     "Zm9vYmE="
+#define BIN_TEST_6     "fooba"
+
+#define B64_TEST_7     "Zm9vYmFy"
+#define BIN_TEST_7     "foobar"
+
+// Adds all white space - also ends the last quantum with only spaces afterwards
+#define B64_TEST_8_IN   " \t\v  Zm9\r\nvYmFy \f  "
+#define BIN_TEST_8      "foobar"
+
+// Not a quantum multiple of 4
+#define B64_ERROR_1  "Zm9vymFy="
+
+// Invalid characters in the string
+#define B64_ERROR_2  "Zm$vymFy"
+
+// Too many '=' characters
+#define B64_ERROR_3 "Z==="
+
+// Poorly placed '='
+#define B64_ERROR_4 "Zm=vYmFy"
+
+#define MAX_TEST_STRING_SIZE (200)
+
+// ------------------------------------------------ Input----------Output-----------Result-------Free--Expected Output Size
+static BASIC_TEST_CONTEXT    mBasicEncodeTest1  = {BIN_TEST_1,     B64_TEST_1,      EFI_SUCCESS, NULL, sizeof(B64_TEST_1)};
+static BASIC_TEST_CONTEXT    mBasicEncodeTest2  = {BIN_TEST_2,     B64_TEST_2,      EFI_SUCCESS, NULL, sizeof(B64_TEST_2)};
+static BASIC_TEST_CONTEXT    mBasicEncodeTest3  = {BIN_TEST_3,     B64_TEST_3,      EFI_SUCCESS, NULL, sizeof(B64_TEST_3)};
+static BASIC_TEST_CONTEXT    mBasicEncodeTest4  = {BIN_TEST_4,     B64_TEST_4,      EFI_SUCCESS, NULL, sizeof(B64_TEST_4)};
+static BASIC_TEST_CONTEXT    mBasicEncodeTest5  = {BIN_TEST_5,     B64_TEST_5,      EFI_SUCCESS, NULL, sizeof(B64_TEST_5)};
+static BASIC_TEST_CONTEXT    mBasicEncodeTest6  = {BIN_TEST_6,     B64_TEST_6,      EFI_SUCCESS, NULL, sizeof(B64_TEST_6)};
+static BASIC_TEST_CONTEXT    mBasicEncodeTest7  = {BIN_TEST_7,     B64_TEST_7,      EFI_SUCCESS, NULL, sizeof(B64_TEST_7)};
+static BASIC_TEST_CONTEXT    mBasicEncodeError1 = {BIN_TEST_7,     B64_TEST_1,      EFI_BUFFER_TOO_SMALL, NULL, sizeof(B64_TEST_7)};
+
+static BASIC_TEST_CONTEXT    mBasicDecodeTest1  = {B64_TEST_1,     BIN_TEST_1,      EFI_SUCCESS, NULL, sizeof(BIN_TEST_1)-1};
+static BASIC_TEST_CONTEXT    mBasicDecodeTest2  = {B64_TEST_2,     BIN_TEST_2,      EFI_SUCCESS, NULL, sizeof(BIN_TEST_2)-1};
+static BASIC_TEST_CONTEXT    mBasicDecodeTest3  = {B64_TEST_3,     BIN_TEST_3,      EFI_SUCCESS, NULL, sizeof(BIN_TEST_3)-1};
+static BASIC_TEST_CONTEXT    mBasicDecodeTest4  = {B64_TEST_4,     BIN_TEST_4,      EFI_SUCCESS, NULL, sizeof(BIN_TEST_4)-1};
+static BASIC_TEST_CONTEXT    mBasicDecodeTest5  = {B64_TEST_5,     BIN_TEST_5,      EFI_SUCCESS, NULL, sizeof(BIN_TEST_5)-1};
+static BASIC_TEST_CONTEXT    mBasicDecodeTest6  = {B64_TEST_6,     BIN_TEST_6,      EFI_SUCCESS, NULL, sizeof(BIN_TEST_6)-1};
+static BASIC_TEST_CONTEXT    mBasicDecodeTest7  = {B64_TEST_7,     BIN_TEST_7,      EFI_SUCCESS, NULL, sizeof(BIN_TEST_7)-1};
+static BASIC_TEST_CONTEXT    mBasicDecodeTest8  = {B64_TEST_8_IN,  BIN_TEST_8,      EFI_SUCCESS, NULL, sizeof(BIN_TEST_8)-1};
+
+static BASIC_TEST_CONTEXT    mBasicDecodeError1 = {B64_ERROR_1,    B64_ERROR_1,     EFI_INVALID_PARAMETER, NULL, 0};
+static BASIC_TEST_CONTEXT    mBasicDecodeError2 = {B64_ERROR_2,    B64_ERROR_2,     EFI_INVALID_PARAMETER, NULL, 0};
+static BASIC_TEST_CONTEXT    mBasicDecodeError3 = {B64_ERROR_3,    B64_ERROR_3,     EFI_INVALID_PARAMETER, NULL, 0};
+static BASIC_TEST_CONTEXT    mBasicDecodeError4 = {B64_ERROR_4,    B64_ERROR_4,     EFI_INVALID_PARAMETER, NULL, 0};
+static BASIC_TEST_CONTEXT    mBasicDecodeError5 = {B64_TEST_7,     BIN_TEST_1,      EFI_BUFFER_TOO_SMALL,  NULL, sizeof(BIN_TEST_7)-1};
+
+/**
+  Simple clean up method to make sure tests clean up even if interrupted and fail
+  in the middle.
+**/
+STATIC
+VOID
+EFIAPI
+CleanUpB64TestContext (
+  IN UNIT_TEST_CONTEXT  Context
+  )
+{
+  BASIC_TEST_CONTEXT  *Btc;
+
+  Btc = (BASIC_TEST_CONTEXT *)Context;
+  if (Btc != NULL) {
+    //free string if set
+    if (Btc->BufferToFree != NULL) {
+      FreePool (Btc->BufferToFree);
+      Btc->BufferToFree = NULL;
+    }
+  }
+}
+
+/**
+  Unit test for Base64 encode APIs of BaseLib.
+
+  @param[in]  Context    [Optional] An optional parameter that enables:
+                         1) test-case reuse with varied parameters and
+                         2) test-case re-entry for Target tests that need a
+                         reboot.  This parameter is a VOID* and it is the
+                         responsibility of the test author to ensure that the
+                         contents are well understood by all test cases that may
+                         consume it.
+
+  @retval  UNIT_TEST_PASSED             The Unit test has completed and the test
+                                        case was successful.
+  @retval  UNIT_TEST_ERROR_TEST_FAILED  A test case assertion has failed.
+**/
+STATIC
+UNIT_TEST_STATUS
+EFIAPI
+RfcEncodeTest (
+  IN UNIT_TEST_CONTEXT  Context
+  )
+{
+  BASIC_TEST_CONTEXT  *Btc;
+  CHAR8               *b64String;
+  CHAR8               *binString;
+  UINTN               b64StringSize;
+  EFI_STATUS          Status;
+  UINT8               *BinData;
+  UINTN               BinSize;
+  CHAR8               *b64WorkString;
+  UINTN               ReturnSize;
+  INTN                CompareStatus;
+  UINTN               indx;
+
+  Btc = (BASIC_TEST_CONTEXT *) Context;
+  binString = Btc->TestInput;
+  b64String = Btc->TestOutput;
+
+  //
+  // Only testing the the translate functionality, so preallocate the proper
+  // string buffer.
+  //
+
+  b64StringSize = AsciiStrnSizeS(b64String, MAX_TEST_STRING_SIZE);
+  BinSize = AsciiStrnLenS(binString, MAX_TEST_STRING_SIZE);
+  BinData = (UINT8 *)  binString;
+
+  b64WorkString = (CHAR8 *) AllocatePool(b64StringSize);
+  UT_ASSERT_NOT_NULL(b64WorkString);
+
+  Btc->BufferToFree = b64WorkString;
+  ReturnSize = b64StringSize;
+
+  Status = Base64Encode(BinData, BinSize, b64WorkString, &ReturnSize);
+
+  UT_ASSERT_STATUS_EQUAL(Status, Btc->ExpectedStatus);
+
+  UT_ASSERT_EQUAL(ReturnSize, Btc->ExpectedSize);
+
+  if (!EFI_ERROR (Btc->ExpectedStatus)) {
+    if (ReturnSize != 0) {
+      CompareStatus = AsciiStrnCmp (b64String, b64WorkString, ReturnSize);
+      if (CompareStatus != 0) {
+        UT_LOG_ERROR ("b64 string compare error - size=%d\n", ReturnSize);
+        for (indx = 0; indx < ReturnSize; indx++) {
+          UT_LOG_ERROR (" %2.2x", 0xff & b64String[indx]);
+        }
+        UT_LOG_ERROR ("\n b64 work string:\n");
+        for (indx = 0; indx < ReturnSize; indx++) {
+          UT_LOG_ERROR (" %2.2x", 0xff & b64WorkString[indx]);
+        }
+        UT_LOG_ERROR ("\n");
+      }
+      UT_ASSERT_EQUAL (CompareStatus, 0);
+    }
+  }
+
+  Btc->BufferToFree = NULL;
+  FreePool (b64WorkString);
+  return UNIT_TEST_PASSED;
+}
+
+/**
+  Unit test for Base64 decode APIs of BaseLib.
+
+  @param[in]  Context    [Optional] An optional parameter that enables:
+                         1) test-case reuse with varied parameters and
+                         2) test-case re-entry for Target tests that need a
+                         reboot.  This parameter is a VOID* and it is the
+                         responsibility of the test author to ensure that the
+                         contents are well understood by all test cases that may
+                         consume it.
+
+  @retval  UNIT_TEST_PASSED             The Unit test has completed and the test
+                                        case was successful.
+  @retval  UNIT_TEST_ERROR_TEST_FAILED  A test case assertion has failed.
+**/
+STATIC
+UNIT_TEST_STATUS
+EFIAPI
+RfcDecodeTest(
+  IN UNIT_TEST_CONTEXT  Context
+  )
+{
+  BASIC_TEST_CONTEXT *Btc;
+  CHAR8              *b64String;
+  CHAR8              *binString;
+  EFI_STATUS          Status;
+  UINTN               b64StringLen;
+  UINTN               ReturnSize;
+  UINT8              *BinData;
+  UINTN               BinSize;
+  INTN                CompareStatus;
+  UINTN               indx;
+
+  Btc = (BASIC_TEST_CONTEXT *)Context;
+  b64String = Btc->TestInput;
+  binString = Btc->TestOutput;
+
+  //
+  //  Only testing the the translate functionality
+  //
+
+  b64StringLen = AsciiStrnLenS (b64String, MAX_TEST_STRING_SIZE);
+  BinSize = AsciiStrnLenS (binString, MAX_TEST_STRING_SIZE);
+
+  BinData = AllocatePool (BinSize);
+  Btc->BufferToFree = BinData;
+
+  ReturnSize = BinSize;
+  Status = Base64Decode (b64String, b64StringLen, BinData, &ReturnSize);
+
+  UT_ASSERT_STATUS_EQUAL (Status, Btc->ExpectedStatus);
+
+  // If an error is not expected, check the results
+  if (EFI_ERROR (Btc->ExpectedStatus)) {
+    if (Btc->ExpectedStatus == EFI_BUFFER_TOO_SMALL) {
+      UT_ASSERT_EQUAL (ReturnSize, Btc->ExpectedSize);
+    }
+  } else {
+    UT_ASSERT_EQUAL (ReturnSize, Btc->ExpectedSize);
+    if (ReturnSize != 0) {
+      CompareStatus = CompareMem (binString, BinData, ReturnSize);
+      if (CompareStatus != 0) {
+        UT_LOG_ERROR ("bin string compare error - size=%d\n", ReturnSize);
+        for (indx = 0; indx < ReturnSize; indx++) {
+          UT_LOG_ERROR (" %2.2x", 0xff & binString[indx]);
+        }
+        UT_LOG_ERROR ("\nBinData:\n");
+        for (indx = 0; indx < ReturnSize; indx++) {
+          UT_LOG_ERROR (" %2.2x", 0xff & BinData[indx]);
+        }
+        UT_LOG_ERROR ("\n");
+      }
+      UT_ASSERT_EQUAL (CompareStatus, 0);
+    }
+  }
+
+  Btc->BufferToFree = NULL;
+  FreePool (BinData);
+  return UNIT_TEST_PASSED;
+}
+
+/**
+  Initialze the unit test framework, suite, and unit tests for the
+  Base64 conversion APIs of BaseLib and run the unit tests.
+
+  @retval  EFI_SUCCESS           All test cases were dispatched.
+  @retval  EFI_OUT_OF_RESOURCES  There are not enough resources available to
+                                 initialize the unit tests.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UnitTestingEntry (
+  VOID
+  )
+{
+  EFI_STATUS                  Status;
+  UNIT_TEST_FRAMEWORK_HANDLE  Fw;
+  UNIT_TEST_SUITE_HANDLE      b64EncodeTests;
+  UNIT_TEST_SUITE_HANDLE      b64DecodeTests;
+
+  Fw = NULL;
+
+  DEBUG ((DEBUG_INFO, "%a v%a\n", UNIT_TEST_APP_NAME, UNIT_TEST_APP_VERSION));
+
+  //
+  // Start setting up the test framework for running the tests.
+  //
+  Status = InitUnitTestFramework (&Fw, UNIT_TEST_APP_NAME, gEfiCallerBaseName, UNIT_TEST_APP_VERSION);
+  if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "Failed in InitUnitTestFramework. Status = %r\n", Status));
+      goto EXIT;
+  }
+
+  //
+  // Populate the B64 Encode Unit Test Suite.
+  //
+  Status = CreateUnitTestSuite (&b64EncodeTests, Fw, "b64 Encode binary to Ascii string", "BaseLib.b64Encode", NULL, NULL);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for b64EncodeTests\n"));
+    Status = EFI_OUT_OF_RESOURCES;
+    goto EXIT;
+  }
+
+  // --------------Suite-----------Description--------------Class Name----------Function--------Pre---Post-------------------Context-----------
+  AddTestCase (b64EncodeTests, "RFC 4686 Test Vector - Empty", "Test1", RfcEncodeTest, NULL, CleanUpB64TestContext, &mBasicEncodeTest1);
+  AddTestCase (b64EncodeTests, "RFC 4686 Test Vector - f", "Test2", RfcEncodeTest, NULL, CleanUpB64TestContext, &mBasicEncodeTest2);
+  AddTestCase (b64EncodeTests, "RFC 4686 Test Vector - fo", "Test3", RfcEncodeTest, NULL, CleanUpB64TestContext, &mBasicEncodeTest3);
+  AddTestCase (b64EncodeTests, "RFC 4686 Test Vector - foo", "Test4", RfcEncodeTest, NULL, CleanUpB64TestContext, &mBasicEncodeTest4);
+  AddTestCase (b64EncodeTests, "RFC 4686 Test Vector - foob", "Test5", RfcEncodeTest, NULL, CleanUpB64TestContext, &mBasicEncodeTest5);
+  AddTestCase (b64EncodeTests, "RFC 4686 Test Vector - fooba", "Test6", RfcEncodeTest, NULL, CleanUpB64TestContext, &mBasicEncodeTest6);
+  AddTestCase (b64EncodeTests, "RFC 4686 Test Vector - foobar", "Test7", RfcEncodeTest, NULL, CleanUpB64TestContext, &mBasicEncodeTest7);
+  AddTestCase (b64EncodeTests, "Too small of output buffer", "Error1", RfcEncodeTest, NULL, CleanUpB64TestContext, &mBasicEncodeError1);
+  //
+  // Populate the B64 Decode Unit Test Suite.
+  //
+  Status = CreateUnitTestSuite (&b64DecodeTests, Fw, "b64 Decode Ascii string to binary", "BaseLib.b64Decode", NULL, NULL);
+  if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for b64Decode Tests\n"));
+      Status = EFI_OUT_OF_RESOURCES;
+      goto EXIT;
+  }
+
+  AddTestCase (b64DecodeTests, "RFC 4686 Test Vector - Empty", "Test1",  RfcDecodeTest, NULL, CleanUpB64TestContext, &mBasicDecodeTest1);
+  AddTestCase (b64DecodeTests, "RFC 4686 Test Vector - f", "Test2",  RfcDecodeTest, NULL, CleanUpB64TestContext, &mBasicDecodeTest2);
+  AddTestCase (b64DecodeTests, "RFC 4686 Test Vector - fo", "Test3",  RfcDecodeTest, NULL, CleanUpB64TestContext, &mBasicDecodeTest3);
+  AddTestCase (b64DecodeTests, "RFC 4686 Test Vector - foo", "Test4",  RfcDecodeTest, NULL, CleanUpB64TestContext, &mBasicDecodeTest4);
+  AddTestCase (b64DecodeTests, "RFC 4686 Test Vector - foob", "Test5",  RfcDecodeTest, NULL, CleanUpB64TestContext, &mBasicDecodeTest5);
+  AddTestCase (b64DecodeTests, "RFC 4686 Test Vector - fooba", "Test6",  RfcDecodeTest, NULL, CleanUpB64TestContext, &mBasicDecodeTest6);
+  AddTestCase (b64DecodeTests, "RFC 4686 Test Vector - foobar", "Test7",  RfcDecodeTest, NULL, CleanUpB64TestContext, &mBasicDecodeTest7);
+  AddTestCase (b64DecodeTests, "Ignore Whitespace test", "Test8",  RfcDecodeTest, NULL, CleanUpB64TestContext, &mBasicDecodeTest8);
+
+  AddTestCase (b64DecodeTests, "Not a quantum multiple of 4", "Error1", RfcDecodeTest, NULL, CleanUpB64TestContext, &mBasicDecodeError1);
+  AddTestCase (b64DecodeTests, "Invalid characters in the string", "Error2", RfcDecodeTest, NULL, CleanUpB64TestContext, &mBasicDecodeError2);
+  AddTestCase (b64DecodeTests, "Too many padding characters", "Error3", RfcDecodeTest, NULL, CleanUpB64TestContext, &mBasicDecodeError3);
+  AddTestCase (b64DecodeTests, "Incorrectly placed padding character", "Error4", RfcDecodeTest, NULL, CleanUpB64TestContext, &mBasicDecodeError4);
+  AddTestCase (b64DecodeTests, "Too small of output buffer", "Error5", RfcDecodeTest, NULL, CleanUpB64TestContext, &mBasicDecodeError5);
+
+  //
+  // Execute the tests.
+  //
+  Status = RunAllTestSuites (Fw);
+
+EXIT:
+  if (Fw) {
+    FreeUnitTestFramework (Fw);
+  }
+
+  return Status;
+}
+
+/**
+  Standard UEFI entry point for target based unit test execution from UEFI Shell.
+**/
+EFI_STATUS
+EFIAPI
+BaseLibUnitTestAppEntry (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  return UnitTestingEntry ();
+}
+
+/**
+  Standard POSIX C entry point for host based unit test execution.
+**/
+int
+main (
+  int argc,
+  char *argv[]
+  )
+{
+  return UnitTestingEntry ();
+}
diff --git a/MdePkg/Test/UnitTest/Library/BaseLib/BaseLibUnitTestsHost.inf b/MdePkg/Test/UnitTest/Library/BaseLib/BaseLibUnitTestsHost.inf
new file mode 100644
index 0000000000..b31afae633
--- /dev/null
+++ b/MdePkg/Test/UnitTest/Library/BaseLib/BaseLibUnitTestsHost.inf
@@ -0,0 +1,32 @@
+## @file
+# Unit tests of Base64 conversion APIs in BaseLib that are run from host
+# environment.
+#
+# Copyright (C) Microsoft Corporation.
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010006
+  BASE_NAME                      = BaseLibUnitTestsHost
+  FILE_GUID                      = 1d005f4c-4dfa-41b5-ab0c-be91fe121459
+  MODULE_TYPE                    = HOST_APPLICATION
+  VERSION_STRING                 = 1.0
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+
+[Sources]
+  Base64UnitTest.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  DebugLib
+  UnitTestLib
diff --git a/MdePkg/Test/UnitTest/Library/BaseLib/BaseLibUnitTestsUefi.inf b/MdePkg/Test/UnitTest/Library/BaseLib/BaseLibUnitTestsUefi.inf
new file mode 100644
index 0000000000..907503898a
--- /dev/null
+++ b/MdePkg/Test/UnitTest/Library/BaseLib/BaseLibUnitTestsUefi.inf
@@ -0,0 +1,33 @@
+## @file
+# Unit tests of Base64 conversion APIs in BaseLib that are run from UEFI Shell.
+#
+# Copyright (C) Microsoft Corporation.
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010006
+  BASE_NAME                      = BaseLibUnitTestsUefi
+  FILE_GUID                      = df5a6fed-8786-4a9d-9d02-eab39497b4a1
+  MODULE_TYPE                    = UEFI_APPLICATION
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = BaseLibUnitTestAppEntry
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+
+[Sources]
+  Base64UnitTest.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  UefiApplicationEntryPoint
+  DebugLib
+  UnitTestLib
diff --git a/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/SafeIntLibUintnIntnUnitTests32.c b/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/SafeIntLibUintnIntnUnitTests32.c
new file mode 100644
index 0000000000..be5c0e15d3
--- /dev/null
+++ b/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/SafeIntLibUintnIntnUnitTests32.c
@@ -0,0 +1,540 @@
+/** @file
+  IA32-specific functions for unit-testing INTN and UINTN functions in
+  SafeIntLib.
+
+  Copyright (c) Microsoft Corporation.<BR>
+  Copyright (c) 2019 - 2020, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "TestBaseSafeIntLib.h"
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt32ToUintn (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT32       Operand;
+  UINTN       Result;
+
+  //
+  // If Operand is non-negative, then it's a cast
+  //
+  Operand = 0x5bababab;
+  Result = 0;
+  Status = SafeInt32ToUintn(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5bababab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (-1537977259);
+  Status = SafeInt32ToUintn(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint32ToIntn (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      Operand;
+  INTN        Result;
+
+  //
+  // If Operand is <= MAX_INTN, then it's a cast
+  //
+  Operand = 0x5bababab;
+  Result = 0;
+  Status = SafeUint32ToIntn(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5bababab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0xabababab);
+  Status = SafeUint32ToIntn(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeIntnToInt32 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INTN        Operand;
+  INT32       Result;
+
+  //
+  // INTN is same as INT32 in IA32, so this is just a cast
+  //
+  Operand = 0x5bababab;
+  Result = 0;
+  Status = SafeIntnToInt32(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5bababab, Result);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeIntnToUint32 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INTN        Operand;
+  UINT32      Result;
+
+  //
+  // If Operand is non-negative, then it's a cast
+  //
+  Operand = 0x5bababab;
+  Result = 0;
+  Status = SafeIntnToUint32(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5bababab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (-1537977259);
+  Status = SafeIntnToUint32(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUintnToUint32 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       Operand;
+  UINT32      Result;
+
+  //
+  // UINTN is same as UINT32 in IA32, so this is just a cast
+  //
+  Operand = 0xabababab;
+  Result = 0;
+  Status = SafeUintnToUint32(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0xabababab, Result);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUintnToIntn (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       Operand;
+  INTN        Result;
+
+  //
+  // If Operand is <= MAX_INTN, then it's a cast
+  //
+  Operand = 0x5bababab;
+  Result = 0;
+  Status = SafeUintnToIntn(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5bababab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0xabababab);
+  Status = SafeUintnToIntn(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUintnToInt64 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       Operand;
+  INT64       Result;
+
+  //
+  // UINTN is same as UINT32 in IA32, and UINT32 is a subset of
+  // INT64, so this is just a cast
+  //
+  Operand = 0xabababab;
+  Result = 0;
+  Status = SafeUintnToInt64(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0xabababab, Result);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt64ToIntn (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT64       Operand;
+  INTN        Result;
+
+  //
+  // If Operand is between MIN_INTN and  MAX_INTN2 inclusive, then it's a cast
+  //
+  Operand = 0x5bababab;
+  Result = 0;
+  Status = SafeInt64ToIntn(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5bababab, Result);
+
+  Operand = (-1537977259);
+  Status = SafeInt64ToIntn(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL((-1537977259), Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0x5babababefefefef);
+  Status = SafeInt64ToIntn(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand =  (-6605562033422200815);
+  Status = SafeInt64ToIntn(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt64ToUintn (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT64       Operand;
+  UINTN       Result;
+
+  //
+  // If Operand is between 0 and  MAX_UINTN inclusive, then it's a cast
+  //
+  Operand = 0xabababab;
+  Result = 0;
+  Status = SafeInt64ToUintn(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0xabababab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0x5babababefefefef);
+  Status = SafeInt64ToUintn(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand =  (-6605562033422200815);
+  Status = SafeInt64ToUintn(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint64ToIntn (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Operand;
+  INTN        Result;
+
+  //
+  // If Operand is <= MAX_INTN, then it's a cast
+  //
+  Operand = 0x5bababab;
+  Result = 0;
+  Status = SafeUint64ToIntn(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5bababab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0xababababefefefef);
+  Status = SafeUint64ToIntn(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint64ToUintn (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Operand;
+  UINTN       Result;
+
+  //
+  // If Operand is <= MAX_UINTN, then it's a cast
+  //
+  Operand = 0xabababab;
+  Result = 0;
+  Status = SafeUint64ToUintn(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0xabababab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0xababababefefefef);
+  Status = SafeUint64ToUintn(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUintnAdd (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       Augend;
+  UINTN       Addend;
+  UINTN       Result;
+
+  //
+  // If the result of addition doesn't overflow MAX_UINTN, then it's addition
+  //
+  Augend = 0x3a3a3a3a;
+  Addend = 0x3a3a3a3a;
+  Result = 0;
+  Status = SafeUintnAdd(Augend, Addend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x74747474, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Augend = 0xabababab;
+  Addend = 0xbcbcbcbc;
+  Status = SafeUintnAdd(Augend, Addend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeIntnAdd (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INTN        Augend;
+  INTN        Addend;
+  INTN        Result;
+
+  //
+  // If the result of addition doesn't overflow MAX_INTN
+  // and doesn't underflow MIN_INTN, then it's addition
+  //
+  Augend = 0x3a3a3a3a;
+  Addend = 0x3a3a3a3a;
+  Result = 0;
+  Status = SafeIntnAdd(Augend, Addend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x74747474, Result);
+
+  Augend = (-976894522);
+  Addend = (-976894522);
+  Status = SafeIntnAdd(Augend, Addend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL((-1953789044), Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Augend = 0x5a5a5a5a;
+  Addend = 0x5a5a5a5a;
+  Status = SafeIntnAdd(Augend, Addend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Augend = (-1515870810);
+  Addend = (-1515870810);
+  Status = SafeIntnAdd(Augend, Addend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUintnSub (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       Minuend;
+  UINTN       Subtrahend;
+  UINTN       Result;
+
+  //
+  // If Minuend >= Subtrahend, then it's subtraction
+  //
+  Minuend = 0x5a5a5a5a;
+  Subtrahend = 0x3b3b3b3b;
+  Result = 0;
+  Status = SafeUintnSub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x1f1f1f1f, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Minuend = 0x5a5a5a5a;
+  Subtrahend = 0x6d6d6d6d;
+  Status = SafeUintnSub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeIntnSub (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INTN        Minuend;
+  INTN        Subtrahend;
+  INTN        Result;
+
+  //
+  // If the result of subtractions doesn't overflow MAX_INTN or
+  // underflow MIN_INTN, then it's subtraction
+  //
+  Minuend = 0x5a5a5a5a;
+  Subtrahend = 0x3a3a3a3a;
+  Result = 0;
+  Status = SafeIntnSub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x20202020, Result);
+
+  Minuend = 0x3a3a3a3a;
+  Subtrahend = 0x5a5a5a5a;
+  Status = SafeIntnSub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL((-538976288), Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Minuend = (-2054847098);
+  Subtrahend = 2054847098;
+  Status = SafeIntnSub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Minuend = (2054847098);
+  Subtrahend = (-2054847098);
+  Status = SafeIntnSub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUintnMult (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       Multiplicand;
+  UINTN       Multiplier;
+  UINTN       Result;
+
+  //
+  // If the result of multiplication doesn't overflow MAX_UINTN, it will succeed
+  //
+  Multiplicand = 0xa122a;
+  Multiplier = 0xd23;
+  Result = 0;
+  Status = SafeUintnMult(Multiplicand, Multiplier, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x844c9dbe, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Multiplicand = 0xa122a;
+  Multiplier = 0xed23;
+  Status = SafeUintnMult(Multiplicand, Multiplier, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeIntnMult (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INTN        Multiplicand;
+  INTN        Multiplier;
+  INTN        Result;
+
+  //
+  // If the result of multiplication doesn't overflow MAX_INTN and doesn't
+  // underflow MIN_UINTN, it will succeed
+  //
+  Multiplicand = 0x123456;
+  Multiplier = 0x678;
+  Result = 0;
+  Status = SafeIntnMult(Multiplicand, Multiplier, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x75c28c50, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Multiplicand = 0x123456;
+  Multiplier = 0xabc;
+  Status = SafeIntnMult(Multiplicand, Multiplier, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
diff --git a/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/SafeIntLibUintnIntnUnitTests64.c b/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/SafeIntLibUintnIntnUnitTests64.c
new file mode 100644
index 0000000000..0fee298172
--- /dev/null
+++ b/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/SafeIntLibUintnIntnUnitTests64.c
@@ -0,0 +1,544 @@
+/** @file
+  x64-specific functions for unit-testing INTN and UINTN functions in
+  SafeIntLib.
+
+  Copyright (c) Microsoft Corporation.<BR>
+  Copyright (c) 2019 - 2020, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "TestBaseSafeIntLib.h"
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt32ToUintn (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT32       Operand;
+  UINTN       Result;
+
+  //
+  // If Operand is non-negative, then it's a cast
+  //
+  Operand = 0x5bababab;
+  Result = 0;
+  Status = SafeInt32ToUintn(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5bababab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (-1537977259);
+  Status = SafeInt32ToUintn(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint32ToIntn (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      Operand;
+  INTN        Result;
+
+  //
+  // For x64, INTN is same as INT64 which is a superset of INT32
+  // This is just a cast then, and it'll never fail
+  //
+
+  //
+  // If Operand is non-negative, then it's a cast
+  //
+  Operand = 0xabababab;
+  Result = 0;
+  Status = SafeUint32ToIntn(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0xabababab, Result);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeIntnToInt32 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INTN        Operand;
+  INT32       Result;
+
+  //
+  // If Operand is between MIN_INT32 and  MAX_INT32 inclusive, then it's a cast
+  //
+  Operand = 0x5bababab;
+  Result = 0;
+  Status = SafeIntnToInt32(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5bababab, Result);
+
+  Operand = (-1537977259);
+  Status = SafeIntnToInt32(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL((-1537977259), Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0x5babababefefefef);
+  Status = SafeIntnToInt32(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand =  (-6605562033422200815);
+  Status = SafeIntnToInt32(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeIntnToUint32 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INTN        Operand;
+  UINT32      Result;
+
+  //
+  // If Operand is between 0 and  MAX_UINT32 inclusive, then it's a cast
+  //
+  Operand = 0xabababab;
+  Result = 0;
+  Status = SafeIntnToUint32(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0xabababab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0x5babababefefefef);
+  Status = SafeIntnToUint32(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand =  (-6605562033422200815);
+  Status = SafeIntnToUint32(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUintnToUint32 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       Operand;
+  UINT32      Result;
+
+  //
+  // If Operand is <= MAX_UINT32, then it's a cast
+  //
+  Operand = 0xabababab;
+  Result = 0;
+  Status = SafeUintnToUint32(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0xabababab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0xababababefefefef);
+  Status = SafeUintnToUint32(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUintnToIntn (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       Operand;
+  INTN        Result;
+
+  //
+  // If Operand is <= MAX_INTN (0x7fff_ffff_ffff_ffff), then it's a cast
+  //
+  Operand = 0x5babababefefefef;
+  Result = 0;
+  Status = SafeUintnToIntn(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5babababefefefef, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0xababababefefefef);
+  Status = SafeUintnToIntn(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUintnToInt64 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       Operand;
+  INT64       Result;
+
+  //
+  // If Operand is <= MAX_INT64, then it's a cast
+  //
+  Operand = 0x5babababefefefef;
+  Result = 0;
+  Status = SafeUintnToInt64(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5babababefefefef, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0xababababefefefef);
+  Status = SafeUintnToInt64(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt64ToIntn (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT64       Operand;
+  INTN        Result;
+
+  //
+  // INTN is same as INT64 in x64, so this is just a cast
+  //
+  Operand = 0x5babababefefefef;
+  Result = 0;
+  Status = SafeInt64ToIntn(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5babababefefefef, Result);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt64ToUintn (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT64       Operand;
+  UINTN       Result;
+
+  //
+  // If Operand is non-negative, then it's a cast
+  //
+  Operand = 0x5babababefefefef;
+  Result = 0;
+  Status = SafeInt64ToUintn(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5babababefefefef, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand =  (-6605562033422200815);
+  Status = SafeInt64ToUintn(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint64ToIntn (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Operand;
+  INTN        Result;
+
+  //
+  // If Operand is <= MAX_INTN (0x7fff_ffff_ffff_ffff), then it's a cast
+  //
+  Operand = 0x5babababefefefef;
+  Result = 0;
+  Status = SafeUint64ToIntn(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5babababefefefef, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0xababababefefefef);
+  Status = SafeUint64ToIntn(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint64ToUintn (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Operand;
+  UINTN       Result;
+
+  //
+  // UINTN is same as UINT64 in x64, so this is just a cast
+  //
+  Operand = 0xababababefefefef;
+  Result = 0;
+  Status = SafeUint64ToUintn(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0xababababefefefef, Result);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUintnAdd (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       Augend;
+  UINTN       Addend;
+  UINTN       Result;
+
+  //
+  // If the result of addition doesn't overflow MAX_UINTN, then it's addition
+  //
+  Augend = 0x3a3a3a3a12121212;
+  Addend = 0x3a3a3a3a12121212;
+  Result = 0;
+  Status = SafeUintnAdd(Augend, Addend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x7474747424242424, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Augend = 0xababababefefefef;
+  Addend = 0xbcbcbcbcdededede;
+  Status = SafeUintnAdd(Augend, Addend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeIntnAdd (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INTN        Augend;
+  INTN        Addend;
+  INTN        Result;
+
+  //
+  // If the result of addition doesn't overflow MAX_INTN
+  // and doesn't underflow MIN_INTN, then it's addition
+  //
+  Augend = 0x3a3a3a3a3a3a3a3a;
+  Addend = 0x3a3a3a3a3a3a3a3a;
+  Result = 0;
+  Status = SafeIntnAdd(Augend, Addend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x7474747474747474, Result);
+
+  Augend = (-4195730024608447034);
+  Addend = (-4195730024608447034);
+  Status = SafeIntnAdd(Augend, Addend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL((-8391460049216894068), Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Augend = 0x5a5a5a5a5a5a5a5a;
+  Addend = 0x5a5a5a5a5a5a5a5a;
+  Status = SafeIntnAdd(Augend, Addend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Augend = (-6510615555426900570);
+  Addend = (-6510615555426900570);
+  Status = SafeIntnAdd(Augend, Addend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUintnSub (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       Minuend;
+  UINTN       Subtrahend;
+  UINTN       Result;
+
+  //
+  // If Minuend >= Subtrahend, then it's subtraction
+  //
+  Minuend = 0x5a5a5a5a5a5a5a5a;
+  Subtrahend = 0x3b3b3b3b3b3b3b3b;
+  Result = 0;
+  Status = SafeUintnSub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x1f1f1f1f1f1f1f1f, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Minuend = 0x5a5a5a5a5a5a5a5a;
+  Subtrahend = 0x6d6d6d6d6d6d6d6d;
+  Status = SafeUintnSub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeIntnSub (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INTN        Minuend;
+  INTN        Subtrahend;
+  INTN        Result;
+
+  //
+  // If the result of subtractions doesn't overflow MAX_INTN or
+  // underflow MIN_INTN, then it's subtraction
+  //
+  Minuend = 0x5a5a5a5a5a5a5a5a;
+  Subtrahend = 0x3a3a3a3a3a3a3a3a;
+  Result = 0;
+  Status = SafeIntnSub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x2020202020202020, Result);
+
+  Minuend = 0x3a3a3a3a3a3a3a3a;
+  Subtrahend = 0x5a5a5a5a5a5a5a5a;
+  Status = SafeIntnSub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL((-2314885530818453536), Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Minuend = (-8825501086245354106);
+  Subtrahend = 8825501086245354106;
+  Status = SafeIntnSub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Minuend = (8825501086245354106);
+  Subtrahend = (-8825501086245354106);
+  Status = SafeIntnSub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUintnMult (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       Multiplicand;
+  UINTN       Multiplier;
+  UINTN       Result;
+
+  //
+  // If the result of multiplication doesn't overflow MAX_UINTN, it will succeed
+  //
+  Multiplicand = 0x123456789a;
+  Multiplier = 0x1234567;
+  Result = 0;
+  Status = SafeUintnMult(Multiplicand, Multiplier, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x14b66db9745a07f6, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Multiplicand = 0x123456789a;
+  Multiplier = 0x12345678;
+  Status = SafeUintnMult(Multiplicand, Multiplier, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeIntnMult (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INTN        Multiplicand;
+  INTN        Multiplier;
+  INTN        Result;
+
+  //
+  // If the result of multiplication doesn't overflow MAX_INTN and doesn't
+  // underflow MIN_UINTN, it will succeed
+  //
+  Multiplicand = 0x123456789;
+  Multiplier = 0x6789abcd;
+  Result = 0;
+  Status = SafeIntnMult(Multiplicand, Multiplier, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x75cd9045220d6bb5, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Multiplicand = 0x123456789;
+  Multiplier = 0xa789abcd;
+  Status = SafeIntnMult(Multiplicand, Multiplier, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
diff --git a/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLib.c b/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLib.c
new file mode 100644
index 0000000000..2b1a2223a0
--- /dev/null
+++ b/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLib.c
@@ -0,0 +1,3064 @@
+/** @file
+  UEFI OS based application for unit testing the SafeIntLib.
+
+  Copyright (c) Microsoft Corporation.<BR>
+  Copyright (c) 2018 - 2020, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "TestBaseSafeIntLib.h"
+
+#define UNIT_TEST_NAME        "Int Safe Lib Unit Test Application"
+#define UNIT_TEST_VERSION     "0.1"
+
+//
+// Conversion function tests:
+//
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt8ToUint8 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT8        Operand;
+  UINT8       Result;
+
+  //
+  // Positive UINT8 should result in just a cast
+  //
+  Operand = 0x5b;
+  Result = 0;
+  Status = SafeInt8ToUint8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b, Result);
+
+  //
+  // Negative number should result in an error status
+  //
+  Operand = (-56);
+  Status = SafeInt8ToUint8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt8ToUint16 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT8        Operand;
+  UINT16      Result;
+
+  //
+  // Positive UINT8 should result in just a cast
+  //
+  Operand = 0x5b;
+  Result = 0;
+  Status = SafeInt8ToUint16(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b, Result);
+
+  //
+  // Negative number should result in an error status
+  //
+  Operand = (-56);
+  Status = SafeInt8ToUint16(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt8ToUint32 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT8        Operand;
+  UINT32      Result;
+
+  //
+  // Positive UINT8 should result in just a cast
+  //
+  Operand = 0x5b;
+  Result = 0;
+  Status = SafeInt8ToUint32(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b, Result);
+
+  //
+  // Negative number should result in an error status
+  //
+  Operand = (-56);
+  Status = SafeInt8ToUint32(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt8ToUintn (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT8        Operand;
+  UINTN       Result;
+
+  //
+  // Positive UINT8 should result in just a cast
+  //
+  Operand = 0x5b;
+  Result = 0;
+  Status = SafeInt8ToUintn(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b, Result);
+
+  //
+  // Negative number should result in an error status
+  //
+  Operand = (-56);
+  Status = SafeInt8ToUintn(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt8ToUint64 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT8        Operand;
+  UINT64      Result;
+
+  //
+  // Positive UINT8 should result in just a cast
+  //
+  Operand = 0x5b;
+  Result = 0;
+  Status = SafeInt8ToUint64(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b, Result);
+
+  //
+  // Negative number should result in an error status
+  //
+  Operand = (-56);
+  Status = SafeInt8ToUint64(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint8ToInt8 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT8       Operand;
+  INT8        Result;
+
+  //
+  // Operand <= 0x7F (MAX_INT8) should result in a cast
+  //
+  Operand = 0x5b;
+  Result = 0;
+  Status = SafeUint8ToInt8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b, Result);
+
+  //
+  // Operand larger than 0x7f should result in an error status
+  //
+  Operand = 0xaf;
+  Status = SafeUint8ToInt8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint8ToChar8 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT8       Operand;
+  CHAR8       Result;
+
+  //
+  // CHAR8 is typedefed as char, which by default is signed, thus
+  // CHAR8 is same as INT8, so same tests as above:
+  //
+
+  //
+  // Operand <= 0x7F (MAX_INT8) should result in a cast
+  //
+  Operand = 0x5b;
+  Result = 0;
+  Status = SafeUint8ToChar8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b, Result);
+
+  //
+  // Operand larger than 0x7f should result in an error status
+  //
+  Operand = 0xaf;
+  Status = SafeUint8ToChar8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt16ToInt8 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT16       Operand;
+  INT8        Result;
+
+  //
+  // If Operand is between MIN_INT8 and MAX_INT8 inclusive, then it's a cast
+  //
+  Operand = 0x5b;
+  Result = 0;
+  Status = SafeInt16ToInt8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b, Result);
+
+  Operand = (-35);
+  Status = SafeInt16ToInt8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL((-35), Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = 0x1234;
+  Status = SafeInt16ToInt8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand = (-17835);
+  Status = SafeInt16ToInt8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt16ToChar8 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT16       Operand;
+  CHAR8       Result;
+
+  //
+  // CHAR8 is typedefed as char, which may be signed or unsigned based
+  // on the compiler. Thus, for compatibility CHAR8 should be between 0 and MAX_INT8.
+  //
+
+  //
+  // If Operand is between 0 and MAX_INT8 inclusive, then it's a cast
+  //
+  Operand = 0x5b;
+  Result = 0;
+  Status = SafeInt16ToChar8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b, Result);
+
+  Operand = 0;
+  Result = 0;
+  Status = SafeInt16ToChar8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0, Result);
+
+  Operand = MAX_INT8;
+  Result = 0;
+  Status = SafeInt16ToChar8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(MAX_INT8, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (-35);
+  Status = SafeInt16ToChar8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand = 0x1234;
+  Status = SafeInt16ToChar8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand = (-17835);
+  Status = SafeInt16ToChar8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt16ToUint8 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT16       Operand;
+  UINT8       Result;
+
+  //
+  // If Operand is between 0 and MAX_INT8 inclusive, then it's a cast
+  //
+  Operand = 0x5b;
+  Result = 0;
+  Status = SafeInt16ToUint8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = 0x1234;
+  Status = SafeInt16ToUint8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand = (-17835);
+  Status = SafeInt16ToUint8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt16ToUint16 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT16 Operand = 0x5b5b;
+  UINT16 Result = 0;
+
+  //
+  // If Operand is non-negative, then it's a cast
+  //
+  Status = SafeInt16ToUint16(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b5b, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (-17835);
+  Status = SafeInt16ToUint16(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt16ToUint32 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT16       Operand;
+  UINT32      Result;
+
+  //
+  // If Operand is non-negative, then it's a cast
+  //
+  Operand = 0x5b5b;
+  Result = 0;
+  Status = SafeInt16ToUint32(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b5b, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (-17835);
+  Status = SafeInt16ToUint32(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt16ToUintn (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT16       Operand;
+  UINTN       Result;
+
+  //
+  // If Operand is non-negative, then it's a cast
+  //
+  Operand = 0x5b5b;
+  Result = 0;
+  Status = SafeInt16ToUintn(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b5b, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (-17835);
+  Status = SafeInt16ToUintn(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt16ToUint64 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT16       Operand;
+  UINT64      Result;
+
+  //
+  // If Operand is non-negative, then it's a cast
+  //
+  Operand = 0x5b5b;
+  Result = 0;
+  Status = SafeInt16ToUint64(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b5b, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (-17835);
+  Status = SafeInt16ToUint64(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint16ToInt8 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT16      Operand;
+  INT8        Result;
+
+  //
+  // If Operand is <= MAX_INT8, it's a cast
+  //
+  Operand = 0x5b;
+  Result = 0;
+  Status = SafeUint16ToInt8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0x5b5b);
+  Status = SafeUint16ToInt8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint16ToChar8 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT16      Operand;
+  CHAR8       Result;
+
+  // CHAR8 is typedefed as char, which by default is signed, thus
+  // CHAR8 is same as INT8, so same tests as above:
+
+  //
+  // If Operand is <= MAX_INT8, it's a cast
+  //
+  Operand = 0x5b;
+  Result = 0;
+  Status = SafeUint16ToChar8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0x5b5b);
+  Status = SafeUint16ToChar8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint16ToUint8 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT16      Operand;
+  UINT8       Result;
+
+  //
+  // If Operand is <= MAX_UINT8 (0xff), it's a cast
+  //
+  Operand = 0xab;
+  Result = 0;
+  Status = SafeUint16ToUint8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0xab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0x5b5b);
+  Status = SafeUint16ToUint8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint16ToInt16 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT16      Operand;
+  INT16       Result;
+
+  //
+  // If Operand is <= MAX_INT16 (0x7fff), it's a cast
+  //
+  Operand = 0x5b5b;
+  Result = 0;
+  Status = SafeUint16ToInt16(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b5b, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0xabab);
+  Status = SafeUint16ToInt16(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt32ToInt8 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT32       Operand;
+  INT8        Result;
+
+  //
+  // If Operand is between MIN_INT8 and MAX_INT8 inclusive, then it's a cast
+  //
+  Operand = 0x5b;
+  Result = 0;
+  Status = SafeInt32ToInt8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b, Result);
+
+  Operand = (-57);
+  Status = SafeInt32ToInt8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL((-57), Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0x5bababab);
+  Status = SafeInt32ToInt8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand = (-1537977259);
+  Status = SafeInt32ToInt8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt32ToChar8 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT32       Operand;
+  CHAR8       Result;
+
+  //
+  // CHAR8 is typedefed as char, which may be signed or unsigned based
+  // on the compiler. Thus, for compatibility CHAR8 should be between 0 and MAX_INT8.
+  //
+
+  //
+  // If Operand is between 0 and MAX_INT8 inclusive, then it's a cast
+  //
+  Operand = 0x5b;
+  Result = 0;
+  Status = SafeInt32ToChar8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b, Result);
+
+  Operand = 0;
+  Result = 0;
+  Status = SafeInt32ToChar8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0, Result);
+
+  Operand = MAX_INT8;
+  Result = 0;
+  Status = SafeInt32ToChar8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(MAX_INT8, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (-57);
+  Status = SafeInt32ToChar8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand = (0x5bababab);
+  Status = SafeInt32ToChar8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand = (-1537977259);
+  Status = SafeInt32ToChar8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt32ToUint8 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT32       Operand;
+  UINT8       Result;
+
+  //
+  // If Operand is between 0 and MAX_INT8 inclusive, then it's a cast
+  //
+  Operand = 0x5b;
+  Result = 0;
+  Status = SafeInt32ToUint8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (-57);
+  Status = SafeInt32ToUint8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand = (0x5bababab);
+  Status = SafeInt32ToUint8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand = (-1537977259);
+  Status = SafeInt32ToUint8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt32ToInt16 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT32       Operand;
+  INT16       Result;
+
+  //
+  // If Operand is between MIN_INT16 and MAX_INT16 inclusive, then it's a cast
+  //
+  Operand = 0x5b5b;
+  Result = 0;
+  Status = SafeInt32ToInt16(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b5b, Result);
+
+  Operand = (-17857);
+  Status = SafeInt32ToInt16(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL((-17857), Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0x5bababab);
+  Status = SafeInt32ToInt16(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand = (-1537977259);
+  Status = SafeInt32ToInt16(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt32ToUint16 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT32       Operand;
+  UINT16      Result;
+
+  //
+  // If Operand is between 0 and MAX_UINT16 inclusive, then it's a cast
+  //
+  Operand = 0xabab;
+  Result = 0;
+  Status = SafeInt32ToUint16(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0xabab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (-17857);
+  Status = SafeInt32ToUint16(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand = (0x5bababab);
+  Status = SafeInt32ToUint16(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand = (-1537977259);
+  Status = SafeInt32ToUint16(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt32ToUint32 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT32       Operand;
+  UINT32      Result;
+
+  //
+  // If Operand is non-negative, then it's a cast
+  //
+  Operand = 0x5bababab;
+  Result = 0;
+  Status = SafeInt32ToUint32(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5bababab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (-1537977259);
+  Status = SafeInt32ToUint32(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt32ToUint64 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT32       Operand;
+  UINT64      Result;
+
+  //
+  // If Operand is non-negative, then it's a cast
+  //
+  Operand = 0x5bababab;
+  Result = 0;
+  Status = SafeInt32ToUint64(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5bababab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (-1537977259);
+  Status = SafeInt32ToUint64(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint32ToInt8 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      Operand;
+  INT8        Result;
+
+  //
+  // If Operand is <= MAX_INT8, then it's a cast
+  //
+  Operand = 0x5b;
+  Result = 0;
+  Status = SafeUint32ToInt8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0x5bababab);
+  Status = SafeUint32ToInt8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint32ToChar8 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      Operand;
+  CHAR8       Result;
+
+  // CHAR8 is typedefed as char, which by default is signed, thus
+  // CHAR8 is same as INT8, so same tests as above:
+
+  //
+  // If Operand is <= MAX_INT8, then it's a cast
+  //
+  Operand = 0x5b;
+  Result = 0;
+  Status = SafeUint32ToChar8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0x5bababab);
+  Status = SafeUint32ToChar8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint32ToUint8 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      Operand;
+  UINT8       Result;
+
+  //
+  // If Operand is <= MAX_UINT8, then it's a cast
+  //
+  Operand = 0xab;
+  Result = 0;
+  Status = SafeUint32ToUint8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0xab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0xabababab);
+  Status = SafeUint32ToUint8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint32ToInt16 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      Operand;
+  INT16       Result;
+
+  //
+  // If Operand is <= MAX_INT16, then it's a cast
+  //
+  Operand = 0x5bab;
+  Result = 0;
+  Status = SafeUint32ToInt16(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5bab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0xabababab);
+  Status = SafeUint32ToInt16(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint32ToUint16 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      Operand;
+  UINT16      Result;
+
+  //
+  // If Operand is <= MAX_UINT16, then it's a cast
+  //
+  Operand = 0xabab;
+  Result = 0;
+  Status = SafeUint32ToUint16(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0xabab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0xabababab);
+  Status = SafeUint32ToUint16(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint32ToInt32 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      Operand;
+  INT32       Result;
+
+  //
+  // If Operand is <= MAX_INT32, then it's a cast
+  //
+  Operand = 0x5bababab;
+  Result = 0;
+  Status = SafeUint32ToInt32(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5bababab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0xabababab);
+  Status = SafeUint32ToInt32(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeIntnToInt8 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INTN        Operand;
+  INT8        Result;
+
+  //
+  // If Operand is between MIN_INT8 and MAX_INT8 inclusive, then it's a cast
+  //
+  Operand = 0x5b;
+  Result = 0;
+  Status = SafeIntnToInt8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b, Result);
+
+  Operand = (-53);
+  Status = SafeIntnToInt8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL((-53), Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0x5bababab);
+  Status = SafeIntnToInt8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand = (-1537977259);
+  Status = SafeIntnToInt8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeIntnToChar8 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INTN        Operand;
+  CHAR8       Result;
+
+  //
+  // CHAR8 is typedefed as char, which may be signed or unsigned based
+  // on the compiler. Thus, for compatibility CHAR8 should be between 0 and MAX_INT8.
+  //
+
+  //
+  // If Operand is between MIN_INT8 and MAX_INT8 inclusive, then it's a cast
+  //
+  Operand = 0x5b;
+  Result = 0;
+  Status = SafeIntnToChar8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b, Result);
+
+  Operand = 0;
+  Result = 0;
+  Status = SafeIntnToChar8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0, Result);
+
+  Operand = MAX_INT8;
+  Result = 0;
+  Status = SafeIntnToChar8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(MAX_INT8, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (-53);
+  Status = SafeIntnToChar8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand = (0x5bababab);
+  Status = SafeIntnToChar8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand = (-1537977259);
+  Status = SafeIntnToChar8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeIntnToUint8 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INTN        Operand;
+  UINT8       Result;
+
+  //
+  // If Operand is between 0 and MAX_UINT8 inclusive, then it's a cast
+  //
+  Operand = 0xab;
+  Result = 0;
+  Status = SafeIntnToUint8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0xab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0x5bababab);
+  Status = SafeIntnToUint8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand = (-1537977259);
+  Status = SafeIntnToUint8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeIntnToInt16 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INTN        Operand;
+  INT16       Result;
+
+  //
+  // If Operand is between MIN_INT16 and MAX_INT16 inclusive, then it's a cast
+  //
+  Operand = 0x5bab;
+  Result = 0;
+  Status = SafeIntnToInt16(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5bab, Result);
+
+  Operand = (-23467);
+  Status = SafeIntnToInt16(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL((-23467), Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0x5bababab);
+  Status = SafeIntnToInt16(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand = (-1537977259);
+  Status = SafeIntnToInt16(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeIntnToUint16 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INTN        Operand;
+  UINT16      Result;
+
+  //
+  // If Operand is between 0 and MAX_UINT16 inclusive, then it's a cast
+  //
+  Operand = 0xabab;
+  Result = 0;
+  Status = SafeIntnToUint16(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0xabab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0x5bababab);
+  Status = SafeIntnToUint16(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand = (-1537977259);
+  Status = SafeIntnToUint16(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeIntnToUintn (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INTN        Operand;
+  UINTN       Result;
+
+  //
+  // If Operand is non-negative, then it's a cast
+  //
+  Operand = 0x5bababab;
+  Result = 0;
+  Status = SafeIntnToUintn(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5bababab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (-1537977259);
+  Status = SafeIntnToUintn(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeIntnToUint64 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INTN        Operand;
+  UINT64      Result;
+
+  //
+  // If Operand is non-negative, then it's a cast
+  //
+  Operand = 0x5bababab;
+  Result = 0;
+  Status = SafeIntnToUint64(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5bababab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (-1537977259);
+  Status = SafeIntnToUint64(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUintnToInt8 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       Operand;
+  INT8        Result;
+
+  //
+  // If Operand is <= MAX_INT8, then it's a cast
+  //
+  Operand = 0x5b;
+  Result = 0;
+  Status = SafeUintnToInt8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0xabab);
+  Status = SafeUintnToInt8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUintnToChar8 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       Operand;
+  CHAR8       Result;
+
+  // CHAR8 is typedefed as char, which by default is signed, thus
+  // CHAR8 is same as INT8, so same tests as above:
+
+  //
+  // If Operand is <= MAX_INT8, then it's a cast
+  //
+  Operand = 0x5b;
+  Result = 0;
+  Status = SafeUintnToChar8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0xabab);
+  Status = SafeUintnToChar8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUintnToUint8 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       Operand;
+  UINT8       Result;
+
+  //
+  // If Operand is <= MAX_UINT8, then it's a cast
+  //
+  Operand = 0xab;
+  Result = 0;
+  Status = SafeUintnToUint8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0xab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0xabab);
+  Status = SafeUintnToUint8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUintnToInt16 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       Operand;
+  INT16       Result;
+
+  //
+  // If Operand is <= MAX_INT16, then it's a cast
+  //
+  Operand = 0x5bab;
+  Result = 0;
+  Status = SafeUintnToInt16(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5bab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0xabab);
+  Status = SafeUintnToInt16(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUintnToUint16 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       Operand;
+  UINT16      Result;
+
+  //
+  // If Operand is <= MAX_UINT16, then it's a cast
+  //
+  Operand = 0xabab;
+  Result = 0;
+  Status = SafeUintnToUint16(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0xabab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0xabababab);
+  Status = SafeUintnToUint16(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUintnToInt32 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       Operand;
+  INT32       Result;
+
+  //
+  // If Operand is <= MAX_INT32, then it's a cast
+  //
+  Operand = 0x5bababab;
+  Result = 0;
+  Status = SafeUintnToInt32(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5bababab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0xabababab);
+  Status = SafeUintnToInt32(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt64ToInt8 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT64       Operand;
+  INT8        Result;
+
+  //
+  // If Operand is between MIN_INT8 and  MAX_INT8 inclusive, then it's a cast
+  //
+  Operand = 0x5b;
+  Result = 0;
+  Status = SafeInt64ToInt8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b, Result);
+
+  Operand = (-37);
+  Status = SafeInt64ToInt8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL((-37), Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0x5babababefefefef);
+  Status = SafeInt64ToInt8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand =  (-6605562033422200815);
+  Status = SafeInt64ToInt8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt64ToChar8 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT64       Operand;
+  CHAR8       Result;
+
+  //
+  // CHAR8 is typedefed as char, which may be signed or unsigned based
+  // on the compiler. Thus, for compatibility CHAR8 should be between 0 and MAX_INT8.
+  //
+
+  //
+  // If Operand is between MIN_INT8 and  MAX_INT8 inclusive, then it's a cast
+  //
+  Operand = 0x5b;
+  Result = 0;
+  Status = SafeInt64ToChar8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b, Result);
+
+  Operand = 0;
+  Result = 0;
+  Status = SafeInt64ToChar8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0, Result);
+
+  Operand = MAX_INT8;
+  Result = 0;
+  Status = SafeInt64ToChar8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(MAX_INT8, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (-37);
+  Status = SafeInt64ToChar8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand = (0x5babababefefefef);
+  Status = SafeInt64ToChar8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand =  (-6605562033422200815);
+  Status = SafeInt64ToChar8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt64ToUint8 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT64       Operand;
+  UINT8       Result;
+
+  //
+  // If Operand is between 0 and  MAX_UINT8 inclusive, then it's a cast
+  //
+  Operand = 0xab;
+  Result = 0;
+  Status = SafeInt64ToUint8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0xab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0x5babababefefefef);
+  Status = SafeInt64ToUint8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand =  (-6605562033422200815);
+  Status = SafeInt64ToUint8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt64ToInt16 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT64       Operand;
+  INT16       Result;
+
+  //
+  // If Operand is between MIN_INT16 and  MAX_INT16 inclusive, then it's a cast
+  //
+  Operand = 0x5bab;
+  Result = 0;
+  Status = SafeInt64ToInt16(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5bab, Result);
+
+  Operand = (-23467);
+  Status = SafeInt64ToInt16(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL((-23467), Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0x5babababefefefef);
+  Status = SafeInt64ToInt16(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand =  (-6605562033422200815);
+  Status = SafeInt64ToInt16(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt64ToUint16 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT64       Operand;
+  UINT16      Result;
+
+  //
+  // If Operand is between 0 and  MAX_UINT16 inclusive, then it's a cast
+  //
+  Operand = 0xabab;
+  Result = 0;
+  Status = SafeInt64ToUint16(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0xabab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0x5babababefefefef);
+  Status = SafeInt64ToUint16(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand =  (-6605562033422200815);
+  Status = SafeInt64ToUint16(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt64ToInt32 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT64       Operand;
+  INT32       Result;
+
+  //
+  // If Operand is between MIN_INT32 and  MAX_INT32 inclusive, then it's a cast
+  //
+  Operand = 0x5bababab;
+  Result = 0;
+  Status = SafeInt64ToInt32(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5bababab, Result);
+
+  Operand = (-1537977259);
+  Status = SafeInt64ToInt32(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL((-1537977259), Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0x5babababefefefef);
+  Status = SafeInt64ToInt32(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand =  (-6605562033422200815);
+  Status = SafeInt64ToInt32(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt64ToUint32 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT64       Operand;
+  UINT32      Result;
+
+  //
+  // If Operand is between 0 and  MAX_UINT32 inclusive, then it's a cast
+  //
+  Operand = 0xabababab;
+  Result = 0;
+  Status = SafeInt64ToUint32(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0xabababab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0x5babababefefefef);
+  Status = SafeInt64ToUint32(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Operand =  (-6605562033422200815);
+  Status = SafeInt64ToUint32(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt64ToUint64 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT64       Operand;
+  UINT64      Result;
+
+  //
+  // If Operand is non-negative, then it's a cast
+  //
+  Operand = 0x5babababefefefef;
+  Result = 0;
+  Status = SafeInt64ToUint64(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5babababefefefef, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand =  (-6605562033422200815);
+  Status = SafeInt64ToUint64(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint64ToInt8 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Operand;
+  INT8        Result;
+
+  //
+  // If Operand is <= MAX_INT8, then it's a cast
+  //
+  Operand = 0x5b;
+  Result = 0;
+  Status = SafeUint64ToInt8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0xababababefefefef);
+  Status = SafeUint64ToInt8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint64ToChar8 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Operand;
+  CHAR8       Result;
+
+  // CHAR8 is typedefed as char, which by default is signed, thus
+  // CHAR8 is same as INT8, so same tests as above:
+
+  //
+  // If Operand is <= MAX_INT8, then it's a cast
+  //
+  Operand = 0x5b;
+  Result = 0;
+  Status = SafeUint64ToChar8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5b, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0xababababefefefef);
+  Status = SafeUint64ToChar8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint64ToUint8 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Operand;
+  UINT8       Result;
+
+  //
+  // If Operand is <= MAX_UINT8, then it's a cast
+  //
+  Operand = 0xab;
+  Result = 0;
+  Status = SafeUint64ToUint8(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0xab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0xababababefefefef);
+  Status = SafeUint64ToUint8(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint64ToInt16 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Operand;
+  INT16       Result;
+
+  //
+  // If Operand is <= MAX_INT16, then it's a cast
+  //
+  Operand = 0x5bab;
+  Result = 0;
+  Status = SafeUint64ToInt16(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5bab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0xababababefefefef);
+  Status = SafeUint64ToInt16(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint64ToUint16 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Operand;
+  UINT16      Result;
+
+  //
+  // If Operand is <= MAX_UINT16, then it's a cast
+  //
+  Operand = 0xabab;
+  Result = 0;
+  Status = SafeUint64ToUint16(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0xabab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0xababababefefefef);
+  Status = SafeUint64ToUint16(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint64ToInt32 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Operand;
+  INT32       Result;
+
+  //
+  // If Operand is <= MAX_INT32, then it's a cast
+  //
+  Operand = 0x5bababab;
+  Result = 0;
+  Status = SafeUint64ToInt32(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5bababab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0xababababefefefef);
+  Status = SafeUint64ToInt32(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint64ToUint32 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Operand;
+  UINT32      Result;
+
+  //
+  // If Operand is <= MAX_UINT32, then it's a cast
+  //
+  Operand = 0xabababab;
+  Result = 0;
+  Status = SafeUint64ToUint32(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0xabababab, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0xababababefefefef);
+  Status = SafeUint64ToUint32(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint64ToInt64 (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Operand;
+  INT64       Result;
+
+  //
+  // If Operand is <= MAX_INT64, then it's a cast
+  //
+  Operand = 0x5babababefefefef;
+  Result = 0;
+  Status = SafeUint64ToInt64(Operand, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x5babababefefefef, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Operand = (0xababababefefefef);
+  Status = SafeUint64ToInt64(Operand, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+//
+// Addition function tests:
+//
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint8Add (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT8       Augend;
+  UINT8       Addend;
+  UINT8       Result;
+
+  //
+  // If the result of addition doesn't overflow MAX_UINT8, then it's addition
+  //
+  Augend = 0x3a;
+  Addend = 0x3a;
+  Result = 0;
+  Status = SafeUint8Add(Augend, Addend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x74, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Augend = 0xab;
+  Addend = 0xbc;
+  Status = SafeUint8Add(Augend, Addend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint16Add (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT16 Augend = 0x3a3a;
+  UINT16 Addend = 0x3a3a;
+  UINT16 Result = 0;
+
+  //
+  // If the result of addition doesn't overflow MAX_UINT16, then it's addition
+  //
+  Status = SafeUint16Add(Augend, Addend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x7474, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Augend = 0xabab;
+  Addend = 0xbcbc;
+  Status = SafeUint16Add(Augend, Addend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint32Add (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      Augend;
+  UINT32      Addend;
+  UINT32      Result;
+
+  //
+  // If the result of addition doesn't overflow MAX_UINT32, then it's addition
+  //
+  Augend = 0x3a3a3a3a;
+  Addend = 0x3a3a3a3a;
+  Result = 0;
+  Status = SafeUint32Add(Augend, Addend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x74747474, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Augend = 0xabababab;
+  Addend = 0xbcbcbcbc;
+  Status = SafeUint32Add(Augend, Addend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint64Add (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Augend;
+  UINT64      Addend;
+  UINT64      Result;
+
+  //
+  // If the result of addition doesn't overflow MAX_UINT64, then it's addition
+  //
+  Augend = 0x3a3a3a3a12121212;
+  Addend = 0x3a3a3a3a12121212;
+  Result = 0;
+  Status = SafeUint64Add(Augend, Addend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x7474747424242424, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Augend = 0xababababefefefef;
+  Addend = 0xbcbcbcbcdededede;
+  Status = SafeUint64Add(Augend, Addend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt8Add (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT8        Augend;
+  INT8        Addend;
+  INT8        Result;
+
+  //
+  // If the result of addition doesn't overflow MAX_INT8
+  // and doesn't underflow MIN_INT8, then it's addition
+  //
+  Augend = 0x3a;
+  Addend = 0x3a;
+  Result = 0;
+  Status = SafeInt8Add(Augend, Addend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x74, Result);
+
+  Augend = (-58);
+  Addend = (-58);
+  Status = SafeInt8Add(Augend, Addend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL((-116), Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Augend = 0x5a;
+  Addend = 0x5a;
+  Status = SafeInt8Add(Augend, Addend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Augend = (-90);
+  Addend = (-90);
+  Status = SafeInt8Add(Augend, Addend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt16Add (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT16       Augend;
+  INT16       Addend;
+  INT16       Result;
+
+  //
+  // If the result of addition doesn't overflow MAX_INT16
+  // and doesn't underflow MIN_INT16, then it's addition
+  //
+  Augend = 0x3a3a;
+  Addend = 0x3a3a;
+  Result = 0;
+  Status = SafeInt16Add(Augend, Addend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x7474, Result);
+
+  Augend = (-14906);
+  Addend = (-14906);
+  Status = SafeInt16Add(Augend, Addend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL((-29812), Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Augend = 0x5a5a;
+  Addend = 0x5a5a;
+  Status = SafeInt16Add(Augend, Addend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Augend = (-23130);
+  Addend = (-23130);
+  Status = SafeInt16Add(Augend, Addend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt32Add (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT32       Augend;
+  INT32       Addend;
+  INT32       Result;
+
+  //
+  // If the result of addition doesn't overflow MAX_INT32
+  // and doesn't underflow MIN_INT32, then it's addition
+  //
+  Augend = 0x3a3a3a3a;
+  Addend = 0x3a3a3a3a;
+  Result = 0;
+  Status = SafeInt32Add(Augend, Addend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x74747474, Result);
+
+  Augend = (-976894522);
+  Addend = (-976894522);
+  Status = SafeInt32Add(Augend, Addend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL((-1953789044), Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Augend = 0x5a5a5a5a;
+  Addend = 0x5a5a5a5a;
+  Status = SafeInt32Add(Augend, Addend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Augend = (-1515870810);
+  Addend = (-1515870810);
+  Status = SafeInt32Add(Augend, Addend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt64Add (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT64       Augend;
+  INT64       Addend;
+  INT64       Result;
+
+  //
+  // If the result of addition doesn't overflow MAX_INT64
+  // and doesn't underflow MIN_INT64, then it's addition
+  //
+  Augend = 0x3a3a3a3a3a3a3a3a;
+  Addend = 0x3a3a3a3a3a3a3a3a;
+  Result = 0;
+  Status = SafeInt64Add(Augend, Addend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x7474747474747474, Result);
+
+  Augend = (-4195730024608447034);
+  Addend = (-4195730024608447034);
+  Status = SafeInt64Add(Augend, Addend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL((-8391460049216894068), Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Augend = 0x5a5a5a5a5a5a5a5a;
+  Addend = 0x5a5a5a5a5a5a5a5a;
+  Status = SafeInt64Add(Augend, Addend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Augend = (-6510615555426900570);
+  Addend = (-6510615555426900570);
+  Status = SafeInt64Add(Augend, Addend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+//
+// Subtraction function tests:
+//
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint8Sub (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT8       Minuend;
+  UINT8       Subtrahend;
+  UINT8       Result;
+
+  //
+  // If Minuend >= Subtrahend, then it's subtraction
+  //
+  Minuend = 0x5a;
+  Subtrahend = 0x3b;
+  Result = 0;
+  Status = SafeUint8Sub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x1f, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Minuend = 0x5a;
+  Subtrahend = 0x6d;
+  Status = SafeUint8Sub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint16Sub (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT16      Minuend;
+  UINT16      Subtrahend;
+  UINT16      Result;
+
+  //
+  // If Minuend >= Subtrahend, then it's subtraction
+  //
+  Minuend = 0x5a5a;
+  Subtrahend = 0x3b3b;
+  Result = 0;
+  Status = SafeUint16Sub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x1f1f, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Minuend = 0x5a5a;
+  Subtrahend = 0x6d6d;
+  Status = SafeUint16Sub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint32Sub (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      Minuend;
+  UINT32      Subtrahend;
+  UINT32      Result;
+
+  //
+  // If Minuend >= Subtrahend, then it's subtraction
+  //
+  Minuend = 0x5a5a5a5a;
+  Subtrahend = 0x3b3b3b3b;
+  Result = 0;
+  Status = SafeUint32Sub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x1f1f1f1f, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Minuend = 0x5a5a5a5a;
+  Subtrahend = 0x6d6d6d6d;
+  Status = SafeUint32Sub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint64Sub (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Minuend;
+  UINT64      Subtrahend;
+  UINT64      Result;
+
+  //
+  // If Minuend >= Subtrahend, then it's subtraction
+  //
+  Minuend = 0x5a5a5a5a5a5a5a5a;
+  Subtrahend = 0x3b3b3b3b3b3b3b3b;
+  Result = 0;
+  Status = SafeUint64Sub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x1f1f1f1f1f1f1f1f, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Minuend = 0x5a5a5a5a5a5a5a5a;
+  Subtrahend = 0x6d6d6d6d6d6d6d6d;
+  Status = SafeUint64Sub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt8Sub (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT8        Minuend;
+  INT8        Subtrahend;
+  INT8        Result;
+
+  //
+  // If the result of subtractions doesn't overflow MAX_INT8 or
+  // underflow MIN_INT8, then it's subtraction
+  //
+  Minuend = 0x5a;
+  Subtrahend = 0x3a;
+  Result = 0;
+  Status = SafeInt8Sub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x20, Result);
+
+  Minuend = 58;
+  Subtrahend = 78;
+  Status = SafeInt8Sub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL((-20), Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Minuend = (-80);
+  Subtrahend = 80;
+  Status = SafeInt8Sub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Minuend = (80);
+  Subtrahend = (-80);
+  Status = SafeInt8Sub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt16Sub (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT16       Minuend;
+  INT16       Subtrahend;
+  INT16       Result;
+
+  //
+  // If the result of subtractions doesn't overflow MAX_INT16 or
+  // underflow MIN_INT16, then it's subtraction
+  //
+  Minuend = 0x5a5a;
+  Subtrahend = 0x3a3a;
+  Result = 0;
+  Status = SafeInt16Sub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x2020, Result);
+
+  Minuend = 0x3a3a;
+  Subtrahend = 0x5a5a;
+  Status = SafeInt16Sub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL((-8224), Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Minuend = (-31354);
+  Subtrahend = 31354;
+  Status = SafeInt16Sub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Minuend = (31354);
+  Subtrahend = (-31354);
+  Status = SafeInt16Sub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt32Sub (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT32       Minuend;
+  INT32       Subtrahend;
+  INT32       Result;
+
+  //
+  // If the result of subtractions doesn't overflow MAX_INT32 or
+  // underflow MIN_INT32, then it's subtraction
+  //
+  Minuend = 0x5a5a5a5a;
+  Subtrahend = 0x3a3a3a3a;
+  Result = 0;
+  Status = SafeInt32Sub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x20202020, Result);
+
+  Minuend = 0x3a3a3a3a;
+  Subtrahend = 0x5a5a5a5a;
+  Status = SafeInt32Sub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL((-538976288), Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Minuend = (-2054847098);
+  Subtrahend = 2054847098;
+  Status = SafeInt32Sub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Minuend = (2054847098);
+  Subtrahend = (-2054847098);
+  Status = SafeInt32Sub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt64Sub (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT64       Minuend;
+  INT64       Subtrahend;
+  INT64       Result;
+
+  //
+  // If the result of subtractions doesn't overflow MAX_INT64 or
+  // underflow MIN_INT64, then it's subtraction
+  //
+  Minuend = 0x5a5a5a5a5a5a5a5a;
+  Subtrahend = 0x3a3a3a3a3a3a3a3a;
+  Result = 0;
+  Status = SafeInt64Sub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x2020202020202020, Result);
+
+  Minuend = 0x3a3a3a3a3a3a3a3a;
+  Subtrahend = 0x5a5a5a5a5a5a5a5a;
+  Status = SafeInt64Sub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL((-2314885530818453536), Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Minuend = (-8825501086245354106);
+  Subtrahend = 8825501086245354106;
+  Status = SafeInt64Sub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  Minuend = (8825501086245354106);
+  Subtrahend = (-8825501086245354106);
+  Status = SafeInt64Sub(Minuend, Subtrahend, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+//
+// Multiplication function tests:
+//
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint8Mult (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT8       Multiplicand;
+  UINT8       Multiplier;
+  UINT8       Result;
+
+  //
+  // If the result of multiplication doesn't overflow MAX_UINT8, it will succeed
+  //
+  Multiplicand = 0x12;
+  Multiplier = 0xa;
+  Result = 0;
+  Status = SafeUint8Mult(Multiplicand, Multiplier, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0xb4, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Multiplicand = 0x12;
+  Multiplier = 0x23;
+  Status = SafeUint8Mult(Multiplicand, Multiplier, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint16Mult (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT16      Multiplicand;
+  UINT16      Multiplier;
+  UINT16      Result;
+
+  //
+  // If the result of multiplication doesn't overflow MAX_UINT16, it will succeed
+  //
+  Multiplicand = 0x212;
+  Multiplier = 0x7a;
+  Result = 0;
+  Status = SafeUint16Mult(Multiplicand, Multiplier, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0xfc94, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Multiplicand = 0x1234;
+  Multiplier = 0x213;
+  Status = SafeUint16Mult(Multiplicand, Multiplier, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint32Mult (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      Multiplicand;
+  UINT32      Multiplier;
+  UINT32      Result;
+
+  //
+  // If the result of multiplication doesn't overflow MAX_UINT32, it will succeed
+  //
+  Multiplicand = 0xa122a;
+  Multiplier = 0xd23;
+  Result = 0;
+  Status = SafeUint32Mult(Multiplicand, Multiplier, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x844c9dbe, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Multiplicand = 0xa122a;
+  Multiplier = 0xed23;
+  Status = SafeUint32Mult(Multiplicand, Multiplier, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint64Mult (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Multiplicand;
+  UINT64      Multiplier;
+  UINT64      Result;
+
+  //
+  // If the result of multiplication doesn't overflow MAX_UINT64, it will succeed
+  //
+  Multiplicand = 0x123456789a;
+  Multiplier = 0x1234567;
+  Result = 0;
+  Status = SafeUint64Mult(Multiplicand, Multiplier, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x14b66db9745a07f6, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Multiplicand = 0x123456789a;
+  Multiplier = 0x12345678;
+  Status = SafeUint64Mult(Multiplicand, Multiplier, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt8Mult (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT8        Multiplicand;
+  INT8        Multiplier;
+  INT8        Result;
+
+  //
+  // If the result of multiplication doesn't overflow MAX_INT8 and doesn't
+  // underflow MIN_UINT8, it will succeed
+  //
+  Multiplicand = 0x12;
+  Multiplier = 0x7;
+  Result = 0;
+  Status = SafeInt8Mult(Multiplicand, Multiplier, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x7e, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Multiplicand = 0x12;
+  Multiplier = 0xa;
+  Status = SafeInt8Mult(Multiplicand, Multiplier, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt16Mult (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT16       Multiplicand;
+  INT16       Multiplier;
+  INT16       Result;
+
+  //
+  // If the result of multiplication doesn't overflow MAX_INT16 and doesn't
+  // underflow MIN_UINT16, it will succeed
+  //
+  Multiplicand = 0x123;
+  Multiplier = 0x67;
+  Result = 0;
+  Status = SafeInt16Mult(Multiplicand, Multiplier, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x7515, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Multiplicand = 0x123;
+  Multiplier = 0xab;
+  Status = SafeInt16Mult(Multiplicand, Multiplier, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt32Mult (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT32       Multiplicand;
+  INT32       Multiplier;
+  INT32       Result;
+
+  //
+  // If the result of multiplication doesn't overflow MAX_INT32 and doesn't
+  // underflow MIN_UINT32, it will succeed
+  //
+  Multiplicand = 0x123456;
+  Multiplier = 0x678;
+  Result = 0;
+  Status = SafeInt32Mult(Multiplicand, Multiplier, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x75c28c50, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Multiplicand = 0x123456;
+  Multiplier = 0xabc;
+  Status = SafeInt32Mult(Multiplicand, Multiplier, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt64Mult (
+  IN UNIT_TEST_CONTEXT           Context
+  )
+{
+  EFI_STATUS  Status;
+  INT64       Multiplicand;
+  INT64       Multiplier;
+  INT64       Result;
+
+  //
+  // If the result of multiplication doesn't overflow MAX_INT64 and doesn't
+  // underflow MIN_UINT64, it will succeed
+  //
+  Multiplicand = 0x123456789;
+  Multiplier = 0x6789abcd;
+  Result = 0;
+  Status = SafeInt64Mult(Multiplicand, Multiplier, &Result);
+  UT_ASSERT_NOT_EFI_ERROR(Status);
+  UT_ASSERT_EQUAL(0x75cd9045220d6bb5, Result);
+
+  //
+  // Otherwise should result in an error status
+  //
+  Multiplicand = 0x123456789;
+  Multiplier = 0xa789abcd;
+  Status = SafeInt64Mult(Multiplicand, Multiplier, &Result);
+  UT_ASSERT_EQUAL(RETURN_BUFFER_TOO_SMALL, Status);
+
+  return UNIT_TEST_PASSED;
+}
+
+/**
+
+  Main fuction sets up the unit test environment
+
+**/
+EFI_STATUS
+EFIAPI
+UefiTestMain (
+  VOID
+  )
+{
+  EFI_STATUS                  Status;
+  UNIT_TEST_FRAMEWORK_HANDLE  Framework;
+  UNIT_TEST_SUITE_HANDLE      ConversionTestSuite;
+  UNIT_TEST_SUITE_HANDLE      AdditionSubtractionTestSuite;
+  UNIT_TEST_SUITE_HANDLE      MultiplicationTestSuite;
+
+  Framework = NULL;
+  ConversionTestSuite = NULL;
+  AdditionSubtractionTestSuite = NULL;
+  MultiplicationTestSuite = NULL;
+
+  DEBUG((DEBUG_INFO, "%a v%a\n", UNIT_TEST_NAME, UNIT_TEST_VERSION));
+
+  //
+  // Start setting up the test framework for running the tests.
+  //
+  Status = InitUnitTestFramework (&Framework, UNIT_TEST_NAME, gEfiCallerBaseName, UNIT_TEST_VERSION);
+  if (EFI_ERROR(Status)) {
+    DEBUG((DEBUG_ERROR, "Failed in InitUnitTestFramework. Status = %r\n", Status));
+    goto EXIT;
+  }
+
+  ///
+  // Test the conversion functions
+  //
+  Status = CreateUnitTestSuite (&ConversionTestSuite, Framework, "Int Safe Conversions Test Suite", "Common.SafeInt.Convert", NULL, NULL);
+  if (EFI_ERROR(Status)) {
+    DEBUG((DEBUG_ERROR, "Failed in CreateUnitTestSuite for Conversions Test Suite\n"));
+    Status = EFI_OUT_OF_RESOURCES;
+    goto EXIT;
+  }
+  AddTestCase(ConversionTestSuite, "Test SafeInt8ToUint8",    "TestSafeInt8ToUint8",    TestSafeInt8ToUint8,    NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt8ToUint16",   "TestSafeInt8ToUint16",   TestSafeInt8ToUint16,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt8ToUint32",   "TestSafeInt8ToUint32",   TestSafeInt8ToUint32,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt8ToUintn",    "TestSafeInt8ToUintn",    TestSafeInt8ToUintn,    NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt8ToUint64",   "TestSafeInt8ToUint64",   TestSafeInt8ToUint64,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUint8ToInt8",    "TestSafeUint8ToInt8",    TestSafeUint8ToInt8,    NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUint8ToChar8",   "TestSafeUint8ToChar8",   TestSafeUint8ToChar8,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt16ToInt8",    "TestSafeInt16ToInt8",    TestSafeInt16ToInt8,    NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt16ToChar8",   "TestSafeInt16ToChar8",   TestSafeInt16ToChar8,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt16ToUint8",   "TestSafeInt16ToUint8",   TestSafeInt16ToUint8,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt16ToUint16",  "TestSafeInt16ToUint16",  TestSafeInt16ToUint16,  NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt16ToUint32",  "TestSafeInt16ToUint32",  TestSafeInt16ToUint32,  NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt16ToUintn",   "TestSafeInt16ToUintn",   TestSafeInt16ToUintn,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt16ToUint64",  "TestSafeInt16ToUint64",  TestSafeInt16ToUint64,  NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUint16ToInt8",   "TestSafeUint16ToInt8",   TestSafeUint16ToInt8,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUint16ToChar8",  "TestSafeUint16ToChar8",  TestSafeUint16ToChar8,  NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUint16ToUint8",  "TestSafeUint16ToUint8",  TestSafeUint16ToUint8,  NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUint16ToInt16",  "TestSafeUint16ToInt16",  TestSafeUint16ToInt16,  NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt32ToInt8",    "TestSafeInt32ToInt8",    TestSafeInt32ToInt8,    NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt32ToChar8",   "TestSafeInt32ToChar8",   TestSafeInt32ToChar8,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt32ToUint8",   "TestSafeInt32ToUint8",   TestSafeInt32ToUint8,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt32ToInt16",   "TestSafeInt32ToInt16",   TestSafeInt32ToInt16,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt32ToUint16",  "TestSafeInt32ToUint16",  TestSafeInt32ToUint16,  NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt32ToUint32",  "TestSafeInt32ToUint32",  TestSafeInt32ToUint32,  NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt32ToUintn",   "TestSafeInt32ToUintn",   TestSafeInt32ToUintn,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt32ToUint64",  "TestSafeInt32ToUint64",  TestSafeInt32ToUint64,  NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUint32ToInt8",   "TestSafeUint32ToInt8",   TestSafeUint32ToInt8,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUint32ToChar8",  "TestSafeUint32ToChar8",  TestSafeUint32ToChar8,  NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUint32ToUint8",  "TestSafeUint32ToUint8",  TestSafeUint32ToUint8,  NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUint32ToInt16",  "TestSafeUint32ToInt16",  TestSafeUint32ToInt16,  NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUint32ToUint16", "TestSafeUint32ToUint16", TestSafeUint32ToUint16, NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUint32ToInt32",  "TestSafeUint32ToInt32",  TestSafeUint32ToInt32,  NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUint32ToIntn",   "TestSafeUint32ToIntn",   TestSafeUint32ToIntn,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeIntnToInt8",     "TestSafeIntnToInt8",     TestSafeIntnToInt8,     NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeIntnToChar8",    "TestSafeIntnToChar8",    TestSafeIntnToChar8,    NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeIntnToUint8",    "TestSafeIntnToUint8",    TestSafeIntnToUint8,    NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeIntnToInt16",    "TestSafeIntnToInt16",    TestSafeIntnToInt16,    NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeIntnToUint16",   "TestSafeIntnToUint16",   TestSafeIntnToUint16,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeIntnToInt32",    "TestSafeIntnToInt32",    TestSafeIntnToInt32,    NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeIntnToUint32",   "TestSafeIntnToUint32",   TestSafeIntnToUint32,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeIntnToUintn",    "TestSafeIntnToUintn",    TestSafeIntnToUintn,    NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeIntnToUint64",   "TestSafeIntnToUint64",   TestSafeIntnToUint64,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUintnToInt8",    "TestSafeUintnToInt8",    TestSafeUintnToInt8,    NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUintnToChar8",   "TestSafeUintnToChar8",   TestSafeUintnToChar8,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUintnToUint8",   "TestSafeUintnToUint8",   TestSafeUintnToUint8,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUintnToInt16",   "TestSafeUintnToInt16",   TestSafeUintnToInt16,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUintnToUint16",  "TestSafeUintnToUint16",  TestSafeUintnToUint16,  NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUintnToInt32",   "TestSafeUintnToInt32",   TestSafeUintnToInt32,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUintnToUint32",  "TestSafeUintnToUint32",  TestSafeUintnToUint32,  NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUintnToIntn",    "TestSafeUintnToIntn",    TestSafeUintnToIntn,    NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUintnToInt64",   "TestSafeUintnToInt64",   TestSafeUintnToInt64,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt64ToInt8",    "TestSafeInt64ToInt8",    TestSafeInt64ToInt8,    NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt64ToChar8",   "TestSafeInt64ToChar8",   TestSafeInt64ToChar8,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt64ToUint8",   "TestSafeInt64ToUint8",   TestSafeInt64ToUint8,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt64ToInt16",   "TestSafeInt64ToInt16",   TestSafeInt64ToInt16,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt64ToUint16",  "TestSafeInt64ToUint16",  TestSafeInt64ToUint16,  NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt64ToInt32",   "TestSafeInt64ToInt32",   TestSafeInt64ToInt32,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt64ToUint32",  "TestSafeInt64ToUint32",  TestSafeInt64ToUint32,  NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt64ToIntn",    "TestSafeInt64ToIntn",    TestSafeInt64ToIntn,    NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt64ToUintn",   "TestSafeInt64ToUintn",   TestSafeInt64ToUintn,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeInt64ToUint64",  "TestSafeInt64ToUint64",  TestSafeInt64ToUint64,  NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUint64ToInt8",   "TestSafeUint64ToInt8",   TestSafeUint64ToInt8,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUint64ToChar8",  "TestSafeUint64ToChar8",  TestSafeUint64ToChar8,  NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUint64ToUint8",  "TestSafeUint64ToUint8",  TestSafeUint64ToUint8,  NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUint64ToInt16",  "TestSafeUint64ToInt16",  TestSafeUint64ToInt16,  NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUint64ToUint16", "TestSafeUint64ToUint16", TestSafeUint64ToUint16, NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUint64ToInt32",  "TestSafeUint64ToInt32",  TestSafeUint64ToInt32,  NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUint64ToUint32", "TestSafeUint64ToUint32", TestSafeUint64ToUint32, NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUint64ToIntn",   "TestSafeUint64ToIntn",   TestSafeUint64ToIntn,   NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUint64ToUintn",  "TestSafeUint64ToUintn",  TestSafeUint64ToUintn,  NULL, NULL, NULL);
+  AddTestCase(ConversionTestSuite, "Test SafeUint64ToInt64",  "TestSafeUint64ToInt64",  TestSafeUint64ToInt64,  NULL, NULL, NULL);
+
+  //
+  // Test the addition and subtraction functions
+  //
+  Status = CreateUnitTestSuite(&AdditionSubtractionTestSuite, Framework, "Int Safe Add/Subtract Test Suite", "Common.SafeInt.AddSubtract", NULL, NULL);
+  if (EFI_ERROR(Status)) {
+    DEBUG((DEBUG_ERROR, "Failed in CreateUnitTestSuite for Int Safe Add/Subtract Test Suite\n"));
+    Status = EFI_OUT_OF_RESOURCES;
+    goto EXIT;
+  }
+  AddTestCase(AdditionSubtractionTestSuite, "Test SafeUint8Add",  "TestSafeUint8Add",  TestSafeUint8Add,  NULL, NULL, NULL);
+  AddTestCase(AdditionSubtractionTestSuite, "Test SafeUint16Add", "TestSafeUint16Add", TestSafeUint16Add, NULL, NULL, NULL);
+  AddTestCase(AdditionSubtractionTestSuite, "Test SafeUint32Add", "TestSafeUint32Add", TestSafeUint32Add, NULL, NULL, NULL);
+  AddTestCase(AdditionSubtractionTestSuite, "Test SafeUintnAdd",  "TestSafeUintnAdd",  TestSafeUintnAdd,  NULL, NULL, NULL);
+  AddTestCase(AdditionSubtractionTestSuite, "Test SafeUint64Add", "TestSafeUint64Add", TestSafeUint64Add, NULL, NULL, NULL);
+  AddTestCase(AdditionSubtractionTestSuite, "Test SafeInt8Add",   "TestSafeInt8Add",   TestSafeInt8Add,   NULL, NULL, NULL);
+  AddTestCase(AdditionSubtractionTestSuite, "Test SafeInt16Add",  "TestSafeInt16Add",  TestSafeInt16Add,  NULL, NULL, NULL);
+  AddTestCase(AdditionSubtractionTestSuite, "Test SafeInt32Add",  "TestSafeInt32Add",  TestSafeInt32Add,  NULL, NULL, NULL);
+  AddTestCase(AdditionSubtractionTestSuite, "Test SafeIntnAdd",   "TestSafeIntnAdd",   TestSafeIntnAdd,   NULL, NULL, NULL);
+  AddTestCase(AdditionSubtractionTestSuite, "Test SafeInt64Add",  "TestSafeInt64Add",  TestSafeInt64Add,  NULL, NULL, NULL);
+  AddTestCase(AdditionSubtractionTestSuite, "Test SafeUint8Sub",  "TestSafeUint8Sub",  TestSafeUint8Sub,  NULL, NULL, NULL);
+  AddTestCase(AdditionSubtractionTestSuite, "Test SafeUint16Sub", "TestSafeUint16Sub", TestSafeUint16Sub, NULL, NULL, NULL);
+  AddTestCase(AdditionSubtractionTestSuite, "Test SafeUint32Sub", "TestSafeUint32Sub", TestSafeUint32Sub, NULL, NULL, NULL);
+  AddTestCase(AdditionSubtractionTestSuite, "Test SafeUintnSub",  "TestSafeUintnSub",  TestSafeUintnSub,  NULL, NULL, NULL);
+  AddTestCase(AdditionSubtractionTestSuite, "Test SafeUint64Sub", "TestSafeUint64Sub", TestSafeUint64Sub, NULL, NULL, NULL);
+  AddTestCase(AdditionSubtractionTestSuite, "Test SafeInt8Sub",   "TestSafeInt8Sub",   TestSafeInt8Sub,   NULL, NULL, NULL);
+  AddTestCase(AdditionSubtractionTestSuite, "Test SafeInt16Sub",  "TestSafeInt16Sub",  TestSafeInt16Sub,  NULL, NULL, NULL);
+  AddTestCase(AdditionSubtractionTestSuite, "Test SafeInt32Sub",  "TestSafeInt32Sub",  TestSafeInt32Sub,  NULL, NULL, NULL);
+  AddTestCase(AdditionSubtractionTestSuite, "Test SafeIntnSub",   "TestSafeIntnSub",   TestSafeIntnSub,   NULL, NULL, NULL);
+  AddTestCase(AdditionSubtractionTestSuite, "Test SafeInt64Sub",  "TestSafeInt64Sub",  TestSafeInt64Sub,  NULL, NULL, NULL);
+
+  //
+  // Test the multiplication functions
+  //
+  Status = CreateUnitTestSuite(&MultiplicationTestSuite, Framework, "Int Safe Multiply Test Suite", "Common.SafeInt.Multiply", NULL, NULL);
+  if (EFI_ERROR(Status)) {
+    DEBUG((DEBUG_ERROR, "Failed in CreateUnitTestSuite for Int Safe Multiply Test Suite\n"));
+    Status = EFI_OUT_OF_RESOURCES;
+    goto EXIT;
+  }
+  AddTestCase(MultiplicationTestSuite, "Test SafeUint8Mult",  "TestSafeUint8Mult",  TestSafeUint8Mult,  NULL, NULL, NULL);
+  AddTestCase(MultiplicationTestSuite, "Test SafeUint16Mult", "TestSafeUint16Mult", TestSafeUint16Mult, NULL, NULL, NULL);
+  AddTestCase(MultiplicationTestSuite, "Test SafeUint32Mult", "TestSafeUint32Mult", TestSafeUint32Mult, NULL, NULL, NULL);
+  AddTestCase(MultiplicationTestSuite, "Test SafeUintnMult",  "TestSafeUintnMult",  TestSafeUintnMult,  NULL, NULL, NULL);
+  AddTestCase(MultiplicationTestSuite, "Test SafeUint64Mult", "TestSafeUint64Mult", TestSafeUint64Mult, NULL, NULL, NULL);
+  AddTestCase(MultiplicationTestSuite, "Test SafeInt8Mult",   "TestSafeInt8Mult",   TestSafeInt8Mult,   NULL, NULL, NULL);
+  AddTestCase(MultiplicationTestSuite, "Test SafeInt16Mult",  "TestSafeInt16Mult",  TestSafeInt16Mult,  NULL, NULL, NULL);
+  AddTestCase(MultiplicationTestSuite, "Test SafeInt32Mult",  "TestSafeInt32Mult",  TestSafeInt32Mult,  NULL, NULL, NULL);
+  AddTestCase(MultiplicationTestSuite, "Test SafeIntnMult",   "TestSafeIntnMult",   TestSafeIntnMult,   NULL, NULL, NULL);
+  AddTestCase(MultiplicationTestSuite, "Test SafeInt64Mult",  "TestSafeInt64Mult",  TestSafeInt64Mult,  NULL, NULL, NULL);
+
+  //
+  // Execute the tests.
+  //
+  Status = RunAllTestSuites(Framework);
+
+EXIT:
+  if (Framework != NULL) {
+    FreeUnitTestFramework(Framework);
+  }
+
+  return Status;
+}
+
+EFI_STATUS
+EFIAPI
+PeiEntryPoint (
+  IN EFI_PEI_FILE_HANDLE       FileHandle,
+  IN CONST EFI_PEI_SERVICES    **PeiServices
+  )
+{
+  return UefiTestMain ();
+}
+
+EFI_STATUS
+EFIAPI
+DxeEntryPoint (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  return UefiTestMain ();
+}
+
+int
+main (
+  int argc,
+  char *argv[]
+  )
+{
+  return UefiTestMain ();
+}
diff --git a/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLib.h b/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLib.h
new file mode 100644
index 0000000000..7957c99a85
--- /dev/null
+++ b/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLib.h
@@ -0,0 +1,123 @@
+/** @file
+  UEFI OS based application for unit testing the SafeIntLib.
+
+  Copyright (c) Microsoft Corporation.<BR>
+  Copyright (c) 2018 - 2020, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _TEST_BASE_SAFE_INT_LIB_H_
+#define _TEST_BASE_SAFE_INT_LIB_H_
+
+#include <PiPei.h>
+#include <Uefi.h>
+#include <Library/UefiLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UnitTestLib.h>
+#include <Library/SafeIntLib.h>
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt32ToUintn(
+  IN UNIT_TEST_CONTEXT           Context
+  );
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint32ToIntn(
+  IN UNIT_TEST_CONTEXT           Context
+  );
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeIntnToInt32(
+  IN UNIT_TEST_CONTEXT           Context
+  );
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeIntnToUint32(
+  IN UNIT_TEST_CONTEXT           Context
+  );
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUintnToUint32(
+  IN UNIT_TEST_CONTEXT           Context
+  );
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUintnToIntn(
+  IN UNIT_TEST_CONTEXT           Context
+  );
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUintnToInt64(
+  IN UNIT_TEST_CONTEXT           Context
+  );
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt64ToIntn(
+  IN UNIT_TEST_CONTEXT           Context
+  );
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeInt64ToUintn(
+  IN UNIT_TEST_CONTEXT           Context
+  );
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint64ToIntn(
+  IN UNIT_TEST_CONTEXT           Context
+  );
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUint64ToUintn(
+  IN UNIT_TEST_CONTEXT           Context
+  );
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUintnAdd(
+  IN UNIT_TEST_CONTEXT           Context
+  );
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeIntnAdd(
+  IN UNIT_TEST_CONTEXT           Context
+  );
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUintnSub(
+  IN UNIT_TEST_CONTEXT           Context
+  );
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeIntnSub(
+  IN UNIT_TEST_CONTEXT           Context
+  );
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeUintnMult(
+  IN UNIT_TEST_CONTEXT           Context
+  );
+
+UNIT_TEST_STATUS
+EFIAPI
+TestSafeIntnMult(
+  IN UNIT_TEST_CONTEXT           Context
+  );
+
+#endif
diff --git a/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLib.uni b/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLib.uni
new file mode 100644
index 0000000000..956835c30d
--- /dev/null
+++ b/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLib.uni
@@ -0,0 +1,13 @@
+// /** @file
+// Application that Unit Tests the SafeIntLib
+//
+// Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT             #language en-US "Application that Unit Tests the SafeIntLib"
+
+#string STR_MODULE_DESCRIPTION          #language en-US "Application that Unit Tests the SafeIntLib."
+
diff --git a/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibDxe.inf b/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibDxe.inf
new file mode 100644
index 0000000000..de67b04bd5
--- /dev/null
+++ b/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibDxe.inf
@@ -0,0 +1,45 @@
+## @file
+# DXE Driver that Unit Tests the SafeIntLib
+#
+# Copyright (c) Microsoft Corporation.<BR>
+# Copyright (c) 2019 - 2020, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+  INF_VERSION     = 0x00010005
+  BASE_NAME       = TestBaseSafeIntLibDxe
+  MODULE_UNI_FILE = TestBaseSafeIntLib.uni
+  FILE_GUID       = 9729DB60-FB9D-4625-9EE1-93B21EC246B8
+  MODULE_TYPE     = DXE_DRIVER
+  VERSION_STRING  = 1.0
+  ENTRY_POINT     = DxeEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+
+[Sources]
+  TestBaseSafeIntLib.c
+  TestBaseSafeIntLib.h
+
+[Sources.Ia32, Sources.ARM]
+  SafeIntLibUintnIntnUnitTests32.c
+
+[Sources.X64, Sources.AARCH64]
+  SafeIntLibUintnIntnUnitTests64.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  UefiDriverEntryPoint
+  BaseLib
+  DebugLib
+  SafeIntLib
+  UnitTestLib
+
+[Depex]
+  TRUE
diff --git a/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibHost.inf b/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibHost.inf
new file mode 100644
index 0000000000..35c93fdeac
--- /dev/null
+++ b/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibHost.inf
@@ -0,0 +1,40 @@
+## @file
+# Host OS based Application that Unit Tests the SafeIntLib
+#
+# Copyright (c) Microsoft Corporation.<BR>
+# Copyright (c) 2019 - 2020, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+  INF_VERSION     = 0x00010005
+  BASE_NAME       = TestBaseSafeIntLibHost
+  MODULE_UNI_FILE = TestBaseSafeIntLib.uni
+  FILE_GUID       = 95487689-9E30-41AD-B773-3650C94BCBE2
+  MODULE_TYPE     = HOST_APPLICATION
+  VERSION_STRING  = 1.0
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+
+[Sources]
+  TestBaseSafeIntLib.c
+  TestBaseSafeIntLib.h
+
+[Sources.Ia32, Sources.ARM]
+  SafeIntLibUintnIntnUnitTests32.c
+
+[Sources.X64, Sources.AARCH64]
+  SafeIntLibUintnIntnUnitTests64.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  BaseLib
+  DebugLib
+  SafeIntLib
+  UnitTestLib
diff --git a/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibPei.inf b/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibPei.inf
new file mode 100644
index 0000000000..c8ba4f44ef
--- /dev/null
+++ b/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibPei.inf
@@ -0,0 +1,45 @@
+## @file
+# PEIM that Unit Tests the SafeIntLib
+#
+# Copyright (c) Microsoft Corporation.<BR>
+# Copyright (c) 2019 - 2020, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+  INF_VERSION     = 0x00010005
+  BASE_NAME       = TestBaseSafeIntLibPei
+  MODULE_UNI_FILE = TestBaseSafeIntLib.uni
+  FILE_GUID       = 7D910602-ED53-45E6-826E-8266705B9734
+  MODULE_TYPE     = PEIM
+  VERSION_STRING  = 1.0
+  ENTRY_POINT     = PeiEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+
+[Sources]
+  TestBaseSafeIntLib.c
+  TestBaseSafeIntLib.h
+
+[Sources.Ia32, Sources.ARM]
+  SafeIntLibUintnIntnUnitTests32.c
+
+[Sources.X64, Sources.AARCH64]
+  SafeIntLibUintnIntnUnitTests64.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  PeimEntryPoint
+  BaseLib
+  DebugLib
+  SafeIntLib
+  UnitTestLib
+
+[Depex]
+  TRUE
diff --git a/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibSmm.inf b/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibSmm.inf
new file mode 100644
index 0000000000..df7288501d
--- /dev/null
+++ b/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibSmm.inf
@@ -0,0 +1,45 @@
+## @file
+# SMM Driver that Unit Tests the SafeIntLib
+#
+# Copyright (c) Microsoft Corporation.<BR>
+# Copyright (c) 2019 - 2020, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+  INF_VERSION     = 0x00010005
+  BASE_NAME       = TestBaseSafeIntLibSmm
+  MODULE_UNI_FILE = TestBaseSafeIntLib.uni
+  FILE_GUID       = 2F2A1907-B1B4-4E33-8B83-62A60AB4F0D4
+  MODULE_TYPE     = DXE_SMM_DRIVER
+  VERSION_STRING  = 1.0
+  ENTRY_POINT     = DxeEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+
+[Sources]
+  TestBaseSafeIntLib.c
+  TestBaseSafeIntLib.h
+
+[Sources.Ia32, Sources.ARM]
+  SafeIntLibUintnIntnUnitTests32.c
+
+[Sources.X64, Sources.AARCH64]
+  SafeIntLibUintnIntnUnitTests64.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  UefiDriverEntryPoint
+  BaseLib
+  DebugLib
+  SafeIntLib
+  UnitTestLib
+
+[Depex]
+  TRUE
diff --git a/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibUefiShell.inf b/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibUefiShell.inf
new file mode 100644
index 0000000000..5a13c1c845
--- /dev/null
+++ b/MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibUefiShell.inf
@@ -0,0 +1,42 @@
+## @file
+# UEFI Shell based Application that Unit Tests the SafeIntLib
+#
+# Copyright (c) Microsoft Corporation.<BR>
+# Copyright (c) 2019 - 2020, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+  INF_VERSION     = 0x00010005
+  BASE_NAME       = TestBaseSafeIntLibUefiShell
+  MODULE_UNI_FILE = TestBaseSafeIntLib.uni
+  FILE_GUID       = 1F91B73E-5B6A-4317-80E8-E7C36A3C7AF4
+  MODULE_TYPE     = UEFI_APPLICATION
+  VERSION_STRING  = 1.0
+  ENTRY_POINT     = DxeEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+
+[Sources]
+  TestBaseSafeIntLib.c
+  TestBaseSafeIntLib.h
+
+[Sources.Ia32, Sources.ARM]
+  SafeIntLibUintnIntnUnitTests32.c
+
+[Sources.X64, Sources.AARCH64]
+  SafeIntLibUintnIntnUnitTests64.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  UefiApplicationEntryPoint
+  BaseLib
+  DebugLib
+  SafeIntLib
+  UnitTestLib
-- 
2.21.0.windows.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [Patch v2 09/11] MdeModulePkg: Add DxeResetSystemLib unit test
  2020-02-07 18:13 [Patch v2 00/11] Add Unit Test Framework Michael D Kinney
                   ` (7 preceding siblings ...)
  2020-02-07 18:13 ` [Patch v2 08/11] MdePkg/Test: Add SafeIntLib and BaseLib Base64 unit tests Michael D Kinney
@ 2020-02-07 18:13 ` Michael D Kinney
  2020-02-07 18:13 ` [Patch v2 10/11] .azurepipelines: Enable CI for UnitTestFrameworkPkg and host tests Michael D Kinney
  2020-02-07 18:13 ` [Patch v2 11/11] Maintainers.txt: Add UnitTestFrameworkPkg Michael D Kinney
  10 siblings, 0 replies; 13+ messages in thread
From: Michael D Kinney @ 2020-02-07 18:13 UTC (permalink / raw)
  To: devel; +Cc: Sean Brogan, Bret Barkelew, Liming Gao, Hao A Wu

https://bugzilla.tianocore.org/show_bug.cgi?id=2505

* Add unit test of DxeResetSystemLib library
  instance that uses cmocka interfaces to mock the
  UEFI Runtime Services Table and its ResetSystem()
  service.  When a unit test uses the cmocka
  interfaces, the unit test does not support being
  run from target environments.

  cmocka APIs: https://api.cmocka.org/index.html

  This example puts the unit test in a UnitTest
  directory below the library INF file and this location
  means the unit test is only designed to work this
  this one library instance.

* Add Test/MdeModulePkgHostTest.dsc to build host
  based unit tests

Cc: Sean Brogan <sean.brogan@microsoft.com>
Cc: Bret Barkelew <Bret.Barkelew@microsoft.com>
Cc: Liming Gao <liming.gao@intel.com>
Cc: Hao A Wu <hao.a.wu@intel.com>
Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com>
Acked-by: Hao A Wu <hao.a.wu@intel.com>
Reviewed-by: Bret Barkelew <Bret.Barkelew@microsoft.com>
---
 .../UnitTest/DxeResetSystemLibUnitTest.c      | 312 ++++++++++++++++++
 .../DxeResetSystemLibUnitTestHost.inf         |  34 ++
 .../MockUefiRuntimeServicesTableLib.c         |  13 +
 .../MockUefiRuntimeServicesTableLib.inf       |  25 ++
 MdeModulePkg/MdeModulePkg.ci.yaml             |  13 +-
 MdeModulePkg/Test/MdeModulePkgHostTest.dsc    |  32 ++
 6 files changed, 428 insertions(+), 1 deletion(-)
 create mode 100644 MdeModulePkg/Library/DxeResetSystemLib/UnitTest/DxeResetSystemLibUnitTest.c
 create mode 100644 MdeModulePkg/Library/DxeResetSystemLib/UnitTest/DxeResetSystemLibUnitTestHost.inf
 create mode 100644 MdeModulePkg/Library/DxeResetSystemLib/UnitTest/MockUefiRuntimeServicesTableLib.c
 create mode 100644 MdeModulePkg/Library/DxeResetSystemLib/UnitTest/MockUefiRuntimeServicesTableLib.inf
 create mode 100644 MdeModulePkg/Test/MdeModulePkgHostTest.dsc

diff --git a/MdeModulePkg/Library/DxeResetSystemLib/UnitTest/DxeResetSystemLibUnitTest.c b/MdeModulePkg/Library/DxeResetSystemLib/UnitTest/DxeResetSystemLibUnitTest.c
new file mode 100644
index 0000000000..3bba38b579
--- /dev/null
+++ b/MdeModulePkg/Library/DxeResetSystemLib/UnitTest/DxeResetSystemLibUnitTest.c
@@ -0,0 +1,312 @@
+/** @file
+  Unit tests of the DxeResetSystemLib instance of the ResetSystemLib class
+
+  Copyright (C) Microsoft Corporation.
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include <Uefi.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+#include <Library/UnitTestLib.h>
+#include <Library/ResetSystemLib.h>
+
+#define UNIT_TEST_APP_NAME        "DxeResetSystemLib Unit Tests"
+#define UNIT_TEST_APP_VERSION     "1.0"
+
+/**
+  Resets the entire platform.
+
+  @param[in]  ResetType         The type of reset to perform.
+  @param[in]  ResetStatus       The status code for the reset.
+  @param[in]  DataSize          The size, in bytes, of ResetData.
+  @param[in]  ResetData         For a ResetType of EfiResetCold, EfiResetWarm, or
+                                EfiResetShutdown the data buffer starts with a Null-terminated
+                                string, optionally followed by additional binary data.
+                                The string is a description that the caller may use to further
+                                indicate the reason for the system reset.
+                                For a ResetType of EfiResetPlatformSpecific the data buffer
+                                also starts with a Null-terminated string that is followed
+                                by an EFI_GUID that describes the specific type of reset to perform.
+**/
+STATIC
+VOID
+EFIAPI
+MockResetSystem (
+  IN EFI_RESET_TYPE           ResetType,
+  IN EFI_STATUS               ResetStatus,
+  IN UINTN                    DataSize,
+  IN VOID                     *ResetData OPTIONAL
+  )
+{
+  check_expected_ptr (ResetType);
+  check_expected_ptr (ResetStatus);
+
+  //
+  // NOTE: Mocked functions can also return values, but that
+  //       is for another demo.
+}
+
+///
+/// Mock version of the UEFI Runtime Services Table
+///
+EFI_RUNTIME_SERVICES  MockRuntime = {
+  {
+    EFI_RUNTIME_SERVICES_SIGNATURE,     // Signature
+    EFI_RUNTIME_SERVICES_REVISION,      // Revision
+    sizeof (EFI_RUNTIME_SERVICES),      // HeaderSize
+    0,                                  // CRC32
+    0                                   // Reserved
+  },
+  NULL,               // GetTime
+  NULL,               // SetTime
+  NULL,               // GetWakeupTime
+  NULL,               // SetWakeupTime
+  NULL,               // SetVirtualAddressMap
+  NULL,               // ConvertPointer
+  NULL,               // GetVariable
+  NULL,               // GetNextVariableName
+  NULL,               // SetVariable
+  NULL,               // GetNextHighMonotonicCount
+  MockResetSystem,    // ResetSystem
+  NULL,               // UpdateCapsule
+  NULL,               // QueryCapsuleCapabilities
+  NULL                // QueryVariableInfo
+};
+
+/**
+  Unit test for ColdReset () API of the ResetSystemLib.
+
+  @param[in]  Context    [Optional] An optional parameter that enables:
+                         1) test-case reuse with varied parameters and
+                         2) test-case re-entry for Target tests that need a
+                         reboot.  This parameter is a VOID* and it is the
+                         responsibility of the test author to ensure that the
+                         contents are well understood by all test cases that may
+                         consume it.
+
+  @retval  UNIT_TEST_PASSED             The Unit test has completed and the test
+                                        case was successful.
+  @retval  UNIT_TEST_ERROR_TEST_FAILED  A test case assertion has failed.
+**/
+UNIT_TEST_STATUS
+EFIAPI
+ResetColdShouldIssueAColdReset (
+  IN UNIT_TEST_CONTEXT  Context
+  )
+{
+  expect_value (MockResetSystem, ResetType, EfiResetCold);
+  expect_value (MockResetSystem, ResetStatus, EFI_SUCCESS);
+
+  ResetCold ();
+
+  return UNIT_TEST_PASSED;
+}
+
+/**
+  Unit test for WarmReset () API of the ResetSystemLib.
+
+  @param[in]  Context    [Optional] An optional parameter that enables:
+                         1) test-case reuse with varied parameters and
+                         2) test-case re-entry for Target tests that need a
+                         reboot.  This parameter is a VOID* and it is the
+                         responsibility of the test author to ensure that the
+                         contents are well understood by all test cases that may
+                         consume it.
+
+  @retval  UNIT_TEST_PASSED             The Unit test has completed and the test
+                                        case was successful.
+  @retval  UNIT_TEST_ERROR_TEST_FAILED  A test case assertion has failed.
+**/
+UNIT_TEST_STATUS
+EFIAPI
+ResetWarmShouldIssueAWarmReset (
+  IN UNIT_TEST_CONTEXT  Context
+  )
+{
+  expect_value (MockResetSystem, ResetType, EfiResetWarm);
+  expect_value (MockResetSystem, ResetStatus, EFI_SUCCESS);
+
+  ResetWarm ();
+
+  return UNIT_TEST_PASSED;
+}
+
+/**
+  Unit test for ResetShutdown () API of the ResetSystemLib.
+
+  @param[in]  Context    [Optional] An optional parameter that enables:
+                         1) test-case reuse with varied parameters and
+                         2) test-case re-entry for Target tests that need a
+                         reboot.  This parameter is a VOID* and it is the
+                         responsibility of the test author to ensure that the
+                         contents are well understood by all test cases that may
+                         consume it.
+
+  @retval  UNIT_TEST_PASSED             The Unit test has completed and the test
+                                        case was successful.
+  @retval  UNIT_TEST_ERROR_TEST_FAILED  A test case assertion has failed.
+**/
+UNIT_TEST_STATUS
+EFIAPI
+ResetShutdownShouldIssueAShutdown (
+  IN UNIT_TEST_CONTEXT  Context
+  )
+{
+  expect_value (MockResetSystem, ResetType, EfiResetShutdown);
+  expect_value (MockResetSystem, ResetStatus, EFI_SUCCESS);
+
+  ResetShutdown ();
+
+  return UNIT_TEST_PASSED;
+}
+
+/**
+  Unit test for ResetPlatformSpecific () API of the ResetSystemLib.
+
+  @param[in]  Context    [Optional] An optional parameter that enables:
+                         1) test-case reuse with varied parameters and
+                         2) test-case re-entry for Target tests that need a
+                         reboot.  This parameter is a VOID* and it is the
+                         responsibility of the test author to ensure that the
+                         contents are well understood by all test cases that may
+                         consume it.
+
+  @retval  UNIT_TEST_PASSED             The Unit test has completed and the test
+                                        case was successful.
+  @retval  UNIT_TEST_ERROR_TEST_FAILED  A test case assertion has failed.
+**/
+UNIT_TEST_STATUS
+EFIAPI
+ResetPlatformSpecificShouldIssueAPlatformSpecificReset (
+  IN UNIT_TEST_CONTEXT  Context
+  )
+{
+  expect_value (MockResetSystem, ResetType, EfiResetPlatformSpecific);
+  expect_value (MockResetSystem, ResetStatus, EFI_SUCCESS);
+
+  ResetPlatformSpecific (0, NULL);
+
+  return UNIT_TEST_PASSED;
+}
+
+/**
+  Unit test for ResetSystem () API of the ResetSystemLib.
+
+  @param[in]  Context    [Optional] An optional parameter that enables:
+                         1) test-case reuse with varied parameters and
+                         2) test-case re-entry for Target tests that need a
+                         reboot.  This parameter is a VOID* and it is the
+                         responsibility of the test author to ensure that the
+                         contents are well understood by all test cases that may
+                         consume it.
+
+  @retval  UNIT_TEST_PASSED             The Unit test has completed and the test
+                                        case was successful.
+  @retval  UNIT_TEST_ERROR_TEST_FAILED  A test case assertion has failed.
+**/
+UNIT_TEST_STATUS
+EFIAPI
+ResetSystemShouldPassTheParametersThrough (
+  IN UNIT_TEST_CONTEXT  Context
+  )
+{
+  expect_value (MockResetSystem, ResetType, EfiResetCold);
+  expect_value (MockResetSystem, ResetStatus, EFI_SUCCESS);
+
+  ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL);
+
+  expect_value (MockResetSystem, ResetType, EfiResetShutdown);
+  expect_value (MockResetSystem, ResetStatus, EFI_SUCCESS);
+
+  ResetSystem (EfiResetShutdown, EFI_SUCCESS, 0, NULL);
+
+  return UNIT_TEST_PASSED;
+}
+
+/**
+  Initialze the unit test framework, suite, and unit tests for the
+  ResetSystemLib and run the ResetSystemLib unit test.
+
+  @retval  EFI_SUCCESS           All test cases were dispatched.
+  @retval  EFI_OUT_OF_RESOURCES  There are not enough resources available to
+                                 initialize the unit tests.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UnitTestingEntry (
+  VOID
+  )
+{
+  EFI_STATUS                  Status;
+  UNIT_TEST_FRAMEWORK_HANDLE  Framework;
+  UNIT_TEST_SUITE_HANDLE      ResetTests;
+
+  Framework = NULL;
+
+  DEBUG(( DEBUG_INFO, "%a v%a\n", UNIT_TEST_APP_NAME, UNIT_TEST_APP_VERSION ));
+
+  //
+  // Start setting up the test framework for running the tests.
+  //
+  Status = InitUnitTestFramework (&Framework, UNIT_TEST_APP_NAME, gEfiCallerBaseName, UNIT_TEST_APP_VERSION);
+  if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "Failed in InitUnitTestFramework. Status = %r\n", Status));
+      goto EXIT;
+  }
+
+  //
+  // Populate the ResetSytemLib Unit Test Suite.
+  //
+  Status = CreateUnitTestSuite (&ResetTests, Framework, "DxeResetSystemLib Reset Tests", "ResetSystemLib.Reset", NULL, NULL);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for ResetTests\n"));
+    Status = EFI_OUT_OF_RESOURCES;
+    goto EXIT;
+  }
+
+  //
+  // --------------Suite-----------Description--------------Name----------Function--------Pre---Post-------------------Context-----------
+  //
+  AddTestCase (ResetTests, "ResetCold should issue a cold reset", "Cold", ResetColdShouldIssueAColdReset, NULL, NULL, NULL);
+  AddTestCase (ResetTests, "ResetWarm should issue a warm reset", "Warm", ResetWarmShouldIssueAWarmReset, NULL, NULL, NULL);
+  AddTestCase (ResetTests, "ResetShutdown should issue a shutdown", "Shutdown", ResetShutdownShouldIssueAShutdown, NULL, NULL, NULL);
+  AddTestCase (ResetTests, "ResetPlatformSpecific should issue a platform-specific reset", "Platform", ResetPlatformSpecificShouldIssueAPlatformSpecificReset, NULL, NULL, NULL);
+  AddTestCase (ResetTests, "ResetSystem should pass all parameters through", "Parameters", ResetSystemShouldPassTheParametersThrough, NULL, NULL, NULL);
+
+  //
+  // Execute the tests.
+  //
+  Status = RunAllTestSuites (Framework);
+
+EXIT:
+  if (Framework) {
+    FreeUnitTestFramework (Framework);
+  }
+
+  return Status;
+}
+
+/**
+  Standard POSIX C entry point for host based unit test execution.
+**/
+int
+main (
+  int argc,
+  char *argv[]
+  )
+{
+  return UnitTestingEntry ();
+}
diff --git a/MdeModulePkg/Library/DxeResetSystemLib/UnitTest/DxeResetSystemLibUnitTestHost.inf b/MdeModulePkg/Library/DxeResetSystemLib/UnitTest/DxeResetSystemLibUnitTestHost.inf
new file mode 100644
index 0000000000..54f968e810
--- /dev/null
+++ b/MdeModulePkg/Library/DxeResetSystemLib/UnitTest/DxeResetSystemLibUnitTestHost.inf
@@ -0,0 +1,34 @@
+## @file
+# Unit tests of the DxeResetSystemLib instance of the ResetSystemLib class
+#
+# Copyright (C) Microsoft Corporation.
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010006
+  BASE_NAME                      = DxeResetSystemLibUnitTestHost
+  FILE_GUID                      = 83E35653-B943-4C5F-BA08-9B2996AE9273
+  MODULE_TYPE                    = HOST_APPLICATION
+  VERSION_STRING                 = 1.0
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+
+[Sources]
+  DxeResetSystemLibUnitTest.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec
+
+[LibraryClasses]
+  ResetSystemLib
+  BaseLib
+  BaseMemoryLib
+  DebugLib
+  UnitTestLib
diff --git a/MdeModulePkg/Library/DxeResetSystemLib/UnitTest/MockUefiRuntimeServicesTableLib.c b/MdeModulePkg/Library/DxeResetSystemLib/UnitTest/MockUefiRuntimeServicesTableLib.c
new file mode 100644
index 0000000000..3540e1c039
--- /dev/null
+++ b/MdeModulePkg/Library/DxeResetSystemLib/UnitTest/MockUefiRuntimeServicesTableLib.c
@@ -0,0 +1,13 @@
+/** @file
+  Mock implementation of the UEFI Runtime Services Table Library.
+
+  Copyright (C) Microsoft Corporation.
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi.h>
+
+extern EFI_RUNTIME_SERVICES  MockRuntime;
+
+EFI_RUNTIME_SERVICES  *gRT = &MockRuntime;
diff --git a/MdeModulePkg/Library/DxeResetSystemLib/UnitTest/MockUefiRuntimeServicesTableLib.inf b/MdeModulePkg/Library/DxeResetSystemLib/UnitTest/MockUefiRuntimeServicesTableLib.inf
new file mode 100644
index 0000000000..e716b855a3
--- /dev/null
+++ b/MdeModulePkg/Library/DxeResetSystemLib/UnitTest/MockUefiRuntimeServicesTableLib.inf
@@ -0,0 +1,25 @@
+## @file
+#  Mock implementation of the UEFI Runtime Services Table Library.
+#
+#  Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = MockUefiRuntimeServicesTableLib
+  FILE_GUID                      = 4EA215EE-85C1-4A0A-847F-D2A8DE20805F
+  MODULE_TYPE                    = UEFI_DRIVER
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = UefiRuntimeServicesTableLib|HOST_APPLICATION
+
+#
+#  VALID_ARCHITECTURES           = IA32 X64 EBC
+#
+
+[Sources]
+  MockUefiRuntimeServicesTableLib.c
+
+[Packages]
+  MdePkg/MdePkg.dec
diff --git a/MdeModulePkg/MdeModulePkg.ci.yaml b/MdeModulePkg/MdeModulePkg.ci.yaml
index 0bf149f205..3b6e747075 100644
--- a/MdeModulePkg/MdeModulePkg.ci.yaml
+++ b/MdeModulePkg/MdeModulePkg.ci.yaml
@@ -9,6 +9,10 @@
     "CompilerPlugin": {
         "DscPath": "MdeModulePkg.dsc"
     },
+    ## options defined ci/Plugin/HostUnitTestCompilerPlugin
+    "HostUnitTestCompilerPlugin": {
+        "DscPath": "Test/MdeModulePkgHostTest.dsc"
+    },
 
     ## options defined ci/Plugin/CharEncodingCheck
     "CharEncodingCheck": {
@@ -24,7 +28,9 @@
             "ArmPkg/ArmPkg.dec"  # this should be fixed by promoting an abstraction
         ],
         # For host based unit tests
-        "AcceptableDependencies-HOST_APPLICATION":[],
+        "AcceptableDependencies-HOST_APPLICATION":[
+            "UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec"
+        ],
         # For UEFI shell based apps
         "AcceptableDependencies-UEFI_APPLICATION":[],
         "IgnoreInf": []
@@ -35,6 +41,11 @@
         "IgnoreInf": [],
         "DscPath": "MdeModulePkg.dsc"
     },
+    ## options defined ci/Plugin/HostUnitTestDscCompleteCheck
+    "HostUnitTestDscCompleteCheck": {
+        "IgnoreInf": [""],
+        "DscPath": "Test/MdeModulePkgHostTest.dsc"
+    },
 
     ## options defined ci/Plugin/GuidCheck
     "GuidCheck": {
diff --git a/MdeModulePkg/Test/MdeModulePkgHostTest.dsc b/MdeModulePkg/Test/MdeModulePkgHostTest.dsc
new file mode 100644
index 0000000000..72a119db45
--- /dev/null
+++ b/MdeModulePkg/Test/MdeModulePkgHostTest.dsc
@@ -0,0 +1,32 @@
+## @file
+# MdeModulePkg DSC file used to build host-based unit tests.
+#
+# Copyright (c) 2019 - 2020, Intel Corporation. All rights reserved.<BR>
+# Copyright (C) Microsoft Corporation.
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  PLATFORM_NAME           = MdeModulePkgHostTest
+  PLATFORM_GUID           = F74AF7C6-698C-4EBA-BA49-FF6816916354
+  PLATFORM_VERSION        = 0.1
+  DSC_SPECIFICATION       = 0x00010005
+  OUTPUT_DIRECTORY        = Build/MdeModulePkg/HostTest
+  SUPPORTED_ARCHITECTURES = IA32|X64
+  BUILD_TARGETS           = NOOPT
+  SKUID_IDENTIFIER        = DEFAULT
+
+!include UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc
+
+[Components]
+  MdeModulePkg/Library/DxeResetSystemLib/UnitTest/MockUefiRuntimeServicesTableLib.inf
+
+  #
+  # Build MdeModulePkg HOST_APPLICATION Tests
+  #
+  MdeModulePkg/Library/DxeResetSystemLib/UnitTest/DxeResetSystemLibUnitTestHost.inf {
+    <LibraryClasses>
+      ResetSystemLib|MdeModulePkg/Library/DxeResetSystemLib/DxeResetSystemLib.inf
+      UefiRuntimeServicesTableLib|MdeModulePkg/Library/DxeResetSystemLib/UnitTest/MockUefiRuntimeServicesTableLib.inf
+  }
-- 
2.21.0.windows.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [Patch v2 10/11] .azurepipelines: Enable CI for UnitTestFrameworkPkg and host tests
  2020-02-07 18:13 [Patch v2 00/11] Add Unit Test Framework Michael D Kinney
                   ` (8 preceding siblings ...)
  2020-02-07 18:13 ` [Patch v2 09/11] MdeModulePkg: Add DxeResetSystemLib unit test Michael D Kinney
@ 2020-02-07 18:13 ` Michael D Kinney
  2020-02-07 18:13 ` [Patch v2 11/11] Maintainers.txt: Add UnitTestFrameworkPkg Michael D Kinney
  10 siblings, 0 replies; 13+ messages in thread
From: Michael D Kinney @ 2020-02-07 18:13 UTC (permalink / raw)
  To: devel; +Cc: Sean Brogan, Bret Barkelew, Liming Gao

https://bugzilla.tianocore.org/show_bug.cgi?id=2505

* Add NOOPT target to MdePkg, MdeModulePkg, and
  UnitTestFrameworkPkg to support building host
  based unit tests with optimization disabled.

Cc: Sean Brogan <sean.brogan@microsoft.com>
Cc: Bret Barkelew <Bret.Barkelew@microsoft.com>
Cc: Liming Gao <liming.gao@intel.com>
Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com>
Reviewed-by: Bret Barkelew <Bret.Barkelew@microsoft.com>
---
 .azurepipelines/templates/pr-gate-build-job.yml | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/.azurepipelines/templates/pr-gate-build-job.yml b/.azurepipelines/templates/pr-gate-build-job.yml
index 981acd68a1..61868554d4 100644
--- a/.azurepipelines/templates/pr-gate-build-job.yml
+++ b/.azurepipelines/templates/pr-gate-build-job.yml
@@ -22,10 +22,10 @@ jobs:
     matrix:
       TARGET_MDE_CPU:
         Build.Pkgs: 'MdePkg,UefiCpuPkg'
-        Build.Targets: 'DEBUG,RELEASE,NO-TARGET'
+        Build.Targets: 'DEBUG,RELEASE,NO-TARGET,NOOPT'
       TARGET_MDEMODULE_DEBUG:
         Build.Pkgs: 'MdeModulePkg'
-        Build.Targets: 'DEBUG'
+        Build.Targets: 'DEBUG,NOOPT'
       TARGET_MDEMODULE_RELEASE:
         Build.Pkgs: 'MdeModulePkg'
         Build.Targets: 'RELEASE,NO-TARGET'
@@ -35,9 +35,9 @@ jobs:
       TARGET_OTHER:
         Build.Pkgs: 'PcAtChipsetPkg,ShellPkg'
         Build.Targets: 'DEBUG,RELEASE,NO-TARGET'
-      TARGET_FMP:
-        Build.Pkgs: 'FmpDevicePkg,FatPkg'
-        Build.Targets: 'DEBUG,RELEASE,NO-TARGET'
+      TARGET_FMP_FAT_TEST:
+        Build.Pkgs: 'FmpDevicePkg,FatPkg,UnitTestFrameworkPkg'
+        Build.Targets: 'DEBUG,RELEASE,NO-TARGET,NOOPT'
       TARGET_CRYPTO:
         Build.Pkgs: 'CryptoPkg'
         Build.Targets: 'DEBUG,RELEASE,NO-TARGET'
-- 
2.21.0.windows.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [Patch v2 11/11] Maintainers.txt: Add UnitTestFrameworkPkg
  2020-02-07 18:13 [Patch v2 00/11] Add Unit Test Framework Michael D Kinney
                   ` (9 preceding siblings ...)
  2020-02-07 18:13 ` [Patch v2 10/11] .azurepipelines: Enable CI for UnitTestFrameworkPkg and host tests Michael D Kinney
@ 2020-02-07 18:13 ` Michael D Kinney
  10 siblings, 0 replies; 13+ messages in thread
From: Michael D Kinney @ 2020-02-07 18:13 UTC (permalink / raw)
  To: devel; +Cc: Sean Brogan, Bret Barkelew, Andrew Fish, Laszlo Ersek,
	Leif Lindholm

https://bugzilla.tianocore.org/show_bug.cgi?id=2505

Add maintainers and reviewers for UnitTestFrameworkPkg

Cc: Sean Brogan <sean.brogan@microsoft.com>
Cc: Bret Barkelew <Bret.Barkelew@microsoft.com>
Cc: Andrew Fish <afish@apple.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Leif Lindholm <leif@nuviainc.com>
Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
---
 Maintainers.txt | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/Maintainers.txt b/Maintainers.txt
index ca9da28925..91059db937 100644
--- a/Maintainers.txt
+++ b/Maintainers.txt
@@ -479,6 +479,13 @@ M: Guo Dong <guo.dong@intel.com>
 M: Benjamin You <benjamin.you@intel.com>
 S: Maintained
 
+UnitTestFrameworkPkg
+F: UnitTestFrameworkPkg/
+M: Michael D Kinney <michael.d.kinney@intel.com>
+R: Sean Brogan <sean.brogan@microsoft.com>
+R: Bret Barkelew <Bret.Barkelew@microsoft.com>
+S: Maintained
+
 StandaloneMmPkg
 F: StandaloneMmPkg/
 M: Ard Biesheuvel <ard.biesheuvel@linaro.org>
-- 
2.21.0.windows.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [edk2-devel] [Patch v2 07/11] UnitTestFrameworkPkg: Add DSC, DSC INC, and YAML files
  2020-02-07 18:13 ` [Patch v2 07/11] UnitTestFrameworkPkg: Add DSC, DSC INC, and YAML files Michael D Kinney
@ 2020-02-07 18:54   ` Sean
  0 siblings, 0 replies; 13+ messages in thread
From: Sean @ 2020-02-07 18:54 UTC (permalink / raw)
  To: Michael D Kinney, devel

[-- Attachment #1: Type: text/plain, Size: 71 bytes --]

Looks great.

Reviewed-by: Sean Brogan <sean.brogan@microsoft.com.>

[-- Attachment #2: Type: text/html, Size: 129 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2020-02-07 18:54 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-02-07 18:13 [Patch v2 00/11] Add Unit Test Framework Michael D Kinney
2020-02-07 18:13 ` [Patch v2 01/11] .pytool: Add CI support for host based unit tests with results Michael D Kinney
2020-02-07 18:13 ` [Patch v2 02/11] BaseTools/Plugin: Add HostBasedUnitTestRunner plugin Michael D Kinney
2020-02-07 18:13 ` [Patch v2 03/11] MdePkg/Include/Library: Add UnitTestLib class Michael D Kinney
2020-02-07 18:13 ` [Patch v2 04/11] UnitTestFrameworkPkg: Add public and private interfaces Michael D Kinney
2020-02-07 18:13 ` [Patch v2 05/11] UnitTestFrameworkPkg/Library: Add library instances Michael D Kinney
2020-02-07 18:13 ` [Patch v2 06/11] UnitTestFrameworkPkg/Test: Add unit test samples Michael D Kinney
2020-02-07 18:13 ` [Patch v2 07/11] UnitTestFrameworkPkg: Add DSC, DSC INC, and YAML files Michael D Kinney
2020-02-07 18:54   ` [edk2-devel] " Sean
2020-02-07 18:13 ` [Patch v2 08/11] MdePkg/Test: Add SafeIntLib and BaseLib Base64 unit tests Michael D Kinney
2020-02-07 18:13 ` [Patch v2 09/11] MdeModulePkg: Add DxeResetSystemLib unit test Michael D Kinney
2020-02-07 18:13 ` [Patch v2 10/11] .azurepipelines: Enable CI for UnitTestFrameworkPkg and host tests Michael D Kinney
2020-02-07 18:13 ` [Patch v2 11/11] Maintainers.txt: Add UnitTestFrameworkPkg Michael D Kinney

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox