]>
Commit | Line | Data |
---|---|---|
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 | |
21 | STATIC\r | |
22 | VOID\r | |
23 | UpdateTestFromSave (\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 | |
40 | STATIC\r | |
41 | BOOLEAN\r | |
42 | IsFrameworkShortNameValid (\r | |
43 | IN CHAR8 *ShortTitleString\r | |
44 | )\r | |
45 | {\r | |
46 | // TODO: Finish this function.\r | |
47 | return TRUE;\r | |
48 | }\r | |
49 | \r | |
50 | STATIC\r | |
7c0ad2c3 | 51 | CHAR8 *\r |
0eb52298 MK |
52 | AllocateAndCopyString (\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 | |
69 | STATIC\r | |
70 | VOID\r | |
71 | SetFrameworkFingerprint (\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 | |
86 | STATIC\r | |
87 | VOID\r | |
88 | SetSuiteFingerprint (\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 | |
105 | STATIC\r | |
106 | VOID\r | |
107 | SetTestFingerprint (\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 | |
124 | STATIC\r | |
125 | BOOLEAN\r | |
126 | CompareFingerprints (\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 | |
148 | EFI_STATUS\r | |
149 | EFIAPI\r | |
150 | FreeUnitTestFramework (\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 | |
158 | STATIC\r | |
159 | EFI_STATUS\r | |
160 | FreeUnitTestSuiteEntry (\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 | |
168 | STATIC\r | |
169 | EFI_STATUS\r | |
170 | FreeUnitTestTestEntry (\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 | |
202 | EFI_STATUS\r | |
203 | EFIAPI\r | |
204 | InitUnitTestFramework (\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 | |
282 | Exit:\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 | |
324 | EFI_STATUS\r | |
325 | EFIAPI\r | |
326 | CreateUnitTestSuite (\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 |
383 | Exit:\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 | |
424 | EFI_STATUS\r | |
425 | EFIAPI\r | |
426 | AddTestCase (\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 | |
497 | Exit:\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 | |
514 | STATIC\r | |
515 | VOID\r | |
516 | UpdateTestFromSave (\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 | |
612 | STATIC\r | |
7c0ad2c3 | 613 | UNIT_TEST_SAVE_HEADER *\r |
0eb52298 MK |
614 | SerializeState (\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 | |
816 | EFI_STATUS\r | |
817 | EFIAPI\r | |
818 | SaveFrameworkState (\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 |