//---------------------------------------------------------------------------------------------------------------------
/// \file
/// File used for test verification purposes.
// Copyright (c) Microsoft Corporation. All Rights Reserved.
//---------------------------------------------------------------------------------------------------------------------
#pragma once
#if defined(_MSC_VER) && _MSC_VER < 1910
# error Unsupported version of Visual Studio. Verify.h requires VS 2017 or later.
#endif
#include "Log.h"
#include "PreserveLastError.h"
#include "WexAssert.h"
#include "WexDebug.h"
#include "WexString.h"
#include "WexTypes.h"
#if defined(__has_include)
# define TAEF_HAS_INCLUDE(X) __has_include(X)
#else
# define TAEF_HAS_INCLUDE(X) 0
#endif
#if defined(__cpp_exceptions)
# include "WexException.h"
// The logic to define TAEF_STL_SUPPORT is in WexTypes.h.
# if defined(TAEF_STL_SUPPORT)
// Include to define library feature test macros.
# if !defined(TAEF_NO_VERSION_SUPPORT) && TAEF_HAS_INCLUDE()
# include
# endif
# pragma push_macro("HUGE")
# undef HUGE
# include
# if defined(__cpp_lib_string_view)
# include
# endif
# pragma pop_macro("HUGE")
# if !defined(TAEF_NO_SYSTEM_ERROR_SUPPORT) && TAEF_HAS_INCLUDE()
# define TAEF_SYSTEM_ERROR_SUPPORT
# include // for ::isspace
# include // for INT_MAX
# if defined(_MSC_VER) && (defined(_M_ARM64) || defined(_M_ARM64EC))
# pragma warning(push)
# pragma warning(disable:4365) // conversion from '{}' to '{}', signed/unsigned mismatch
# endif
# include
# if defined(_MSC_VER) && (defined(_M_ARM64) || defined(_M_ARM64EC))
# pragma warning(pop)
# endif
# endif
// Verify.h has two implementations for formatting values: a newer one based on std::format
// and an older one that works even when exceptions and the STL are disabled. Tests can define
// TAEF_NO_STD_FORMAT to opt into using the older implementation even when std::format is
// available. Tests that want to use the std::format implementation but don't yet have the
// STL header available can define TAEF_USE_FMTLIB to have TAEF use fmt::format from
// the open source fmtlib project (https://github.com/fmtlib/fmt) instead of using std::format.
# if !defined(TAEF_NO_STD_FORMAT) && defined(__cpp_concepts) && defined(__cpp_lib_string_view)
# if defined(TAEF_USE_FMTLIB)
# define TAEF_USE_STD_FORMAT
# define TAEF_STD_FORMAT_NAMESPACE fmt
# if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable:6285) // {} is always a non-zero constant.
# pragma warning(disable:26437) // Do not slice.
# pragma warning(disable:26451) // Using operator '{}' on a 4 byte value and then casting the result to a 8 byte value.
# pragma warning(disable:26495) // Always initialize a member variable.
# pragma warning(disable:26498) // The function '{}' is constexpr, mark variable '{}' constexpr if compile-time evaluation is desired (con.5).
# pragma warning(disable:26800) // Use of a moved from object: {}.
# pragma warning(disable:26812) // Prefer 'enum class' over 'enum'.
# endif
# include
# if defined(_MSC_VER)
# pragma warning(pop)
# endif
# elif defined(__cpp_lib_format)
# define TAEF_USE_STD_FORMAT
# define TAEF_STD_FORMAT_NAMESPACE std
# include
# endif
# if defined(TAEF_USE_STD_FORMAT)
// is needed for using std::back_inserter to append to a string using format_to.
# include
// is for std::underlying_type_t and std::is_same_v.
# include
// is for std::move.
# include
# endif
# endif // #if !defined(TAEF_NO_STD_FORMAT)
# endif // #if defined(TAEF_STL_SUPPORT)
# include
// When TAEF_USE_STD_EXCEPTION_FOR_VERIFY_FAILURE is defined, VerifyFailureException inherits from std::exception,
// rather than WEX::Common::Exception. In the future, WEX::Common::Exception will likely be updated to itself inherit from
// std::exception. When this occurs, this configuration option might be removed since VerifyFailureException would inherit
// from std::exception either way. This is on by default for Linux, but is opt-in for Windows.
# if !defined(_WIN32) && !defined(TAEF_USE_STD_EXCEPTION_FOR_VERIFY_FAILURE)
# define TAEF_USE_STD_EXCEPTION_FOR_VERIFY_FAILURE
# endif
#else // ^^ defined(__cpp_exceptions) ^^ | vv !defined(__cpp_exceptions) vv
// Disable verify exception usage if exceptions are disabled for this project.
# if !defined(NO_VERIFY_EXCEPTIONS)
# define NO_VERIFY_EXCEPTIONS
# endif
#endif
#include // For memcpy, memcmp, and strlen. On Windows, we also use wcslen.
#if defined(_WIN32)
// Without deprecation support StrSafe.h will define 'malicious' macros in an attempt to prevent older API usage.
// These errors are confusing - deprecation is a lot clearer.
# if !defined(MIDL_PASS) && !defined(DEPRECATE_SUPPORTED)
# define DEPRECATE_SUPPORTED
# endif
# include
# include
#endif
// These macros are equivalent to the Windows macros of the same name except without "TAEF_".
// TAEF defines its own version so it can use the same error code values on Linux.
#define TAEF_S_OK static_cast(0)
#define TAEF_S_FALSE static_cast(1)
#define TAEF_E_FAIL static_cast(0x80004005)
#define TAEF_E_OUTOFMEMORY static_cast(0x8007000E)
#define TAEF_E_UNEXPECTED static_cast(0x8000FFFF)
#if defined(CPPWINRT_VERSION)
#define TAEF_CPPWINRT_SUPPORT
#endif
#if !defined(_WIN32)
#pragma push_macro("_In_")
#pragma push_macro("_In_reads_bytes_")
#pragma push_macro("_Out_")
#pragma push_macro("_Out_opt_")
#pragma push_macro("_Out_writes_bytes_to_")
#pragma push_macro("_Post_equal_to_")
#pragma push_macro("_Post_invalid_")
#pragma push_macro("_Printf_format_string_")
#pragma push_macro("_Success_")
#if !defined(_In_)
#define _In_
#endif
#if !defined(_In_reads_bytes_)
#define _In_reads_bytes_(x)
#endif
#if !defined(_Out_)
#define _Out_
#endif
#if !defined(_Out_opt_)
#define _Out_opt_
#endif
#if !defined(_Out_writes_bytes_to_)
#define _Out_writes_bytes_to_(x,y)
#endif
#if !defined(_Post_equal_to_)
#define _Post_equal_to_(x)
#endif
#if !defined(_Post_invalid_)
#define _Post_invalid_
#endif
#if !defined(_Printf_format_string_)
#define _Printf_format_string_
#endif
#if !defined(_Success_)
#define _Success_(x)
#endif
#endif // #if !defined(_WIN32)
// Allow anyone who has defined a Verify, or Trace macro to compile with this header file included.
#pragma push_macro("Verify")
#undef Verify
#pragma push_macro("Trace")
#undef Trace
///
/// \def VERIFY_ARE_EQUAL(expected, actual, ...)
/// Verifies that two specified objects are equal.
///
#define VERIFY_ARE_EQUAL(expected, actual, ...) \
(bool)WEX::TestExecution::Private::MacroVerify::AreEqual((expected), (actual), \
u8 ## #expected, u8 ## #actual, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__)
// Verifies that two specified objects are not equal.
#define VERIFY_ARE_NOT_EQUAL(unexpected, actual, ...) \
(bool)WEX::TestExecution::Private::MacroVerify::AreNotEqual((unexpected), (actual), \
u8 ## #unexpected, u8 ## #actual, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__)
// Verifies that the first parameter is greater than the second parameter.
#define VERIFY_IS_GREATER_THAN(expectedGreater, expectedLess, ...) \
(bool)WEX::TestExecution::Private::MacroVerify::IsGreaterThan((expectedGreater), (expectedLess), \
u8 ## #expectedGreater, u8 ## #expectedLess, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__)
// Verifies that the first parameter is greater than or equal to the second parameter.
#define VERIFY_IS_GREATER_THAN_OR_EQUAL(expectedGreater, expectedLess, ...) \
(bool)WEX::TestExecution::Private::MacroVerify::IsGreaterThanOrEqual((expectedGreater), (expectedLess), \
u8 ## #expectedGreater, u8 ## #expectedLess, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__)
// Verifies that the first parameter is less than the second parameter.
#define VERIFY_IS_LESS_THAN(expectedLess, expectedGreater, ...) \
(bool)WEX::TestExecution::Private::MacroVerify::IsLessThan((expectedLess), (expectedGreater), \
u8 ## #expectedLess, u8 ## #expectedGreater, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__)
// Verifies that the first parameter is less than or equal to the second parameter.
#define VERIFY_IS_LESS_THAN_OR_EQUAL(expectedLess, expectedGreater, ...) \
(bool)WEX::TestExecution::Private::MacroVerify::IsLessThanOrEqual((expectedLess), (expectedGreater), \
u8 ## #expectedLess, u8 ## #expectedGreater, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__)
// Verifies that the two parameters specified refer to the same object.
#define VERIFY_ARE_SAME(expected, actual, ...) \
(bool)WEX::TestExecution::Private::MacroVerify::AreSame((expected), (actual), \
u8 ## #expected, u8 ## #actual, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__)
// Verifies that the two parameters specified do not refer to the same object.
#define VERIFY_ARE_NOT_SAME(unexpected, actual, ...) \
(bool)WEX::TestExecution::Private::MacroVerify::AreNotSame((unexpected), (actual), \
u8 ## #unexpected, u8 ## #actual, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__)
// Fails without checking any conditions.
#define VERIFY_FAIL(...) \
(bool)WEX::TestExecution::Private::MacroVerify::Fail( \
__FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__)
// Verifies that the specified condition is true.
#define VERIFY_IS_TRUE(condition, ...) \
(bool)WEX::TestExecution::Private::MacroVerify::IsTrue((condition), \
u8 ## #condition, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__)
// Verifies that the specified condition is false.
#define VERIFY_IS_FALSE(condition, ...) \
(bool)WEX::TestExecution::Private::MacroVerify::IsFalse((condition), \
u8 ## #condition, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__)
// Verifies that the specified parameter is null.
#define VERIFY_IS_NULL(object, ...) \
(bool)WEX::TestExecution::Private::MacroVerify::IsNull((object), \
u8 ## #object, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__)
// Verifies that the specified parameter is not null.
#define VERIFY_IS_NOT_NULL(object, ...) \
(bool)WEX::TestExecution::Private::MacroVerify::IsNotNull((object), \
u8 ## #object, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__)
#if defined(_WIN32)
// Verifies that the specified HRESULT is successful.
# define VERIFY_SUCCEEDED(hresult, ...) \
(bool)WEX::TestExecution::Private::MacroVerify::Succeeded((hresult), \
u8 ## #hresult, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__)
// Verifies that the specified HRESULT is successful, and returns the HRESULT that was passed in.
# define VERIFY_SUCCEEDED_RETURN(hresult, ...) \
(HRESULT)WEX::TestExecution::Private::MacroVerify::SucceededReturn((hresult), \
u8 ## #hresult, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__)
// Verifies that the specified HRESULT is not successful.
# define VERIFY_FAILED(hresult, ...) \
(bool)WEX::TestExecution::Private::MacroVerify::Failed((hresult), \
u8 ## #hresult, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__)
// Verifies that the specified HRESULT is not successful, and returns the HRESULT that was passed in.
# define VERIFY_FAILED_RETURN(hresult, ...) \
(HRESULT)WEX::TestExecution::Private::MacroVerify::FailedReturn((hresult), \
u8 ## #hresult, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__)
// Verifies that the specified Win32 result succeeded.
# define VERIFY_WIN32_SUCCEEDED(win32Result, ...) \
(bool)WEX::TestExecution::Private::MacroVerify::Win32Succeeded((win32Result), \
u8 ## #win32Result, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__)
// Verifies that the specified Win32 result succeeded, and returns the Win32 value that was passed in.
# define VERIFY_WIN32_SUCCEEDED_RETURN(win32Result, ...) \
(::LONG)WEX::TestExecution::Private::MacroVerify::Win32SucceededReturn((win32Result), \
u8 ## #win32Result, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__)
// Verifies that the specified Win32 result failed.
# define VERIFY_WIN32_FAILED(win32Result, ...) \
(bool)WEX::TestExecution::Private::MacroVerify::Win32Failed((win32Result), \
u8 ## #win32Result, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__)
// Verifies that the specified Win32 result failed, and returns the Win32 value that was passed in.
# define VERIFY_WIN32_FAILED_RETURN(win32Result, ...) \
(::LONG)WEX::TestExecution::Private::MacroVerify::Win32FailedReturn((win32Result), \
u8 ## #win32Result, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__)
// Verifies that the specified Win32 BOOL succeeded and logs the error code returned from GetLastError if not.
# define VERIFY_WIN32_BOOL_SUCCEEDED(win32Bool, ...) \
(bool)WEX::TestExecution::Private::MacroVerify::Win32BoolSucceeded((win32Bool), \
u8 ## #win32Bool, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__)
// Verifies that the specified Win32 BOOL succeeded and logs the error code returned from GetLastError if not; returns the Win32 BOOL that was passed in.
# define VERIFY_WIN32_BOOL_SUCCEEDED_RETURN(win32Bool, ...) \
(::BOOL)WEX::TestExecution::Private::MacroVerify::Win32BoolSucceededReturn((win32Bool), \
u8 ## #win32Bool, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__)
// Verifies that the specified Win32 BOOL failed.
# define VERIFY_WIN32_BOOL_FAILED(win32Bool, ...) \
(bool)WEX::TestExecution::Private::MacroVerify::Win32BoolFailed((win32Bool), \
u8 ## #win32Bool, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__)
// Verifies that the specified Win32 BOOL failed and returns the Win32 BOOL that was passed in.
# define VERIFY_WIN32_BOOL_FAILED_RETURN(win32Bool, ...) \
(::BOOL)WEX::TestExecution::Private::MacroVerify::Win32BoolFailedReturn((win32Bool), \
u8 ## #win32Bool, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__)
# if defined(NT_SUCCESS)
# define VERIFY_NT_SUCCESS(ntStatus, ...) \
(bool)WEX::TestExecution::Private::MacroVerify::NtSuccess((ntStatus), \
u8 ## #ntStatus, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__)
# define VERIFY_NOT_NT_SUCCESS(ntStatus, ...) \
(bool)WEX::TestExecution::Private::MacroVerify::NotNtSuccess((ntStatus), \
u8 ## #ntStatus, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__)
# endif // #if defined(NT_SUCCESS)
#endif // #if defined(_WIN32)
// Only enable the THROWS/NO_THROW macros if exceptions are enabled.
#if defined(__cpp_exceptions)
// Verifies that the specified operation throws an exception of the specified type that meets the functor criteria.
// The temporary TAEF_exceptionIsExpected variable is necessary to workaround a bug in nested lambdas used in conditionals.
# define VERIFY_THROWS_SPECIFIC(operation, exceptionType, func, ...) \
{ \
bool TAEF_exceptionHit = false; \
try \
{ \
operation; \
} \
catch(exceptionType& TAEF_caughtException) \
{ \
const bool TAEF_exceptionIsExpected = func(TAEF_caughtException); \
if (TAEF_exceptionIsExpected) \
{ \
WEX::TestExecution::Private::MacroVerify::ExpectedExceptionThrown(u8 ## #exceptionType, u8 ## #operation, ## __VA_ARGS__); \
TAEF_exceptionHit = true; \
} \
else \
{ \
throw; \
} \
} \
\
if (!TAEF_exceptionHit) \
{ \
WEX::TestExecution::Private::MacroVerify::ExpectedExceptionNotThrown( \
u8 ## #exceptionType, u8 ## #operation, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__); \
} \
}
// Verifies that the specified operation throws the specified exception.
# define VERIFY_THROWS(operation, exceptionType, ...) \
VERIFY_THROWS_SPECIFIC(operation, exceptionType, WEX::TestExecution::Private::TrueUnaryFunctor(), ## __VA_ARGS__)
# if defined(__cplusplus_winrt)
# if !defined(VERIFY_THROWS_SPECIFIC_CX)
// C++/CX versions of VERIFY_THROWS macros
# define VERIFY_THROWS_SPECIFIC_CX(operation, exceptionType, func, ...) \
{ \
bool TAEF_exceptionHit = false; \
try \
{ \
operation; \
} \
catch (exceptionType^ TAEF_caughtException) \
{ \
const bool TAEF_exceptionIsExpected = func(TAEF_caughtException); \
if (TAEF_exceptionIsExpected) \
{ \
WEX::TestExecution::Private::MacroVerify::ExpectedExceptionThrown(u8 ## #exceptionType, u8 ## #operation, ## __VA_ARGS__); \
TAEF_exceptionHit = true; \
} \
else \
{ \
throw; \
} \
} \
\
if (!TAEF_exceptionHit) \
{ \
WEX::TestExecution::Private::MacroVerify::ExpectedExceptionNotThrown( \
u8 ## #exceptionType, u8 ## #operation, __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__); \
} \
}
# endif // VERIFY_THROWS_SPECIFIC_CX is not defined
# if !defined(VERIFY_THROWS_CX)
# define VERIFY_THROWS_CX(operation, exceptionType, ...) \
VERIFY_THROWS_SPECIFIC_CX(operation, exceptionType, WEX::TestExecution::Private::TrueUnaryFunctor(), ## __VA_ARGS__)
# endif // VERIFY_THROWS_CX is not defined
# endif // __cplusplus_winrt is defined
// Verifies that the specified operation does not throw an exception.
# define VERIFY_NO_THROW(operation, ...) \
{ \
bool TAEF_exceptionHit = false; \
try \
{ \
operation; \
} \
catch(...) \
{ \
WEX::TestExecution::Private::MacroVerify::UnexpectedExceptionThrown(u8 ## #operation, \
__FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__); \
TAEF_exceptionHit = true; \
} \
\
if (!TAEF_exceptionHit) \
{ \
WEX::TestExecution::Private::MacroVerify::UnexpectedExceptionNotThrown(u8 ## #operation, ## __VA_ARGS__); \
} \
}
#endif // #if defined(__cpp_exceptions)
// There are two VERIFY implementations for formatting values so they can be included in log messages.
// One depends on C++20 features including concepts and std::format. The other works even for tests which
// have disabled C++ exceptions and the STL.
//
// When the VERIFY implementation is using std::format as the mechanism to display values, it passes the
// values to GetFormattableVerifyOutputValue and uses the return value as the format argument with {}.
// The code is similar to this:
// std::format(" - Value ({})", GetFormattableVerifyOutputValue(value))
//
// GetFormattableVerifyOutputValue overloads are provided that work for most cases. You can add more
// overloads for your own types if desired. Overloads for your own types can be in the same namespace as
// the type due to C++ argument-dependent lookup. If a custom implementation throws an exception, the
// verify message will still be logged, but no values will be included in the message.
//
// If your custom type can be used with std::format directly due to having a std::formatter
// specialization, then you don't need to write a GetFormattableVerifyOutputValue function. One of the
// provided overloads takes care of this case for you.
//
// In the fallback implementation which does not use std::format, you can format custom types by
// specializing the VerifyOutputTraits class template. On Windows, specializations of the
// VerifyOutputTraits class template still work with the new std::format-based implementation, but a
// std::formatter specialization is preferred over a VerifyOutputTraits specialization if both are
// available. The VerifyOutputTraits class is currently Windows-specific; so Linux tests using the
// fallback implementation have no mechanism to provide formatting for custom types.
// These definitions allow you to customize formatting for the VerifyOutputTraits specializations
// for common types. If these are customized, the std::format implementation is disabled so the
// fallback implemention will be used since only it honors these macros.
#if defined(LONG_VERIFY_FORMATTING)
# undef TAEF_USE_STD_FORMAT
#else
# define LONG_VERIFY_FORMATTING L"0x%x" // Default to formatting for HRESULTS
#endif
#if defined(INT_VERIFY_FORMATTING)
# undef TAEF_USE_STD_FORMAT
#else
# define INT_VERIFY_FORMATTING L"%d"
#endif
#if defined(CHAR_VERIFY_FORMATTING)
# undef TAEF_USE_STD_FORMAT
#else
# define CHAR_VERIFY_FORMATTING L"%c"
#endif
#if defined(SHORT_VERIFY_FORMATTING)
# undef TAEF_USE_STD_FORMAT
#else
# define SHORT_VERIFY_FORMATTING INT_VERIFY_FORMATTING
#endif
#if defined(FLOAT_VERIFY_FORMATTING)
# undef TAEF_USE_STD_FORMAT
#else
# define FLOAT_VERIFY_FORMATTING L"%fl"
#endif
#if defined(SIZE_T_VERIFY_FORMATTING)
# undef TAEF_USE_STD_FORMAT
#else
# define SIZE_T_VERIFY_FORMATTING L"%d"
#endif
#if defined(DOUBLE_VERIFY_FORMATTING)
# undef TAEF_USE_STD_FORMAT
#else
# define DOUBLE_VERIFY_FORMATTING FLOAT_VERIFY_FORMATTING
#endif
#if defined(UNSIGNED_LONG_VERIFY_FORMATTING)
# undef TAEF_USE_STD_FORMAT
#else
# define UNSIGNED_LONG_VERIFY_FORMATTING L"%u"
#endif
#if defined(UNSIGNED_INT_VERIFY_FORMATTING)
# undef TAEF_USE_STD_FORMAT
#else
# define UNSIGNED_INT_VERIFY_FORMATTING L"%u"
#endif
#if defined(UNSIGNED_CHAR_VERIFY_FORMATTING)
# undef TAEF_USE_STD_FORMAT
#else
# define UNSIGNED_CHAR_VERIFY_FORMATTING UNSIGNED_INT_VERIFY_FORMATTING
#endif
#if defined(UNSIGNED_SHORT_VERIFY_FORMATTING)
# undef TAEF_USE_STD_FORMAT
#else
# define UNSIGNED_SHORT_VERIFY_FORMATTING UNSIGNED_INT_VERIFY_FORMATTING
#endif
#if defined(INT_64_VERIFY_FORMATTING)
# undef TAEF_USE_STD_FORMAT
#else
# define INT_64_VERIFY_FORMATTING L"%I64d"
#endif
#if defined(UNSIGNED_INT_64_VERIFY_FORMATTING)
# undef TAEF_USE_STD_FORMAT
#else
# define UNSIGNED_INT_64_VERIFY_FORMATTING L"%I64u"
#endif
#if defined(POINTER_VERIFY_FORMATTING)
# undef TAEF_USE_STD_FORMAT
#else
# define POINTER_VERIFY_FORMATTING L"0x%p"
#endif
#pragma warning(push)
// Disable "Prefer enum class over enum".
#pragma warning(disable:26812)
namespace WEX { namespace TestExecution
{
namespace Private
{
// Please do not call functions in the Private namespace directly. They are implementation details of the code in the
// public interface.
extern "C"
{
_Success_(return)
WEXLOGGER_API bool WEXLOGGER_STDCALL TAEF_TryGetUtf8ByteCountForCharStringValue(const char* string, size_t stringSize,
_Out_ size_t& utf8ByteCount) noexcept;
_Success_(return)
WEXLOGGER_API bool WEXLOGGER_STDCALL TAEF_TryWriteUtf8ForCharStringValue(const char* string, size_t stringSize,
char* pOutputBuffer, size_t utf8ByteCount, _Out_ size_t& bytesWritten) noexcept;
#if defined(_WIN32)
_Success_(return)
WEXLOGGER_API bool WEXLOGGER_STDCALL TAEF_TryGetUtf8ByteCountForWCharStringValue(const wchar_t* string, size_t stringSize,
_Out_ size_t& utf8ByteCount) noexcept;
_Success_(return)
WEXLOGGER_API bool WEXLOGGER_STDCALL TAEF_TryWriteUtf8ForWCharStringValue(const wchar_t* string, size_t stringSize,
char* pOutputBuffer, size_t utf8ByteCount, _Out_ size_t& bytesWritten) noexcept;
#endif // #if defined(_WIN32)
}
#if defined(_WIN32) && defined(TAEF_USE_STD_FORMAT)
inline bool TryConvertWideStringToUtf8String(const wchar_t* string, size_t stringSize, std::string& result) noexcept
{
result.clear();
size_t utf8ByteCount = 0;
if (TAEF_TryGetUtf8ByteCountForWCharStringValue(string, stringSize, utf8ByteCount))
{
try
{
result.resize(utf8ByteCount);
}
catch (std::bad_alloc&)
{
return false;
}
size_t bytesWritten = 0;
if (TAEF_TryWriteUtf8ForWCharStringValue(string, stringSize, result.data(), utf8ByteCount, bytesWritten))
{
result.resize(bytesWritten);
return true;
}
result.clear();
}
return false;
}
#endif // #if defined(_WIN32) && defined(TAEF_USE_STD_FORMAT)
struct VerifyFormatAsHResult { HRESULT m_hr; };
struct DoNotFormatWithVerify {};
}/* namespace Private */
#if defined(_WIN32)
// See the large comment above for an explanation of this class.
template
class VerifyOutputTraits;
template
class VerifyOutputTraits
{
public:
static WEX::Common::NoThrowString ToString(const T& ref)
{
// Default implementation call through to non-reference type.
return VerifyOutputTraits::ToString(ref);
}
};
template
class VerifyOutputTraits
{
public:
static WEX::Common::NoThrowString ToString(T* const& p)
{
return WEX::Common::NoThrowString().Format(POINTER_VERIFY_FORMATTING, p);
}
};
template
class VerifyOutputTraits
{
public:
static WEX::Common::NoThrowString ToString(const volatile T& ref)
{
// Default implementation call through to non-volatile type.
// The const_cast removes volatile, which reduces the risk of getting a confusing error message such as
// VERIFY_ARE_EQUAL failing with (4, 4) if a volatile value is changed between the equality check and the error message.
return VerifyOutputTraits::ToString(const_cast(ref));
}
};
#define BUILD_OUTPUT_TRAIT(Type, FormatString) \
template<> \
class VerifyOutputTraits \
{ \
public: \
static WEX::Common::NoThrowString ToString(Type const& val) \
{ \
return WEX::Common::NoThrowString().Format(FormatString, val); \
} \
};
BUILD_OUTPUT_TRAIT(long, LONG_VERIFY_FORMATTING);
BUILD_OUTPUT_TRAIT(int, INT_VERIFY_FORMATTING);
BUILD_OUTPUT_TRAIT(char, CHAR_VERIFY_FORMATTING);
BUILD_OUTPUT_TRAIT(short, SHORT_VERIFY_FORMATTING);
BUILD_OUTPUT_TRAIT(float, FLOAT_VERIFY_FORMATTING);
BUILD_OUTPUT_TRAIT(double, DOUBLE_VERIFY_FORMATTING);
BUILD_OUTPUT_TRAIT(unsigned long, UNSIGNED_LONG_VERIFY_FORMATTING);
BUILD_OUTPUT_TRAIT(unsigned int, UNSIGNED_INT_VERIFY_FORMATTING);
BUILD_OUTPUT_TRAIT(unsigned char, UNSIGNED_CHAR_VERIFY_FORMATTING);
BUILD_OUTPUT_TRAIT(unsigned short, UNSIGNED_SHORT_VERIFY_FORMATTING);
#if defined(_WIN32)
BUILD_OUTPUT_TRAIT(__int64, INT_64_VERIFY_FORMATTING);
BUILD_OUTPUT_TRAIT(unsigned __int64, UNSIGNED_INT_64_VERIFY_FORMATTING);
#endif // #if defined(_WIN32)
BUILD_OUTPUT_TRAIT(bool, UNSIGNED_CHAR_VERIFY_FORMATTING);
#undef BUILD_OUTPUT_TRAIT
#if defined(TAEF_STL_SUPPORT)
// Custom class to output the value of std::wstring
template<>
class VerifyOutputTraits
{
public:
static WEX::Common::NoThrowString ToString(std::wstring const& string)
{
return WEX::Common::NoThrowString(string.c_str());
}
};
// Custom class to output the value of std::string
template<>
class VerifyOutputTraits
{
public:
static WEX::Common::NoThrowString ToString(std::string const& string)
{
return WEX::Common::NoThrowString(string.c_str());
}
};
#endif // #if defined(TAEF_STL_SUPPORT)
// Custom class to output the value of WEX::Common::String
template<>
class VerifyOutputTraits
{
public:
static WEX::Common::NoThrowString ToString(WEX::Common::String const& string)
{
return WEX::Common::NoThrowString(string);
}
};
// Custom class to output the value of WEX::Common::NoThrowString
template<>
class VerifyOutputTraits
{
public:
static WEX::Common::NoThrowString ToString(WEX::Common::NoThrowString const& string)
{
return string;
}
};
#if !defined(TAEF_USE_CUSTOM_GUID_VERIFY) && defined(GUID_DEFINED)
// Custom class to output the value of GUID
template <>
class VerifyOutputTraits
{
public:
static WEX::Common::NoThrowString ToString(GUID const & Guid)
{
return WEX::Common::NoThrowString().Format(L"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
Guid.Data1,
Guid.Data2,
Guid.Data3,
Guid.Data4[0],
Guid.Data4[1],
Guid.Data4[2],
Guid.Data4[3],
Guid.Data4[4],
Guid.Data4[5],
Guid.Data4[6],
Guid.Data4[7]);
}
};
#endif //TAEF_USE_CUSTOM_GUID_VERIFY && GUID_DEFINED
template
class DefaultOutputTraits;
template
class DefaultOutputTraits
{
public:
static WEX::Common::NoThrowString ToString(const T& t)
{
// Return value as an unsigned long for enums
return VerifyOutputTraits::ToString(static_cast(t));
}
};
template
class DefaultOutputTraits
{
public:
static WEX::Common::NoThrowString ToString(const T&)
{
// Just return an empty string by default
return WEX::Common::NoThrowString();
}
};
// Default implementation
template
class VerifyOutputTraits
{
public:
struct IsDefaultImplementation {};
static WEX::Common::NoThrowString ToString(const T& t)
{
return DefaultOutputTraits::ToString(t);
}
};
#endif // #if defined(_WIN32)
#if defined(TAEF_USE_STD_FORMAT)
namespace Private
{
template
concept HasStdFormatter = requires(const T& value, TAEF_STD_FORMAT_NAMESPACE::format_context& formatContext) {
std::declval>().format(value, formatContext);
};
#if defined(_WIN32)
template
concept HasVerifyOutputTraits = !requires(const T& value) {
typename VerifyOutputTraits::IsDefaultImplementation;
};
#else
template
concept HasVerifyOutputTraits = false;
#endif
}/* namespace Private */
inline const char* GetFormattableVerifyOutputValue(const char* pszValue) noexcept
{
return pszValue ? pszValue : "";
}
# if defined(__cpp_char8_t)
inline char GetFormattableVerifyOutputValue(char8_t value) noexcept { return static_cast(value); }
inline const char* GetFormattableVerifyOutputValue(const char8_t* pszValue) noexcept
{
return GetFormattableVerifyOutputValue(reinterpret_cast(pszValue));
}
inline const char* GetFormattableVerifyOutputValue(char8_t* pszValue) noexcept
{
return GetFormattableVerifyOutputValue(reinterpret_cast(pszValue));
}
# if defined(__cpp_lib_char8_t)
inline std::string_view GetFormattableVerifyOutputValue(std::u8string_view value) noexcept
{
return std::string_view{reinterpret_cast(value.data()), value.size()};
}
inline std::string_view GetFormattableVerifyOutputValue(const std::u8string& value) noexcept
{
return std::string_view{reinterpret_cast(value.c_str()), value.size()};
}
# endif // #if defined(__cpp_lib_char8_t)
# endif // #if defined(__cpp_char8_t)
# if defined(_WIN32)
inline std::string GetFormattableVerifyOutputValue(const wchar_t* pszValue) noexcept
{
std::string result;
if (pszValue)
{
Private::TryConvertWideStringToUtf8String(pszValue, ::wcslen(pszValue), result);
}
// On failure, result will be empty.
return result;
}
inline std::string GetFormattableVerifyOutputValue(wchar_t* pszValue) noexcept
{
return GetFormattableVerifyOutputValue(static_cast(pszValue));
}
inline std::string GetFormattableVerifyOutputValue(std::wstring_view value) noexcept
{
std::string result;
Private::TryConvertWideStringToUtf8String(value.data(), value.size(), result);
// On failure, result will be empty.
return result;
}
inline std::string GetFormattableVerifyOutputValue(const std::wstring& value) noexcept
{
return GetFormattableVerifyOutputValue(std::wstring_view{value});
}
inline std::string GetFormattableVerifyOutputValue(wchar_t value) noexcept
{
return GetFormattableVerifyOutputValue(std::wstring_view{&value, 1});
}
inline std::string GetFormattableVerifyOutputValue(const WEX::Common::String& value) noexcept
{
return GetFormattableVerifyOutputValue(std::wstring_view{static_cast(value), static_cast(value.GetLength())});
}
inline std::string GetFormattableVerifyOutputValue(const WEX::Common::NoThrowString& value) noexcept
{
return GetFormattableVerifyOutputValue(std::wstring_view{static_cast(value), static_cast(value.GetLength())});
}
inline std::string GetFormattableVerifyOutputValue(const Private::VerifyFormatAsHResult& value) noexcept
{
// The static_cast here is based on the definition of HRESULT in WexTypes.h.
#if defined(_WIN32)
return TAEF_STD_FORMAT_NAMESPACE::format("0x{:x}", static_cast(value.m_hr));
#else
return TAEF_STD_FORMAT_NAMESPACE::format("0x{:x}", static_cast(value.m_hr));
#endif
}
// Use VerifyOutputTraits for types that have a custom VerifyOutputTraits specialization,
// but not a std::formatter specialization.
template requires (!Private::HasStdFormatter)
std::string GetFormattableVerifyOutputValue(const T& value) noexcept
{
return GetFormattableVerifyOutputValue(VerifyOutputTraits::ToString(value));
}
# endif // #if defined(_WIN32)
// Pointers without a std::formatter implementation and without a more specific overload
// are converted to a const void*, which std::format displays as an address.
template requires (!Private::HasStdFormatter)
const void* GetFormattableVerifyOutputValue(T* pValue) noexcept { return pValue; }
// If the type has a std::formatter implementation, we can let it through as is.
template
const T& GetFormattableVerifyOutputValue(const T& value) noexcept { return value; }
// This overload handles unformattable enums which have an underlying type that can be formatted.
template requires (!Private::HasStdFormatter)
&& Private::HasStdFormatter>
&& (!Private::HasVerifyOutputTraits)
auto GetFormattableVerifyOutputValue(const T& value) noexcept
{
return static_cast>(value);
}
// For any other type, do not include values in the verify output.
template
Private::DoNotFormatWithVerify GetFormattableVerifyOutputValue(const T&) noexcept { return {}; }
#endif // #if defined(TAEF_USE_STD_FORMAT)
// Users can specialize VerifyCompareTraits for their classes to perform more detailed comparisons.
template
class VerifyCompareTraits
{
public:
static bool AreEqual(const T1& expected, const T2& actual)
{
return (expected == actual) != 0; // != 0 to handle overloads of operator== that return BOOL instead of bool
}
static bool AreSame(const T1& expected, const T2& actual)
{
return static_cast(&expected) == static_cast(&actual);
}
static bool IsLessThan(const T1& expectedLess, const T2& expectedGreater)
{
return expectedLess < expectedGreater;
}
static bool IsGreaterThan(const T1& expectedGreater, const T2& expectedLess)
{
return expectedGreater > expectedLess;
}
static bool IsNull(const T1& object)
{
// This works with various kinds of bool checkable types, such as smart pointers.
return object ? false : true;
}
};
template
class VerifyCompareTraits
{
public:
static bool AreEqual(T1* const& expected, T2* const& actual)
{
return static_cast(expected) == static_cast(actual);
}
static bool AreSame(T1* const& expected, T2* const& actual)
{
return static_cast(&expected) == static_cast(&actual);
}
static bool IsLessThan(T1* const& expectedLess, T2* const& expectedGreater)
{
return static_cast(expectedLess) < static_cast(expectedGreater);
}
static bool IsGreaterThan(T1* const& expectedGreater, T2* const& expectedLess)
{
return static_cast(expectedGreater) > static_cast(expectedLess);
}
static bool IsNull(T1* const& object)
{
return object == nullptr;
}
};
#if !defined(TAEF_USE_CUSTOM_GUID_VERIFY) && defined(GUID_DEFINED)
template <>
class VerifyCompareTraits
{
public:
static bool AreEqual(GUID const & expected, GUID const & actual)
{
return ::memcmp(&expected, &actual, sizeof(GUID)) == 0;
}
static bool AreSame(GUID const & expected, GUID const & actual)
{
return &expected == &actual;
}
static bool IsLessThan(GUID const & expectedLess, GUID const & expectedGreater)
{
return ::memcmp(&expectedLess, &expectedGreater, sizeof(GUID)) < 0;
}
static bool IsGreaterThan(GUID const & expectedGreater, GUID const & expectedLess)
{
return ::memcmp(&expectedGreater, &expectedLess, sizeof(GUID)) > 0;
}
static bool IsNull(GUID const & object)
{
// GUID_NULL is defined as a GUID with all zeros
const GUID c_guidNull = { 0x00000000, 0x0000, 0x0000,{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
return ::memcmp(&object, &c_guidNull, sizeof(GUID)) == 0;
}
};
#endif // TAEF_USE_CUSTOM_GUID_VERIFY && GUID_DEFINED
class ComVerify;
class Verify;
namespace Private
{
class MacroVerify;
#if defined(__cpp_char8_t)
using TaefChar8_t = char8_t;
#else
using TaefChar8_t = char;
#endif
inline void Memcpy(
_Out_writes_bytes_to_(destSize, count) void* pDest, size_t destSize,
_In_reads_bytes_(count) const void* pSrc, size_t count)
{
// Internal details: One existing test had a linker error when we added a memcpy dependency.
// We updated it to define TAEF_NO_MEMCPY to activate a manual implementation of the API.
#if defined(TAEF_NO_MEMCPY)
char* pDestAsChar = static_cast(pDest);
const char* pSrcAsChar = static_cast(pSrc);
for (size_t i = 0; i < count; ++i)
{
pDestAsChar[i] = pSrcAsChar[i];
}
#elif defined(__STDC_WANT_SECURE_LIB__) && __STDC_WANT_SECURE_LIB__
::memcpy_s(pDest, destSize, pSrc, count);
#else
::memcpy(pDest, pSrc, count);
#endif
}
// This is used to store string data for cases where TAEF needs to work with exceptions and the STL disabled.
// Do not use this directly. It is an internal implementation detail of TAEF and is subject to change.
class StringStorage
{
public:
StringStorage() = default;
explicit StringStorage(const char* pszString)
{
Set(pszString);
}
~StringStorage() noexcept
{
Clear();
}
// Copy construction is needed so VerifyFailureException can have a StringStorage member.
// C++ exception types must be copyable.
StringStorage(const StringStorage& other)
{
Set(other.Data());
}
StringStorage& operator=(const StringStorage&) = delete;
void Set(const char* pszString) noexcept
{
const size_t stringLength = ::strlen(pszString);
if (TryAllocate(stringLength))
{
Memcpy(m_pData, m_allocationSize, pszString, stringLength + 1);
}
}
bool TryAllocate(size_t stringLength) noexcept
{
const size_t allocationSize = sizeof(TaefChar8_t) * (stringLength + 1);
m_pData = AllocateString(allocationSize);
if (m_pData)
{
m_allocationSize = allocationSize;
return true;
}
return false;
}
char* Data() const noexcept { return reinterpret_cast(m_pData); }
TaefChar8_t* DataAsChar8T() const noexcept { return m_pData; }
size_t AllocationSize() const noexcept { return m_allocationSize; }
char* Detach() noexcept
{
char* const pData = Data();
m_pData = nullptr;
m_allocationSize = 0;
return pData;
}
static void TAEF_CDECL TaefAbiStringDeallocator(_In_ _Post_invalid_ char* pStr)
{
FreeString(pStr);
}
private:
void Clear() noexcept
{
if (m_pData)
{
FreeString(m_pData);
m_pData = nullptr;
m_allocationSize = 0;
}
}
static TaefChar8_t* AllocateString(size_t allocationSize) noexcept
{
#if defined(__cpp_exceptions)
try
{
#endif
// Ideally we would use a std::nothrow here to use a non-throwing new, but we are very restricted in what we can
// do while still having all existing TAEF tests be able to compile. Using raw sized operator new is the most portable
// across all the different configurations of TAEF tests.
return static_cast(::operator new(allocationSize));
#if defined(__cpp_exceptions)
}
catch (const std::exception&)
{
return nullptr;
}
#endif
}
static void FreeString(_In_ _Post_invalid_ void* pszString) noexcept
{
// This corresponds with the use of ::operator new(size_t) to allocate the string.
::operator delete(pszString);
}
TaefChar8_t* m_pData = nullptr;
size_t m_allocationSize = 0;
};
}/* namespace Private */
#if defined(_MSC_VER)
# pragma warning(push)
// Disable warnings related to implicitly deleted special member functions.
# pragma warning(disable: 4623 4625 4626)
#endif
#if defined(__cpp_exceptions) && !defined(NO_VERIFY_EXCEPTIONS)
// Thrown when a Verify method fails
class VerifyFailureException
// TAEF_USE_STD_EXCEPTION_FOR_VERIFY_FAILURE is always defined on Linux, but it is off-by-default on Windows for compatibility.
// This options will likely be removed in the future once WEX::Common::Exception is updated to itself inherit from std::exception.
# if defined(TAEF_USE_STD_EXCEPTION_FOR_VERIFY_FAILURE)
: public std::exception
# else
: public WEX::Common::Exception
# endif
{
public:
VerifyFailureException(const VerifyFailureException&) = default;
~VerifyFailureException() = default;
# if defined(TAEF_USE_STD_EXCEPTION_FOR_VERIFY_FAILURE)
HRESULT ErrorCode() const noexcept
{
return m_errorCode;
}
const char* what() const noexcept override
{
return m_message.Data() ? m_message.Data() : "An allocation failure occurred when storing the message for the VerifyFailureException.";
}
# endif
private:
friend class Private::MacroVerify;
friend class Verify;
VerifyFailureException(HRESULT errorCode, const char* pszMessage)
# if defined(TAEF_USE_STD_EXCEPTION_FOR_VERIFY_FAILURE)
: m_errorCode{errorCode}
, m_message{pszMessage}
# else
: WEX::Common::Exception{errorCode, pszMessage}
# endif
{}
# if !defined(TAEF_USE_STD_EXCEPTION_FOR_VERIFY_FAILURE)
VerifyFailureException(HRESULT errorCode, const wchar_t* pszMessage)
: WEX::Common::Exception{errorCode, pszMessage}
{}
# endif
// Making 'operator new' private prevents heap allocation of Exception, and forces Exception instances
// to be thrown by value.
static void* operator new(size_t) = delete;
static void operator delete(void*) {}
# if defined(TAEF_USE_STD_EXCEPTION_FOR_VERIFY_FAILURE)
const HRESULT m_errorCode;
// When exceptions are enabled while STL support is disabled, we include but not any other STL headers.
// This means that we can't use std::string here as it won't be available in this scenario.
const Private::StringStorage m_message;
# endif
};
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
namespace Private
{
extern "C"
{
// One counter per thread across all modules.
WEXLOGGER_API int WEXLOGGER_STDCALL GetDisabledVerifyThrowCount() noexcept;
WEXLOGGER_API void WEXLOGGER_STDCALL IncrementDisabledVerifyThrowCount() noexcept;
WEXLOGGER_API void WEXLOGGER_STDCALL DecrementDisabledVerifyThrowCount() noexcept;
}
}
// Class used to disable VerifyFailureException throwing for its lifetime (on a per-thread basis)
class DisableVerifyExceptions final
{
public:
DisableVerifyExceptions() noexcept
{
Private::IncrementDisabledVerifyThrowCount();
}
~DisableVerifyExceptions() noexcept
{
Private::DecrementDisabledVerifyThrowCount();
}
int GetDisabledVerifyThrowCount() noexcept
{
return Private::GetDisabledVerifyThrowCount();
}
private:
// Making 'operator new' private prevents heap allocation
static void* operator new(size_t) = delete;
static void operator delete(void*) = delete;
DisableVerifyExceptions(const DisableVerifyExceptions&) = delete;
DisableVerifyExceptions& operator=(const DisableVerifyExceptions&) = delete;
};
#endif // #if defined(__cpp_exceptions) && !defined(NO_VERIFY_EXCEPTIONS)
namespace VerifyOutputSettings
{
enum Setting
{
None = 0,
LogOnlyFailures = 0x01,
LogFailuresAsBlocked = 0x02,
LogFailuresAsWarnings = 0x04,
LogValuesOnSuccess = 0x08
};
inline Setting& operator|=(Setting& lhs, Setting rhs) noexcept
{
lhs = static_cast((static_cast(lhs) | static_cast(rhs)));
return lhs;
}
inline Setting operator|(Setting lhs, Setting rhs) noexcept
{
return lhs |= rhs;
}
inline int operator&(Setting lhs, Setting rhs) noexcept
{
return (static_cast(lhs) & static_cast(rhs));
}
}/* namespace VerifyOutputSettings */
namespace Private
{
extern "C"
{
// One setting per thread across all modules.
WEXLOGGER_API VerifyOutputSettings::Setting WEXLOGGER_STDCALL GetVerifyOutputSettings() noexcept;
WEXLOGGER_API void WEXLOGGER_STDCALL SetVerifyOutputSettings(VerifyOutputSettings::Setting settings) noexcept;
}
inline bool LogValuesOnSuccess() noexcept
{
#if defined(LOG_VALUES_ON_SUCCESS)
return true;
#else
// If LOG_VALUES_ON_SUCCESS wasn't globally defined, look at the current VerifyOutputSettings.
return ((GetVerifyOutputSettings() & VerifyOutputSettings::LogValuesOnSuccess) != 0);
#endif
}
inline bool LogOnlyFailures() noexcept
{
#if defined(LOG_ONLY_FAILURES)
return true;
#else
// If LOG_ONLY_FAILURES wasn't globally defined, look at the current VerifyOutputSettings.
return ((GetVerifyOutputSettings() & VerifyOutputSettings::LogOnlyFailures) != 0);
#endif
}
}
// This class modifies the verify logging settings for the current thread while it is in scope.
class SetVerifyOutput final
{
public:
SetVerifyOutput(VerifyOutputSettings::Setting outputSettings) noexcept
: m_previousSetting(Private::GetVerifyOutputSettings())
{
Private::SetVerifyOutputSettings(outputSettings);
}
~SetVerifyOutput() noexcept
{
Private::SetVerifyOutputSettings(m_previousSetting);
}
private:
VerifyOutputSettings::Setting m_previousSetting;
// Making 'operator new' private prevents heap allocation, and forces instances
// to be thrown by value.
static void* operator new(size_t) = delete;
static void operator delete(void*) = delete;
SetVerifyOutput(const SetVerifyOutput&) = delete;
SetVerifyOutput& operator=(const SetVerifyOutput&) = delete;
};
// When using TAEF_USE_STD_EXCEPTION_FOR_VERIFY_FAILURE, you must use the VERIFY macros instead
// of the Verify class. This is because the VERIFY macros have switched over to UTF-8, but the
// Verify class still uses UTF-16.
#if defined(_WIN32) && (defined(NO_VERIFY_EXCEPTIONS) || !defined(TAEF_USE_STD_EXCEPTION_FOR_VERIFY_FAILURE))
// Macro for creating an ErrorInfo object with file, function and line information
# define PRIVATE_VERIFY_ERROR_INFO WEX::TestExecution::ErrorInfo(__WFILE__, __WFUNCTION__, __LINE__)
struct ErrorInfo
{
ErrorInfo(const wchar_t* pszFileName, const wchar_t* pszFunctionName, int lineNumber) noexcept
: pszFile(pszFileName)
, pszFunction(pszFunctionName)
, line(lineNumber)
{
}
ErrorInfo(const ErrorInfo& other) noexcept
: pszFile(other.pszFile)
, pszFunction(other.pszFunction)
, line(other.line)
{
}
const wchar_t* pszFile;
const wchar_t* pszFunction;
int line;
};
const wchar_t c_szVerifyContext[] = L"Verify";
const wchar_t c_szErrorGeneratingPassMessage[] = L"Error generating pass message; possibly out of memory.";
const wchar_t c_szErrorGeneratingFailureMessage[] = L"Error generating failure message; possibly out of memory.";
// Tests should generally use the VERIFY macros. However, this class is an alternative which is still provided for
// backwards compatibility.
class Verify final
{
friend class WEX::TestExecution::ComVerify;
friend class WEX::TestExecution::Private::MacroVerify;
public:
// Verifies that two specified objects are equal.
template
static bool AreEqual(const T1& expected, const T2& actual, const ErrorInfo& errorInfo)
{
return AreEqual(expected, actual, nullptr, errorInfo);
}
// Verifies that two specified objects are equal.
template
static bool AreEqual(const T1& expected, const T2& actual, const wchar_t* pszMessage, const ErrorInfo& errorInfo)
{
VerifyMessageFunctor buildMessage(L"AreEqual", pszMessage);
return AreEqualImpl(expected, actual, errorInfo, buildMessage);
}
// Verifies that two specified objects are not equal.
template
static bool AreNotEqual(const T1& expected, const T2& actual, const ErrorInfo& errorInfo)
{
return AreNotEqual(expected, actual, nullptr, errorInfo);
}
// Verifies that two specified objects are not equal.
template
static bool AreNotEqual(const T1& expected, const T2& actual, const wchar_t* pszMessage, const ErrorInfo& errorInfo)
{
VerifyMessageFunctor buildMessage(L"AreNotEqual", pszMessage);
return AreNotEqualImpl(expected, actual, errorInfo, buildMessage);
}
// Verifies that the first parameter is less than the second parameter.
template
static bool IsLessThan(const T1& expectedLess, const T2& expectedGreater, const ErrorInfo& errorInfo)
{
return IsLessThan(expectedLess, expectedGreater, nullptr, errorInfo);
}
// Verifies that the first parameter is less than the second parameter.
template
static bool IsLessThan(const T1& expectedLess, const T2& expectedGreater, const wchar_t* pszMessage, const ErrorInfo& errorInfo)
{
VerifyMessageFunctor buildMessage(L"IsLessThan", pszMessage);
return IsLessThanImpl(expectedLess, expectedGreater, errorInfo, buildMessage);
}
// Verifies that the first parameter is less than or equal to the second parameter.
template
static bool IsLessThanOrEqual(const T1& expectedLess, const T2& expectedGreater, const ErrorInfo& errorInfo)
{
return IsLessThanOrEqual(expectedLess, expectedGreater, nullptr, errorInfo);
}
// Verifies that the first parameter is less than or equal to the second parameter.
template
static bool IsLessThanOrEqual(const T1& expectedLess, const T2& expectedGreater, const wchar_t* pszMessage, const ErrorInfo& errorInfo)
{
VerifyMessageFunctor buildMessage(L"IsLessThanOrEqual", pszMessage);
return IsLessThanOrEqualImpl(expectedLess, expectedGreater, errorInfo, buildMessage);
}
// Verifies that the first parameter is greater than the second parameter.
template
static bool IsGreaterThan(const T1& expectedGreater, const T2& expectedLess, const ErrorInfo& errorInfo)
{
return IsGreaterThan(expectedGreater, expectedLess, nullptr, errorInfo);
}
// Verifies that the first parameter is greater than the second parameter.
template
static bool IsGreaterThan(const T1& expectedGreater, const T2& expectedLess, const wchar_t* pszMessage, const ErrorInfo& errorInfo)
{
VerifyMessageFunctor buildMessage(L"IsGreaterThan", pszMessage);
return IsGreaterThanImpl(expectedGreater, expectedLess, errorInfo, buildMessage);
}
// Verifies that the first parameter is greater than or equal to the second parameter.
template
static bool IsGreaterThanOrEqual(const T1& expectedGreater, const T2& expectedLess, const ErrorInfo& errorInfo)
{
return IsGreaterThanOrEqual(expectedGreater, expectedLess, nullptr, errorInfo);
}
// Verifies that the first parameter is greater than or equal to the second parameter.
template
static bool IsGreaterThanOrEqual(const T1& expectedGreater, const T2& expectedLess, const wchar_t* pszMessage, const ErrorInfo& errorInfo)
{
VerifyMessageFunctor buildMessage(L"IsGreaterThanOrEqual", pszMessage);
return IsGreaterThanOrEqualImpl(expectedGreater, expectedLess, errorInfo, buildMessage);
}
private:
// Returns the address of T
template
class GetAddressOf
{
public:
static const T* Value(const T& ref)
{
return &ref;
}
typedef const T* Type;
};
// Returns the address of T
template
class GetAddressOf
{
public:
static const T* Value(const T& ref)
{
return &ref;
}
typedef const T* Type;
};
// Returns the address T* (a pointer-to-pointer) as void*
template
class GetAddressOf
{
public:
static const void* Value(T* const& p)
{
return &p;
}
typedef const void* Type;
};
public:
// Verifies that the two parameters specified refer to the same object.
template
static bool AreSame(const T1& expected, const T2& actual, const ErrorInfo& errorInfo)
{
return AreSame(expected, actual, nullptr, errorInfo);
}
// Verifies that the two parameters specified refer to the same object.
template
static bool AreSame(const T1& expected, const T2& actual, const wchar_t* pszMessage, const ErrorInfo& errorInfo)
{
VerifyMessageFunctor buildMessage(L"AreSame", pszMessage);
return AreSameImpl(expected, actual, errorInfo, buildMessage);
}
// Verifies that the two parameters specified do not refer to the same object.
template
static bool AreNotSame(const T1& expected, const T2& actual, const ErrorInfo& errorInfo)
{
return AreNotSame(expected, actual, nullptr, errorInfo);
}
// Verifies that the two parameters specified do not refer to the same object.
template
static bool AreNotSame(const T1& expected, const T2& actual, const wchar_t* pszMessage, const ErrorInfo& errorInfo)
{
VerifyMessageFunctor buildMessage(L"AreNotSame", pszMessage);
return AreNotSameImpl(expected, actual, errorInfo, buildMessage);
}
// Fails without checking any conditions.
_Post_equal_to_(false)
static bool Fail(const ErrorInfo& errorInfo)
{
return Fail(nullptr, errorInfo);
}
// Fails without checking any conditions.
_Post_equal_to_(false)
static bool Fail(const wchar_t* pszMessage, const ErrorInfo& errorInfo)
{
if (WEX::Common::NoThrowString::IsNullOrEmpty(pszMessage))
{
return VerificationFailed([]()->WEX::Common::NoThrowString { return L"Fail"; }, errorInfo);
}
return VerificationFailed([&]()->WEX::Common::NoThrowString { return pszMessage; }, errorInfo);
}
// Verifies that the specified condition is true.
_Post_equal_to_(condition)
static bool IsTrue(bool condition, const ErrorInfo& errorInfo)
{
return IsTrue(condition, nullptr, errorInfo);
}
// Verifies that the specified condition is true.
_Post_equal_to_(condition)
static bool IsTrue(bool condition, const wchar_t* pszMessage, const ErrorInfo& errorInfo)
{
VerifyMessageFunctor buildMessage(L"IsTrue", pszMessage);
return IsTrueImpl(condition, errorInfo, buildMessage);
}
// Verifies that the specified condition is false.
_Post_equal_to_(!condition)
static bool IsFalse(bool condition, const ErrorInfo& errorInfo)
{
return IsFalse(condition, nullptr, errorInfo);
}
// Verifies that the specified condition is false.
_Post_equal_to_(!condition)
static bool IsFalse(bool condition, const wchar_t* pszMessage, const ErrorInfo& errorInfo)
{
VerifyMessageFunctor buildMessage(L"IsFalse", pszMessage);
return IsFalseImpl(condition, errorInfo, buildMessage);
}
// Verifies that the specified parameter is null.
template
static bool IsNull(const T& object, const ErrorInfo& errorInfo)
{
return IsNull(object, nullptr, errorInfo);
}
// Verifies that the specified parameter is null.
template
static bool IsNull(const T& object, const wchar_t* pszMessage, const ErrorInfo& errorInfo)
{
VerifyMessageFunctor buildMessage(L"IsNull", pszMessage);
return IsNullImpl(object, errorInfo, buildMessage);
}
// Verifies that the specified parameter is not null.
template
static bool IsNotNull(const T& object, const ErrorInfo& errorInfo)
{
return IsNotNull(object, nullptr, errorInfo);
}
// Verifies that the specified parameter is not null.
template
static bool IsNotNull(const T& object, const wchar_t* pszMessage, const ErrorInfo& errorInfo)
{
VerifyMessageFunctor buildMessage(L"IsNotNull", pszMessage);
return IsNotNullImpl(object, errorInfo, buildMessage);
}
// Verifies that the specified HRESULT is successful.
_Post_equal_to_(SUCCEEDED(hr))
static bool Succeeded(HRESULT hr, const ErrorInfo& errorInfo)
{
return Succeeded(hr, nullptr, errorInfo);
}
// Verifies that the specified HRESULT is successful.
_Post_equal_to_(SUCCEEDED(hr))
static bool Succeeded(HRESULT hr, const wchar_t* pszMessage, const ErrorInfo& errorInfo)
{
VerifyMessageFunctor buildMessage(L"Succeeded", pszMessage);
return SucceededImpl(hr, errorInfo, buildMessage);
}
// Verifies that the specified HRESULT is not successful.
_Post_equal_to_(FAILED(hr))
static bool Failed(HRESULT hr, const ErrorInfo& errorInfo)
{
return Failed(hr, nullptr, errorInfo);
}
// Verifies that the specified HRESULT is not successful.
_Post_equal_to_(FAILED(hr))
static bool Failed(HRESULT hr, const wchar_t* pszMessage, const ErrorInfo& errorInfo)
{
VerifyMessageFunctor buildMessage(L"Failed", pszMessage);
return FailedImpl(hr, errorInfo, buildMessage);
}
// Verifies that the specified Win32 error code is successful.
_Post_equal_to_(win32ErrorCode == 0)
static bool Win32Succeeded(::LONG win32ErrorCode, const ErrorInfo& errorInfo)
{
return Win32Succeeded(win32ErrorCode, nullptr, errorInfo);
}
// Verifies that the specified Win32 error code is successful.
_Post_equal_to_(win32ErrorCode == 0)
static bool Win32Succeeded(::LONG win32ErrorCode, const wchar_t* pszMessage, const ErrorInfo& errorInfo)
{
VerifyMessageFunctor buildMessage(L"Win32Succeeded", pszMessage);
return Win32SucceededImpl(win32ErrorCode, errorInfo, buildMessage);
}
// Verifies that the specified Win32 error code is not successful.
_Post_equal_to_(win32ErrorCode != 0)
static bool Win32Failed(::LONG win32ErrorCode, const ErrorInfo& errorInfo)
{
return Win32Failed(win32ErrorCode, nullptr, errorInfo);
}
// Verifies that the specified Win32 error code is not successful.
_Post_equal_to_(win32ErrorCode != 0)
static bool Win32Failed(::LONG win32ErrorCode, const wchar_t* pszMessage, const ErrorInfo& errorInfo)
{
VerifyMessageFunctor buildMessage(L"Win32Failed", pszMessage);
return Win32FailedImpl(win32ErrorCode, errorInfo, buildMessage);
}
// Verifies that the specified Win32 BOOL succeeded, and logs the error code returned from GetLastError if not.
_Post_equal_to_(win32Bool != 0)
static bool Win32BoolSucceeded(::BOOL win32Bool, const ErrorInfo& errorInfo)
{
return Win32BoolSucceeded(win32Bool, nullptr, errorInfo);
}
// Verifies that the specified Win32 BOOL succeeded, and logs the error code returned from GetLastError if not.
_Post_equal_to_(win32Bool != 0)
static bool Win32BoolSucceeded(::BOOL win32Bool, const wchar_t* pszMessage, const ErrorInfo& errorInfo)
{
VerifyMessageFunctor buildMessage(L"Win32BoolSucceeded", pszMessage);
return Win32BoolSucceededImpl(win32Bool, errorInfo, buildMessage);
}
// Verifies that the specified Win32 error code is not successful.
_Post_equal_to_(win32Bool == 0)
static bool Win32BoolFailed(::BOOL win32Bool, const ErrorInfo& errorInfo)
{
return Win32BoolFailed(win32Bool, nullptr, errorInfo);
}
// Verifies that the specified Win32 error code is not successful.
_Post_equal_to_(win32Bool == 0)
static bool Win32BoolFailed(::BOOL win32Bool, const wchar_t* pszMessage, const ErrorInfo& errorInfo)
{
VerifyMessageFunctor buildMessage(L"Win32BoolFailed", pszMessage);
return Win32BoolFailedImpl(win32Bool, errorInfo, buildMessage);
}
# if defined(NT_SUCCESS)
// Verifies that the specified NTSTATUS error code is successful.
_Post_equal_to_(NT_SUCCESS(ntStatus))
static bool NtSuccess(NTSTATUS ntStatus, const ErrorInfo& errorInfo)
{
return NtSuccess(ntStatus, nullptr, errorInfo);
}
// Verifies that the specified NTSTATUS error code is successful.
_Post_equal_to_(NT_SUCCESS(ntStatus))
static bool NtSuccess(NTSTATUS ntStatus, const wchar_t* pszMessage, const ErrorInfo& errorInfo)
{
VerifyMessageFunctor buildMessage(L"NtSuccess", pszMessage);
return NtSuccessImpl(ntStatus, errorInfo, buildMessage);
}
// Verifies that the specified NTSTATUS error code is not successful.
_Post_equal_to_(!NT_SUCCESS(ntStatus))
static bool NotNtSuccess(NTSTATUS ntStatus, const ErrorInfo& errorInfo)
{
return NotNtSuccess(ntStatus, nullptr, errorInfo);
}
// Verifies that the specified NTSTATUS error code is not successful.
_Post_equal_to_(!NT_SUCCESS(ntStatus))
static bool NotNtSuccess(NTSTATUS ntStatus, const wchar_t* pszMessage, const ErrorInfo& errorInfo)
{
VerifyMessageFunctor buildMessage(L"NotNtSuccess", pszMessage);
return NotNtSuccessImpl(ntStatus, errorInfo, buildMessage);
}
# endif // #if defined(NT_SUCCESS)
private:
template
static bool AppendValueToMessage(WEX::Common::NoThrowString& message, const T& value)
{
WEX::Common::NoThrowString valueString(VerifyOutputTraits::ToString(value));
if (!valueString.IsEmpty())
{
message.Append(valueString);
return true;
}
return false;
}
static bool AppendValueToMessage(WEX::Common::NoThrowString& message, const wchar_t* pszValue)
{
message.Append(pszValue);
return true;
}
static bool AppendValueToMessage(WEX::Common::NoThrowString& message, const char* paszValue)
{
message.Append(WEX::Common::NoThrowString(paszValue));
return true;
}
static bool AppendValueToMessage(WEX::Common::NoThrowString& message, const WEX::Common::String& value)
{
message.Append(static_cast(value));
return true;
}
static bool AppendValueToMessage(WEX::Common::NoThrowString& message, const WEX::Common::NoThrowString& value)
{
message.Append(value);
return true;
}
# if defined(TAEF_STL_SUPPORT)
static bool AppendValueToMessage(WEX::Common::NoThrowString& message, const std::wstring& value)
{
message.Append(value.c_str());
return true;
}
static bool AppendValueToMessage(WEX::Common::NoThrowString& message, const std::string& value)
{
message.Append(WEX::Common::NoThrowString(value.c_str()));
return true;
}
# endif // #if defined(TAEF_STL_SUPPORT)
template
static void AddValuesListToMessage(WEX::Common::NoThrowString& message, const T1& expected, const T2& actual)
{
const int lengthWithoutValues = message.GetLength();
message.Append(L" - Values (");
if (!AppendValueToMessage(message, expected))
{
// Roll back the append of " - Values (".
message.GetBufferSetLength(lengthWithoutValues);
message.ReleaseBuffer(lengthWithoutValues);
return;
}
message.Append(L", ");
AppendValueToMessage(message, actual);
message.Append(L")");
}
template
static void AddValuesListToMessage(WEX::Common::NoThrowString& message, const T& expected)
{
const int lengthWithoutValue = message.GetLength();
message.Append(L" - Value (");
if (!AppendValueToMessage(message, expected))
{
// Roll back the append of " - Value (".
message.GetBufferSetLength(lengthWithoutValue);
message.ReleaseBuffer(lengthWithoutValue);
return;
}
message.Append(L")");
}
template
static bool VerificationFailed(const T1& expected, const T2& actual, const TBuildMessage& buildMessage, const ErrorInfo& errorInfo)
{
WEX::Common::PreserveLastError preserveLastError;
WEX::Common::NoThrowString message{ buildMessage() };
AddValuesListToMessage(message, expected, actual);
return FailImpl(message, errorInfo, TAEF_E_FAIL /*Work around the compiler issue on the woa build*/);
}
template
static bool VerificationFailed(const T& expected, const TBuildMessage& buildMessage, const ErrorInfo& errorInfo)
{
WEX::Common::PreserveLastError preserveLastError;
WEX::Common::NoThrowString message{ buildMessage() };
AddValuesListToMessage(message, expected);
return FailImpl(message, errorInfo, TAEF_E_FAIL /*Work around the compiler issue on the woa build*/);
}
template
_Post_equal_to_(false)
static bool VerificationFailed(const TBuildMessage& buildMessage, const ErrorInfo& errorInfo)
{
WEX::Common::PreserveLastError preserveLastError;
return FailImpl(buildMessage(), errorInfo);
}
_Post_equal_to_(false)
static bool FailImpl(const WEX::Common::NoThrowString& message, const ErrorInfo& errorInfo, HRESULT hrToThrow = TAEF_E_FAIL)
{
if (!message.IsEmpty())
{
VerifyOutputSettings::Setting outputSetting = Private::GetVerifyOutputSettings();
if (outputSetting & VerifyOutputSettings::LogFailuresAsBlocked)
{
WEX::Logging::Log::Result(WEX::Logging::TestResults::Blocked,
message.ToCStrWithFallbackTo(c_szErrorGeneratingFailureMessage), c_szVerifyContext);
}
else if (outputSetting & VerifyOutputSettings::LogFailuresAsWarnings)
{
WEX::Logging::Log::Warning(message.ToCStrWithFallbackTo(c_szErrorGeneratingFailureMessage), c_szVerifyContext,
errorInfo.pszFile, errorInfo.pszFunction, errorInfo.line);
}
else
{
WEX::Logging::Log::Error(message.ToCStrWithFallbackTo(c_szErrorGeneratingFailureMessage), c_szVerifyContext,
errorInfo.pszFile, errorInfo.pszFunction, errorInfo.line);
}
}
# if defined(__cpp_exceptions) && !defined(NO_VERIFY_EXCEPTIONS)
if (Private::GetDisabledVerifyThrowCount() == 0)
{
throw VerifyFailureException(hrToThrow, message);
}
# else
UNREFERENCED_PARAMETER(hrToThrow); // Unreferenced if exceptions are disabled
# endif
return false;
}
template
_Post_equal_to_(true)
static bool VerificationPassed(const T1& expected, const T2& actual, const TBuildMessage& buildMessage)
{
if (Private::LogOnlyFailures())
{
return true;
}
WEX::Common::PreserveLastError preserveLastError;
WEX::Common::NoThrowString message{ buildMessage() };
if (Private::LogValuesOnSuccess())
{
AddValuesListToMessage(message, expected, actual);
}
return PassImpl(message);
}
template
_Post_equal_to_(true)
static bool VerificationPassed(const T& expected, const TBuildMessage& buildMessage)
{
if (Private::LogOnlyFailures())
{
return true;
}
WEX::Common::PreserveLastError preserveLastError;
WEX::Common::NoThrowString message{ buildMessage() };
if (Private::LogValuesOnSuccess())
{
AddValuesListToMessage(message, expected);
}
return PassImpl(message);
}
template
_Post_equal_to_(true)
static bool VerificationPassed(const TBuildMessage& buildMessage)
{
if (Private::LogOnlyFailures())
{
return true;
}
WEX::Common::PreserveLastError preserveLastError;
return PassImpl(buildMessage());
}
_Post_equal_to_(true)
static bool PassImpl(const WEX::Common::NoThrowString& message)
{
if (!message.IsEmpty())
{
WEX::Logging::Log::Comment(message.ToCStrWithFallbackTo(c_szErrorGeneratingPassMessage), c_szVerifyContext);
}
return true;
}
class VerifyMessageFunctor
{
public:
VerifyMessageFunctor(const wchar_t* pszDefaultMessage, const wchar_t* pszMessage)
: m_pszDefaultMessage(pszDefaultMessage)
, m_pszMessage(pszMessage)
{
}
WEX::Common::NoThrowString operator()() const
{
if (WEX::Common::NoThrowString::IsNullOrEmpty(m_pszMessage))
{
return WEX::Common::NoThrowString(m_pszDefaultMessage);
}
return WEX::Common::NoThrowString().Format(L"%s: %s", m_pszDefaultMessage, m_pszMessage);
}
private:
const wchar_t* m_pszDefaultMessage;
const wchar_t* m_pszMessage;
};
template
static bool AreEqualImpl(const T1& expected, const T2& actual, const ErrorInfo& errorInfo, const TBuildMessage& buildMessage)
{
if (!VerifyCompareTraits::AreEqual(expected, actual))
{
return VerificationFailed(expected, actual, buildMessage, errorInfo);
}
if (!Private::LogOnlyFailures())
{
return VerificationPassed(expected, actual, buildMessage);
}
return true;
}
template
static bool AreNotEqualImpl(const T1& expected, const T2& actual, const ErrorInfo& errorInfo, const TBuildMessage& buildMessage)
{
if (VerifyCompareTraits::AreEqual(expected, actual))
{
return VerificationFailed(expected, actual, buildMessage, errorInfo);
}
if (!Private::LogOnlyFailures())
{
return VerificationPassed(expected, actual, buildMessage);
}
return true;
}
template
static bool IsLessThanImpl(const T1& expectedLess, const T2& expectedGreater, const ErrorInfo& errorInfo, const TBuildMessage& buildMessage)
{
if (!VerifyCompareTraits::IsLessThan(expectedLess, expectedGreater))
{
return VerificationFailed(expectedLess, expectedGreater, buildMessage, errorInfo);
}
if (!Private::LogOnlyFailures())
{
return VerificationPassed(expectedLess, expectedGreater, buildMessage);
}
return true;
}
template
static bool IsLessThanOrEqualImpl(const T1& expectedLess, const T2& expectedGreater,
const ErrorInfo& errorInfo, const TBuildMessage& buildMessage)
{
if (!VerifyCompareTraits::IsLessThan(expectedLess, expectedGreater) &&
!VerifyCompareTraits::AreEqual(expectedLess, expectedGreater))
{
return VerificationFailed(expectedLess, expectedGreater, buildMessage, errorInfo);
}
if (!Private::LogOnlyFailures())
{
return VerificationPassed(expectedLess, expectedGreater, buildMessage);
}
return true;
}
template
static bool IsGreaterThanImpl(const T1& expectedGreater, const T2& expectedLess, const ErrorInfo& errorInfo, const TBuildMessage& buildMessage)
{
if (!VerifyCompareTraits::IsGreaterThan(expectedGreater, expectedLess))
{
return VerificationFailed(expectedGreater, expectedLess, buildMessage, errorInfo);
}
if (!Private::LogOnlyFailures())
{
return VerificationPassed(expectedGreater, expectedLess, buildMessage);
}
return true;
}
template
static bool IsGreaterThanOrEqualImpl(const T1& expectedGreater, const T2& expectedLess,
const ErrorInfo& errorInfo, const TBuildMessage& buildMessage)
{
if (!VerifyCompareTraits::IsGreaterThan(expectedGreater, expectedLess) &&
!VerifyCompareTraits::AreEqual(expectedGreater, expectedLess))
{
return VerificationFailed(expectedGreater, expectedLess, buildMessage, errorInfo);
}
if (!Private::LogOnlyFailures())
{
return VerificationPassed(expectedGreater, expectedLess, buildMessage);
}
return true;
}
template
static bool AreSameImpl(const T1& expected, const T2& actual, const ErrorInfo& errorInfo, const TBuildMessage& buildMessage)
{
if (!VerifyCompareTraits::AreSame(expected, actual))
{
return VerificationFailed::Type, typename GetAddressOf::Type>(
GetAddressOf::Value(expected), GetAddressOf::Value(actual), buildMessage, errorInfo);
}
if (!Private::LogOnlyFailures())
{
return VerificationPassed::Type, typename GetAddressOf::Type>(
GetAddressOf::Value(expected), GetAddressOf::Value(actual), buildMessage);
}
return true;
}
template
static bool AreNotSameImpl(const T1& expected, const T2& actual, const ErrorInfo& errorInfo, const TBuildMessage& buildMessage)
{
if (VerifyCompareTraits::AreSame(expected, actual))
{
return VerificationFailed::Type, typename GetAddressOf::Type>(
GetAddressOf::Value(expected), GetAddressOf::Value(actual), buildMessage, errorInfo);
}
if (!Private::LogOnlyFailures())
{
return VerificationPassed::Type, typename GetAddressOf::Type>(
GetAddressOf::Value(expected), GetAddressOf::Value(actual), buildMessage);
}
return true;
}
template
static bool IsTrueImpl(bool condition, const ErrorInfo& errorInfo, const TBuildMessage& buildMessage)
{
if (!condition)
{
return VerificationFailed(buildMessage, errorInfo);
}
if (!Private::LogOnlyFailures())
{
return VerificationPassed(buildMessage);
}
return true;
}
template
static bool IsFalseImpl(bool condition, const ErrorInfo& errorInfo, const TBuildMessage& buildMessage)
{
if (condition)
{
return VerificationFailed(buildMessage, errorInfo);
}
if (!Private::LogOnlyFailures())
{
return VerificationPassed(buildMessage);
}
return true;
}
template
static bool IsNullImpl(const T& object, const ErrorInfo& errorInfo, const TBuildMessage& buildMessage)
{
if (VerifyCompareTraits::IsNull(object))
{
if (!Private::LogOnlyFailures())
{
return VerificationPassed(object, buildMessage);
}
return true;
}
return VerificationFailed(object, buildMessage, errorInfo);
}
// Verifies that the specified parameter is not null.
template
static bool IsNotNullImpl(const T& object, const ErrorInfo& errorInfo, const TBuildMessage& buildMessage)
{
if (!VerifyCompareTraits::IsNull(object))
{
if (!Private::LogOnlyFailures())
{
return VerificationPassed(object, buildMessage);
}
return true;
}
return VerificationFailed(object, buildMessage, errorInfo);
}
template
_Post_equal_to_(SUCCEEDED(hr))
static bool SucceededImpl(HRESULT hr, const ErrorInfo& errorInfo, const TBuildMessage& buildMessage)
{
if (FAILED(hr))
{
return VerificationFailed(hr, buildMessage, errorInfo);
}
if (!Private::LogOnlyFailures())
{
return VerificationPassed(hr, buildMessage);
}
return true;
}
template
_Post_equal_to_(FAILED(hr))
static bool FailedImpl(HRESULT hr, const ErrorInfo& errorInfo, const TBuildMessage& buildMessage)
{
if (!FAILED(hr))
{
return VerificationFailed(hr, buildMessage, errorInfo);
}
if (!Private::LogOnlyFailures())
{
return VerificationPassed(hr, buildMessage);
}
return true;
}
template
_Post_equal_to_(win32ErrorCode == 0)
static bool Win32SucceededImpl(::LONG win32ErrorCode, const ErrorInfo& errorInfo, const TBuildMessage& buildMessage)
{
if (win32ErrorCode != ERROR_SUCCESS)
{
return VerificationFailed< ::LONG>(win32ErrorCode, buildMessage, errorInfo);
}
if (!Private::LogOnlyFailures())
{
return VerificationPassed< ::LONG>(win32ErrorCode, buildMessage);
}
return true;
}
template
_Post_equal_to_(win32ErrorCode != 0)
static bool Win32FailedImpl(::LONG win32ErrorCode, const ErrorInfo& errorInfo, const TBuildMessage& buildMessage)
{
if (win32ErrorCode == ERROR_SUCCESS)
{
return VerificationFailed< ::LONG>(win32ErrorCode, buildMessage, errorInfo);
}
if (!Private::LogOnlyFailures())
{
return VerificationPassed< ::LONG>(win32ErrorCode, buildMessage);
}
return true;
}
template
_Post_equal_to_(win32Bool != 0)
static bool Win32BoolSucceededImpl(::BOOL win32Bool, const ErrorInfo& errorInfo, const TBuildMessage& buildMessage)
{
if (0 == win32Bool)
{
// Log the value returned from Debug::GetLastError()
return VerificationFailed< ::DWORD>(WEX::Common::Debug::GetLastError(), buildMessage, errorInfo);
}
if (!Private::LogOnlyFailures())
{
return VerificationPassed< ::BOOL>(win32Bool, buildMessage);
}
return true;
}
template
_Post_equal_to_(win32Bool == 0)
static bool Win32BoolFailedImpl(::BOOL win32Bool, const ErrorInfo& errorInfo, const TBuildMessage& buildMessage)
{
if (0 != win32Bool)
{
return VerificationFailed< ::BOOL>(win32Bool, buildMessage, errorInfo);
}
if (!Private::LogOnlyFailures())
{
return VerificationPassed< ::BOOL>(win32Bool, buildMessage);
}
return true;
}
# if defined(NT_SUCCESS)
template
_Post_equal_to_(NT_SUCCESS(ntStatus))
static bool NtSuccessImpl(NTSTATUS ntStatus, const ErrorInfo& errorInfo, const TBuildMessage& buildMessage)
{
if (!NT_SUCCESS(ntStatus))
{
return VerificationFailed(ntStatus, buildMessage, errorInfo);
}
if (!Private::LogOnlyFailures())
{
return VerificationPassed(ntStatus, buildMessage);
}
return true;
}
template
_Post_equal_to_(!NT_SUCCESS(ntStatus))
static bool NotNtSuccessImpl(NTSTATUS ntStatus, const ErrorInfo& errorInfo, const TBuildMessage& buildMessage)
{
if (NT_SUCCESS(ntStatus))
{
return VerificationFailed(ntStatus, buildMessage, errorInfo);
}
if (!Private::LogOnlyFailures())
{
return VerificationPassed(ntStatus, buildMessage);
}
return true;
}
# endif // #if defined(NT_SUCCESS)
// Disallow construction of static class
Verify() = delete;
~Verify() = delete;
Verify(const Verify&) = delete;
Verify& operator=(const Verify&) = delete;
};
#endif // #if defined(_WIN32) && (defined(NO_VERIFY_EXCEPTIONS) || !defined(TAEF_USE_STD_EXCEPTION_FOR_VERIFY_FAILURE))
namespace Private
{
#if defined(_MSC_VER)
# pragma warning(push)
// Disable warnings related to implicitly deleted special member functions.
# pragma warning(disable: 4623 4625 4626)
#endif
class Utf8StringView
{
public:
template
Utf8StringView(const TaefChar8_t(&szLiteral)[LiteralSize]) noexcept
: m_pData{szLiteral}
// Subtract off the null terminator.
, m_size{LiteralSize - 1}
{}
Utf8StringView(const TaefChar8_t* pszString) noexcept
: m_pData{pszString}
, m_size{(pszString)?::strlen(reinterpret_cast(pszString)):0}
{}
const TaefChar8_t* Data() const noexcept { return m_pData; }
size_t Size() const noexcept { return m_size; }
private:
const TaefChar8_t* const m_pData;
const size_t m_size;
};
class StringWriter
{
public:
StringWriter(char* pData, size_t size) noexcept
: m_pData{pData}
, m_allocationSize{size}
{}
void Append(const char* pszValue, size_t length) noexcept
{
Memcpy(m_pData + m_initializedCharCount, m_allocationSize - m_initializedCharCount, pszValue, length);
m_initializedCharCount += length;
}
void Append(Utf8StringView utf8StringView) noexcept
{
Append(reinterpret_cast(utf8StringView.Data()), utf8StringView.Size());
}
void AppendChar(char charToAppend) noexcept
{
m_pData[m_initializedCharCount] = charToAppend;
++m_initializedCharCount;
}
// Appends " [HRESULT: 0x%x]".
// 14 characters for " [HRESULT: 0x]" plus 8 for the number in hex.
static constexpr size_t c_maxStringSizeForHResult = 14 + 8;
void AppendHResult(HRESULT hr) noexcept
{
Append(Utf8StringView{u8" [HRESULT: 0x"});
constexpr size_t c_hexDigitsInHResult = 8;
for (size_t i = c_hexDigitsInHResult; i > 0; --i)
{
unsigned char valueForChar = static_cast(hr & 0xf);
m_pData[m_initializedCharCount + i - 1] = valueForChar < 10 ? valueForChar + '0' : valueForChar - 10 + 'a';
hr >>= 4;
}
Advance(c_hexDigitsInHResult);
AppendChar(']');
}
// Appends " [Error code: %d]".
// 15 characters for " [Error code: ]" plus 11 for the longest possible int (-1234567890).
static constexpr size_t c_maxStringSizeForUnrecognizedErrorCode = 15 + 11;
void AppendUnrecognizedErrorCode(int errorCode) noexcept
{
Append(Utf8StringView{u8" [Error code: "});
// This is similar to std::to_chars from , but not all tests have access to that.
unsigned int unsignedErrorCode = static_cast(errorCode);
if (errorCode < 0)
{
AppendChar('-');
unsignedErrorCode = static_cast(0-unsignedErrorCode);
}
static_assert(sizeof(unsignedErrorCode) == 4, "Verify.h currently depends on unsigned ints being four bytes.");
constexpr size_t c_maxStringSizeForUnsignedInt = 10;
char buffer[c_maxStringSizeForUnsignedInt]{};
char* pEndOfBuffer = buffer + c_maxStringSizeForUnsignedInt;
char* pStartOfNumber = pEndOfBuffer;
// Using do instead of while ensures this works when unsignedErrorCode is 0.
do
{
--pStartOfNumber;
*pStartOfNumber = static_cast(unsignedErrorCode % 10 + '0');
unsignedErrorCode /= 10;
} while (unsignedErrorCode > 0);
Append(pStartOfNumber, static_cast(pEndOfBuffer - pStartOfNumber));
AppendChar(']');
}
char* GetWriteBuffer() const noexcept
{
return &m_pData[m_initializedCharCount];
}
void Advance(size_t count) noexcept
{
m_initializedCharCount += count;
}
size_t GetInitializedCharCount() const noexcept
{
return m_initializedCharCount;
}
void SetInitializedCharCount(size_t initializedCharCount) noexcept
{
m_initializedCharCount = initializedCharCount;
}
void WriteNullTerminator() noexcept
{
AppendChar('\0');
}
private:
char* const m_pData;
const size_t m_allocationSize;
size_t m_initializedCharCount = 0;
};
#if defined _MSC_VER
# pragma warning(pop)
#endif
#if defined(TAEF_SYSTEM_ERROR_SUPPORT)
inline HRESULT GetHResultForStdErrorCode(const std::error_code& errorCode, bool& isExactConvesion)
{
# if defined(_WIN32)
if (errorCode.category() == std::system_category())
{
// MSVC's STL uses Win32 error codes for the error code when system_category is used.
// We also allow the use of HRESULT for the error code in this case.
if (errorCode.value() != 0)
{
isExactConvesion = true;
// If the error code is already a failed HRESULT, HRESULT_FROM_WIN32 will leave it unchanged.
return HRESULT_FROM_WIN32(static_cast(errorCode.value()));
}
// An errorCode of 0 is not expected for an exception. Fall back to TAEF_E_UNEXPECTED.
}
# endif
// If the error category is not system_category, then there is no prescribed
// way to translate to an HRESULT.
isExactConvesion = false;
return TAEF_E_UNEXPECTED;
}
#endif // if defined(TAEF_SYSTEM_ERROR_SUPPORT)
#if defined(__cpp_exceptions)
# if defined(_WIN32)
// This returns a message with one of these formats:
// "{beginningOfMessage}: {pszExceptionMessage} [HRESULT: 0x{exceptionHResult}]" (if includeHResultInMessage is true)
// "{beginningOfMessage}: {pszExceptionMessage}" (if includeHResultInMessage is false)
// If formatting fails, this returns beginningOfMessage.Data() as a fallback; so beginningOfMessage must be null terminated.
inline const TaefChar8_t* GetExceptionMessageForExceptionWithHResultAndWideMessage(StringStorage& messageStorage,
Utf8StringView beginningOfMessage, const wchar_t* pszExceptionMessage, size_t exceptionMessageSize, HRESULT exceptionHResult,
bool includeHResultInMessage) noexcept
{
size_t utf8ByteCountForExceptionMessage = 0;
if (TAEF_TryGetUtf8ByteCountForWCharStringValue(pszExceptionMessage, exceptionMessageSize, utf8ByteCountForExceptionMessage))
{
// beginningOfMessage + ::strlen(": ") + pszExceptionMessage + pExceptionHResult (if includeHResultInMessage is true)
const size_t totalSize = beginningOfMessage.Size() + 2 + utf8ByteCountForExceptionMessage +
(includeHResultInMessage ? StringWriter::c_maxStringSizeForHResult : 0);
if (messageStorage.TryAllocate(totalSize))
{
StringWriter messageWriter{messageStorage.Data(), messageStorage.AllocationSize()};
messageWriter.Append(beginningOfMessage);
messageWriter.AppendChar(':');
messageWriter.AppendChar(' ');
size_t bytesWritten = 0;
if (TAEF_TryWriteUtf8ForWCharStringValue(pszExceptionMessage, exceptionMessageSize,
messageWriter.GetWriteBuffer(), utf8ByteCountForExceptionMessage, bytesWritten))
{
messageWriter.Advance(bytesWritten);
if (includeHResultInMessage)
{
messageWriter.AppendHResult(exceptionHResult);
}
messageWriter.WriteNullTerminator();
return messageStorage.DataAsChar8T();
}
}
}
return beginningOfMessage.Data();
}
# endif // #if defined(_WIN32)
inline const TaefChar8_t* GetExceptionInfo(StringStorage& messageStorage, _Out_opt_ HRESULT* pHResult = nullptr,
bool includeHResultInMessage = true)
{
try
{
throw;
}
# if !defined(NO_VERIFY_EXCEPTIONS)
catch (const ::WEX::TestExecution::VerifyFailureException&)
{
if (pHResult)
{
*pHResult = TAEF_E_FAIL;
}
return u8"Caught a VerifyFailureException";
}
# endif // #if !defined(NO_VERIFY_EXCEPTIONS)
# if defined(_WIN32)
catch (const WEX::Common::Exception& ex)
{
if (pHResult)
{
*pHResult = ex.ErrorCode();
}
const wchar_t* const pszExceptionMessage = reinterpret_cast(ex.Message());
return GetExceptionMessageForExceptionWithHResultAndWideMessage(messageStorage, u8"Caught WEX::Common::Exception",
pszExceptionMessage, ::wcslen(pszExceptionMessage), ex.ErrorCode(), includeHResultInMessage);
}
# endif // #if defined(_WIN32)
# if defined(_WIN32) && defined(__cplusplus_winrt)
catch (Platform::Exception^ ex)
{
if (pHResult)
{
*pHResult = ex->HResult;
}
Platform::String^ exceptionMessage = ex->Message;
// The cast is needed for code compiling with /Zc:wchar_t-. In that case, Platform::String::Data returns "const __wchar_t*".
return GetExceptionMessageForExceptionWithHResultAndWideMessage(messageStorage, u8"Caught Platform::Exception^",
reinterpret_cast(exceptionMessage->Data()), exceptionMessage->Length(), ex->HResult, includeHResultInMessage);
}
# endif // if defined(_WIN32) && defined(__cplusplus_winrt)
# if defined(_WIN32) && defined(TAEF_CPPWINRT_SUPPORT)
catch (const winrt::hresult_error& ex)
{
if (pHResult)
{
*pHResult = ex.code();
}
const winrt::hstring exceptionMessage = ex.message();
return GetExceptionMessageForExceptionWithHResultAndWideMessage(messageStorage, u8"Caught winrt::hresult_error",
exceptionMessage.data(), exceptionMessage.size(), ex.code(), includeHResultInMessage);
}
# endif // if defined(_WIN32) && defined(TAEF_CPPWINRT_SUPPORT)
# if defined(TAEF_SYSTEM_ERROR_SUPPORT)
catch (const std::system_error& ex)
{
bool isExactConvesion;
const HRESULT errorCode = GetHResultForStdErrorCode(ex.code(), isExactConvesion);
if (pHResult)
{
*pHResult = errorCode;
}
const char* const pszExceptionMessage = ex.what();
size_t exceptionMessageLength = ::strlen(pszExceptionMessage);
// std::system_error often ends its messages with a new-line on Windows. Exclude that from the length if it is present.
while (exceptionMessageLength > 0 && ::isspace(static_cast(pszExceptionMessage[exceptionMessageLength - 1])))
{
--exceptionMessageLength;
}
if (exceptionMessageLength > INT_MAX)
{
exceptionMessageLength = INT_MAX;
}
// This returns a message with one of these formats:
// "Caught std::system_error: {pszExceptionMessage} [HRESULT: 0x{errorCode}]" (if isExactConvesion and includeHResultInMessage are true)
// "Caught std::system_error: {pszExceptionMessage}" (if isExactConvesion is true and includeHResultInMessage is false)
// "Caught std::system_error: {pszExceptionMessage} [Error code: {ex.code().value()}]" (if isExactConvesion is false)
const Utf8StringView beginningOfMessage{u8"Caught std::system_error: "};
// beginningOfMessage + pszExceptionMessage + exceptionHResult
size_t totalSize = beginningOfMessage.Size() + exceptionMessageLength;
if (!isExactConvesion)
{
totalSize += StringWriter::c_maxStringSizeForUnrecognizedErrorCode;
}
else if (includeHResultInMessage)
{
totalSize += StringWriter::c_maxStringSizeForHResult;
}
if (messageStorage.TryAllocate(totalSize))
{
StringWriter messageWriter{messageStorage.Data(), messageStorage.AllocationSize()};
messageWriter.Append(beginningOfMessage);
// Currently we assume std::system_error messages are UTF-8 encoded.
messageWriter.Append(pszExceptionMessage, exceptionMessageLength);
if (!isExactConvesion)
{
messageWriter.AppendUnrecognizedErrorCode(ex.code().value());
}
else if (includeHResultInMessage)
{
messageWriter.AppendHResult(errorCode);
}
messageWriter.WriteNullTerminator();
return messageStorage.DataAsChar8T();
}
return u8"Caught std::system_error";
}
# endif // if defined(TAEF_SYSTEM_ERROR_SUPPORT)
catch (const std::exception& ex)
{
if (pHResult)
{
*pHResult = TAEF_E_UNEXPECTED;
}
// This returns a message with this format:
// "Caught std::exception: {ex.what()}"
const Utf8StringView beginningOfMessage{u8"Caught std::exception: "};
const char* const pszExceptionMessage = ex.what();
const size_t exceptionMessageSize = ::strlen(pszExceptionMessage);
// beginningOfMessage + pszExceptionMessage
const size_t totalSize = beginningOfMessage.Size() + exceptionMessageSize;
if (messageStorage.TryAllocate(totalSize))
{
StringWriter messageWriter{messageStorage.Data(), messageStorage.AllocationSize()};
messageWriter.Append(beginningOfMessage);
// Currently we assume std::exception messages are UTF-8 encoded.
messageWriter.Append(pszExceptionMessage, exceptionMessageSize);
messageWriter.WriteNullTerminator();
return messageStorage.DataAsChar8T();
}
return u8"Caught std::exception";
}
catch (...)
{
if (pHResult)
{
*pHResult = TAEF_E_UNEXPECTED;
}
return u8"Caught an unidentified C++ exception";
}
}
#endif // #if defined(__cpp_exceptions)
#if !defined(TAEF_USE_STD_FORMAT)
# if defined _MSC_VER
# pragma warning(push)
// Disable warnings related to implicitly deleted special member functions.
# pragma warning(disable: 4623 4625 4626)
# endif
// This would be in the private section of the MacroVerify class, but it needs to be outside the class at namespace scope to avoid
// build breaks with GCC due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85282.
template
struct ValueOutputHandler;
template <>
struct ValueOutputHandler
{
// On Windows, TAEF treats "char" strings as using the active code page; so we need to convert to UTF-8 for logging.
_Success_(return)
bool TryGetUtf8ByteCountForValue(_Out_ size_t& utf8ByteCount) const noexcept
{
return TAEF_TryGetUtf8ByteCountForCharStringValue(m_pszString, (m_pszString)?::strlen(m_pszString):0, utf8ByteCount);
}
_Success_(return)
bool TryWriteUtf8ForValue(char* pOutputBuffer, size_t utf8ByteCount, _Out_ size_t& bytesWritten) const noexcept
{
return TAEF_TryWriteUtf8ForCharStringValue(m_pszString, (m_pszString)?::strlen(m_pszString):0, pOutputBuffer, utf8ByteCount, bytesWritten);
}
const char* const m_pszString;
};
template <>
struct ValueOutputHandler : public ValueOutputHandler
{
ValueOutputHandler(char* pszString) noexcept
: ValueOutputHandler{pszString}
{}
};
# if defined(__cpp_char8_t)
template <>
struct ValueOutputHandler
{
_Success_(return)
bool TryGetUtf8ByteCountForValue(_Out_ size_t& utf8ByteCount) const noexcept
{
utf8ByteCount = (m_pszString)?::strlen(reinterpret_cast(m_pszString)):0;
return true;
}
_Success_(return)
bool TryWriteUtf8ForValue(char* pOutputBuffer, size_t utf8ByteCount, _Out_ size_t& bytesWritten) const noexcept
{
Memcpy(pOutputBuffer, utf8ByteCount, m_pszString, utf8ByteCount);
bytesWritten = utf8ByteCount;
return true;
}
const char8_t* const m_pszString;
};
template <>
struct ValueOutputHandler : public ValueOutputHandler {};
# endif // #if defined(__cpp_char8_t)
# if defined(_WIN32)
template <>
struct ValueOutputHandler
{
_Success_(return)
bool TryGetUtf8ByteCountForValue(_Out_ size_t& utf8ByteCount) const noexcept
{
return TAEF_TryGetUtf8ByteCountForWCharStringValue(m_pszString, (m_pszString)?::wcslen(m_pszString):0, utf8ByteCount);
}
_Success_(return)
bool TryWriteUtf8ForValue(char* pOutputBuffer, size_t utf8ByteCount, _Out_ size_t& bytesWritten) const noexcept
{
return TAEF_TryWriteUtf8ForWCharStringValue(m_pszString, (m_pszString)?::wcslen(m_pszString):0, pOutputBuffer, utf8ByteCount, bytesWritten);
}
const wchar_t* const m_pszString;
};
template <>
struct ValueOutputHandler : public ValueOutputHandler
{
ValueOutputHandler(wchar_t* pszString) noexcept
: ValueOutputHandler{pszString}
{}
};
template <>
struct ValueOutputHandler<::WEX::Common::String>
{
_Success_(return)
bool TryGetUtf8ByteCountForValue(_Out_ size_t& utf8ByteCount) const noexcept
{
return TAEF_TryGetUtf8ByteCountForWCharStringValue(m_string, static_cast(m_string.GetLength()), utf8ByteCount);
}
_Success_(return)
bool TryWriteUtf8ForValue(char* pOutputBuffer, size_t utf8ByteCount, _Out_ size_t& bytesWritten) const noexcept
{
return TAEF_TryWriteUtf8ForWCharStringValue(m_string, static_cast(m_string.GetLength()),
pOutputBuffer, utf8ByteCount, bytesWritten);
}
const ::WEX::Common::String& m_string;
};
template <>
struct ValueOutputHandler<::WEX::Common::NoThrowString>
{
_Success_(return)
bool TryGetUtf8ByteCountForValue(_Out_ size_t& utf8ByteCount) const noexcept
{
return TAEF_TryGetUtf8ByteCountForWCharStringValue(m_string, static_cast(m_string.GetLength()), utf8ByteCount);
}
_Success_(return)
bool TryWriteUtf8ForValue(char* pOutputBuffer, size_t utf8ByteCount, _Out_ size_t& bytesWritten) const noexcept
{
return TAEF_TryWriteUtf8ForWCharStringValue(m_string, static_cast(m_string.GetLength()),
pOutputBuffer, utf8ByteCount, bytesWritten);
}
const ::WEX::Common::NoThrowString& m_string;
};
# endif // #if defined(_WIN32)
# if defined(TAEF_STL_SUPPORT)
# if defined(__cpp_lib_string_view)
template <>
struct ValueOutputHandler
{
// On Windows, TAEF treats "char" strings as using the active code page; so we need to convert to UTF-8 for logging.
_Success_(return)
bool TryGetUtf8ByteCountForValue(_Out_ size_t& utf8ByteCount) const noexcept
{
return TAEF_TryGetUtf8ByteCountForCharStringValue(m_stringView.data(), m_stringView.length(), utf8ByteCount);
}
_Success_(return)
bool TryWriteUtf8ForValue(char* pOutputBuffer, size_t utf8ByteCount, _Out_ size_t& bytesWritten) const noexcept
{
return TAEF_TryWriteUtf8ForCharStringValue(m_stringView.data(), m_stringView.length(), pOutputBuffer, utf8ByteCount, bytesWritten);
}
const std::string_view m_stringView;
};
# endif // #if defined(__cpp_lib_string_view)
template <>
struct ValueOutputHandler
{
// On Windows, TAEF treats "char" strings as using the active code page; so we need to convert to UTF-8 for logging.
_Success_(return)
bool TryGetUtf8ByteCountForValue(_Out_ size_t& utf8ByteCount) const noexcept
{
return TAEF_TryGetUtf8ByteCountForCharStringValue(m_string.c_str(), m_string.length(), utf8ByteCount);
}
_Success_(return)
bool TryWriteUtf8ForValue(char* pOutputBuffer, size_t utf8ByteCount, _Out_ size_t& bytesWritten) const noexcept
{
return TAEF_TryWriteUtf8ForCharStringValue(m_string.c_str(), m_string.length(), pOutputBuffer, utf8ByteCount, bytesWritten);
}
const std::string& m_string;
};
# if defined(__cpp_lib_char8_t)
template <>
struct ValueOutputHandler
{
_Success_(return)
bool TryGetUtf8ByteCountForValue(_Out_ size_t& utf8ByteCount) const noexcept
{
utf8ByteCount = m_stringView.length();
return true;
}
_Success_(return)
bool TryWriteUtf8ForValue(char* pOutputBuffer, size_t utf8ByteCount, _Out_ size_t& bytesWritten) const noexcept
{
Memcpy(pOutputBuffer, utf8ByteCount, m_stringView.data(), m_stringView.length());
bytesWritten = m_stringView.length();
return true;
}
const std::u8string_view m_stringView;
};
template <>
struct ValueOutputHandler : public ValueOutputHandler {};
# endif // #if defined(__cpp_lib_char8_t)
# if defined(_WIN32)
# if defined(__cpp_lib_string_view)
template <>
struct ValueOutputHandler
{
_Success_(return)
bool TryGetUtf8ByteCountForValue(_Out_ size_t& utf8ByteCount) const noexcept
{
return TAEF_TryGetUtf8ByteCountForWCharStringValue(m_stringView.data(), m_stringView.length(), utf8ByteCount);
}
_Success_(return)
bool TryWriteUtf8ForValue(char* pOutputBuffer, size_t utf8ByteCount, _Out_ size_t& bytesWritten) const noexcept
{
return TAEF_TryWriteUtf8ForWCharStringValue(m_stringView.data(), m_stringView.length(), pOutputBuffer, utf8ByteCount, bytesWritten);
}
const std::wstring_view m_stringView;
};
# endif // #if defined(__cpp_lib_string_view)
template <>
struct ValueOutputHandler
{
_Success_(return)
bool TryGetUtf8ByteCountForValue(_Out_ size_t& utf8ByteCount) const noexcept
{
return TAEF_TryGetUtf8ByteCountForWCharStringValue(m_string.c_str(), m_string.length(), utf8ByteCount);
}
_Success_(return)
bool TryWriteUtf8ForValue(char* pOutputBuffer, size_t utf8ByteCount, _Out_ size_t& bytesWritten) const noexcept
{
return TAEF_TryWriteUtf8ForWCharStringValue(m_string.c_str(), m_string.length(), pOutputBuffer, utf8ByteCount, bytesWritten);
}
const std::wstring& m_string;
};
# endif // #if defined(_WIN32)
# endif // #if defined(TAEF_STL_SUPPORT)
// Default implementation:
# if defined(_WIN32)
template
struct ValueOutputHandler
{
ValueOutputHandler(const T& value) noexcept
: m_formattedValue{VerifyOutputTraits::ToString(value)}
{}
_Success_(return)
bool TryGetUtf8ByteCountForValue(_Out_ size_t& utf8ByteCount) const noexcept
{
return TAEF_TryGetUtf8ByteCountForWCharStringValue(m_formattedValue, static_cast