]> git.proxmox.com Git - mirror_edk2.git/blame - UnitTestFrameworkPkg/ReadMe.md
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / UnitTestFrameworkPkg / ReadMe.md
CommitLineData
0f7fb5c5
MK
1# Unit Test Framework Package\r
2\r
3## About\r
4\r
cef0c5c6 5This package provides unit test frameworks capable of building tests for multiple contexts including\r
0f7fb5c5 6the UEFI shell environment and host-based environments. It allows for unit test development to focus\r
cef0c5c6 7on the tests and leave error logging, result formatting, context persistence, and test running to the framework.\r
0f7fb5c5
MK
8The unit test framework works well for low level unit tests as well as system level tests and\r
9fits easily in automation frameworks.\r
10\r
cef0c5c6
MK
11### Framework\r
12\r
13The first unit test framework is called **Framework** and is implemented as a set of EDK II libraries.\r
14The Framework supports both host-based unit tests and target-based unit tests that share the same\r
15source style, macros, and APIs. In some scenarios, the same unit test case sources can be built\r
16for both host-based unit test execution and target-based unit test execution. Host-based unit tests\r
17that require mocked interfaces can use the mocking infrastructure provided by\r
18[cmocka](https://api.cmocka.org/) that is included in the UnitTestFrameworkPkg as a submodule.\r
19\r
20### GoogleTest\r
21\r
22The second unit test framework supported by the UnitTestFrameworkPkg is\r
23[GoogleTest](http://google.github.io/googletest/) that can be used to implement host-based unit tests.\r
24Use of GoogleTest for target-based unit tests of EDK II components is not supported. If a\r
25host-based unit test requires mocked interfaces, then the Framework with cmocka support should be\r
26used instead. Enabling support for mocked interfaces with GoogleTest is being actively investigated.\r
27[GoogleTest on GitHub](https://github.com/google/googletest) is included in the UnitTestFrameworkPkg\r
28as a submodule.\r
29\r
30GoogleTest requires less overhead to register test suites and test cases compared to the Framework.\r
31There are also a number of tools that layer on top of GoogleTest that improve developer productivity.\r
32One example is the VS Code extension\r
33[C++ TestMate](https://marketplace.visualstudio.com/items?itemName=matepek.vscode-catch2-test-adapter)\r
34that may be used to implement, run, and debug unit tests implemented using GoogleTest.\r
35\r
36If a component can be tested with host-based unit tests without support for mocked interfaces,\r
37then GoogleTest is recommended. The MdePkg contains a port of the BaseSafeIntLib unit tests in\r
38the GoogleTest style so the differences between GoogleTest and Framework unit tests can be reviewed.\r
39The paths to the BaseSafeIntLib unit tests are:\r
40\r
41* MdePkg\Test\UnitTest\Library\BaseSafeIntLib\r
42* MdePkg\Test\GoogleTest\Library\BaseSafeIntLib\r
43\r
44## Framework and GoogleTest Feature Comparison\r
45\r
46| Feature | Framework | GoogleTest |\r
47|:----------------------------|:---------:|:----------:|\r
48| Host Based Unit Tests | YES | YES |\r
49| Target Based Unit Tests | YES | NO |\r
50| Unit Test Source Language | C | C++ |\r
51| Register Test Suite | YES | Auto |\r
52| Register Test Case | YES | Auto |\r
53| Death/Expected Assert Tests | YES | YES |\r
54| Setup/Teardown Hooks | YES | YES |\r
55| Value-Parameterized Tests | NO | YES |\r
56| Typed Tests | NO | YES |\r
57| Type-Parameterized Tests | NO | YES |\r
58| Timeout Support | NO | YES |\r
59| Mocking Support | Cmocka | NO |\r
60| JUNIT XML Reports | YES | YES |\r
61| Execute subset of tests | NO | YES |\r
62| VS Code Extensions | NO | YES |\r
63\r
64## Framework Libraries\r
65\r
0f7fb5c5
MK
66### UnitTestLib\r
67\r
68The main "framework" library. The core of the framework is the Framework object, which can have any number\r
69of test cases and test suites registered with it. The Framework object is also what drives test execution.\r
70\r
71The Framework also provides helper macros and functions for checking test conditions and\r
72reporting errors. Status and error info will be logged into the test context. There are a number\r
73of Assert macros that make the unit test code friendly to view and easy to understand.\r
74\r
75Finally, the Framework also supports logging strings during the test execution. This data is logged\r
76to the test context and will be available in the test reporting phase. This should be used for\r
77logging test details and helpful messages to resolve test failures.\r
78\r
79### UnitTestPersistenceLib\r
80\r
81Persistence lib has the main job of saving and restoring test context to a storage medium so that for tests\r
82that require exiting the active process and then resuming state can be maintained. This is critical\r
83in supporting a system reboot in the middle of a test run.\r
84\r
85### UnitTestResultReportLib\r
86\r
87Library provides function to run at the end of a framework test run and handles formatting the report.\r
88This is a common customization point and allows the unit test framework to fit its output reports into\r
cef0c5c6 89other test infrastructure. In this package simple library instances have been supplied to output test\r
0f7fb5c5
MK
90results to the console as plain text.\r
91\r
cef0c5c6 92## Framework Samples\r
0f7fb5c5
MK
93\r
94There is a sample unit test provided as both an example of how to write a unit test and leverage\r
95many of the features of the framework. This sample can be found in the `Test/UnitTest/Sample/SampleUnitTest`\r
96directory.\r
97\r
98The sample is provided in PEI, SMM, DXE, and UEFI App flavors. It also has a flavor for the HOST_APPLICATION\r
99build type, which can be run on a host system without needing a target.\r
100\r
cef0c5c6 101## Framework Usage\r
0f7fb5c5
MK
102\r
103This section is built a lot like a "Getting Started". We'll go through some of the components that are needed\r
104when constructing a unit test and some of the decisions that are made by the test writer. We'll also describe\r
105how to check for expected conditions in test cases and a bit of the logging characteristics.\r
106\r
107Most of these examples will refer to the SampleUnitTestUefiShell app found in this package.\r
108\r
cef0c5c6 109### Framework Requirements - INF\r
0f7fb5c5
MK
110\r
111In our INF file, we'll need to bring in the `UnitTestLib` library. Conveniently, the interface\r
112header for the `UnitTestLib` is located in `MdePkg`, so you shouldn't need to depend on any other\r
113packages. As long as your DSC file knows where to find the lib implementation that you want to use,\r
114you should be good to go.\r
115\r
4403bbd7 116See this example in 'SampleUnitTestUefiShell.inf'...\r
0f7fb5c5
MK
117\r
118```\r
119[Packages]\r
120 MdePkg/MdePkg.dec\r
121\r
122[LibraryClasses]\r
123 UefiApplicationEntryPoint\r
124 BaseLib\r
125 DebugLib\r
126 UnitTestLib\r
127 PrintLib\r
128```\r
129\r
4403bbd7
BB
130Also, if you want you test to automatically be picked up by the Test Runner plugin, you will need\r
131to make sure that the module `BASE_NAME` contains the word `Test`...\r
132\r
133```\r
134[Defines]\r
135 BASE_NAME = SampleUnitTestUefiShell\r
136```\r
137\r
cef0c5c6 138### Framework Requirements - Code\r
0f7fb5c5
MK
139\r
140Not to state the obvious, but let's make sure we have the following include before getting too far along...\r
141\r
142```c\r
143#include <Library/UnitTestLib.h>\r
144```\r
145\r
146Now that we've got that squared away, let's look at our 'Main()'' routine (or DriverEntryPoint() or whatever).\r
147\r
cef0c5c6 148### Framework Configuration\r
0f7fb5c5 149\r
cef0c5c6 150Everything in the UnitTestFrameworkPkg framework is built around an object called -- conveniently -- the Framework.\r
0f7fb5c5
MK
151This Framework object will contain all the information about our test, the test suites and test cases associated\r
152with it, the current location within the test pass, and any results that have been recorded so far.\r
153\r
154To get started with a test, we must first create a Framework instance. The function for this is\r
155`InitUnitTestFramework`. It takes in `CHAR8` strings for the long name, short name, and test version.\r
156The long name and version strings are just for user presentation and relatively flexible. The short name\r
157will be used to name any cache files and/or test results, so should be a name that makes sense in that context.\r
158These strings are copied internally to the Framework, so using stack-allocated or literal strings is fine.\r
159\r
cef0c5c6 160In the 'SampleUnitTestUefiShell' app, the module name is used as the short name, so the initialization looks like this.\r
0f7fb5c5
MK
161\r
162```c\r
163DEBUG(( DEBUG_INFO, "%a v%a\n", UNIT_TEST_APP_NAME, UNIT_TEST_APP_VERSION ));\r
164\r
165//\r
166// Start setting up the test framework for running the tests.\r
167//\r
168Status = InitUnitTestFramework( &Framework, UNIT_TEST_APP_NAME, gEfiCallerBaseName, UNIT_TEST_APP_VERSION );\r
169```\r
170\r
171The `&Framework` returned here is the handle to the Framework. If it's successfully returned, we can start adding\r
172test suites and test cases.\r
173\r
174Test suites exist purely to help organize test cases and to differentiate the results in reports. If you're writing\r
175a small unit test, you can conceivably put all test cases into a single suite. However, if you end up with 20+ test\r
176cases, it may be beneficial to organize them according to purpose. You _must_ have at least one test suite, even if\r
177it's just a catch-all. The function to create a test suite is `CreateUnitTestSuite`. It takes in a handle to\r
178the Framework object, a `CHAR8` string for the suite title and package name, and optional function pointers for\r
179a setup function and a teardown function.\r
180\r
181The suite title is for user presentation. The package name is for xUnit type reporting and uses a '.'-separated\r
182hierarchical format (see 'SampleUnitTestApp' for example). If provided, the setup and teardown functions will be\r
183called once at the start of the suite (before _any_ tests have run) and once at the end of the suite (after _all_\r
184tests have run), respectively. If either or both of these are unneeded, pass `NULL`. The function prototypes are\r
185`UNIT_TEST_SUITE_SETUP` and `UNIT_TEST_SUITE_TEARDOWN`.\r
186\r
187Looking at 'SampleUnitTestUefiShell' app, you can see that the first test suite is created as below...\r
188\r
189```c\r
190//\r
191// Populate the SimpleMathTests Unit Test Suite.\r
192//\r
193Status = CreateUnitTestSuite( &SimpleMathTests, Fw, "Simple Math Tests", "Sample.Math", NULL, NULL );\r
194```\r
195\r
196This test suite has no setup or teardown functions. The `&SimpleMathTests` returned here is a handle to the suite and\r
197will be used when adding test cases.\r
198\r
199Great! Now we've finished some of the cruft, red tape, and busy work. We're ready to add some tests. Adding a test\r
200to a test suite is accomplished with the -- you guessed it -- `AddTestCase` function. It takes in the suite handle;\r
201a `CHAR8` string for the description and class name; a function pointer for the test case itself; additional, optional\r
cef0c5c6 202function pointers for prerequisite check and cleanup routines; and an optional pointer to a context structure.\r
0f7fb5c5
MK
203\r
204Okay, that's a lot. Let's take it one piece at a time. The description and class name strings are very similar in\r
205usage to the suite title and package name strings in the test suites. The former is for user presentation and the\r
cef0c5c6 206latter is for xUnit parsing. The test case function pointer is what is executed as the "test" and the\r
0f7fb5c5
MK
207prototype should be `UNIT_TEST_FUNCTION`. The last three parameters require a little bit more explaining.\r
208\r
209The prerequisite check function has a prototype of `UNIT_TEST_PREREQUISITE` and -- if provided -- will be called\r
210immediately before the test case. If this function returns any error, the test case will not be run and will be\r
211recorded as `UNIT_TEST_ERROR_PREREQUISITE_NOT_MET`. The cleanup function (prototype `UNIT_TEST_CLEANUP`) will be called\r
212immediately after the test case to provide an opportunity to reset any global state that may have been changed in the\r
213test case. In the event of a prerequisite failure, the cleanup function will also be skipped. If either of these\r
214functions is not needed, pass `NULL`.\r
215\r
216The context pointer is entirely case-specific. It will be passed to the test case upon execution. One of the purposes\r
217of the context pointer is to allow test case reuse with different input data. (Another use is for testing that wraps\r
218around a system reboot, but that's beyond the scope of this guide.) The test case must know how to interpret the context\r
219pointer, so it could be a simple value, or it could be a complex structure. If unneeded, pass `NULL`.\r
220\r
221In 'SampleUnitTestUefiShell' app, the first test case is added using the code below...\r
222\r
223```c\r
224AddTestCase( SimpleMathTests, "Adding 1 to 1 should produce 2", "Addition", OnePlusOneShouldEqualTwo, NULL, NULL, NULL );\r
225```\r
226\r
227This test case calls the function `OnePlusOneShouldEqualTwo` and has no prerequisite, cleanup, or context.\r
228\r
229Once all the suites and cases are added, it's time to run the Framework.\r
230\r
231```c\r
232//\r
233// Execute the tests.\r
234//\r
235Status = RunAllTestSuites( Framework );\r
236```\r
237\r
cef0c5c6 238### Framework - A Simple Test Case\r
0f7fb5c5
MK
239\r
240We'll take a look at the below test case from 'SampleUnitTestApp'...\r
241\r
242```c\r
243UNIT_TEST_STATUS\r
244EFIAPI\r
245OnePlusOneShouldEqualTwo (\r
246 IN UNIT_TEST_FRAMEWORK_HANDLE Framework,\r
247 IN UNIT_TEST_CONTEXT Context\r
248 )\r
249{\r
250 UINTN A, B, C;\r
251\r
252 A = 1;\r
253 B = 1;\r
254 C = A + B;\r
255\r
256 UT_ASSERT_EQUAL(C, 2);\r
257 return UNIT_TEST_PASSED;\r
258} // OnePlusOneShouldEqualTwo()\r
259```\r
260\r
261The prototype for this function matches the `UNIT_TEST_FUNCTION` prototype. It takes in a handle to the Framework\r
262itself and the context pointer. The context pointer could be cast and interpreted as anything within this test case,\r
263which is why it's important to configure contexts carefully. The test case returns a value of `UNIT_TEST_STATUS`, which\r
264will be recorded in the Framework and reported at the end of all suites.\r
265\r
266In this test case, the `UT_ASSERT_EQUAL` assertion is being used to establish that the business logic has functioned\r
267correctly. There are several assertion macros, and you are encouraged to use one that matches as closely to your\r
268intended test criterium as possible, because the logging is specific to the macro and more specific macros have more\r
269detailed logs. When in doubt, there are always `UT_ASSERT_TRUE` and `UT_ASSERT_FALSE`. Assertion macros that fail their\r
270test criterium will immediately return from the test case with `UNIT_TEST_ERROR_TEST_FAILED` and log an error string.\r
271_Note_ that this early return can have implications for memory leakage.\r
272\r
273At the end, if all test criteria pass, you should return `UNIT_TEST_PASSED`.\r
274\r
cef0c5c6 275### Framework - More Complex Cases\r
0f7fb5c5 276\r
cef0c5c6 277To write more advanced tests, first look at all the Assertion and Logging macros provided in the framework.\r
0f7fb5c5
MK
278\r
279Beyond that, if you're writing host-based tests and want to take a dependency on the UnitTestFrameworkPkg, you can\r
280leverage the `cmocka.h` interface and write tests with all the features of the Cmocka framework.\r
281\r
282Documentation for Cmocka can be found here:\r
283https://api.cmocka.org/\r
284\r
cef0c5c6
MK
285## GoogleTest Samples\r
286\r
287There is a sample unit test provided as both an example of how to write a unit test and leverage\r
288many of the GoogleTest features. This sample can be found in the `Test/GoogleTest/Sample/SampleGoogleTest`\r
289directory.\r
290\r
291The sample is provided for the HOST_APPLICATION build type, which can be run on a host system without\r
292needing a target.\r
293\r
294## GoogleTest Usage\r
295\r
296This section is built a lot like a "Getting Started". We'll go through some of the components that are needed\r
297when constructing a unit test and some of the decisions that are made by the test writer. We'll also describe\r
298how to check for expected conditions in test cases and a bit of the logging characteristics.\r
299\r
300Most of these examples will refer to the SampleGoogleTestHost app found in this package.\r
301\r
302### GoogleTest Requirements - INF\r
303\r
304In our INF file, we'll need to bring in the `GoogleTest` library. Conveniently, the interface\r
305header for the `GoogleTest` is in `UnitTestFrameworkPkg`, so you shouldn't need to depend on any other\r
306packages. As long as your DSC file knows where to find the lib implementation that you want to use,\r
307you should be good to go.\r
308\r
309See this example in 'SampleGoogleTestHost.inf'...\r
310\r
311```\r
312[Packages]\r
313 MdePkg/MdePkg.dec\r
314 UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec\r
315\r
316[LibraryClasses]\r
317 GoogleTestLib\r
318 BaseLib\r
319 DebugLib\r
320```\r
321\r
322Also, if you want you test to automatically be picked up by the Test Runner plugin, you will need\r
323to make sure that the module `BASE_NAME` contains the word `Test`...\r
324\r
325```\r
326[Defines]\r
327 BASE_NAME = SampleGoogleTestHost\r
328```\r
329\r
330### GoogleTest Requirements - Code\r
331\r
332Not to state the obvious, but let's make sure we have the following include before getting too far along...\r
333\r
334```\r
335#include <gtest/gtest.h>\r
336extern "C" {\r
337 #include <Uefi.h>\r
338 #include <Library/BaseLib.h>\r
339 #include <Library/DebugLib.h>\r
340}\r
341```\r
342\r
343GoogleTest applications are implemented in C++. The first include brings in the\r
344GoogleTest definitions. Other EDK II related include files must be wrapped in\r
345`extern "C" {}` because they are C include files. Link failures will occur if\r
346this is not done.\r
347\r
348Now that we've got that squared away, let's look at our 'Main()'' routine (or DriverEntryPoint() or whatever).\r
349\r
350### GoogleTest Configuration\r
351\r
352Unlike the Framework, GoogleTest does not require test suites or test cases to\r
353be registered. Instead, the test cases declare the test suite name and test\r
354case name as part of their implementation. The only requirement for GoogleTest\r
355is to have a `main()` function that initialize the GoogleTest infrastructure and\r
356call the service `RUN_ALL_TESTS()` to run all the unit tests.\r
357\r
358```c\r
359int main(int argc, char* argv[]) {\r
360 testing::InitGoogleTest(&argc, argv);\r
361 return RUN_ALL_TESTS();\r
362}\r
363```\r
364\r
365### GoogleTest - A Simple Test Case\r
366\r
367We'll look at the below test case from 'SampleGoogleTestHost'...\r
368\r
369```c\r
370TEST(SimpleMathTests, OnePlusOneShouldEqualTwo) {\r
371 UINTN A;\r
372 UINTN B;\r
373 UINTN C;\r
374\r
375 A = 1;\r
376 B = 1;\r
377 C = A + B;\r
378\r
379 ASSERT_EQ (C, 2);\r
380}\r
381```\r
382\r
383This uses the simplest form of a GoogleTest unit test using `TEST()` that\r
384declares the test suite name and the unit test name within that test suite.\r
385The unit test performs actions and typically makes calls to the code under test\r
386and contains test assertions to verify that the code under test behaves as\r
387expected for the given inputs.\r
388\r
389In this test case, the `ASSERT_EQ` assertion is being used to establish that the business logic has functioned\r
390correctly. There are several assertion macros, and you are encouraged to use one that matches as closely to your\r
391intended test criterium as possible, because the logging is specific to the macro and more specific macros have more\r
392detailed logs. When in doubt, there are always `ASSERT_TRUE` and `ASSERT_FALSE`. Assertion macros that fail their\r
393test criterium will immediately return from the test case with a failed status and log an error string.\r
394_Note_ that this early return can have implications for memory leakage.\r
395\r
396There is no return status from a GooglTest unit test. If no assertions are\r
397triggered then the unit test has a passing status.\r
398\r
399### GoogleTest - More Complex Cases\r
400\r
401To write more advanced tests, take a look at the\r
402[GoogleTest User's Guide](http://google.github.io/googletest/).\r
403\r
0f7fb5c5
MK
404## Development\r
405\r
4698f544
BB
406### Iterating on a Single Test\r
407\r
4403bbd7
BB
408When using the EDK2 Pytools for CI testing, the host-based unit tests will be built and run on any build that includes\r
409the `NOOPT` build target.\r
0f7fb5c5 410\r
4403bbd7
BB
411If you are trying to iterate on a single test, a convenient pattern is to build only that test module. For example,\r
412the following command will build only the SafeIntLib host-based test from the MdePkg...\r
0f7fb5c5
MK
413\r
414```bash\r
415stuart_ci_build -c .pytool/CISettings.py TOOL_CHAIN_TAG=VS2017 -p MdePkg -t NOOPT BUILDMODULE=MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLib.inf\r
416```\r
417\r
4698f544
BB
418### Hooking BaseLib\r
419\r
cef0c5c6 420Most unit test mocking can be performed by the functions provided in the UnitTestFrameworkPkg libraries, but since\r
4698f544
BB
421BaseLib is consumed by the Framework itself, it requires different techniques to substitute parts of the\r
422functionality.\r
423\r
cef0c5c6 424To solve some of this, the UnitTestFrameworkPkg consumes a special implementation of BaseLib for host-based tests.\r
4698f544
BB
425This implementation contains a [hook table](https://github.com/tianocore/edk2/blob/e188ecc8b4aed8fdd26b731d43883861f5e5e7b4/MdePkg/Test/UnitTest/Include/Library/UnitTestHostBaseLib.h#L507)\r
426that can be used to substitute test functionality for any of the BaseLib functions. By default, this implementation\r
427will use the underlying BaseLib implementation, so the unit test writer only has to supply minimal code to test a\r
428particular case.\r
429\r
430### Debugging the Framework Itself\r
431\r
cef0c5c6 432While most of the tests that are produced by the UnitTestFrameworkPkg are easy to step through in a debugger, the Framework\r
4698f544
BB
433itself consumes code (mostly Cmocka) that sets its own build flags. These flags cause parts of the Framework to not\r
434export symbols and captures exceptions, and as such are harder to debug. We have provided a Stuart parameter to force\r
435symbolic debugging to be enabled.\r
436\r
437You can run a build by adding the `BLD_*_UNIT_TESTING_DEBUG=TRUE` parameter to enable this build option.\r
438\r
439```bash\r
440stuart_ci_build -c .pytool/CISettings.py TOOL_CHAIN_TAG=VS2019 -p MdePkg -t NOOPT BLD_*_UNIT_TESTING_DEBUG=TRUE\r
441```\r
442\r
443## Building and Running Host-Based Tests\r
444\r
445The EDK2 CI infrastructure provides a convenient way to run all host-based tests -- in the the entire tree or just\r
cef0c5c6
MK
446selected packages -- and aggregate all the reports, including highlighting any failures. This functionality is\r
447provided through the Stuart build system (published by EDK2-PyTools) and the `NOOPT` build target. The sections that\r
448follow use Framework examples. Unit tests based on GoogleTest are built and run the same way. The text output and\r
449JUNIT XML output format have small differences.\r
4698f544
BB
450\r
451### Building Locally\r
452\r
453First, to make sure you're working with the latest PyTools, run the following command:\r
454\r
455```bash\r
cef0c5c6 456# Would recommend running this in a Python venv, but that's out of scope for this doc.\r
4698f544
BB
457python -m pip install --upgrade -r ./pip-requirements.txt\r
458```\r
459\r
460After that, the following commands will set up the build and run the host-based tests.\r
461\r
462```bash\r
463# Setup repo for building\r
464# stuart_setup -c ./.pytool/CISettings.py TOOL_CHAIN_TAG=<GCC5, VS2019, etc.>\r
465stuart_setup -c ./.pytool/CISettings.py TOOL_CHAIN_TAG=VS2019\r
466\r
467# Update all binary dependencies\r
468# stuart_update -c ./.pytool/CISettings.py TOOL_CHAIN_TAG=<GCC5, VS2019, etc.>\r
469stuart_update -c ./.pytool/CISettings.py TOOL_CHAIN_TAG=VS2019\r
470\r
471# Build and run the tests\r
472# stuart_ci_build -c ./.pytool/CISettings.py TOOL_CHAIN_TAG=<GCC5, VS2019, etc.> -t NOOPT [-p <Package Name>]\r
473stuart_ci_build -c ./.pytool/CISettings.py TOOL_CHAIN_TAG=VS2019 -t NOOPT -p MdePkg\r
474```\r
475\r
476### Evaluating the Results\r
477\r
478In your immediate output, any build failures will be highlighted. You can see these below as "WARNING" and "ERROR" messages.\r
479\r
480```text\r
481(edk_env) PS C:\_uefi\edk2> stuart_ci_build -c .\.pytool\CISettings.py TOOL_CHAIN_TAG=VS2019 -t NOOPT -p MdePkg\r
482\r
483SECTION - Init SDE\r
484SECTION - Loading Plugins\r
485SECTION - Start Invocable Tool\r
486SECTION - Getting Environment\r
487SECTION - Loading plugins\r
488SECTION - Building MdePkg Package\r
489PROGRESS - --Running MdePkg: Host Unit Test Compiler Plugin NOOPT --\r
490WARNING - Allowing Override for key TARGET_ARCH\r
491PROGRESS - Start time: 2020-07-27 17:18:08.521672\r
492PROGRESS - Setting up the Environment\r
493PROGRESS - Running Pre Build\r
494PROGRESS - Running Build NOOPT\r
495PROGRESS - Running Post Build\r
496SECTION - Run Host based Unit Tests\r
497SUBSECTION - Testing for architecture: X64\r
498WARNING - TestBaseSafeIntLibHost.exe Test Failed\r
499WARNING - Test SafeInt8ToUint8 - UT_ASSERT_EQUAL(0x5b:5b, Result:5c)\r
500c:\_uefi\edk2\MdePkg\Test\UnitTest\Library\BaseSafeIntLib\TestBaseSafeIntLib.c:35: error: Failure!\r
501ERROR - Plugin Failed: Host-Based Unit Test Runner returned 1\r
502CRITICAL - Post Build failed\r
503PROGRESS - End time: 2020-07-27 17:18:19.792313 Total time Elapsed: 0:00:11\r
504ERROR - --->Test Failed: Host Unit Test Compiler Plugin NOOPT returned 1\r
505ERROR - Overall Build Status: Error\r
506PROGRESS - There were 1 failures out of 1 attempts\r
507SECTION - Summary\r
508ERROR - Error\r
509\r
510(edk_env) PS C:\_uefi\edk2>\r
511```\r
512\r
513If a test fails, you can run it manually to get more details...\r
514\r
515```text\r
516(edk_env) PS C:\_uefi\edk2> .\Build\MdePkg\HostTest\NOOPT_VS2019\X64\TestBaseSafeIntLibHost.exe\r
517\r
518Int Safe Lib Unit Test Application v0.1\r
519---------------------------------------------------------\r
520------------ RUNNING ALL TEST SUITES --------------\r
521---------------------------------------------------------\r
522---------------------------------------------------------\r
523RUNNING TEST SUITE: Int Safe Conversions Test Suite\r
524---------------------------------------------------------\r
525[==========] Running 71 test(s).\r
526[ RUN ] Test SafeInt8ToUint8\r
527[ ERROR ] --- UT_ASSERT_EQUAL(0x5b:5b, Result:5c)\r
528[ LINE ] --- c:\_uefi\edk2\MdePkg\Test\UnitTest\Library\BaseSafeIntLib\TestBaseSafeIntLib.c:35: error: Failure!\r
529[ FAILED ] Test SafeInt8ToUint8\r
530[ RUN ] Test SafeInt8ToUint16\r
531[ OK ] Test SafeInt8ToUint16\r
532[ RUN ] Test SafeInt8ToUint32\r
533[ OK ] Test SafeInt8ToUint32\r
534[ RUN ] Test SafeInt8ToUintn\r
535[ OK ] Test SafeInt8ToUintn\r
536...\r
537```\r
538\r
539You can also, if you are so inclined, read the output from the exact instance of the test that was run during\r
cef0c5c6 540`stuart_ci_build`. The output file can be found on a path that looks like:\r
4698f544
BB
541\r
542`Build/<Package>/HostTest/<Arch>/<TestName>.<TestSuiteName>.<Arch>.result.xml`\r
543\r
544A sample of this output looks like:\r
545\r
546```xml\r
547<!--\r
548 Excerpt taken from:\r
549 Build\MdePkg\HostTest\NOOPT_VS2019\X64\TestBaseSafeIntLibHost.exe.Int Safe Conversions Test Suite.X64.result.xml\r
550 -->\r
551<?xml version="1.0" encoding="UTF-8" ?>\r
552<testsuites>\r
553 <testsuite name="Int Safe Conversions Test Suite" time="0.000" tests="71" failures="1" errors="0" skipped="0" >\r
554 <testcase name="Test SafeInt8ToUint8" time="0.000" >\r
555 <failure><![CDATA[UT_ASSERT_EQUAL(0x5c:5c, Result:5b)\r
556c:\_uefi\MdePkg\Test\UnitTest\Library\BaseSafeIntLib\TestBaseSafeIntLib.c:35: error: Failure!]]></failure>\r
557 </testcase>\r
558 <testcase name="Test SafeInt8ToUint16" time="0.000" >\r
559 </testcase>\r
560 <testcase name="Test SafeInt8ToUint32" time="0.000" >\r
561 </testcase>\r
562 <testcase name="Test SafeInt8ToUintn" time="0.000" >\r
563 </testcase>\r
564```\r
565\r
566### XML Reporting Mode\r
567\r
cef0c5c6
MK
568Unit test applications using Framework are built using Cmocka that requires the\r
569following environment variables to be set to generate structured XML output\r
570rather than text:\r
4698f544 571\r
cef0c5c6 572```\r
4698f544
BB
573CMOCKA_MESSAGE_OUTPUT=xml\r
574CMOCKA_XML_FILE=<absolute or relative path to output file>\r
575```\r
576\r
cef0c5c6
MK
577Unit test applications using GoogleTest require the following environment\r
578variable to be set to generate structured XML output rather than text:\r
579\r
580```\r
581GTEST_OUTPUT=xml:<absolute or relative path to output file>\r
582```\r
583\r
4698f544
BB
584This mode is used by the test running plugin to aggregate the results for CI test status reporting in the web view.\r
585\r
3701f105
GG
586### Code Coverage\r
587\r
588Host based Unit Tests will automatically enable coverage data.\r
589\r
590For Windows, This is primarily leverage for pipeline builds, but this can be leveraged locally using the\r
591OpenCppCoverage windows tool to parse coverage data to cobertura xml format.\r
592\r
593- Windows Prerequisite\r
594 ```bash\r
595 Download and install https://github.com/OpenCppCoverage/OpenCppCoverage/releases\r
596 python -m pip install --upgrade -r ./pip-requirements.txt\r
597 stuart_ci_build -c .pytool/CISettings.py -t NOOPT TOOL_CHAIN_TAG=VS2019 -p MdeModulePkg\r
598 Open Build/coverage.xml\r
599 ```\r
600\r
601 - How to see code coverage data on IDE Visual Studio\r
602 ```\r
603 Open Visual Studio VS2019 or above version\r
604 Click "Tools" -> "OpenCppCoverage Settings"\r
605 Fill your execute file into "Program to run:"\r
606 Click "Tools" -> "Run OpenCppCoverage"\r
607 ```\r
608\r
609\r
610For Linux, This is primarily leveraged for pipeline builds, but this can be leveraged locally using the\r
611lcov linux tool, and parsed using the lcov_cobertura python tool to parse it to cobertura xml format.\r
612\r
613- Linux Prerequisite\r
614 ```bash\r
615 sudo apt-get install -y lcov\r
616 python -m pip install --upgrade -r ./pip-requirements.txt\r
617 stuart_ci_build -c .pytool/CISettings.py -t NOOPT TOOL_CHAIN_TAG=GCC5 -p MdeModulePkg\r
618 Open Build/coverage.xml\r
619 ```\r
620 - How to see code coverage data on IDE Visual Studio Code\r
621 ```\r
622 Download plugin "Coverage Gutters"\r
623 Press Hot Key "Ctrl + Shift + P" and click option "Coverage Gutters: Display Coverage"\r
624 ```\r
625\r
626\r
4698f544
BB
627### Important Note\r
628\r
cef0c5c6 629This works on both Windows and Linux but is currently limited to x64 architectures. Working on getting others, but we\r
4698f544
BB
630also welcome contributions.\r
631\r
cef0c5c6 632## Framework Known Limitations\r
0f7fb5c5
MK
633\r
634### PEI, DXE, SMM\r
635\r
636While sample tests have been provided for these execution environments, only cursory build validation\r
637has been performed. Care has been taken while designing the frameworks to allow for execution during\r
638boot phases, but only UEFI Shell and host-based tests have been thoroughly evaluated. Full support for\r
639PEI, DXE, and SMM is forthcoming, but should be considered beta/staging for now.\r
640\r
641### Host-Based Support vs Other Tests\r
642\r
643The host-based test framework is powered internally by the Cmocka framework. As such, it has abilities\r
644that the target-based tests don't (yet). It would be awesome if this meant that it was a super set of\r
645the target-based tests, and it worked just like the target-based tests but with more features. Unfortunately,\r
cef0c5c6 646this is not the case. While care has been taken to keep them as close as possible, there are a few known\r
0f7fb5c5
MK
647inconsistencies that we're still ironing out. For example, the logging messages in the target-based tests\r
648are cached internally and associated with the running test case. They can be saved later as part of the\r
649reporting lib. This isn't currently possible with host-based. Only the assertion failures are logged.\r
650\r
651We will continue trying to make these as similar as possible.\r
652\r
4403bbd7
BB
653## Unit Test Location/Layout Rules\r
654\r
655Code/Test | Location\r
656--------- | --------\r
657Host-Based Unit Tests for a Library/Protocol/PPI/GUID Interface | If what's being tested is an interface (e.g. a library with a public header file, like DebugLib), the test should be scoped to the parent package.<br/>Example: `MdePkg/Test/UnitTest/[Library/Protocol/Ppi/Guid]/`<br/><br/>A real-world example of this is the BaseSafeIntLib test in MdePkg.<br/>`MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibHost.inf`\r
658Host-Based Unit Tests for a Library/Driver (PEI/DXE/SMM) implementation | If what's being tested is a specific implementation (e.g. BaseDebugLibSerialPort for DebugLib), the test should be scoped to the implementation directory itself, in a UnitTest subdirectory.<br/><br/>Module Example: `MdeModulePkg/Universal/EsrtFmpDxe/UnitTest/`<br/>Library Example: `MdePkg/Library/BaseMemoryLib/UnitTest/`\r
659Host-Based Tests for a Functionality or Feature | If you're writing a functional test that operates at the module level (i.e. if it's more than a single file or library), the test should be located in the package-level Tests directory under the HostFuncTest subdirectory.<br/>For example, if you were writing a test for the entire FMP Device Framework, you might put your test in:<br/>`FmpDevicePkg/Test/HostFuncTest/FmpDeviceFramework`<br/><br/>If the feature spans multiple packages, it's location should be determined by the package owners related to the feature.\r
660Non-Host-Based (PEI/DXE/SMM/Shell) Tests for a Functionality or Feature | Similar to Host-Based, if the feature is in one package, should be located in the `*Pkg/Test/[Shell/Dxe/Smm/Pei]Test` directory.<br/><br/>If the feature spans multiple packages, it's location should be determined by the package owners related to the feature.<br/><br/>USAGE EXAMPLES<br/>PEI Example: MP_SERVICE_PPI. Or check MTRR configuration in a notification function.<br/> SMM Example: a test in a protocol callback function. (It is different with the solution that SmmAgent+ShellApp)<br/>DXE Example: a test in a UEFI event call back to check SPI/SMRAM status. <br/> Shell Example: the SMM handler audit test has a shell-based app that interacts with an SMM handler to get information. The SMM paging audit test gathers information about both DXE and SMM. And the SMM paging functional test actually forces errors into SMM via a DXE driver.\r
661\r
662### Example Directory Tree\r
663\r
664```text\r
665<PackageName>Pkg/\r
666 ComponentY/\r
667 ComponentY.inf\r
668 ComponentY.c\r
cef0c5c6
MK
669 GoogleTest/\r
670 ComponentYHostGoogleTest.inf # Host-Based Test for Driver Module\r
671 ComponentYGoogleTest.cpp\r
4403bbd7
BB
672 UnitTest/\r
673 ComponentYHostUnitTest.inf # Host-Based Test for Driver Module\r
674 ComponentYUnitTest.c\r
675\r
676 Library/\r
677 GeneralPurposeLibBase/\r
678 ...\r
679\r
680 GeneralPurposeLibSerial/\r
681 ...\r
682\r
683 SpecificLibDxe/\r
684 SpecificLibDxe.c\r
685 SpecificLibDxe.inf\r
cef0c5c6
MK
686 GoogleTest/ # Host-Based Test for Specific Library Implementation\r
687 SpecificLibDxeHostGoogleTest.cpp\r
688 SpecificLibDxeHostGoogleTest.inf\r
4403bbd7
BB
689 UnitTest/ # Host-Based Test for Specific Library Implementation\r
690 SpecificLibDxeHostUnitTest.c\r
691 SpecificLibDxeHostUnitTest.inf\r
692 Test/\r
693 <Package>HostTest.dsc # Host-Based Test Apps\r
cef0c5c6
MK
694 GoogleTest/\r
695 InterfaceX\r
696 InterfaceXHostGoogleTest.inf # Host-Based App (should be in Test/<Package>HostTest.dsc)\r
697 InterfaceXUnitTest.cpp # Test Logic\r
698\r
699 GeneralPurposeLib/ # Host-Based Test for any implementation of GeneralPurposeLib\r
700 GeneralPurposeLibTest.cpp\r
701 GeneralPurposeLibHostUnitTest.inf\r
702\r
4403bbd7
BB
703 UnitTest/\r
704 InterfaceX\r
705 InterfaceXHostUnitTest.inf # Host-Based App (should be in Test/<Package>HostTest.dsc)\r
706 InterfaceXPeiUnitTest.inf # PEIM Target-Based Test (if applicable)\r
707 InterfaceXDxeUnitTest.inf # DXE Target-Based Test (if applicable)\r
708 InterfaceXSmmUnitTest.inf # SMM Target-Based Test (if applicable)\r
709 InterfaceXShellUnitTest.inf # Shell App Target-Based Test (if applicable)\r
710 InterfaceXUnitTest.c # Test Logic\r
711\r
712 GeneralPurposeLib/ # Host-Based Test for any implementation of GeneralPurposeLib\r
713 GeneralPurposeLibTest.c\r
714 GeneralPurposeLibHostUnitTest.inf\r
715\r
716 <Package>Pkg.dsc # Standard Modules and any Target-Based Test Apps (including in Test/)\r
717\r
718```\r
719\r
720### Future Locations in Consideration\r
721\r
722We don't know if these types will exist or be applicable yet, but if you write a support library or module that matches the following, please make sure they live in the correct place.\r
723\r
724Code/Test | Location\r
725--------- | --------\r
726Host-Based Library Implementations | Host-Based Implementations of common libraries (eg. MemoryAllocationLibHost) should live in the same package that declares the library interface in its .DEC file in the `*Pkg/HostLibrary` directory. Should have 'Host' in the name.\r
727Host-Based Mocks and Stubs | Mock and Stub libraries should live in the `UefiTestFrameworkPkg/StubLibrary` with either 'Mock' or 'Stub' in the library name.\r
728\r
729### If still in doubt...\r
730\r
731Hop on GitHub and ask @corthon, @mdkinney, or @spbrogan. ;)\r
732\r
0f7fb5c5
MK
733## Copyright\r
734\r
735Copyright (c) Microsoft Corporation.\r
736SPDX-License-Identifier: BSD-2-Clause-Patent\r