1 /**
2   Implement UnitTestLib
3 
4   Copyright (c) Microsoft Corporation.
5   SPDX-License-Identifier: BSD-2-Clause-Patent
6 **/
7 
8 #include <Uefi.h>
9 #include <Library/UnitTestLib.h>
10 #include <Library/BaseLib.h>
11 #include <Library/BaseMemoryLib.h>
12 #include <Library/MemoryAllocationLib.h>
13 #include <Library/DebugLib.h>
14 #include <Library/UnitTestPersistenceLib.h>
15 #include <Library/UnitTestResultReportLib.h>
16 
17 ///
18 /// Forward declaration of prototype
19 ///
20 STATIC
21 VOID
22 UpdateTestFromSave (
23   IN OUT UNIT_TEST              *Test,
24   IN     UNIT_TEST_SAVE_HEADER  *SavedState
25   );
26 
27 /**
28   This function will determine whether the short name violates any rules that would
29   prevent it from being used as a reporting name or as a serialization name.
30 
31   Example: If the name cannot be serialized to a filesystem file name.
32 
33   @param[in]  ShortTitleString  A pointer to the short title string to be evaluated.
34 
35   @retval  TRUE   The string is acceptable.
36   @retval  FALSE  The string should not be used.
37 
38 **/
39 STATIC
40 BOOLEAN
IsFrameworkShortNameValid(IN CHAR8 * ShortTitleString)41 IsFrameworkShortNameValid (
42   IN CHAR8  *ShortTitleString
43   )
44 {
45   // TODO: Finish this function.
46   return TRUE;
47 }
48 
49 STATIC
50 CHAR8*
AllocateAndCopyString(IN CHAR8 * StringToCopy)51 AllocateAndCopyString (
52   IN CHAR8  *StringToCopy
53   )
54 {
55   CHAR8  *NewString;
56   UINTN  NewStringLength;
57 
58   NewString = NULL;
59   NewStringLength = AsciiStrnLenS (StringToCopy, UNIT_TEST_MAX_STRING_LENGTH) + 1;
60   NewString = AllocatePool (NewStringLength * sizeof( CHAR8 ));
61   if (NewString != NULL) {
62     AsciiStrCpyS (NewString, NewStringLength, StringToCopy);
63   }
64   return NewString;
65 }
66 
67 STATIC
68 VOID
SetFrameworkFingerprint(OUT UINT8 * Fingerprint,IN UNIT_TEST_FRAMEWORK * Framework)69 SetFrameworkFingerprint (
70   OUT UINT8                *Fingerprint,
71   IN  UNIT_TEST_FRAMEWORK  *Framework
72   )
73 {
74   UINT32  NewFingerprint;
75 
76   // For this one we'll just use the title and version as the unique fingerprint.
77   NewFingerprint = CalculateCrc32( Framework->Title, (AsciiStrLen( Framework->Title ) * sizeof( CHAR8 )) );
78   NewFingerprint = (NewFingerprint >> 8) ^ CalculateCrc32( Framework->VersionString, (AsciiStrLen( Framework->VersionString ) * sizeof( CHAR8 )) );
79 
80   CopyMem( Fingerprint, &NewFingerprint, UNIT_TEST_FINGERPRINT_SIZE );
81   return;
82 }
83 
84 STATIC
85 VOID
SetSuiteFingerprint(OUT UINT8 * Fingerprint,IN UNIT_TEST_FRAMEWORK * Framework,IN UNIT_TEST_SUITE * Suite)86 SetSuiteFingerprint (
87   OUT UINT8                *Fingerprint,
88   IN  UNIT_TEST_FRAMEWORK  *Framework,
89   IN  UNIT_TEST_SUITE      *Suite
90   )
91 {
92   UINT32  NewFingerprint;
93 
94   // For this one, we'll use the fingerprint from the framework, and the title of the suite.
95   NewFingerprint = CalculateCrc32( &Framework->Fingerprint[0], UNIT_TEST_FINGERPRINT_SIZE );
96   NewFingerprint = (NewFingerprint >> 8) ^ CalculateCrc32( Suite->Title, (AsciiStrLen( Suite->Title ) * sizeof( CHAR8 )) );
97   NewFingerprint = (NewFingerprint >> 8) ^ CalculateCrc32( Suite->Name, (AsciiStrLen(Suite->Name) * sizeof(CHAR8)) );
98 
99   CopyMem( Fingerprint, &NewFingerprint, UNIT_TEST_FINGERPRINT_SIZE );
100   return;
101 }
102 
103 STATIC
104 VOID
SetTestFingerprint(OUT UINT8 * Fingerprint,IN UNIT_TEST_SUITE * Suite,IN UNIT_TEST * Test)105 SetTestFingerprint (
106   OUT UINT8            *Fingerprint,
107   IN  UNIT_TEST_SUITE  *Suite,
108   IN  UNIT_TEST        *Test
109   )
110 {
111   UINT32  NewFingerprint;
112 
113   // For this one, we'll use the fingerprint from the suite, and the description and classname of the test.
114   NewFingerprint = CalculateCrc32( &Suite->Fingerprint[0], UNIT_TEST_FINGERPRINT_SIZE );
115   NewFingerprint = (NewFingerprint >> 8) ^ CalculateCrc32( Test->Description, (AsciiStrLen( Test->Description ) * sizeof( CHAR8 )) );
116   NewFingerprint = (NewFingerprint >> 8) ^ CalculateCrc32( Test->Name, (AsciiStrLen(Test->Name) * sizeof(CHAR8)) );
117 
118   CopyMem( Fingerprint, &NewFingerprint, UNIT_TEST_FINGERPRINT_SIZE );
119   return;
120 }
121 
122 STATIC
123 BOOLEAN
CompareFingerprints(IN UINT8 * FingerprintA,IN UINT8 * FingerprintB)124 CompareFingerprints (
125   IN UINT8  *FingerprintA,
126   IN UINT8  *FingerprintB
127   )
128 {
129   return (CompareMem( FingerprintA, FingerprintB, UNIT_TEST_FINGERPRINT_SIZE ) == 0);
130 }
131 
132 /**
133   Cleanup a test framework.
134 
135   After tests are run, this will teardown the entire framework and free all
136   allocated data within.
137 
138   @param[in]  FrameworkHandle  A handle to the current running framework that
139                                dispatched the test.  Necessary for recording
140                                certain test events with the framework.
141 
142   @retval  EFI_SUCCESS            All resources associated with framework were
143                                   freed.
144   @retval  EFI_INVALID_PARAMETER  FrameworkHandle is NULL.
145 **/
146 EFI_STATUS
147 EFIAPI
FreeUnitTestFramework(IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle)148 FreeUnitTestFramework (
149   IN UNIT_TEST_FRAMEWORK_HANDLE  FrameworkHandle
150   )
151 {
152   // TODO: Finish this function.
153   return EFI_SUCCESS;
154 }
155 
156 STATIC
157 EFI_STATUS
FreeUnitTestSuiteEntry(IN UNIT_TEST_SUITE_LIST_ENTRY * SuiteEntry)158 FreeUnitTestSuiteEntry (
159   IN UNIT_TEST_SUITE_LIST_ENTRY  *SuiteEntry
160   )
161 {
162   // TODO: Finish this function.
163   return EFI_SUCCESS;
164 }
165 
166 STATIC
167 EFI_STATUS
FreeUnitTestTestEntry(IN UNIT_TEST_LIST_ENTRY * TestEntry)168 FreeUnitTestTestEntry (
169   IN UNIT_TEST_LIST_ENTRY  *TestEntry
170   )
171 {
172   // TODO: Finish this function.
173   return EFI_SUCCESS;
174 }
175 
176 /**
177   Method to Initialize the Unit Test framework.  This function registers the
178   test name and also initializes the internal state of the test framework to
179   receive any new suites and tests.
180 
181   @param[out]  FrameworkHandle  Unit test framework to be created.
182   @param[in]   Title            Null-terminated ASCII string that is the user
183                                 friendly name of the framework. String is
184                                 copied.
185   @param[in]   ShortTitle       Null-terminated ASCII short string that is the
186                                 short name of the framework with no spaces.
187                                 String is copied.
188   @param[in]   VersionString    Null-terminated ASCII version string for the
189                                 framework. String is copied.
190 
191   @retval  EFI_SUCCESS            The unit test framework was initialized.
192   @retval  EFI_INVALID_PARAMETER  FrameworkHandle is NULL.
193   @retval  EFI_INVALID_PARAMETER  Title is NULL.
194   @retval  EFI_INVALID_PARAMETER  ShortTitle is NULL.
195   @retval  EFI_INVALID_PARAMETER  VersionString is NULL.
196   @retval  EFI_INVALID_PARAMETER  ShortTitle is invalid.
197   @retval  EFI_OUT_OF_RESOURCES   There are not enough resources available to
198                                   initialize the unit test framework.
199 **/
200 EFI_STATUS
201 EFIAPI
InitUnitTestFramework(OUT UNIT_TEST_FRAMEWORK_HANDLE * FrameworkHandle,IN CHAR8 * Title,IN CHAR8 * ShortTitle,IN CHAR8 * VersionString)202 InitUnitTestFramework (
203   OUT UNIT_TEST_FRAMEWORK_HANDLE  *FrameworkHandle,
204   IN  CHAR8                       *Title,
205   IN  CHAR8                       *ShortTitle,
206   IN  CHAR8                       *VersionString
207   )
208 {
209   EFI_STATUS                  Status;
210   UNIT_TEST_FRAMEWORK_HANDLE  NewFrameworkHandle;
211   UNIT_TEST_FRAMEWORK         *NewFramework;
212 
213   Status       = EFI_SUCCESS;
214   NewFramework = NULL;
215 
216   //
217   // First, check all pointers and make sure nothing's broked.
218   //
219   if (FrameworkHandle == NULL || Title == NULL ||
220       ShortTitle == NULL || VersionString == NULL) {
221     return EFI_INVALID_PARAMETER;
222   }
223 
224   //
225   // Next, determine whether all of the strings are good to use.
226   //
227   if (!IsFrameworkShortNameValid (ShortTitle)) {
228     return EFI_INVALID_PARAMETER;
229   }
230 
231   //
232   // Next, set aside some space to start messing with the framework.
233   //
234   NewFramework = AllocateZeroPool (sizeof (UNIT_TEST_FRAMEWORK));
235   if (NewFramework == NULL) {
236     return EFI_OUT_OF_RESOURCES;
237   }
238 
239   //
240   // Next, set up all the test data.
241   //
242   NewFrameworkHandle          = (UNIT_TEST_FRAMEWORK_HANDLE)NewFramework;
243   NewFramework->Title         = AllocateAndCopyString (Title);
244   NewFramework->ShortTitle    = AllocateAndCopyString (ShortTitle);
245   NewFramework->VersionString = AllocateAndCopyString (VersionString);
246   NewFramework->Log           = NULL;
247   NewFramework->CurrentTest   = NULL;
248   NewFramework->SavedState    = NULL;
249   if (NewFramework->Title == NULL ||
250       NewFramework->ShortTitle == NULL ||
251       NewFramework->VersionString == NULL) {
252     Status = EFI_OUT_OF_RESOURCES;
253     goto Exit;
254   }
255   InitializeListHead (&(NewFramework->TestSuiteList));
256 
257   //
258   // Create the framework fingerprint.
259   //
260   SetFrameworkFingerprint (&NewFramework->Fingerprint[0], NewFramework);
261 
262   //
263   // If there is a persisted context, load it now.
264   //
265   if (DoesCacheExist (NewFrameworkHandle)) {
266     Status = LoadUnitTestCache (NewFrameworkHandle,  (UNIT_TEST_SAVE_HEADER**)(&NewFramework->SavedState));
267     if (EFI_ERROR (Status)) {
268       //
269       // Don't actually report it as an error, but emit a warning.
270       //
271       DEBUG (( DEBUG_ERROR, "%a - Cache was detected, but failed to load.\n", __FUNCTION__ ));
272       Status = EFI_SUCCESS;
273     }
274   }
275 
276 Exit:
277   //
278   // If we're good, then let's copy the framework.
279   //
280   if (!EFI_ERROR (Status)) {
281     *FrameworkHandle = NewFrameworkHandle;
282   } else {
283     //
284     // Otherwise, we need to undo this horrible thing that we've done.
285     //
286     FreeUnitTestFramework (NewFrameworkHandle);
287   }
288 
289   return Status;
290 }
291 
292 /**
293   Registers a Unit Test Suite in the Unit Test Framework.
294   At least one test suite must be registered, because all test cases must be
295   within a unit test suite.
296 
297   @param[out]  SuiteHandle      Unit test suite to create
298   @param[in]   FrameworkHandle  Unit test framework to add unit test suite to
299   @param[in]   Title            Null-terminated ASCII string that is the user
300                                 friendly name of the test suite.  String is
301                                 copied.
302   @param[in]   Name             Null-terminated ASCII string that is the short
303                                 name of the test suite with no spaces.  String
304                                 is copied.
305   @param[in]   Setup            Setup function, runs before suite.  This is an
306                                 optional parameter that may be NULL.
307   @param[in]   Teardown         Teardown function, runs after suite.  This is an
308                                 optional parameter that may be NULL.
309 
310   @retval  EFI_SUCCESS            The unit test suite was created.
311   @retval  EFI_INVALID_PARAMETER  SuiteHandle is NULL.
312   @retval  EFI_INVALID_PARAMETER  FrameworkHandle is NULL.
313   @retval  EFI_INVALID_PARAMETER  Title is NULL.
314   @retval  EFI_INVALID_PARAMETER  Name is NULL.
315   @retval  EFI_OUT_OF_RESOURCES   There are not enough resources available to
316                                   initialize the unit test suite.
317 **/
318 EFI_STATUS
319 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)320 CreateUnitTestSuite (
321   OUT UNIT_TEST_SUITE_HANDLE      *SuiteHandle,
322   IN  UNIT_TEST_FRAMEWORK_HANDLE  FrameworkHandle,
323   IN  CHAR8                       *Title,
324   IN  CHAR8                       *Name,
325   IN  UNIT_TEST_SUITE_SETUP       Setup     OPTIONAL,
326   IN  UNIT_TEST_SUITE_TEARDOWN    Teardown  OPTIONAL
327   )
328 {
329   EFI_STATUS                  Status;
330   UNIT_TEST_SUITE_LIST_ENTRY  *NewSuiteEntry;
331   UNIT_TEST_FRAMEWORK         *Framework;
332 
333   Status = EFI_SUCCESS;
334   Framework = (UNIT_TEST_FRAMEWORK *)FrameworkHandle;
335 
336   //
337   // First, let's check to make sure that our parameters look good.
338   //
339   if ((SuiteHandle == NULL) || (Framework == NULL) || (Title == NULL) || (Name == NULL)) {
340     return EFI_INVALID_PARAMETER;
341   }
342 
343   //
344   // Create the new entry.
345   //
346   NewSuiteEntry = AllocateZeroPool (sizeof (UNIT_TEST_SUITE_LIST_ENTRY));
347   if (NewSuiteEntry == NULL) {
348     return EFI_OUT_OF_RESOURCES;
349   }
350 
351   //
352   // Copy the fields we think we need.
353   //
354   NewSuiteEntry->UTS.NumTests         = 0;
355   NewSuiteEntry->UTS.Title            = AllocateAndCopyString (Title);
356   NewSuiteEntry->UTS.Name             = AllocateAndCopyString (Name);
357   NewSuiteEntry->UTS.Setup            = Setup;
358   NewSuiteEntry->UTS.Teardown         = Teardown;
359   NewSuiteEntry->UTS.ParentFramework  = FrameworkHandle;
360   InitializeListHead (&(NewSuiteEntry->Entry));             // List entry for sibling suites.
361   InitializeListHead (&(NewSuiteEntry->UTS.TestCaseList));  // List entry for child tests.
362   if (NewSuiteEntry->UTS.Title == NULL) {
363     Status = EFI_OUT_OF_RESOURCES;
364     goto Exit;
365   }
366 
367   if (NewSuiteEntry->UTS.Name == NULL) {
368     Status = EFI_OUT_OF_RESOURCES;
369     goto Exit;
370   }
371 
372   //
373   // Create the suite fingerprint.
374   //
375   SetSuiteFingerprint( &NewSuiteEntry->UTS.Fingerprint[0], Framework, &NewSuiteEntry->UTS );
376 
377 Exit:
378   //
379   // If everything is going well, add the new suite to the tail list for the framework.
380   //
381   if (!EFI_ERROR( Status )) {
382     InsertTailList (&(Framework->TestSuiteList), (LIST_ENTRY *)NewSuiteEntry);
383     *SuiteHandle = (UNIT_TEST_SUITE_HANDLE)(&NewSuiteEntry->UTS);
384   } else {
385     //
386     // Otherwise, make with the destruction.
387     //
388     FreeUnitTestSuiteEntry (NewSuiteEntry);
389   }
390 
391   return Status;
392 }
393 
394 /**
395   Adds test case to Suite
396 
397   @param[in]  SuiteHandle   Unit test suite to add test to.
398   @param[in]  Description   Null-terminated ASCII string that is the user
399                             friendly description of a test.  String is copied.
400   @param[in]  Name          Null-terminated ASCII string that is the short name
401                             of the test with no spaces.  String is copied.
402   @param[in]  Function      Unit test function.
403   @param[in]  Prerequisite  Prerequisite function, runs before test.  This is
404                             an optional parameter that may be NULL.
405   @param[in]  CleanUp       Clean up function, runs after test.  This is an
406                             optional parameter that may be NULL.
407   @param[in]  Context       Pointer to context.    This is an optional parameter
408                             that may be NULL.
409 
410   @retval  EFI_SUCCESS            The unit test case was added to Suite.
411   @retval  EFI_INVALID_PARAMETER  SuiteHandle is NULL.
412   @retval  EFI_INVALID_PARAMETER  Description is NULL.
413   @retval  EFI_INVALID_PARAMETER  Name is NULL.
414   @retval  EFI_INVALID_PARAMETER  Function is NULL.
415   @retval  EFI_OUT_OF_RESOURCES   There are not enough resources available to
416                                   add the unit test case to Suite.
417 **/
418 EFI_STATUS
419 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)420 AddTestCase (
421   IN UNIT_TEST_SUITE_HANDLE  SuiteHandle,
422   IN CHAR8                   *Description,
423   IN CHAR8                   *Name,
424   IN UNIT_TEST_FUNCTION      Function,
425   IN UNIT_TEST_PREREQUISITE  Prerequisite  OPTIONAL,
426   IN UNIT_TEST_CLEANUP       CleanUp       OPTIONAL,
427   IN UNIT_TEST_CONTEXT       Context       OPTIONAL
428   )
429 {
430   EFI_STATUS            Status;
431   UNIT_TEST_LIST_ENTRY  *NewTestEntry;
432   UNIT_TEST_FRAMEWORK   *ParentFramework;
433   UNIT_TEST_SUITE       *Suite;
434 
435   Status          = EFI_SUCCESS;
436   Suite           = (UNIT_TEST_SUITE *)SuiteHandle;
437 
438   //
439   // First, let's check to make sure that our parameters look good.
440   //
441   if ((Suite == NULL) || (Description == NULL) || (Name == NULL) || (Function == NULL)) {
442     return EFI_INVALID_PARAMETER;
443   }
444 
445   ParentFramework = (UNIT_TEST_FRAMEWORK *)Suite->ParentFramework;
446   //
447   // Create the new entry.
448   NewTestEntry = AllocateZeroPool (sizeof( UNIT_TEST_LIST_ENTRY ));
449   if (NewTestEntry == NULL) {
450     return EFI_OUT_OF_RESOURCES;
451   }
452 
453   //
454   // Copy the fields we think we need.
455   NewTestEntry->UT.Description       = AllocateAndCopyString (Description);
456   NewTestEntry->UT.Name              = AllocateAndCopyString (Name);
457   NewTestEntry->UT.FailureType       = FAILURETYPE_NOFAILURE;
458   NewTestEntry->UT.FailureMessage[0] = '\0';
459   NewTestEntry->UT.Log               = NULL;
460   NewTestEntry->UT.Prerequisite      = Prerequisite;
461   NewTestEntry->UT.CleanUp           = CleanUp;
462   NewTestEntry->UT.RunTest           = Function;
463   NewTestEntry->UT.Context           = Context;
464   NewTestEntry->UT.Result            = UNIT_TEST_PENDING;
465   NewTestEntry->UT.ParentSuite       = SuiteHandle;
466   InitializeListHead (&(NewTestEntry->Entry));  // List entry for sibling tests.
467   if (NewTestEntry->UT.Description == NULL) {
468     Status = EFI_OUT_OF_RESOURCES;
469     goto Exit;
470   }
471   if (NewTestEntry->UT.Name == NULL) {
472     Status = EFI_OUT_OF_RESOURCES;
473     goto Exit;
474   }
475 
476   //
477   // Create the test fingerprint.
478   //
479   SetTestFingerprint (&NewTestEntry->UT.Fingerprint[0], Suite, &NewTestEntry->UT);
480 
481   // TODO: Make sure that duplicate fingerprints cannot be created.
482 
483   //
484   // If there is saved test data, update this record.
485   //
486   if (ParentFramework->SavedState != NULL) {
487     UpdateTestFromSave (&NewTestEntry->UT, ParentFramework->SavedState);
488   }
489 
490 Exit:
491   //
492   // If everything is going well, add the new suite to the tail list for the framework.
493   //
494   if (!EFI_ERROR (Status)) {
495     InsertTailList (&(Suite->TestCaseList), (LIST_ENTRY*)NewTestEntry);
496     Suite->NumTests++;
497   } else {
498     //
499     // Otherwise, make with the destruction.
500     //
501     FreeUnitTestTestEntry (NewTestEntry);
502   }
503 
504   return Status;
505 }
506 
507 STATIC
508 VOID
UpdateTestFromSave(IN OUT UNIT_TEST * Test,IN UNIT_TEST_SAVE_HEADER * SavedState)509 UpdateTestFromSave (
510   IN OUT UNIT_TEST              *Test,
511   IN     UNIT_TEST_SAVE_HEADER  *SavedState
512   )
513 {
514   UNIT_TEST_SAVE_TEST     *CurrentTest;
515   UNIT_TEST_SAVE_TEST     *MatchingTest;
516   UINT8                   *FloatingPointer;
517   UNIT_TEST_SAVE_CONTEXT  *SavedContext;
518   UINTN                   Index;
519 
520   //
521   // First, evaluate the inputs.
522   //
523   if (Test == NULL || SavedState == NULL) {
524     return;
525   }
526   if (SavedState->TestCount == 0) {
527     return;
528   }
529 
530   //
531   // Next, determine whether a matching test can be found.
532   // Start at the beginning.
533   //
534   MatchingTest    = NULL;
535   FloatingPointer = (UINT8 *)SavedState + sizeof (*SavedState);
536   for (Index = 0; Index < SavedState->TestCount; Index++) {
537     CurrentTest = (UNIT_TEST_SAVE_TEST *)FloatingPointer;
538     if (CompareFingerprints (&Test->Fingerprint[0], &CurrentTest->Fingerprint[0])) {
539       MatchingTest = CurrentTest;
540       //
541       // If there's a saved context, it's important that we iterate through the entire list.
542       //
543       if (!SavedState->HasSavedContext) {
544         break;
545       }
546     }
547 
548     //
549     // If we didn't find it, we have to increment to the next test.
550     //
551     FloatingPointer = (UINT8 *)CurrentTest + CurrentTest->Size;
552   }
553 
554   //
555   // If a matching test was found, copy the status.
556   //
557   if (MatchingTest) {
558     //
559     // Override the test status with the saved status.
560     //
561     Test->Result = MatchingTest->Result;
562 
563     Test->FailureType = MatchingTest->FailureType;
564     AsciiStrnCpyS (
565       &Test->FailureMessage[0],
566       UNIT_TEST_TESTFAILUREMSG_LENGTH,
567       &MatchingTest->FailureMessage[0],
568       UNIT_TEST_TESTFAILUREMSG_LENGTH
569       );
570 
571     //
572     // If there is a log string associated, grab that.
573     // We can tell that there's a log string because the "size" will be larger than
574     // the structure size.
575     // IMPORTANT NOTE: There are security implications here.
576     //                 This data is user-supplied and we're about to play kinda
577     //                 fast and loose with data buffers.
578     //
579     if (MatchingTest->Size > sizeof (UNIT_TEST_SAVE_TEST)) {
580       UnitTestLogInit (Test, (UINT8 *)MatchingTest->Log, MatchingTest->Size - sizeof (UNIT_TEST_SAVE_TEST));
581     }
582   }
583 
584   //
585   // If the saved context exists and matches this test, grab it, too.
586   //
587   if (SavedState->HasSavedContext) {
588     //
589     // If there was a saved context, the "matching test" loop will have placed the FloatingPointer
590     // at the beginning of the context structure.
591     //
592     SavedContext = (UNIT_TEST_SAVE_CONTEXT *)FloatingPointer;
593     if ((SavedContext->Size - sizeof (UNIT_TEST_SAVE_CONTEXT)) > 0 &&
594         CompareFingerprints (&Test->Fingerprint[0], &SavedContext->Fingerprint[0])) {
595       //
596       // Override the test context with the saved context.
597       //
598       Test->Context = (VOID*)SavedContext->Data;
599     }
600   }
601 }
602 
603 STATIC
604 UNIT_TEST_SAVE_HEADER*
SerializeState(IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle,IN UNIT_TEST_CONTEXT ContextToSave,OPTIONAL IN UINTN ContextToSaveSize)605 SerializeState (
606   IN UNIT_TEST_FRAMEWORK_HANDLE  FrameworkHandle,
607   IN UNIT_TEST_CONTEXT           ContextToSave,      OPTIONAL
608   IN UINTN                       ContextToSaveSize
609   )
610 {
611   UNIT_TEST_FRAMEWORK     *Framework;
612   UNIT_TEST_SAVE_HEADER   *Header;
613   LIST_ENTRY              *SuiteListHead;
614   LIST_ENTRY              *Suite;
615   LIST_ENTRY              *TestListHead;
616   LIST_ENTRY              *Test;
617   UINT32                  TestCount;
618   UINT32                  TotalSize;
619   UINTN                   LogSize;
620   UNIT_TEST_SAVE_TEST     *TestSaveData;
621   UNIT_TEST_SAVE_CONTEXT  *TestSaveContext;
622   UNIT_TEST               *UnitTest;
623   UINT8                   *FloatingPointer;
624 
625   Framework = (UNIT_TEST_FRAMEWORK *)FrameworkHandle;
626   Header    = NULL;
627 
628   //
629   // First, let's not make assumptions about the parameters.
630   //
631   if (Framework == NULL ||
632       (ContextToSave != NULL && ContextToSaveSize == 0) ||
633       ContextToSaveSize > MAX_UINT32) {
634     return NULL;
635   }
636 
637   //
638   // Next, we've gotta figure out the resources that will be required to serialize the
639   // the framework state so that we can persist it.
640   // To start with, we're gonna need a header.
641   //
642   TotalSize = sizeof (UNIT_TEST_SAVE_HEADER);
643   //
644   // Now we need to figure out how many tests there are.
645   //
646   TestCount = 0;
647   //
648   // Iterate all suites.
649   //
650   SuiteListHead = &Framework->TestSuiteList;
651   for (Suite = GetFirstNode (SuiteListHead); Suite != SuiteListHead; Suite = GetNextNode (SuiteListHead, Suite)) {
652     //
653     // Iterate all tests within the suite.
654     //
655     TestListHead = &((UNIT_TEST_SUITE_LIST_ENTRY *)Suite)->UTS.TestCaseList;
656     for (Test = GetFirstNode (TestListHead); Test != TestListHead; Test = GetNextNode (TestListHead, Test)) {
657       UnitTest = &((UNIT_TEST_LIST_ENTRY *)Test)->UT;
658       //
659       // Account for the size of a test structure.
660       //
661       TotalSize += sizeof( UNIT_TEST_SAVE_TEST );
662       //
663       // If there's a log, make sure to account for the log size.
664       //
665       if (UnitTest->Log != NULL)     {
666         //
667         // The +1 is for the NULL character. Can't forget the NULL character.
668         //
669         LogSize = (AsciiStrLen (UnitTest->Log) + 1) * sizeof (CHAR8);
670         ASSERT (LogSize < MAX_UINT32);
671         TotalSize += (UINT32)LogSize;
672       }
673       //
674       // Increment the test count.
675       //
676       TestCount++;
677     }
678   }
679   //
680   // If there are no tests, we're done here.
681   //
682   if (TestCount == 0) {
683     return NULL;
684   }
685   //
686   // Add room for the context, if there is one.
687   //
688   if (ContextToSave != NULL) {
689     TotalSize += sizeof (UNIT_TEST_SAVE_CONTEXT) + (UINT32)ContextToSaveSize;
690   }
691 
692   //
693   // Now that we know the size, we need to allocate space for the serialized output.
694   //
695   Header = AllocateZeroPool (TotalSize);
696   if (Header == NULL) {
697     return NULL;
698   }
699 
700   //
701   // Alright, let's start setting up some data.
702   //
703   Header->Version         = UNIT_TEST_PERSISTENCE_LIB_VERSION;
704   Header->SaveStateSize   = TotalSize;
705   CopyMem (&Header->Fingerprint[0], &Framework->Fingerprint[0], UNIT_TEST_FINGERPRINT_SIZE);
706   CopyMem (&Header->StartTime, &Framework->StartTime, sizeof (EFI_TIME));
707   Header->TestCount       = TestCount;
708   Header->HasSavedContext = FALSE;
709 
710   //
711   // Start adding all of the test cases.
712   // Set the floating pointer to the start of the current test save buffer.
713   //
714   FloatingPointer = (UINT8*)Header + sizeof( UNIT_TEST_SAVE_HEADER );
715   //
716   // Iterate all suites.
717   //
718   SuiteListHead = &Framework->TestSuiteList;
719   for (Suite = GetFirstNode (SuiteListHead); Suite != SuiteListHead; Suite = GetNextNode (SuiteListHead, Suite)) {
720     //
721     // Iterate all tests within the suite.
722     //
723     TestListHead = &((UNIT_TEST_SUITE_LIST_ENTRY *)Suite)->UTS.TestCaseList;
724     for (Test = GetFirstNode (TestListHead); Test != TestListHead; Test = GetNextNode (TestListHead, Test)) {
725       TestSaveData  = (UNIT_TEST_SAVE_TEST *)FloatingPointer;
726       UnitTest      = &((UNIT_TEST_LIST_ENTRY *)Test)->UT;
727 
728       //
729       // Save the fingerprint.
730       //
731       CopyMem (&TestSaveData->Fingerprint[0], &UnitTest->Fingerprint[0], UNIT_TEST_FINGERPRINT_SIZE);
732 
733       //
734       // Save the result.
735       //
736       TestSaveData->Result = UnitTest->Result;
737       TestSaveData->FailureType = UnitTest->FailureType;
738       AsciiStrnCpyS (&TestSaveData->FailureMessage[0], UNIT_TEST_TESTFAILUREMSG_LENGTH, &UnitTest->FailureMessage[0], UNIT_TEST_TESTFAILUREMSG_LENGTH);
739 
740 
741       //
742       // If there is a log, save the log.
743       //
744       FloatingPointer += sizeof (UNIT_TEST_SAVE_TEST);
745       if (UnitTest->Log != NULL) {
746         //
747         // The +1 is for the NULL character. Can't forget the NULL character.
748         //
749         LogSize = (AsciiStrLen (UnitTest->Log) + 1) * sizeof (CHAR8);
750         CopyMem (FloatingPointer, UnitTest->Log, LogSize);
751         FloatingPointer += LogSize;
752       }
753 
754       //
755       // Update the size once the structure is complete.
756       // NOTE: Should this be a straight cast without validation?
757       //
758       TestSaveData->Size = (UINT32)(FloatingPointer - (UINT8 *)TestSaveData);
759     }
760   }
761 
762   //
763   // If there is a context to save, let's do that now.
764   //
765   if (ContextToSave != NULL && Framework->CurrentTest != NULL) {
766     TestSaveContext         = (UNIT_TEST_SAVE_CONTEXT*)FloatingPointer;
767     TestSaveContext->Size   = (UINT32)ContextToSaveSize + sizeof (UNIT_TEST_SAVE_CONTEXT);
768     CopyMem (&TestSaveContext->Fingerprint[0], &Framework->CurrentTest->Fingerprint[0], UNIT_TEST_FINGERPRINT_SIZE);
769     CopyMem (((UINT8 *)TestSaveContext + sizeof (UNIT_TEST_SAVE_CONTEXT)), ContextToSave, ContextToSaveSize);
770     Header->HasSavedContext = TRUE;
771   }
772 
773   return Header;
774 }
775 
776 /**
777   Leverages a framework-specific mechanism (see UnitTestPersistenceLib if you're
778   a framework author) to save the state of the executing framework along with
779   any allocated data so that the test may be resumed upon reentry. A test case
780   should pass any needed context (which, to prevent an infinite loop, should be
781   at least the current execution count) which will be saved by the framework and
782   passed to the test case upon resume.
783 
784   This should be called while the current test framework is valid and active. It is
785   generally called from within a test case prior to quitting or rebooting.
786 
787   @param[in]  ContextToSave      A buffer of test case-specific data to be saved
788                                  along with framework state.  Will be passed as
789                                  "Context" to the test case upon resume.  This
790                                  is an optional parameter that may be NULL.
791   @param[in]  ContextToSaveSize  Size of the ContextToSave buffer.
792 
793   @retval  EFI_SUCCESS            The framework state and context were saved.
794   @retval  EFI_NOT_FOUND          An active framework handle was not found.
795   @retval  EFI_INVALID_PARAMETER  ContextToSave is not NULL and
796                                   ContextToSaveSize is 0.
797   @retval  EFI_INVALID_PARAMETER  ContextToSave is >= 4GB.
798   @retval  EFI_OUT_OF_RESOURCES   There are not enough resources available to
799                                   save the framework and context state.
800   @retval  EFI_DEVICE_ERROR       The framework and context state could not be
801                                   saved to a persistent storage device due to a
802                                   device error.
803 **/
804 EFI_STATUS
805 EFIAPI
SaveFrameworkState(IN UNIT_TEST_CONTEXT ContextToSave OPTIONAL,IN UINTN ContextToSaveSize)806 SaveFrameworkState (
807   IN UNIT_TEST_CONTEXT           ContextToSave     OPTIONAL,
808   IN UINTN                       ContextToSaveSize
809   )
810 {
811   EFI_STATUS                  Status;
812   UNIT_TEST_FRAMEWORK_HANDLE  FrameworkHandle;
813   UNIT_TEST_SAVE_HEADER       *Header;
814 
815   Header = NULL;
816   FrameworkHandle = GetActiveFrameworkHandle ();
817 
818   //
819   // Return a unique error code if the framework is not set.
820   //
821   if (FrameworkHandle == NULL) {
822     return EFI_NOT_FOUND;
823   }
824 
825   //
826   // First, let's not make assumptions about the parameters.
827   //
828   if ((ContextToSave != NULL && ContextToSaveSize == 0) ||
829       ContextToSaveSize > MAX_UINT32) {
830     return EFI_INVALID_PARAMETER;
831   }
832 
833   //
834   // Now, let's package up all the data for saving.
835   //
836   Header = SerializeState (FrameworkHandle, ContextToSave, ContextToSaveSize);
837   if (Header == NULL) {
838     return EFI_OUT_OF_RESOURCES;
839   }
840 
841   //
842   // All that should be left to do is save it using the associated persistence lib.
843   //
844   Status = SaveUnitTestCache (FrameworkHandle, Header);
845   if (EFI_ERROR (Status)) {
846     DEBUG ((DEBUG_ERROR, "%a - Could not save state! %r\n", __FUNCTION__, Status));
847     Status = EFI_DEVICE_ERROR;
848   }
849 
850   //
851   // Free data that was used.
852   //
853   FreePool (Header);
854 
855   return Status;
856 }
857