UnitTestFrameworkPkg/Library: Add library instances
https://bugzilla.tianocore.org/show_bug.cgi?id=2505 Add the following library instances that are used to build unit tests for host and target environments. * CmockaLib with cmocka submodule to: https://git.cryptomilk.org/projects/cmocka.git * DebugLibPosix - Instance of DebugLib based on POSIX APIs (e.g. printf). * MemoryAllocationLibPosix - Instance of MemoryAllocationLib based on POSIX APIs (e.g. malloc/free). * UnitTestBootLibNull - Null instance of the UnitTestBootLib * UnitTestBootLibUsbClass - UnitTestBootLib instances that supports setting boot next to a USB device. * UnitTestLib - UnitTestLib instance that is designed to work with PEI, DXE, SMM, and UEFI Shell target environments. * UnitTestLibCmocka - UintTestLib instance that uses cmocka APIs and can only be use in a host environment. * UnitTestPersistenceLibNull - Null instance of the UnitTestPersistenceLib * UnitTestPersistenceLibSimpleFileSystem - UnitTestPersistenceLib instance that can safe the unit test framework state to a media device that supports the UEFI Simple File System Protocol. * UnitTestResultReportLibConOut - UnitTestResultReportLib instance that sends report results to the UEFI standard output console. * UnitTestResultReportLibDebugLib - UnitTestResultReportLib instance that sends report results to a DebugLib using DEBUG() macros. Cc: Sean Brogan <sean.brogan@microsoft.com> Cc: Bret Barkelew <Bret.Barkelew@microsoft.com> Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com> Reviewed-by: Bret Barkelew <Bret.Barkelew@microsoft.com>
This commit is contained in:
committed by
mergify[bot]
parent
0f7fb5c5e5
commit
0eb522987f
@ -0,0 +1,416 @@
|
||||
/** @file
|
||||
This is an instance of the Unit Test Persistence Lib that will utilize
|
||||
the filesystem that a test application is running from to save a serialized
|
||||
version of the internal test state in case the test needs to quit and restore.
|
||||
|
||||
Copyright (c) Microsoft Corporation.<BR>
|
||||
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
**/
|
||||
|
||||
#include <PiDxe.h>
|
||||
#include <Library/UnitTestPersistenceLib.h>
|
||||
#include <Library/BaseLib.h>
|
||||
#include <Library/DebugLib.h>
|
||||
#include <Library/MemoryAllocationLib.h>
|
||||
#include <Library/UefiBootServicesTableLib.h>
|
||||
#include <Library/DevicePathLib.h>
|
||||
#include <Library/ShellLib.h>
|
||||
#include <Protocol/LoadedImage.h>
|
||||
|
||||
#define CACHE_FILE_SUFFIX L"_Cache.dat"
|
||||
|
||||
/**
|
||||
Generate the device path to the cache file.
|
||||
|
||||
@param[in] FrameworkHandle A pointer to the framework that is being persisted.
|
||||
|
||||
@retval !NULL A pointer to the EFI_FILE protocol instance for the filesystem.
|
||||
@retval NULL Filesystem could not be found or an error occurred.
|
||||
|
||||
**/
|
||||
STATIC
|
||||
EFI_DEVICE_PATH_PROTOCOL*
|
||||
GetCacheFileDevicePath (
|
||||
IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
UNIT_TEST_FRAMEWORK *Framework;
|
||||
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
|
||||
CHAR16 *AppPath;
|
||||
CHAR16 *CacheFilePath;
|
||||
CHAR16 *TestName;
|
||||
UINTN DirectorySlashOffset;
|
||||
UINTN CacheFilePathLength;
|
||||
EFI_DEVICE_PATH_PROTOCOL *CacheFileDevicePath;
|
||||
|
||||
Framework = (UNIT_TEST_FRAMEWORK*)FrameworkHandle;
|
||||
AppPath = NULL;
|
||||
CacheFilePath = NULL;
|
||||
TestName = NULL;
|
||||
CacheFileDevicePath = NULL;
|
||||
|
||||
//
|
||||
// First, we need to get some information from the loaded image.
|
||||
//
|
||||
Status = gBS->HandleProtocol (
|
||||
gImageHandle,
|
||||
&gEfiLoadedImageProtocolGuid,
|
||||
(VOID**)&LoadedImage
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_WARN, "%a - Failed to locate DevicePath for loaded image. %r\n", __FUNCTION__, Status));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//
|
||||
// Before we can start, change test name from ASCII to Unicode.
|
||||
//
|
||||
CacheFilePathLength = AsciiStrLen (Framework->ShortTitle) + 1;
|
||||
TestName = AllocatePool (CacheFilePathLength);
|
||||
if (!TestName) {
|
||||
goto Exit;
|
||||
}
|
||||
AsciiStrToUnicodeStrS (Framework->ShortTitle, TestName, CacheFilePathLength);
|
||||
|
||||
//
|
||||
// Now we should have the device path of the root device and a file path for the rest.
|
||||
// In order to target the directory for the test application, we must process
|
||||
// the file path a little.
|
||||
//
|
||||
// NOTE: This may not be necessary... Path processing functions exist...
|
||||
// PathCleanUpDirectories (FileNameCopy);
|
||||
// if (PathRemoveLastItem (FileNameCopy)) {
|
||||
//
|
||||
AppPath = ConvertDevicePathToText (LoadedImage->FilePath, TRUE, TRUE); // NOTE: This must be freed.
|
||||
DirectorySlashOffset = StrLen (AppPath);
|
||||
//
|
||||
// Make sure we didn't get any weird data.
|
||||
//
|
||||
if (DirectorySlashOffset == 0) {
|
||||
DEBUG ((DEBUG_ERROR, "%a - Weird 0-length string when processing app path.\n", __FUNCTION__));
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
//
|
||||
// Now that we know we have a decent string, let's take a deeper look.
|
||||
//
|
||||
do {
|
||||
if (AppPath[DirectorySlashOffset] == L'\\') {
|
||||
break;
|
||||
}
|
||||
DirectorySlashOffset--;
|
||||
} while (DirectorySlashOffset > 0);
|
||||
|
||||
//
|
||||
// After that little maneuver, DirectorySlashOffset should be pointing at the last '\' in AppString.
|
||||
// That would be the path to the parent directory that the test app is executing from.
|
||||
// Let's check and make sure that's right.
|
||||
//
|
||||
if (AppPath[DirectorySlashOffset] != L'\\') {
|
||||
DEBUG ((DEBUG_ERROR, "%a - Could not find a single directory separator in app path.\n", __FUNCTION__));
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
//
|
||||
// Now we know some things, we're ready to produce our output string, I think.
|
||||
//
|
||||
CacheFilePathLength = DirectorySlashOffset + 1;
|
||||
CacheFilePathLength += StrLen (TestName);
|
||||
CacheFilePathLength += StrLen (CACHE_FILE_SUFFIX);
|
||||
CacheFilePathLength += 1; // Don't forget the NULL terminator.
|
||||
CacheFilePath = AllocateZeroPool (CacheFilePathLength * sizeof (CHAR16));
|
||||
if (!CacheFilePath) {
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
//
|
||||
// Let's produce our final path string, shall we?
|
||||
//
|
||||
StrnCpyS (CacheFilePath, CacheFilePathLength, AppPath, DirectorySlashOffset + 1); // Copy the path for the parent directory.
|
||||
StrCatS (CacheFilePath, CacheFilePathLength, TestName); // Copy the base name for the test cache.
|
||||
StrCatS (CacheFilePath, CacheFilePathLength, CACHE_FILE_SUFFIX); // Copy the file suffix.
|
||||
|
||||
//
|
||||
// Finally, try to create the device path for the thing thing.
|
||||
//
|
||||
CacheFileDevicePath = FileDevicePath (LoadedImage->DeviceHandle, CacheFilePath);
|
||||
|
||||
Exit:
|
||||
//
|
||||
// Free allocated buffers.
|
||||
//
|
||||
if (AppPath != NULL) {
|
||||
FreePool (AppPath);
|
||||
}
|
||||
if (CacheFilePath != NULL) {
|
||||
FreePool (CacheFilePath);
|
||||
}
|
||||
if (TestName != NULL) {
|
||||
FreePool (TestName);
|
||||
}
|
||||
|
||||
return CacheFileDevicePath;
|
||||
}
|
||||
|
||||
/**
|
||||
Determines whether a persistence cache already exists for
|
||||
the given framework.
|
||||
|
||||
@param[in] FrameworkHandle A pointer to the framework that is being persisted.
|
||||
|
||||
@retval TRUE
|
||||
@retval FALSE Cache doesn't exist or an error occurred.
|
||||
|
||||
**/
|
||||
BOOLEAN
|
||||
EFIAPI
|
||||
DoesCacheExist (
|
||||
IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle
|
||||
)
|
||||
{
|
||||
EFI_DEVICE_PATH_PROTOCOL *FileDevicePath;
|
||||
EFI_STATUS Status;
|
||||
SHELL_FILE_HANDLE FileHandle;
|
||||
|
||||
//
|
||||
// NOTE: This devpath is allocated and must be freed.
|
||||
//
|
||||
FileDevicePath = GetCacheFileDevicePath (FrameworkHandle);
|
||||
|
||||
//
|
||||
// Check to see whether the file exists. If the file can be opened for
|
||||
// reading, it exists. Otherwise, probably not.
|
||||
//
|
||||
Status = ShellOpenFileByDevicePath (
|
||||
&FileDevicePath,
|
||||
&FileHandle,
|
||||
EFI_FILE_MODE_READ,
|
||||
0
|
||||
);
|
||||
if (!EFI_ERROR (Status)) {
|
||||
ShellCloseFile (&FileHandle);
|
||||
}
|
||||
|
||||
if (FileDevicePath != NULL) {
|
||||
FreePool (FileDevicePath);
|
||||
}
|
||||
|
||||
DEBUG ((DEBUG_VERBOSE, "%a - Returning %d\n", __FUNCTION__, !EFI_ERROR (Status)));
|
||||
|
||||
return (!EFI_ERROR (Status));
|
||||
}
|
||||
|
||||
/**
|
||||
Will save the data associated with an internal Unit Test Framework
|
||||
state in a manner that can persist a Unit Test Application quit or
|
||||
even a system reboot.
|
||||
|
||||
@param[in] FrameworkHandle A pointer to the framework that is being persisted.
|
||||
@param[in] SaveData A pointer to the buffer containing the serialized
|
||||
framework internal state.
|
||||
|
||||
@retval EFI_SUCCESS Data is persisted and the test can be safely quit.
|
||||
@retval Others Data is not persisted and test cannot be resumed upon exit.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
SaveUnitTestCache (
|
||||
IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle,
|
||||
IN UNIT_TEST_SAVE_HEADER *SaveData
|
||||
)
|
||||
{
|
||||
EFI_DEVICE_PATH_PROTOCOL *FileDevicePath;
|
||||
EFI_STATUS Status;
|
||||
SHELL_FILE_HANDLE FileHandle;
|
||||
UINTN WriteCount;
|
||||
|
||||
//
|
||||
// Check the inputs for sanity.
|
||||
//
|
||||
if (FrameworkHandle == NULL || SaveData == NULL) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
//
|
||||
// Determine the path for the cache file.
|
||||
// NOTE: This devpath is allocated and must be freed.
|
||||
//
|
||||
FileDevicePath = GetCacheFileDevicePath (FrameworkHandle);
|
||||
|
||||
//
|
||||
//First lets open the file if it exists so we can delete it...This is the work around for truncation
|
||||
//
|
||||
Status = ShellOpenFileByDevicePath (
|
||||
&FileDevicePath,
|
||||
&FileHandle,
|
||||
(EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE),
|
||||
0
|
||||
);
|
||||
|
||||
if (!EFI_ERROR (Status)) {
|
||||
//
|
||||
// If file handle above was opened it will be closed by the delete.
|
||||
//
|
||||
Status = ShellDeleteFile (&FileHandle);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_ERROR, "%a failed to delete file %r\n", __FUNCTION__, Status));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Now that we know the path to the file... let's open it for writing.
|
||||
//
|
||||
Status = ShellOpenFileByDevicePath (
|
||||
&FileDevicePath,
|
||||
&FileHandle,
|
||||
(EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE),
|
||||
0
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_ERROR, "%a - Opening file for writing failed! %r\n", __FUNCTION__, Status));
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
//
|
||||
// Write the data to the file.
|
||||
//
|
||||
WriteCount = SaveData->SaveStateSize;
|
||||
DEBUG ((DEBUG_INFO, "%a - Writing %d bytes to file...\n", __FUNCTION__, WriteCount));
|
||||
Status = ShellWriteFile (
|
||||
FileHandle,
|
||||
&WriteCount,
|
||||
SaveData
|
||||
);
|
||||
|
||||
if (EFI_ERROR (Status) || WriteCount != SaveData->SaveStateSize) {
|
||||
DEBUG ((DEBUG_ERROR, "%a - Writing to file failed! %r\n", __FUNCTION__, Status));
|
||||
} else {
|
||||
DEBUG ((DEBUG_INFO, "%a - SUCCESS!\n", __FUNCTION__));
|
||||
}
|
||||
|
||||
//
|
||||
// No matter what, we should probably close the file.
|
||||
//
|
||||
ShellCloseFile (&FileHandle);
|
||||
|
||||
Exit:
|
||||
if (FileDevicePath != NULL) {
|
||||
FreePool (FileDevicePath);
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
/**
|
||||
Will retrieve any cached state associated with the given framework.
|
||||
Will allocate a buffer to hold the loaded data.
|
||||
|
||||
@param[in] FrameworkHandle A pointer to the framework that is being persisted.
|
||||
@param[in] SaveData A pointer pointer that will be updated with the address
|
||||
of the loaded data buffer.
|
||||
|
||||
@retval EFI_SUCCESS Data has been loaded successfully and SaveData is updated
|
||||
with a pointer to the buffer.
|
||||
@retval Others An error has occurred and no data has been loaded. SaveData
|
||||
is set to NULL.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
LoadUnitTestCache (
|
||||
IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle,
|
||||
OUT UNIT_TEST_SAVE_HEADER **SaveData
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
EFI_DEVICE_PATH_PROTOCOL *FileDevicePath;
|
||||
SHELL_FILE_HANDLE FileHandle;
|
||||
BOOLEAN IsFileOpened;
|
||||
UINT64 LargeFileSize;
|
||||
UINTN FileSize;
|
||||
UNIT_TEST_SAVE_HEADER *Buffer;
|
||||
|
||||
IsFileOpened = FALSE;
|
||||
Buffer = NULL;
|
||||
|
||||
//
|
||||
// Check the inputs for sanity.
|
||||
//
|
||||
if (FrameworkHandle == NULL || SaveData == NULL) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
//
|
||||
// Determine the path for the cache file.
|
||||
// NOTE: This devpath is allocated and must be freed.
|
||||
//
|
||||
FileDevicePath = GetCacheFileDevicePath (FrameworkHandle);
|
||||
|
||||
//
|
||||
// Now that we know the path to the file... let's open it for writing.
|
||||
//
|
||||
Status = ShellOpenFileByDevicePath (
|
||||
&FileDevicePath,
|
||||
&FileHandle,
|
||||
EFI_FILE_MODE_READ,
|
||||
0
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_ERROR, "%a - Opening file for writing failed! %r\n", __FUNCTION__, Status));
|
||||
goto Exit;
|
||||
} else {
|
||||
IsFileOpened = TRUE;
|
||||
}
|
||||
|
||||
//
|
||||
// Now that the file is opened, we need to determine how large a buffer we need.
|
||||
//
|
||||
Status = ShellGetFileSize (FileHandle, &LargeFileSize);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_ERROR, "%a - Failed to determine file size! %r\n", __FUNCTION__, Status));
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
//
|
||||
// Now that we know the size, let's allocated a buffer to hold the contents.
|
||||
//
|
||||
FileSize = (UINTN)LargeFileSize; // You know what... if it's too large, this lib don't care.
|
||||
Buffer = AllocatePool (FileSize);
|
||||
if (Buffer == NULL) {
|
||||
DEBUG ((DEBUG_ERROR, "%a - Failed to allocate a pool to hold the file contents! %r\n", __FUNCTION__, Status));
|
||||
Status = EFI_OUT_OF_RESOURCES;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
//
|
||||
// Finally, let's read the data.
|
||||
//
|
||||
Status = ShellReadFile (FileHandle, &FileSize, Buffer);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_ERROR, "%a - Failed to read the file contents! %r\n", __FUNCTION__, Status));
|
||||
}
|
||||
|
||||
Exit:
|
||||
//
|
||||
// Free allocated buffers
|
||||
//
|
||||
if (FileDevicePath != NULL) {
|
||||
FreePool (FileDevicePath);
|
||||
}
|
||||
if (IsFileOpened) {
|
||||
ShellCloseFile (&FileHandle);
|
||||
}
|
||||
|
||||
//
|
||||
// If we're returning an error, make sure
|
||||
// the state is sane.
|
||||
if (EFI_ERROR (Status) && Buffer != NULL) {
|
||||
FreePool (Buffer);
|
||||
Buffer = NULL;
|
||||
}
|
||||
|
||||
*SaveData = Buffer;
|
||||
return Status;
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
## @file
|
||||
# UEFI Simple File System based version of the Unit Test Persistence Lib
|
||||
#
|
||||
# Instance of the Unit Test Persistence Lib that utilizes the UEFI filesystem
|
||||
# that a test application is running from to save a serialized version of the
|
||||
# internal test state in case the test needs to quit and restore.
|
||||
#
|
||||
# Copyright (c) Microsoft Corporation.<BR>
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
##
|
||||
|
||||
[Defines]
|
||||
INF_VERSION = 0x00010017
|
||||
BASE_NAME = UnitTestPersistenceLibSimpleFileSystem
|
||||
MODULE_UNI_FILE = UnitTestPersistenceLibSimpleFileSystem.uni
|
||||
FILE_GUID = 9200844A-CDFD-4368-B4BD-106354702605
|
||||
VERSION_STRING = 1.0
|
||||
MODULE_TYPE = UEFI_APPLICATION
|
||||
LIBRARY_CLASS = UnitTestPersistenceLib
|
||||
|
||||
#
|
||||
# The following information is for reference only and not required by the build tools.
|
||||
#
|
||||
# VALID_ARCHITECTURES = IA32 X64
|
||||
#
|
||||
|
||||
[Sources]
|
||||
UnitTestPersistenceLibSimpleFileSystem.c
|
||||
|
||||
[Packages]
|
||||
MdePkg/MdePkg.dec
|
||||
UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec
|
||||
ShellPkg/ShellPkg.dec
|
||||
|
||||
[LibraryClasses]
|
||||
DebugLib
|
||||
UefiBootServicesTableLib
|
||||
BaseLib
|
||||
ShellLib
|
||||
|
||||
[Protocols]
|
||||
gEfiLoadedImageProtocolGuid
|
||||
gEfiSimpleFileSystemProtocolGuid
|
||||
|
||||
[Guids]
|
||||
gEfiFileInfoGuid
|
||||
gEfiFileSystemInfoGuid
|
@ -0,0 +1,15 @@
|
||||
// /** @file
|
||||
// UEFI Simple File System based version of the Unit Test Persistence Lib
|
||||
//
|
||||
// Instance of the Unit Test Persistence Lib that utilizes the UEFI filesystem
|
||||
// that a test application is running from to save a serialized version of the
|
||||
// internal test state in case the test needs to quit and restore.
|
||||
//
|
||||
// Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
|
||||
// SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
//
|
||||
// **/
|
||||
|
||||
#string STR_MODULE_ABSTRACT #language en-US "UEFI Simple File System based version of the Unit Test Persistence Lib"
|
||||
|
||||
#string STR_MODULE_DESCRIPTION #language en-US "UEFI Simple File System based version of the Unit Test Persistence Lib."
|
Reference in New Issue
Block a user