]> git.proxmox.com Git - mirror_edk2.git/blob - UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.c
UnitTestFrameworkPkg: Change OPTIONAL keyword usage style
[mirror_edk2.git] / UnitTestFrameworkPkg / Library / UnitTestLib / UnitTestLib.c
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
41 IsFrameworkShortNameValid (
42 IN CHAR8 *ShortTitleString
43 )
44 {
45 // TODO: Finish this function.
46 return TRUE;
47 }
48
49 STATIC
50 CHAR8*
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
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
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
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
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
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
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
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
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
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
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
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*
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
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 }