VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.c

Last change on this file was 108794, checked in by vboxsync, 4 weeks ago

Devices/EFI/FirmwareNew: Merge edk2-stable202502 from the vendor branch and make it build for the important platforms, bugref:4643

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette