]> git.proxmox.com Git - mirror_edk2.git/blame - UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.c
UnitTestFrameworkPkg/UnitTestLib: Check Suite pointer before use.
[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
212 UNIT_TEST_SAVE_HEADER *SavedState;\r
213\r
214 Status = EFI_SUCCESS;\r
215 NewFramework = NULL;\r
216\r
217 //\r
218 // First, check all pointers and make sure nothing's broked.\r
219 //\r
220 if (FrameworkHandle == NULL || Title == NULL ||\r
221 ShortTitle == NULL || VersionString == NULL) {\r
222 return EFI_INVALID_PARAMETER;\r
223 }\r
224\r
225 //\r
226 // Next, determine whether all of the strings are good to use.\r
227 //\r
228 if (!IsFrameworkShortNameValid (ShortTitle)) {\r
229 return EFI_INVALID_PARAMETER;\r
230 }\r
231\r
232 //\r
233 // Next, set aside some space to start messing with the framework.\r
234 //\r
235 NewFramework = AllocateZeroPool (sizeof (UNIT_TEST_FRAMEWORK));\r
236 if (NewFramework == NULL) {\r
237 return EFI_OUT_OF_RESOURCES;\r
238 }\r
239\r
240 //\r
241 // Next, set up all the test data.\r
242 //\r
243 NewFrameworkHandle = (UNIT_TEST_FRAMEWORK_HANDLE)NewFramework;\r
244 NewFramework->Title = AllocateAndCopyString (Title);\r
245 NewFramework->ShortTitle = AllocateAndCopyString (ShortTitle);\r
246 NewFramework->VersionString = AllocateAndCopyString (VersionString);\r
247 NewFramework->Log = NULL;\r
248 NewFramework->CurrentTest = NULL;\r
249 NewFramework->SavedState = NULL;\r
250 if (NewFramework->Title == NULL ||\r
251 NewFramework->ShortTitle == NULL ||\r
252 NewFramework->VersionString == NULL) {\r
253 Status = EFI_OUT_OF_RESOURCES;\r
254 goto Exit;\r
255 }\r
256 InitializeListHead (&(NewFramework->TestSuiteList));\r
257\r
258 //\r
259 // Create the framework fingerprint.\r
260 //\r
261 SetFrameworkFingerprint (&NewFramework->Fingerprint[0], NewFramework);\r
262\r
263 //\r
264 // If there is a persisted context, load it now.\r
265 //\r
266 if (DoesCacheExist (NewFrameworkHandle)) {\r
267 SavedState = (UNIT_TEST_SAVE_HEADER *)NewFramework->SavedState;\r
268 Status = LoadUnitTestCache (NewFrameworkHandle, &SavedState);\r
269 if (EFI_ERROR (Status)) {\r
270 //\r
271 // Don't actually report it as an error, but emit a warning.\r
272 //\r
273 DEBUG (( DEBUG_ERROR, "%a - Cache was detected, but failed to load.\n", __FUNCTION__ ));\r
274 Status = EFI_SUCCESS;\r
275 }\r
276 }\r
277\r
278Exit:\r
279 //\r
280 // If we're good, then let's copy the framework.\r
281 //\r
282 if (!EFI_ERROR (Status)) {\r
283 *FrameworkHandle = NewFrameworkHandle;\r
284 } else {\r
285 //\r
286 // Otherwise, we need to undo this horrible thing that we've done.\r
287 //\r
288 FreeUnitTestFramework (NewFrameworkHandle);\r
289 }\r
290\r
291 return Status;\r
292}\r
293\r
294/**\r
295 Registers a Unit Test Suite in the Unit Test Framework.\r
296 At least one test suite must be registered, because all test cases must be\r
297 within a unit test suite.\r
298\r
299 @param[out] SuiteHandle Unit test suite to create\r
300 @param[in] FrameworkHandle Unit test framework to add unit test suite to\r
301 @param[in] Title Null-terminated ASCII string that is the user\r
302 friendly name of the test suite. String is\r
303 copied.\r
304 @param[in] Name Null-terminated ASCII string that is the short\r
305 name of the test suite with no spaces. String\r
306 is copied.\r
307 @param[in] Setup Setup function, runs before suite. This is an\r
308 optional parameter that may be NULL.\r
309 @param[in] Teardown Teardown function, runs after suite. This is an\r
310 optional parameter that may be NULL.\r
311\r
312 @retval EFI_SUCCESS The unit test suite was created.\r
313 @retval EFI_INVALID_PARAMETER SuiteHandle is NULL.\r
314 @retval EFI_INVALID_PARAMETER FrameworkHandle is NULL.\r
315 @retval EFI_INVALID_PARAMETER Title is NULL.\r
316 @retval EFI_INVALID_PARAMETER Name is NULL.\r
317 @retval EFI_OUT_OF_RESOURCES There are not enough resources available to\r
318 initialize the unit test suite.\r
319**/\r
320EFI_STATUS\r
321EFIAPI\r
322CreateUnitTestSuite (\r
323 OUT UNIT_TEST_SUITE_HANDLE *SuiteHandle,\r
324 IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle,\r
325 IN CHAR8 *Title,\r
326 IN CHAR8 *Name,\r
327 IN UNIT_TEST_SUITE_SETUP Setup OPTIONAL,\r
328 IN UNIT_TEST_SUITE_TEARDOWN Teardown OPTIONAL\r
329 )\r
330{\r
331 EFI_STATUS Status;\r
332 UNIT_TEST_SUITE_LIST_ENTRY *NewSuiteEntry;\r
333 UNIT_TEST_FRAMEWORK *Framework;\r
334\r
335 Status = EFI_SUCCESS;\r
336 Framework = (UNIT_TEST_FRAMEWORK *)FrameworkHandle;\r
337\r
338 //\r
339 // First, let's check to make sure that our parameters look good.\r
340 //\r
341 if ((SuiteHandle == NULL) || (Framework == NULL) || (Title == NULL) || (Name == NULL)) {\r
342 return EFI_INVALID_PARAMETER;\r
343 }\r
344\r
345 //\r
346 // Create the new entry.\r
347 //\r
348 NewSuiteEntry = AllocateZeroPool (sizeof (UNIT_TEST_SUITE_LIST_ENTRY));\r
349 if (NewSuiteEntry == NULL) {\r
350 return EFI_OUT_OF_RESOURCES;\r
351 }\r
352\r
353 //\r
354 // Copy the fields we think we need.\r
355 //\r
356 NewSuiteEntry->UTS.NumTests = 0;\r
357 NewSuiteEntry->UTS.Title = AllocateAndCopyString (Title);\r
358 NewSuiteEntry->UTS.Name = AllocateAndCopyString (Name);\r
359 NewSuiteEntry->UTS.Setup = Setup;\r
360 NewSuiteEntry->UTS.Teardown = Teardown;\r
361 NewSuiteEntry->UTS.ParentFramework = FrameworkHandle;\r
362 InitializeListHead (&(NewSuiteEntry->Entry)); // List entry for sibling suites.\r
363 InitializeListHead (&(NewSuiteEntry->UTS.TestCaseList)); // List entry for child tests.\r
364 if (NewSuiteEntry->UTS.Title == NULL) {\r
365 Status = EFI_OUT_OF_RESOURCES;\r
366 goto Exit;\r
367 }\r
368\r
369 if (NewSuiteEntry->UTS.Name == NULL) {\r
370 Status = EFI_OUT_OF_RESOURCES;\r
371 goto Exit;\r
372 }\r
373\r
374 //\r
375 // Create the suite fingerprint.\r
376 //\r
377 SetSuiteFingerprint( &NewSuiteEntry->UTS.Fingerprint[0], Framework, &NewSuiteEntry->UTS );\r
378\r
379Exit:\r
380 //\r
381 // If everything is going well, add the new suite to the tail list for the framework.\r
382 //\r
383 if (!EFI_ERROR( Status )) {\r
384 InsertTailList (&(Framework->TestSuiteList), (LIST_ENTRY *)NewSuiteEntry);\r
385 *SuiteHandle = (UNIT_TEST_SUITE_HANDLE)(&NewSuiteEntry->UTS);\r
386 } else {\r
387 //\r
388 // Otherwise, make with the destruction.\r
389 //\r
390 FreeUnitTestSuiteEntry (NewSuiteEntry);\r
391 }\r
392\r
393 return Status;\r
394}\r
395\r
396/**\r
397 Adds test case to Suite\r
398\r
399 @param[in] SuiteHandle Unit test suite to add test to.\r
400 @param[in] Description Null-terminated ASCII string that is the user\r
401 friendly description of a test. String is copied.\r
402 @param[in] Name Null-terminated ASCII string that is the short name\r
403 of the test with no spaces. String is copied.\r
404 @param[in] Function Unit test function.\r
405 @param[in] Prerequisite Prerequisite function, runs before test. This is\r
406 an optional parameter that may be NULL.\r
407 @param[in] CleanUp Clean up function, runs after test. This is an\r
408 optional parameter that may be NULL.\r
409 @param[in] Context Pointer to context. This is an optional parameter\r
410 that may be NULL.\r
411\r
412 @retval EFI_SUCCESS The unit test case was added to Suite.\r
413 @retval EFI_INVALID_PARAMETER SuiteHandle is NULL.\r
414 @retval EFI_INVALID_PARAMETER Description is NULL.\r
415 @retval EFI_INVALID_PARAMETER Name is NULL.\r
416 @retval EFI_INVALID_PARAMETER Function is NULL.\r
417 @retval EFI_OUT_OF_RESOURCES There are not enough resources available to\r
418 add the unit test case to Suite.\r
419**/\r
420EFI_STATUS\r
421EFIAPI\r
422AddTestCase (\r
423 IN UNIT_TEST_SUITE_HANDLE SuiteHandle,\r
424 IN CHAR8 *Description,\r
425 IN CHAR8 *Name,\r
426 IN UNIT_TEST_FUNCTION Function,\r
427 IN UNIT_TEST_PREREQUISITE Prerequisite OPTIONAL,\r
428 IN UNIT_TEST_CLEANUP CleanUp OPTIONAL,\r
429 IN UNIT_TEST_CONTEXT Context OPTIONAL\r
430 )\r
431{\r
432 EFI_STATUS Status;\r
433 UNIT_TEST_LIST_ENTRY *NewTestEntry;\r
434 UNIT_TEST_FRAMEWORK *ParentFramework;\r
435 UNIT_TEST_SUITE *Suite;\r
436\r
437 Status = EFI_SUCCESS;\r
438 Suite = (UNIT_TEST_SUITE *)SuiteHandle;\r
0eb52298
MK
439\r
440 //\r
441 // First, let's check to make sure that our parameters look good.\r
442 //\r
443 if ((Suite == NULL) || (Description == NULL) || (Name == NULL) || (Function == NULL)) {\r
444 return EFI_INVALID_PARAMETER;\r
445 }\r
446\r
5bc09cf0 447 ParentFramework = (UNIT_TEST_FRAMEWORK *)Suite->ParentFramework;\r
0eb52298
MK
448 //\r
449 // Create the new entry.\r
450 NewTestEntry = AllocateZeroPool (sizeof( UNIT_TEST_LIST_ENTRY ));\r
451 if (NewTestEntry == NULL) {\r
452 return EFI_OUT_OF_RESOURCES;\r
453 }\r
454\r
455 //\r
456 // Copy the fields we think we need.\r
457 NewTestEntry->UT.Description = AllocateAndCopyString (Description);\r
458 NewTestEntry->UT.Name = AllocateAndCopyString (Name);\r
459 NewTestEntry->UT.FailureType = FAILURETYPE_NOFAILURE;\r
460 NewTestEntry->UT.FailureMessage[0] = '\0';\r
461 NewTestEntry->UT.Log = NULL;\r
462 NewTestEntry->UT.Prerequisite = Prerequisite;\r
463 NewTestEntry->UT.CleanUp = CleanUp;\r
464 NewTestEntry->UT.RunTest = Function;\r
465 NewTestEntry->UT.Context = Context;\r
466 NewTestEntry->UT.Result = UNIT_TEST_PENDING;\r
467 NewTestEntry->UT.ParentSuite = SuiteHandle;\r
468 InitializeListHead (&(NewTestEntry->Entry)); // List entry for sibling tests.\r
469 if (NewTestEntry->UT.Description == NULL) {\r
470 Status = EFI_OUT_OF_RESOURCES;\r
471 goto Exit;\r
472 }\r
473 if (NewTestEntry->UT.Name == NULL) {\r
474 Status = EFI_OUT_OF_RESOURCES;\r
475 goto Exit;\r
476 }\r
477\r
478 //\r
479 // Create the test fingerprint.\r
480 //\r
481 SetTestFingerprint (&NewTestEntry->UT.Fingerprint[0], Suite, &NewTestEntry->UT);\r
482\r
483 // TODO: Make sure that duplicate fingerprints cannot be created.\r
484\r
485 //\r
486 // If there is saved test data, update this record.\r
487 //\r
488 if (ParentFramework->SavedState != NULL) {\r
489 UpdateTestFromSave (&NewTestEntry->UT, ParentFramework->SavedState);\r
490 }\r
491\r
492Exit:\r
493 //\r
494 // If everything is going well, add the new suite to the tail list for the framework.\r
495 //\r
496 if (!EFI_ERROR (Status)) {\r
497 InsertTailList (&(Suite->TestCaseList), (LIST_ENTRY*)NewTestEntry);\r
498 Suite->NumTests++;\r
499 } else {\r
500 //\r
501 // Otherwise, make with the destruction.\r
502 //\r
503 FreeUnitTestTestEntry (NewTestEntry);\r
504 }\r
505\r
506 return Status;\r
507}\r
508\r
509STATIC\r
510VOID\r
511UpdateTestFromSave (\r
512 IN OUT UNIT_TEST *Test,\r
513 IN UNIT_TEST_SAVE_HEADER *SavedState\r
514 )\r
515{\r
516 UNIT_TEST_SAVE_TEST *CurrentTest;\r
517 UNIT_TEST_SAVE_TEST *MatchingTest;\r
518 UINT8 *FloatingPointer;\r
519 UNIT_TEST_SAVE_CONTEXT *SavedContext;\r
520 UINTN Index;\r
521\r
522 //\r
523 // First, evaluate the inputs.\r
524 //\r
525 if (Test == NULL || SavedState == NULL) {\r
526 return;\r
527 }\r
528 if (SavedState->TestCount == 0) {\r
529 return;\r
530 }\r
531\r
532 //\r
533 // Next, determine whether a matching test can be found.\r
534 // Start at the beginning.\r
535 //\r
536 MatchingTest = NULL;\r
537 FloatingPointer = (UINT8 *)SavedState + sizeof (*SavedState);\r
538 for (Index = 0; Index < SavedState->TestCount; Index++) {\r
539 CurrentTest = (UNIT_TEST_SAVE_TEST *)FloatingPointer;\r
540 if (CompareFingerprints (&Test->Fingerprint[0], &CurrentTest->Fingerprint[0])) {\r
541 MatchingTest = CurrentTest;\r
542 //\r
543 // If there's a saved context, it's important that we iterate through the entire list.\r
544 //\r
545 if (!SavedState->HasSavedContext) {\r
546 break;\r
547 }\r
548 }\r
549\r
550 //\r
551 // If we didn't find it, we have to increment to the next test.\r
552 //\r
553 FloatingPointer = (UINT8 *)CurrentTest + CurrentTest->Size;\r
554 }\r
555\r
556 //\r
557 // If a matching test was found, copy the status.\r
558 //\r
559 if (MatchingTest) {\r
560 //\r
561 // Override the test status with the saved status.\r
562 //\r
563 Test->Result = MatchingTest->Result;\r
564\r
565 Test->FailureType = MatchingTest->FailureType;\r
566 AsciiStrnCpyS (\r
567 &Test->FailureMessage[0],\r
568 UNIT_TEST_TESTFAILUREMSG_LENGTH,\r
569 &MatchingTest->FailureMessage[0],\r
570 UNIT_TEST_TESTFAILUREMSG_LENGTH\r
571 );\r
572\r
573 //\r
574 // If there is a log string associated, grab that.\r
575 // We can tell that there's a log string because the "size" will be larger than\r
576 // the structure size.\r
577 // IMPORTANT NOTE: There are security implications here.\r
578 // This data is user-supplied and we're about to play kinda\r
579 // fast and loose with data buffers.\r
580 //\r
581 if (MatchingTest->Size > sizeof (UNIT_TEST_SAVE_TEST)) {\r
582 UnitTestLogInit (Test, (UINT8 *)MatchingTest->Log, MatchingTest->Size - sizeof (UNIT_TEST_SAVE_TEST));\r
583 }\r
584 }\r
585\r
586 //\r
587 // If the saved context exists and matches this test, grab it, too.\r
588 //\r
589 if (SavedState->HasSavedContext) {\r
590 //\r
591 // If there was a saved context, the "matching test" loop will have placed the FloatingPointer\r
592 // at the beginning of the context structure.\r
593 //\r
594 SavedContext = (UNIT_TEST_SAVE_CONTEXT *)FloatingPointer;\r
595 if ((SavedContext->Size - sizeof (UNIT_TEST_SAVE_CONTEXT)) > 0 &&\r
596 CompareFingerprints (&Test->Fingerprint[0], &SavedContext->Fingerprint[0])) {\r
597 //\r
598 // Override the test context with the saved context.\r
599 //\r
600 Test->Context = (VOID*)SavedContext->Data;\r
601 }\r
602 }\r
603}\r
604\r
605STATIC\r
606UNIT_TEST_SAVE_HEADER*\r
607SerializeState (\r
608 IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle,\r
609 IN UNIT_TEST_CONTEXT ContextToSave, OPTIONAL\r
610 IN UINTN ContextToSaveSize\r
611 )\r
612{\r
613 UNIT_TEST_FRAMEWORK *Framework;\r
614 UNIT_TEST_SAVE_HEADER *Header;\r
615 LIST_ENTRY *SuiteListHead;\r
616 LIST_ENTRY *Suite;\r
617 LIST_ENTRY *TestListHead;\r
618 LIST_ENTRY *Test;\r
619 UINT32 TestCount;\r
620 UINT32 TotalSize;\r
621 UINTN LogSize;\r
622 UNIT_TEST_SAVE_TEST *TestSaveData;\r
623 UNIT_TEST_SAVE_CONTEXT *TestSaveContext;\r
624 UNIT_TEST *UnitTest;\r
625 UINT8 *FloatingPointer;\r
626\r
627 Framework = (UNIT_TEST_FRAMEWORK *)FrameworkHandle;\r
628 Header = NULL;\r
629\r
630 //\r
631 // First, let's not make assumptions about the parameters.\r
632 //\r
633 if (Framework == NULL ||\r
634 (ContextToSave != NULL && ContextToSaveSize == 0) ||\r
635 ContextToSaveSize > MAX_UINT32) {\r
636 return NULL;\r
637 }\r
638\r
639 //\r
640 // Next, we've gotta figure out the resources that will be required to serialize the\r
641 // the framework state so that we can persist it.\r
642 // To start with, we're gonna need a header.\r
643 //\r
644 TotalSize = sizeof (UNIT_TEST_SAVE_HEADER);\r
645 //\r
646 // Now we need to figure out how many tests there are.\r
647 //\r
648 TestCount = 0;\r
649 //\r
650 // Iterate all suites.\r
651 //\r
652 SuiteListHead = &Framework->TestSuiteList;\r
653 for (Suite = GetFirstNode (SuiteListHead); Suite != SuiteListHead; Suite = GetNextNode (SuiteListHead, Suite)) {\r
654 //\r
655 // Iterate all tests within the suite.\r
656 //\r
657 TestListHead = &((UNIT_TEST_SUITE_LIST_ENTRY *)Suite)->UTS.TestCaseList;\r
658 for (Test = GetFirstNode (TestListHead); Test != TestListHead; Test = GetNextNode (TestListHead, Test)) {\r
659 UnitTest = &((UNIT_TEST_LIST_ENTRY *)Test)->UT;\r
660 //\r
661 // Account for the size of a test structure.\r
662 //\r
663 TotalSize += sizeof( UNIT_TEST_SAVE_TEST );\r
664 //\r
665 // If there's a log, make sure to account for the log size.\r
666 //\r
667 if (UnitTest->Log != NULL) {\r
668 //\r
669 // The +1 is for the NULL character. Can't forget the NULL character.\r
670 //\r
671 LogSize = (AsciiStrLen (UnitTest->Log) + 1) * sizeof (CHAR8);\r
672 ASSERT (LogSize < MAX_UINT32);\r
673 TotalSize += (UINT32)LogSize;\r
674 }\r
675 //\r
676 // Increment the test count.\r
677 //\r
678 TestCount++;\r
679 }\r
680 }\r
681 //\r
682 // If there are no tests, we're done here.\r
683 //\r
684 if (TestCount == 0) {\r
685 return NULL;\r
686 }\r
687 //\r
688 // Add room for the context, if there is one.\r
689 //\r
690 if (ContextToSave != NULL) {\r
691 TotalSize += sizeof (UNIT_TEST_SAVE_CONTEXT) + (UINT32)ContextToSaveSize;\r
692 }\r
693\r
694 //\r
695 // Now that we know the size, we need to allocate space for the serialized output.\r
696 //\r
697 Header = AllocateZeroPool (TotalSize);\r
698 if (Header == NULL) {\r
699 return NULL;\r
700 }\r
701\r
702 //\r
703 // Alright, let's start setting up some data.\r
704 //\r
705 Header->Version = UNIT_TEST_PERSISTENCE_LIB_VERSION;\r
706 Header->SaveStateSize = TotalSize;\r
707 CopyMem (&Header->Fingerprint[0], &Framework->Fingerprint[0], UNIT_TEST_FINGERPRINT_SIZE);\r
708 CopyMem (&Header->StartTime, &Framework->StartTime, sizeof (EFI_TIME));\r
709 Header->TestCount = TestCount;\r
710 Header->HasSavedContext = FALSE;\r
711\r
712 //\r
713 // Start adding all of the test cases.\r
714 // Set the floating pointer to the start of the current test save buffer.\r
715 //\r
716 FloatingPointer = (UINT8*)Header + sizeof( UNIT_TEST_SAVE_HEADER );\r
717 //\r
718 // Iterate all suites.\r
719 //\r
720 SuiteListHead = &Framework->TestSuiteList;\r
721 for (Suite = GetFirstNode (SuiteListHead); Suite != SuiteListHead; Suite = GetNextNode (SuiteListHead, Suite)) {\r
722 //\r
723 // Iterate all tests within the suite.\r
724 //\r
725 TestListHead = &((UNIT_TEST_SUITE_LIST_ENTRY *)Suite)->UTS.TestCaseList;\r
726 for (Test = GetFirstNode (TestListHead); Test != TestListHead; Test = GetNextNode (TestListHead, Test)) {\r
727 TestSaveData = (UNIT_TEST_SAVE_TEST *)FloatingPointer;\r
728 UnitTest = &((UNIT_TEST_LIST_ENTRY *)Test)->UT;\r
729\r
730 //\r
731 // Save the fingerprint.\r
732 //\r
733 CopyMem (&TestSaveData->Fingerprint[0], &UnitTest->Fingerprint[0], UNIT_TEST_FINGERPRINT_SIZE);\r
734\r
735 //\r
736 // Save the result.\r
737 //\r
738 TestSaveData->Result = UnitTest->Result;\r
739 TestSaveData->FailureType = UnitTest->FailureType;\r
740 AsciiStrnCpyS (&TestSaveData->FailureMessage[0], UNIT_TEST_TESTFAILUREMSG_LENGTH, &UnitTest->FailureMessage[0], UNIT_TEST_TESTFAILUREMSG_LENGTH);\r
741\r
742\r
743 //\r
744 // If there is a log, save the log.\r
745 //\r
746 FloatingPointer += sizeof (UNIT_TEST_SAVE_TEST);\r
747 if (UnitTest->Log != NULL) {\r
748 //\r
749 // The +1 is for the NULL character. Can't forget the NULL character.\r
750 //\r
751 LogSize = (AsciiStrLen (UnitTest->Log) + 1) * sizeof (CHAR8);\r
752 CopyMem (FloatingPointer, UnitTest->Log, LogSize);\r
753 FloatingPointer += LogSize;\r
754 }\r
755\r
756 //\r
757 // Update the size once the structure is complete.\r
758 // NOTE: Should this be a straight cast without validation?\r
759 //\r
760 TestSaveData->Size = (UINT32)(FloatingPointer - (UINT8 *)TestSaveData);\r
761 }\r
762 }\r
763\r
764 //\r
765 // If there is a context to save, let's do that now.\r
766 //\r
767 if (ContextToSave != NULL && Framework->CurrentTest != NULL) {\r
768 TestSaveContext = (UNIT_TEST_SAVE_CONTEXT*)FloatingPointer;\r
769 TestSaveContext->Size = (UINT32)ContextToSaveSize + sizeof (UNIT_TEST_SAVE_CONTEXT);\r
770 CopyMem (&TestSaveContext->Fingerprint[0], &Framework->CurrentTest->Fingerprint[0], UNIT_TEST_FINGERPRINT_SIZE);\r
771 CopyMem (((UINT8 *)TestSaveContext + sizeof (UNIT_TEST_SAVE_CONTEXT)), ContextToSave, ContextToSaveSize);\r
772 Header->HasSavedContext = TRUE;\r
773 }\r
774\r
775 return Header;\r
776}\r
777\r
778/**\r
779 Leverages a framework-specific mechanism (see UnitTestPersistenceLib if you're\r
780 a framework author) to save the state of the executing framework along with\r
781 any allocated data so that the test may be resumed upon reentry. A test case\r
782 should pass any needed context (which, to prevent an infinite loop, should be\r
783 at least the current execution count) which will be saved by the framework and\r
784 passed to the test case upon resume.\r
785\r
786 Generally called from within a test case prior to quitting or rebooting.\r
787\r
788 @param[in] FrameworkHandle A handle to the current running framework that\r
789 dispatched the test. Necessary for recording\r
790 certain test events with the framework.\r
791 @param[in] ContextToSave A buffer of test case-specific data to be saved\r
792 along with framework state. Will be passed as\r
793 "Context" to the test case upon resume. This\r
794 is an optional parameter that may be NULL.\r
795 @param[in] ContextToSaveSize Size of the ContextToSave buffer.\r
796\r
797 @retval EFI_SUCCESS The framework state and context were saved.\r
798 @retval EFI_INVALID_PARAMETER FrameworkHandle is NULL.\r
799 @retval EFI_INVALID_PARAMETER ContextToSave is not NULL and\r
800 ContextToSaveSize is 0.\r
801 @retval EFI_INVALID_PARAMETER ContextToSave is >= 4GB.\r
802 @retval EFI_OUT_OF_RESOURCES There are not enough resources available to\r
803 save the framework and context state.\r
804 @retval EFI_DEVICE_ERROR The framework and context state could not be\r
805 saved to a persistent storage device due to a\r
806 device error.\r
807**/\r
808EFI_STATUS\r
809EFIAPI\r
810SaveFrameworkState (\r
811 IN UNIT_TEST_FRAMEWORK_HANDLE FrameworkHandle,\r
812 IN UNIT_TEST_CONTEXT ContextToSave OPTIONAL,\r
813 IN UINTN ContextToSaveSize\r
814 )\r
815{\r
816 EFI_STATUS Status;\r
817 UNIT_TEST_SAVE_HEADER *Header;\r
818\r
819 Header = NULL;\r
820\r
821 //\r
822 // First, let's not make assumptions about the parameters.\r
823 //\r
824 if (FrameworkHandle == NULL ||\r
825 (ContextToSave != NULL && ContextToSaveSize == 0) ||\r
826 ContextToSaveSize > MAX_UINT32) {\r
827 return EFI_INVALID_PARAMETER;\r
828 }\r
829\r
830 //\r
831 // Now, let's package up all the data for saving.\r
832 //\r
833 Header = SerializeState (FrameworkHandle, ContextToSave, ContextToSaveSize);\r
834 if (Header == NULL) {\r
835 return EFI_OUT_OF_RESOURCES;\r
836 }\r
837\r
838 //\r
839 // All that should be left to do is save it using the associated persistence lib.\r
840 //\r
841 Status = SaveUnitTestCache (FrameworkHandle, Header);\r
842 if (EFI_ERROR (Status)) {\r
843 DEBUG ((DEBUG_ERROR, "%a - Could not save state! %r\n", __FUNCTION__, Status));\r
844 Status = EFI_DEVICE_ERROR;\r
845 }\r
846\r
847 //\r
848 // Free data that was used.\r
849 //\r
850 FreePool (Header);\r
851\r
852 return Status;\r
853}\r