From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mga11.intel.com (mga11.intel.com []) by mx.groups.io with SMTP id smtpd.web11.2047.1594433352364265985 for ; Fri, 10 Jul 2020 19:09:14 -0700 Authentication-Results: mx.groups.io; dkim=missing; spf=fail (domain: intel.com, ip: , mailfrom: michael.d.kinney@intel.com) IronPort-SDR: Qw8I8WyPsGgZzGttxwlf0kp2Du44NBDy2pHLlMwJF+F02c6DfOcAhkEcR+MhsapCaFsNmvjEqE ZvNbdEGRuHBQ== X-IronPort-AV: E=McAfee;i="6000,8403,9678"; a="146380576" X-IronPort-AV: E=Sophos;i="5.75,337,1589266800"; d="scan'208";a="146380576" X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga002.jf.intel.com ([10.7.209.21]) by fmsmga102.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 10 Jul 2020 19:09:14 -0700 IronPort-SDR: zDKYPt5eHsWOKgAv/PDqhGmHYncDqx08CNTipEeOdgRxvVsY9KPbGZ8UTCQgxQkAMooJfsvZ15 I9L+YhwUvufQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.75,337,1589266800"; d="scan'208";a="298602319" Received: from mdkinney-mobl2.amr.corp.intel.com ([10.254.75.186]) by orsmga002.jf.intel.com with ESMTP; 10 Jul 2020 19:09:13 -0700 From: "Michael D Kinney" To: devel@edk2.groups.io Cc: Liming Gao , Sean Brogan , Bret Barkelew , Jiewen Yao Subject: [Patch v3 14/16] MdePkg/Include: Add UT_EXPECT_ASSERT_FAILURE() to UnitTestLib Date: Fri, 10 Jul 2020 19:09:02 -0700 Message-Id: <20200711020904.24116-15-michael.d.kinney@intel.com> X-Mailer: git-send-email 2.21.0.windows.1 In-Reply-To: <20200711020904.24116-1-michael.d.kinney@intel.com> References: <20200711020904.24116-1-michael.d.kinney@intel.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit REF: https://bugzilla.tianocore.org/show_bug.cgi?id=2801 Add the UT_EXPECT_ASSERT_FAILURE(FunctionCall, Status) macro to the UnitTestLib that can be used to check if a function under test triggers an ASSERT() condition. If an ASSERT() condition is triggered, then the macro returns. If the ASSERT() condition is not triggered, then the current unit test fails with a status of UNIT_TEST_ERROR_TEST_FAILED. If ASSERT()s are disabled, then this check for ASSERT() behavior is not possible, and the check is skipped. The global variable gUnitTestExpectAssertFailureJumpBuffer is added to the UnitTestLib to save/restore context when the UT_EXPECT_ASSERT_FAILURE(FunctionCall, Status) macro is used. The UT_EXPECT_ASSERT_FAILURE() macro uses the SetJump() service with this global variable. The UnitTestLib service UnitTestDebugAssert() uses the LongJump() service with this global to restore context if an ASSERT() is triggered by the code under test. Add UnitTestExpectAssertFailure() to the UnitTestLib class. The UnitTestExpectAssertFailure() is called from the new UT_EXPECT_ASSERT_FAILURE() macro after the status of this macro check is known. Add UnitTestDebugAssert() to the UnitTestLib class. The UnitTestDebugAssert() service is the same as the DebugLib DebugAssert() service and is invoked from the DebugLib _ASSERT() macro if unit testing is enabled. This allows the Unit Test Framework to know when code under test triggers an ASSERT() condition. Cc: Liming Gao Cc: Sean Brogan Cc: Bret Barkelew Cc: Jiewen Yao Signed-off-by: Michael D Kinney --- MdePkg/Include/Library/UnitTestLib.h | 90 ++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/MdePkg/Include/Library/UnitTestLib.h b/MdePkg/Include/Library/UnitTestLib.h index a4374580a8..99175496c8 100644 --- a/MdePkg/Include/Library/UnitTestLib.h +++ b/MdePkg/Include/Library/UnitTestLib.h @@ -441,6 +441,56 @@ SaveFrameworkState ( return UNIT_TEST_ERROR_TEST_FAILED; \ } +/** + This macro uses the framework assertion logic to check whether a function call + triggers an ASSERT() condition. The BaseLib SetJump()/LongJump() services + are used to establish a safe return point when an ASSERT() is triggered. + If an ASSERT() is triggered, unit test execution continues and Status is set + to UNIT_TEST_PASSED. Otherwise, a unit test case failure is raised and + Status is set to UNIT_TEST_ERROR_TEST_FAILED. + + If ASSERT() macros are disabled, then the test case is skipped and a warning + message is added to the unit test log. Status is set to UNIT_TEST_SKIPPED. + + @param[in] FunctionCall Function call that is expected to trigger ASSERT(). + @param[out] Status Pointer to a UNIT_TEST_STATUS return value. This + is an optional parameter that may be NULL. +**/ +#if defined (EDKII_UNIT_TEST_FRAMEWORK_ENABLED) + #include + + /// + /// Pointer to jump buffer used with SetJump()/LongJump() to test if a + /// function under test generates an expected ASSERT() condition. + /// + extern BASE_LIBRARY_JUMP_BUFFER *gUnitTestExpectAssertFailureJumpBuffer; + + #define UT_EXPECT_ASSERT_FAILURE(FunctionCall, Status) \ + do { \ + UNIT_TEST_STATUS UnitTestJumpStatus; \ + BASE_LIBRARY_JUMP_BUFFER UnitTestJumpBuffer; \ + UnitTestJumpStatus = UNIT_TEST_SKIPPED; \ + if (DebugAssertEnabled ()) { \ + gUnitTestExpectAssertFailureJumpBuffer = &UnitTestJumpBuffer; \ + if (SetJump (gUnitTestExpectAssertFailureJumpBuffer) == 0) { \ + FunctionCall; \ + UnitTestJumpStatus = UNIT_TEST_ERROR_TEST_FAILED; \ + } else { \ + UnitTestJumpStatus = UNIT_TEST_PASSED; \ + } \ + gUnitTestExpectAssertFailureJumpBuffer = NULL; \ + } \ + if (!UnitTestExpectAssertFailure ( \ + UnitTestJumpStatus, \ + __FUNCTION__, __LINE__, __FILE__, \ + #FunctionCall, Status)) { \ + return UNIT_TEST_ERROR_TEST_FAILED; \ + } \ + } while (FALSE) +#else + #define UT_EXPECT_ASSERT_FAILURE(FunctionCall, Status) FunctionCall; +#endif + /** If Expression is TRUE, then TRUE is returned. If Expression is FALSE, then an assert is triggered and the location of the @@ -690,6 +740,46 @@ UnitTestAssertNotNull ( IN CONST CHAR8 *PointerName ); +/** + If UnitTestStatus is UNIT_TEST_PASSED, then log an info message and return + TRUE because an ASSERT() was expected when FunctionCall was executed and an + ASSERT() was triggered. If UnitTestStatus is UNIT_TEST_SKIPPED, then log a + warning message and return TRUE because ASSERT() macros are disabled. If + UnitTestStatus is UNIT_TEST_ERROR_TEST_FAILED, then log an error message and + return FALSE because an ASSERT() was expected when FunctionCall was executed, + but no ASSERT() conditions were triggered. The log messages contain + FunctionName, LineNumber, and FileName strings to provide the location of the + UT_EXPECT_ASSERT_FAILURE() macro. + + @param[in] UnitTestStatus The status from UT_EXPECT_ASSERT_FAILURE() that + is either pass, skipped, or failed. + @param[in] FunctionName Null-terminated ASCII string of the function + executing the UT_EXPECT_ASSERT_FAILURE() macro. + @param[in] LineNumber The source file line number of the the function + executing the UT_EXPECT_ASSERT_FAILURE() macro. + @param[in] FileName Null-terminated ASCII string of the filename + executing the UT_EXPECT_ASSERT_FAILURE() macro. + @param[in] FunctionCall Null-terminated ASCII string of the function call + executed by the UT_EXPECT_ASSERT_FAILURE() macro. + @param[out] ResultStatus Used to return the UnitTestStatus value to the + caller of UT_EXPECT_ASSERT_FAILURE(). This is + optional parameter that may be NULL. + + @retval TRUE UnitTestStatus is UNIT_TEST_PASSED. + @retval TRUE UnitTestStatus is UNIT_TEST_SKIPPED. + @retval FALSE UnitTestStatus is UNIT_TEST_ERROR_TEST_FAILED. +**/ +BOOLEAN +EFIAPI +UnitTestExpectAssertFailure ( + IN UNIT_TEST_STATUS UnitTestStatus, + IN CONST CHAR8 *FunctionName, + IN UINTN LineNumber, + IN CONST CHAR8 *FileName, + IN CONST CHAR8 *FunctionCall, + OUT UNIT_TEST_STATUS *ResultStatus OPTIONAL + ); + /** Test logging macro that records an ERROR message in the test framework log. Record is associated with the currently executing test case. -- 2.21.0.windows.1