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