390 lines
11 KiB
C
390 lines
11 KiB
C
|
#ifndef UNITTEST_CHECKS_H
|
||
|
#define UNITTEST_CHECKS_H
|
||
|
|
||
|
#if defined(__GNUC__) && !defined(__clang__) && !defined(__llvm__)
|
||
|
#pragma GCC diagnostic push
|
||
|
#pragma GCC diagnostic ignored "-Wsign-compare"
|
||
|
#pragma GCC diagnostic ignored "-Wfloat-equal"
|
||
|
#endif
|
||
|
|
||
|
#if defined(__clang__) || defined(__llvm__)
|
||
|
#pragma clang diagnostic push
|
||
|
#pragma clang diagnostic ignored "-Wsign-compare"
|
||
|
#pragma clang diagnostic ignored "-Wfloat-equal"
|
||
|
#endif
|
||
|
|
||
|
#include "Config.h"
|
||
|
#include "TestResults.h"
|
||
|
#include "MemoryOutStream.h"
|
||
|
#include<iomanip>
|
||
|
|
||
|
namespace UnitTest
|
||
|
{
|
||
|
template <typename T>
|
||
|
typename std::enable_if<!std::is_pointer<T>::value, std::string>::type
|
||
|
DisplayValue(const T& c)
|
||
|
{
|
||
|
std::ostringstream oss;
|
||
|
|
||
|
oss << c;
|
||
|
|
||
|
return oss.str();
|
||
|
}
|
||
|
|
||
|
template <>
|
||
|
inline std::string DisplayValue(const char& c)
|
||
|
{
|
||
|
typedef std::char_traits<char>::int_type type;
|
||
|
|
||
|
std::ostringstream oss;
|
||
|
|
||
|
oss << type(c) << " ('" << c << "')";
|
||
|
|
||
|
return oss.str();
|
||
|
}
|
||
|
|
||
|
#if (__cplusplus >= 202002L)
|
||
|
template <>
|
||
|
inline std::string DisplayValue(const char8_t& c)
|
||
|
{
|
||
|
typedef std::char_traits<char8_t>::int_type type;
|
||
|
|
||
|
std::ostringstream oss;
|
||
|
|
||
|
oss << type(c);
|
||
|
|
||
|
return oss.str();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
template <>
|
||
|
inline std::string DisplayValue(const wchar_t& c)
|
||
|
{
|
||
|
typedef std::char_traits<wchar_t>::int_type type;
|
||
|
|
||
|
std::ostringstream oss;
|
||
|
|
||
|
oss << type(c);
|
||
|
|
||
|
return oss.str();
|
||
|
}
|
||
|
|
||
|
#if (__cplusplus >= 201103L)
|
||
|
template <>
|
||
|
inline std::string DisplayValue(const char16_t& c)
|
||
|
{
|
||
|
typedef std::char_traits<char16_t>::int_type type;
|
||
|
|
||
|
std::ostringstream oss;
|
||
|
|
||
|
oss << type(c);
|
||
|
|
||
|
return oss.str();
|
||
|
}
|
||
|
|
||
|
template <>
|
||
|
inline std::string DisplayValue(const char32_t& c)
|
||
|
{
|
||
|
typedef std::char_traits<char32_t>::int_type type;
|
||
|
|
||
|
std::ostringstream oss;
|
||
|
|
||
|
oss << type(c);
|
||
|
|
||
|
return oss.str();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
template <typename T>
|
||
|
std::string DisplayValue(const T* c)
|
||
|
{
|
||
|
std::ostringstream oss;
|
||
|
|
||
|
oss << c;
|
||
|
|
||
|
return oss.str();
|
||
|
}
|
||
|
|
||
|
template <>
|
||
|
inline std::string DisplayValue(const char* c)
|
||
|
{
|
||
|
std::ostringstream oss;
|
||
|
|
||
|
oss << c;
|
||
|
|
||
|
return oss.str();
|
||
|
}
|
||
|
|
||
|
#if (__cplusplus >= 202002L)
|
||
|
template <>
|
||
|
inline std::string DisplayValue(const char8_t* c)
|
||
|
{
|
||
|
std::ostringstream oss;
|
||
|
|
||
|
oss << static_cast<const void*>(c);
|
||
|
|
||
|
return oss.str();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
template <>
|
||
|
inline std::string DisplayValue(const wchar_t* c)
|
||
|
{
|
||
|
std::ostringstream oss;
|
||
|
|
||
|
oss << static_cast<const void*>(c);
|
||
|
|
||
|
return oss.str();
|
||
|
}
|
||
|
|
||
|
#if (__cplusplus >= 201103L)
|
||
|
template <>
|
||
|
inline std::string DisplayValue(const char16_t* c)
|
||
|
{
|
||
|
std::ostringstream oss;
|
||
|
|
||
|
oss << static_cast<const void*>(c);
|
||
|
|
||
|
return oss.str();
|
||
|
}
|
||
|
|
||
|
template <>
|
||
|
inline std::string DisplayValue(const char32_t* c)
|
||
|
{
|
||
|
std::ostringstream oss;
|
||
|
|
||
|
oss << static_cast<const void*>(c);
|
||
|
|
||
|
return oss.str();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
template< typename Value >
|
||
|
bool Check(Value const& value)
|
||
|
{
|
||
|
return !!value; // doing double negative to avoid silly VS warnings
|
||
|
}
|
||
|
|
||
|
template< typename Value >
|
||
|
bool CheckFalse(Value const& value)
|
||
|
{
|
||
|
return !value;
|
||
|
}
|
||
|
|
||
|
#if __cplusplus >= 201103L
|
||
|
template< typename Expected, typename Actual >
|
||
|
void CheckEqual(TestResults& results, Expected&& expected, Actual&& actual, TestDetails const& details)
|
||
|
{
|
||
|
if (!(expected == actual))
|
||
|
{
|
||
|
UnitTest::MemoryOutStream stream;
|
||
|
stream << "Expected "
|
||
|
<< DisplayValue(expected) << " but was " << DisplayValue(actual);
|
||
|
|
||
|
results.OnTestFailure(details, stream.GetText());
|
||
|
}
|
||
|
}
|
||
|
#else
|
||
|
template< typename Expected, typename Actual >
|
||
|
void CheckEqual(TestResults& results, Expected const& expected, Actual const& actual, TestDetails const& details)
|
||
|
{
|
||
|
if (!(expected == actual))
|
||
|
{
|
||
|
UnitTest::MemoryOutStream stream;
|
||
|
stream << "Expected "
|
||
|
<< DisplayValue(expected) << " but was " << DisplayValue(actual);
|
||
|
|
||
|
results.OnTestFailure(details, stream.GetText());
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
template< typename Expected, typename Actual >
|
||
|
void CheckEqualHex(TestResults& results, Expected const& expected, Actual const& actual, TestDetails const& details)
|
||
|
{
|
||
|
if (!(expected == actual))
|
||
|
{
|
||
|
UnitTest::MemoryOutStream stream;
|
||
|
stream << std::hex << std::uppercase << std::setfill('0')
|
||
|
<< "Expected 0x" << std::setw(2 * sizeof(Expected)) << (expected & ~(typename std::make_unsigned<Expected>::type(0)))
|
||
|
<< " but was 0x" << std::setw(2 * sizeof(Actual)) << (actual & ~(typename std::make_unsigned<Actual>::type(0)));
|
||
|
|
||
|
results.OnTestFailure(details, stream.GetText());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template< typename Expected, typename Actual >
|
||
|
void CheckNotEqual(TestResults& results, Expected const& expected, Actual const& actual, TestDetails const& details)
|
||
|
{
|
||
|
if (expected == actual)
|
||
|
{
|
||
|
UnitTest::MemoryOutStream stream;
|
||
|
stream << "Expected not equal, but both values are" << actual;
|
||
|
|
||
|
results.OnTestFailure(details, stream.GetText());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template< typename Expected, typename Actual >
|
||
|
void CheckNotEqualHex(TestResults& results, Expected const& expected, Actual const& actual, TestDetails const& details)
|
||
|
{
|
||
|
if (expected == actual)
|
||
|
{
|
||
|
UnitTest::MemoryOutStream stream;
|
||
|
stream << std::hex << std::uppercase << std::setfill('0') << std::setw(2 * sizeof(Actual))
|
||
|
<< "Expected not equal, but both values are " << (actual & ~(typename std::make_unsigned<Actual>::type(0)));
|
||
|
|
||
|
results.OnTestFailure(details, stream.GetText());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
UNITTEST_LINKAGE void CheckEqual(TestResults& results, char const* expected, char const* actual, TestDetails const& details);
|
||
|
|
||
|
UNITTEST_LINKAGE void CheckEqual(TestResults& results, char* expected, char* actual, TestDetails const& details);
|
||
|
|
||
|
UNITTEST_LINKAGE void CheckEqual(TestResults& results, char* expected, char const* actual, TestDetails const& details);
|
||
|
|
||
|
UNITTEST_LINKAGE void CheckEqual(TestResults& results, char const* expected, char* actual, TestDetails const& details);
|
||
|
|
||
|
template< typename Expected, typename Actual, typename Tolerance >
|
||
|
bool AreClose(Expected const& expected, Actual const& actual, Tolerance const& tolerance)
|
||
|
{
|
||
|
return (actual >= (expected - tolerance)) && (actual <= (expected + tolerance));
|
||
|
}
|
||
|
|
||
|
template< typename Expected, typename Actual, typename Tolerance >
|
||
|
void CheckClose(TestResults& results, Expected const& expected, Actual const& actual, Tolerance const& tolerance,
|
||
|
TestDetails const& details)
|
||
|
{
|
||
|
if (!AreClose(expected, actual, tolerance))
|
||
|
{
|
||
|
UnitTest::MemoryOutStream stream;
|
||
|
stream << "Expected " << expected << " +/- " << tolerance << " but was " << actual;
|
||
|
|
||
|
results.OnTestFailure(details, stream.GetText());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template< typename Expected, typename Actual, typename Tolerance >
|
||
|
void CheckNotClose(TestResults& results, Expected const& expected, Actual const& actual, Tolerance const& tolerance,
|
||
|
TestDetails const& details)
|
||
|
{
|
||
|
if (AreClose(expected, actual, tolerance))
|
||
|
{
|
||
|
UnitTest::MemoryOutStream stream;
|
||
|
stream << "Expected " << expected << " +/- " << tolerance << " but was " << actual;
|
||
|
|
||
|
results.OnTestFailure(details, stream.GetText());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template< typename Expected, typename Actual >
|
||
|
void CheckArrayEqual(TestResults& results, Expected const& expected, Actual const& actual,
|
||
|
size_t const count, TestDetails const& details)
|
||
|
{
|
||
|
bool equal = true;
|
||
|
for (size_t i = 0; i < count; ++i)
|
||
|
equal &= (expected[i] == actual[i]);
|
||
|
|
||
|
if (!equal)
|
||
|
{
|
||
|
UnitTest::MemoryOutStream stream;
|
||
|
|
||
|
stream << "Expected [ ";
|
||
|
|
||
|
for (size_t expectedIndex = 0; expectedIndex < count; ++expectedIndex)
|
||
|
stream << DisplayValue(expected[expectedIndex]) << " ";
|
||
|
|
||
|
stream << "] but was [ ";
|
||
|
|
||
|
for (size_t actualIndex = 0; actualIndex < count; ++actualIndex)
|
||
|
stream << DisplayValue(actual[actualIndex]) << " ";
|
||
|
|
||
|
stream << "]";
|
||
|
|
||
|
results.OnTestFailure(details, stream.GetText());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template< typename Expected, typename Actual, typename Tolerance >
|
||
|
bool ArrayAreClose(Expected const& expected, Actual const& actual, size_t const count, Tolerance const& tolerance)
|
||
|
{
|
||
|
bool equal = true;
|
||
|
for (size_t i = 0; i < count; ++i)
|
||
|
equal &= AreClose(expected[i], actual[i], tolerance);
|
||
|
return equal;
|
||
|
}
|
||
|
|
||
|
template< typename Expected, typename Actual, typename Tolerance >
|
||
|
void CheckArrayClose(TestResults& results, Expected const& expected, Actual const& actual,
|
||
|
size_t const count, Tolerance const& tolerance, TestDetails const& details)
|
||
|
{
|
||
|
bool equal = ArrayAreClose(expected, actual, count, tolerance);
|
||
|
|
||
|
if (!equal)
|
||
|
{
|
||
|
UnitTest::MemoryOutStream stream;
|
||
|
|
||
|
stream << "Expected [ ";
|
||
|
for (size_t expectedIndex = 0; expectedIndex < count; ++expectedIndex)
|
||
|
stream << expected[expectedIndex] << " ";
|
||
|
stream << "] +/- " << tolerance << " but was [ ";
|
||
|
|
||
|
for (size_t actualIndex = 0; actualIndex < count; ++actualIndex)
|
||
|
stream << actual[actualIndex] << " ";
|
||
|
stream << "]";
|
||
|
|
||
|
results.OnTestFailure(details, stream.GetText());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template< typename Expected, typename Actual, typename Tolerance >
|
||
|
void CheckArray2DClose(TestResults& results, Expected const& expected, Actual const& actual,
|
||
|
size_t const rows, size_t const columns, Tolerance const& tolerance, TestDetails const& details)
|
||
|
{
|
||
|
bool equal = true;
|
||
|
for (size_t i = 0; i < rows; ++i)
|
||
|
equal &= ArrayAreClose(expected[i], actual[i], columns, tolerance);
|
||
|
|
||
|
if (!equal)
|
||
|
{
|
||
|
UnitTest::MemoryOutStream stream;
|
||
|
|
||
|
stream << "Expected [ ";
|
||
|
|
||
|
for (size_t expectedRow = 0; expectedRow < rows; ++expectedRow)
|
||
|
{
|
||
|
stream << "[ ";
|
||
|
for (size_t expectedColumn = 0; expectedColumn < columns; ++expectedColumn)
|
||
|
stream << expected[expectedRow][expectedColumn] << " ";
|
||
|
stream << "] ";
|
||
|
}
|
||
|
|
||
|
stream << "] +/- " << tolerance << " but was [ ";
|
||
|
|
||
|
for (size_t actualRow = 0; actualRow < rows; ++actualRow)
|
||
|
{
|
||
|
stream << "[ ";
|
||
|
for (size_t actualColumn = 0; actualColumn < columns; ++actualColumn)
|
||
|
stream << actual[actualRow][actualColumn] << " ";
|
||
|
stream << "] ";
|
||
|
}
|
||
|
|
||
|
stream << "]";
|
||
|
|
||
|
results.OnTestFailure(details, stream.GetText());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
#if defined(__GNUC__) && !defined(__clang__) && !defined(__llvm__)
|
||
|
#pragma GCC diagnostic pop
|
||
|
#endif
|
||
|
|
||
|
#if defined(__clang__) || defined(__llvm__)
|
||
|
#pragma clang diagnostic pop
|
||
|
#endif
|
||
|
|
||
|
#endif
|