From nobody Tue Feb 3 16:35:22 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of groups.io designates 66.175.222.12 as permitted sender) client-ip=66.175.222.12; envelope-from=bounce+27952+53463+1787277+3901457@groups.io; helo=web01.groups.io; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of groups.io designates 66.175.222.12 as permitted sender) smtp.mailfrom=bounce+27952+53463+1787277+3901457@groups.io; dmarc=fail(p=none dis=none) header.from=intel.com ARC-Seal: i=1; a=rsa-sha256; t=1579831839; cv=none; d=zohomail.com; s=zohoarc; b=anlUIkxJfzt1tLuvFw+IyIRAJhNXEGOsoT3NtHsOeoMCksxXu6jBBHTzlY0M81KEJMLXG9A89QKks7Z8I1jVxN4Pb/5/HW2Tri1BXBr08iBXTbRC9xLOhPkj+FS2ABGD1CsHOBhPn1zIEBiEsg+N19bIib2l5kg5xFZ87qVVaGk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1579831839; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Id:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:References:Sender:Subject:To; bh=ViPL5FHuDNTaMvVX0MyYQEbl+SOcBX3v+NQT3/Ojnfw=; b=MwKTcQgdaZUP06haZffuG3J9lHItH5R4TlYqL3cT4rhvtt5eXWsyaOuNi2BmJQJkRdcZNzVuCaRPGhHnp8maLmTnXnZ5DVpvRPygRBR3jtL+1CSYwOVAEz7sv8ddUMifJ/Tz6rd3mY/ilDOYwPrJl4AhQlpnzlj4ehWRzXdpZmk= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of groups.io designates 66.175.222.12 as permitted sender) smtp.mailfrom=bounce+27952+53463+1787277+3901457@groups.io; dmarc=fail header.from= (p=none dis=none) header.from= Received: from web01.groups.io (web01.groups.io [66.175.222.12]) by mx.zohomail.com with SMTPS id 1579831839655940.9877175243473; Thu, 23 Jan 2020 18:10:39 -0800 (PST) Return-Path: X-Received: by 127.0.0.2 with SMTP id ui14YY1788612xcVEKmZMLJg; Thu, 23 Jan 2020 18:10:38 -0800 X-Received: from mga09.intel.com (mga09.intel.com []) by mx.groups.io with SMTP id smtpd.web11.7301.1579831835785711151 for ; Thu, 23 Jan 2020 18:10:37 -0800 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False X-Received: from orsmga004.jf.intel.com ([10.7.209.38]) by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 23 Jan 2020 18:10:36 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.70,356,1574150400"; d="scan'208";a="375395295" X-Received: from unknown (HELO mdkinney-MOBL2.amr.corp.intel.com) ([10.241.98.74]) by orsmga004.jf.intel.com with ESMTP; 23 Jan 2020 18:10:35 -0800 From: "Michael D Kinney" To: devel@edk2.groups.io Cc: Sean Brogan , Bret Barkelew Subject: [edk2-devel] [Patch 05/11] UnitTestFrameworkPkg/Library: Add library instances Date: Thu, 23 Jan 2020 18:10:26 -0800 Message-Id: <20200124021032.13808-6-michael.d.kinney@intel.com> In-Reply-To: <20200124021032.13808-1-michael.d.kinney@intel.com> References: <20200124021032.13808-1-michael.d.kinney@intel.com> MIME-Version: 1.0 Precedence: Bulk List-Unsubscribe: Sender: devel@edk2.groups.io List-Id: Mailing-List: list devel@edk2.groups.io; contact devel+owner@edk2.groups.io Reply-To: devel@edk2.groups.io,michael.d.kinney@intel.com X-Gm-Message-State: nZQF7HIm3m6saecIi603Bi2rx1787277AA= Content-Transfer-Encoding: quoted-printable DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=groups.io; q=dns/txt; s=20140610; t=1579831838; bh=OsL7OOSJkZ6HifH1ZK0XTECyKlbYswCilEWYE2Ew2zs=; h=Cc:Date:From:Reply-To:Subject:To; b=vLVVmLJweNnHFomjfFD6SFzPHsgvrIDE4W8zjQbdHq2kaovRjtxf7wgn+OC+ezJU+zP yRogvYUhWJq/iFoSNZ2LBs9rs6qjD+tl11M9lq76b/QKjwd85PcNOhEr3F1L6Am3ODu1Q xWfHQfSgI7a8AuCD79/oYznPQ+sHcFiegXc= X-ZohoMail-DKIM: pass (identity @groups.io) Content-Type: text/plain; charset="utf-8" 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 Cc: Bret Barkelew Signed-off-by: Michael D Kinney Reviewed-by: Bret Barkelew --- .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/DebugL= ibPosix.c create mode 100644 UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugL= ibPosix.inf create mode 100644 UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugL= ibPosix.uni create mode 100644 UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibP= osix/MemoryAllocationLibPosix.c create mode 100644 UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibP= osix/MemoryAllocationLibPosix.inf create mode 100644 UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibP= osix/MemoryAllocationLibPosix.uni create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTe= stBootLibNull.c create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTe= stBootLibNull.inf create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTe= stBootLibNull.uni create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/Un= itTestBootLibUsbClass.c create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/Un= itTestBootLibUsbClass.inf create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/Un= itTestBootLibUsbClass.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/UnitTestLibCmo= cka.inf create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLibCmo= cka.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/UnitTestPersistenceLibSimp= leFileSystem/UnitTestPersistenceLibSimpleFileSystem.c create mode 100644 UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimp= leFileSystem/UnitTestPersistenceLibSimpleFileSystem.inf create mode 100644 UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimp= leFileSystem/UnitTestPersistenceLibSimpleFileSystem.uni create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/Un= itTestResultReportLib.c create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/Un= itTestResultReportLibConOut.c create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/Un= itTestResultReportLibConOut.inf create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/Un= itTestResultReportLibConOut.uni create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/Un= itTestResultReportLibDebugLib.c create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/Un= itTestResultReportLibDebugLib.inf create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/Un= itTestResultReportLibDebugLib.uni diff --git a/.gitmodules b/.gitmodules index 508f0c1828..b30f5bf136 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "SoftFloat"] path =3D ArmPkg/Library/ArmSoftFloatLib/berkeley-softfloat-3 url =3D https://github.com/ucb-bar/berkeley-softfloat-3.git +[submodule "UnitTestFrameworkPkg/Library/CmockaLib/cmocka"] + path =3D UnitTestFrameworkPkg/Library/CmockaLib/cmocka + url =3D https://git.cryptomilk.org/projects/cmocka.git diff --git a/UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.inf b/UnitTes= tFrameworkPkg/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.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION =3D 0x00010005 + BASE_NAME =3D CmockaLib + MODULE_UNI_FILE =3D CmockaLib.uni + FILE_GUID =3D F1662152-3399-49AC-BE44-CAA97575FACE + MODULE_TYPE =3D BASE + VERSION_STRING =3D 0.1 + LIBRARY_CLASS =3D CmockaLib|HOST_APPLICATION + +# +# VALID_ARCHITECTURES =3D IA32 X64 ARM AARCH64 +# + +[Sources] + cmocka/src/cmocka.c + +[Packages] + UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec + +[BuildOptions] + MSFT:*_*_*_CC_FLAGS =3D=3D /c -DHAVE_VSNPRINTF -DHAVE_SNPRINTF + MSFT:NOOPT_*_*_CC_FLAGS =3D /Od + + GCC:*_*_*_CC_FLAGS =3D=3D -g -DHAVE_SIGNAL_H + GCC:NOOPT_*_*_CC_FLAGS =3D -O0 + GCC:*_*_IA32_CC_FLAGS =3D -m32 + GCC:*_*_X64_CC_FLAGS =3D -m64 diff --git a/UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.uni b/UnitTes= tFrameworkPkg/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.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_MODULE_ABSTRACT #language en-US "Cmocka Library im= plementation" + +#string STR_MODULE_DESCRIPTION #language en-US "This module provi= des Cmocka Library implementation." diff --git a/UnitTestFrameworkPkg/Library/CmockaLib/cmocka b/UnitTestFramew= orkPkg/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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#include +#include +#include +#include +#include + +/// +/// Define the maximum debug and assert message length that this library s= upports +/// +#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 a= nd 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 access= ed + 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 a= nd + 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 a= nd + 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 descrip= tion. + This may be followed by a breakpoint or a dead loop. + + Print a message of the form "ASSERT (): \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 se= t then + CpuDeadLoop() is called. If neither of these bits are set, then this fu= nction + returns immediately after the message is printed to the debug output dev= ice. + DebugAssert() must actively prevent recursion. If DebugAssert() is call= ed while + processing another DebugAssert(), then DebugAssert() must return immedia= tely. + + If FileName is NULL, then a string of "(NULL) Filename" is pr= inted. + If Description is NULL, then a string of "(NULL) Descripti= on" is printed. + + @param FileName The pointer to the name of the source file that gen= erated the assert condition. + @param LineNumber The line number in the source file that generated t= he assert condition + @param Description The pointer to the description of the assert condit= ion. + +**/ +VOID +EFIAPI +DebugAssert ( + IN CONST CHAR8 *FileName, + IN UINTN LineNumber, + IN CONST CHAR8 *Description + ) +{ + printf ("ASSERT: %s(%d): %s\n", FileName, (INT32)(UINT32)LineNumber, Des= cription); + + // + // Generate a Breakpoint, DeadLoop, or NOP based on PCD settings + // + if ((PcdGet8(PcdDebugPropertyMask) & DEBUG_PROPERTY_ASSERT_BREAKPOINT_EN= ABLED) !=3D 0) { + CpuBreakpoint (); + } else if ((PcdGet8(PcdDebugPropertyMask) & DEBUG_PROPERTY_ASSERT_DEADLO= OP_ENABLED) !=3D 0) { + CpuDeadLoop (); + } +} + +/** + Fills a target buffer with PcdDebugClearMemoryValue, and returns the tar= get 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 PcdD= ebugClearMemoryValue. + @param Length The number of bytes in Buffer to fill with zeros PcdDeb= ugClearMemoryValue. + + @return Buffer The pointer to the target buffer filled with PcdDebugCl= earMemoryValue. + +**/ +VOID * +EFIAPI +DebugClearMemory ( + OUT VOID *Buffer, + IN UINTN Length + ) +{ + // + // If Buffer is NULL, then ASSERT(). + // + ASSERT (Buffer !=3D 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 bi= t of + PcdDebugPropertyMask is set. Otherwise FALSE is returned. + + @retval TRUE The DEBUG_PROPERTY_DEBUG_ASSERT_ENABLED bit of PcdDebug= PropertyMask is set. + @retval FALSE The DEBUG_PROPERTY_DEBUG_ASSERT_ENABLED bit of PcdDebug= PropertyMask is clear. + +**/ +BOOLEAN +EFIAPI +DebugAssertEnabled ( + VOID + ) +{ + return (BOOLEAN) ((PcdGet8(PcdDebugPropertyMask) & DEBUG_PROPERTY_DEBUG_= ASSERT_ENABLED) !=3D 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 PcdDebugP= ropertyMask is set. + @retval FALSE The DEBUG_PROPERTY_DEBUG_PRINT_ENABLED bit of PcdDebugP= ropertyMask is clear. + +**/ +BOOLEAN +EFIAPI +DebugPrintEnabled ( + VOID + ) +{ + return (BOOLEAN) ((PcdGet8(PcdDebugPropertyMask) & DEBUG_PROPERTY_DEBUG_= PRINT_ENABLED) !=3D 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 PcdDebugPr= opertyMask is set. + @retval FALSE The DEBUG_PROPERTY_DEBUG_CODE_ENABLED bit of PcdDebugPr= opertyMask is clear. + +**/ +BOOLEAN +EFIAPI +DebugCodeEnabled ( + VOID + ) +{ + return (BOOLEAN) ((PcdGet8(PcdDebugPropertyMask) & DEBUG_PROPERTY_DEBUG_= CODE_ENABLED) !=3D 0); +} + +/** + Returns TRUE if DEBUG_CLEAR_MEMORY() macro is enabled. + + This function returns TRUE if the DEBUG_PROPERTY_CLEAR_MEMORY_ENABLED bi= t of + PcdDebugPropertyMask is set. Otherwise FALSE is returned. + + @retval TRUE The DEBUG_PROPERTY_CLEAR_MEMORY_ENABLED bit of PcdDebug= PropertyMask is set. + @retval FALSE The DEBUG_PROPERTY_CLEAR_MEMORY_ENABLED bit of PcdDebug= PropertyMask is clear. + +**/ +BOOLEAN +EFIAPI +DebugClearMemoryEnabled ( + VOID + ) +{ + return (BOOLEAN) ((PcdGet8(PcdDebugPropertyMask) & DEBUG_PROPERTY_CLEAR_= MEMORY_ENABLED) !=3D 0); +} + +/** + Returns TRUE if any one of the bit is set both in ErrorLevel and PcdFixe= dDebugPrintErrorLevel. + + This function compares the bit mask of ErrorLevel and PcdFixedDebugPrint= ErrorLevel. + + @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)) = !=3D 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.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION =3D 0x00010005 + BASE_NAME =3D DebugLibPosix + MODULE_UNI_FILE =3D DebugLibPosix.uni + FILE_GUID =3D 6A77CE89-C1B6-4A6B-9561-07D7127514A7 + MODULE_TYPE =3D BASE + VERSION_STRING =3D 1.0 + LIBRARY_CLASS =3D DebugLib|HOST_APPLICATION + +[Sources] + DebugLibPosix.c + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + BaseMemoryLib + PcdLib + PrintLib + BaseLib + +[Pcd] + gEfiMdePkgTokenSpaceGuid.PcdDebugClearMemoryValue ## SOMETIMES_CONSU= MES + 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.
+// +// 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 Librar= y to produce formatted output strings sent to printf()." diff --git a/UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/Me= moryAllocationLibPosix.c b/UnitTestFrameworkPkg/Library/Posix/MemoryAllocat= ionLibPosix/MemoryAllocationLibPosix.c new file mode 100644 index 0000000000..1f590524d8 --- /dev/null +++ b/UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAll= ocationLibPosix.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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include + +#include +#include +#include + +/// +/// 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 th= en +/// 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 return= s 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 req= uest, 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 ret= urns 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 req= uest, 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 retu= rns 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 req= uest, 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 t= he page allocation + functions in the Memory Allocation Library. + + Frees the number of 4KB pages specified by Pages from the buffer specifi= ed by Buffer. Buffer + must have been allocated on a previous call to the page allocation servi= ces 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 Memor= y 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 specifi= ed alignment. + + Allocates the number of 4KB pages specified by Pages of type EfiBootServ= icesData 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 rema= ining to satisfy the + request, then NULL is returned. + + If Alignment is not a power of two and Alignment is not zero, then ASSER= T(). + 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)) =3D=3D 0); + + if (Alignment < SIZE_4KB) { + Alignment =3D SIZE_4KB; + } + AlignmentMask =3D Alignment - 1; + + // + // We need reserve Alignment pages for PAGE_HEAD, as meta data. + // + PageHead.Signature =3D PAGE_HEAD_PRIVATE_SIGNATURE; + PageHead.TotalPages =3D Pages + EFI_SIZE_TO_PAGES (Alignment) * 2; + PageHead.AlignedPages =3D Pages; + PageHead.AllocatedBufffer =3D malloc (EFI_PAGES_TO_SIZE (PageHead.TotalP= ages)); + if (PageHead.AllocatedBufffer =3D=3D NULL) { + return NULL; + } + PageHead.AlignedBuffer =3D (VOID *)(((UINTN) PageHead.AllocatedBufffer += AlignmentMask) & ~AlignmentMask); + if ((UINTN)PageHead.AlignedBuffer - (UINTN)PageHead.AllocatedBufffer < s= izeof(PAGE_HEAD)) { + PageHead.AlignedBuffer =3D (VOID *)((UINTN)PageHead.AlignedBuffer + Al= ignment); + } + + PageHeadPtr =3D (VOID *)((UINTN)PageHead.AlignedBuffer - sizeof(PAGE_HEA= D)); + memcpy (PageHeadPtr, &PageHead, sizeof(PAGE_HEAD)); + + return PageHead.AlignedBuffer; +} + +/** + Allocates one or more 4KB pages of type EfiRuntimeServicesData at a spec= ified alignment. + + Allocates the number of 4KB pages specified by Pages of type EfiRuntimeS= ervicesData 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 rema= ining to satisfy the + request, then NULL is returned. + + If Alignment is not a power of two and Alignment is not zero, then ASSER= T(). + 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 speci= fied alignment. + + Allocates the number of 4KB pages specified by Pages of type EfiReserved= MemoryType 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 rema= ining to satisfy the + request, then NULL is returned. + + If Alignment is not a power of two and Alignment is not zero, then ASSER= T(). + 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 t= he aligned page + allocation functions in the Memory Allocation Library. + + Frees the number of 4KB pages specified by Pages from the buffer specifi= ed by Buffer. Buffer + must have been allocated on a previous call to the aligned page allocati= on 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 =3D (VOID *)((UINTN)Buffer - sizeof(PAGE_HEAD)); + if (PageHeadPtr->Signature !=3D PAGE_HEAD_PRIVATE_SIGNATURE) { + return; + } + if (PageHeadPtr->AlignedPages !=3D Pages) { + return; + } + + PageHeadPtr->Signature =3D 0; + free (PageHeadPtr->AllocatedBufffer); +} + +/** + Allocates a buffer of type EfiBootServicesData. + + Allocates the number bytes specified by AllocationSize of type EfiBootSe= rvicesData and returns a + pointer to the allocated buffer. If AllocationSize is 0, then a valid b= uffer of 0 size is + returned. If there is not enough memory remaining to satisfy the reques= t, 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 EfiRuntim= eServicesData 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 reques= t, 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 EfiReserv= edMemoryType 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 reques= t, 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 EfiBootSe= rvicesData, clears the + buffer with zeros, and returns a pointer to the allocated buffer. If Al= locationSize is 0, then a + valid buffer of 0 size is returned. If there is not enough memory remai= ning 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 =3D malloc (AllocationSize); + if (Buffer =3D=3D 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 EfiRuntim= eServicesData, clears the + buffer with zeros, and returns a pointer to the allocated buffer. If Al= locationSize is 0, then a + valid buffer of 0 size is returned. If there is not enough memory remai= ning 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 EfiReserv= edMemoryType, clears the + buffer with zeros, and returns a pointer to the allocated buffer. If Al= locationSize is 0, then a + valid buffer of 0 size is returned. If there is not enough memory remai= ning 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 EfiBootSe= rvicesData, copies + AllocationSize bytes from Buffer to the newly allocated buffer, and retu= rns 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 retu= rned. + + If Buffer is NULL, then ASSERT(). + If AllocationSize is greater than (MAX_ADDRESS - Buffer + 1), then ASSER= T(). + + @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 =3D malloc (AllocationSize); + if (Memory =3D=3D 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 EfiRuntim= eServicesData, copies + AllocationSize bytes from Buffer to the newly allocated buffer, and retu= rns 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 retu= rned. + + If Buffer is NULL, then ASSERT(). + If AllocationSize is greater than (MAX_ADDRESS - Buffer + 1), then ASSER= T(). + + @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 EfiReserv= edMemoryType, copies + AllocationSize bytes from Buffer to the newly allocated buffer, and retu= rns 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 retu= rned. + + If Buffer is NULL, then ASSERT(). + If AllocationSize is greater than (MAX_ADDRESS - Buffer + 1), then ASSER= T(). + + @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 OldS= ize and + NewSize bytes are copied from OldBuffer to the newly allocated buffer, a= nd + 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 i= s 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 New= Size 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 =3D malloc (NewSize); + if (NewBuffer !=3D NULL && OldBuffer !=3D NULL) { + memcpy (NewBuffer, OldBuffer, MIN (OldSize, NewSize)); + } + if (OldBuffer !=3D 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 O= ldSize and + NewSize bytes are copied from OldBuffer to the newly allocated buffer, a= nd + 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 i= s 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 New= Size 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 Ol= dSize and + NewSize bytes are copied from OldBuffer to the newly allocated buffer, a= nd + 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 i= s 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 New= Size 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 alloca= tion functions in the + Memory Allocation Library. + + Frees the buffer specified by Buffer. Buffer must have been allocated o= n 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 Memor= y 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/Me= moryAllocationLibPosix.inf b/UnitTestFrameworkPkg/Library/Posix/MemoryAlloc= ationLibPosix/MemoryAllocationLibPosix.inf new file mode 100644 index 0000000000..44ec3fd517 --- /dev/null +++ b/UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAll= ocationLibPosix.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.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION =3D 0x00010005 + BASE_NAME =3D MemoryAllocationLibPosix + MODULE_UNI_FILE =3D MemoryAllocationLibPosix.uni + FILE_GUID =3D A1672454-A3D3-4AAC-A86B-8D63132BBB91 + MODULE_TYPE =3D UEFI_DRIVER + VERSION_STRING =3D 1.0 + LIBRARY_CLASS =3D MemoryAllocationLib|HOST_APPLICATION + +[Sources] + MemoryAllocationLibPosix.c + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + BaseLib diff --git a/UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/Me= moryAllocationLibPosix.uni b/UnitTestFrameworkPkg/Library/Posix/MemoryAlloc= ationLibPosix/MemoryAllocationLibPosix.uni new file mode 100644 index 0000000000..854b427976 --- /dev/null +++ b/UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAll= ocationLibPosix.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.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_MODULE_ABSTRACT #language en-US "Instance of Memor= y Allocation Library based on POSIX APIs" + +#string STR_MODULE_DESCRIPTION #language en-US "Uses POSIX APIs m= alloc() and free() to allocate and free memory." diff --git a/UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootL= ibNull.c b/UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootLib= Null.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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +/** + Set the boot manager to boot from a specific device on the next boot. Th= is + should be set only for the next boot and shouldn't require any manual cl= ean 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/UnitTestBootL= ibNull.inf b/UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootL= ibNull.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.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION =3D 0x00010017 + BASE_NAME =3D UnitTestBootLibNull + MODULE_UNI_FILE =3D UnitTestBootLibNull.uni + FILE_GUID =3D f143e75d-76e1-4040-b134-8f4f0bd5e3bd + VERSION_STRING =3D 1.0 + MODULE_TYPE =3D DXE_DRIVER + LIBRARY_CLASS =3D UnitTestBootLib + +[Sources] + UnitTestBootLibNull.c + +[Packages] + MdePkg/MdePkg.dec + diff --git a/UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootL= ibNull.uni b/UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootL= ibNull.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.
+// 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/UnitTestB= ootLibUsbClass.c b/UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/Uni= tTestBootLibUsbClass.c new file mode 100644 index 0000000000..4ce48bd233 --- /dev/null +++ b/UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibU= sbClass.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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include +#include +#include +#include + +/** + Set the boot manager to boot from a specific device on the next boot. Th= is + should be set only for the next boot and shouldn't require any manual cl= ean 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 =3D NULL; + OptionalDataSize =3D 0; + BootNextValue =3D 0xABCD; // this should be a safe number... + DpEnd =3D NULL; + Dp =3D NULL; + NewOptionValid =3D FALSE; + + UsbDp.Header.Length[0] =3D (UINT8)(sizeof(USB_CLASS_DEVICE_PATH) & 0xff); + UsbDp.Header.Length[1] =3D (UINT8)(sizeof(USB_CLASS_DEVICE_PATH) >> 8); + UsbDp.Header.Type =3D MESSAGING_DEVICE_PATH; + UsbDp.Header.SubType =3D MSG_USB_CLASS_DP; + UsbDp.VendorId =3D 0xFFFF; + UsbDp.ProductId =3D 0xFFFF; + UsbDp.DeviceClass =3D 0xFF; + UsbDp.DeviceSubClass =3D 0xFF; + UsbDp.DeviceProtocol =3D 0xFF; + + Attributes =3D LOAD_OPTION_ACTIVE; + + DpEnd =3D AppendDevicePathNode (NULL, NULL); + if (DpEnd =3D=3D NULL) { + DEBUG ((DEBUG_ERROR, "%a: Unable to create device path. DpEnd is NULL= .\n", __FUNCTION__)); + Status =3D EFI_OUT_OF_RESOURCES; + goto CLEANUP; + } + + //@MRT --- Is this memory leak because we lose the old Dp memory + Dp =3D AppendDevicePathNode ( + DpEnd, + (EFI_DEVICE_PATH_PROTOCOL *)&UsbDp + ); + if (Dp =3D=3D NULL) { + DEBUG((DEBUG_ERROR, "%a: Unable to create device path. Dp is NULL.\n"= , __FUNCTION__)); + Status =3D EFI_OUT_OF_RESOURCES; + goto CLEANUP; + } + + Status =3D 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 =3D %r\n= ", __FUNCTION__, Status)); + goto CLEANUP; + } + + NewOptionValid =3D TRUE; + DEBUG ((DEBUG_VERBOSE, "%a: Generic USB Class Device boot option created= .\n", __FUNCTION__)); + Status =3D EfiBootManagerLoadOptionToVariable (&NewOption); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: Error Saving boot option NV variable. Status= =3D %r\n", __FUNCTION__, Status)); + goto CLEANUP; + } + + // + // Set Boot Next + // + Status =3D 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__, S= tatus)); + +CLEANUP: + if (Dp !=3D NULL) { + FreePool (Dp); + } + if (DpEnd !=3D NULL) { + FreePool (DpEnd); + } + if (NewOptionValid) { + EfiBootManagerFreeLoadOption (&NewOption); + } + return Status; +} diff --git a/UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestB= ootLibUsbClass.inf b/UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/U= nitTestBootLibUsbClass.inf new file mode 100644 index 0000000000..80c4e4f111 --- /dev/null +++ b/UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibU= sbClass.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.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION =3D 0x00010017 + BASE_NAME =3D UnitTestBootLibUsbClass + MODULE_UNI_FILE =3D UnitTestBootLibUsbClass.uni + FILE_GUID =3D DFADE2A2-DB69-47DE-A37A-40FB6D52E844 + VERSION_STRING =3D 1.0 + MODULE_TYPE =3D UEFI_APPLICATION + LIBRARY_CLASS =3D 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 se= t BootNext. diff --git a/UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestB= ootLibUsbClass.uni b/UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/U= nitTestBootLibUsbClass.uni new file mode 100644 index 0000000000..8468b3537c --- /dev/null +++ b/UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibU= sbClass.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.
+// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_MODULE_ABSTRACT #language en-US "Library to suppor= t booting to USB on the next boot" + +#string STR_MODULE_DESCRIPTION #language en-US "This instance use= s the industry standard usb class boot option.." diff --git a/UnitTestFrameworkPkg/Library/UnitTestLib/Assert.c b/UnitTestFr= ameworkPkg/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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include +#include +#include +#include + +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 =3D=3D NULL || FailureMessage =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + + UnitTest->FailureType =3D 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 =3D 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 a= re + recorded and FALSE is returned. + + @param[in] Expression The BOOLEAN result of the expression evaluatio= n. + @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 macr= o. + @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 t= he + assert provided by FunctionName, LineNumber, FileName, and Description a= re + recorded and FALSE is returned. + + @param[in] Expression The BOOLEAN result of the expression evaluatio= n. + @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 macr= o. + @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 locatio= n of + the assert provided by FunctionName, LineNumber, FileName, and Descripti= on 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 macr= o. + @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 lo= cation + of the assert provided by FunctionName, LineNumber, FileName, Descriptio= nA, + 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 macr= o. + @param[in] FileName Null-terminated ASCII string of the filename + executing the assert macro. + @param[in] DescriptionA Null-terminated ASCII string that is a descrip= tion + of ValueA. + @param[in] DescriptionB Null-terminated ASCII string that is a descrip= tion + 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 !=3D ValueB)) { + UnitTestLogFailure ( + FAILURETYPE_ASSERTEQUAL, + "%a::%d Value %a !=3D %a (%d !=3D %d)!\n", + FunctionName, + LineNumber, + DescriptionA, + DescriptionB, + ValueA, + ValueB + ); + UT_LOG_ERROR ( + "[ASSERT FAIL] %a::%d Value %a !=3D %a (%d !=3D %d)!\n", + FunctionName, + LineNumber, + DescriptionA, + DescriptionB, + ValueA, + ValueB + ); + } + return (ValueA =3D=3D ValueB); +} + +/** + If the contents of BufferA are identical to the contents of BufferB, the= n TRUE + is returned. If the contents of BufferA are not identical to the conten= ts of + BufferB, then an assert is triggered and the location of the assert prov= ided + 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 Buff= erB. + @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 macr= o. + @param[in] FileName Null-terminated ASCII string of the filename + executing the assert macro. + @param[in] DescriptionA Null-terminated ASCII string that is a descrip= tion + of BufferA. + @param[in] DescriptionB Null-terminated ASCII string that is a descrip= tion + 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 content= s 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) !=3D 0) { + UnitTestLogFailure ( + FAILURETYPE_ASSERTEQUAL, + "%a::%d Memory at %a !=3D %a for length %d bytes!\n", + FunctionName, + LineNumber, + DescriptionA, + DescriptionB, + Length + ); + UT_LOG_ERROR ( + "[ASSERT FAIL] %a::%d Value %a !=3D %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 locati= on + of the assert provided by FunctionName, LineNumber, FileName, Descriptio= nA + 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 macr= o. + @param[in] FileName Null-terminated ASCII string of the filename + executing the assert macro. + @param[in] DescriptionA Null-terminated ASCII string that is a descrip= tion + of ValueA. + @param[in] DescriptionB Null-terminated ASCII string that is a descrip= tion + 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 =3D=3D ValueB)) { + UnitTestLogFailure ( + FAILURETYPE_ASSERTNOTEQUAL, + "%a::%d Value %a =3D=3D %a (%d =3D=3D %d)!\n", + FunctionName, + LineNumber, + DescriptionA, + DescriptionB, + ValueA, + ValueB + ); + UT_LOG_ERROR ( + "[ASSERT FAIL] %a::%d Value %a =3D=3D %a (%d =3D=3D %d)!\n", + FunctionName, + LineNumber, + DescriptionA, + DescriptionB, + ValueA, + ValueB + ); + } + return (ValueA !=3D 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, a= nd + Description are recorded and FALSE is returned. + + @param[in] Status EFI_STATUS value returned from an API under te= st. + @param[in] Expected The expected EFI_STATUS return value from an A= PI + 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 macr= o. + @param[in] FileName Null-terminated ASCII string of the filename + executing the assert macro. + @param[in] Description Null-terminated ASCII string that is a descrip= tion + 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 !=3D 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 =3D=3D 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 locatio= n of + the assert provided by FunctionName, LineNumber, FileName, and PointerNa= me + 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 fu= nction + 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 macr= o. + @param[in] FileName Null-terminated ASCII string of the filename + executing the assert macro. + @param[in] PointerName Null-terminated ASCII string that is a descrip= tion + 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 =3D=3D 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 !=3D NULL); +} diff --git a/UnitTestFrameworkPkg/Library/UnitTestLib/AssertCmocka.c b/Unit= TestFrameworkPkg/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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 a= re + recorded and FALSE is returned. + + @param[in] Expression The BOOLEAN result of the expression evaluatio= n. + @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 macr= o. + @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 t= he + assert provided by FunctionName, LineNumber, FileName, and Description a= re + recorded and FALSE is returned. + + @param[in] Expression The BOOLEAN result of the expression evaluatio= n. + @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 macr= o. + @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)", Descriptio= n, 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 locatio= n of + the assert provided by FunctionName, LineNumber, FileName, and Descripti= on 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 macr= o. + @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)", De= scription, (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 lo= cation + of the assert provided by FunctionName, LineNumber, FileName, Descriptio= nA, + 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 macr= o. + @param[in] FileName Null-terminated ASCII string of the filename + executing the assert macro. + @param[in] DescriptionA Null-terminated ASCII string that is a descrip= tion + of ValueA. + @param[in] DescriptionB Null-terminated ASCII string that is a descrip= tion + 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 =3D=3D ValueB), TempStr, FileName, (INT32)LineNumb= er); + + return (ValueA =3D=3D ValueB); +} + +/** + If the contents of BufferA are identical to the contents of BufferB, the= n TRUE + is returned. If the contents of BufferA are not identical to the conten= ts of + BufferB, then an assert is triggered and the location of the assert prov= ided + 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 Buff= erB. + @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 macr= o. + @param[in] FileName Null-terminated ASCII string of the filename + executing the assert macro. + @param[in] DescriptionA Null-terminated ASCII string that is a descrip= tion + of BufferA. + @param[in] DescriptionB Null-terminated ASCII string that is a descrip= tion + 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 content= s 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 =3D (CompareMem(BufferA, BufferB, Length) =3D=3D 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 locati= on + of the assert provided by FunctionName, LineNumber, FileName, Descriptio= nA + 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 macr= o. + @param[in] FileName Null-terminated ASCII string of the filename + executing the assert macro. + @param[in] DescriptionA Null-terminated ASCII string that is a descrip= tion + of ValueA. + @param[in] DescriptionB Null-terminated ASCII string that is a descrip= tion + 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:%ll= x)", DescriptionA, ValueA, DescriptionB, ValueB); + _assert_true ((ValueA !=3D ValueB), TempStr, FileName, (INT32)LineNumber= ); + + return (ValueA !=3D 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, a= nd + Description are recorded and FALSE is returned. + + @param[in] Status EFI_STATUS value returned from an API under te= st. + @param[in] Expected The expected EFI_STATUS return value from an A= PI + 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 macr= o. + @param[in] FileName Null-terminated ASCII string of the filename + executing the assert macro. + @param[in] Description Null-terminated ASCII string that is a descrip= tion + 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)", Des= cription, (VOID *)Status); + _assert_true ((Status =3D=3D Expected), TempStr, FileName, (INT32)LineNu= mber); + + return (Status =3D=3D 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 locatio= n of + the assert provided by FunctionName, LineNumber, FileName, and PointerNa= me + 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 fu= nction + 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 macr= o. + @param[in] FileName Null-terminated ASCII string of the filename + executing the assert macro. + @param[in] PointerName Null-terminated ASCII string that is a descrip= tion + 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)", Pointer= Name, Pointer); + _assert_true ((Pointer !=3D NULL), TempStr, FileName, (INT32)LineNumber); + + return (Pointer !=3D NULL); +} diff --git a/UnitTestFrameworkPkg/Library/UnitTestLib/Log.c b/UnitTestFrame= workPkg/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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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[] =3D { + { 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 =3D NULL; + for (Index =3D 0; Index < ARRAY_SIZE (mLogPrefixStrings); Index++) { + if (mLogPrefixStrings[Index].LogLevel =3D=3D LogLevel) { + Result =3D 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 =3D=3D NULL || String =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + + // If this is the first log for the test allocate log space + if (UnitTest->Log =3D=3D NULL) { + UnitTestLogInit (UnitTest, NULL, 0); + } + + if (UnitTest->Log =3D=3D NULL) { + DEBUG ((DEBUG_ERROR, "Failed to allocate space for unit test log\n")); + ASSERT (UnitTest->Log !=3D NULL); + return EFI_OUT_OF_RESOURCES; + } + + Status =3D 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 =3D = %r\n", Status)); + return Status; + } + + return EFI_SUCCESS; +} + +/** + This function is responsible for initializing the log buffer for a singl= e test. It can + be used internally, but may also be consumed by the test framework to ad= d 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 d= ata that should + be used to initialize the log. Should incl= ude a NULL terminator. + @param[in] BufferSize [Optional] The size of the pre-existing lo= g 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 =3D=3D NULL) { + DEBUG ((DEBUG_ERROR, "%a called with invalid Test parameter\n", __FUNC= TION__)); + return; + } + + // + // If this is the first log for the test allocate log space + // + if (Test->Log =3D=3D NULL) { + Test->Log =3D AllocateZeroPool (UNIT_TEST_MAX_LOG_BUFFER); + } + + // + //check again to make sure allocate worked + // + if(Test->Log =3D=3D NULL) { + DEBUG ((DEBUG_ERROR, "Failed to allocate memory for the log\n")); + return; + } + + if((Buffer !=3D NULL) && (BufferSize > 0) && ((BufferSize <=3D 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 i= n 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_STR= ING_LENGTH]; + CHAR8 LogString[UNIT_TEST_MAX_SINGLE_LOG_STRING_LE= NGTH]; + CONST CHAR8 *LogTypePrefix; + VA_LIST Marker; + + FrameworkHandle =3D GetActiveFrameworkHandle (); + + LogTypePrefix =3D NULL; + + // + // Make sure that this unit test log level is enabled. + // + if ((ErrorLevel & (UINTN)PcdGet32 (PcdUnitTestLogLevel)) =3D=3D 0) { + return; + } + + // + // If we need to define a new format string... + // well... get to it. + // + LogTypePrefix =3D GetStringForStatusLogPrefix (ErrorLevel); + if (LogTypePrefix !=3D NULL) { + AsciiSPrint (NewFormatString, sizeof (NewFormatString), "%a%a", LogTyp= ePrefix, 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)->Curren= tTest, LogString); +} diff --git a/UnitTestFrameworkPkg/Library/UnitTestLib/RunTests.c b/UnitTest= FrameworkPkg/Library/UnitTestLib/RunTests.c new file mode 100644 index 0000000000..d66382e2d3 --- /dev/null +++ b/UnitTestFrameworkPkg/Library/UnitTestLib/RunTests.c @@ -0,0 +1,171 @@ +/** + UnitTestLib APIs to run unit tests + + Copyright (c) Microsoft Corporation. + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include +#include +#include + +STATIC UNIT_TEST_FRAMEWORK_HANDLE mFrameworkHandle =3D NULL; + +UNIT_TEST_FRAMEWORK_HANDLE +GetActiveFrameworkHandle ( + VOID + ) +{ + ASSERT (mFrameworkHandle !=3D 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 =3D NULL; + ParentFramework =3D (UNIT_TEST_FRAMEWORK *)Suite->ParentFramework; + + if (Suite =3D=3D 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 !=3D NULL) { + Suite->Setup (); + } + + // + // Iterate all tests within the suite + // + for (TestEntry =3D (UNIT_TEST_LIST_ENTRY *)GetFirstNode (&(Suite->TestCa= seList)); + (LIST_ENTRY*)TestEntry !=3D &(Suite->TestCaseList); + TestEntry =3D (UNIT_TEST_LIST_ENTRY *)GetNextNode (&(Suite->TestCas= eList), (LIST_ENTRY *)TestEntry)) { + Test =3D &TestEntry->UT; + ParentFramework->CurrentTest =3D 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 de= tected and loaded. + // + if (Test->Result !=3D UNIT_TEST_PENDING && Test->Result !=3D UNIT_TEST= _RUNNING) { + DEBUG ((DEBUG_VERBOSE, "Test was run on a previous pass. Skipping.\n= ")); + ParentFramework->CurrentTest =3D NULL; + continue; + } + + // + // Next, if we're still running, make sure that our test prerequisites= are in place. + if (Test->Result =3D=3D UNIT_TEST_PENDING && Test->Prerequisite !=3D N= ULL) { + DEBUG ((DEBUG_VERBOSE, "PREREQ\n")); + if (Test->Prerequisite (Test->Context) !=3D UNIT_TEST_PASSED) { + DEBUG ((DEBUG_ERROR, "Prerequisite Not Met\n")); + Test->Result =3D UNIT_TEST_ERROR_PREREQUISITE_NOT_MET; + ParentFramework->CurrentTest =3D 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 re= boot + // or quit. The UNIT_TEST_RUNNING state will allow the test to resume + // but will prevent the Prerequisite from being dispatched a second ti= me. + Test->Result =3D UNIT_TEST_RUNNING; + Test->Result =3D Test->RunTest (Test->Context); + + // + // Finally, clean everything up, if need be. + if (Test->CleanUp !=3D NULL) { + DEBUG ((DEBUG_VERBOSE, "CLEANUP\n")); + Test->CleanUp (Test->Context); + } + + // + // End the test. + // + ParentFramework->CurrentTest =3D NULL; + } + + if (Suite->Teardown !=3D 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 u= nit + test cases are registered, this function will cause the unit test framew= ork to + dispatch all unit test cases in sequence and record the results for repo= rting. + + @param[in] FrameworkHandle A handle to the current running framework t= hat + dispatched the test. Necessary for recordi= ng + certain test events with the framework. + + @retval EFI_SUCCESS All test cases were dispached. + @retval EFI_INVALID_PARAMETER FrameworkHandle is NULL. +**/ +EFI_STATUS +EFIAPI +RunAllTestSuites ( + IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle + ) +{ + UNIT_TEST_FRAMEWORK *Framework; + UNIT_TEST_SUITE_LIST_ENTRY *Suite; + EFI_STATUS Status; + + Framework =3D (UNIT_TEST_FRAMEWORK *)FrameworkHandle; + Suite =3D NULL; + + if (Framework =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + + DEBUG ((DEBUG_VERBOSE, "------------------------------------------------= ---------\n")); + DEBUG ((DEBUG_VERBOSE, "------------ RUNNING ALL TEST SUITES -----= ---------\n")); + DEBUG ((DEBUG_VERBOSE, "------------------------------------------------= ---------\n")); + mFrameworkHandle =3D FrameworkHandle; + + // + // Iterate all suites + // + for (Suite =3D (UNIT_TEST_SUITE_LIST_ENTRY *)GetFirstNode (&Framework->T= estSuiteList); + (LIST_ENTRY *)Suite !=3D &Framework->TestSuiteList; + Suite =3D (UNIT_TEST_SUITE_LIST_ENTRY *)GetNextNode (&Framework->TestS= uiteList, (LIST_ENTRY *)Suite)) { + Status =3D 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 =3D NULL; + + return EFI_SUCCESS; +} diff --git a/UnitTestFrameworkPkg/Library/UnitTestLib/RunTestsCmocka.c b/Un= itTestFrameworkPkg/Library/UnitTestLib/RunTestsCmocka.c new file mode 100644 index 0000000000..cb9c881723 --- /dev/null +++ b/UnitTestFrameworkPkg/Library/UnitTestLib/RunTestsCmocka.c @@ -0,0 +1,278 @@ +/** @file + UnitTestLib APIs to run unit tests using cmocka + + Copyright (c) 2019 - 2020, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +STATIC UNIT_TEST_FRAMEWORK_HANDLE mFrameworkHandle =3D NULL; + +UNIT_TEST_FRAMEWORK_HANDLE +GetActiveFrameworkHandle ( + VOID + ) +{ + ASSERT (mFrameworkHandle !=3D NULL); + return mFrameworkHandle; +} + +// +// The currently active test suite +// +UNIT_TEST_SUITE *mActiveUnitTestSuite =3D NULL; + +void +CmockaUnitTestFunctionRunner ( + void **state + ) +{ + UNIT_TEST *UnitTest; + UNIT_TEST_SUITE *Suite; + UNIT_TEST_FRAMEWORK *Framework; + + UnitTest =3D (UNIT_TEST *)(*state); + Suite =3D (UNIT_TEST_SUITE *)(UnitTest->ParentSuite); + Framework =3D (UNIT_TEST_FRAMEWORK *)(Suite->ParentFramework); + + if (UnitTest->RunTest =3D=3D NULL) { + UnitTest->Result =3D UNIT_TEST_SKIPPED; + } else { + UnitTest->Result =3D UNIT_TEST_RUNNING; + + Framework->CurrentTest =3D UnitTest; + UnitTest->Result =3D UnitTest->RunTest (UnitTest->Context); + Framework->CurrentTest =3D 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 !=3D NULL) { + print_message("UnitTest: %s - %s\n", UnitTest->Name, UnitTest->Descr= iption); + 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 =3D (UNIT_TEST *)(*state); + Suite =3D (UNIT_TEST_SUITE *)(UnitTest->ParentSuite); + Framework =3D (UNIT_TEST_FRAMEWORK *)(Suite->ParentFramework); + + if (UnitTest->Prerequisite =3D=3D NULL) { + return 0; + } + + Framework->CurrentTest =3D UnitTest; + Result =3D UnitTest->Prerequisite (UnitTest->Context); + Framework->CurrentTest =3D 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 =3D (UNIT_TEST *)(*state); + Suite =3D (UNIT_TEST_SUITE *)(UnitTest->ParentSuite); + Framework =3D (UNIT_TEST_FRAMEWORK *)(Suite->ParentFramework); + + if (UnitTest->CleanUp =3D=3D NULL) { + return 0; + } + + Framework->CurrentTest =3D UnitTest; + UnitTest->CleanUp (UnitTest->Context); + Framework->CurrentTest =3D NULL; + // + // Return 0 for success. Non-zero for error. + // + return 0; +} + +int +CmockaUnitTestSuiteSetupFunctionRunner ( + void **state + ) +{ + if (mActiveUnitTestSuite =3D=3D NULL) { + return -1; + } + if (mActiveUnitTestSuite->Setup =3D=3D NULL) { + return 0; + } + + mActiveUnitTestSuite->Setup (); + // + // Always succeed + // + return 0; +} + +int +CmockaUnitTestSuiteTeardownFunctionRunner ( + void **state + ) +{ + if (mActiveUnitTestSuite =3D=3D NULL) { + return -1; + } + if (mActiveUnitTestSuite->Teardown =3D=3D 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 =3D NULL; + + if (Suite =3D=3D 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 =3D AllocateZeroPool (Suite->NumTests * sizeof (struct CMUnitTest)= ); + ASSERT (Tests !=3D NULL); + + // + // Populate buffer of CMUnitTest entries + // + Index =3D 0; + for (TestEntry =3D (UNIT_TEST_LIST_ENTRY *)GetFirstNode (&(Suite->TestCa= seList)); + (LIST_ENTRY *)TestEntry !=3D &(Suite->TestCaseList); + TestEntry =3D (UNIT_TEST_LIST_ENTRY *)GetNextNode (&(Suite->TestCas= eList), (LIST_ENTRY *)TestEntry)) { + UnitTest =3D &TestEntry->UT; + Tests[Index].name =3D UnitTest->Description; + Tests[Index].test_func =3D CmockaUnitTestFunctionRunner; + Tests[Index].setup_func =3D CmockaUnitTestSetupFunctionRunner; + Tests[Index].teardown_func =3D CmockaUnitTestTeardownFunctionRunner; + Tests[Index].initial_state =3D UnitTest; + Index++; + } + ASSERT (Index =3D=3D Suite->NumTests); + + // + // Run all unit tests in a test suite + // + mActiveUnitTestSuite =3D Suite; + _cmocka_run_group_tests ( + Suite->Title, + Tests, + Suite->NumTests, + CmockaUnitTestSuiteSetupFunctionRunner, + CmockaUnitTestSuiteTeardownFunctionRunner + ); + mActiveUnitTestSuite =3D 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 u= nit + test cases are registered, this function will cause the unit test framew= ork to + dispatch all unit test cases in sequence and record the results for repo= rting. + + @param[in] FrameworkHandle A handle to the current running framework t= hat + dispatched the test. Necessary for recordi= ng + certain test events with the framework. + + @retval EFI_SUCCESS All test cases were dispached. + @retval EFI_INVALID_PARAMETER FrameworkHandle is NULL. +**/ +EFI_STATUS +EFIAPI +RunAllTestSuites ( + IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle + ) +{ + UNIT_TEST_FRAMEWORK *Framework; + UNIT_TEST_SUITE_LIST_ENTRY *Suite; + EFI_STATUS Status; + + Framework =3D (UNIT_TEST_FRAMEWORK *)FrameworkHandle; + Suite =3D NULL; + + if (Framework =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + + DEBUG((DEBUG_VERBOSE, "-------------------------------------------------= --------\n")); + DEBUG((DEBUG_VERBOSE, "------------ RUNNING ALL TEST SUITES ------= --------\n")); + DEBUG((DEBUG_VERBOSE, "-------------------------------------------------= --------\n")); + mFrameworkHandle =3D FrameworkHandle; + + // + // Iterate all suites + // + for (Suite =3D (UNIT_TEST_SUITE_LIST_ENTRY *)GetFirstNode (&Framework->T= estSuiteList); + (LIST_ENTRY *)Suite !=3D &Framework->TestSuiteList; + Suite =3D (UNIT_TEST_SUITE_LIST_ENTRY *)GetNextNode (&Framework->TestS= uiteList, (LIST_ENTRY *)Suite)) { + Status =3D RunTestSuite (&(Suite->UTS)); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Test Suite Failed with Error. %r\n", Status)); + } + } + + mFrameworkHandle =3D NULL; + + return EFI_SUCCESS; +} diff --git a/UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.c b/UnitT= estFrameworkPkg/Library/UnitTestLib/UnitTestLib.c new file mode 100644 index 0000000000..e37c78a41d --- /dev/null +++ b/UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.c @@ -0,0 +1,853 @@ +/** + Implement UnitTestLib + + Copyright (c) Microsoft Corporation. + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include +#include +#include +#include +#include + +/// +/// 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 t= hat would + prevent it from being used as a reporting name or as a serialization nam= e. + + 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 =3D NULL; + NewStringLength =3D AsciiStrnLenS (StringToCopy, UNIT_TEST_MAX_STRING_LE= NGTH) + 1; + NewString =3D AllocatePool (NewStringLength * sizeof( CHAR8 )); + if (NewString !=3D 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 finge= rprint. + NewFingerprint =3D CalculateCrc32( Framework->Title, (AsciiStrLen( Frame= work->Title ) * sizeof( CHAR8 )) ); + NewFingerprint =3D (NewFingerprint >> 8) ^ CalculateCrc32( Framework->Ve= rsionString, (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 t= itle of the suite. + NewFingerprint =3D CalculateCrc32( &Framework->Fingerprint[0], UNIT_TEST= _FINGERPRINT_SIZE ); + NewFingerprint =3D (NewFingerprint >> 8) ^ CalculateCrc32( Suite->Title,= (AsciiStrLen( Suite->Title ) * sizeof( CHAR8 )) ); + NewFingerprint =3D (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 descr= iption and classname of the test. + NewFingerprint =3D CalculateCrc32( &Suite->Fingerprint[0], UNIT_TEST_FIN= GERPRINT_SIZE ); + NewFingerprint =3D (NewFingerprint >> 8) ^ CalculateCrc32( Test->Descrip= tion, (AsciiStrLen( Test->Description ) * sizeof( CHAR8 )) ); + NewFingerprint =3D (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_SI= ZE ) =3D=3D 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 t= hat + dispatched the test. Necessary for recordi= ng + 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 t= he + 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 u= ser + friendly name of the framework. String is + copied. + @param[in] ShortTitle Null-terminaled ASCII short string that is= the + short name of the framework with no spaces. + String is copied. + @param[in] VersionString Null-terminaled ASCII version string for t= he + 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 =3D EFI_SUCCESS; + NewFramework =3D NULL; + + // + // First, check all pointers and make sure nothing's broked. + // + if (FrameworkHandle =3D=3D NULL || Title =3D=3D NULL || + ShortTitle =3D=3D NULL || VersionString =3D=3D 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 =3D AllocateZeroPool (sizeof (UNIT_TEST_FRAMEWORK)); + if (NewFramework =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Next, set up all the test data. + // + NewFrameworkHandle =3D (UNIT_TEST_FRAMEWORK_HANDLE)NewFramework; + NewFramework->Title =3D AllocateAndCopyString (Title); + NewFramework->ShortTitle =3D AllocateAndCopyString (ShortTitle); + NewFramework->VersionString =3D AllocateAndCopyString (VersionString); + NewFramework->Log =3D NULL; + NewFramework->CurrentTest =3D NULL; + NewFramework->SavedState =3D NULL; + if (NewFramework->Title =3D=3D NULL || + NewFramework->ShortTitle =3D=3D NULL || + NewFramework->VersionString =3D=3D NULL) { + Status =3D 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 =3D (UNIT_TEST_SAVE_HEADER *)NewFramework->SavedState; + Status =3D 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 =3D EFI_SUCCESS; + } + } + +Exit: + // + // If we're good, then let's copy the framework. + // + if (!EFI_ERROR (Status)) { + *FrameworkHandle =3D 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 u= ser + friendly name of the test suite. String is + copied. + @param[in] Name Null-terminated ASCII string that is the s= hort + name of the test suite with no spaces. St= ring + is copied. + @param[in] Setup Setup function, runs before suite. This i= s 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 =3D EFI_SUCCESS; + Framework =3D (UNIT_TEST_FRAMEWORK *)FrameworkHandle; + + // + // First, let's check to make sure that our parameters look good. + // + if ((SuiteHandle =3D=3D NULL) || (Framework =3D=3D NULL) || (Title =3D= =3D NULL) || (Name =3D=3D NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Create the new entry. + // + NewSuiteEntry =3D AllocateZeroPool (sizeof (UNIT_TEST_SUITE_LIST_ENTRY)); + if (NewSuiteEntry =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Copy the fields we think we need. + // + NewSuiteEntry->UTS.NumTests =3D 0; + NewSuiteEntry->UTS.Title =3D AllocateAndCopyString (Title); + NewSuiteEntry->UTS.Name =3D AllocateAndCopyString (Name); + NewSuiteEntry->UTS.Setup =3D Setup; + NewSuiteEntry->UTS.Teardown =3D Teardown; + NewSuiteEntry->UTS.ParentFramework =3D FrameworkHandle; + InitializeListHead (&(NewSuiteEntry->Entry)); // List entry = for sibling suites. + InitializeListHead (&(NewSuiteEntry->UTS.TestCaseList)); // List entry = for child tests. + if (NewSuiteEntry->UTS.Title =3D=3D NULL) { + Status =3D EFI_OUT_OF_RESOURCES; + goto Exit; + } + + if (NewSuiteEntry->UTS.Name =3D=3D NULL) { + Status =3D EFI_OUT_OF_RESOURCES; + goto Exit; + } + + // + // Create the suite fingerprint. + // + SetSuiteFingerprint( &NewSuiteEntry->UTS.Fingerprint[0], Framework, &New= SuiteEntry->UTS ); + +Exit: + // + // If everything is going well, add the new suite to the tail list for t= he framework. + // + if (!EFI_ERROR( Status )) { + InsertTailList (&(Framework->TestSuiteList), (LIST_ENTRY *)NewSuiteEnt= ry); + *SuiteHandle =3D (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 cop= ied. + @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 par= ameter + 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 =3D EFI_SUCCESS; + Suite =3D (UNIT_TEST_SUITE *)SuiteHandle; + ParentFramework =3D (UNIT_TEST_FRAMEWORK *)Suite->ParentFramework; + + // + // First, let's check to make sure that our parameters look good. + // + if ((Suite =3D=3D NULL) || (Description =3D=3D NULL) || (Name =3D=3D NUL= L) || (Function =3D=3D NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Create the new entry. + NewTestEntry =3D AllocateZeroPool (sizeof( UNIT_TEST_LIST_ENTRY )); + if (NewTestEntry =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Copy the fields we think we need. + NewTestEntry->UT.Description =3D AllocateAndCopyString (Descriptio= n); + NewTestEntry->UT.Name =3D AllocateAndCopyString (Name); + NewTestEntry->UT.FailureType =3D FAILURETYPE_NOFAILURE; + NewTestEntry->UT.FailureMessage[0] =3D '\0'; + NewTestEntry->UT.Log =3D NULL; + NewTestEntry->UT.Prerequisite =3D Prerequisite; + NewTestEntry->UT.CleanUp =3D CleanUp; + NewTestEntry->UT.RunTest =3D Function; + NewTestEntry->UT.Context =3D Context; + NewTestEntry->UT.Result =3D UNIT_TEST_PENDING; + NewTestEntry->UT.ParentSuite =3D SuiteHandle; + InitializeListHead (&(NewTestEntry->Entry)); // List entry for sibling = tests. + if (NewTestEntry->UT.Description =3D=3D NULL) { + Status =3D EFI_OUT_OF_RESOURCES; + goto Exit; + } + if (NewTestEntry->UT.Name =3D=3D NULL) { + Status =3D EFI_OUT_OF_RESOURCES; + goto Exit; + } + + // + // Create the test fingerprint. + // + SetTestFingerprint (&NewTestEntry->UT.Fingerprint[0], Suite, &NewTestEnt= ry->UT); + + // TODO: Make sure that duplicate fingerprints cannot be created. + + // + // If there is saved test data, update this record. + // + if (ParentFramework->SavedState !=3D NULL) { + UpdateTestFromSave (&NewTestEntry->UT, ParentFramework->SavedState); + } + +Exit: + // + // If everything is going well, add the new suite to the tail list for t= he 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 =3D=3D NULL || SavedState =3D=3D NULL) { + return; + } + if (SavedState->TestCount =3D=3D 0) { + return; + } + + // + // Next, determine whether a matching test can be found. + // Start at the beginning. + // + MatchingTest =3D NULL; + FloatingPointer =3D (UINT8 *)SavedState + sizeof (*SavedState); + for (Index =3D 0; Index < SavedState->TestCount; Index++) { + CurrentTest =3D (UNIT_TEST_SAVE_TEST *)FloatingPointer; + if (CompareFingerprints (&Test->Fingerprint[0], &CurrentTest->Fingerpr= int[0])) { + MatchingTest =3D CurrentTest; + // + // If there's a saved context, it's important that we iterate throug= h the entire list. + // + if (!SavedState->HasSavedContext) { + break; + } + } + + // + // If we didn't find it, we have to increment to the next test. + // + FloatingPointer =3D (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 =3D MatchingTest->Result; + + Test->FailureType =3D 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 la= rger 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->Siz= e - 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 pl= aced the FloatingPointer + // at the beginning of the context structure. + // + SavedContext =3D (UNIT_TEST_SAVE_CONTEXT *)FloatingPointer; + if ((SavedContext->Size - sizeof (UNIT_TEST_SAVE_CONTEXT)) > 0 && + CompareFingerprints (&Test->Fingerprint[0], &SavedContext->Fingerp= rint[0])) { + // + // Override the test context with the saved context. + // + Test->Context =3D (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 =3D (UNIT_TEST_FRAMEWORK *)FrameworkHandle; + Header =3D NULL; + + // + // First, let's not make assumptions about the parameters. + // + if (Framework =3D=3D NULL || + (ContextToSave !=3D NULL && ContextToSaveSize =3D=3D 0) || + ContextToSaveSize > MAX_UINT32) { + return NULL; + } + + // + // Next, we've gotta figure out the resources that will be required to s= erialize the + // the framework state so that we can persist it. + // To start with, we're gonna need a header. + // + TotalSize =3D sizeof (UNIT_TEST_SAVE_HEADER); + // + // Now we need to figure out how many tests there are. + // + TestCount =3D 0; + // + // Iterate all suites. + // + SuiteListHead =3D &Framework->TestSuiteList; + for (Suite =3D GetFirstNode (SuiteListHead); Suite !=3D SuiteListHead; S= uite =3D GetNextNode (SuiteListHead, Suite)) { + // + // Iterate all tests within the suite. + // + TestListHead =3D &((UNIT_TEST_SUITE_LIST_ENTRY *)Suite)->UTS.TestCaseL= ist; + for (Test =3D GetFirstNode (TestListHead); Test !=3D TestListHead; Tes= t =3D GetNextNode (TestListHead, Test)) { + UnitTest =3D &((UNIT_TEST_LIST_ENTRY *)Test)->UT; + // + // Account for the size of a test structure. + // + TotalSize +=3D sizeof( UNIT_TEST_SAVE_TEST ); + // + // If there's a log, make sure to account for the log size. + // + if (UnitTest->Log !=3D NULL) { + // + // The +1 is for the NULL character. Can't forget the NULL charact= er. + // + LogSize =3D (AsciiStrLen (UnitTest->Log) + 1) * sizeof (CHAR8); + ASSERT (LogSize < MAX_UINT32); + TotalSize +=3D (UINT32)LogSize; + } + // + // Increment the test count. + // + TestCount++; + } + } + // + // If there are no tests, we're done here. + // + if (TestCount =3D=3D 0) { + return NULL; + } + // + // Add room for the context, if there is one. + // + if (ContextToSave !=3D NULL) { + TotalSize +=3D sizeof (UNIT_TEST_SAVE_CONTEXT) + (UINT32)ContextToSave= Size; + } + + // + // Now that we know the size, we need to allocate space for the serializ= ed output. + // + Header =3D AllocateZeroPool (TotalSize); + if (Header =3D=3D NULL) { + return NULL; + } + + // + // Alright, let's start setting up some data. + // + Header->Version =3D UNIT_TEST_PERSISTENCE_LIB_VERSION; + Header->SaveStateSize =3D TotalSize; + CopyMem (&Header->Fingerprint[0], &Framework->Fingerprint[0], UNIT_TEST_= FINGERPRINT_SIZE); + CopyMem (&Header->StartTime, &Framework->StartTime, sizeof (EFI_TIME)); + Header->TestCount =3D TestCount; + Header->HasSavedContext =3D FALSE; + + // + // Start adding all of the test cases. + // Set the floating pointer to the start of the current test save buffer. + // + FloatingPointer =3D (UINT8*)Header + sizeof( UNIT_TEST_SAVE_HEADER ); + // + // Iterate all suites. + // + SuiteListHead =3D &Framework->TestSuiteList; + for (Suite =3D GetFirstNode (SuiteListHead); Suite !=3D SuiteListHead; S= uite =3D GetNextNode (SuiteListHead, Suite)) { + // + // Iterate all tests within the suite. + // + TestListHead =3D &((UNIT_TEST_SUITE_LIST_ENTRY *)Suite)->UTS.TestCaseL= ist; + for (Test =3D GetFirstNode (TestListHead); Test !=3D TestListHead; Tes= t =3D GetNextNode (TestListHead, Test)) { + TestSaveData =3D (UNIT_TEST_SAVE_TEST *)FloatingPointer; + UnitTest =3D &((UNIT_TEST_LIST_ENTRY *)Test)->UT; + + // + // Save the fingerprint. + // + CopyMem (&TestSaveData->Fingerprint[0], &UnitTest->Fingerprint[0], U= NIT_TEST_FINGERPRINT_SIZE); + + // + // Save the result. + // + TestSaveData->Result =3D UnitTest->Result; + TestSaveData->FailureType =3D UnitTest->FailureType; + AsciiStrnCpyS (&TestSaveData->FailureMessage[0], UNIT_TEST_TESTFAILU= REMSG_LENGTH, &UnitTest->FailureMessage[0], UNIT_TEST_TESTFAILUREMSG_LENGTH= ); + + + // + // If there is a log, save the log. + // + FloatingPointer +=3D sizeof (UNIT_TEST_SAVE_TEST); + if (UnitTest->Log !=3D NULL) { + // + // The +1 is for the NULL character. Can't forget the NULL charact= er. + // + LogSize =3D (AsciiStrLen (UnitTest->Log) + 1) * sizeof (CHAR8); + CopyMem (FloatingPointer, UnitTest->Log, LogSize); + FloatingPointer +=3D LogSize; + } + + // + // Update the size once the structure is complete. + // NOTE: Should this be a straight cast without validation? + // + TestSaveData->Size =3D (UINT32)(FloatingPointer - (UINT8 *)TestSaveD= ata); + } + } + + // + // If there is a context to save, let's do that now. + // + if (ContextToSave !=3D NULL && Framework->CurrentTest !=3D NULL) { + TestSaveContext =3D (UNIT_TEST_SAVE_CONTEXT*)FloatingPointer; + TestSaveContext->Size =3D (UINT32)ContextToSaveSize + sizeof (UNIT_T= EST_SAVE_CONTEXT); + CopyMem (&TestSaveContext->Fingerprint[0], &Framework->CurrentTest->Fi= ngerprint[0], UNIT_TEST_FINGERPRINT_SIZE); + CopyMem (((UINT8 *)TestSaveContext + sizeof (UNIT_TEST_SAVE_CONTEXT)),= ContextToSave, ContextToSaveSize); + Header->HasSavedContext =3D 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 w= ith + 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, shou= ld be + at least the current execution count) which will be saved by the framewo= rk 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 recor= ding + 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 pass= ed 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 sav= ed. + @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 >=3D 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 no= t 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 =3D NULL; + + // + // First, let's not make assumptions about the parameters. + // + if (FrameworkHandle =3D=3D NULL || + (ContextToSave !=3D NULL && ContextToSaveSize =3D=3D 0) || + ContextToSaveSize > MAX_UINT32) { + return EFI_INVALID_PARAMETER; + } + + // + // Now, let's package up all the data for saving. + // + Header =3D SerializeState (FrameworkHandle, ContextToSave, ContextToSave= Size); + if (Header =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // All that should be left to do is save it using the associated persist= ence lib. + // + Status =3D SaveUnitTestCache (FrameworkHandle, Header); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a - Could not save state! %r\n", __FUNCTION__, = Status)); + Status =3D EFI_DEVICE_ERROR; + } + + // + // Free data that was used. + // + FreePool (Header); + + return Status; +} diff --git a/UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.inf b/Uni= tTestFrameworkPkg/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 Application= s. +# +# Copyright (c) Microsoft Corporation.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION =3D 0x00010017 + BASE_NAME =3D UnitTestLib + MODULE_UNI_FILE =3D UnitTestLib.uni + FILE_GUID =3D 98CEF9CA-15CE-40A3-ADE8-C299953CD0F6 + VERSION_STRING =3D 1.0 + MODULE_TYPE =3D UEFI_DRIVER + LIBRARY_CLASS =3D UnitTestLib|PEIM DXE_DRIVER DXE_SMM_DRIVER UEFI_DRIV= ER 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/Uni= tTestFrameworkPkg/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 Applicatio= ns. +// +// Copyright (c) 2020, Intel Corporation. All rights reserved.
+// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_MODULE_ABSTRACT #language en-US "Library to suppor= t Unit Testing from PEI, DXE, SMM, and UEFI Applications" + +#string STR_MODULE_DESCRIPTION #language en-US "Library to suppor= t 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 serv= ices. +# +# Copyright (c) 2019 - 2020, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION =3D 0x00010017 + BASE_NAME =3D UnitTestLibCmocka + MODULE_UNI_FILE =3D UnitTestLibCmocka.uni + FILE_GUID =3D C800595F-45A3-45A1-8B50-28F01C2A5A4F + VERSION_STRING =3D 1.0 + MODULE_TYPE =3D UEFI_DRIVER + LIBRARY_CLASS =3D 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 ser= vices. +// +// Copyright (c) 2020, Intel Corporation. All rights reserved.
+// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_MODULE_ABSTRACT #language en-US "Library to suppor= t Unit Testing from host environments using Cmocka services" + +#string STR_MODULE_DESCRIPTION #language en-US "Library to suppor= t Unit Testing from host environments using Cmocka services." diff --git a/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTe= stPersistenceLibNull.c b/UnitTestFrameworkPkg/Library/UnitTestPersistenceLi= bNull/UnitTestPersistenceLibNull.c new file mode 100644 index 0000000000..e28327652e --- /dev/null +++ b/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersi= stenceLibNull.c @@ -0,0 +1,75 @@ +/** @file + This is an instance of the Unit Test Persistence Lib that does nothing. + + Copyright (c) Microsoft Corporation.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include + +/** + Determines whether a persistence cache already exists for + the given framework. + + @param[in] FrameworkHandle A pointer to the framework that is being pe= rsisted. + + @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 pe= rsisted. + @param[in] SaveData A pointer to the buffer containing the seri= alized + 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 u= pon 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 pe= rsisted. + @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/UnitTe= stPersistenceLibNull.inf b/UnitTestFrameworkPkg/Library/UnitTestPersistence= LibNull/UnitTestPersistenceLibNull.inf new file mode 100644 index 0000000000..1175772662 --- /dev/null +++ b/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersi= stenceLibNull.inf @@ -0,0 +1,28 @@ +## @file +# This is an instance of the Unit Test Persistence Lib does nothing. +# +# Copyright (c) Microsoft Corporation.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION =3D 0x00010017 + BASE_NAME =3D UnitTestPersistenceLibNull + MODULE_UNI_FILE =3D UnitTestPersistenceLibNull.uni + FILE_GUID =3D B8553C7A-0B0B-4BBD-9DF3-825804BF26AB + VERSION_STRING =3D 1.0 + MODULE_TYPE =3D UEFI_DRIVER + LIBRARY_CLASS =3D UnitTestPersistenceLib + +# +# The following information is for reference only and not required by the = build tools. +# +# VALID_ARCHITECTURES =3D IA32 X64 +# + +[Sources] + UnitTestPersistenceLibNull.c + +[Packages] + MdePkg/MdePkg.dec + UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec diff --git a/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTe= stPersistenceLibNull.uni b/UnitTestFrameworkPkg/Library/UnitTestPersistence= LibNull/UnitTestPersistenceLibNull.uni new file mode 100644 index 0000000000..00f7d8d7f0 --- /dev/null +++ b/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersi= stenceLibNull.uni @@ -0,0 +1,11 @@ +// /** @file +// NULL library for Unit Test Persistence Lib. +// +// Copyright (c) 2020, Intel Corporation. All rights reserved.
+// 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/UnitTestPersistenceLibSimpleFileS= ystem/UnitTestPersistenceLibSimpleFileSystem.c b/UnitTestFrameworkPkg/Libra= ry/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileS= ystem.c new file mode 100644 index 0000000000..ccca9bfacb --- /dev/null +++ b/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/U= nitTestPersistenceLibSimpleFileSystem.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 seriali= zed + version of the internal test state in case the test needs to quit and re= store. + + Copyright (c) Microsoft Corporation.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 pe= rsisted. + + @retval !NULL A pointer to the EFI_FILE protocol instance for the file= system. + @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 =3D (UNIT_TEST_FRAMEWORK*)FrameworkHandle; + AppPath =3D NULL; + CacheFilePath =3D NULL; + TestName =3D NULL; + CacheFileDevicePath =3D NULL; + + // + // First, we need to get some information from the loaded image. + // + Status =3D 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 =3D AsciiStrLen (Framework->ShortTitle) + 1; + TestName =3D AllocatePool (CacheFilePathLength); + if (!TestName) { + goto Exit; + } + AsciiStrToUnicodeStrS (Framework->ShortTitle, TestName, CacheFilePathLen= gth); + + // + // 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 pr= ocess + // the file path a little. + // + // NOTE: This may not be necessary... Path processing functions exist... + // PathCleanUpDirectories (FileNameCopy); + // if (PathRemoveLastItem (FileNameCopy)) { + // + AppPath =3D ConvertDevicePathToText (LoadedImage->FilePath, TRUE, TRUE);= // NOTE: This must be freed. + DirectorySlashOffset =3D StrLen (AppPath); + // + // Make sure we didn't get any weird data. + // + if (DirectorySlashOffset =3D=3D 0) { + DEBUG ((DEBUG_ERROR, "%a - Weird 0-length string when processing app p= ath.\n", __FUNCTION__)); + goto Exit; + } + + // + // Now that we know we have a decent string, let's take a deeper look. + // + do { + if (AppPath[DirectorySlashOffset] =3D=3D L'\\') { + break; + } + DirectorySlashOffset--; + } while (DirectorySlashOffset > 0); + + // + // After that little maneuver, DirectorySlashOffset should be pointing a= t the last '\' in AppString. + // That would be the path to the parent directory that the test app is e= xecuting from. + // Let's check and make sure that's right. + // + if (AppPath[DirectorySlashOffset] !=3D 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 =3D DirectorySlashOffset + 1; + CacheFilePathLength +=3D StrLen (TestName); + CacheFilePathLength +=3D StrLen (CACHE_FILE_SUFFIX); + CacheFilePathLength +=3D 1; // Don't forget the NULL terminator. + CacheFilePath =3D AllocateZeroPool (CacheFilePathLength * sizeof (= CHAR16)); + if (!CacheFilePath) { + goto Exit; + } + + // + // Let's produce our final path string, shall we? + // + StrnCpyS (CacheFilePath, CacheFilePathLength, AppPath, DirectorySlashOff= set + 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 =3D FileDevicePath (LoadedImage->DeviceHandle, Cache= FilePath); + +Exit: + // + // Free allocated buffers. + // + if (AppPath !=3D NULL) { + FreePool (AppPath); + } + if (CacheFilePath !=3D NULL) { + FreePool (CacheFilePath); + } + if (TestName !=3D 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 pe= rsisted. + + @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 =3D GetCacheFileDevicePath (FrameworkHandle); + + // + // Check to see whether the file exists. If the file can be opened for + // reading, it exists. Otherwise, probably not. + // + Status =3D ShellOpenFileByDevicePath ( + &FileDevicePath, + &FileHandle, + EFI_FILE_MODE_READ, + 0 + ); + if (!EFI_ERROR (Status)) { + ShellCloseFile (&FileHandle); + } + + if (FileDevicePath !=3D 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 pe= rsisted. + @param[in] SaveData A pointer to the buffer containing the seri= alized + 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 u= pon 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 =3D=3D NULL || SaveData =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Determine the path for the cache file. + // NOTE: This devpath is allocated and must be freed. + // + FileDevicePath =3D GetCacheFileDevicePath (FrameworkHandle); + + // + //First lets open the file if it exists so we can delete it...This is th= e work around for truncation + // + Status =3D 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 =3D 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 =3D ShellOpenFileByDevicePath ( + &FileDevicePath, + &FileHandle, + (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CRE= ATE), + 0 + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a - Opening file for writing failed! %r\n", __F= UNCTION__, Status)); + goto Exit; + } + + // + // Write the data to the file. + // + WriteCount =3D SaveData->SaveStateSize; + DEBUG ((DEBUG_INFO, "%a - Writing %d bytes to file...\n", __FUNCTION__, = WriteCount)); + Status =3D ShellWriteFile ( + FileHandle, + &WriteCount, + SaveData + ); + + if (EFI_ERROR (Status) || WriteCount !=3D 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 !=3D 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 pe= rsisted. + @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 =3D FALSE; + Buffer =3D NULL; + + // + // Check the inputs for sanity. + // + if (FrameworkHandle =3D=3D NULL || SaveData =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Determine the path for the cache file. + // NOTE: This devpath is allocated and must be freed. + // + FileDevicePath =3D GetCacheFileDevicePath (FrameworkHandle); + + // + // Now that we know the path to the file... let's open it for writing. + // + Status =3D ShellOpenFileByDevicePath ( + &FileDevicePath, + &FileHandle, + EFI_FILE_MODE_READ, + 0 + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a - Opening file for writing failed! %r\n", __F= UNCTION__, Status)); + goto Exit; + } else { + IsFileOpened =3D TRUE; + } + + // + // Now that the file is opened, we need to determine how large a buffer = we need. + // + Status =3D ShellGetFileSize (FileHandle, &LargeFileSize); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a - Failed to determine file size! %r\n", __FUN= CTION__, Status)); + goto Exit; + } + + // + // Now that we know the size, let's allocated a buffer to hold the conte= nts. + // + FileSize =3D (UINTN)LargeFileSize; // You know what... if it's too la= rge, this lib don't care. + Buffer =3D AllocatePool (FileSize); + if (Buffer =3D=3D NULL) { + DEBUG ((DEBUG_ERROR, "%a - Failed to allocate a pool to hold the file = contents! %r\n", __FUNCTION__, Status)); + Status =3D EFI_OUT_OF_RESOURCES; + goto Exit; + } + + // + // Finally, let's read the data. + // + Status =3D 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 !=3D NULL) { + FreePool (FileDevicePath); + } + if (IsFileOpened) { + ShellCloseFile (&FileHandle); + } + + // + // If we're returning an error, make sure + // the state is sane. + if (EFI_ERROR (Status) && Buffer !=3D NULL) { + FreePool (Buffer); + Buffer =3D NULL; + } + + *SaveData =3D Buffer; + return Status; +} diff --git a/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileS= ystem/UnitTestPersistenceLibSimpleFileSystem.inf b/UnitTestFrameworkPkg/Lib= rary/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFil= eSystem.inf new file mode 100644 index 0000000000..c518c4e5ce --- /dev/null +++ b/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/U= nitTestPersistenceLibSimpleFileSystem.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 filesys= tem +# 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.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION =3D 0x00010017 + BASE_NAME =3D UnitTestPersistenceLibSimpleFileSystem + MODULE_UNI_FILE =3D UnitTestPersistenceLibSimpleFileSystem.uni + FILE_GUID =3D 9200844A-CDFD-4368-B4BD-106354702605 + VERSION_STRING =3D 1.0 + MODULE_TYPE =3D UEFI_APPLICATION + LIBRARY_CLASS =3D UnitTestPersistenceLib + +# +# The following information is for reference only and not required by the = build tools. +# +# VALID_ARCHITECTURES =3D 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/UnitTestPersistenceLibSimpleFileS= ystem/UnitTestPersistenceLibSimpleFileSystem.uni b/UnitTestFrameworkPkg/Lib= rary/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFil= eSystem.uni new file mode 100644 index 0000000000..e6593be137 --- /dev/null +++ b/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/U= nitTestPersistenceLibSimpleFileSystem.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 filesy= stem +// 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.
+// 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/UnitTestR= esultReportLib.c b/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/Uni= tTestResultReportLib.c new file mode 100644 index 0000000000..687a04f55d --- /dev/null +++ b/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultRe= portLib.c @@ -0,0 +1,216 @@ +/** @file + Implement UnitTestResultReportLib doing plain txt out to console + + Copyright (c) Microsoft Corporation.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include + +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[] =3D { + { 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[] =3D { + { 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 =3D 0; Index < ARRAY_SIZE (mStatusStrings); Index++) { + if (mStatusStrings[Index].Status =3D=3D 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 =3D 0; Index < ARRAY_SIZE (mFailureTypeStrings); Index++) { + if (mFailureTypeStrings[Index].Type =3D=3D 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 =3D 0; + Failed =3D 0; + NotRun =3D 0; + Suite =3D NULL; + + Framework =3D (UNIT_TEST_FRAMEWORK *)FrameworkHandle; + if (Framework =3D=3D 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 =3D (UNIT_TEST_SUITE_LIST_ENTRY*)GetFirstNode(&Framework->Tes= tSuiteList); + (LIST_ENTRY*)Suite !=3D &Framework->TestSuiteList; + Suite =3D (UNIT_TEST_SUITE_LIST_ENTRY*)GetNextNode(&Framework->TestSui= teList, (LIST_ENTRY*)Suite)) { + + Test =3D NULL; + SPassed =3D 0; + SFailed =3D 0; + SNotRun =3D 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 =3D (UNIT_TEST_LIST_ENTRY*)GetFirstNode(&(Suite->UTS.TestCas= eList)); + (LIST_ENTRY*)Test !=3D &(Suite->UTS.TestCaseList); + Test =3D (UNIT_TEST_LIST_ENTRY*)GetNextNode(&(Suite->UTS.TestCaseLis= t), (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.Fa= ilureType)); + ReportPrint (" FAILURE MESSAGE:\n%a\n", Test->UT.FailureMessage); + + if (Test->UT.Log !=3D 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)/(SPass= ed+SFailed+SNotRun)); + ReportPrint (" Failed: %d (%d%%)\n", SFailed, (SFailed * 100) / (SPa= ssed + SFailed + SNotRun)); + ReportPrint (" Not Run: %d (%d%%)\n", SNotRun, (SNotRun * 100) / (SPa= ssed + SFailed + SNotRun)); + ReportPrint ("++++++++++++++++++++++++++++++++++++++++++++++++++++++++= +\n" ); + + Passed +=3D SPassed; //add to global counters + Failed +=3D SFailed; //add to global counters + NotRun +=3D SNotRun; //add to global counters + }//End Suite iteration + + ReportPrint ("=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D\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 ("=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D\n" ); + + return EFI_SUCCESS; +} diff --git a/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestR= esultReportLibConOut.c b/UnitTestFrameworkPkg/Library/UnitTestResultReportL= ib/UnitTestResultReportLibConOut.c new file mode 100644 index 0000000000..139360ee16 --- /dev/null +++ b/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultRe= portLibConOut.c @@ -0,0 +1,48 @@ +/** @file + Implement UnitTestResultReportLib doing plain txt out to console + + Copyright (c) Microsoft Corporation.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include +#include + +VOID +ReportPrint ( + IN CONST CHAR8 *Format, + ... + ) +{ + VA_LIST Marker; + CHAR16 String[256]; + UINTN Length; + + VA_START (Marker, Format); + Length =3D UnicodeVSPrintAsciiFormat (String, sizeof (String), Format, M= arker); + if (Length =3D=3D 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 =3D AsciiStrLen (Output); + for (Index =3D 0; Index < Length; Index +=3D (sizeof (AsciiString) - 1))= { + AsciiStrCpyS (AsciiString, sizeof (AsciiString), &Output[Index]); + ReportPrint ("%a", AsciiString); + } +} diff --git a/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestR= esultReportLibConOut.inf b/UnitTestFrameworkPkg/Library/UnitTestResultRepor= tLib/UnitTestResultReportLibConOut.inf new file mode 100644 index 0000000000..4382199fbc --- /dev/null +++ b/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultRe= portLibConOut.inf @@ -0,0 +1,29 @@ +## @file +# Library to support printing out the unit test report to a UEFI console +# +# Copyright (c) Microsoft Corporation.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION =3D 0x00010017 + BASE_NAME =3D UnitTestResultReportLibConOut + MODULE_UNI_FILE =3D UnitTestResultReportLibConOut.uni + FILE_GUID =3D C659641D-BA1F-4B58-946E-B1E1103903F9 + VERSION_STRING =3D 1.0 + MODULE_TYPE =3D UEFI_DRIVER + LIBRARY_CLASS =3D UnitTestResultReportLib + +[LibraryClasses] + BaseLib + DebugLib + UefiBootServicesTableLib + PrintLib + +[Packages] + MdePkg/MdePkg.dec + UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec + +[Sources] + UnitTestResultReportLib.c + UnitTestResultReportLibConOut.c diff --git a/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestR= esultReportLibConOut.uni b/UnitTestFrameworkPkg/Library/UnitTestResultRepor= tLib/UnitTestResultReportLibConOut.uni new file mode 100644 index 0000000000..92ba1b84da --- /dev/null +++ b/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultRe= portLibConOut.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.
+// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_MODULE_ABSTRACT #language en-US "Library to suppor= t printing out the unit test report to a UEFI console" + +#string STR_MODULE_DESCRIPTION #language en-US "Library to suppor= t printing out the unit test report to a UEFI console." diff --git a/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestR= esultReportLibDebugLib.c b/UnitTestFrameworkPkg/Library/UnitTestResultRepor= tLib/UnitTestResultReportLibDebugLib.c new file mode 100644 index 0000000000..743aad2958 --- /dev/null +++ b/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultRe= portLibDebugLib.c @@ -0,0 +1,47 @@ +/** @file + Implement UnitTestResultReportLib doing plain txt out to console + + Copyright (c) Microsoft Corporation.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include + +VOID +ReportPrint ( + IN CONST CHAR8 *Format, + ... + ) +{ + VA_LIST Marker; + CHAR8 String[256]; + UINTN Length; + + VA_START (Marker, Format); + Length =3D AsciiVSPrint (String, sizeof (String), Format, Marker); + if (Length =3D=3D 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 =3D AsciiStrLen (Output); + for (Index =3D 0; Index < Length; Index +=3D (sizeof (AsciiString) - 1))= { + AsciiStrCpyS (AsciiString, sizeof (AsciiString), &Output[Index]); + DEBUG ((DEBUG_INFO, AsciiString)); + } +} diff --git a/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestR= esultReportLibDebugLib.inf b/UnitTestFrameworkPkg/Library/UnitTestResultRep= ortLib/UnitTestResultReportLibDebugLib.inf new file mode 100644 index 0000000000..a1c786a700 --- /dev/null +++ b/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultRe= portLibDebugLib.inf @@ -0,0 +1,28 @@ +## @file +# Library to support printing out the unit test report using DEBUG() macro= s. +# +# Copyright (c) Microsoft Corporation.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION =3D 0x00010017 + BASE_NAME =3D UnitTestResultReportLibDebugLib + MODULE_UNI_FILE =3D UnitTestResultReportLibDebugLib.uni + FILE_GUID =3D BED736D4-D197-475F-B7CE-0D828FF2C9A6 + VERSION_STRING =3D 1.0 + MODULE_TYPE =3D UEFI_DRIVER + LIBRARY_CLASS =3D UnitTestResultReportLib + +[LibraryClasses] + BaseLib + DebugLib + PrintLib + +[Packages] + MdePkg/MdePkg.dec + UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec + +[Sources] + UnitTestResultReportLib.c + UnitTestResultReportLibDebugLib.c diff --git a/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestR= esultReportLibDebugLib.uni b/UnitTestFrameworkPkg/Library/UnitTestResultRep= ortLib/UnitTestResultReportLibDebugLib.uni new file mode 100644 index 0000000000..4f1993417a --- /dev/null +++ b/UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultRe= portLibDebugLib.uni @@ -0,0 +1,11 @@ +// /** @file +// Library to support printing out the unit test report using DEBUG() macr= os. +// +// Copyright (c) 2020, Intel Corporation. All rights reserved.
+// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_MODULE_ABSTRACT #language en-US "Library to suppor= t printing out the unit test report using DEBUG() macros" + +#string STR_MODULE_DESCRIPTION #language en-US "Library to suppor= t printing out the unit test report using DEBUG() macros." --=20 2.21.0.windows.1 -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#53463): https://edk2.groups.io/g/devel/message/53463 Mute This Topic: https://groups.io/mt/70061164/1787277 Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org] -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-