edk2/UnitTestFrameworkPkg/Include/Library/GoogleTestLib.h
Michael D Kinney 81a7efddd5 UnitTestFrameworkPkg/Include: Update GoogleTestLib for CLANG
Update GoogleTestLib.h to support Windows/CLANGPDB build.
When using Windows/CLANGPDB, _MSC_VER is not defined. If
it is not defined, then set to 1900 for Visual Studio 2015
or newer compatibility when using windows include files for
host based unit test application builds. If _MSC_VER is not
set, then those application fail to build.

Also address a Windows/CLANG issue when ASAN is enabled
by providing an alternate implementation of the
EXPECT_THROW_MESSAGE() and ASSERT_THROW_MESSAGE() macros.
Without this alternate implementation, an exception is
generated due to the description of the ASSERT() condition
being NULL.

Update all implementations of EXPECT_THROW_MESSAGE() and
ASSERT_THROW_MESSAGE() to support parameters to the
statement executed to return values.

Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com>
2025-09-28 23:38:21 +00:00

187 lines
6.6 KiB
C++

/** @file
GoogleTestLib class with APIs from the googletest project
Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#ifndef GOOGLE_TEST_LIB_H_
#define GOOGLE_TEST_LIB_H_
//
// For Windows/clang builds if _MSC_VER is not defined, then set to
// Visual Studio 2015 value of 1900
//
#if defined (__clang__) && defined (_WIN32)
#ifndef _MSC_VER
#define _MSC_VER 1900
#endif
#endif
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <cstring>
#include <iomanip>
//
// For all use of GoogleTestLib, make sure NULL is defined to nullptr to
// support matching any unit test pointer value to NULL.
//
#ifdef NULL
#undef NULL
#define NULL nullptr
#endif
using ::testing::Throws;
using ::testing::ThrowsMessage;
using ::testing::HasSubstr;
//
// Extended macros for testing exceptions with a specific description string
// in the exception message. Typically used to check that the expression
// that generates an ASSERT() matches the expected expression.
//
// NOTE: Windows/CLANG builds with ASAN enabled generate an exception
// due to the generated exception string being NULL. An alternate implementation
// of these macros is provided for Windows/CLANG builds that disables ASAN
// checks in a function that uses try/catch for the expected exception type
// and provides the correct exception string for comparison.
//
#if defined (__clang__) && defined (_WIN32)
#define EXPECT_THROW_MESSAGE(statement, description) \
[&]() __attribute__((no_sanitize_address)){ \
try { \
(statement); \
FAIL() << "Exception not thrown"; \
} catch (const std::runtime_error& ex) { \
EXPECT_THAT ( \
std::string(ex.what()), \
HasSubstr(description) \
); \
} catch(...) { \
FAIL() << "Unexpected exception"; \
} \
}()
#define ASSERT_THROW_MESSAGE(statement, description) \
[&]() __attribute__((no_sanitize_address)){ \
try { \
(statement); \
FAIL() << "Exception not thrown"; \
} catch (const std::runtime_error& ex) { \
ASSERT_THAT ( \
std::string(ex.what()), \
HasSubstr(description) \
); \
} catch(...) { \
FAIL() << "Unexpected exception"; \
} \
}()
#else
#define EXPECT_THROW_MESSAGE(statement, description) \
EXPECT_THAT ( \
[&]() { statement; }, \
ThrowsMessage<std::runtime_error>(HasSubstr (description)) \
)
#define ASSERT_THROW_MESSAGE(statement, description) \
ASSERT_THAT ( \
[&]() { statement; }, \
ThrowsMessage<std::runtime_error>(HasSubstr (description)) \
)
#endif
extern "C" {
#include <Uefi.h>
}
//////////////////////////////////////////////////////////////////////////////
// Below are the action extensions to GoogleTest and gmock for EDK2 types.
// These actions are intended to be used in EXPECT_CALL (and related gmock
// macros) to support assignments to output arguments in the expected call.
//
// Action to support pointer types to a buffer (such as UINT8* or VOID*)
ACTION_TEMPLATE (
SetArgBuffer,
HAS_1_TEMPLATE_PARAMS (size_t, ArgNum),
AND_2_VALUE_PARAMS (Buffer, ByteSize)
) {
auto ArgBuffer = std::get<ArgNum>(args);
std::memcpy (ArgBuffer, Buffer, ByteSize);
}
//////////////////////////////////////////////////////////////////////////////
// Below are the matcher extensions to GoogleTest and gmock for EDK2 types.
// These matchers are intended to be used in EXPECT_CALL (and related gmock
// macros) to support comparisons to input arguments in the expected call.
//
// Note that these matchers can also be used in the EXPECT_THAT or ASSERT_THAT
// macros to compare whether two values are equal.
//
// Matcher to support pointer types to a buffer (such as UINT8* or VOID* or
// any structure pointer)
MATCHER_P2 (
BufferEq,
Buffer,
ByteSize,
std::string ("buffer data to ") + (negation ? "not " : "") + "be the same"
) {
UINT8 *Actual = (UINT8 *)arg;
UINT8 *Expected = (UINT8 *)Buffer;
for (size_t i = 0; i < ByteSize; i++) {
if (Actual[i] != Expected[i]) {
*result_listener << "byte at offset " << i
<< " does not match expected. [" << std::hex
<< "Actual: 0x" << std::setw (2) << std::setfill ('0')
<< (unsigned int)Actual[i] << ", "
<< "Expected: 0x" << std::setw (2) << std::setfill ('0')
<< (unsigned int)Expected[i] << "]";
return false;
}
}
*result_listener << "all bytes match";
return true;
}
// Matcher to support CHAR16* type
MATCHER_P (
Char16StrEq,
String,
std::string ("strings to ") + (negation ? "not " : "") + "be the same"
) {
CHAR16 *Actual = (CHAR16 *)arg;
CHAR16 *Expected = (CHAR16 *)String;
for (size_t i = 0; Actual[i] != 0; i++) {
if (Actual[i] != Expected[i]) {
*result_listener << "character at offset " << i
<< " does not match expected. [" << std::hex
<< "Actual: 0x" << std::setw (4) << std::setfill ('0')
<< Actual[i];
if (std::isprint (Actual[i])) {
*result_listener << " ('" << (char)Actual[i] << "')";
}
*result_listener << ", Expected: 0x" << std::setw (4) << std::setfill ('0')
<< Expected[i];
if (std::isprint (Expected[i])) {
*result_listener << " ('" << (char)Expected[i] << "')";
}
*result_listener << "]";
return false;
}
}
*result_listener << "strings match";
return true;
}
#endif