REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2612 Removes the FrameworkHandle parameter from SaveFrameworkState() in the UnitTestLib library instance and updates callers of the function in the library to use the new function signature. Cc: Bret Barkelew <Bret.Barkelew@microsoft.com> Cc: Sean Brogan <sean.brogan@microsoft.com> Cc: Michael D Kinney <michael.d.kinney@intel.com> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> Reviewed-by: Bret Barkelew <bret.barkelew@microsoft.com> Reviewed-by: Michael D Kinney <michael.d.kinney@intel.com>
		
			
				
	
	
		
			857 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			857 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /**
 | |
|   Implement UnitTestLib
 | |
| 
 | |
|   Copyright (c) Microsoft Corporation.
 | |
|   SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| **/
 | |
| 
 | |
| #include <Uefi.h>
 | |
| #include <Library/UnitTestLib.h>
 | |
| #include <Library/BaseLib.h>
 | |
| #include <Library/BaseMemoryLib.h>
 | |
| #include <Library/MemoryAllocationLib.h>
 | |
| #include <Library/DebugLib.h>
 | |
| #include <Library/UnitTestPersistenceLib.h>
 | |
| #include <Library/UnitTestResultReportLib.h>
 | |
| 
 | |
| ///
 | |
| /// Forward declaration of prototype
 | |
| ///
 | |
| STATIC
 | |
| VOID
 | |
| UpdateTestFromSave (
 | |
|   IN OUT UNIT_TEST              *Test,
 | |
|   IN     UNIT_TEST_SAVE_HEADER  *SavedState
 | |
|   );
 | |
| 
 | |
| /**
 | |
|   This function will determine whether the short name violates any rules that would
 | |
|   prevent it from being used as a reporting name or as a serialization name.
 | |
| 
 | |
|   Example: If the name cannot be serialized to a filesystem file name.
 | |
| 
 | |
|   @param[in]  ShortTitleString  A pointer to the short title string to be evaluated.
 | |
| 
 | |
|   @retval  TRUE   The string is acceptable.
 | |
|   @retval  FALSE  The string should not be used.
 | |
| 
 | |
| **/
 | |
| STATIC
 | |
| BOOLEAN
 | |
| IsFrameworkShortNameValid (
 | |
|   IN CHAR8  *ShortTitleString
 | |
|   )
 | |
| {
 | |
|   // TODO: Finish this function.
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| STATIC
 | |
| CHAR8*
 | |
| AllocateAndCopyString (
 | |
|   IN CHAR8  *StringToCopy
 | |
|   )
 | |
| {
 | |
|   CHAR8  *NewString;
 | |
|   UINTN  NewStringLength;
 | |
| 
 | |
|   NewString = NULL;
 | |
|   NewStringLength = AsciiStrnLenS (StringToCopy, UNIT_TEST_MAX_STRING_LENGTH) + 1;
 | |
|   NewString = AllocatePool (NewStringLength * sizeof( CHAR8 ));
 | |
|   if (NewString != NULL) {
 | |
|     AsciiStrCpyS (NewString, NewStringLength, StringToCopy);
 | |
|   }
 | |
|   return NewString;
 | |
| }
 | |
| 
 | |
| STATIC
 | |
| VOID
 | |
| SetFrameworkFingerprint (
 | |
|   OUT UINT8                *Fingerprint,
 | |
|   IN  UNIT_TEST_FRAMEWORK  *Framework
 | |
|   )
 | |
| {
 | |
|   UINT32  NewFingerprint;
 | |
| 
 | |
|   // For this one we'll just use the title and version as the unique fingerprint.
 | |
|   NewFingerprint = CalculateCrc32( Framework->Title, (AsciiStrLen( Framework->Title ) * sizeof( CHAR8 )) );
 | |
|   NewFingerprint = (NewFingerprint >> 8) ^ CalculateCrc32( Framework->VersionString, (AsciiStrLen( Framework->VersionString ) * sizeof( CHAR8 )) );
 | |
| 
 | |
|   CopyMem( Fingerprint, &NewFingerprint, UNIT_TEST_FINGERPRINT_SIZE );
 | |
|   return;
 | |
| }
 | |
| 
 | |
| STATIC
 | |
| VOID
 | |
| SetSuiteFingerprint (
 | |
|   OUT UINT8                *Fingerprint,
 | |
|   IN  UNIT_TEST_FRAMEWORK  *Framework,
 | |
|   IN  UNIT_TEST_SUITE      *Suite
 | |
|   )
 | |
| {
 | |
|   UINT32  NewFingerprint;
 | |
| 
 | |
|   // For this one, we'll use the fingerprint from the framework, and the title of the suite.
 | |
|   NewFingerprint = CalculateCrc32( &Framework->Fingerprint[0], UNIT_TEST_FINGERPRINT_SIZE );
 | |
|   NewFingerprint = (NewFingerprint >> 8) ^ CalculateCrc32( Suite->Title, (AsciiStrLen( Suite->Title ) * sizeof( CHAR8 )) );
 | |
|   NewFingerprint = (NewFingerprint >> 8) ^ CalculateCrc32( Suite->Name, (AsciiStrLen(Suite->Name) * sizeof(CHAR8)) );
 | |
| 
 | |
|   CopyMem( Fingerprint, &NewFingerprint, UNIT_TEST_FINGERPRINT_SIZE );
 | |
|   return;
 | |
| }
 | |
| 
 | |
| STATIC
 | |
| VOID
 | |
| SetTestFingerprint (
 | |
|   OUT UINT8            *Fingerprint,
 | |
|   IN  UNIT_TEST_SUITE  *Suite,
 | |
|   IN  UNIT_TEST        *Test
 | |
|   )
 | |
| {
 | |
|   UINT32  NewFingerprint;
 | |
| 
 | |
|   // For this one, we'll use the fingerprint from the suite, and the description and classname of the test.
 | |
|   NewFingerprint = CalculateCrc32( &Suite->Fingerprint[0], UNIT_TEST_FINGERPRINT_SIZE );
 | |
|   NewFingerprint = (NewFingerprint >> 8) ^ CalculateCrc32( Test->Description, (AsciiStrLen( Test->Description ) * sizeof( CHAR8 )) );
 | |
|   NewFingerprint = (NewFingerprint >> 8) ^ CalculateCrc32( Test->Name, (AsciiStrLen(Test->Name) * sizeof(CHAR8)) );
 | |
| 
 | |
|   CopyMem( Fingerprint, &NewFingerprint, UNIT_TEST_FINGERPRINT_SIZE );
 | |
|   return;
 | |
| }
 | |
| 
 | |
| STATIC
 | |
| BOOLEAN
 | |
| CompareFingerprints (
 | |
|   IN UINT8  *FingerprintA,
 | |
|   IN UINT8  *FingerprintB
 | |
|   )
 | |
| {
 | |
|   return (CompareMem( FingerprintA, FingerprintB, UNIT_TEST_FINGERPRINT_SIZE ) == 0);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Cleanup a test framework.
 | |
| 
 | |
|   After tests are run, this will teardown the entire framework and free all
 | |
|   allocated data within.
 | |
| 
 | |
|   @param[in]  FrameworkHandle  A handle to the current running framework that
 | |
|                                dispatched the test.  Necessary for recording
 | |
|                                certain test events with the framework.
 | |
| 
 | |
|   @retval  EFI_SUCCESS            All resources associated with framework were
 | |
|                                   freed.
 | |
|   @retval  EFI_INVALID_PARAMETER  FrameworkHandle is NULL.
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| FreeUnitTestFramework (
 | |
|   IN UNIT_TEST_FRAMEWORK_HANDLE  FrameworkHandle
 | |
|   )
 | |
| {
 | |
|   // TODO: Finish this function.
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| STATIC
 | |
| EFI_STATUS
 | |
| FreeUnitTestSuiteEntry (
 | |
|   IN UNIT_TEST_SUITE_LIST_ENTRY  *SuiteEntry
 | |
|   )
 | |
| {
 | |
|   // TODO: Finish this function.
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| STATIC
 | |
| EFI_STATUS
 | |
| FreeUnitTestTestEntry (
 | |
|   IN UNIT_TEST_LIST_ENTRY  *TestEntry
 | |
|   )
 | |
| {
 | |
|   // TODO: Finish this function.
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Method to Initialize the Unit Test framework.  This function registers the
 | |
|   test name and also initializes the internal state of the test framework to
 | |
|   receive any new suites and tests.
 | |
| 
 | |
|   @param[out]  FrameworkHandle  Unit test framework to be created.
 | |
|   @param[in]   Title            Null-terminated ASCII string that is the user
 | |
|                                 friendly name of the framework. String is
 | |
|                                 copied.
 | |
|   @param[in]   ShortTitle       Null-terminated ASCII short string that is the
 | |
|                                 short name of the framework with no spaces.
 | |
|                                 String is copied.
 | |
|   @param[in]   VersionString    Null-terminated ASCII version string for the
 | |
|                                 framework. String is copied.
 | |
| 
 | |
|   @retval  EFI_SUCCESS            The unit test framework was initialized.
 | |
|   @retval  EFI_INVALID_PARAMETER  FrameworkHandle is NULL.
 | |
|   @retval  EFI_INVALID_PARAMETER  Title is NULL.
 | |
|   @retval  EFI_INVALID_PARAMETER  ShortTitle is NULL.
 | |
|   @retval  EFI_INVALID_PARAMETER  VersionString is NULL.
 | |
|   @retval  EFI_INVALID_PARAMETER  ShortTitle is invalid.
 | |
|   @retval  EFI_OUT_OF_RESOURCES   There are not enough resources available to
 | |
|                                   initialize the unit test framework.
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| InitUnitTestFramework (
 | |
|   OUT UNIT_TEST_FRAMEWORK_HANDLE  *FrameworkHandle,
 | |
|   IN  CHAR8                       *Title,
 | |
|   IN  CHAR8                       *ShortTitle,
 | |
|   IN  CHAR8                       *VersionString
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                  Status;
 | |
|   UNIT_TEST_FRAMEWORK_HANDLE  NewFrameworkHandle;
 | |
|   UNIT_TEST_FRAMEWORK         *NewFramework;
 | |
| 
 | |
|   Status       = EFI_SUCCESS;
 | |
|   NewFramework = NULL;
 | |
| 
 | |
|   //
 | |
|   // First, check all pointers and make sure nothing's broked.
 | |
|   //
 | |
|   if (FrameworkHandle == NULL || Title == NULL ||
 | |
|       ShortTitle == NULL || VersionString == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Next, determine whether all of the strings are good to use.
 | |
|   //
 | |
|   if (!IsFrameworkShortNameValid (ShortTitle)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Next, set aside some space to start messing with the framework.
 | |
|   //
 | |
|   NewFramework = AllocateZeroPool (sizeof (UNIT_TEST_FRAMEWORK));
 | |
|   if (NewFramework == NULL) {
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Next, set up all the test data.
 | |
|   //
 | |
|   NewFrameworkHandle          = (UNIT_TEST_FRAMEWORK_HANDLE)NewFramework;
 | |
|   NewFramework->Title         = AllocateAndCopyString (Title);
 | |
|   NewFramework->ShortTitle    = AllocateAndCopyString (ShortTitle);
 | |
|   NewFramework->VersionString = AllocateAndCopyString (VersionString);
 | |
|   NewFramework->Log           = NULL;
 | |
|   NewFramework->CurrentTest   = NULL;
 | |
|   NewFramework->SavedState    = NULL;
 | |
|   if (NewFramework->Title == NULL ||
 | |
|       NewFramework->ShortTitle == NULL ||
 | |
|       NewFramework->VersionString == NULL) {
 | |
|     Status = EFI_OUT_OF_RESOURCES;
 | |
|     goto Exit;
 | |
|   }
 | |
|   InitializeListHead (&(NewFramework->TestSuiteList));
 | |
| 
 | |
|   //
 | |
|   // Create the framework fingerprint.
 | |
|   //
 | |
|   SetFrameworkFingerprint (&NewFramework->Fingerprint[0], NewFramework);
 | |
| 
 | |
|   //
 | |
|   // If there is a persisted context, load it now.
 | |
|   //
 | |
|   if (DoesCacheExist (NewFrameworkHandle)) {
 | |
|     Status = LoadUnitTestCache (NewFrameworkHandle,  (UNIT_TEST_SAVE_HEADER**)(&NewFramework->SavedState));
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       //
 | |
|       // Don't actually report it as an error, but emit a warning.
 | |
|       //
 | |
|       DEBUG (( DEBUG_ERROR, "%a - Cache was detected, but failed to load.\n", __FUNCTION__ ));
 | |
|       Status = EFI_SUCCESS;
 | |
|     }
 | |
|   }
 | |
| 
 | |
| Exit:
 | |
|   //
 | |
|   // If we're good, then let's copy the framework.
 | |
|   //
 | |
|   if (!EFI_ERROR (Status)) {
 | |
|     *FrameworkHandle = NewFrameworkHandle;
 | |
|   } else {
 | |
|     //
 | |
|     // Otherwise, we need to undo this horrible thing that we've done.
 | |
|     //
 | |
|     FreeUnitTestFramework (NewFrameworkHandle);
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Registers a Unit Test Suite in the Unit Test Framework.
 | |
|   At least one test suite must be registered, because all test cases must be
 | |
|   within a unit test suite.
 | |
| 
 | |
|   @param[out]  SuiteHandle      Unit test suite to create
 | |
|   @param[in]   FrameworkHandle  Unit test framework to add unit test suite to
 | |
|   @param[in]   Title            Null-terminated ASCII string that is the user
 | |
|                                 friendly name of the test suite.  String is
 | |
|                                 copied.
 | |
|   @param[in]   Name             Null-terminated ASCII string that is the short
 | |
|                                 name of the test suite with no spaces.  String
 | |
|                                 is copied.
 | |
|   @param[in]   Setup            Setup function, runs before suite.  This is an
 | |
|                                 optional parameter that may be NULL.
 | |
|   @param[in]   Teardown         Teardown function, runs after suite.  This is an
 | |
|                                 optional parameter that may be NULL.
 | |
| 
 | |
|   @retval  EFI_SUCCESS            The unit test suite was created.
 | |
|   @retval  EFI_INVALID_PARAMETER  SuiteHandle is NULL.
 | |
|   @retval  EFI_INVALID_PARAMETER  FrameworkHandle is NULL.
 | |
|   @retval  EFI_INVALID_PARAMETER  Title is NULL.
 | |
|   @retval  EFI_INVALID_PARAMETER  Name is NULL.
 | |
|   @retval  EFI_OUT_OF_RESOURCES   There are not enough resources available to
 | |
|                                   initialize the unit test suite.
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| CreateUnitTestSuite (
 | |
|   OUT UNIT_TEST_SUITE_HANDLE      *SuiteHandle,
 | |
|   IN  UNIT_TEST_FRAMEWORK_HANDLE  FrameworkHandle,
 | |
|   IN  CHAR8                       *Title,
 | |
|   IN  CHAR8                       *Name,
 | |
|   IN  UNIT_TEST_SUITE_SETUP       Setup     OPTIONAL,
 | |
|   IN  UNIT_TEST_SUITE_TEARDOWN    Teardown  OPTIONAL
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                  Status;
 | |
|   UNIT_TEST_SUITE_LIST_ENTRY  *NewSuiteEntry;
 | |
|   UNIT_TEST_FRAMEWORK         *Framework;
 | |
| 
 | |
|   Status = EFI_SUCCESS;
 | |
|   Framework = (UNIT_TEST_FRAMEWORK *)FrameworkHandle;
 | |
| 
 | |
|   //
 | |
|   // First, let's check to make sure that our parameters look good.
 | |
|   //
 | |
|   if ((SuiteHandle == NULL) || (Framework == NULL) || (Title == NULL) || (Name == NULL)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Create the new entry.
 | |
|   //
 | |
|   NewSuiteEntry = AllocateZeroPool (sizeof (UNIT_TEST_SUITE_LIST_ENTRY));
 | |
|   if (NewSuiteEntry == NULL) {
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Copy the fields we think we need.
 | |
|   //
 | |
|   NewSuiteEntry->UTS.NumTests         = 0;
 | |
|   NewSuiteEntry->UTS.Title            = AllocateAndCopyString (Title);
 | |
|   NewSuiteEntry->UTS.Name             = AllocateAndCopyString (Name);
 | |
|   NewSuiteEntry->UTS.Setup            = Setup;
 | |
|   NewSuiteEntry->UTS.Teardown         = Teardown;
 | |
|   NewSuiteEntry->UTS.ParentFramework  = FrameworkHandle;
 | |
|   InitializeListHead (&(NewSuiteEntry->Entry));             // List entry for sibling suites.
 | |
|   InitializeListHead (&(NewSuiteEntry->UTS.TestCaseList));  // List entry for child tests.
 | |
|   if (NewSuiteEntry->UTS.Title == NULL) {
 | |
|     Status = EFI_OUT_OF_RESOURCES;
 | |
|     goto Exit;
 | |
|   }
 | |
| 
 | |
|   if (NewSuiteEntry->UTS.Name == NULL) {
 | |
|     Status = EFI_OUT_OF_RESOURCES;
 | |
|     goto Exit;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Create the suite fingerprint.
 | |
|   //
 | |
|   SetSuiteFingerprint( &NewSuiteEntry->UTS.Fingerprint[0], Framework, &NewSuiteEntry->UTS );
 | |
| 
 | |
| Exit:
 | |
|   //
 | |
|   // If everything is going well, add the new suite to the tail list for the framework.
 | |
|   //
 | |
|   if (!EFI_ERROR( Status )) {
 | |
|     InsertTailList (&(Framework->TestSuiteList), (LIST_ENTRY *)NewSuiteEntry);
 | |
|     *SuiteHandle = (UNIT_TEST_SUITE_HANDLE)(&NewSuiteEntry->UTS);
 | |
|   } else {
 | |
|     //
 | |
|     // Otherwise, make with the destruction.
 | |
|     //
 | |
|     FreeUnitTestSuiteEntry (NewSuiteEntry);
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Adds test case to Suite
 | |
| 
 | |
|   @param[in]  SuiteHandle   Unit test suite to add test to.
 | |
|   @param[in]  Description   Null-terminated ASCII string that is the user
 | |
|                             friendly description of a test.  String is copied.
 | |
|   @param[in]  Name          Null-terminated ASCII string that is the short name
 | |
|                             of the test with no spaces.  String is copied.
 | |
|   @param[in]  Function      Unit test function.
 | |
|   @param[in]  Prerequisite  Prerequisite function, runs before test.  This is
 | |
|                             an optional parameter that may be NULL.
 | |
|   @param[in]  CleanUp       Clean up function, runs after test.  This is an
 | |
|                             optional parameter that may be NULL.
 | |
|   @param[in]  Context       Pointer to context.    This is an optional parameter
 | |
|                             that may be NULL.
 | |
| 
 | |
|   @retval  EFI_SUCCESS            The unit test case was added to Suite.
 | |
|   @retval  EFI_INVALID_PARAMETER  SuiteHandle is NULL.
 | |
|   @retval  EFI_INVALID_PARAMETER  Description is NULL.
 | |
|   @retval  EFI_INVALID_PARAMETER  Name is NULL.
 | |
|   @retval  EFI_INVALID_PARAMETER  Function is NULL.
 | |
|   @retval  EFI_OUT_OF_RESOURCES   There are not enough resources available to
 | |
|                                   add the unit test case to Suite.
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| AddTestCase (
 | |
|   IN UNIT_TEST_SUITE_HANDLE  SuiteHandle,
 | |
|   IN CHAR8                   *Description,
 | |
|   IN CHAR8                   *Name,
 | |
|   IN UNIT_TEST_FUNCTION      Function,
 | |
|   IN UNIT_TEST_PREREQUISITE  Prerequisite  OPTIONAL,
 | |
|   IN UNIT_TEST_CLEANUP       CleanUp       OPTIONAL,
 | |
|   IN UNIT_TEST_CONTEXT       Context       OPTIONAL
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS            Status;
 | |
|   UNIT_TEST_LIST_ENTRY  *NewTestEntry;
 | |
|   UNIT_TEST_FRAMEWORK   *ParentFramework;
 | |
|   UNIT_TEST_SUITE       *Suite;
 | |
| 
 | |
|   Status          = EFI_SUCCESS;
 | |
|   Suite           = (UNIT_TEST_SUITE *)SuiteHandle;
 | |
| 
 | |
|   //
 | |
|   // First, let's check to make sure that our parameters look good.
 | |
|   //
 | |
|   if ((Suite == NULL) || (Description == NULL) || (Name == NULL) || (Function == NULL)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   ParentFramework = (UNIT_TEST_FRAMEWORK *)Suite->ParentFramework;
 | |
|   //
 | |
|   // Create the new entry.
 | |
|   NewTestEntry = AllocateZeroPool (sizeof( UNIT_TEST_LIST_ENTRY ));
 | |
|   if (NewTestEntry == NULL) {
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Copy the fields we think we need.
 | |
|   NewTestEntry->UT.Description       = AllocateAndCopyString (Description);
 | |
|   NewTestEntry->UT.Name              = AllocateAndCopyString (Name);
 | |
|   NewTestEntry->UT.FailureType       = FAILURETYPE_NOFAILURE;
 | |
|   NewTestEntry->UT.FailureMessage[0] = '\0';
 | |
|   NewTestEntry->UT.Log               = NULL;
 | |
|   NewTestEntry->UT.Prerequisite      = Prerequisite;
 | |
|   NewTestEntry->UT.CleanUp           = CleanUp;
 | |
|   NewTestEntry->UT.RunTest           = Function;
 | |
|   NewTestEntry->UT.Context           = Context;
 | |
|   NewTestEntry->UT.Result            = UNIT_TEST_PENDING;
 | |
|   NewTestEntry->UT.ParentSuite       = SuiteHandle;
 | |
|   InitializeListHead (&(NewTestEntry->Entry));  // List entry for sibling tests.
 | |
|   if (NewTestEntry->UT.Description == NULL) {
 | |
|     Status = EFI_OUT_OF_RESOURCES;
 | |
|     goto Exit;
 | |
|   }
 | |
|   if (NewTestEntry->UT.Name == NULL) {
 | |
|     Status = EFI_OUT_OF_RESOURCES;
 | |
|     goto Exit;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Create the test fingerprint.
 | |
|   //
 | |
|   SetTestFingerprint (&NewTestEntry->UT.Fingerprint[0], Suite, &NewTestEntry->UT);
 | |
| 
 | |
|   // TODO: Make sure that duplicate fingerprints cannot be created.
 | |
| 
 | |
|   //
 | |
|   // If there is saved test data, update this record.
 | |
|   //
 | |
|   if (ParentFramework->SavedState != NULL) {
 | |
|     UpdateTestFromSave (&NewTestEntry->UT, ParentFramework->SavedState);
 | |
|   }
 | |
| 
 | |
| Exit:
 | |
|   //
 | |
|   // If everything is going well, add the new suite to the tail list for the framework.
 | |
|   //
 | |
|   if (!EFI_ERROR (Status)) {
 | |
|     InsertTailList (&(Suite->TestCaseList), (LIST_ENTRY*)NewTestEntry);
 | |
|     Suite->NumTests++;
 | |
|   } else {
 | |
|     //
 | |
|     // Otherwise, make with the destruction.
 | |
|     //
 | |
|     FreeUnitTestTestEntry (NewTestEntry);
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| STATIC
 | |
| VOID
 | |
| UpdateTestFromSave (
 | |
|   IN OUT UNIT_TEST              *Test,
 | |
|   IN     UNIT_TEST_SAVE_HEADER  *SavedState
 | |
|   )
 | |
| {
 | |
|   UNIT_TEST_SAVE_TEST     *CurrentTest;
 | |
|   UNIT_TEST_SAVE_TEST     *MatchingTest;
 | |
|   UINT8                   *FloatingPointer;
 | |
|   UNIT_TEST_SAVE_CONTEXT  *SavedContext;
 | |
|   UINTN                   Index;
 | |
| 
 | |
|   //
 | |
|   // First, evaluate the inputs.
 | |
|   //
 | |
|   if (Test == NULL || SavedState == NULL) {
 | |
|     return;
 | |
|   }
 | |
|   if (SavedState->TestCount == 0) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Next, determine whether a matching test can be found.
 | |
|   // Start at the beginning.
 | |
|   //
 | |
|   MatchingTest    = NULL;
 | |
|   FloatingPointer = (UINT8 *)SavedState + sizeof (*SavedState);
 | |
|   for (Index = 0; Index < SavedState->TestCount; Index++) {
 | |
|     CurrentTest = (UNIT_TEST_SAVE_TEST *)FloatingPointer;
 | |
|     if (CompareFingerprints (&Test->Fingerprint[0], &CurrentTest->Fingerprint[0])) {
 | |
|       MatchingTest = CurrentTest;
 | |
|       //
 | |
|       // If there's a saved context, it's important that we iterate through the entire list.
 | |
|       //
 | |
|       if (!SavedState->HasSavedContext) {
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // If we didn't find it, we have to increment to the next test.
 | |
|     //
 | |
|     FloatingPointer = (UINT8 *)CurrentTest + CurrentTest->Size;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // If a matching test was found, copy the status.
 | |
|   //
 | |
|   if (MatchingTest) {
 | |
|     //
 | |
|     // Override the test status with the saved status.
 | |
|     //
 | |
|     Test->Result = MatchingTest->Result;
 | |
| 
 | |
|     Test->FailureType = MatchingTest->FailureType;
 | |
|     AsciiStrnCpyS (
 | |
|       &Test->FailureMessage[0],
 | |
|       UNIT_TEST_TESTFAILUREMSG_LENGTH,
 | |
|       &MatchingTest->FailureMessage[0],
 | |
|       UNIT_TEST_TESTFAILUREMSG_LENGTH
 | |
|       );
 | |
| 
 | |
|     //
 | |
|     // If there is a log string associated, grab that.
 | |
|     // We can tell that there's a log string because the "size" will be larger than
 | |
|     // the structure size.
 | |
|     // IMPORTANT NOTE: There are security implications here.
 | |
|     //                 This data is user-supplied and we're about to play kinda
 | |
|     //                 fast and loose with data buffers.
 | |
|     //
 | |
|     if (MatchingTest->Size > sizeof (UNIT_TEST_SAVE_TEST)) {
 | |
|       UnitTestLogInit (Test, (UINT8 *)MatchingTest->Log, MatchingTest->Size - sizeof (UNIT_TEST_SAVE_TEST));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // If the saved context exists and matches this test, grab it, too.
 | |
|   //
 | |
|   if (SavedState->HasSavedContext) {
 | |
|     //
 | |
|     // If there was a saved context, the "matching test" loop will have placed the FloatingPointer
 | |
|     // at the beginning of the context structure.
 | |
|     //
 | |
|     SavedContext = (UNIT_TEST_SAVE_CONTEXT *)FloatingPointer;
 | |
|     if ((SavedContext->Size - sizeof (UNIT_TEST_SAVE_CONTEXT)) > 0 &&
 | |
|         CompareFingerprints (&Test->Fingerprint[0], &SavedContext->Fingerprint[0])) {
 | |
|       //
 | |
|       // Override the test context with the saved context.
 | |
|       //
 | |
|       Test->Context = (VOID*)SavedContext->Data;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| STATIC
 | |
| UNIT_TEST_SAVE_HEADER*
 | |
| SerializeState (
 | |
|   IN UNIT_TEST_FRAMEWORK_HANDLE  FrameworkHandle,
 | |
|   IN UNIT_TEST_CONTEXT           ContextToSave,      OPTIONAL
 | |
|   IN UINTN                       ContextToSaveSize
 | |
|   )
 | |
| {
 | |
|   UNIT_TEST_FRAMEWORK     *Framework;
 | |
|   UNIT_TEST_SAVE_HEADER   *Header;
 | |
|   LIST_ENTRY              *SuiteListHead;
 | |
|   LIST_ENTRY              *Suite;
 | |
|   LIST_ENTRY              *TestListHead;
 | |
|   LIST_ENTRY              *Test;
 | |
|   UINT32                  TestCount;
 | |
|   UINT32                  TotalSize;
 | |
|   UINTN                   LogSize;
 | |
|   UNIT_TEST_SAVE_TEST     *TestSaveData;
 | |
|   UNIT_TEST_SAVE_CONTEXT  *TestSaveContext;
 | |
|   UNIT_TEST               *UnitTest;
 | |
|   UINT8                   *FloatingPointer;
 | |
| 
 | |
|   Framework = (UNIT_TEST_FRAMEWORK *)FrameworkHandle;
 | |
|   Header    = NULL;
 | |
| 
 | |
|   //
 | |
|   // First, let's not make assumptions about the parameters.
 | |
|   //
 | |
|   if (Framework == NULL ||
 | |
|       (ContextToSave != NULL && ContextToSaveSize == 0) ||
 | |
|       ContextToSaveSize > MAX_UINT32) {
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Next, we've gotta figure out the resources that will be required to serialize the
 | |
|   // the framework state so that we can persist it.
 | |
|   // To start with, we're gonna need a header.
 | |
|   //
 | |
|   TotalSize = sizeof (UNIT_TEST_SAVE_HEADER);
 | |
|   //
 | |
|   // Now we need to figure out how many tests there are.
 | |
|   //
 | |
|   TestCount = 0;
 | |
|   //
 | |
|   // Iterate all suites.
 | |
|   //
 | |
|   SuiteListHead = &Framework->TestSuiteList;
 | |
|   for (Suite = GetFirstNode (SuiteListHead); Suite != SuiteListHead; Suite = GetNextNode (SuiteListHead, Suite)) {
 | |
|     //
 | |
|     // Iterate all tests within the suite.
 | |
|     //
 | |
|     TestListHead = &((UNIT_TEST_SUITE_LIST_ENTRY *)Suite)->UTS.TestCaseList;
 | |
|     for (Test = GetFirstNode (TestListHead); Test != TestListHead; Test = GetNextNode (TestListHead, Test)) {
 | |
|       UnitTest = &((UNIT_TEST_LIST_ENTRY *)Test)->UT;
 | |
|       //
 | |
|       // Account for the size of a test structure.
 | |
|       //
 | |
|       TotalSize += sizeof( UNIT_TEST_SAVE_TEST );
 | |
|       //
 | |
|       // If there's a log, make sure to account for the log size.
 | |
|       //
 | |
|       if (UnitTest->Log != NULL)     {
 | |
|         //
 | |
|         // The +1 is for the NULL character. Can't forget the NULL character.
 | |
|         //
 | |
|         LogSize = (AsciiStrLen (UnitTest->Log) + 1) * sizeof (CHAR8);
 | |
|         ASSERT (LogSize < MAX_UINT32);
 | |
|         TotalSize += (UINT32)LogSize;
 | |
|       }
 | |
|       //
 | |
|       // Increment the test count.
 | |
|       //
 | |
|       TestCount++;
 | |
|     }
 | |
|   }
 | |
|   //
 | |
|   // If there are no tests, we're done here.
 | |
|   //
 | |
|   if (TestCount == 0) {
 | |
|     return NULL;
 | |
|   }
 | |
|   //
 | |
|   // Add room for the context, if there is one.
 | |
|   //
 | |
|   if (ContextToSave != NULL) {
 | |
|     TotalSize += sizeof (UNIT_TEST_SAVE_CONTEXT) + (UINT32)ContextToSaveSize;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Now that we know the size, we need to allocate space for the serialized output.
 | |
|   //
 | |
|   Header = AllocateZeroPool (TotalSize);
 | |
|   if (Header == NULL) {
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Alright, let's start setting up some data.
 | |
|   //
 | |
|   Header->Version         = UNIT_TEST_PERSISTENCE_LIB_VERSION;
 | |
|   Header->SaveStateSize   = TotalSize;
 | |
|   CopyMem (&Header->Fingerprint[0], &Framework->Fingerprint[0], UNIT_TEST_FINGERPRINT_SIZE);
 | |
|   CopyMem (&Header->StartTime, &Framework->StartTime, sizeof (EFI_TIME));
 | |
|   Header->TestCount       = TestCount;
 | |
|   Header->HasSavedContext = FALSE;
 | |
| 
 | |
|   //
 | |
|   // Start adding all of the test cases.
 | |
|   // Set the floating pointer to the start of the current test save buffer.
 | |
|   //
 | |
|   FloatingPointer = (UINT8*)Header + sizeof( UNIT_TEST_SAVE_HEADER );
 | |
|   //
 | |
|   // Iterate all suites.
 | |
|   //
 | |
|   SuiteListHead = &Framework->TestSuiteList;
 | |
|   for (Suite = GetFirstNode (SuiteListHead); Suite != SuiteListHead; Suite = GetNextNode (SuiteListHead, Suite)) {
 | |
|     //
 | |
|     // Iterate all tests within the suite.
 | |
|     //
 | |
|     TestListHead = &((UNIT_TEST_SUITE_LIST_ENTRY *)Suite)->UTS.TestCaseList;
 | |
|     for (Test = GetFirstNode (TestListHead); Test != TestListHead; Test = GetNextNode (TestListHead, Test)) {
 | |
|       TestSaveData  = (UNIT_TEST_SAVE_TEST *)FloatingPointer;
 | |
|       UnitTest      = &((UNIT_TEST_LIST_ENTRY *)Test)->UT;
 | |
| 
 | |
|       //
 | |
|       // Save the fingerprint.
 | |
|       //
 | |
|       CopyMem (&TestSaveData->Fingerprint[0], &UnitTest->Fingerprint[0], UNIT_TEST_FINGERPRINT_SIZE);
 | |
| 
 | |
|       //
 | |
|       // Save the result.
 | |
|       //
 | |
|       TestSaveData->Result = UnitTest->Result;
 | |
|       TestSaveData->FailureType = UnitTest->FailureType;
 | |
|       AsciiStrnCpyS (&TestSaveData->FailureMessage[0], UNIT_TEST_TESTFAILUREMSG_LENGTH, &UnitTest->FailureMessage[0], UNIT_TEST_TESTFAILUREMSG_LENGTH);
 | |
| 
 | |
| 
 | |
|       //
 | |
|       // If there is a log, save the log.
 | |
|       //
 | |
|       FloatingPointer += sizeof (UNIT_TEST_SAVE_TEST);
 | |
|       if (UnitTest->Log != NULL) {
 | |
|         //
 | |
|         // The +1 is for the NULL character. Can't forget the NULL character.
 | |
|         //
 | |
|         LogSize = (AsciiStrLen (UnitTest->Log) + 1) * sizeof (CHAR8);
 | |
|         CopyMem (FloatingPointer, UnitTest->Log, LogSize);
 | |
|         FloatingPointer += LogSize;
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // Update the size once the structure is complete.
 | |
|       // NOTE: Should this be a straight cast without validation?
 | |
|       //
 | |
|       TestSaveData->Size = (UINT32)(FloatingPointer - (UINT8 *)TestSaveData);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // If there is a context to save, let's do that now.
 | |
|   //
 | |
|   if (ContextToSave != NULL && Framework->CurrentTest != NULL) {
 | |
|     TestSaveContext         = (UNIT_TEST_SAVE_CONTEXT*)FloatingPointer;
 | |
|     TestSaveContext->Size   = (UINT32)ContextToSaveSize + sizeof (UNIT_TEST_SAVE_CONTEXT);
 | |
|     CopyMem (&TestSaveContext->Fingerprint[0], &Framework->CurrentTest->Fingerprint[0], UNIT_TEST_FINGERPRINT_SIZE);
 | |
|     CopyMem (((UINT8 *)TestSaveContext + sizeof (UNIT_TEST_SAVE_CONTEXT)), ContextToSave, ContextToSaveSize);
 | |
|     Header->HasSavedContext = TRUE;
 | |
|   }
 | |
| 
 | |
|   return Header;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Leverages a framework-specific mechanism (see UnitTestPersistenceLib if you're
 | |
|   a framework author) to save the state of the executing framework along with
 | |
|   any allocated data so that the test may be resumed upon reentry. A test case
 | |
|   should pass any needed context (which, to prevent an infinite loop, should be
 | |
|   at least the current execution count) which will be saved by the framework and
 | |
|   passed to the test case upon resume.
 | |
| 
 | |
|   This should be called while the current test framework is valid and active. It is
 | |
|   generally called from within a test case prior to quitting or rebooting.
 | |
| 
 | |
|   @param[in]  ContextToSave      A buffer of test case-specific data to be saved
 | |
|                                  along with framework state.  Will be passed as
 | |
|                                  "Context" to the test case upon resume.  This
 | |
|                                  is an optional parameter that may be NULL.
 | |
|   @param[in]  ContextToSaveSize  Size of the ContextToSave buffer.
 | |
| 
 | |
|   @retval  EFI_SUCCESS            The framework state and context were saved.
 | |
|   @retval  EFI_NOT_FOUND          An active framework handle was not found.
 | |
|   @retval  EFI_INVALID_PARAMETER  ContextToSave is not NULL and
 | |
|                                   ContextToSaveSize is 0.
 | |
|   @retval  EFI_INVALID_PARAMETER  ContextToSave is >= 4GB.
 | |
|   @retval  EFI_OUT_OF_RESOURCES   There are not enough resources available to
 | |
|                                   save the framework and context state.
 | |
|   @retval  EFI_DEVICE_ERROR       The framework and context state could not be
 | |
|                                   saved to a persistent storage device due to a
 | |
|                                   device error.
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| SaveFrameworkState (
 | |
|   IN UNIT_TEST_CONTEXT           ContextToSave     OPTIONAL,
 | |
|   IN UINTN                       ContextToSaveSize
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                  Status;
 | |
|   UNIT_TEST_FRAMEWORK_HANDLE  FrameworkHandle;
 | |
|   UNIT_TEST_SAVE_HEADER       *Header;
 | |
| 
 | |
|   Header = NULL;
 | |
|   FrameworkHandle = GetActiveFrameworkHandle ();
 | |
| 
 | |
|   //
 | |
|   // Return a unique error code if the framework is not set.
 | |
|   //
 | |
|   if (FrameworkHandle == NULL) {
 | |
|     return EFI_NOT_FOUND;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // First, let's not make assumptions about the parameters.
 | |
|   //
 | |
|   if ((ContextToSave != NULL && ContextToSaveSize == 0) ||
 | |
|       ContextToSaveSize > MAX_UINT32) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Now, let's package up all the data for saving.
 | |
|   //
 | |
|   Header = SerializeState (FrameworkHandle, ContextToSave, ContextToSaveSize);
 | |
|   if (Header == NULL) {
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // All that should be left to do is save it using the associated persistence lib.
 | |
|   //
 | |
|   Status = SaveUnitTestCache (FrameworkHandle, Header);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_ERROR, "%a - Could not save state! %r\n", __FUNCTION__, Status));
 | |
|     Status = EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Free data that was used.
 | |
|   //
 | |
|   FreePool (Header);
 | |
| 
 | |
|   return Status;
 | |
| }
 |