diff options
| author | kx <kx@radix.pro> | 2024-06-04 06:43:08 +0300 |
|---|---|---|
| committer | kx <kx@radix.pro> | 2024-06-04 06:43:08 +0300 |
| commit | 7e2ccccace24636f29ddc210b94606abd4c7e42b (patch) | |
| tree | 545d43ea12671048956cafeb12303e84d601e571 /UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.c | |
| parent | 27b044605cd5f6b33a3d231576003850b3fe305b (diff) | |
| download | edk2-trunk.tar.xz | |
Renormalized end-of-lines from master@27b044605cd5f6b33a3d231576003850b3fe305btrunk
Diffstat (limited to 'UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.c')
| -rw-r--r-- | UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.c | 1020 |
1 files changed, 510 insertions, 510 deletions
diff --git a/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.c b/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.c index 8f7f03646c..25b84c2740 100644 --- a/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.c +++ b/UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.c @@ -1,510 +1,510 @@ -/** @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>
- Copyright (c) 2022, Intel Corporation. All rights reserved.<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 <Library/UefiLib.h>
-#include <Protocol/LoadedImage.h>
-#include <UnitTestFrameworkTypes.h>
-
-#define CACHE_FILE_SUFFIX L"_Cache.dat"
-
-CHAR16 *mCachePath = NULL;
-
-/**
- Generate the file name and 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
-CHAR16 *
-GetCacheFileName (
- 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;
-
- Framework = (UNIT_TEST_FRAMEWORK *)FrameworkHandle;
- AppPath = NULL;
- CacheFilePath = NULL;
- TestName = 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", __func__, Status));
- return NULL;
- }
-
- //
- // Before we can start, change test name from ASCII to Unicode.
- //
- CacheFilePathLength = AsciiStrLen (Framework->ShortTitle) + 1;
- TestName = AllocatePool (CacheFilePathLength * sizeof (CHAR16));
- 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)) {
- //
- if (mCachePath == NULL) {
- AppPath = ConvertDevicePathToText (LoadedImage->FilePath, TRUE, TRUE); // NOTE: This must be freed.
- if (AppPath == NULL) {
- goto Exit;
- }
-
- 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", __func__));
- 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", __func__));
- goto Exit;
- }
- } else {
- AppPath = FullyQualifyPath (mCachePath); // NOTE: This must be freed.
- if (AppPath == NULL) {
- goto Exit;
- }
-
- DirectorySlashOffset = StrLen (AppPath);
-
- if (AppPath[DirectorySlashOffset - 1] != L'\\') {
- // Set the slash if user did not specify it on the newly allocated pool
- AppPath = ReallocatePool (
- (DirectorySlashOffset + 1) * sizeof (CHAR16),
- (DirectorySlashOffset + 2) * sizeof (CHAR16),
- AppPath
- );
- AppPath[DirectorySlashOffset] = L'\\';
- AppPath[DirectorySlashOffset + 1] = L'\0';
- } else {
- // Otherwise the user input is good enough to go, mostly
- DirectorySlashOffset--;
- }
- }
-
- //
- // 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.
-
-Exit:
- //
- // Free allocated buffers.
- //
- if (AppPath != NULL) {
- FreePool (AppPath);
- }
-
- if (TestName != NULL) {
- FreePool (TestName);
- }
-
- return CacheFilePath;
-}
-
-/**
- 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
- )
-{
- CHAR16 *FileName;
- EFI_STATUS Status;
- SHELL_FILE_HANDLE FileHandle;
-
- //
- // NOTE: This devpath is allocated and must be freed.
- //
- FileName = GetCacheFileName (FrameworkHandle);
- if (FileName == NULL) {
- return FALSE;
- }
-
- //
- // Check to see whether the file exists. If the file can be opened for
- // reading, it exists. Otherwise, probably not.
- //
- Status = ShellOpenFileByName (
- FileName,
- &FileHandle,
- EFI_FILE_MODE_READ,
- 0
- );
- if (!EFI_ERROR (Status)) {
- ShellCloseFile (&FileHandle);
- }
-
- if (FileName != NULL) {
- FreePool (FileName);
- }
-
- DEBUG ((DEBUG_VERBOSE, "%a - Returning %d\n", __func__, !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.
- @param[in] SaveStateSize The size of SaveData in bytes.
-
- @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 VOID *SaveData,
- IN UINTN SaveStateSize
- )
-{
- CHAR16 *FileName;
- 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.
- //
- FileName = GetCacheFileName (FrameworkHandle);
- if (FileName == NULL) {
- return EFI_INVALID_PARAMETER;
- }
-
- //
- // First lets open the file if it exists so we can delete it...This is the work around for truncation
- //
- Status = ShellOpenFileByName (
- FileName,
- &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", __func__, Status));
- }
- }
-
- //
- // Now that we know the path to the file... let's open it for writing.
- //
- Status = ShellOpenFileByName (
- FileName,
- &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", __func__, Status));
- goto Exit;
- }
-
- //
- // Write the data to the file.
- //
- WriteCount = SaveStateSize;
- DEBUG ((DEBUG_INFO, "%a - Writing %d bytes to file...\n", __func__, WriteCount));
- Status = ShellWriteFile (
- FileHandle,
- &WriteCount,
- SaveData
- );
-
- if (EFI_ERROR (Status) || (WriteCount != SaveStateSize)) {
- DEBUG ((DEBUG_ERROR, "%a - Writing to file failed! %r\n", __func__, Status));
- } else {
- DEBUG ((DEBUG_INFO, "%a - SUCCESS!\n", __func__));
- }
-
- //
- // No matter what, we should probably close the file.
- //
- ShellCloseFile (&FileHandle);
-
-Exit:
- if (FileName != NULL) {
- FreePool (FileName);
- }
-
- 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[out] SaveData A pointer pointer that will be updated with the address
- of the loaded data buffer.
- @param[out] SaveStateSize Return the size of SaveData in bytes.
-
- @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 VOID **SaveData,
- OUT UINTN *SaveStateSize
- )
-{
- EFI_STATUS Status;
- CHAR16 *FileName;
- SHELL_FILE_HANDLE FileHandle;
- BOOLEAN IsFileOpened;
- UINT64 LargeFileSize;
- UINTN FileSize;
- VOID *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.
- //
- FileName = GetCacheFileName (FrameworkHandle);
- if (FileName == NULL) {
- return EFI_INVALID_PARAMETER;
- }
-
- //
- // Now that we know the path to the file... let's open it for writing.
- //
- Status = ShellOpenFileByName (
- FileName,
- &FileHandle,
- EFI_FILE_MODE_READ,
- 0
- );
- if (EFI_ERROR (Status)) {
- DEBUG ((DEBUG_ERROR, "%a - Opening file for writing failed! %r\n", __func__, 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", __func__, 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.
- *SaveStateSize = FileSize;
- Buffer = AllocatePool (FileSize);
- if (Buffer == NULL) {
- DEBUG ((DEBUG_ERROR, "%a - Failed to allocate a pool to hold the file contents! %r\n", __func__, 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", __func__, Status));
- }
-
-Exit:
- //
- // Free allocated buffers
- //
- if (FileName != NULL) {
- FreePool (FileName);
- }
-
- 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;
-}
-
-/**
- Shell based UnitTestPersistenceLib library constructor.
-
- @param[in] ImageHandle The firmware allocated handle for the EFI image.
- @param[in] SystemTable A pointer to the EFI System Table.
-
- @retval EFI_SUCCESS The constructor finished successfully.
- @retval Others Error codes returned from gBS->HandleProtocol.
- **/
-EFI_STATUS
-EFIAPI
-UnitTestPersistenceLibConstructor (
- IN EFI_HANDLE ImageHandle,
- IN EFI_SYSTEM_TABLE *SystemTable
- )
-{
- UINTN Index;
- UINTN Argc;
- CHAR16 **Argv;
- EFI_STATUS Status;
- EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters;
-
- Status = gBS->HandleProtocol (
- gImageHandle,
- &gEfiShellParametersProtocolGuid,
- (VOID **)&ShellParameters
- );
- if (EFI_ERROR (Status)) {
- ASSERT_EFI_ERROR (Status);
- goto Done;
- }
-
- Argc = ShellParameters->Argc;
- Argv = ShellParameters->Argv;
-
- Status = EFI_SUCCESS;
- if ((Argc > 1) && (Argv != NULL)) {
- // This might be our cue, check for whether we need to do anything
- for (Index = 1; Index < Argc; Index++) {
- if (StrCmp (Argv[Index], L"--CachePath") == 0) {
- // Need to update the potential cache path to designated path
- if (Index < Argc - 1) {
- mCachePath = Argv[Index + 1];
- } else {
- Print (L" --CachePath <Path of where to save unit test cache files, i.e. FS0:TestFolder>\n");
- }
- }
- }
- }
-
-Done:
- return Status;
-}
+/** @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> + Copyright (c) 2022, Intel Corporation. All rights reserved.<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 <Library/UefiLib.h> +#include <Protocol/LoadedImage.h> +#include <UnitTestFrameworkTypes.h> + +#define CACHE_FILE_SUFFIX L"_Cache.dat" + +CHAR16 *mCachePath = NULL; + +/** + Generate the file name and 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 +CHAR16 * +GetCacheFileName ( + 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; + + Framework = (UNIT_TEST_FRAMEWORK *)FrameworkHandle; + AppPath = NULL; + CacheFilePath = NULL; + TestName = 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", __func__, Status)); + return NULL; + } + + // + // Before we can start, change test name from ASCII to Unicode. + // + CacheFilePathLength = AsciiStrLen (Framework->ShortTitle) + 1; + TestName = AllocatePool (CacheFilePathLength * sizeof (CHAR16)); + 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)) { + // + if (mCachePath == NULL) { + AppPath = ConvertDevicePathToText (LoadedImage->FilePath, TRUE, TRUE); // NOTE: This must be freed. + if (AppPath == NULL) { + goto Exit; + } + + 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", __func__)); + 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", __func__)); + goto Exit; + } + } else { + AppPath = FullyQualifyPath (mCachePath); // NOTE: This must be freed. + if (AppPath == NULL) { + goto Exit; + } + + DirectorySlashOffset = StrLen (AppPath); + + if (AppPath[DirectorySlashOffset - 1] != L'\\') { + // Set the slash if user did not specify it on the newly allocated pool + AppPath = ReallocatePool ( + (DirectorySlashOffset + 1) * sizeof (CHAR16), + (DirectorySlashOffset + 2) * sizeof (CHAR16), + AppPath + ); + AppPath[DirectorySlashOffset] = L'\\'; + AppPath[DirectorySlashOffset + 1] = L'\0'; + } else { + // Otherwise the user input is good enough to go, mostly + DirectorySlashOffset--; + } + } + + // + // 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. + +Exit: + // + // Free allocated buffers. + // + if (AppPath != NULL) { + FreePool (AppPath); + } + + if (TestName != NULL) { + FreePool (TestName); + } + + return CacheFilePath; +} + +/** + 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 + ) +{ + CHAR16 *FileName; + EFI_STATUS Status; + SHELL_FILE_HANDLE FileHandle; + + // + // NOTE: This devpath is allocated and must be freed. + // + FileName = GetCacheFileName (FrameworkHandle); + if (FileName == NULL) { + return FALSE; + } + + // + // Check to see whether the file exists. If the file can be opened for + // reading, it exists. Otherwise, probably not. + // + Status = ShellOpenFileByName ( + FileName, + &FileHandle, + EFI_FILE_MODE_READ, + 0 + ); + if (!EFI_ERROR (Status)) { + ShellCloseFile (&FileHandle); + } + + if (FileName != NULL) { + FreePool (FileName); + } + + DEBUG ((DEBUG_VERBOSE, "%a - Returning %d\n", __func__, !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. + @param[in] SaveStateSize The size of SaveData in bytes. + + @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 VOID *SaveData, + IN UINTN SaveStateSize + ) +{ + CHAR16 *FileName; + 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. + // + FileName = GetCacheFileName (FrameworkHandle); + if (FileName == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // First lets open the file if it exists so we can delete it...This is the work around for truncation + // + Status = ShellOpenFileByName ( + FileName, + &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", __func__, Status)); + } + } + + // + // Now that we know the path to the file... let's open it for writing. + // + Status = ShellOpenFileByName ( + FileName, + &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", __func__, Status)); + goto Exit; + } + + // + // Write the data to the file. + // + WriteCount = SaveStateSize; + DEBUG ((DEBUG_INFO, "%a - Writing %d bytes to file...\n", __func__, WriteCount)); + Status = ShellWriteFile ( + FileHandle, + &WriteCount, + SaveData + ); + + if (EFI_ERROR (Status) || (WriteCount != SaveStateSize)) { + DEBUG ((DEBUG_ERROR, "%a - Writing to file failed! %r\n", __func__, Status)); + } else { + DEBUG ((DEBUG_INFO, "%a - SUCCESS!\n", __func__)); + } + + // + // No matter what, we should probably close the file. + // + ShellCloseFile (&FileHandle); + +Exit: + if (FileName != NULL) { + FreePool (FileName); + } + + 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[out] SaveData A pointer pointer that will be updated with the address + of the loaded data buffer. + @param[out] SaveStateSize Return the size of SaveData in bytes. + + @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 VOID **SaveData, + OUT UINTN *SaveStateSize + ) +{ + EFI_STATUS Status; + CHAR16 *FileName; + SHELL_FILE_HANDLE FileHandle; + BOOLEAN IsFileOpened; + UINT64 LargeFileSize; + UINTN FileSize; + VOID *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. + // + FileName = GetCacheFileName (FrameworkHandle); + if (FileName == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Now that we know the path to the file... let's open it for writing. + // + Status = ShellOpenFileByName ( + FileName, + &FileHandle, + EFI_FILE_MODE_READ, + 0 + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a - Opening file for writing failed! %r\n", __func__, 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", __func__, 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. + *SaveStateSize = FileSize; + Buffer = AllocatePool (FileSize); + if (Buffer == NULL) { + DEBUG ((DEBUG_ERROR, "%a - Failed to allocate a pool to hold the file contents! %r\n", __func__, 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", __func__, Status)); + } + +Exit: + // + // Free allocated buffers + // + if (FileName != NULL) { + FreePool (FileName); + } + + 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; +} + +/** + Shell based UnitTestPersistenceLib library constructor. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The constructor finished successfully. + @retval Others Error codes returned from gBS->HandleProtocol. + **/ +EFI_STATUS +EFIAPI +UnitTestPersistenceLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + UINTN Index; + UINTN Argc; + CHAR16 **Argv; + EFI_STATUS Status; + EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters; + + Status = gBS->HandleProtocol ( + gImageHandle, + &gEfiShellParametersProtocolGuid, + (VOID **)&ShellParameters + ); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + goto Done; + } + + Argc = ShellParameters->Argc; + Argv = ShellParameters->Argv; + + Status = EFI_SUCCESS; + if ((Argc > 1) && (Argv != NULL)) { + // This might be our cue, check for whether we need to do anything + for (Index = 1; Index < Argc; Index++) { + if (StrCmp (Argv[Index], L"--CachePath") == 0) { + // Need to update the potential cache path to designated path + if (Index < Argc - 1) { + mCachePath = Argv[Index + 1]; + } else { + Print (L" --CachePath <Path of where to save unit test cache files, i.e. FS0:TestFolder>\n"); + } + } + } + } + +Done: + return Status; +} |
