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 | ///
|
---|
21 | STATIC
|
---|
22 | VOID
|
---|
23 | UpdateTestFromSave (
|
---|
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 | **/
|
---|
40 | STATIC
|
---|
41 | BOOLEAN
|
---|
42 | IsFrameworkShortNameValid (
|
---|
43 | IN CHAR8 *ShortTitleString
|
---|
44 | )
|
---|
45 | {
|
---|
46 | // TODO: Finish this function.
|
---|
47 | return TRUE;
|
---|
48 | }
|
---|
49 |
|
---|
50 | STATIC
|
---|
51 | CHAR8 *
|
---|
52 | AllocateAndCopyString (
|
---|
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 |
|
---|
69 | STATIC
|
---|
70 | VOID
|
---|
71 | SetFrameworkFingerprint (
|
---|
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 |
|
---|
86 | STATIC
|
---|
87 | VOID
|
---|
88 | SetSuiteFingerprint (
|
---|
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 |
|
---|
105 | STATIC
|
---|
106 | VOID
|
---|
107 | SetTestFingerprint (
|
---|
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 |
|
---|
124 | STATIC
|
---|
125 | BOOLEAN
|
---|
126 | CompareFingerprints (
|
---|
127 | IN UINT8 *FingerprintA,
|
---|
128 | IN UINT8 *FingerprintB
|
---|
129 | )
|
---|
130 | {
|
---|
131 | return (CompareMem (FingerprintA, FingerprintB, UNIT_TEST_FINGERPRINT_SIZE) == 0);
|
---|
132 | }
|
---|
133 |
|
---|
134 | STATIC
|
---|
135 | VOID
|
---|
136 | FreeUnitTestTestEntry (
|
---|
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 |
|
---|
153 | STATIC
|
---|
154 | EFI_STATUS
|
---|
155 | FreeUnitTestSuiteEntry (
|
---|
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 | **/
|
---|
201 | EFI_STATUS
|
---|
202 | EFIAPI
|
---|
203 | FreeUnitTestFramework (
|
---|
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 | **/
|
---|
263 | EFI_STATUS
|
---|
264 | EFIAPI
|
---|
265 | InitUnitTestFramework (
|
---|
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 |
|
---|
343 | Exit:
|
---|
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 | **/
|
---|
385 | EFI_STATUS
|
---|
386 | EFIAPI
|
---|
387 | CreateUnitTestSuite (
|
---|
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 |
|
---|
444 | Exit:
|
---|
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 | **/
|
---|
485 | EFI_STATUS
|
---|
486 | EFIAPI
|
---|
487 | AddTestCase (
|
---|
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 |
|
---|
558 | Exit:
|
---|
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 |
|
---|
575 | STATIC
|
---|
576 | VOID
|
---|
577 | UpdateTestFromSave (
|
---|
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 |
|
---|
673 | STATIC
|
---|
674 | UNIT_TEST_SAVE_HEADER *
|
---|
675 | SerializeState (
|
---|
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 | **/
|
---|
877 | EFI_STATUS
|
---|
878 | EFIAPI
|
---|
879 | SaveFrameworkState (
|
---|
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 | }
|
---|