From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from walk.intel-email.com (walk.intel-email.com [101.227.64.242]) by mx.groups.io with SMTP id smtpd.web11.5325.1667893641972510785 for ; Mon, 07 Nov 2022 23:47:25 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@byosoft.com.cn header.s=cloud-union header.b=X1BTmLXJ; spf=pass (domain: byosoft.com.cn, ip: 101.227.64.242, mailfrom: gaoliming@byosoft.com.cn) Received: from walk.intel-email.com (localhost [127.0.0.1]) by walk.intel-email.com (Postfix) with ESMTP id 4CE3BCD1F6ED for ; Tue, 8 Nov 2022 15:47:19 +0800 (CST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=byosoft.com.cn; s=cloud-union; t=1667893639; bh=GEUOA0XunGlZ1Pj2SG39tBIZoIx4iFz8evBp0YyuKiM=; h=From:To:Cc:References:In-Reply-To:Subject:Date; b=X1BTmLXJEiOEgUMTKnhSzIPTl7KqqgHTD/Y6FhAHb36m54Cu4Z4A3K9UZTBHp7kpw whldVZ6fxFL2OWndGJOFqoevslZb+FWmtsN0nOCLwwxapGYtZrhOghG3ryLquiiIRd S7s0ej7L8stoU3dW2Q0mrHR2hCVsUscw9Z5VvDvI= Received: from localhost (localhost [127.0.0.1]) by walk.intel-email.com (Postfix) with ESMTP id 48804CD1F6C6 for ; Tue, 8 Nov 2022 15:47:19 +0800 (CST) Received: from walk.intel-email.com (localhost [127.0.0.1]) by walk.intel-email.com (Postfix) with ESMTP id 0CB75CD1F683 for ; Tue, 8 Nov 2022 15:47:19 +0800 (CST) Authentication-Results: walk.intel-email.com; none Received: from mail.byosoft.com.cn (mail.byosoft.com.cn [58.240.74.242]) by walk.intel-email.com (Postfix) with SMTP id 90E9ACD1F713 for ; Tue, 8 Nov 2022 15:47:16 +0800 (CST) Received: from DESKTOPS6D0PVI ([58.246.60.130]) (envelope-sender ) by 192.168.6.13 with ESMTP for ; Tue, 08 Nov 2022 15:47:15 +0800 X-WM-Sender: gaoliming@byosoft.com.cn X-Originating-IP: 58.246.60.130 X-WM-AuthFlag: YES X-WM-AuthUser: gaoliming@byosoft.com.cn From: "gaoliming" To: , Cc: "'Michael Kubacki'" , "'Sean Brogan'" References: <20221104202528.1157-1-michael.d.kinney@intel.com> <20221104202528.1157-4-michael.d.kinney@intel.com> In-Reply-To: <20221104202528.1157-4-michael.d.kinney@intel.com> Subject: =?UTF-8?B?5Zue5aSNOiBbZWRrMi1kZXZlbF0gW1BhdGNoIFYyIDMvN10gVW5pdFRlc3RGcmFtZXdvcmtQa2c6IEFkZCBnb29nbGV0ZXN0IHN1Ym1vZHVsZSBhbmQgR29vZ2xlVGVzdExpYg==?= Date: Tue, 8 Nov 2022 15:47:15 +0800 Message-ID: <03b001d8f346$5247ff20$f6d7fd60$@byosoft.com.cn> MIME-Version: 1.0 X-Mailer: Microsoft Outlook 16.0 Thread-Index: AQFH1jmsDKlCXelHkMPmoRIYImxWkgIoBgEmr0Wf1pA= Sender: "gaoliming" Content-Type: text/plain; charset="gb2312" Content-Transfer-Encoding: quoted-printable Content-Language: zh-cn Mike: For new submodule, I think it is also required to be mentioned in License Details section in Edk2\ReadMe.rst Thanks Liming > -----=D3=CA=BC=FE=D4=AD=BC=FE----- > =B7=A2=BC=FE=C8=CB: devel@edk2.groups.io =B4=FA=B1= =ED Michael D > Kinney > =B7=A2=CB=CD=CA=B1=BC=E4: 2022=C4=EA11=D4=C25=C8=D5 4:25 > =CA=D5=BC=FE=C8=CB: devel@edk2.groups.io > =B3=AD=CB=CD: Michael Kubacki ; Sean Brogan > > =D6=F7=CC=E2: [edk2-devel] [Patch V2 3/7] UnitTestFrameworkPkg: Add googl= etest > submodule and GoogleTestLib >=20 > REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3D4134 >=20 > Add submodule for googletest and add GoogleTestLib that is > required for GoogleTest based unit tests. Add GoogleTest > documentation to Readme.md along with a port of the sample > unit test to the GoogleTest style. >=20 > Cc: Michael Kubacki > Cc: Sean Brogan > Signed-off-by: Michael D Kinney > Reviewed-by: Michael Kubacki > --- > .gitmodules | 3 + > .../Include/Library/GoogleTestLib.h | 14 + > .../Library/GoogleTestLib/GoogleTestLib.inf | 36 +++ > .../Library/GoogleTestLib/GoogleTestLib.uni | 14 + > .../Library/GoogleTestLib/googletest | 1 + > UnitTestFrameworkPkg/ReadMe.md | 255 > +++++++++++++++-- > .../SampleGoogleTest/SampleGoogleTest.cpp | 263 > ++++++++++++++++++ > .../SampleGoogleTest/SampleGoogleTestHost.inf | 35 +++ > .../Test/UnitTestFrameworkPkgHostTest.dsc | 4 +- > .../UnitTestFrameworkPkg.ci.yaml | 4 +- > UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec | 8 + > .../UnitTestFrameworkPkgHost.dsc.inc | 4 +- > 12 files changed, 610 insertions(+), 31 deletions(-) > create mode 100644 > UnitTestFrameworkPkg/Include/Library/GoogleTestLib.h > create mode 100644 > UnitTestFrameworkPkg/Library/GoogleTestLib/GoogleTestLib.inf > create mode 100644 > UnitTestFrameworkPkg/Library/GoogleTestLib/GoogleTestLib.uni > create mode 160000 > UnitTestFrameworkPkg/Library/GoogleTestLib/googletest > create mode 100644 > UnitTestFrameworkPkg/Test/GoogleTest/Sample/SampleGoogleTest/SampleG > oogleTest.cpp > create mode 100644 > UnitTestFrameworkPkg/Test/GoogleTest/Sample/SampleGoogleTest/SampleG > oogleTestHost.inf >=20 > diff --git a/.gitmodules b/.gitmodules > index b845c9ee3ff0..8011a88d9d25 100644 > --- a/.gitmodules > +++ b/.gitmodules > @@ -20,3 +20,6 @@ > [submodule "RedfishPkg/Library/JsonLib/jansson"] > path =3D RedfishPkg/Library/JsonLib/jansson > url =3D https://github.com/akheron/jansson > +[submodule "UnitTestFrameworkPkg/Library/GoogleTestLib/googletest"] > + path =3D UnitTestFrameworkPkg/Library/GoogleTestLib/googletest > + url =3D https://github.com/google/googletest.git > diff --git a/UnitTestFrameworkPkg/Include/Library/GoogleTestLib.h > b/UnitTestFrameworkPkg/Include/Library/GoogleTestLib.h > new file mode 100644 > index 000000000000..ebec766d4cf7 > --- /dev/null > +++ b/UnitTestFrameworkPkg/Include/Library/GoogleTestLib.h > @@ -0,0 +1,14 @@ > +/** @file > + GoogleTestLib class with APIs from the googletest project > + > + Copyright (c) 2022, Intel Corporation. All rights reserved.
> + SPDX-License-Identifier: BSD-2-Clause-Patent > + > +**/ > + > +#ifndef GOOGLE_TEST_LIB_H_ > +#define GOOGLE_TEST_LIB_H_ > + > +#include > + > +#endif > diff --git a/UnitTestFrameworkPkg/Library/GoogleTestLib/GoogleTestLib.inf > b/UnitTestFrameworkPkg/Library/GoogleTestLib/GoogleTestLib.inf > new file mode 100644 > index 000000000000..68db75d7023f > --- /dev/null > +++ b/UnitTestFrameworkPkg/Library/GoogleTestLib/GoogleTestLib.inf > @@ -0,0 +1,36 @@ > +## @file > +# This module provides GoogleTest Library implementation. > +# > +# Copyright (c) 2022, Intel Corporation. All rights reserved.
> +# SPDX-License-Identifier: BSD-2-Clause-Patent > +# > +## > + > +[Defines] > + INF_VERSION =3D 0x00010005 > + BASE_NAME =3D GoogleTestLib > + MODULE_UNI_FILE =3D GoogleTestLib.uni > + FILE_GUID =3D A90E4751-AD30-43CC-980B-01E356B49ADF > + MODULE_TYPE =3D BASE > + VERSION_STRING =3D 0.1 > + LIBRARY_CLASS =3D GoogleTestLib|HOST_APPLICATION > + > +# > +# VALID_ARCHITECTURES =3D IA32 X64 ARM AARCH64 > +# > + > +[Sources] > + googletest/googletest/src/gtest-all.cc > + > +[Packages] > + UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec > + > +[BuildOptions] > + MSFT:*_*_*_CC_FLAGS =3D=3D /c /EHsc /Zi > + MSFT:NOOPT_*_*_CC_FLAGS =3D /Od > + > + GCC:*_*_*_CC_FLAGS =3D=3D -g -c > + > + GCC:NOOPT_*_*_CC_FLAGS =3D -O0 > + GCC:*_*_IA32_CC_FLAGS =3D -m32 > + GCC:*_*_X64_CC_FLAGS =3D -m64 > diff --git a/UnitTestFrameworkPkg/Library/GoogleTestLib/GoogleTestLib.uni > b/UnitTestFrameworkPkg/Library/GoogleTestLib/GoogleTestLib.uni > new file mode 100644 > index 000000000000..14c862a23744 > --- /dev/null > +++ b/UnitTestFrameworkPkg/Library/GoogleTestLib/GoogleTestLib.uni > @@ -0,0 +1,14 @@ > +// /** @file > +// This module provides GoogleTest Library implementation. > +// > +// This module provides GoogleTest Library implementation. > +// > +// Copyright (c) 2022, Intel Corporation. All rights reserved.
> +// > +// SPDX-License-Identifier: BSD-2-Clause-Patent > +// > +// **/ > + > +#string STR_MODULE_ABSTRACT #language en-US > "GoogleTest Library implementation" > + > +#string STR_MODULE_DESCRIPTION #language en-US "This > module provides GoogleTest Library implementation." > diff --git a/UnitTestFrameworkPkg/Library/GoogleTestLib/googletest > b/UnitTestFrameworkPkg/Library/GoogleTestLib/googletest > new file mode 160000 > index 000000000000..86add13493e5 > --- /dev/null > +++ b/UnitTestFrameworkPkg/Library/GoogleTestLib/googletest > @@ -0,0 +1 @@ > +Subproject commit 86add13493e5c881d7e4ba77fb91c1f57752b3a4 > diff --git a/UnitTestFrameworkPkg/ReadMe.md > b/UnitTestFrameworkPkg/ReadMe.md > index e696412cb3cf..9ce04b7f3eb6 100644 > --- a/UnitTestFrameworkPkg/ReadMe.md > +++ b/UnitTestFrameworkPkg/ReadMe.md > @@ -2,12 +2,67 @@ >=20 > ## About >=20 > -This package adds a unit test framework capable of building tests for multiple > contexts including > +This package provides unit test frameworks capable of building tests for > multiple contexts including > the UEFI shell environment and host-based environments. It allows for unit > test development to focus > -on the tests and leave error logging, result formatting, context persistance, > and test running to the framework. > +on the tests and leave error logging, result formatting, context persistence, > and test running to the framework. > The unit test framework works well for low level unit tests as well as system > level tests and > fits easily in automation frameworks. >=20 > +### Framework > + > +The first unit test framework is called **Framework** and is implemented > as a set of EDK II libraries. > +The Framework supports both host-based unit tests and target-based unit > tests that share the same > +source style, macros, and APIs. In some scenarios, the same unit test case > sources can be built > +for both host-based unit test execution and target-based unit test execution. > Host-based unit tests > +that require mocked interfaces can use the mocking infrastructure provided > by > +[cmocka](https://api.cmocka.org/) that is included in the > UnitTestFrameworkPkg as a submodule. > + > +### GoogleTest > + > +The second unit test framework supported by the UnitTestFrameworkPkg is > +[GoogleTest](http://google.github.io/googletest/) that can be used to > implement host-based unit tests. > +Use of GoogleTest for target-based unit tests of EDK II components is no= t > supported. If a > +host-based unit test requires mocked interfaces, then the Framework with > cmocka support should be > +used instead. Enabling support for mocked interfaces with GoogleTest is > being actively investigated. > +[GoogleTest on GitHub](https://github.com/google/googletest) is included in > the UnitTestFrameworkPkg > +as a submodule. > + > +GoogleTest requires less overhead to register test suites and test cases > compared to the Framework. > +There are also a number of tools that layer on top of GoogleTest that improve > developer productivity. > +One example is the VS Code extension > +[C++ > TestMate](https://marketplace.visualstudio.com/items?itemName=3Dmatepek. > vscode-catch2-test-adapter) > +that may be used to implement, run, and debug unit tests implemented using > GoogleTest. > + > +If a component can be tested with host-based unit tests without support for > mocked interfaces, > +then GoogleTest is recommended. The MdePkg contains a port of the > BaseSafeIntLib unit tests in > +the GoogleTest style so the differences between GoogleTest and Framework > unit tests can be reviewed. > +The paths to the BaseSafeIntLib unit tests are: > + > +* MdePkg\Test\UnitTest\Library\BaseSafeIntLib > +* MdePkg\Test\GoogleTest\Library\BaseSafeIntLib > + > +## Framework and GoogleTest Feature Comparison > + > +| Feature | Framework | GoogleTest | > +|:----------------------------|:---------:|:----------:| > +| Host Based Unit Tests | YES | YES | > +| Target Based Unit Tests | YES | NO | > +| Unit Test Source Language | C | C++ | > +| Register Test Suite | YES | Auto | > +| Register Test Case | YES | Auto | > +| Death/Expected Assert Tests | YES | YES | > +| Setup/Teardown Hooks | YES | YES | > +| Value-Parameterized Tests | NO | YES | > +| Typed Tests | NO | YES | > +| Type-Parameterized Tests | NO | YES | > +| Timeout Support | NO | YES | > +| Mocking Support | Cmocka | NO | > +| JUNIT XML Reports | YES | YES | > +| Execute subset of tests | NO | YES | > +| VS Code Extensions | NO | YES | > + > +## Framework Libraries > + > ### UnitTestLib >=20 > The main "framework" library. The core of the framework is the Framework > object, which can have any number > @@ -31,10 +86,10 @@ in supporting a system reboot in the middle of a test > run. >=20 > Library provides function to run at the end of a framework test run and > handles formatting the report. > This is a common customization point and allows the unit test framework to > fit its output reports into > -other test infrastructure. In this package a simple library instances ha= s been > supplied to output test > +other test infrastructure. In this package simple library instances have been > supplied to output test > results to the console as plain text. >=20 > -## Samples > +## Framework Samples >=20 > There is a sample unit test provided as both an example of how to write = a > unit test and leverage > many of the features of the framework. This sample can be found in the > `Test/UnitTest/Sample/SampleUnitTest` > @@ -43,7 +98,7 @@ directory. > The sample is provided in PEI, SMM, DXE, and UEFI App flavors. It also has a > flavor for the HOST_APPLICATION > build type, which can be run on a host system without needing a target. >=20 > -## Usage > +## Framework Usage >=20 > This section is built a lot like a "Getting Started". We'll go through some of > the components that are needed > when constructing a unit test and some of the decisions that are made by > the test writer. We'll also describe > @@ -51,7 +106,7 @@ how to check for expected conditions in test cases and > a bit of the logging char >=20 > Most of these examples will refer to the SampleUnitTestUefiShell app found > in this package. >=20 > -### Requirements - INF > +### Framework Requirements - INF >=20 > In our INF file, we'll need to bring in the `UnitTestLib` library. Conveniently, > the interface > header for the `UnitTestLib` is located in `MdePkg`, so you shouldn't need to > depend on any other > @@ -80,7 +135,7 @@ to make sure that the module `BASE_NAME` contains > the word `Test`... > BASE_NAME =3D SampleUnitTestUefiShell > ``` >=20 > -### Requirements - Code > +### Framework Requirements - Code >=20 > Not to state the obvious, but let's make sure we have the following include > before getting too far along... >=20 > @@ -90,9 +145,9 @@ Not to state the obvious, but let's make sure we have > the following include befo >=20 > Now that we've got that squared away, let's look at our 'Main()'' routin= e (or > DriverEntryPoint() or whatever). >=20 > -### Configuring the Framework > +### Framework Configuration >=20 > -Everything in the UnitTestPkg framework is built around an object called -- > conveniently -- the Framework. > +Everything in the UnitTestFrameworkPkg framework is built around an object > called -- conveniently -- the Framework. > This Framework object will contain all the information about our test, the > test suites and test cases associated > with it, the current location within the test pass, and any results that have > been recorded so far. >=20 > @@ -102,7 +157,7 @@ The long name and version strings are just for user > presentation and relatively > will be used to name any cache files and/or test results, so should be a name > that makes sense in that context. > These strings are copied internally to the Framework, so using > stack-allocated or literal strings is fine. >=20 > -In the 'SampleUnitTestUefiShell' app, the module name is used as the short > name, so the init looks like this. > +In the 'SampleUnitTestUefiShell' app, the module name is used as the short > name, so the initialization looks like this. >=20 > ```c > DEBUG(( DEBUG_INFO, "%a v%a\n", UNIT_TEST_APP_NAME, > UNIT_TEST_APP_VERSION )); > @@ -144,11 +199,11 @@ will be used when adding test cases. > Great! Now we've finished some of the cruft, red tape, and busy work. We're > ready to add some tests. Adding a test > to a test suite is accomplished with the -- you guessed it -- `AddTestCase` > function. It takes in the suite handle; > a `CHAR8` string for the description and class name; a function pointer for > the test case itself; additional, optional > -function pointers for prerequisite check and cleanup routines; and and > optional pointer to a context structure. > +function pointers for prerequisite check and cleanup routines; and an > optional pointer to a context structure. >=20 > Okay, that's a lot. Let's take it one piece at a time. The description and class > name strings are very similar in > usage to the suite title and package name strings in the test suites. Th= e > former is for user presentation and the > -latter is for xUnit parsing. The test case function pointer is what is actually > executed as the "test" and the > +latter is for xUnit parsing. The test case function pointer is what is executed > as the "test" and the > prototype should be `UNIT_TEST_FUNCTION`. The last three parameters > require a little bit more explaining. >=20 > The prerequisite check function has a prototype of > `UNIT_TEST_PREREQUISITE` and -- if provided -- will be called > @@ -180,7 +235,7 @@ Once all the suites and cases are added, it's time to > run the Framework. > Status =3D RunAllTestSuites( Framework ); > ``` >=20 > -### A Simple Test Case > +### Framework - A Simple Test Case >=20 > We'll take a look at the below test case from 'SampleUnitTestApp'... >=20 > @@ -217,9 +272,9 @@ _Note_ that this early return can have implications for > memory leakage. >=20 > At the end, if all test criteria pass, you should return `UNIT_TEST_PASSED`. >=20 > -### More Complex Cases > +### Framework - More Complex Cases >=20 > -To write more advanced tests, first take a look at all the Assertion and > Logging macros provided in the framework. > +To write more advanced tests, first look at all the Assertion and Loggin= g > macros provided in the framework. >=20 > Beyond that, if you're writing host-based tests and want to take a > dependency on the UnitTestFrameworkPkg, you can > leverage the `cmocka.h` interface and write tests with all the features of the > Cmocka framework. > @@ -227,6 +282,125 @@ leverage the `cmocka.h` interface and write tests > with all the features of the C > Documentation for Cmocka can be found here: > https://api.cmocka.org/ >=20 > +## GoogleTest Samples > + > +There is a sample unit test provided as both an example of how to write = a > unit test and leverage > +many of the GoogleTest features. This sample can be found in the > `Test/GoogleTest/Sample/SampleGoogleTest` > +directory. > + > +The sample is provided for the HOST_APPLICATION build type, which can be > run on a host system without > +needing a target. > + > +## GoogleTest Usage > + > +This section is built a lot like a "Getting Started". We'll go through some of > the components that are needed > +when constructing a unit test and some of the decisions that are made by > the test writer. We'll also describe > +how to check for expected conditions in test cases and a bit of the logging > characteristics. > + > +Most of these examples will refer to the SampleGoogleTestHost app found in > this package. > + > +### GoogleTest Requirements - INF > + > +In our INF file, we'll need to bring in the `GoogleTest` library. Conveniently, > the interface > +header for the `GoogleTest` is in `UnitTestFrameworkPkg`, so you shouldn't > need to depend on any other > +packages. As long as your DSC file knows where to find the lib > implementation that you want to use, > +you should be good to go. > + > +See this example in 'SampleGoogleTestHost.inf'... > + > +``` > +[Packages] > + MdePkg/MdePkg.dec > + UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec > + > +[LibraryClasses] > + GoogleTestLib > + BaseLib > + DebugLib > +``` > + > +Also, if you want you test to automatically be picked up by the Test Runner > plugin, you will need > +to make sure that the module `BASE_NAME` contains the word `Test`... > + > +``` > +[Defines] > + BASE_NAME =3D SampleGoogleTestHost > +``` > + > +### GoogleTest Requirements - Code > + > +Not to state the obvious, but let's make sure we have the following include > before getting too far along... > + > +``` > +#include > +extern "C" { > + #include > + #include > + #include > +} > +``` > + > +GoogleTest applications are implemented in C++. The first include brings in > the > +GoogleTest definitions. Other EDK II related include files must be wrapped in > +`extern "C" {}` because they are C include files. Link failures will occur if > +this is not done. > + > +Now that we've got that squared away, let's look at our 'Main()'' routin= e (or > DriverEntryPoint() or whatever). > + > +### GoogleTest Configuration > + > +Unlike the Framework, GoogleTest does not require test suites or test cases > to > +be registered. Instead, the test cases declare the test suite name and test > +case name as part of their implementation. The only requirement for > GoogleTest > +is to have a `main()` function that initialize the GoogleTest infrastructure and > +call the service `RUN_ALL_TESTS()` to run all the unit tests. > + > +```c > +int main(int argc, char* argv[]) { > + testing::InitGoogleTest(&argc, argv); > + return RUN_ALL_TESTS(); > +} > +``` > + > +### GoogleTest - A Simple Test Case > + > +We'll look at the below test case from 'SampleGoogleTestHost'... > + > +```c > +TEST(SimpleMathTests, OnePlusOneShouldEqualTwo) { > + UINTN A; > + UINTN B; > + UINTN C; > + > + A =3D 1; > + B =3D 1; > + C =3D A + B; > + > + ASSERT_EQ (C, 2); > +} > +``` > + > +This uses the simplest form of a GoogleTest unit test using `TEST()` tha= t > +declares the test suite name and the unit test name within that test suite. > +The unit test performs actions and typically makes calls to the code under > test > +and contains test assertions to verify that the code under test behaves as > +expected for the given inputs. > + > +In this test case, the `ASSERT_EQ` assertion is being used to establish that > the business logic has functioned > +correctly. There are several assertion macros, and you are encouraged to > use one that matches as closely to your > +intended test criterium as possible, because the logging is specific to the > macro and more specific macros have more > +detailed logs. When in doubt, there are always `ASSERT_TRUE` and > `ASSERT_FALSE`. Assertion macros that fail their > +test criterium will immediately return from the test case with a failed status > and log an error string. > +_Note_ that this early return can have implications for memory leakage. > + > +There is no return status from a GooglTest unit test. If no assertions are > +triggered then the unit test has a passing status. > + > +### GoogleTest - More Complex Cases > + > +To write more advanced tests, take a look at the > +[GoogleTest User's Guide](http://google.github.io/googletest/). > + > ## Development >=20 > ### Iterating on a Single Test > @@ -243,11 +417,11 @@ stuart_ci_build -c .pytool/CISettings.py > TOOL_CHAIN_TAG=3DVS2017 -p MdePkg -t NOOP >=20 > ### Hooking BaseLib >=20 > -Most unit test mocking can be performed by the functions provided in the > UnitTestFramework libraries, but since > +Most unit test mocking can be performed by the functions provided in the > UnitTestFrameworkPkg libraries, but since > BaseLib is consumed by the Framework itself, it requires different techniques > to substitute parts of the > functionality. >=20 > -To solve some of this, the UnitTestFramework consumes a special > implementation of BaseLib for host-based tests. > +To solve some of this, the UnitTestFrameworkPkg consumes a special > implementation of BaseLib for host-based tests. > This implementation contains a [hook > table](https://github.com/tianocore/edk2/blob/e188ecc8b4aed8fdd26b731d > 43883861f5e5e7b4/MdePkg/Test/UnitTest/Include/Library/UnitTestHostBase > Lib.h#L507) > that can be used to substitute test functionality for any of the BaseLib > functions. By default, this implementation > will use the underlying BaseLib implementation, so the unit test writer only > has to supply minimal code to test a > @@ -255,7 +429,7 @@ particular case. >=20 > ### Debugging the Framework Itself >=20 > -While most of the tests that are produced by the UnitTestFramework are > easy to step through in a debugger, the Framework > +While most of the tests that are produced by the UnitTestFrameworkPkg ar= e > easy to step through in a debugger, the Framework > itself consumes code (mostly Cmocka) that sets its own build flags. Thes= e > flags cause parts of the Framework to not > export symbols and captures exceptions, and as such are harder to debug. > We have provided a Stuart parameter to force > symbolic debugging to be enabled. > @@ -269,15 +443,17 @@ stuart_ci_build -c .pytool/CISettings.py > TOOL_CHAIN_TAG=3DVS2019 -p MdePkg -t NOOP > ## Building and Running Host-Based Tests >=20 > The EDK2 CI infrastructure provides a convenient way to run all host-based > tests -- in the the entire tree or just > -selected packages -- and aggregate all the the reports, including highlighting > any failures. This functionality is > -provided through the Stuart build system (published by EDK2-PyTools) and > the `NOOPT` build target. > +selected packages -- and aggregate all the reports, including highlighting any > failures. This functionality is > +provided through the Stuart build system (published by EDK2-PyTools) and > the `NOOPT` build target. The sections that > +follow use Framework examples. Unit tests based on GoogleTest are built > and run the same way. The text output and > +JUNIT XML output format have small differences. >=20 > ### Building Locally >=20 > First, to make sure you're working with the latest PyTools, run the following > command: >=20 > ```bash > -# Would recommend to run this in a Python venv, but that's out of scope for > this doc. > +# Would recommend running this in a Python venv, but that's out of scope for > this doc. > python -m pip install --upgrade -r ./pip-requirements.txt > ``` >=20 > @@ -361,7 +537,7 @@ RUNNING TEST SUITE: Int Safe Conversions Test > Suite > ``` >=20 > You can also, if you are so inclined, read the output from the exact instance > of the test that was run during > -`stuart_ci_build`. The ouput file can be found on a path that looks like= : > +`stuart_ci_build`. The output file can be found on a path that looks like: >=20 >=20 > `Build//HostTest//...re > sult.xml` >=20 > @@ -389,22 +565,30 @@ > c:\_uefi\MdePkg\Test\UnitTest\Library\BaseSafeIntLib\TestBaseSafeIntLib.c= : > 35: er >=20 > ### XML Reporting Mode >=20 > -Since these applications are built using the CMocka framework, they can also > use the following env variables to output > -in a structured XML rather than text: > +Unit test applications using Framework are built using Cmocka that requires > the > +following environment variables to be set to generate structured XML output > +rather than text: >=20 > -```text > +``` > CMOCKA_MESSAGE_OUTPUT=3Dxml > CMOCKA_XML_FILE=3D > ``` >=20 > +Unit test applications using GoogleTest require the following environmen= t > +variable to be set to generate structured XML output rather than text: > + > +``` > +GTEST_OUTPUT=3Dxml: > +``` > + > This mode is used by the test running plugin to aggregate the results fo= r CI > test status reporting in the web view. >=20 > ### Important Note >=20 > -This works on both Windows and Linux, but is currently limited to x64 > architectures. Working on getting others, but we > +This works on both Windows and Linux but is currently limited to x64 > architectures. Working on getting others, but we > also welcome contributions. >=20 > -## Known Limitations > +## Framework Known Limitations >=20 > ### PEI, DXE, SMM >=20 > @@ -418,7 +602,7 @@ PEI, DXE, and SMM is forthcoming, but should be > considered beta/staging for now. > The host-based test framework is powered internally by the Cmocka > framework. As such, it has abilities > that the target-based tests don't (yet). It would be awesome if this meant > that it was a super set of > the target-based tests, and it worked just like the target-based tests but > with more features. Unfortunately, > -this is not the case. While care has been taken to keep them as close a > possible, there are a few known > +this is not the case. While care has been taken to keep them as close as > possible, there are a few known > inconsistencies that we're still ironing out. For example, the logging > messages in the target-based tests > are cached internally and associated with the running test case. They ca= n be > saved later as part of the > reporting lib. This isn't currently possible with host-based. Only the assertion > failures are logged. > @@ -441,6 +625,9 @@ Non-Host-Based (PEI/DXE/SMM/Shell) Tests for a > Functionality or Feature | Simi > ComponentY/ > ComponentY.inf > ComponentY.c > + GoogleTest/ > + ComponentYHostGoogleTest.inf # Host-Based Test for Driver > Module > + ComponentYGoogleTest.cpp > UnitTest/ > ComponentYHostUnitTest.inf # Host-Based Test for Driver > Module > ComponentYUnitTest.c > @@ -455,11 +642,23 @@ Non-Host-Based (PEI/DXE/SMM/Shell) Tests for a > Functionality or Feature | Simi > SpecificLibDxe/ > SpecificLibDxe.c > SpecificLibDxe.inf > + GoogleTest/ # Host-Based Test for Specific > Library Implementation > + SpecificLibDxeHostGoogleTest.cpp > + SpecificLibDxeHostGoogleTest.inf > UnitTest/ # Host-Based Test for Specific > Library Implementation > SpecificLibDxeHostUnitTest.c > SpecificLibDxeHostUnitTest.inf > Test/ > HostTest.dsc # Host-Based Test Apps > + GoogleTest/ > + InterfaceX > + InterfaceXHostGoogleTest.inf # Host-Based App (should be in > Test/HostTest.dsc) > + InterfaceXUnitTest.cpp # Test Logic > + > + GeneralPurposeLib/ # Host-Based Test for any > implementation of GeneralPurposeLib > + GeneralPurposeLibTest.cpp > + GeneralPurposeLibHostUnitTest.inf > + > UnitTest/ > InterfaceX > InterfaceXHostUnitTest.inf # Host-Based App (should be in > Test/HostTest.dsc) > diff --git > a/UnitTestFrameworkPkg/Test/GoogleTest/Sample/SampleGoogleTest/Sampl > eGoogleTest.cpp > b/UnitTestFrameworkPkg/Test/GoogleTest/Sample/SampleGoogleTest/Sampl > eGoogleTest.cpp > new file mode 100644 > index 000000000000..c83e58596a82 > --- /dev/null > +++ > b/UnitTestFrameworkPkg/Test/GoogleTest/Sample/SampleGoogleTest/Sampl > eGoogleTest.cpp > @@ -0,0 +1,263 @@ > +/** @file > + This is a sample to demonstrates the use of GoogleTest that supports host > + execution environments. > + > + Copyright (c) 2022, Intel Corporation. All rights reserved.
> + SPDX-License-Identifier: BSD-2-Clause-Patent > + > +**/ > + > +#include > +extern "C" { > + #include > + #include > + #include > +} > + > +/** > + Sample unit test that verifies the expected result of an unsigned integer > + addition operation. > +**/ > +TEST(SimpleMathTests, OnePlusOneShouldEqualTwo) { > + UINTN A; > + UINTN B; > + UINTN C; > + > + A =3D 1; > + B =3D 1; > + C =3D A + B; > + > + ASSERT_EQ (C, (UINTN)2); > +} > + > +/** > + Sample unit test that verifies that a global BOOLEAN is updatable. > +**/ > +class GlobalBooleanVarTests : public ::testing::Test { > + public: > + BOOLEAN SampleGlobalTestBoolean =3D FALSE; > +}; > + > +TEST_F(GlobalBooleanVarTests, GlobalBooleanShouldBeChangeable) { > + SampleGlobalTestBoolean =3D TRUE; > + ASSERT_TRUE (SampleGlobalTestBoolean); > + > + SampleGlobalTestBoolean =3D FALSE; > + ASSERT_FALSE (SampleGlobalTestBoolean); > +} > + > +/** > + Sample unit test that logs a warning message and verifies that a globa= l > + pointer is updatable. > +**/ > +class GlobalVarTests : public ::testing::Test { > + public: > + VOID *SampleGlobalTestPointer =3D NULL; > + > + protected: > + void SetUp() override { > + ASSERT_EQ ((UINTN)SampleGlobalTestPointer, (UINTN)NULL); > + } > + void TearDown() { > + SampleGlobalTestPointer =3D NULL; > + } > +}; > + > +TEST_F(GlobalVarTests, GlobalPointerShouldBeChangeable) { > + SampleGlobalTestPointer =3D (VOID *)-1; > + ASSERT_EQ ((UINTN)SampleGlobalTestPointer, (UINTN)((VOID *)-1)); > +} > + > + > +/** > + Set PcdDebugPropertyMask for each MacroTestsAssertsEnabledDisabled > test > +**/ > +class MacroTestsAssertsEnabledDisabled : public > testing::TestWithParam { > + void SetUp() { > + PatchPcdSet8 (PcdDebugPropertyMask, GetParam()); > + } > +}; > + > +/** > + Sample unit test using the ASSERT_TRUE() macro. > +**/ > +TEST_P(MacroTestsAssertsEnabledDisabled, MacroAssertTrue) { > + UINT64 Result; > + > + // > + // This test passes because expression always evaluated to TRUE. > + // > + ASSERT_TRUE (TRUE); > + > + // > + // This test passes because expression always evaluates to TRUE. > + // > + Result =3D LShiftU64 (BIT0, 1); > + ASSERT_TRUE (Result =3D=3D BIT1); > +} > + > +/** > + Sample unit test using the ASSERT_FALSE() macro. > +**/ > +TEST_P(MacroTestsAssertsEnabledDisabled, MacroAssertFalse) { > + UINT64 Result; > + > + // > + // This test passes because expression always evaluated to FALSE. > + // > + ASSERT_FALSE (FALSE); > + > + // > + // This test passes because expression always evaluates to FALSE. > + // > + Result =3D LShiftU64 (BIT0, 1); > + ASSERT_FALSE (Result =3D=3D BIT0); > +} > + > +/** > + Sample unit test using the ASSERT_EQ() macro. > +**/ > +TEST_P(MacroTestsAssertsEnabledDisabled, MacroAssertEqual) { > + UINT64 Result; > + > + // > + // This test passes because both values are always equal. > + // > + ASSERT_EQ (1, 1); > + > + // > + // This test passes because both values are always equal. > + // > + Result =3D LShiftU64 (BIT0, 1); > + ASSERT_EQ (Result, (UINT64)BIT1); > +} > + > +/** > + Sample unit test using the ASSERT_STREQ() macro. > +**/ > +TEST_P(MacroTestsAssertsEnabledDisabled, MacroAssertMemEqual) { > + CHAR8 *String1; > + CHAR8 *String2; > + > + // > + // This test passes because String1 and String2 are the same. > + // > + String1 =3D (CHAR8 *)"Hello"; > + String2 =3D (CHAR8 *)"Hello"; > + ASSERT_STREQ (String1, String2); > +} > + > +/** > + Sample unit test using the ASSERT_NE() macro. > +**/ > +TEST_P(MacroTestsAssertsEnabledDisabled, MacroAssertNotEqual) { > + UINT64 Result; > + > + // > + // This test passes because both values are never equal. > + // > + ASSERT_NE (0, 1); > + > + // > + // This test passes because both values are never equal. > + // > + Result =3D LShiftU64 (BIT0, 1); > + ASSERT_NE (Result, (UINT64)BIT0); > +} > + > +/** > + Sample unit test using the ASSERT_TRUE() and ASSERT(FALSE) > + and EFI_EFFOR() macros to check status > +**/ > +TEST_P(MacroTestsAssertsEnabledDisabled, MacroAssertNotEfiError) { > + // > + // This test passes because the status is not an EFI error. > + // > + ASSERT_FALSE (EFI_ERROR (EFI_SUCCESS)); > + > + // > + // This test passes because the status is not an EFI error. > + // > + ASSERT_FALSE (EFI_ERROR (EFI_WARN_BUFFER_TOO_SMALL)); > +} > + > +/** > + Sample unit test using the ASSERT_EQ() macro to compare EFI_STATUS > values. > +**/ > +TEST_P(MacroTestsAssertsEnabledDisabled, MacroAssertStatusEqual) { > + // > + // This test passes because the status value are always equal. > + // > + ASSERT_EQ (EFI_SUCCESS, EFI_SUCCESS); > +} > + > +/** > + Sample unit test using ASSERT_NE() macro to make sure a pointer is not > NULL. > +**/ > +TEST_P(MacroTestsAssertsEnabledDisabled, MacroAssertNotNull) { > + UINT64 Result; > + > + // > + // This test passes because the pointer is never NULL. > + // > + ASSERT_NE (&Result, (UINT64 *)NULL); > +} > + > +/** > + Sample unit test using that should not generate any ASSERTs() > +**/ > +TEST_P(MacroTestsAssertsEnabledDisabled, MacroExpectNoAssertFailure) { > + // > + // This test passes because it never triggers an ASSERT(). > + // > + ASSERT (TRUE); > + > + // > + // This test passes because DecimalToBcd() does not ASSERT() if the > + // value passed in is <=3D 99. > + // > + DecimalToBcd8 (99); > +} > + > +/** > + Sample unit test using the ASSERT_DEATH() macro to test expected > ASSERT()s. > +**/ > +TEST_P(MacroTestsAssertsEnabledDisabled, MacroExpectAssertFailure) { > + // > + // Skip tests that verify an ASSERT() is triggered if ASSERT()s are disabled. > + // > + if ((PcdGet8 (PcdDebugPropertyMask) & BIT0) =3D=3D 0x00) { > + return; > + } > + > + // > + // This test passes because it directly triggers an ASSERT(). > + // > + ASSERT_DEATH (ASSERT (FALSE), ""); > + > + // > + // This test passes because DecimalToBcd() generates an ASSERT() if th= e > + // value passed in is >=3D 100. The expected ASSERT() is caught by th= e unit > + // test framework and ASSERT_DEATH() returns without an error. > + // > + ASSERT_DEATH (DecimalToBcd8 (101), ""); > +} > + > +INSTANTIATE_TEST_SUITE_P(ValidInput, > + MacroTestsAssertsEnabledDisabled, > + ::testing::Values(PcdGet8 > (PcdDebugPropertyMask) | BIT0, PcdGet8 (PcdDebugPropertyMask) & > (~BIT0))); > + > +/** > + Sample unit test using the SCOPED_TRACE() macro for trace messages. > +**/ > +TEST(MacroTestsMessages, MacroTraceMessage) { > + // > + // Example of logging. > + // > + SCOPED_TRACE ("SCOPED_TRACE message\n"); > +} > + > +int main(int argc, char* argv[]) { > + testing::InitGoogleTest(&argc, argv); > + return RUN_ALL_TESTS(); > +} > diff --git > a/UnitTestFrameworkPkg/Test/GoogleTest/Sample/SampleGoogleTest/Sampl > eGoogleTestHost.inf > b/UnitTestFrameworkPkg/Test/GoogleTest/Sample/SampleGoogleTest/Sampl > eGoogleTestHost.inf > new file mode 100644 > index 000000000000..37e7c86910ed > --- /dev/null > +++ > b/UnitTestFrameworkPkg/Test/GoogleTest/Sample/SampleGoogleTest/Sampl > eGoogleTestHost.inf > @@ -0,0 +1,35 @@ > +## @file > +# This is a sample to demonstrates the use of GoogleTest that supports host > +# execution environments. > +# > +# Copyright (c) 2022, Intel Corporation. All rights reserved.
> +# SPDX-License-Identifier: BSD-2-Clause-Patent > +## > + > +[Defines] > + INF_VERSION =3D 0x00010005 > + BASE_NAME =3D SampleGoogleTestHost > + FILE_GUID =3D 7D8BBFBB-7977-4AEE-A59F-257BF5C2F87C > + MODULE_TYPE =3D HOST_APPLICATION > + VERSION_STRING =3D 1.0 > + > +# > +# The following information is for reference only and not required by th= e > build tools. > +# > +# VALID_ARCHITECTURES =3D IA32 X64 > +# > + > +[Sources] > + SampleGoogleTest.cpp > + > +[Packages] > + MdePkg/MdePkg.dec > + UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec > + > +[LibraryClasses] > + GoogleTestLib > + BaseLib > + DebugLib > + > +[Pcd] > + gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask > diff --git a/UnitTestFrameworkPkg/Test/UnitTestFrameworkPkgHostTest.dsc > b/UnitTestFrameworkPkg/Test/UnitTestFrameworkPkgHostTest.dsc > index 184fdec87acf..708ef7f9ab35 100644 > --- a/UnitTestFrameworkPkg/Test/UnitTestFrameworkPkgHostTest.dsc > +++ b/UnitTestFrameworkPkg/Test/UnitTestFrameworkPkgHostTest.dsc > @@ -23,14 +23,16 @@ [PcdsPatchableInModule] >=20 > [Components] > # > - # Build HOST_APPLICATION that tests the SampleUnitTest > + # Build HOST_APPLICATIONs that test the SampleUnitTest > # >=20 > UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTe > stHost.inf > + > UnitTestFrameworkPkg/Test/GoogleTest/Sample/SampleGoogleTest/SampleG > oogleTestHost.inf >=20 > # > # Build HOST_APPLICATION Libraries > # > UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.inf > + UnitTestFrameworkPkg/Library/GoogleTestLib/GoogleTestLib.inf > UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.inf >=20 > UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllo > cationLibPosix.inf > UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLibCmocka.inf > diff --git a/UnitTestFrameworkPkg/UnitTestFrameworkPkg.ci.yaml > b/UnitTestFrameworkPkg/UnitTestFrameworkPkg.ci.yaml > index 77d51e13484c..072df6208c92 100644 > --- a/UnitTestFrameworkPkg/UnitTestFrameworkPkg.ci.yaml > +++ b/UnitTestFrameworkPkg/UnitTestFrameworkPkg.ci.yaml > @@ -78,7 +78,8 @@ > "SpellCheck": { > "AuditOnly": False, # Fails test but run in AuditOnly > mode to collect log > "IgnoreFiles": [ # use gitignore syntax to ignore > errors in matching files > - "Library/CmockaLib/cmocka/**/*.*" # not going to spell > check a submodule > + "Library/CmockaLib/cmocka/**/*.*", # not going to spell > check a submodule > + "Library/GoogleTestLib/googletest/**/*.*" # not going to > spell check a submodule > ], > "ExtendWords": [ # words to extend to the > dictionary for this package > "testcase", > @@ -91,6 +92,7 @@ > "NOFAILURE", > "cmockery", > "DHAVE", # build flag for cmocka in the INF > + "gtest", # file name in GoogleTestLib.inf > "corthon", # Contact GitHub account in Readme > "mdkinney", # Contact GitHub account in Readme > "spbrogan" # Contact GitHub account in Readme > diff --git a/UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec > b/UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec > index 069289f00969..ed12f32009d8 100644 > --- a/UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec > +++ b/UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec > @@ -16,11 +16,15 @@ [Defines] > PACKAGE_VERSION =3D 1.00 >=20 > [Includes] > + Include > Library/CmockaLib/cmocka/include > + Library/GoogleTestLib/googletest/googletest/include > + Library/GoogleTestLib/googletest/googlemock/include >=20 > [Includes.Common.Private] > PrivateInclude > Library/CmockaLib/cmocka/include/cmockery > + Library/GoogleTestLib/googletest/googletest >=20 > [LibraryClasses.Common.Private] > ## @libraryclass Allows save and restore unit test internal state > @@ -35,6 +39,10 @@ [LibraryClasses.Common.Private] > # > UnitTestBootLib|PrivateInclude/Library/UnitTestBootLib.h >=20 > + ## @libraryclass GoogleTest infrastructure > + # > + GoogleTestLib|Include/Library/GoogleTestLib.h > + > [Guids] > gUnitTestFrameworkPkgTokenSpaceGuid =3D { 0x833d3aba, 0x39b4, 0x43a2, > { 0xb9, 0x30, 0x7a, 0x34, 0x53, 0x39, 0x31, 0xb3 } } >=20 > diff --git a/UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc > b/UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc > index f249813713a8..6d5f651f689f 100644 > --- a/UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc > +++ b/UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc > @@ -14,6 +14,7 @@ [LibraryClasses.common.HOST_APPLICATION] > CpuLib|MdePkg/Library/BaseCpuLibNull/BaseCpuLibNull.inf >=20 > CacheMaintenanceLib|MdePkg/Library/BaseCacheMaintenanceLibNull/Base > CacheMaintenanceLibNull.inf > CmockaLib|UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.inf > + > GoogleTestLib|UnitTestFrameworkPkg/Library/GoogleTestLib/GoogleTestLib.i > nf >=20 > UnitTestLib|UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLibCmocka.i > nf >=20 > DebugLib|UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosi > x.inf >=20 > MemoryAllocationLib|UnitTestFrameworkPkg/Library/Posix/MemoryAllocatio > nLibPosix/MemoryAllocationLibPosix.inf > @@ -30,6 +31,7 @@ [BuildOptions.common.EDKII.HOST_APPLICATION] > # > # MSFT > # > + MSFT:*_*_*_CC_FLAGS =3D /EHsc > MSFT:*_*_*_DLINK_FLAGS =3D=3D > /out:"$(BIN_DIR)\$(MODULE_NAME_GUID).exe" > /pdb:"$(BIN_DIR)\$(MODULE_NAME_GUID).pdb" /IGNORE:4001 /NOLOGO > /SUBSYSTEM:CONSOLE /DEBUG /STACK:0x40000,0x40000 > /NODEFAULTLIB:libcmt.lib libcmtd.lib > MSFT:*_*_IA32_DLINK_FLAGS =3D /MACHINE:I386 > MSFT:*_*_X64_DLINK_FLAGS =3D /MACHINE:AMD64 > @@ -49,7 +51,7 @@ [BuildOptions.common.EDKII.HOST_APPLICATION] > # > GCC:*_*_IA32_DLINK_FLAGS =3D=3D -o $(BIN_DIR)/$(MODULE_NAME_GUID) > -m32 -no-pie > GCC:*_*_X64_DLINK_FLAGS =3D=3D -o > $(BIN_DIR)/$(MODULE_NAME_GUID) -m64 -no-pie > - GCC:*_*_*_DLINK2_FLAGS =3D=3D -lgcov > + GCC:*_*_*_DLINK2_FLAGS =3D=3D -lgcov -lpthread -lstdc++ -lm >=20 > # > # Need to do this link via gcc and not ld as the pathing to libraries changes > from OS version to OS version > -- > 2.37.1.windows.1 >=20 >=20 >=20 >=20 >=20 >=20 >=20