andry81/tacklelib

View on GitHub
src/utility/assert.hpp

Summary

Maintainability
Test Coverage
#pragma once

#ifdef UTILITY_ASSERT_PUBLIC_HPP
#error You must not include private header after public!
#endif

// DO NOT REMOVE, exists to avoid private/public headers mixing!
#ifndef UTILITY_ASSERT_HPP
#define UTILITY_ASSERT_HPP

#include <src/tacklelib_private.hpp>

#include <tacklelib/utility/preprocessor.hpp>
#include <tacklelib/utility/platform.hpp>
#include <tacklelib/utility/type_identity.hpp>
#include <tacklelib/utility/debug.hpp>

#if !defined(GTEST_INCLUDE_FROM_EXTERNAL)

#if defined(GTEST_FAIL)
#error <utility/assert.hpp> header must be included instead of the <gtest.h> header
#endif

#if defined(TACKLE_TESTLIB) || defined(UNIT_TESTS) || defined(BENCH_TESTS)
#define GTEST_DONT_DEFINE_ASSERT_TRUE 1
#define GTEST_DONT_DEFINE_ASSERT_FALSE 1
#define GTEST_DONT_DEFINE_ASSERT_EQ 1
#define GTEST_DONT_DEFINE_ASSERT_NE 1
#define GTEST_DONT_DEFINE_ASSERT_LE 1
#define GTEST_DONT_DEFINE_ASSERT_LT 1
#define GTEST_DONT_DEFINE_ASSERT_GE 1
#define GTEST_DONT_DEFINE_ASSERT_GT 1

#include <gtest/gtest.h>

#include <src/testlib/gtest_ext.hpp>

// back compatability
#undef ASSERT_TRUE
#undef ASSERT_FALSE
#undef ASSERT_EQ
#undef ASSERT_NE
#undef ASSERT_LE
#undef ASSERT_LT
#undef ASSERT_GE
#undef ASSERT_GT
#endif

#endif

// enable assertion in the Release
#ifndef NDEBUG
#include <cassert>
#else
#undef NDEBUG
#include <cassert>
#define NDEBUG
#endif


// heap corruption provoke simple check
#if ERROR_IF_EMPTY_PP_DEF(USE_MEMORY_REALLOCATION_IN_VERIFY_ASSERT)
#define UTILITY_DBG_HEAP_CHECK() delete [] (new char [1])
#else
#define UTILITY_DBG_HEAP_CHECK() (void)0
#endif


// FPU precision control check
#if ERROR_IF_EMPTY_PP_DEF(USE_FPU_PRECISION_CHECK_IN_VERIFY_ASSERT)
#define UTILITY_FPU_PRECISION_CHECK() if ((_controlfp(0, 0) & _MCW_PC) != (USE_FPU_PRECISION_CHECK_IN_VERIFY_ASSERT_VALUE)) \
    { \
        ASSERT_FAIL("UTILITY_FPU_PRECISION_CHECK()", L"UTILITY_FPU_PRECISION_CHECK()", UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG); \
    } (void)0
#else
#define UTILITY_FPU_PRECISION_CHECK() (void)0
#endif

// verify/assert post test macro
#define UTILITY_ASSERT_POST_TEST() \
    UTILITY_DBG_HEAP_CHECK(); \
    UTILITY_FPU_PRECISION_CHECK()


#if defined(UTILITY_PLATFORM_WINDOWS)
#   define ASSERT_FAIL(msg, msg_w, file, file_w, line, funcsig) _wassert(msg_w, file_w, line)
#   define ASSERT_FAIL_WIDE(msg, file, line, funcsig) _wassert(msg, file, line)
#elif defined(UTILITY_PLATFORM_POSIX)
#   if !defined(UTILITY_PLATFORM_MINGW)
#       define ASSERT_FAIL(msg, msg_w, file, file_w, line, funcsig) __assert_fail(msg, file, line, funcsig)
#       define ASSERT_FAIL_ANSI(msg, file, line, funcsig) __assert_fail(msg, file, line, funcsig)
#   else
#       define ASSERT_FAIL(msg, msg_w, file, file_w, line, funcsig) __assert_func(file, line, funcsig, msg)
#       define ASSERT_FAIL_ANSI(msg, file, line, funcsig) __assert_func(file, line, funcsig, msg)
#   endif
#else
#   error platform is not implemented
#endif


// CAUTION:
//  Below `gtest_fail_*` functions avoides dramatic slow down of compilation or linkage (in case of /LTCG) times in the Full Optimization Release in the MSVC2015 Update 3.
//  Direct inclusion of respective gtest macroses (`GTEST_TEST_BOOLEAN_` and `GTEST_ASSERT_`) through the macro call or through the `__forceinline`-ed function call in the Full Optimization Release is EXTREMELY NOT RECOMMENDED!

// TIPS:
//  * if debugger is attached but `::testing::GTEST_FLAG(break_on_failure)` has not been setted, then an assertion does explicit break.

#if defined(UNIT_TESTS) || defined(BENCH_TESTS)

#define UTILITY_ASSERT_GTEST_MESSAGE_(message, result_type) \
    GTEST_MESSAGE_AT_(file, line, message, result_type) // `file` and `line` must be external respective variables

#define UTILITY_ASSERT_GTEST_FATAL_FAILURE_(message) \
    UTILITY_ASSERT_GTEST_MESSAGE_(message, ::testing::TestPartResult::kFatalFailure)

#define UTILITY_ASSERT_GTEST_NONFATAL_FAILURE_(message) \
    UTILITY_ASSERT_GTEST_MESSAGE_(message, ::testing::TestPartResult::kNonFatalFailure)


// uses inside a function inlinement, has significant differences, DO NOT MERGE!
#define UTILITY_GTEST_FAIL_TRUE_FUNC_INLINE(exp_str, file, line) \
    ::utility::gtest_fail_true(exp_str, file, line);

#define UTILITY_GTEST_FAIL_FALSE_FUNC_INLINE(exp_str, file, line) \
    ::utility::gtest_fail_false(exp_str, file, line);

#define UTILITY_GTEST_FAIL_EXP_FUNC_INLINE(exp_value, file, line) \
    ::utility::gtest_fail_exp(exp_value, file, line);


// uses inside a macro inlinement, has significant differences, DO NOT MERGE!
#define UTILITY_GTEST_FAIL_TRUE_MACRO_INLINE(exp_str, file, line) \
    ::utility::gtest_fail_true(exp_str, file, line);

#define UTILITY_GTEST_FAIL_FALSE_MACRO_INLINE(exp_str, file, line) \
    ::utility::gtest_fail_false(exp_str, file, line);

#define UTILITY_GTEST_FAIL_EXP_MACRO_INLINE(exp_value, file, line) \
    ::utility::gtest_fail_exp(exp_value, file, line);


#if !ERROR_IF_EMPTY_PP_DEF(DONT_USE_UNIT_ASSERT_CALL_THROUGH_MACRO_INLINE)

#define UNIT_VERIFY_TRUE(exp) (( ::utility::UniAssertTrue{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.gtest_verify(exp, UTILITY_PP_STRINGIZE(exp)) ))
#define UNIT_ASSERT_TRUE(exp) \
    if ((exp) ? true : false); else do {{ \
        UTILITY_GTEST_FAIL_TRUE_MACRO_INLINE(UTILITY_PP_STRINGIZE(exp), UTILITY_PP_FILE, UTILITY_PP_LINE); \
    }} while(false); \
    UTILITY_ASSERT_POST_TEST()

#define UNIT_VERIFY_FALSE(exp) (( ::utility::UniAssertFalse{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.gtest_verify(exp, UTILITY_PP_STRINGIZE(!(exp))) ))
#define UNIT_ASSERT_FALSE(exp) \
    if ((exp) ? false : true); else do {{ \
        UTILITY_GTEST_FAIL_FALSE_MACRO_INLINE(UTILITY_PP_STRINGIZE(exp), UTILITY_PP_FILE, UTILITY_PP_LINE); \
    }} while(false); \
    UTILITY_ASSERT_POST_TEST()

#define UNIT_VERIFY_EQ(v1, v2) (( ::utility::UniAssertEQ{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.gtest_verify(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2)) ))
#define UNIT_ASSERT_EQ(v1, v2) \
    do {{ \
        const auto & exp_var_1 = (v1); \
        if (const ::testing::AssertionResult exp_value = ::testing::internal::EqHelper IF_GTEST_VERSION_LESS_1_10_0(<GTEST_IS_NULL_LITERAL_(exp_var_1)>) ::Compare(UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2), exp_var_1, v2)); \
        else UTILITY_GTEST_FAIL_EXP_MACRO_INLINE(exp_value, UTILITY_PP_FILE, UTILITY_PP_LINE); \
        UTILITY_ASSERT_POST_TEST(); \
    }} while(false)

#define UNIT_VERIFY_NE(v1, v2) (( ::utility::UniAssertNE{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.gtest_verify(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2)) ))
#define UNIT_ASSERT_NE(v1, v2) \
    do {{ \
        const auto & exp_var_1 = (v1); \
        if (const ::testing::AssertionResult exp_value = ::testing::internal::CmpHelperNE(UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2), exp_var_1, v2)); \
        else UTILITY_GTEST_FAIL_EXP_MACRO_INLINE(exp_value, UTILITY_PP_FILE, UTILITY_PP_LINE); \
        UTILITY_ASSERT_POST_TEST(); \
    }} while(false)

#define UNIT_VERIFY_LE(v1, v2) (( ::utility::UniAssertLE{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.gtest_verify(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2)) ))
#define UNIT_ASSERT_LE(v1, v2) \
    do {{ \
        const auto & exp_var_1 = (v1); \
        if (const ::testing::AssertionResult exp_value = ::testing::internal::CmpHelperLE(UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2), exp_var_1, v2)); \
        else UTILITY_GTEST_FAIL_EXP_MACRO_INLINE(exp_value, UTILITY_PP_FILE, UTILITY_PP_LINE); \
        UTILITY_ASSERT_POST_TEST(); \
    }} while(false)

#define UNIT_VERIFY_LT(v1, v2) (( ::utility::UniAssertLT{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.gtest_verify(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2)) ))
#define UNIT_ASSERT_LT(v1, v2) \
    do {{ \
        const auto & exp_var_1 = (v1); \
        if (const ::testing::AssertionResult exp_value = ::testing::internal::CmpHelperLT(UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2), exp_var_1, v2)); \
        else UTILITY_GTEST_FAIL_EXP_MACRO_INLINE(exp_value, UTILITY_PP_FILE, UTILITY_PP_LINE); \
        UTILITY_ASSERT_POST_TEST(); \
    }} while(false)

#define UNIT_VERIFY_GE(v1, v2) (( ::utility::UniAssertGE{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.gtest_verify(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2)) ))
#define UNIT_ASSERT_GE(v1, v2) \
    do {{ \
        const auto & exp_var_1 = (v1); \
        if (const ::testing::AssertionResult exp_value = ::testing::internal::CmpHelperGE(UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2), exp_var_1, v2)); \
        else UTILITY_GTEST_FAIL_EXP_MACRO_INLINE(exp_value, UTILITY_PP_FILE, UTILITY_PP_LINE); \
        UTILITY_ASSERT_POST_TEST(); \
    }} while(false)

#define UNIT_VERIFY_GT(v1, v2) (( ::utility::UniAssertGT{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.gtest_verify(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2)) ))
#define UNIT_ASSERT_GT(v1, v2) \
    do {{ \
        const auto & exp_var_1 = (v1); \
        if (const ::testing::AssertionResult exp_value = ::testing::internal::CmpHelperGT(UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2), exp_var_1, v2)); \
        else UTILITY_GTEST_FAIL_EXP_MACRO_INLINE(exp_value, UTILITY_PP_FILE, UTILITY_PP_LINE); \
        UTILITY_ASSERT_POST_TEST(); \
    }} while(false)

#else

#if ERROR_IF_EMPTY_PP_DEF(USE_UNIT_ASSERT_CALL_THROUGH_TEMPLATE_FUNCTION_INSTEAD_LAMBDAS)

#define UNIT_VERIFY_TRUE(exp) (( ::utility::UniAssertTrue{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.gtest_verify(exp, UTILITY_PP_STRINGIZE(exp)) ))
#if ERROR_IF_EMPTY_PP_DEF(DONT_USE_UNIT_ASSERT_CALL_THROUGH_MACRO_INLINE)
    #define UNIT_ASSERT_TRUE(exp) do {{ ::utility::UniAssertTrue{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.gtest_verify(exp, UTILITY_PP_STRINGIZE(exp)); UTILITY_ASSERT_POST_TEST(); }} while(false)
#endif

////

#define UNIT_VERIFY_FALSE(exp) (( ::utility::UniAssertFalse{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.gtest_verify(exp, UTILITY_PP_STRINGIZE(!(exp))) ))
#if ERROR_IF_EMPTY_PP_DEF(DONT_USE_UNIT_ASSERT_CALL_THROUGH_MACRO_INLINE)
    #define UNIT_ASSERT_FALSE(exp) do {{ ::utility::UniAssertFalse{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.gtest_verify(exp, UTILITY_PP_STRINGIZE(!(exp))); UTILITY_ASSERT_POST_TEST(); }} while(false)
#endif

////

#define UNIT_VERIFY_EQ(v1, v2) (( ::utility::UniAssertEQ{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.gtest_verify(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2)) ))
#if ERROR_IF_EMPTY_PP_DEF(DONT_USE_UNIT_ASSERT_CALL_THROUGH_MACRO_INLINE)
    #define UNIT_ASSERT_EQ(v1, v2) /*do {{*/ ::utility::UniAssertEQ{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.gtest_verify(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2)); UTILITY_ASSERT_POST_TEST()//; }} while(false)
#endif

////

#define UNIT_VERIFY_NE(v1, v2) (( ::utility::UniAssertNE{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.gtest_verify(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2)) ))
#if ERROR_IF_EMPTY_PP_DEF(DONT_USE_UNIT_ASSERT_CALL_THROUGH_MACRO_INLINE)
    #define UNIT_ASSERT_NE(v1, v2) do {{ ::utility::UniAssertNE{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.gtest_verify(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2)); UTILITY_ASSERT_POST_TEST(); }} while(false)
#endif

////

#define UNIT_VERIFY_LE(v1, v2) (( ::utility::UniAssertLE{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.gtest_verify(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2)) ))
#if ERROR_IF_EMPTY_PP_DEF(DONT_USE_UNIT_ASSERT_CALL_THROUGH_MACRO_INLINE)
    #define UNIT_ASSERT_LE(v1, v2) do {{ ::utility::UniAssertLE{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.gtest_verify(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2)); UTILITY_ASSERT_POST_TEST(); }} while(false)
#endif

////

#define UNIT_VERIFY_LT(v1, v2) (( ::utility::UniAssertLT{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.gtest_verify(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2)) ))
#if ERROR_IF_EMPTY_PP_DEF(DONT_USE_UNIT_ASSERT_CALL_THROUGH_MACRO_INLINE)
    #define UNIT_ASSERT_LT(v1, v2) do {{ ::utility::UniAssertLT{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.gtest_verify(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2)); UTILITY_ASSERT_POST_TEST(); }} while(false)
#endif

////

#define UNIT_VERIFY_GE(v1, v2) (( ::utility::UniAssertGE{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.gtest_verify(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2)) ))
#if ERROR_IF_EMPTY_PP_DEF(DONT_USE_UNIT_ASSERT_CALL_THROUGH_MACRO_INLINE)
    #define UNIT_ASSERT_GE(v1, v2) do {{ ::utility::UniAssertGE{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.gtest_verify(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2)); UTILITY_ASSERT_POST_TEST(); }} while(false)
#endif

////

#define UNIT_VERIFY_GT(v1, v2) (( ::utility::UniAssertGT{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.gtest_verify(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2)) ))
#if ERROR_IF_EMPTY_PP_DEF(DONT_USE_UNIT_ASSERT_CALL_THROUGH_MACRO_INLINE)
    #define UNIT_ASSERT_GT(v1, v2) do {{ ::utility::UniAssertGT{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.gtest_verify(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2)); UTILITY_ASSERT_POST_TEST(); }} while(false)
#endif

#else

#ifdef _MSC_VER
    #if _MSC_VER < 1600 // < MSVC++ 10 (Visual Studio 2010)
        #error lambda is not supported
    #endif
#else
    #if __cplusplus < 201103L
        #error lambda is not supported
    #endif
#endif

#define UNIT_VERIFY_TRUE_IMPL(exp) [&](const auto & exp_var, const char * exp_str, const char * file, unsigned int line) -> const auto & { \
        if (exp_var ? true : false); \
        else UTILITY_GTEST_FAIL_TRUE_FUNC_INLINE(exp_str, file, line); \
        UTILITY_ASSERT_POST_TEST(); \
        return exp_var; \
    }

#define UNIT_VERIFY_TRUE(exp) (( UNIT_VERIFY_TRUE_IMPL(exp)(exp, UTILITY_PP_STRINGIZE(exp), UTILITY_PP_FILE, UTILITY_PP_LINE) ))
#if ERROR_IF_EMPTY_PP_DEF(DONT_USE_UNIT_ASSERT_CALL_THROUGH_MACRO_INLINE)
    #define UNIT_ASSERT_TRUE(exp) do {{ UNIT_VERIFY_TRUE_IMPL(exp)(exp, UTILITY_PP_STRINGIZE(exp), UTILITY_PP_FILE, UTILITY_PP_LINE); }} while(false)
#endif

#define UNIT_VERIFY_FALSE_IMPL(exp) [&](const auto & exp_var, const char * exp_str, const char * file, unsigned int line) -> const auto & { \
        if (exp_var ? false : true); \
        else UTILITY_GTEST_FAIL_FALSE_FUNC_INLINE(exp_str, file, line); \
        UTILITY_ASSERT_POST_TEST(); \
        return exp_var; \
    }

#define UNIT_VERIFY_FALSE(exp) (( UNIT_VERIFY_FALSE_IMPL(exp)(exp, UTILITY_PP_STRINGIZE(exp), UTILITY_PP_FILE, UTILITY_PP_LINE) ))
#if ERROR_IF_EMPTY_PP_DEF(DONT_USE_UNIT_ASSERT_CALL_THROUGH_MACRO_INLINE)
    #define UNIT_ASSERT_FALSE(exp) do {{ UNIT_VERIFY_FALSE_IMPL(exp)(exp, UTILITY_PP_STRINGIZE(exp), UTILITY_PP_FILE, UTILITY_PP_LINE); }} while(false)
#endif

#define UNIT_VERIFY_EQ_IMPL(v1, v2) [&](const auto & v_1, const auto & v_2, const char * v1_str, const char * v2_str, const char * file, unsigned int line) -> const auto & { \
        if (const ::testing::AssertionResult exp_value = ::testing::internal:: IF_GTEST_VERSION_LESS_1_10_0(EqHelper<GTEST_IS_NULL_LITERAL_(v_1)>) ::Compare(v1_str, v2_str, v_1, v_2)); \
        else UTILITY_GTEST_FAIL_EXP_FUNC_INLINE(exp_value, file, line); \
        UTILITY_ASSERT_POST_TEST(); \
        return v_1; \
    }

#define UNIT_VERIFY_EQ(v1, v2) (( UNIT_VERIFY_EQ_IMPL(v1, v2)(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2), UTILITY_PP_FILE, UTILITY_PP_LINE) ))
#if ERROR_IF_EMPTY_PP_DEF(DONT_USE_UNIT_ASSERT_CALL_THROUGH_MACRO_INLINE)
    #define UNIT_ASSERT_EQ(v1, v2) do {{ UNIT_VERIFY_EQ_IMPL(v1, v2)(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2), UTILITY_PP_FILE, UTILITY_PP_LINE); }} while(false)
#endif

////

#define UNIT_VERIFY_NE_IMPL(v1, v2) [&](const auto & v_1, const auto & v_2, const char * v1_str, const char * v2_str, const char * file, unsigned int line) -> const auto & { \
        if (const ::testing::AssertionResult exp_value = ::testing::internal::CmpHelperNE(v1_str, v2_str, v_1, v_2)); \
        else UTILITY_GTEST_FAIL_EXP_FUNC_INLINE(exp_value, file, line); \
        UTILITY_ASSERT_POST_TEST(); \
        return v_1; \
    }

#define UNIT_VERIFY_NE(v1, v2) (( UNIT_VERIFY_NE_IMPL(v1, v2)(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2), UTILITY_PP_FILE, UTILITY_PP_LINE) ))
#if ERROR_IF_EMPTY_PP_DEF(DONT_USE_UNIT_ASSERT_CALL_THROUGH_MACRO_INLINE)
    #define UNIT_ASSERT_NE(v1, v2) do {{ UNIT_VERIFY_NE_IMPL(v1, v2)(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2), UTILITY_PP_FILE, UTILITY_PP_LINE); }} while(false)
#endif

////

#define UNIT_VERIFY_LE_IMPL(v1, v2) [&](const auto & v_1, const auto & v_2, const char * v1_str, const char * v2_str, const char * file, unsigned int line) -> const auto & { \
        if (const ::testing::AssertionResult exp_value = ::testing::internal::CmpHelperLE(v1_str, v2_str, v_1, v_2)); \
        else UTILITY_GTEST_FAIL_EXP_FUNC_INLINE(exp_value, file, line); \
        UTILITY_ASSERT_POST_TEST(); \
        return v_1; \
    }

#define UNIT_VERIFY_LE(v1, v2) (( UNIT_VERIFY_LE_IMPL(v1, v2)(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2), UTILITY_PP_FILE, UTILITY_PP_LINE) ))
#if ERROR_IF_EMPTY_PP_DEF(DONT_USE_UNIT_ASSERT_CALL_THROUGH_MACRO_INLINE)
    #define UNIT_ASSERT_LE(v1, v2) do {{ UNIT_VERIFY_LE_IMPL(v1, v2)(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2), UTILITY_PP_FILE, UTILITY_PP_LINE); }} while(false)
#endif

////

#define UNIT_VERIFY_LT_IMPL(v1, v2) [&](const auto & v_1, const auto & v_2, const char * v1_str, const char * v2_str, const char * file, unsigned int line) -> const auto & { \
        if (const ::testing::AssertionResult exp_value = ::testing::internal::CmpHelperLT(v1_str, v2_str, v_1, v_2)); \
        else UTILITY_GTEST_FAIL_EXP_FUNC_INLINE(exp_value, file, line); \
        UTILITY_ASSERT_POST_TEST(); \
        return v_1; \
    }

#define UNIT_VERIFY_LT(v1, v2) (( UNIT_VERIFY_LT_IMPL(v1, v2)(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2), UTILITY_PP_FILE, UTILITY_PP_LINE) ))
#if ERROR_IF_EMPTY_PP_DEF(DONT_USE_UNIT_ASSERT_CALL_THROUGH_MACRO_INLINE)
    #define UNIT_ASSERT_LT(v1, v2) do {{ UNIT_VERIFY_LT_IMPL(v1, v2)(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2), UTILITY_PP_FILE, UTILITY_PP_LINE); }} while(false)
#endif

////

#define UNIT_VERIFY_GE_IMPL(v1, v2) [&](const auto & v_1, const auto & v_2, const char * v1_str, const char * v2_str, const char * file, unsigned int line) -> const auto & { \
        if (const ::testing::AssertionResult exp_value = ::testing::internal::CmpHelperGE(v1_str, v2_str, v_1, v_2)); \
        else UTILITY_GTEST_FAIL_EXP_FUNC_INLINE(exp_value, file, line); \
        UTILITY_ASSERT_POST_TEST(); \
        return v_1; \
    }

#define UNIT_VERIFY_GE(v1, v2) (( UNIT_VERIFY_GE_IMPL(v1, v2)(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2), UTILITY_PP_FILE, UTILITY_PP_LINE) ))
#if ERROR_IF_EMPTY_PP_DEF(DONT_USE_UNIT_ASSERT_CALL_THROUGH_MACRO_INLINE)
    #define UNIT_ASSERT_GE(v1, v2) do {{ UNIT_VERIFY_GE_IMPL(v1, v2)(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2), UTILITY_PP_FILE, UTILITY_PP_LINE); }} while(false)
#endif

////

#define UNIT_VERIFY_GT_IMPL(v1, v2) [&](const auto & v_1, const auto & v_2, const char * v1_str, const char * v2_str, const char * file, unsigned int line) -> const auto & { \
        if (const ::testing::AssertionResult exp_value = ::testing::internal::CmpHelperGT(v1_str, v2_str, v_1, v_2)); \
        else UTILITY_GTEST_FAIL_EXP_FUNC_INLINE(exp_value, file, line); \
        UTILITY_ASSERT_POST_TEST(); \
        return v_1; \
    }

#define UNIT_VERIFY_GT(v1, v2) (( UNIT_VERIFY_GT_IMPL(v1, v2)(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2), UTILITY_PP_FILE, UTILITY_PP_LINE) ))
#if ERROR_IF_EMPTY_PP_DEF(DONT_USE_UNIT_ASSERT_CALL_THROUGH_MACRO_INLINE)
    #define UNIT_ASSERT_GT(v1, v2) do {{ UNIT_VERIFY_GT_IMPL(v1, v2)(v1, v2, UTILITY_PP_STRINGIZE(v1), UTILITY_PP_STRINGIZE(v2), UTILITY_PP_FILE, UTILITY_PP_LINE); }} while(false)
#endif

#endif

#endif

#endif


// always enabled basic asserts

#define BASIC_VERIFY_TRUE(exp)      (( ::utility::UniAssertTrue{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.verify(exp, UTILITY_PP_STRINGIZE(exp), UTILITY_PP_STRINGIZE_WIDE(exp)) ))
#define BASIC_VERIFY_FALSE(exp)     (( ::utility::UniAssertFalse{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.verify(exp, UTILITY_PP_STRINGIZE(!(exp)), UTILITY_PP_STRINGIZE_WIDE(!(exp))) ))

#define BASIC_VERIFY_EQ(v1, v2)     (( ::utility::UniAssertEQ{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.verify(v1, v2, UTILITY_PP_STRINGIZE((v1) == (v2)), UTILITY_PP_STRINGIZE_WIDE((v1) == (v2))) ))
#define BASIC_VERIFY_NE(v1, v2)     (( ::utility::UniAssertNE{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.verify(v1, v2, UTILITY_PP_STRINGIZE((v1) != (v2)), UTILITY_PP_STRINGIZE_WIDE((v1) != (v2))) ))
#define BASIC_VERIFY_LE(v1, v2)     (( ::utility::UniAssertLE{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.verify(v1, v2, UTILITY_PP_STRINGIZE((v1) <= (v2)), UTILITY_PP_STRINGIZE_WIDE((v1) <= (v2))) ))
#define BASIC_VERIFY_LT(v1, v2)     (( ::utility::UniAssertLT{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.verify(v1, v2, UTILITY_PP_STRINGIZE((v1) < (v2)), UTILITY_PP_STRINGIZE_WIDE((v1) < (v2))) ))
#define BASIC_VERIFY_GE(v1, v2)     (( ::utility::UniAssertGE{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.verify(v1, v2, UTILITY_PP_STRINGIZE((v1) >= (v2)), UTILITY_PP_STRINGIZE_WIDE((v1) >= (v2))) ))
#define BASIC_VERIFY_GT(v1, v2)     (( ::utility::UniAssertGT{ UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG}.verify(v1, v2, UTILITY_PP_STRINGIZE((v1) > (v2)), UTILITY_PP_STRINGIZE_WIDE((v1) > (v2))) ))

// `? true : false` to suppress: `warning C4127: conditional expression is constant`
#define BASIC_ASSERT_TRUE(exp) \
    if ((exp) ? true : false); else do {{ \
        DEBUG_BREAK_IN_DEBUGGER(true); \
        ASSERT_FAIL(UTILITY_PP_STRINGIZE((exp) ? true : false), UTILITY_PP_STRINGIZE_WIDE((exp) ? true : false), UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG); \
    }} while(false); \
    UTILITY_ASSERT_POST_TEST()

#define BASIC_ASSERT_FALSE(exp) \
    if ((exp) ? false : true); else do {{ \
        DEBUG_BREAK_IN_DEBUGGER(true); \
        ASSERT_FAIL(UTILITY_PP_STRINGIZE((exp) ? false : true), UTILITY_PP_STRINGIZE_WIDE((exp) ? false : true), UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG); \
    }} while(false); \
    UTILITY_ASSERT_POST_TEST()

#define BASIC_ASSERT_EQ(v1, v2) \
    if ((v1) == (v2) ? true : false); else do {{ \
        DEBUG_BREAK_IN_DEBUGGER(true); \
        ASSERT_FAIL(UTILITY_PP_STRINGIZE((v1) == (v2)), UTILITY_PP_STRINGIZE_WIDE((v1) == (v2)), UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG); \
    }} while(false); \
    UTILITY_ASSERT_POST_TEST()

#define BASIC_ASSERT_NE(v1, v2) \
    if ((v1) != (v2) ? true : false); else do {{ \
        DEBUG_BREAK_IN_DEBUGGER(true); \
        ASSERT_FAIL(UTILITY_PP_STRINGIZE((v1) != (v2)), UTILITY_PP_STRINGIZE_WIDE((v1) != (v2)), UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG); \
    }} while(false); \
    UTILITY_ASSERT_POST_TEST()

#define BASIC_ASSERT_LE(v1, v2) \
    if ((v1) <= (v2) ? true : false); else do {{ \
        DEBUG_BREAK_IN_DEBUGGER(true); \
        ASSERT_FAIL(UTILITY_PP_STRINGIZE((v1) <= (v2)), UTILITY_PP_STRINGIZE_WIDE((v1) <= (v2)), UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG); \
    }} while(false); \
    UTILITY_ASSERT_POST_TEST()

#define BASIC_ASSERT_LT(v1, v2) \
    if ((v1) < (v2) ? true : false); else do {{ \
        DEBUG_BREAK_IN_DEBUGGER(true); \
        ASSERT_FAIL(UTILITY_PP_STRINGIZE((v1) < (v2)), UTILITY_PP_STRINGIZE_WIDE((v1) < (v2)), UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG); \
    }} while(false); \
    UTILITY_ASSERT_POST_TEST()

#define BASIC_ASSERT_GE(v1, v2) \
    if ((v1) >= (v2) ? true : false); else do {{ \
        DEBUG_BREAK_IN_DEBUGGER(true); \
        ASSERT_FAIL(UTILITY_PP_STRINGIZE((v1) >= (v2)), UTILITY_PP_STRINGIZE_WIDE((v1) >= (v2)), UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG); \
    }} while(false); \
    UTILITY_ASSERT_POST_TEST()

#define BASIC_ASSERT_GT(v1, v2) \
    if ((v1) > (v2) ? true : false); else do {{ \
        DEBUG_BREAK_IN_DEBUGGER(true); \
        ASSERT_FAIL(UTILITY_PP_STRINGIZE((v1) > (v2)), UTILITY_PP_STRINGIZE_WIDE((v1) > (v2)), UTILITY_PP_FILE, UTILITY_PP_FILE_WIDE, UTILITY_PP_LINE, UTILITY_PP_FUNCSIG); \
    }} while(false); \
    UTILITY_ASSERT_POST_TEST()


// always disabled asserts with unused parameters warnings suppression

#define DISABLED_VERIFY_TRUE(exp)   (( ::utility::unused_true(exp) ))
#define DISABLED_VERIFY_FALSE(exp)  (( ::utility::unused_false(exp) ))

#define DISABLED_VERIFY_EQ(v1, v2)  (( ::utility::unused_equal(v1, v2) ))
#define DISABLED_VERIFY_NE(v1, v2)  (( ::utility::unused_not_equal(v1, v2) ))
#define DISABLED_VERIFY_LE(v1, v2)  (( ::utility::unused_less_or_equal(v1, v2) ))
#define DISABLED_VERIFY_LT(v1, v2)  (( ::utility::unused_less(v1, v2) ))
#define DISABLED_VERIFY_GE(v1, v2)  (( ::utility::unused_greater_or_equal(v1, v2) ))
#define DISABLED_VERIFY_GT(v1, v2)  (( ::utility::unused_greater(v1, v2) ))

#define DISABLED_ASSERT_TRUE(exp)   do {{ UTILITY_UNUSED_STATEMENT((exp) ? true : false); UTILITY_ASSERT_POST_TEST(); }} while(false)
#define DISABLED_ASSERT_FALSE(exp)  do {{ UTILITY_UNUSED_STATEMENT((exp) ? false : true); UTILITY_ASSERT_POST_TEST(); }} while(false)

// `? true : false` to suppress: `warning C4127: conditional expression is constant`
#define DISABLED_ASSERT_EQ(v1, v2)  do {{ UTILITY_UNUSED_EXPR((v1) == (v2) ? true : false); UTILITY_ASSERT_POST_TEST(); }} while(false)
#define DISABLED_ASSERT_NE(v1, v2)  do {{ UTILITY_UNUSED_EXPR((v1) != (v2) ? true : false); UTILITY_ASSERT_POST_TEST(); }} while(false)
#define DISABLED_ASSERT_LE(v1, v2)  do {{ UTILITY_UNUSED_EXPR((v1) <= (v2) ? true : false); UTILITY_ASSERT_POST_TEST(); }} while(false)
#define DISABLED_ASSERT_LT(v1, v2)  do {{ UTILITY_UNUSED_EXPR((v1) < (v2) ? true : false); UTILITY_ASSERT_POST_TEST(); }} while(false)
#define DISABLED_ASSERT_GE(v1, v2)  do {{ UTILITY_UNUSED_EXPR((v1) >= (v2) ? true : false); UTILITY_ASSERT_POST_TEST(); }} while(false)
#define DISABLED_ASSERT_GT(v1, v2)  do {{ UTILITY_UNUSED_EXPR((v1) > (v2) ? true : false); UTILITY_ASSERT_POST_TEST(); }} while(false)


// classic debug assert

#if defined(_DEBUG) && !ERROR_IF_EMPTY_PP_DEF(DISABLE_DEBUG_VERIFY_ASSERT)

#define DEBUG_VERIFY_TRUE       BASIC_VERIFY_TRUE
#define DEBUG_VERIFY_FALSE      BASIC_VERIFY_FALSE

#define DEBUG_VERIFY_EQ         BASIC_VERIFY_EQ
#define DEBUG_VERIFY_NE         BASIC_VERIFY_NE
#define DEBUG_VERIFY_LE         BASIC_VERIFY_LE
#define DEBUG_VERIFY_LT         BASIC_VERIFY_LT
#define DEBUG_VERIFY_GE         BASIC_VERIFY_GE
#define DEBUG_VERIFY_GT         BASIC_VERIFY_GT

#define DEBUG_ASSERT_TRUE       BASIC_ASSERT_TRUE
#define DEBUG_ASSERT_FALSE      BASIC_ASSERT_FALSE

#define DEBUG_ASSERT_EQ         BASIC_ASSERT_EQ
#define DEBUG_ASSERT_NE         BASIC_ASSERT_NE
#define DEBUG_ASSERT_LE         BASIC_ASSERT_LE
#define DEBUG_ASSERT_LT         BASIC_ASSERT_LT
#define DEBUG_ASSERT_GE         BASIC_ASSERT_GE
#define DEBUG_ASSERT_GT         BASIC_ASSERT_GT

#define DEBUG_ASSERT_VERIFY_ENABLED 1

#define IF_DEBUG_ASSERT_VERIFY_ENABLED(x) if(x)

#else

#define DEBUG_VERIFY_TRUE       DISABLED_VERIFY_TRUE
#define DEBUG_VERIFY_FALSE      DISABLED_VERIFY_FALSE

#define DEBUG_VERIFY_EQ         DISABLED_VERIFY_EQ
#define DEBUG_VERIFY_NE         DISABLED_VERIFY_NE
#define DEBUG_VERIFY_LE         DISABLED_VERIFY_LE
#define DEBUG_VERIFY_LT         DISABLED_VERIFY_LT
#define DEBUG_VERIFY_GE         DISABLED_VERIFY_GE
#define DEBUG_VERIFY_GT         DISABLED_VERIFY_GT

#define DEBUG_ASSERT_TRUE       DISABLED_ASSERT_TRUE
#define DEBUG_ASSERT_FALSE      DISABLED_ASSERT_FALSE

#define DEBUG_ASSERT_EQ         DISABLED_ASSERT_EQ
#define DEBUG_ASSERT_NE         DISABLED_ASSERT_NE
#define DEBUG_ASSERT_LE         DISABLED_ASSERT_LE
#define DEBUG_ASSERT_LT         DISABLED_ASSERT_LT
#define DEBUG_ASSERT_GE         DISABLED_ASSERT_GE
#define DEBUG_ASSERT_GT         DISABLED_ASSERT_GT

#define DEBUG_ASSERT_VERIFY_ENABLED 0

#define IF_DEBUG_ASSERT_VERIFY_ENABLED(x) if(false)

#endif


// Special local assert, switches between common and basic assert by runtime value.
// If value evaluated to 0, then common version has used, otherwise the basic has used.
// Useful to force assert to stay as basic (for example, to make assertion in the Release)
// if standalone macro definition has used, otherwise use the common one.

#define LOCAL_VERIFY_TRUE(is_local, exp)   (( (is_local) ? BASIC_VERIFY_TRUE(exp) : VERIFY_TRUE(exp) ))
#define LOCAL_VERIFY_FALSE(is_local, exp)  (( (is_local) ? BASIC_VERIFY_FALSE(exp) : VERIFY_FALSE(exp) ))

#define LOCAL_VERIFY_EQ(is_local, v1, v2)  (( (is_local) ? BASIC_VERIFY_EQ(v1, v2) : VERIFY_EQ(v1, v2) ))
#define LOCAL_VERIFY_NE(is_local, v1, v2)  (( (is_local) ? BASIC_VERIFY_NE(v1, v2) : VERIFY_NE(v1, v2) ))
#define LOCAL_VERIFY_LE(is_local, v1, v2)  (( (is_local) ? BASIC_VERIFY_LE(v1, v2) : VERIFY_LE(v1, v2) ))
#define LOCAL_VERIFY_LT(is_local, v1, v2)  (( (is_local) ? BASIC_VERIFY_LT(v1, v2) : VERIFY_LT(v1, v2) ))
#define LOCAL_VERIFY_GE(is_local, v1, v2)  (( (is_local) ? BASIC_VERIFY_GE(v1, v2) : VERIFY_GE(v1, v2) ))
#define LOCAL_VERIFY_GT(is_local, v1, v2)  (( (is_local) ? BASIC_VERIFY_GT(v1, v2) : VERIFY_GT(v1, v2) ))

#define LOCAL_ASSERT_TRUE(is_local, exp)   do {{ if(is_local) BASIC_ASSERT_TRUE(exp); else ASSERT_TRUE(exp); }} while(false)
#define LOCAL_ASSERT_FALSE(is_local, exp)  do {{ if(is_local) BASIC_ASSERT_FALSE(exp); else ASSERT_FALSE(exp); }} while(false)

#define LOCAL_ASSERT_EQ(is_local, v1, v2)  do {{ if(is_local) BASIC_ASSERT_EQ(v1, v2); else ASSERT_EQ(v1, v2); }} while(false)
#define LOCAL_ASSERT_NE(is_local, v1, v2)  do {{ if(is_local) BASIC_ASSERT_NE(v1, v2); else ASSERT_NE(v1, v2); }} while(false)
#define LOCAL_ASSERT_LE(is_local, v1, v2)  do {{ if(is_local) BASIC_ASSERT_LE(v1, v2); else ASSERT_LE(v1, v2); }} while(false)
#define LOCAL_ASSERT_LT(is_local, v1, v2)  do {{ if(is_local) BASIC_ASSERT_LT(v1, v2); else ASSERT_LT(v1, v2); }} while(false)
#define LOCAL_ASSERT_GE(is_local, v1, v2)  do {{ if(is_local) BASIC_ASSERT_GE(v1, v2); else ASSERT_GE(v1, v2); }} while(false)
#define LOCAL_ASSERT_GT(is_local, v1, v2)  do {{ if(is_local) BASIC_ASSERT_GT(v1, v2); else ASSERT_GT(v1, v2); }} while(false)


#if ERROR_IF_EMPTY_PP_DEF(DISABLE_VERIFY_ASSERT) || defined(BENCH_TESTS)

#define VERIFY_TRUE     DISABLED_VERIFY_TRUE
#define VERIFY_FALSE    DISABLED_VERIFY_FALSE

#define VERIFY_EQ       DISABLED_VERIFY_EQ
#define VERIFY_NE       DISABLED_VERIFY_NE
#define VERIFY_LE       DISABLED_VERIFY_LE
#define VERIFY_LT       DISABLED_VERIFY_LT
#define VERIFY_GE       DISABLED_VERIFY_GE
#define VERIFY_GT       DISABLED_VERIFY_GT

#define ASSERT_TRUE     DISABLED_ASSERT_TRUE
#define ASSERT_FALSE    DISABLED_ASSERT_FALSE

#define ASSERT_EQ       DISABLED_ASSERT_EQ
#define ASSERT_NE       DISABLED_ASSERT_NE
#define ASSERT_LE       DISABLED_ASSERT_LE
#define ASSERT_LT       DISABLED_ASSERT_LT
#define ASSERT_GE       DISABLED_ASSERT_GE
#define ASSERT_GT       DISABLED_ASSERT_GT

#define ASSERT_VERIFY_ENABLED 0

#define IF_ASSERT_VERIFY_ENABLED(x) if(UTILITY_CONSTEXPR(false))

#else

// TIPS:
//  * avoid usage the unit test asserts in debug because of greater runtime slow down in respect to basic assert implementation
//

#if defined(_DEBUG)

#define VERIFY_TRUE     DEBUG_VERIFY_TRUE
#define VERIFY_FALSE    DEBUG_VERIFY_FALSE

#define VERIFY_EQ       DEBUG_VERIFY_EQ
#define VERIFY_NE       DEBUG_VERIFY_NE
#define VERIFY_LE       DEBUG_VERIFY_LE
#define VERIFY_LT       DEBUG_VERIFY_LT
#define VERIFY_GE       DEBUG_VERIFY_GE
#define VERIFY_GT       DEBUG_VERIFY_GT

#define ASSERT_TRUE     DEBUG_ASSERT_TRUE
#define ASSERT_FALSE    DEBUG_ASSERT_FALSE

#define ASSERT_EQ       DEBUG_ASSERT_EQ
#define ASSERT_NE       DEBUG_ASSERT_NE
#define ASSERT_LE       DEBUG_ASSERT_LE
#define ASSERT_LT       DEBUG_ASSERT_LT
#define ASSERT_GE       DEBUG_ASSERT_GE
#define ASSERT_GT       DEBUG_ASSERT_GT

#define ASSERT_VERIFY_ENABLED DEBUG_ASSERT_VERIFY_ENABLED

#define IF_ASSERT_VERIFY_ENABLED(x) IF_DEBUG_ASSERT_VERIFY_ENABLED(UTILITY_CONSTEXPR(x))

#elif defined(UNIT_TESTS)

#if ERROR_IF_EMPTY_PP_DEF(USE_BASIC_ASSERT_INSTEAD_UNIT_ASSERT)

#define VERIFY_TRUE     BASIC_VERIFY_TRUE
#define VERIFY_FALSE    BASIC_VERIFY_FALSE

#define VERIFY_EQ       BASIC_VERIFY_EQ
#define VERIFY_NE       BASIC_VERIFY_NE
#define VERIFY_LE       BASIC_VERIFY_LE
#define VERIFY_LT       BASIC_VERIFY_LT
#define VERIFY_GE       BASIC_VERIFY_GE
#define VERIFY_GT       BASIC_VERIFY_GT

#define ASSERT_TRUE     BASIC_ASSERT_TRUE
#define ASSERT_FALSE    BASIC_ASSERT_FALSE

#define ASSERT_EQ       BASIC_ASSERT_EQ
#define ASSERT_NE       BASIC_ASSERT_NE
#define ASSERT_LE       BASIC_ASSERT_LE
#define ASSERT_LT       BASIC_ASSERT_LT
#define ASSERT_GE       BASIC_ASSERT_GE
#define ASSERT_GT       BASIC_ASSERT_GT

#define ASSERT_VERIFY_ENABLED 1

#define IF_ASSERT_VERIFY_ENABLED(x) if(UTILITY_CONSTEXPR(x))

#else

#define VERIFY_TRUE     UNIT_VERIFY_TRUE
#define VERIFY_FALSE    UNIT_VERIFY_FALSE

#define VERIFY_EQ       UNIT_VERIFY_EQ
#define VERIFY_NE       UNIT_VERIFY_NE
#define VERIFY_LE       UNIT_VERIFY_LE
#define VERIFY_LT       UNIT_VERIFY_LT
#define VERIFY_GE       UNIT_VERIFY_GE
#define VERIFY_GT       UNIT_VERIFY_GT

#define ASSERT_TRUE     UNIT_ASSERT_TRUE
#define ASSERT_FALSE    UNIT_ASSERT_FALSE

#define ASSERT_EQ       UNIT_ASSERT_EQ
#define ASSERT_NE       UNIT_ASSERT_NE
#define ASSERT_LE       UNIT_ASSERT_LE
#define ASSERT_LT       UNIT_ASSERT_LT
#define ASSERT_GE       UNIT_ASSERT_GE
#define ASSERT_GT       UNIT_ASSERT_GT

#define ASSERT_VERIFY_ENABLED 1

#define IF_ASSERT_VERIFY_ENABLED(x) if(UTILITY_CONSTEXPR(x))

#endif

#else

#define VERIFY_TRUE     DISABLED_VERIFY_TRUE
#define VERIFY_FALSE    DISABLED_VERIFY_FALSE

#define VERIFY_EQ       DISABLED_VERIFY_EQ
#define VERIFY_NE       DISABLED_VERIFY_NE
#define VERIFY_LE       DISABLED_VERIFY_LE
#define VERIFY_LT       DISABLED_VERIFY_LT
#define VERIFY_GE       DISABLED_VERIFY_GE
#define VERIFY_GT       DISABLED_VERIFY_GT

#define ASSERT_TRUE     DISABLED_ASSERT_TRUE
#define ASSERT_FALSE    DISABLED_ASSERT_FALSE

#define ASSERT_EQ       DISABLED_ASSERT_EQ
#define ASSERT_NE       DISABLED_ASSERT_NE
#define ASSERT_LE       DISABLED_ASSERT_LE
#define ASSERT_LT       DISABLED_ASSERT_LT
#define ASSERT_GE       DISABLED_ASSERT_GE
#define ASSERT_GT       DISABLED_ASSERT_GT

#define ASSERT_VERIFY_ENABLED 0

#define IF_ASSERT_VERIFY_ENABLED(x) if(UTILITY_CONSTEXPR(false))

#endif

#endif

#ifndef GTEST_FAIL

// redirect expect asserts to common asserts
#define EXPECT_TRUE     ASSERT_TRUE
#define EXPECT_FALSE    ASSERT_FALSE

#define EXPECT_EQ       ASSERT_EQ
#define EXPECT_NE       ASSERT_NE
#define EXPECT_LE       ASSERT_LE
#define EXPECT_LT       ASSERT_LT
#define EXPECT_GE       ASSERT_GE
#define EXPECT_GT       ASSERT_GT

#endif


namespace utility
{
    // TIPS:
    // * to capture parameters by reference in macro definitions for single evaluation
    // * to suppress `unused variable` warnings like: `warning C4101: '...': unreferenced local variable`
    template<typename T>
    FORCE_INLINE const T & unused_true(const T & exp_var)
    {
        const T & r = (exp_var ? exp_var : exp_var); // to avoid warnings of truncation to bool
        UTILITY_ASSERT_POST_TEST();
        return r;
    }

    template<typename T>
    FORCE_INLINE const T & unused_false(const T & exp_var)
    {
        const T & r = (exp_var ? exp_var : exp_var); // to avoid warnings of truncation to bool
        UTILITY_ASSERT_POST_TEST();
        return r;
    }

    template<typename T1, typename T2>
    FORCE_INLINE const T1 & unused_equal(const T1 & v1, const T2 & v2)
    {
        const T1 & r = (v1 == v2 ? v1 : v1);
        UTILITY_ASSERT_POST_TEST();
        return r;
    }

    template<typename T1, typename T2>
    FORCE_INLINE const T1 & unused_not_equal(const T1 & v1, const T2 & v2)
    {
        const T1 & r = (v1 != v2 ? v1 : v1);
        UTILITY_ASSERT_POST_TEST();
        return r;
    }

    template<typename T1, typename T2>
    FORCE_INLINE const T1 & unused_less_or_equal(const T1 & v1, const T2 & v2)
    {
        const T1 & r = (v1 <= v2 ? v1 : v1);
        UTILITY_ASSERT_POST_TEST();
        return r;
    }

    template<typename T1, typename T2>
    FORCE_INLINE const T1 & unused_less(const T1 & v1, const T2 & v2)
    {
        const T1 & r = (v1 < v2 ? v1 : v1);
        UTILITY_ASSERT_POST_TEST();
        return r;
    }

    template<typename T1, typename T2>
    FORCE_INLINE const T1 & unused_greater_or_equal(const T1 & v1, const T2 & v2)
    {
        const T1 & r = (v1 >= v2 ? v1 : v1);
        UTILITY_ASSERT_POST_TEST();
        return r;
    }

    template<typename T1, typename T2>
    FORCE_INLINE const T1 & unused_greater(const T1 & v1, const T2 & v2)
    {
        const T1 & r = (v1 > v2 ? v1 : v1);
        UTILITY_ASSERT_POST_TEST();
        return r;
    }

#if defined(UNIT_TESTS) || defined(BENCH_TESTS)
    inline void gtest_fail_true(const char * exp_str, const char * file, unsigned int line) // must be not `__forceinline`-ed!
    {
        DEBUG_BREAK_IN_DEBUGGER(true);
        GTEST_TEST_BOOLEAN_(false, exp_str, false, true, UTILITY_ASSERT_GTEST_FATAL_FAILURE_);
    }

    inline void gtest_fail_false(const char * exp_str, const char * file, unsigned int line) // must be not `__forceinline`-ed!
    {
        DEBUG_BREAK_IN_DEBUGGER(true);
        GTEST_TEST_BOOLEAN_(false, exp_str, true, false, UTILITY_ASSERT_GTEST_FATAL_FAILURE_);
    }

    inline void gtest_fail_exp(const ::testing::AssertionResult & exp_value, const char * file, unsigned int line) // must be not `__forceinline`-ed!
    {
        DEBUG_BREAK_IN_DEBUGGER(true);
        GTEST_ASSERT_(exp_value, UTILITY_ASSERT_GTEST_FATAL_FAILURE_);
    }
#endif

    struct UniAssertTrue
    {
        // TIPS:
        // * to capture parameters by reference in macro definitions for single evaluation
        // * to suppress `unused variable` warnings like: `warning C4101: '...': unreferenced local variable`
        template<typename T>
        FORCE_INLINE const T & verify(const T & exp_var, const char * exp_str, const wchar_t * exp_str_w)
        {
            if (exp_var ? true : false);
            else {
                DEBUG_BREAK_IN_DEBUGGER(true);
                ASSERT_FAIL(exp_str, exp_str_w, file, file_w, line, funcsig);
            }

            UTILITY_ASSERT_POST_TEST();

            return exp_var;
        }

#if defined(UNIT_TESTS) || defined(BENCH_TESTS)
        template <typename T>
        FORCE_INLINE const T & gtest_verify(const T & exp_var, const char * exp_str) const {
            if (exp_var ? true : false); // to avoid `warning C4800: forcing value to bool 'true' or 'false' (performance warning)`
            else UTILITY_GTEST_FAIL_TRUE_FUNC_INLINE(exp_str, file, line);

            UTILITY_ASSERT_POST_TEST();

            return exp_var;
        }
#endif

        const char *    file;
        const wchar_t * file_w;
        unsigned int    line;
        const char *    funcsig;
    };

    struct UniAssertFalse
    {
        // TIPS:
        // * to capture parameters by reference in macro definitions for single evaluation
        // * to suppress `unused variable` warnings like: `warning C4101: '...': unreferenced local variable`
        template<typename T>
        FORCE_INLINE const T & verify(const T & exp_var, const char * exp_str, const wchar_t * exp_str_w)
        {
            if (exp_var ? false : true);
            else {
                DEBUG_BREAK_IN_DEBUGGER(true);
                ASSERT_FAIL(exp_str, exp_str_w, file, file_w, line, funcsig);
            }

            UTILITY_ASSERT_POST_TEST();

            return exp_var;
        }

#if defined(UNIT_TESTS) || defined(BENCH_TESTS)
        template <typename T>
        FORCE_INLINE const T & gtest_verify(const T & exp_var, const char * exp_str) const {
            if (exp_var ? false : true); // to avoid `warning C4800: forcing value to bool 'true' or 'false' (performance warning)`
            else UTILITY_GTEST_FAIL_FALSE_FUNC_INLINE(exp_str, file, line);

            UTILITY_ASSERT_POST_TEST();

            return exp_var;
        }
#endif

        const char *    file;
        const wchar_t * file_w;
        unsigned int    line;
        const char *    funcsig;
    };

    struct UniAssertEQ
    {
        // TIPS:
        // * to capture parameters by reference in macro definitions for single evaluation
        // * to suppress `unused variable` warnings like: `warning C4101: '...': unreferenced local variable`
        template<typename T1, typename T2>
        FORCE_INLINE const T1 & verify(const T1 & v1, const T2 & v2, const char * exp_str, const wchar_t * exp_str_w)
        {
            if (v1 == v2);
            else {
                DEBUG_BREAK_IN_DEBUGGER(true);
                ASSERT_FAIL(exp_str, exp_str_w, file, file_w, line, funcsig);
            }

            UTILITY_ASSERT_POST_TEST();

            return v1;
        }

#if defined(UNIT_TESTS) || defined(BENCH_TESTS)
        template <typename T1, typename T2>
        FORCE_INLINE const T1 & gtest_verify(const T1 & v1, const T2 & v2, const char * v1_str, const char * v2_str) const {
            if (const ::testing::AssertionResult exp_value = ::testing::internal::EqHelper IF_GTEST_VERSION_LESS_1_10_0(<GTEST_IS_NULL_LITERAL_(v1)>) ::Compare(v1_str, v2_str, v1, v2));
            else UTILITY_GTEST_FAIL_EXP_FUNC_INLINE(exp_value, file, line);

            UTILITY_ASSERT_POST_TEST();

            return v1;
        }
#endif

        const char *    file;
        const wchar_t * file_w;
        unsigned int    line;
        const char *    funcsig;
    };

    struct UniAssertNE
    {
        // TIPS:
        // * to capture parameters by reference in macro definitions for single evaluation
        // * to suppress `unused variable` warnings like: `warning C4101: '...': unreferenced local variable`
        template<typename T1, typename T2>
        FORCE_INLINE const T1 & verify(const T1 & v1, const T2 & v2, const char * exp_str, const wchar_t * exp_str_w)
        {
            if (v1 != v2);
            else {
                DEBUG_BREAK_IN_DEBUGGER(true);
                ASSERT_FAIL(exp_str, exp_str_w, file, file_w, line, funcsig);
            }

            UTILITY_ASSERT_POST_TEST();

            return v1;
        }

#if defined(UNIT_TESTS) || defined(BENCH_TESTS)
        template <typename T1, typename T2>
        FORCE_INLINE const T1 & gtest_verify(const T1 & v1, const T2 & v2, const char * v1_str, const char * v2_str) const {
            if (const ::testing::AssertionResult exp_value = ::testing::internal::CmpHelperNE(v1_str, v2_str, v1, v2));
            else UTILITY_GTEST_FAIL_EXP_FUNC_INLINE(exp_value, file, line);

            UTILITY_ASSERT_POST_TEST();

            return v1;
        }
#endif

        const char *    file;
        const wchar_t * file_w;
        unsigned int    line;
        const char *    funcsig;
    };

    struct UniAssertLE
    {
        // TIPS:
        // * to capture parameters by reference in macro definitions for single evaluation
        // * to suppress `unused variable` warnings like: `warning C4101: '...': unreferenced local variable`
        template<typename T1, typename T2>
        FORCE_INLINE const T1 & verify(const T1 & v1, const T2 & v2, const char * exp_str, const wchar_t * exp_str_w)
        {
            if (v1 <= v2);
            else {
                DEBUG_BREAK_IN_DEBUGGER(true);
                ASSERT_FAIL(exp_str, exp_str_w, file, file_w, line, funcsig);
            }

            UTILITY_ASSERT_POST_TEST();

            return v1;
        }

#if defined(UNIT_TESTS) || defined(BENCH_TESTS)
        template <typename T1, typename T2>
        FORCE_INLINE const T1 & gtest_verify(const T1 & v1, const T2 & v2, const char * v1_str, const char * v2_str) const {
            if (const ::testing::AssertionResult exp_value = ::testing::internal::CmpHelperLE(v1_str, v2_str, v1, v2));
            else UTILITY_GTEST_FAIL_EXP_FUNC_INLINE(exp_value, file, line);

            UTILITY_ASSERT_POST_TEST();

            return v1;
        }
#endif

        const char *    file;
        const wchar_t * file_w;
        unsigned int    line;
        const char *    funcsig;
    };

    struct UniAssertLT
    {
        // TIPS:
        // * to capture parameters by reference in macro definitions for single evaluation
        // * to suppress `unused variable` warnings like: `warning C4101: '...': unreferenced local variable`
        template<typename T1, typename T2>
        FORCE_INLINE const T1 & verify(const T1 & v1, const T2 & v2, const char * exp_str, const wchar_t * exp_str_w)
        {
            if (v1 < v2);
            else {
                DEBUG_BREAK_IN_DEBUGGER(true);
                ASSERT_FAIL(exp_str, exp_str_w, file, file_w, line, funcsig);
            }

            UTILITY_ASSERT_POST_TEST();

            return v1;
        }

#if defined(UNIT_TESTS) || defined(BENCH_TESTS)
        template <typename T1, typename T2>
        FORCE_INLINE const T1 & gtest_verify(const T1 & v1, const T2 & v2, const char * v1_str, const char * v2_str) const {
            if (const ::testing::AssertionResult exp_value = ::testing::internal::CmpHelperLT(v1_str, v2_str, v1, v2));
            else UTILITY_GTEST_FAIL_EXP_FUNC_INLINE(exp_value, file, line);

            UTILITY_ASSERT_POST_TEST();

            return v1;
        }
#endif

        const char *    file;
        const wchar_t * file_w;
        unsigned int    line;
        const char *    funcsig;
    };

    struct UniAssertGE
    {
        // TIPS:
        // * to capture parameters by reference in macro definitions for single evaluation
        // * to suppress `unused variable` warnings like: `warning C4101: '...': unreferenced local variable`
        template<typename T1, typename T2>
        FORCE_INLINE const T1 & verify(const T1 & v1, const T2 & v2, const char * exp_str, const wchar_t * exp_str_w)
        {
            if (v1 >= v2);
            else {
                DEBUG_BREAK_IN_DEBUGGER(true);
                ASSERT_FAIL(exp_str, exp_str_w, file, file_w, line, funcsig);
            }

            UTILITY_ASSERT_POST_TEST();

            return v1;
        }

#if defined(UNIT_TESTS) || defined(BENCH_TESTS)
        template <typename T1, typename T2>
        FORCE_INLINE const T1 & gtest_verify(const T1 & v1, const T2 & v2, const char * v1_str, const char * v2_str) const {
            if (const ::testing::AssertionResult exp_value = ::testing::internal::CmpHelperGE(v1_str, v2_str, v1, v2));
            else UTILITY_GTEST_FAIL_EXP_FUNC_INLINE(exp_value, file, line);

            UTILITY_ASSERT_POST_TEST();

            return v1;
        }
#endif

        const char *    file;
        const wchar_t * file_w;
        unsigned int    line;
        const char *    funcsig;
    };

    struct UniAssertGT
    {
        // TIPS:
        // * to capture parameters by reference in macro definitions for single evaluation
        // * to suppress `unused variable` warnings like: `warning C4101: '...': unreferenced local variable`
        template<typename T1, typename T2>
        FORCE_INLINE const T1 & verify(const T1 & v1, const T2 & v2, const char * exp_str, const wchar_t * exp_str_w)
        {
            if (v1 > v2);
            else {
                DEBUG_BREAK_IN_DEBUGGER(true);
                ASSERT_FAIL(exp_str, exp_str_w, file, file_w, line, funcsig);
            }

            UTILITY_ASSERT_POST_TEST();

            return v1;
        }

#if defined(UNIT_TESTS) || defined(BENCH_TESTS)
        template <typename T1, typename T2>
        FORCE_INLINE const T1 & gtest_verify(const T1 & v1, const T2 & v2, const char * v1_str, const char * v2_str) const {
            if (const ::testing::AssertionResult exp_value = ::testing::internal::CmpHelperGT(v1_str, v2_str, v1, v2));
            else UTILITY_GTEST_FAIL_EXP_FUNC_INLINE(exp_value, file, line);

            UTILITY_ASSERT_POST_TEST();

            return v1;
        }
#endif

        const char *    file;
        const wchar_t * file_w;
        unsigned int    line;
        const char *    funcsig;
    };

}

#endif