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