From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mga17.intel.com (mga17.intel.com [192.55.52.151]) by mx.groups.io with SMTP id smtpd.web11.4159.1680027815193259647 for ; Tue, 28 Mar 2023 11:23:35 -0700 Authentication-Results: mx.groups.io; dkim=fail reason="unable to parse pub key" header.i=@intel.com header.s=intel header.b=Pa/UE/Yy; spf=pass (domain: intel.com, ip: 192.55.52.151, mailfrom: chris.n.johnson@intel.com) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1680027815; x=1711563815; h=from:to:cc:subject:date:message-id:references: in-reply-to:content-transfer-encoding:mime-version; bh=T3qJoioLNnfZ6SHX8dUpHefBfJ0Dq4lTPz2JNODYDEA=; b=Pa/UE/YyQjnjvXlLoiBh4EPjcMdHs2PkVWsuK2x7rJd0hOMyOiplmZYl pbgyayg8SW8+kLRXlKA617xEbva4uBaZmlyHnNm3x/RRhcgi69cKUtrME DsH7x2x26KujPEB1wdYfy9+ahhq3ZKZr+HVEzN4jdo0RYMoz74jpKlmRG WhuM+XLDS9rXVpPJcL1GzTPWvppVpc/ac3kM8QOe4K+uA1uHorbZMkx+K lVrCYrGZtD2fleRJakIEHACONULiJiAOZvqeMAcWPhnb+yg1ydRNKOS9R nbKx11iB050XX5ltFK9IUYAF96kSZHPUUA0gCeeYCO6w44/6wHlVEnN8I Q==; X-IronPort-AV: E=McAfee;i="6600,9927,10663"; a="321055684" X-IronPort-AV: E=Sophos;i="5.98,297,1673942400"; d="scan'208";a="321055684" Received: from orsmga001.jf.intel.com ([10.7.209.18]) by fmsmga107.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 28 Mar 2023 11:23:26 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10663"; a="716571289" X-IronPort-AV: E=Sophos;i="5.98,297,1673942400"; d="scan'208";a="716571289" Received: from fmsmsx601.amr.corp.intel.com ([10.18.126.81]) by orsmga001.jf.intel.com with ESMTP; 28 Mar 2023 11:23:25 -0700 Received: from fmsmsx603.amr.corp.intel.com (10.18.126.83) by fmsmsx601.amr.corp.intel.com (10.18.126.81) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.21; Tue, 28 Mar 2023 11:23:24 -0700 Received: from fmsedg601.ED.cps.intel.com (10.1.192.135) by fmsmsx603.amr.corp.intel.com (10.18.126.83) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.21 via Frontend Transport; Tue, 28 Mar 2023 11:23:24 -0700 Received: from NAM11-DM6-obe.outbound.protection.outlook.com (104.47.57.170) by edgegateway.intel.com (192.55.55.70) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.1.2507.21; Tue, 28 Mar 2023 11:23:24 -0700 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=OdUyxUPNOScFZbUrtEOd0nokiIcOdSBs4JAWwl3M3KXPVBFg+oRoDeZXoK/ixLGFaGrFNtDNkX0eQq4SN2MqN88g5yIr372gSkWLMo041bZacTXmfIO1sa1wQs17e+mx+uAF98rsTNPhxaM6VSllQyRLnKc0VmLAddcySNhYO+l/mGHAhdUjlM3+JuZalgByHXQRDn1h3pEkYCpn6yGjM3c1VnvpchIOSdSAQw7qvRTb3zD/DlfRbbAVLH2Ir8ElB/cWLdZo3YZyc1z4k8JGmwx8TJlwVVFlafjzkjFb3ujvrZ16Wf/ZJ+1piyrYjEIdzIgPiO3JB8UT27PUh42e4Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=IWi5AaFn2S6Sx8cNfjLiA65o+NnDdm6dg4TKIWJKUL8=; b=Ku8ri9py+sWAeSYsXUxCw0U+/rx9lXGnw3v1S7SCqovyk3u08RcNAhJpltAucyBxSLU4ab7dnf11O2SKcMlRl2045B1s6mY9GyUCd2ztFu3CGm+pABZ2fhXH01IraNQGPtH4xpg6oLDsQeuOIeRRyUxYmvyZqH8yEZ3tamTbh3ltNgV9HUIemcYaow/yUnRkDqe7DWiXMTuVAgCQf79s8Zx4HSPOmPQcnANCQEtV9SWpXylce0nkeUNeS5CltDEUxcRcYw9iDXn2NvFpS7axmJ3w+bzCnq4uXIyYMaLDbhBXFTgJd3vtNZcT8fPukqG+n3CjCU2CfHEeltR8CBu+zg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=intel.com; dmarc=pass action=none header.from=intel.com; dkim=pass header.d=intel.com; arc=none Received: from BN0PR11MB5725.namprd11.prod.outlook.com (2603:10b6:408:16a::10) by LV2PR11MB5974.namprd11.prod.outlook.com (2603:10b6:408:17e::10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6178.41; Tue, 28 Mar 2023 18:23:20 +0000 Received: from BN0PR11MB5725.namprd11.prod.outlook.com ([fe80::8faa:30e8:12b6:ebe0]) by BN0PR11MB5725.namprd11.prod.outlook.com ([fe80::8faa:30e8:12b6:ebe0%4]) with mapi id 15.20.6222.032; Tue, 28 Mar 2023 18:23:20 +0000 From: "Johnson, Chris N" To: "Kinney, Michael D" , "devel@edk2.groups.io" CC: Sean Brogan , Michael Kubacki Subject: Re: [Patch 04/12] UnitTestFrameworkPkg/ReadMe.md: Add gmock documentation Thread-Topic: [Patch 04/12] UnitTestFrameworkPkg/ReadMe.md: Add gmock documentation Thread-Index: AQHZX0oJcYvl0oS2Ak2qc1ziqJcvEK8Qg0dg Date: Tue, 28 Mar 2023 18:23:20 +0000 Message-ID: References: <20230325184541.596-1-michael.d.kinney@intel.com> <20230325184541.596-5-michael.d.kinney@intel.com> In-Reply-To: <20230325184541.596-5-michael.d.kinney@intel.com> Accept-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=intel.com; x-ms-publictraffictype: Email x-ms-traffictypediagnostic: BN0PR11MB5725:EE_|LV2PR11MB5974:EE_ x-ms-office365-filtering-correlation-id: 85357f6e-67c4-44ac-ad3a-08db2fb9827e x-ms-exchange-senderadcheck: 1 x-ms-exchange-antispam-relay: 0 x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: I1WEUsL6A+MxYeU5WbOQvUsgjUS/cX/CWP3Y4sBHNij6DE01LZLa+wAmWV6NJ20vMZnidTKXIe1PEguDOuzY7EEabGlkHhk1H7lkxhAOdceE4edG0x8HE65NbFAIGhUORgkVvX7hMp38Fns4ohh2DPA1tMC0Ri/FOwOMOdNZO+Mk09mrfyBCuMhB6eEvkezBs9bAERAv/oUoFUQj6y+V4Rp4quZrz+z6t+/LXXA7A95I+B4dMajh8pBHq29nVFL40W8q3vDKXc8wrhOo0nT6j4trhtw47ZjsPBJNLlVdrwPNbqgf0SDYdLFR60Cdnd8qBaQ1nt6pbzrP0uqquHagcUeETtDP0JNJyWMU1NPhyTQtB3rK66b1eX848nps/VYySVNcrrFhrOdC8ZhKPQRqYDJTpvdkpia0KVQEH4v2Ds18VNPIuPXlWi2gmXOGnvWZYJakkjdQEesSPeiAB9XDnWdJ8z2uv0h+kp3nZ4IprKsUDeu3scqdYUwlUrCm4HSvyCze9uqL+C/inoGlHWNR2c/4p5H/mVvvbVneShYNZiRgtOPpWnFMoRSGIByObsbktWXlOJwxIPDefvQjGjm0B/eGbEFa5WMfKl3X2mDENmmbOO85O5CzhXya2VMgZbRfgUarnYlFKaiEwgybR6yu8Fq+CIPqZHiu/2L92TJV8CzYIrj8nTotixgUIW4v42uGy4DVlWlqslpf3As2opc0oQ== x-forefront-antispam-report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:BN0PR11MB5725.namprd11.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230028)(396003)(39860400002)(366004)(346002)(376002)(136003)(451199021)(26005)(66556008)(64756008)(8676002)(66946007)(76116006)(122000001)(66446008)(186003)(4326008)(316002)(110136005)(82960400001)(30864003)(38100700002)(5660300002)(41300700001)(52536014)(6506007)(8936002)(54906003)(53546011)(9686003)(7696005)(71200400001)(83380400001)(966005)(66476007)(478600001)(33656002)(86362001)(38070700005)(2906002)(55016003)(16940595002)(579004)(559001)(457024003);DIR:OUT;SFP:1102; x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?us-ascii?Q?O3aaHd6IbnjJyfupxeD27cTCaqiJiQ+cgVb8/XOLEDz6vUU9uNnoldGWGCRx?= =?us-ascii?Q?nDY9g06JFVZ4fPCq/5xMj7m0oKW9ONijzb97MqJo6mgojEgooy8+CZsU2xTe?= =?us-ascii?Q?l9mKPmcCgs6ALzNDZYL/6RsPCbwowOHEAZWG3V6xWSmXmtagknAAkoycNcFM?= =?us-ascii?Q?UPs4CVutnFVc1BN0K38N/2uf9zIYoVttdvmMVkB9bR7A3n7X8m+8EuRwFofI?= =?us-ascii?Q?dRSowwakVVe0L87WNTiQ7R+v9x4WtQ4mZ0X7FHPhSkexhoGczdCHrGLS9j2V?= =?us-ascii?Q?SuX7iUopCBrYaqPAwoy/VIZcr9ALJvDeq+u5AI6qiEk3QEuYamVTZamiZdfE?= =?us-ascii?Q?yiwcimzCrxBpEiX+3DWCd630KTttK+KP9Yo7pG1XZ7rf824Q91+MuLfGrZ7B?= =?us-ascii?Q?zcng81l4rEJuCMLpnDwdIf/jSb5Ave/j3owdpxNWl7hbXTs8fSc/XKAqmD9r?= =?us-ascii?Q?q8JNNegXFJOTkUu2SBSG9DmHqYJAz4Kv3ul1H47JKLcLmf8zFszpPMu/dKYg?= =?us-ascii?Q?DgmF8RfV1B3g7+tzCXeX0NQnp+Cz7w5j6ykIQ4GlQ3CYL6qkeD+iq8cOri8x?= =?us-ascii?Q?BDWaZX9d2RBVfFbC2nE5v0SQdppEERWWvEsm7BRh6BgZz6n2/FandGtyB8Y/?= =?us-ascii?Q?8ZEk9S+yIYeH1XHk6Urg+7YbGpn4GcGaOrlc3NaRq51Bsn7W5ANxFtqT67hd?= =?us-ascii?Q?eqUn3KlQwV2KxKxiHfAZFee7ylR42t53Zt9hiA5RwP4XIHlxFfCxHEGPGrPs?= =?us-ascii?Q?lJz5Nd+e0TjWozKCe+IdBFEH1eOgchR78kWwEYfckjAwimasAbZdCKZfLNLB?= =?us-ascii?Q?qSoXY2K/nAm1q93vwJylKSlmZduGgcex7iAB57efA8zfZbjU4AWx6avvu8Cz?= =?us-ascii?Q?cNFyp2FEV80SLCK82duU9QSSBaHZ/3wGkUc3Z16FX6hbenO5JGVDtQNkLahu?= =?us-ascii?Q?4DwAftLdcJLjBNEs/NrSMjjoGBzm3WJzJYw7kaJPTvldKFNkH+fmnLkna0DY?= =?us-ascii?Q?ypciMmtdtif6Ty3I6hX4bY9bWH1Oj+GHddVe+l/yS8tXd8d6wd2aaML9ywod?= =?us-ascii?Q?YIeWiWjuIOPmMoxyhidNKzQOT006PxHjuIR4jj2McIQajPOdIr+qs9UcyZ3b?= =?us-ascii?Q?yO+k0oS9M6apn/1tYoyHNVE7jV1CfzpAMTuJxlEIjChoz/Ri8IRK26pXLqfm?= =?us-ascii?Q?Z67whOt31+j/kw/zDU/X5r8CscKlmcsW6ef+n4TdfsJCCsEfnAWcg7L2Ez3x?= =?us-ascii?Q?5+FXkE+Erbtfx49SFxa5gJUxm57aIyqtsn4KOg6AMe+qZ/xrc1GC3XJIOPAX?= =?us-ascii?Q?k38KDwxBART5VS/a02EaNGHmN/1kQjGp19nRGdXcM9Q2oSiA6dQJhxbp0XBa?= =?us-ascii?Q?WDTGClM6wtf0SFbF7syAXWRZbqADRFIhASUj4xfEGeUOBXCFxsEiQJKPjYGN?= =?us-ascii?Q?lHRxd87idGzRF1pdQM6Yux3X9RqI3f7o+kE+wsRV26BJYudQFOxJBYc+bTPo?= =?us-ascii?Q?xMirVCFhqsj85K6VD9AzIsjM3psApcg6n0pB9XswyXbg0K/sKc1Eu2ZmwROh?= =?us-ascii?Q?dyQ9PscWsAJFMWsd89q+fPZfHe+oLq+H0mSbkiZW?= MIME-Version: 1.0 X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: BN0PR11MB5725.namprd11.prod.outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: 85357f6e-67c4-44ac-ad3a-08db2fb9827e X-MS-Exchange-CrossTenant-originalarrivaltime: 28 Mar 2023 18:23:20.7378 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 46c98d88-e344-4ed4-8496-4ed7712e255d X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: gWSNYCKLtGelqNa4lUjJM96YCf/z33w8fU1wqED03aH7aszMFH4KLbCKnU6TtHh2tk7GD9ZRIEMUgT4HEQa40BEcuIaRmn4gqBNHNKQekKs= X-MS-Exchange-Transport-CrossTenantHeadersStamped: LV2PR11MB5974 Return-Path: chris.n.johnson@intel.com X-OriginatorOrg: intel.com Content-Language: en-US Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable In reviewing the README, I found a few items that need to be updated. * Fix FunctionMockLib example of the MockUefiLib declaration to align with = the design code * Fix FunctionMockLib Mocks header file location paths in the tables * Fix FunctionMockLib Mocks example (and description) of the MockUefiLib de= claration to align with the design code * Fix FunctionMockLib Mocks example (and description) of the MockUefiLib IN= F file to align with the design file * Fix typos in new Code Coverage section -Chris -----Original Message----- From: Kinney, Michael D =20 Sent: Saturday, March 25, 2023 11:46 AM To: devel@edk2.groups.io Cc: Johnson, Chris N ; Sean Brogan ; Michael Kubacki Subject: [Patch 04/12] UnitTestFrameworkPkg/ReadMe.md: Add gmock documentat= ion From: Chris Johnson REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3D4389 Cc: Sean Brogan Cc: Michael Kubacki Cc: Michael D Kinney Signed-off-by: Chris Johnson --- UnitTestFrameworkPkg/ReadMe.md | 999 +++++++++++++++++++++++++++++++-- 1 file changed, 958 insertions(+), 41 deletions(-) diff --git a/UnitTestFrameworkPkg/ReadMe.md b/UnitTestFrameworkPkg/ReadMe.m= d index 3fa4e1910f71..ddbdd14bee33 100644 --- a/UnitTestFrameworkPkg/ReadMe.md +++ b/UnitTestFrameworkPkg/ReadMe.md @@ -20,12 +20,15 @@ that require mocked interfaces can use the mocking infr= astructure provided by ### GoogleTest =20 The second unit test framework supported by the UnitTestFrameworkPkg is -[GoogleTest](http://google.github.io/googletest/) that can be used to impl= ement host-based unit tests. -Use of GoogleTest for target-based unit tests of EDK II components is not = supported. If a -host-based unit test requires mocked interfaces, then the Framework with c= mocka support should be -used instead. Enabling support for mocked interfaces with GoogleTest is be= ing actively investigated. +[GoogleTest](http://google.github.io/googletest/) and can be used to imple= ment host-based unit tests. [GoogleTest on GitHub](https://github.com/google/googletest) is included i= n the UnitTestFrameworkPkg -as a submodule. +as a submodule. Use of GoogleTest for target-based unit tests of EDK II co= mponents is not supported. +Host-based unit tests that require mocked interfaces can use the mocking i= nfrastructure included with +GoogleTest called [gMock](https://github.com/google/googletest/tree/main/g= ooglemock). Note that the +gMock framework does not directly support mocking of free (C style) functi= ons, so the FunctionMockLib +(containing a set of macros that wrap gMock's MOCK_METHOD macro) was creat= ed within the +UnitTestFrameworkPkg to enable this support. The details and usage of thes= e macros in the +FunctionMockLib are described later. =20 GoogleTest requires less overhead to register test suites and test cases c= ompared to the Framework. There are also a number of tools that layer on top of GoogleTest that impr= ove developer productivity. @@ -33,13 +36,19 @@ One example is the VS Code extension [C++ TestMate](https://marketplace.visualstudio.com/items?itemName=3Dmatep= ek.vscode-catch2-test-adapter) that may be used to implement, run, and debug unit tests implemented using= GoogleTest. =20 -If a component can be tested with host-based unit tests without support fo= r mocked interfaces, -then GoogleTest is recommended. The MdePkg contains a port of the BaseSafe= IntLib unit tests in -the GoogleTest style so the differences between GoogleTest and Framework u= nit tests can be reviewed. -The paths to the BaseSafeIntLib unit tests are: +If a component can be tested with host-based unit tests, then GoogleTest i= s recommended. The MdePkg +contains a port of the BaseSafeIntLib unit tests in the GoogleTest style s= o the differences between +GoogleTest and Framework unit tests can be reviewed. The paths to the Base= SafeIntLib unit tests are: =20 -* MdePkg\Test\UnitTest\Library\BaseSafeIntLib -* MdePkg\Test\GoogleTest\Library\BaseSafeIntLib +* `MdePkg/Test/UnitTest/Library/BaseSafeIntLib` +* `MdePkg/Test/GoogleTest/Library/BaseSafeIntLib` + +Furthermore, the SecurityPkg contains unit tests for the SecureBootVariabl= eLib using mocks in both +the Framework/cmocka and GoogleTest/gMock style so the differences between= cmocka and gMock can be +reviewed. The paths to the SecureBootVariableLib unit tests are: + +* `SecurityPkg/Library/SecureBootVariableLib/UnitTest` +* `SecurityPkg/Library/SecureBootVariableLib/GoogleTest` =20 ## Framework and GoogleTest Feature Comparison =20 @@ -56,7 +65,7 @@ The paths to the BaseSafeIntLib unit tests are: | Typed Tests | NO | YES | | Type-Parameterized Tests | NO | YES | | Timeout Support | NO | YES | -| Mocking Support | Cmocka | NO | +| Mocking Support | Cmocka | gMock | | JUNIT XML Reports | YES | YES | | Execute subset of tests | NO | YES | | VS Code Extensions | NO | YES | @@ -104,7 +113,7 @@ This section is built a lot like a "Getting Started". W= e'll go through some of t when constructing a unit test and some of the decisions that are made by t= he test writer. We'll also describe how to check for expected conditions in test cases and a bit of the loggin= g characteristics. =20 -Most of these examples will refer to the SampleUnitTestUefiShell app found= in this package. +Most of these examples will refer to the `SampleUnitTestUefiShell` app fou= nd in this package. =20 ### Framework Requirements - INF =20 @@ -113,7 +122,7 @@ header for the `UnitTestLib` is located in `MdePkg`, so= you shouldn't need to de packages. As long as your DSC file knows where to find the lib implementat= ion that you want to use, you should be good to go. =20 -See this example in 'SampleUnitTestUefiShell.inf'... +See this example in `SampleUnitTestUefiShell.inf`... =20 ``` [Packages] @@ -127,7 +136,7 @@ See this example in 'SampleUnitTestUefiShell.inf'... PrintLib ``` =20 -Also, if you want you test to automatically be picked up by the Test Runne= r plugin, you will need +Also, if you want your test to automatically be picked up by the Test Runn= er plugin, you will need to make sure that the module `BASE_NAME` contains the word `Test`... =20 ``` @@ -135,6 +144,43 @@ to make sure that the module `BASE_NAME` contains the = word `Test`... BASE_NAME =3D SampleUnitTestUefiShell ``` =20 +### Framework Requirements - DSC + +In our DSC file, we'll need to bring in the INF file that was just created= into the `[Components]` +section so that the unit tests will be built. + +See this example in `UnitTestFrameworkPkg.dsc`... + +``` +[Components] + UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestU= efiShell.inf +``` + +Also, based on the type of tests that are being created, the associated DS= C include file from the +UnitTestFrameworkPkg for Host or Target based tests should also be include= d at the top of the DSC +file. + +``` +!include UnitTestFrameworkPkg/UnitTestFrameworkPkgTarget.dsc.inc +``` + +Lastly, in the case that the test build has specific dependent libraries a= ssociated with it, +they should be added in the \ sub-section for the INF fil= e in the +`[Components]` section of the DSC file. + +See this example in `SecurityPkgHostTest.dsc`... + +``` +[Components] + SecurityPkg/Library/SecureBootVariableLib/UnitTest/SecureBootVariableLib= UnitTest.inf { + + SecureBootVariableLib|SecurityPkg/Library/SecureBootVariableLib/Secu= reBootVariableLib.inf + UefiRuntimeServicesTableLib|SecurityPkg/Library/SecureBootVariableLi= b/UnitTest/MockUefiRuntimeServicesTableLib.inf + PlatformPKProtectionLib|SecurityPkg/Library/SecureBootVariableLib/Un= itTest/MockPlatformPKProtectionLib.inf + UefiLib|SecurityPkg/Library/SecureBootVariableLib/UnitTest/MockUefiL= ib.inf + } +``` + ### Framework Requirements - Code =20 Not to state the obvious, but let's make sure we have the following includ= e before getting too far along... @@ -143,7 +189,7 @@ Not to state the obvious, but let's make sure we have t= he following include befo #include ``` =20 -Now that we've got that squared away, let's look at our 'Main()'' routine = (or DriverEntryPoint() or whatever). +Now that we've got that squared away, let's look at our 'Main()' routine (= or DriverEntryPoint() or whatever). =20 ### Framework Configuration =20 @@ -157,7 +203,7 @@ The long name and version strings are just for user pre= sentation and relatively will be used to name any cache files and/or test results, so should be a n= ame that makes sense in that context. These strings are copied internally to the Framework, so using stack-alloc= ated or literal strings is fine. =20 -In the 'SampleUnitTestUefiShell' app, the module name is used as the short= name, so the initialization 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 = )); @@ -184,7 +230,7 @@ called once at the start of the suite (before _any_ tes= ts have run) and once at tests have run), respectively. If either or both of these are unneeded, pa= ss `NULL`. The function prototypes are `UNIT_TEST_SUITE_SETUP` and `UNIT_TEST_SUITE_TEARDOWN`. =20 -Looking at 'SampleUnitTestUefiShell' app, you can see that the first test = suite is created as below... +Looking at `SampleUnitTestUefiShell` app, you can see that the first test = suite is created as below... =20 ```c // @@ -218,7 +264,7 @@ of the context pointer is to allow test case reuse with= different input data. (A around a system reboot, but that's beyond the scope of this guide.) The te= st case must know how to interpret the context pointer, so it could be a simple value, or it could be a complex structure= . If unneeded, pass `NULL`. =20 -In 'SampleUnitTestUefiShell' app, the first test case is added using the c= ode below... +In `SampleUnitTestUefiShell` app, the first test case is added using the c= ode below... =20 ```c AddTestCase( SimpleMathTests, "Adding 1 to 1 should produce 2", "Addition"= , OnePlusOneShouldEqualTwo, NULL, NULL, NULL ); @@ -282,6 +328,607 @@ leverage the `cmocka.h` interface and write tests wit= h all the features of the C Documentation for Cmocka can be found here: https://api.cmocka.org/ =20 +## GoogleTest Libraries + +### GoogleTestLib + +GoogleTestLib is the core library used for GoogleTest in EDK II. This libr= ary is mainly a wrapper +around the GoogleTest and gMock header and source files. So all the standa= rd +[GoogleTest](http://google.github.io/googletest/) and +[gMock](https://github.com/google/googletest/tree/main/googlemock) documen= tation for writing tests +and using mocks applies. + +Additionally, to support the use of some primitive types that are not dire= ctly supported by +GoogleTest and gMock (but are needed to allow gMock to be used in EDK II),= some custom gMock +actions and matchers were added to GoogleTestLib. These customizations are= briefly described in +the following tables. + +#### Custom Actions + +| Action Name | Similar gMock Generic Action | Usage | +|:--- |:--- |:--- | +| `SetArgBuffer()` | `SetArgPointee()` | Used to set a buffer output argum= ent (such as UINT8*, VOID*, a structure pointer, etc.) with data in an expe= ct call. Can be used in an `EXPECT_CALL()` | + +#### Custom Matchers + +| Matcher Name | Similar gMock Generic Matcher | Usage | +|:--- |:--- |:--- | +| `BufferEq()` | `Pointee(Eq())` | Used to compare two buffer pointer type= s (such as UINT8*, VOID*, a structure pointer, etc.). Can be used in an `EX= PECT_CALL()`, `EXPECT_THAT()`, or anywhere else a matcher to compare two bu= ffers is needed. | +| `Char16StrEq()` | `Pointee(Eq())` | Used to compare two CHAR16* strings.= Can be used in an `EXPECT_CALL()`, `EXPECT_THAT()`, or anywhere else a mat= cher to compare two CHAR16* strings is needed. | + +### FunctionMockLib + +FunctionMockLib is the library that allows gMock to be used with free (C s= tyle) functions. This +library contains a set of macros that wrap gMock's MOCK_METHOD macro to en= able the standard gMock +capabilities to be used with free functions. The details of how this is im= plemented is outside the +scope of this document, but a brief description of each of the public macr= os in FunctionMockLib is +described below. A full example of how to use these macros to create a moc= k is described in a +later section. + +In total there are six public macros in FunctionMockLib. Four of the macro= s are related to creating +the mock functions, and the other two macros are related to creating an in= terface that is necessary +to contain the mock functions and connect them into the gMock framework. + +The macros used to create the interface are... +1. `MOCK_INTERFACE_DECLARATION(MOCK)` +2. `MOCK_INTERFACE_DEFINITION(MOCK)` + +These macros both take one argument which is the name of the interface for= the mock. The +`MOCK_INTERFACE_DECLARATION` macro is used to declare the interface in the= `.h` file and the +`MOCK_INTERFACE_DEFINITION` macro is used to define the interface in the `= .cpp` file. For +example, to create a mock for the `UefiLib`, a `MockUefiLib.h` file would = be created and the +below code would be added to it. + +```cpp +struct MockUefiLib { + MOCK_INTERFACE_DECLARATION(MockUefiLib); +}; +``` + +Additionally, the below code would be written into a `MockUefiLib.cpp` fil= e. + +```cpp +MOCK_INTERFACE_DEFINITION(MockUefiLib); +``` + +The macros used to create the mock functions are... +1. `MOCK_FUNCTION_DECLARATION(RET_TYPE, FUNC, ARGS)` +2. `MOCK_FUNCTION_DEFINITION(MOCK, FUNC, NUM_ARGS, CALL_TYPE)` +3. `MOCK_FUNCTION_INTERNAL_DECLARATION(RET_TYPE, FUNC, ARGS)` +4. `MOCK_FUNCTION_INTERNAL_DEFINITION(MOCK, FUNC, NUM_ARGS, CALL_TYPE)` + +You will notice that there are two sets of macros: one to mock external fu= nctions and +another to mock internal functions. Each set of macros has the exact same = arguments, but +they are used for slightly different use cases. The details of these diffe= rent use cases +is described in detail in a later section. For now, we will focus on the u= sage of the macro +arguments since that is common between them. + +The `MOCK_FUNCTION_DECLARATION` macro is used to declare the mock function= in the `.h` file, +and it takes three arguments: return type, function name, and argument lis= t. The +`MOCK_FUNCTION_DEFINITION` macro is used to define the mock function in th= e `.cpp` file, +and it takes four arguments: name of the interface for the mock, function = name, number of +arguments the function takes, and calling convention type of the function.= For example, to +continue with the `UefiLib` mock example above, the `GetVariable2` and `Ge= tEfiGlobalVariable2` +functions are declared in `UefiLib.h` as shown below. + +```cpp +EFI_STATUS +EFIAPI +GetVariable2 ( + IN CONST CHAR16 *Name, + IN CONST EFI_GUID *Guid, + OUT VOID **Value, + OUT UINTN *Size OPTIONAL + ); + +EFI_STATUS +EFIAPI +GetEfiGlobalVariable2 ( + IN CONST CHAR16 *Name, + OUT VOID **Value, + OUT UINTN *Size OPTIONAL + ); +``` + +To declare mocks for these functions within the previously created `MockUe= fiLib` interface, +the below code would be added to the `MockUefiLib.h` file. Note that the p= reviously added +interface declaration is also included in the code below for context. + +```cpp +struct MockUefiLib { + MOCK_INTERFACE_DECLARATION(MockUefiLib); + + MOCK_FUNCTION_DECLARATION(EFI_STATUS, GetVariable2, + (IN CONST CHAR16 *Name, + IN CONST EFI_GUID *Guid, + OUT VOID **Value, + OUT UINTN *Size OPTIONAL)); + + MOCK_FUNCTION_DECLARATION(EFI_STATUS, GetEfiGlobalVariable2, + (IN CONST CHAR16 *Name, + OUT VOID **Value, + OUT UINTN *Size OPTIONAL)); +}; +``` + +Additionally, the below code would be added into the `MockUefiLib.cpp` fil= e to provide +the definitions for these mock functions. Again, the previously added inte= rface +definition is also included in the code below for context. + +```cpp +MOCK_INTERFACE_DEFINITION(MockUefiLib); + +MOCK_FUNCTION_DEFINITION(MockUefiLib, GetVariable2, 4, EFIAPI); +MOCK_FUNCTION_DEFINITION(MockUefiLib, GetEfiGlobalVariable2, 3, EFIAPI); +``` + +That concludes the basic overview on how to use the macros, but a more det= ailed +description on how to name the mocks, where to put the files, how to build= the +mocks, and how to use the mocks is described in detail later. + +### SubhookLib + +SubhookLib is the library used by FunctionMockLib to implement the macros = to +mock internal functions: `MOCK_FUNCTION_INTERNAL_DECLARATION` and +`MOCK_FUNCTION_INTERNAL_DEFINITION`. These macros require the additional +functionality provided by SubhookLib because they create mock functions +for functions that are already defined and compiled within the module bein= g +tested. More detail on this is provided in a later section, but for now it= is +sufficient to know that the SubhookLib allows a second definition of the +function to be compiled into the test application and then hooked to durin= g a +test. + +This library is mainly a wrapper around the +[subhook](https://github.com/Zeex/subhook) header and source files. It is +important to note that the use of the mock function macros and the creatio= n +of mock functions requires no knowledge about the SubhookLib. The SubhookL= ib +library is entirely hidden and encapsulated within FunctionMockLib, and it +is only mentioned here to provide a complete explanation on all the librar= ies +used in the implementation. + +## FunctionMockLib Mocks + +This section describes the details on how to use the mock function macros = in +FunctionMockLib to create mock functions, name them, organize their files, +and build them so that they can be used within GoogleTest tests. The usage +of the mock functions is detailed in a later section while this section +simply details how to create them, build them, and where to put them. + +### FunctionMockLib Mocks - External vs. Internal + +The first question to ask when creating a mock function is if the function= being +mocked is external or internal to the module being tested. This is very im= portant +because the macros in FunctionMockLib used to create the mock function are= named +differently for these two use cases. Fortunately, the arguments to these m= acros +and the usage of the mock functions within the tests are exactly the same. +However, because of the different underlying implementations, two differen= t sets +of macros are used. + +A more detailed description of when to use the external vs. internal mock = function +macros is in the following sections, but the quick summary is as follows. + +* External mock function macros are used to mock functions that are outsid= e the +module being tested and use link-time replacement. +* Internal mock function macros are used to mock functions that are inside= the +module being tested and use run-time replacement. + +The below table shows which macros to use in these two use cases. However,= note that +for the creation of the interface, the same macros are used in both cases. + +| Mock Function Use Case | Mock Interface Macros | Mock Function Macros | +|:--- |:--- |:--- | +| External mock functions | `MOCK_INTERFACE_DECLARATION`
`MOCK_INTERFA= CE_DEFINITION` | `MOCK_FUNCTION_DECLARATION`
`MOCK_FUNCTION_DEFINITION`= | +| Internal mock functions | `MOCK_INTERFACE_DECLARATION`
`MOCK_INTERFA= CE_DEFINITION` | `MOCK_FUNCTION_INTERNAL_DECLARATION`
`MOCK_FUNCTION_IN= TERNAL_DEFINITION` | + +#### FunctionMockLib Mocks - External mock function + +The external mock function macros are used to create mock function definit= ions +for a library, global service, or protocol that is defined outside of the = module +being tested. These mock function definitions are linked into the test app= lication +instead of linking in the design function definitions. In other words, the +external mock function macros use link-time replacement of the design func= tions. + +The `.h/.cpp` files for these mock functions are created within the packag= e +directory where the library, global table, or protocol that is being mocke= d is +declared. These files are compiled into their own separate library (using +an INF file) that can be shared and linked into many test applications, bu= t more +on that later. + +#### FunctionMockLib Mocks - Internal mock function + +The internal mock function macros are used to create mock function definit= ions +for functions that are defined within the module being tested. These mock +function definitions are compiled into the test application along with the= design +function definitions. This is possible because the mock functions are give= n a +slightly different name during compilation. Then during test execution, th= e +design function is hooked and replaced with the mock function. In other wo= rds, +the internal mock function macros use run-time replacement of the design +functions. + +The `.h/.cpp` files for these mock functions are created within the Google= Test +directory containing the specific tests that are using them. These files a= re +compiled directly in the GoogleTest INF file that builds the test applicat= ion, +and they are not shared outside of that GoogleTest directory, but more on = that +later. + +### FunctionMockLib Mocks - Declaration + +The declaration of mock functions using the FunctionMockLib macros are don= e +in header files. The name of the header file is determined by the interfac= e +(such as a library or a protocol) that is being created for the mock funct= ions. +The rules for naming the file are shown in the table below. + +| Interface Type | Header File Name | +| :--- | :--- | +| Library | Mock\Lib.h | +| Global Table (e.g. gRT, gBS, etc.) | Mock\Lib.h= | +| Protocol | Mock\Protocol.h | + +The below table shows examples for file names with each of the above cases= . + +| Interface Type | Interface Name | Header File Name | +| :--- | :--- | :--- | +| Library | UefiLib | MockUefiLib.h | +| Global Table (e.g. gRT, gBS, etc.) | UefiRuntimeServicesTableLib | MockU= efiRuntimeServicesTableLib.h | +| Protocol | EFI_USB_IO_PROTOCOL | MockEfiUsbIoProtocol.h | + +Once the header file name is known, the file needs to be created in the pr= oper +location. For internal mock functions, the location is simply the same +GoogleTest directory that contains the INF file that builds the test appli= cation. +For external mock functions, the location is within the `Test` directory u= nder the +package where the library, global table, or protocol that is being mocked = is +declared. The exact location depends on the interface type and is shown in= the +below table. + +| Interface Type | Header File Location | +| :--- | :--- | +| Library | \/Test/Mock/Include/Library/GoogleTest | +| Global Table (e.g. gRT, gBS, etc.) | \/Test/Mock/Include/L= ibrary/GoogleTest | +| Protocol | \/Test/Mock/Include/Protocol/GoogleTest | + +The below table shows examples for file locations with each of the above c= ases. + +| Interface Type | Interface Name | Header File Location | +| :--- | :--- | :--- | +| Library | UefiLib | MdePkg/Test/Mock/Include/GoogleTest/Library/MockUefi= Lib.h | +| Global Table (e.g. gRT, gBS, etc.) | UefiRuntimeServicesTableLib | MdePk= g/Test/Mock/Include/GoogleTest/Library/MockUefiRuntimeServicesTableLib.h | +| Protocol | EFI_USB_IO_PROTOCOL | MdePkg/Test/Mock/Include/Protocol/Googl= eTest/MockEfiUsbIoProtocol.h | + +Now that the file location is known, the contents can be added to it. At t= he top +of the file, the `GoogleTestLib.h` and `FunctionMockLib.h` files are alway= s added. +Following these includes other EDK II related include files are added and = must be +wrapped in `extern "C" {}` because they are C include files. Failure to do= this +will cause link errors to occur. Note that a `#include` of the interface b= eing +mocked must also be added. This causes the declarations of the functions b= eing +mocked to be included in the compilation and allows the compilation to ver= ify +that the function signatures of the mock and design functions are identica= l. + +After the standard `#ifndef` for a header file is added, a `struct` is dec= lared +using the same name as the header file (which was determined using the rul= es +above). Within this structure declaration a `MOCK_INTERFACE_DECLARATION` i= s +added along with a `MOCK_FUNCTION_DECLARATION` (or a +`MOCK_FUNCTION_INTERNAL_DECLARATION` if this interface is for internal moc= k +functions) for each function in the interface. To build on the examples ab= ove, +the complete `MockUefiLib.h` file would be as shown below. Note that for b= revity +only the `GetVariable2` and `GetEfiGlobalVariable2` declarations are inclu= ded in +the example. + +```cpp +#include +#include +extern "C" { + #include + #include +} + +#ifndef MOCK_UEFI_LIB_H +#define MOCK_UEFI_LIB_H + +struct MockUefiLib { + MOCK_INTERFACE_DECLARATION(MockUefiLib); + + MOCK_FUNCTION_DECLARATION(EFI_STATUS, GetVariable2, + (IN CONST CHAR16 *Name, + IN CONST EFI_GUID *Guid, + OUT VOID **Value, + OUT UINTN *Size OPTIONAL)); + + MOCK_FUNCTION_DECLARATION(EFI_STATUS, GetEfiGlobalVariable2, + (IN CONST CHAR16 *Name, + OUT VOID **Value, + OUT UINTN *Size OPTIONAL)); +}; + +#endif +``` + +In the case of libraries, the function names in the mock declarations +align exactly with the function names in the design. However, in the +case of global tables and protocols, to eliminate possible function +name collisions, the names are adjusted slightly in the mock +declarations as shown in the below table. + +| Mock Function Use Case | Design Function Name | Mock Function Name | +| :--- | :--- | :--- | +| Library | GetVariable2 | GetVariable2 | +| Global Table (e.g. gRT, gBS, etc.) | gRT->GetVariable | gRT_GetVariable = | +| Protocol | UsbIoProtocol->UsbPortReset | UsbIoProtocol_UsbPortReset | + +Lastly, when creating mock functions, there are two limitations to be +aware of in gMock that extend into FunctionMockLib. + +1. gMock does not support mocking functions that have more than 15 argumen= ts. +2. gMock does not support mocking variadic functions. + +With those limitations in mind, that completes the mock function +declarations, and now the mock function definitions for those declarations +can be created. + +### FunctionMockLib Mocks - Definition + +The definition of mock functions using the FunctionMockLib macros are done +in source files. The name of the source file is determined by the interfac= e +(such as a library or a protocol) that is being created for the mock funct= ions. +The rules for naming the file align with the naming of the file for declar= ations +and are shown in the table below. + +| Interface Type | Source File Name | +| :--- | :--- | +| Library | Mock\Lib.cpp | +| Global Table (e.g. gRT, gBS, etc.) | Mock\Lib.c= pp | +| Protocol | Mock\Protocol.cpp | + +The below table shows examples for file names with each of the above cases= . + +| Interface Type | Interface Name | Source File Name | +| :--- | :--- | :--- | +| Library | UefiLib | MockUefiLib.cpp | +| Global Table (e.g. gRT, gBS, etc.) | UefiRuntimeServicesTableLib | MockU= efiRuntimeServicesTableLib.cpp | +| Protocol | EFI_USB_IO_PROTOCOL | MockEfiUsbIoProtocol.cpp | + +Once the source file name is known, the file needs to be created in the pr= oper +location. The location of the source file is aligned with the location for= the +header file. For internal mock functions, the location is simply the same +GoogleTest directory that contains the INF file that builds the test appli= cation. +For external mock functions, the location is within the `Test` directory u= nder the +package where the library, global table, or protocol that is being mocked = is +declared. The exact location depends on the interface type and is shown in= the +below table. + +| Interface Type | Source File Location | +| :--- | :--- | +| Library | \/Test/Mock/Library/GoogleTest/Mock\Lib | +| Global Table (e.g. gRT, gBS, etc.) | \/Test/Mock/Library/G= oogleTest/Mock\Lib | +| Protocol | \/Test/Mock/Library/GoogleTest/Mock\Protocol | + +The below table shows examples for file locations with each of the above c= ases. + +| Interface Type | Interface Name | Source File Location | +| :--- | :--- | :--- | +| Library | UefiLib | MdePkg/Test/Mock/Library/GoogleTest/MockUefiLib/Mock= UefiLib.cpp | +| Global Table (e.g. gRT, gBS, etc.) | UefiRuntimeServicesTableLib | MdePk= g/Test/Mock/Library/GoogleTest/MockUefiRuntimeServicesTableLib/MockUefiRunt= imeServicesTableLib.cpp | +| Protocol | EFI_USB_IO_PROTOCOL | MdePkg/Test/Mock/Library/GoogleTest/Moc= kEfiUsbIoProtocol/MockEfiUsbIoProtocol.cpp | + +Now that the file location is known, the contents can be added to it. At t= he top +of the file, the header file containing the mock function declarations is = always +added. After this `#include`, the interface definition is created using +`MOCK_INTERFACE_DEFINITION` with the interface name that was used in the m= ock +function declaration header file. A `MOCK_FUNCTION_DEFINITION` is then add= ed (or +a `MOCK_FUNCTION_INTERNAL_DEFINITION` if this interface is for internal mo= ck +functions) for each function that was declared in the interface. To build = on the +prior declaration examples, the complete `MockUefiLib.cpp` file would be a= s shown +below. Note that for brevity only the `GetVariable2` and `GetEfiGlobalVari= able2` +definitions are included in the example. + +```cpp +#include + +MOCK_INTERFACE_DEFINITION(MockUefiLib); + +MOCK_FUNCTION_DEFINITION(MockUefiLib, GetVariable2, 4, EFIAPI); +MOCK_FUNCTION_DEFINITION(MockUefiLib, GetEfiGlobalVariable2, 3, EFIAPI); +``` + +When creating the defintions, there are a few things to keep in mind. + +First, when using `MOCK_FUNCTION_DEFINITION`, some functions being mocked = do +not specify a calling convention. In this case, it is fine to leave the la= st +argument of `MOCK_FUNCTION_DEFINITION` empty. For example, if `GetVariable= 2` +did not specify the `EFIAPI` calling convention in its declaration, then t= he +below code would be used for the mock function definition. + +```cpp +MOCK_FUNCTION_DEFINITION(MockUefiLib, GetVariable2, 4, ); +``` + +Second, the function name used in `MOCK_FUNCTION_DEFINITION` must align wi= th +the function name used in the associated `MOCK_FUNCTION_DECLARATION` in th= e +header file. + +Last, if the interface is mocking a global table or protocol, then the str= ucture +of function pointers for that interface must also be defined within the so= urce +file as a `static` structure with the mock function definitions being assi= gned +to the associated entries in the structure. The address of this `static` +structure is then assigned to the global table or protocol pointer. Note t= hat +this pointer must be wrapped in `extern "C" {}` because it needs C style +linkage. Failure to do this will cause link errors to occur. For example, = when +creating the definition of the mock for the global runtime services table,= the +complete `MockUefiRuntimeServicesTableLib.cpp` file would be as shown belo= w. +Note that for brevity only the `GetVariable` and `SetVariable` definitions= are +included in the example. + +```cpp +#include + +MOCK_INTERFACE_DEFINITION(MockUefiRuntimeServicesTableLib); + +MOCK_FUNCTION_DEFINITION(MockUefiRuntimeServicesTableLib, gRT_GetVariable,= 5, EFIAPI); +MOCK_FUNCTION_DEFINITION(MockUefiRuntimeServicesTableLib, gRT_SetVariable,= 5, EFIAPI); + +static EFI_RUNTIME_SERVICES localRt =3D { + {0}, // EFI_TABLE_HEADER + + NULL, // EFI_GET_TIME + NULL, // EFI_SET_TIME + NULL, // EFI_GET_WAKEUP_TIME + NULL, // EFI_SET_WAKEUP_TIME + + NULL, // EFI_SET_VIRTUAL_ADDRESS_MAP + NULL, // EFI_CONVERT_POINTER + + gRT_GetVariable, // EFI_GET_VARIABLE + NULL, // EFI_GET_NEXT_VARIABLE_NAME + gRT_SetVariable, // EFI_SET_VARIABLE + + NULL, // EFI_GET_NEXT_HIGH_MONO_COUNT + NULL, // EFI_RESET_SYSTEM + + NULL, // EFI_UPDATE_CAPSULE + NULL, // EFI_QUERY_CAPSULE_CAPABILITIES + + NULL, // EFI_QUERY_VARIABLE_INFO +}; + +extern "C" { + EFI_RUNTIME_SERVICES* gRT =3D &localRt; +} +``` + +That completes the mock function definitions. So now these mock function +definitions can be compiled. + +### FunctionMockLib Mocks - Build + +The building of mock functions using FunctionMockLib is done slightly +differently for external and internal function mocks. External mock +functions are built using their own separate INF file and internal mock +functions are built as source files directly referenced in the GoogleTest +INF file that builds the test application. + +#### FunctionMockLib Mocks - Build External Mock Functions + +The building of external mock functions is done using their own separate I= NF +file which is placed in the same location as the associated source file +containing the mock function definitions. The name of the INF file is exac= tly +the same as the mock function definitions file, but uses the `.inf` extens= ion +rather than `.cpp`. + +Within the `.inf` file the `BASE_NAME` should be set to the same name as t= he +file (minus the extension), the `MODULE_TYPE` should be set to `BASE`, and= the +`LIBRARY_CLASS` should be the same as the `BASE_NAME` but without the `Moc= k` +prefix. Additionally, the `LIBRARY_CLASS` should be specified to only supp= ort +the `HOST_APPLICATION` module type since these mock function definitions w= ill +only be compiled into host application test applications. + +The `[Sources]` section will contain the single mock function definition +source file, the `[Packages]` section will contain all the necessary DEC +files to compile the mock functions (which at a minimum will include the +`UnitTestFrameworkPkg.dec` file, the `[LibraryClasses]` section will conta= in +the `GoogleTestLib`, and the `[BuildOptions]` will need to append the `/EH= sc` +compilation flag to all MSFT builds to enable proper use of the C++ except= ion +handler. Below is the complete `MockUefiLib.inf` as an example. + +``` +[Defines] + INF_VERSION =3D 0x00010005 + BASE_NAME =3D MockUefiLib + FILE_GUID =3D 47211F7A-6D90-4DFB-BDF9-610B69197C2E + MODULE_TYPE =3D BASE + VERSION_STRING =3D 1.0 + LIBRARY_CLASS =3D UefiLib|HOST_APPLICATION + +# +# The following information is for reference only and not required by the = build tools. +# +# VALID_ARCHITECTURES =3D IA32 X64 +# + +[Sources] + MockUefiLib.cpp + +[Packages] + MdePkg/MdePkg.dec + UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec + +[LibraryClasses] + GoogleTestLib + +[BuildOptions] + MSFT:*_*_*_CC_FLAGS =3D /EHsc + +``` + +To ensure that this specific set of mock functions are always buildable ev= en +if no test uses it yet, this created INF file needs to be added into the +`[Components]` section of the associated `Test` DSC file for the package i= n +which this INF file resides. For example, the above `MockUefiLib.inf` woul= d +need to be added to the `MdePkg/Test/MdePkgHostTest.dsc` file as shown bel= ow. + +``` +[Components] + MdePkg/Test/Mock/Library/GoogleTest/MockUefiLib/MockUefiLib.inf +``` + +This created INF file will also be referenced within the necessary `Test` = DSC +files in order to include the mock function definitions in the test +applications which use this set of mock functions, but more on that later. + +One small additional requirement is that if this INF file is added into a +package that does not yet have any other external mock functions in it, th= en +that package's DEC file will need to have the mock include directory (more +specifically the `Test/Mock/Include` directory) added to its `[Includes]` +section so that test files who want to use the mock functions will be able= to +locate the mock function header file. For example, if `MockUefiLib.inf` we= re +the first mock added to the `MdePkg`, then the below snippet would need to= be +added to the `MdePkg.dec` file. + +``` +[Includes] + Test/Mock/Include +``` + +#### FunctionMockLib Mocks - Build Internal Mock Functions + +The building of internal mock functions is done using the GoogleTest INF f= ile +that already needs to exist to build the test application. This is easy to +manage since the source and header files for the internal mock functions a= re +also located in the same GoogleTest directory as the GoogleTest INF file t= hat +will reference them. + +The only additions that are required to the GoogleTest INF file are that t= he +mock function definitions file be added to the `[Sources]` section, the +`UnitTestFrameworkPkg.dec` file be added to the `[Packages]` section, and = the +`GoogleTestLib` and `SubhookLib` be added to the `[LibraryClasses]` sectio= n. +Below is a minimal contrived example for a `MyModuleGoogleTest.inf` that u= ses a +`MockMyModuleInternalFunctions.cpp` source file for its internal mock func= tions. + +``` +[Defines] + INF_VERSION =3D 0x00010017 + BASE_NAME =3D MyModuleGoogleTest + FILE_GUID =3D 814B09B9-2D51-4786-8A77-2E10CD1C55F3 + VERSION_STRING =3D 1.0 + MODULE_TYPE =3D HOST_APPLICATION + +# +# The following information is for reference only and not required by the = build tools. +# +# VALID_ARCHITECTURES =3D IA32 X64 +# + +[Sources] + MyModuleGoogleTest.cpp + MockMyModuleInternalFunctions.cpp + +[Packages] + MdePkg/MdePkg.dec + UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec + +[LibraryClasses] + GoogleTestLib + SubhookLib +``` + ## GoogleTest Samples =20 There is a sample unit test provided as both an example of how to write a = unit test and leverage @@ -291,22 +938,31 @@ directory. The sample is provided for the HOST_APPLICATION build type, which can be r= un on a host system without needing a target. =20 +There is also a sample unit test provided as both an example of how to wri= te a unit test with +mock functions and leverage some of the gMock features. This sample can be= found in the +`SecurityPkg/Library/SecureBootVariableLib/GoogleTest` directory. + +It too is provided for the HOST_APPLICATION build type, which can be run o= n a host system without +needing a target. + ## GoogleTest Usage =20 This section is built a lot like a "Getting Started". We'll go through som= e of the components that are needed when constructing a unit test and some of the decisions that are made by t= he test writer. We'll also describe how to check for expected conditions in test cases and a bit of the loggin= g characteristics. =20 -Most of these examples will refer to the SampleGoogleTestHost app found in= this package. +Most of these examples will refer to the `SampleGoogleTestHost` app found = in this package, but +the examples related to mock functions will refer to the `SecureBootVariab= leLibGoogleTest` app +found in the `SecurityPkg`. =20 ### GoogleTest Requirements - INF =20 -In our INF file, we'll need to bring in the `GoogleTest` library. Convenie= ntly, the interface -header for the `GoogleTest` is in `UnitTestFrameworkPkg`, so you shouldn't= need to depend on any other +In our INF file, we'll need to bring in the `GoogleTestLib` library. Conve= niently, the interface +header for the `GoogleTestLib` is in `UnitTestFrameworkPkg`, so you should= n't need to depend on any other packages. As long as your DSC file knows where to find the lib implementat= ion that you want to use, you should be good to go. =20 -See this example in 'SampleGoogleTestHost.inf'... +See this example in `SampleGoogleTestHost.inf`... =20 ``` [Packages] @@ -319,7 +975,7 @@ See this example in 'SampleGoogleTestHost.inf'... DebugLib ``` =20 -Also, if you want you test to automatically be picked up by the Test Runne= r plugin, you will need +Also, if you want your test to automatically be picked up by the Test Runn= er plugin, you will need to make sure that the module `BASE_NAME` contains the word `Test`... =20 ``` @@ -327,12 +983,54 @@ to make sure that the module `BASE_NAME` contains the= word `Test`... BASE_NAME =3D SampleGoogleTestHost ``` =20 +### GoogleTest Requirements - DSC + +In our DSC file, we'll need to bring in the INF file that was just created= into the `[Components]` +section so that the unit tests will be built. + +See this example in `UnitTestFrameworkPkgHostTest.dsc`... + +``` +[Components] + UnitTestFrameworkPkg/Test/GoogleTest/Sample/SampleGoogleTest/SampleGoogl= eTestHost.inf +``` + +Also, based on the type of tests that are being created, the associated DS= C include file from the +UnitTestFrameworkPkg for Host or Target based tests should also be include= d at the top of the DSC +file. + +``` +!include UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc +``` + +Lastly, in the case that the test build has specific dependent libraries a= ssociated with it, +they should be added in the \ sub-section for the INF fil= e in the +`[Components]` section of the DSC file. Note that it is within this sub-se= ction where you can +control whether the design or mock version of a component is linked into t= he test exectuable. + +See this example in `SecurityPkgHostTest.dsc` where the `SecureBootVariabl= eLib` design is +being tested using mock versions of `UefiRuntimeServicesTableLib`, `Platfo= rmPKProtectionLib`, +and `UefiLib`... + +``` +[Components] + SecurityPkg/Library/SecureBootVariableLib/GoogleTest/SecureBootVariableL= ibGoogleTest.inf { + + SecureBootVariableLib|SecurityPkg/Library/SecureBootVariableLib/Secu= reBootVariableLib.inf + UefiRuntimeServicesTableLib|MdePkg/Test/Mock/Library/GoogleTest/Mock= UefiRuntimeServicesTableLib/MockUefiRuntimeServicesTableLib.inf + PlatformPKProtectionLib|SecurityPkg/Test/Mock/Library/GoogleTest/Moc= kPlatformPKProtectionLib/MockPlatformPKProtectionLib.inf + UefiLib|MdePkg/Test/Mock/Library/GoogleTest/MockUefiLib/MockUefiLib.= inf + } +``` + ### GoogleTest Requirements - Code =20 -Not to state the obvious, but let's make sure we have the following includ= e before getting too far along... +GoogleTest applications are implemented in C++, so make sure that your tes= t file has +a `.cpp` extension. With that behind us, not to state the obvious, but let= 's make sure +we have the following includes before getting too far along in the file... =20 -``` -#include +```cpp +#include extern "C" { #include #include @@ -340,33 +1038,75 @@ extern "C" { } ``` =20 -GoogleTest applications are implemented in C++. The first include brings i= n 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. +The first include brings in the GoogleTest definitions. Other EDK II relat= ed include +files must be wrapped in `extern "C" {}` because they are C include files.= Link +failures will occur if this is not done. =20 -Now that we've got that squared away, let's look at our 'Main()'' routine = (or DriverEntryPoint() or whatever). +Also, when using GoogleTest it is helpful to add a `using` declaration for= its +`testing` namespace. This `using` statement greatly reduces the amount of = code you +need to write in the tests when referencing the utilities within the `test= ing` +namespace. For example, instead of writing `::testing::Return` or `::testi= ng::Test`, +you can just write `Return` or `Test` respectively, and these types of ref= erences +occur numerous times within the tests. + +Lastly, in the case that tests within a GoogleTest application require the= usage of +mock functions, it is also necessary to include the header files for those= interfaces +as well. As an example, the `SecureBootVariableLibGoogleTest` uses the moc= k versions +of `UefiLib` and `UefiRuntimeServicesTableLib`. So its test file contains = the below +includes. Note that the `using` declaration mentioned above is also shown = in the code +below for completeness of the example. + +```cpp +#include +#include +#include + +extern "C" { + #include + ... +} + +using namespace testing; +``` + +Now that we've got that squared away, let's look at our 'Main()' routine (= or DriverEntryPoint() or whatever). =20 ### GoogleTest Configuration =20 Unlike the Framework, GoogleTest does not require test suites or test case= s to be registered. Instead, the test cases declare the test suite name and tes= t case name as part of their implementation. The only requirement for Google= Test -is to have a `main()` function that initialize the GoogleTest infrastructu= re and -call the service `RUN_ALL_TESTS()` to run all the unit tests. +is to have a `main()` function that initializes the GoogleTest infrastruct= ure +and calls the service `RUN_ALL_TESTS()` to run all the unit tests. =20 -```c +```cpp int main(int argc, char* argv[]) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ``` =20 +However, while GoogleTest does not require test suites or test cases to be +registered, there is still one rule within EDK II that currently needs to = be +followed. This rule is that all tests for a given GoogleTest application m= ust +be contained within the same source file that contains the `main()` functi= on +shown above. These tests can be written directly in the file or a `#includ= e` +can be used to add them into the file indirectly. + +The reason for this is due to EDK II taking the host application INF file = and +first compiling all of its source files into a static library. This static +library is then linked into the final host application. The problem with t= his +method is that only the tests in the object file containing the `main()` +function are linked into the final host application. This is because the o= ther +tests are contained in their own object files within the static library an= d +they have no symbols in them that the final host application depends on, s= o +those object files are not linked into the final host application. + ### GoogleTest - A Simple Test Case =20 -We'll look at the below test case from 'SampleGoogleTestHost'... +Below is a sample test case from `SampleGoogleTestHost`. =20 -```c +```cpp TEST(SimpleMathTests, OnePlusOneShouldEqualTwo) { UINTN A; UINTN B; @@ -393,9 +1133,175 @@ detailed logs. When in doubt, there are always `ASSE= RT_TRUE` and `ASSERT_FALSE`. test criterium will immediately return from the test case with a failed st= atus and log an error string. _Note_ that this early return can have implications for memory leakage. =20 -There is no return status from a GooglTest unit test. If no assertions are +For most `ASSERT` macros in GoogleTest there is also an equivalent `EXPECT= ` macro. Both macro versions +will ultimately cause the `TEST` to fail if the check fails. However, the = difference between the two +macro versions is that when the check fails, the `ASSERT` version immediat= ely returns from the `TEST` +while the `EXPECT` version continues running the `TEST`. + +There is no return status from a GooglTest unit test. If no assertions (or= expectations) are triggered then the unit test has a passing status. =20 +### GoogleTest - A gMock Test Case + +Below is a sample test case from `SecureBootVariableLibGoogleTest`. Althou= gh +actually, the test case is not written exactly like this in the test file,= but +more on that in a bit. + +```cpp +TEST(SetSecureBootModeTest, SetVarError) { + MockUefiRuntimeServicesTableLib RtServicesMock; + UINT8 SecureBootMode; + EFI_STATUS Status; + + // Any random magic number can be used for these tests + SecureBootMode =3D 0xAB; + + EXPECT_CALL(RtServicesMock, gRT_SetVariable) + .WillOnce(Return(EFI_INVALID_PARAMETER)); + + Status =3D SetSecureBootMode(SecureBootMode); + EXPECT_EQ(Status, EFI_INVALID_PARAMETER); +} +``` + +Keep in mind that this test is written to verify that `SetSecureBootMode()= ` will +return `EFI_INVALID_PARAMETER` when the call to `gRT->SetVariable()` withi= n the +implementation of `SetSecureBootMode()` returns `EFI_INVALID_PARAMETER`. W= ith that +in mind, let's discuss how a mock function is used to accomplish this in t= he test. + +In this test case, the `MockUefiRuntimeServicesTableLib` interface is inst= antiated as +`RtServicesMock` which enables its associated mock functions. These interf= ace +instantiations that contain the mock functions are very important for mock= function +based unit tests because without these instantiations, the mock functions = within that +interface will not exist and can not be used. + +The next line of interest is the `EXPECT_CALL`, which is a standard part o= f the gMock +framework. This macro is telling the test that a call is expected to occur= to a +specific function on a specific interface. The first argument is the name = of the +interface object that was instantiated in this test, and the second argume= nt is the +name of the mock function within that interface that is expected to be cal= led. The +`WillOnce(Return(EFI_INVALID_PARAMETER))` associated with this `EXPECT_CAL= L` states +that the `gRT_SetVariable()` function (remember from earlier in this docum= entation +that this refers to the `gRT->SetVariable()` function) will be called once= during +this test, and when it does get called, we want it to return `EFI_INVALID_= PARAMETER`. + +Once this `EXPECT_CALL` has been setup, the call to `SetSecureBootMode()` = occurs in +the test, and its return value is saved in `Status` so that it can be test= ed. Based +on the `EXPECT_CALL` that was setup earlier, when `SetSecureBootMode()` in= ternally +calls `gRT->SetVariable()`, it returns `EFI_INVALID_PARAMETER`. This value= should +then be returned by `SetSecureBootMode()`, and the +`EXPECT_EQ(Status, EFI_INVALID_PARAMETER)` verifies this is the case. + +There is much more that can be done with `EXPECT_CALL` and mock functions,= but we +will leave those details to be explained in the gMock documentation. + +Now it was mentioned earlier that this test case is not written exactly li= ke this +in the test file, and the next section describes how this test is slightly +refactored to reduce the total amount of code in the entire test suite. + +### GoogleTest - A gMock Test Case (refactored) + +The sample test case from `SecureBootVariableLibGoogleTest` in the prior s= ection is +actually written as shown below. + +```cpp +class SetSecureBootModeTest : public Test { + protected: + MockUefiRuntimeServicesTableLib RtServicesMock; + UINT8 SecureBootMode; + EFI_STATUS Status; + + void SetUp() override { + // Any random magic number can be used for these tests + SecureBootMode =3D 0xAB; + } +}; + +TEST_F(SetSecureBootModeTest, SetVarError) { + EXPECT_CALL(RtServicesMock, gRT_SetVariable) + .WillOnce(Return(EFI_INVALID_PARAMETER)); + + Status =3D SetSecureBootMode(SecureBootMode); + EXPECT_EQ(Status, EFI_INVALID_PARAMETER); +} +``` + +This code may at first seem more complicated, but you will notice that the= code +with in it is still the same. There is still a `MockUefiRuntimeServicesTab= leLib` +instantiation, there is still a `SecureBootMode` and `Status` variable def= ined, +there is still an `EXPECT_CALL`, and etc. However, the benefit of construc= ting +the test this way is that the new `TEST_F()` requires less code than the p= rior +`TEST()`. + +This is made possible by the usage of what GoogleTest calls a _test fixtur= e_. +This concept of a test fixture allows multiple tests to use (or more speci= fically +inherit from a base class) a common set of variables and initial condition= s. +Notice that using `TEST_F()` requires the first argument to be a name that= aligns +with a test fixture (in this case `SetSecureBootModeTest`), and the second +argument is the name of the test (just like in the `TEST()` macro). + +All `TEST_F()` tests that use a specific test fixture can be thought of as= having +all of that test fixture's variables automatically defined in the test as = well as +having that text fixture's `SetUp()` function called before entering the t= est. + +This means that another `TEST_F()` can be written without needing to worry= about +defining a bunch of variables or instantiating a bunch of interfaces for m= ock +functions. For example, the below test (also in `SecureBootVariableLibGoog= leTest`) +uses the same test fixture and makes use of its `RtServicesMock`, `Status`= , and +`SecureBootMode` variables. + +```cpp +TEST_F(SetSecureBootModeTest, PropogateModeToSetVar) { + EXPECT_CALL(RtServicesMock, + gRT_SetVariable( + Char16StrEq(EFI_CUSTOM_MODE_NAME), + BufferEq(&gEfiCustomModeEnableGuid, sizeof(EFI_GUID)), + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof(SecureBootMode), + BufferEq(&SecureBootMode, sizeof(SecureBootMode)))) + .WillOnce(Return(EFI_SUCCESS)); + + Status =3D SetSecureBootMode(SecureBootMode); + EXPECT_EQ(Status, EFI_SUCCESS); +} +``` + +The biggest benefit is that the `TEST_F()` code can now focus on what is b= eing +tested and not worry about any repetitive setup. There is more that can be= done +with test fixtures, but we will leave those details to be explained in the +gMock documentation. + +Now, as for what is in the above test, it is slightly more complicated tha= n the +first test. So let's explain this added complexity and what it is actually +testing. In this test, there is still an `EXPECT_CALL` for the +`gRT_SetVariable()` function. However, in this test we are stating that we +expect the input arguments passed to `gRT_SetVariable()` be specific value= s. +The order they are provided in the `EXPECT_CALL` align with the order of t= he +arguments in the `gRT_SetVariable()` function. In this case the order of t= he +`gRT_SetVariable()` arguments is as shown below. + +```cpp +IN CHAR16 *VariableName, +IN EFI_GUID *VendorGuid, +IN UINT32 Attributes, +IN UINTN DataSize, +IN VOID *Data +``` + +So in the `EXPECT_CALL` we are stating that the call to `gRT_SetVariable()= ` +will be made with the below input argument values. + +1. `VariableName` is equal to the `EFI_CUSTOM_MODE_NAME` string +2. `VendorGuid` is equal to the `gEfiCustomModeEnableGuid` GUID (which has= a byte length of `sizeof(EFI_GUID)`) +3. `Attributes` is equal to `EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOT= SERVICE_ACCESS` +4. `DataSize` is equal to `sizeof(SecureBootMode)` +5. `Data` is equal to `SecureBootMode` (which has a byte length of `sizeof= (SecureBootMode)`) + +If any one of these input arguments does not match in the actual call to +`gRT_SetVariable()` in the design, then the test will fail. There is much = more +that can be done with `EXPECT_CALL` and mock functions, but again we will +leave those details to be explained in the gMock documentation. + ### GoogleTest - More Complex Cases =20 To write more advanced tests, take a look at the @@ -654,8 +1560,8 @@ We will continue trying to make these as similar as po= ssible. =20 Code/Test | Location --------- | -------- -Host-Based Unit Tests for a Library/Protocol/PPI/GUID Interface | If wha= t'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.
Examp= le: `MdePkg/Test/UnitTest/[Library/Protocol/Ppi/Guid]/`

A real-wor= ld example of this is the BaseSafeIntLib test in MdePkg.
`MdePkg/Test/U= nitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibHost.inf` -Host-Based Unit Tests for a Library/Driver (PEI/DXE/SMM) implementation = | If what's being tested is a specific implementation (e.g. BaseDebugLibSer= ialPort for DebugLib), the test should be scoped to the implementation dire= ctory itself, in a UnitTest subdirectory.

Module Example: `MdeModu= lePkg/Universal/EsrtFmpDxe/UnitTest/`
Library Example: `MdePkg/Library/= BaseMemoryLib/UnitTest/` +Host-Based Unit Tests for a Library/Protocol/PPI/GUID Interface | If wha= t's being tested is an interface (e.g. a library with a public header file,= like DebugLib) and the test is agnostic to a specific implementation, then= the test should be scoped to the parent package.
Example: `MdePkg/Test= /UnitTest/[Library/Protocol/Ppi/Guid]/`

A real-world example of th= is is the BaseSafeIntLib test in MdePkg.
`MdePkg/Test/UnitTest/Library/= BaseSafeIntLib/TestBaseSafeIntLibHost.inf` +Host-Based Unit Tests for a Library/Driver (PEI/DXE/SMM) implementation = | If what's being tested is a specific implementation (e.g. BaseDebugLibSer= ialPort for DebugLib), then the test should be scoped to the implementation= directory itself, in a UnitTest (or GoogleTest) subdirectory.

Mod= ule Example: `MdeModulePkg/Universal/EsrtFmpDxe/UnitTest/`
Library Exam= ple: `MdePkg/Library/BaseMemoryLib/UnitTest/`
Library Example (GoogleTe= st): `SecurityPkg/Library/SecureBootVariableLib/GoogleTest` Host-Based Tests for a Functionality or Feature | If you're writing a fu= nctional test that operates at the module level (i.e. if it's more than a s= ingle file or library), the test should be located in the package-level Tes= ts directory under the HostFuncTest subdirectory.
For example, if you w= ere writing a test for the entire FMP Device Framework, you might put your = test in:
`FmpDevicePkg/Test/HostFuncTest/FmpDeviceFramework`

I= f the feature spans multiple packages, it's location should be determined b= y the package owners related to the feature. Non-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 locate= d in the `*Pkg/Test/[Shell/Dxe/Smm/Pei]Test` directory.

If the fea= ture spans multiple packages, it's location should be determined by the pac= kage owners related to the feature.

USAGE EXAMPLES
PEI Example= : MP_SERVICE_PPI. Or check MTRR configuration in a notification function. SMM Example: a test in a protocol callback function. (It is different w= ith the solution that SmmAgent+ShellApp)
DXE Example: a test in a UEFI = event call back to check SPI/SMRAM status.
Shell Example: the SMM han= dler 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. =20 @@ -713,8 +1619,19 @@ Non-Host-Based (PEI/DXE/SMM/Shell) Tests for a Functi= onality or Feature | Simi GeneralPurposeLibTest.c GeneralPurposeLibHostUnitTest.inf =20 + Mock/ + Include/ + GoogleTest/ + Library/ + MockGeneralPurposeLib.h + + Library/ + GoogleTest/ + MockGeneralPurposeLib/ + MockGeneralPurposeLib.cpp + MockGeneralPurposeLib.inf + Pkg.dsc # Standard Modules and any Target-Based Test A= pps (including in Test/) - ``` =20 ### Future Locations in Consideration --=20 2.39.1.windows.1