19 #ifndef SRC_TESTS_UNITTEST_H_ 20 #define SRC_TESTS_UNITTEST_H_ 38 #include "../include/smash/fpenvironment.h" 40 #if defined(__GNUC__) && !defined(_WIN32) && defined(_GLIBCXX_OSTREAM) 41 #define HACK_OSTREAM_FOR_TTY 1 44 #ifdef HACK_OSTREAM_FOR_TTY 46 #include <ext/stdio_sync_filebuf.h> 282 #define TEST(function_name) 291 #define TEST_CATCH(function_name, ExceptionType) 300 #define TEST_BEGIN(T, function_name, typelist) 310 #define VERIFY(condition) 315 #define COMPARE(test_value, reference) 325 #define COMPARE_ABSOLUTE_ERROR(test_value, reference, allowed_difference) 344 #define COMPARE_RELATIVE_ERROR(test_value, reference, \ 345 allowed_relative_difference) 393 #define FUZZY_COMPARE(test_value, reference) 403 #define EXPECT_ASSERT_FAILURE(code) 417 namespace AnsiColor {
421 static const Type green = {
"\033[1;40;32m"};
422 static const Type yellow = {
"\033[1;40;33m"};
423 static const Type blue = {
"\033[1;40;34m"};
424 static const Type
normal = {
"\033[0m"};
427 #ifdef HACK_OSTREAM_FOR_TTY 428 class hacked_ostream :
public std::ostream {
430 using std::ostream::_M_streambuf;
432 static __attribute__((__const__))
bool mayUseColor(
const std::ostream &os) {
433 std::basic_streambuf<char> *hack1 =
const_cast<std::basic_streambuf<char> *
>(
434 os.*(&hacked_ostream::_M_streambuf));
435 __gnu_cxx::stdio_sync_filebuf<char> *hack =
436 dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char> *
>(hack1);
440 FILE *file = hack->file();
441 return 1 == isatty(fileno(file));
444 static bool mayUseColor(
const std::ostream &) {
return false; }
447 static std::ostream &
operator<<(std::ostream &s,
const AnsiColor::Type &color) {
448 if (mayUseColor(s)) {
449 return s << color.data;
455 static inline void printPass() {
459 class UnitTestFailure {};
461 using TestFunction = void (*)(void);
467 expect_failure(
false),
469 expect_assert_failure(
false),
470 float_fuzzyness(1.f),
471 double_fuzzyness(1.),
479 std::cout <<
"\n Testing done. " << passedTests <<
" tests passed. " 480 << failedTests <<
" tests failed." << std::endl;
484 void runTestInt(TestFunction fun,
const char *name);
489 bool expect_assert_failure;
490 float float_fuzzyness;
491 double double_fuzzyness;
492 const char *only_name;
493 bool vim_lines =
false;
503 static UnitTester global_unit_test_object_;
504 #endif // make this visible to doxygen: 525 global_unit_test_object_.expect_failure =
true;
527 #ifndef DOXYGEN // make the following invisible to DOXYGEN 529 static const char *_unittest_fail() {
530 if (global_unit_test_object_.expect_failure) {
533 static const char *str = 0;
535 if (mayUseColor(std::cout)) {
536 static const char *fail =
" \033[1;40;31mFAIL:\033[0m ";
539 static const char *fail =
" FAIL: ";
545 static void initTest(
int argc,
char **argv) {
546 for (
int i = 1; i < argc; ++i) {
547 if (0 == std::strcmp(argv[i],
"--help") ||
548 0 == std::strcmp(argv[i],
"-h")) {
549 std::cout <<
"Usage: " << argv[0]
550 <<
" [-h|--help] [--only <testname>] [-v|--vim]\n";
552 }
else if (0 == std::strcmp(argv[i],
"--only") && i + 1 < argc) {
553 global_unit_test_object_.only_name = argv[i + 1];
554 }
else if (0 == std::strcmp(argv[i],
"--vim") ||
555 0 == std::strcmp(argv[i],
"-v")) {
556 global_unit_test_object_.vim_lines =
true;
561 template <
typename T>
562 static inline void setFuzzyness(T);
564 inline void setFuzzyness<float>(
float fuzz) {
565 global_unit_test_object_.float_fuzzyness = fuzz;
568 inline void setFuzzyness<double>(
double fuzz) {
569 global_unit_test_object_.double_fuzzyness = fuzz;
571 void UnitTester::runTestInt(TestFunction fun,
const char *name) {
572 if (global_unit_test_object_.only_name &&
573 0 != std::strcmp(name, global_unit_test_object_.only_name)) {
576 global_unit_test_object_.status =
true;
577 global_unit_test_object_.expect_failure =
false;
579 setFuzzyness<float>(1);
580 setFuzzyness<double>(1);
582 }
catch (UnitTestFailure) {
583 }
catch (std::exception &e) {
584 std::cout << _unittest_fail() <<
"┍ " << name
585 <<
" threw an unexpected exception:\n";
586 std::cout << _unittest_fail() <<
"│ " << e.what() <<
'\n';
587 global_unit_test_object_.status =
false;
589 std::cout << _unittest_fail() <<
"┍ " << name
590 <<
" threw an unexpected exception, of unknown type\n";
591 global_unit_test_object_.status =
false;
593 if (global_unit_test_object_.expect_failure) {
594 if (!global_unit_test_object_.status) {
595 std::cout <<
"XFAIL: " << name << std::endl;
598 <<
"unexpected PASS: " << name
599 <<
"\n This test should have failed but didn't. Check the code!" 604 if (!global_unit_test_object_.status) {
605 std::cout << _unittest_fail();
609 std::cout << name << std::endl;
615 UnitTest::printPass();
617 std::cout << std::endl;
624 template <
typename T>
625 T ulpDiffToReferenceWrapper(T a, T b) {
626 const T diff = ulpDiffToReference(a, b);
630 template <
typename T>
631 static inline bool unittest_fuzzyCompareHelper(
const T &a,
const T &b) {
635 inline bool unittest_fuzzyCompareHelper<float>(
const float &a,
const float &b) {
636 return ulpDiffToReferenceWrapper(a, b) <=
637 global_unit_test_object_.float_fuzzyness;
640 inline bool unittest_fuzzyCompareHelper<double>(
const double &a,
642 return ulpDiffToReferenceWrapper(a, b) <=
643 global_unit_test_object_.double_fuzzyness;
647 template <
typename T1,
typename T2,
typename M>
648 inline void unitttest_comparePrintHelper(
const T1 &a,
const T2 &b,
const M &m,
649 const char *aa,
const char *bb,
650 const char *file,
int line,
651 double fuzzyness = 0.) {
652 std::cout <<
" " << aa <<
" (" << std::setprecision(10) << a
653 << std::setprecision(6) <<
") == " << bb <<
" (" 654 << std::setprecision(10) << b << std::setprecision(6) <<
") -> " 656 if (fuzzyness > 0.) {
657 std::cout <<
" with fuzzyness " << fuzzyness;
659 std::cout <<
" at " << file <<
":" << line <<
" failed.\n";
663 template <
typename T>
664 inline double unittest_fuzzynessHelper(
const T &) {
668 inline double unittest_fuzzynessHelper<float>(
const float &) {
669 return global_unit_test_object_.float_fuzzyness;
672 inline double unittest_fuzzynessHelper<double>(
const double &) {
673 return global_unit_test_object_.double_fuzzyness;
676 class _UnitTest_Compare {
677 template <
typename T,
typename ET>
678 static bool absoluteErrorTest(
const T &a,
const T &b, ET error) {
681 return a - b > error;
683 return b - a > error;
687 template <
typename T,
typename ET>
688 static bool relativeErrorTest(
const T &a,
const T &b, ET error) {
693 }
else if (std::is_floating_point<T>::value) {
695 error *= std::numeric_limits<T>::min();
701 return a - b > error;
703 return b - a > error;
707 template <
typename T,
typename = decltype(std::declval<std::ostream &>()
708 << std::declval<T>())>
709 static std::true_type has_ostream_operator_impl(
int);
710 template <
typename T>
711 static std::false_type has_ostream_operator_impl(...);
712 template <
typename T>
713 struct has_ostream_operator
714 :
public decltype(has_ostream_operator_impl<T>(1)) {};
718 struct AbsoluteError {};
719 struct RelativeError {};
722 template <
typename T1,
typename T2>
723 ALWAYS_INLINE _UnitTest_Compare(
const T1 &a,
const T2 &b,
const char *_a,
724 const char *_b,
const char *_file,
int _line)
725 : m_ip(getIp()), m_failed(!(a == b)) {
726 if (IS_UNLIKELY(m_failed)) {
728 printPosition(_file, _line);
731 print(std::setprecision(10));
736 print(std::setprecision(10));
738 print(std::setprecision(6));
745 template <
typename T>
746 ALWAYS_INLINE _UnitTest_Compare(
const T &a,
const T &b,
const char *_a,
747 const char *_b,
const char *_file,
int _line,
749 : m_ip(getIp()), m_failed(!unittest_fuzzyCompareHelper(a, b)) {
750 if (IS_UNLIKELY(m_failed)) {
752 printPosition(_file, _line);
755 print(std::setprecision(10));
760 print(std::setprecision(10));
762 print(std::setprecision(6));
765 printFuzzyInfo(a, b);
770 template <
typename T,
typename ET>
771 ALWAYS_INLINE _UnitTest_Compare(
const T &a,
const T &b,
const char *_a,
772 const char *_b,
const char *_file,
int _line,
773 AbsoluteError, ET error)
774 : m_ip(getIp()), m_failed(absoluteErrorTest(a, b, error)) {
775 if (IS_UNLIKELY(m_failed)) {
777 printPosition(_file, _line);
780 print(std::setprecision(10));
785 print(std::setprecision(10));
787 print(std::setprecision(6));
790 print(
"\ndifference: ");
797 print(
", allowed difference: ±");
799 print(
"\ndistance: ");
800 print(ulpDiffToReferenceSigned(a, b));
806 template <
typename T,
typename ET>
807 ALWAYS_INLINE _UnitTest_Compare(
const T &a,
const T &b,
const char *_a,
808 const char *_b,
const char *_file,
int _line,
809 RelativeError, ET error)
810 : m_ip(getIp()), m_failed(relativeErrorTest(a, b, error)) {
811 if (IS_UNLIKELY(m_failed)) {
813 printPosition(_file, _line);
816 print(std::setprecision(10));
821 print(std::setprecision(10));
823 print(std::setprecision(6));
826 print(
"\nrelative difference: ");
828 print((a - b) / (b > 0 ? b : -b));
831 print((b - a) / (b > 0 ? b : -b));
833 print(
", allowed: ±");
835 print(
"\nabsolute difference: ");
842 print(
", allowed: ±");
843 print(error * (b > 0 ? b : -b));
844 print(
"\ndistance: ");
845 print(ulpDiffToReferenceSigned(a, b));
850 ALWAYS_INLINE _UnitTest_Compare(
bool good,
const char *cond,
851 const char *_file,
int _line)
852 : m_ip(getIp()), m_failed(!good) {
853 if (IS_UNLIKELY(m_failed)) {
855 printPosition(_file, _line);
861 ALWAYS_INLINE _UnitTest_Compare(
const char *_file,
int _line)
862 : m_ip(getIp()), m_failed(
true) {
864 printPosition(_file, _line);
868 template <
typename T>
869 ALWAYS_INLINE
const _UnitTest_Compare &
operator<<(
const T &x)
const {
870 if (IS_UNLIKELY(m_failed)) {
876 ALWAYS_INLINE
const _UnitTest_Compare &
operator<<(
const char *str)
const {
877 if (IS_UNLIKELY(m_failed)) {
883 ALWAYS_INLINE
const _UnitTest_Compare &
operator<<(
const char ch)
const {
884 if (IS_UNLIKELY(m_failed)) {
890 ALWAYS_INLINE
const _UnitTest_Compare &
operator<<(
bool b)
const {
891 if (IS_UNLIKELY(m_failed)) {
897 ALWAYS_INLINE ~_UnitTest_Compare()
899 throw(UnitTestFailure)
904 if (IS_UNLIKELY(m_failed)) {
911 static ALWAYS_INLINE
size_t getIp() {
915 asm volatile(
"lea 0(%%rip),%0" :
"=r"(_ip));
918 asm volatile(
"1: movl $1b,%0" :
"=r"(_ip));
925 static char hexChar(
char x) {
return x + (x > 9 ? 87 : 48); }
926 template <
typename T>
927 static void printMem(
const T &x)
929 constexpr std::size_t length =
sizeof(T) * 2 +
sizeof(T) / 4;
930 std::unique_ptr<char[]> s{
new char[length + 1]};
931 std::memset(s.get(),
'\'', length - 1);
932 s[length - 1] =
'\0';
934 const auto bytes =
reinterpret_cast<const std::uint8_t *
>(&x);
935 for (std::size_t i = 0; i <
sizeof(T); ++i) {
936 s[i * 2 + i / 4] = hexChar(bytes[i] >> 4);
937 s[i * 2 + 1 + i / 4] = hexChar(bytes[i] & 0xf);
939 std::cout << s.get();
941 static void printFirst() {
942 if (!global_unit_test_object_.vim_lines) {
943 std::cout << _unittest_fail() <<
"┍ ";
947 template <
typename T>
949 typename std::enable_if<has_ostream_operator<T>::value,
void>::type
953 template <
typename T>
955 typename std::enable_if<!has_ostream_operator<T>::value,
void>::type
959 static void print(
const std::type_info &x) { std::cout << x.name(); }
960 static void print(
const char *str) {
962 if (0 != (pos = std::strchr(str,
'\n'))) {
964 std::cout <<
'\n' << _unittest_fail();
965 if (!global_unit_test_object_.vim_lines) {
970 char *left = strdup(str);
971 left[pos - str] =
'\0';
972 std::cout << left <<
'\n' << _unittest_fail();
973 if (!global_unit_test_object_.vim_lines) {
983 static void print(
const char ch) {
985 std::cout <<
'\n' << _unittest_fail();
986 if (!global_unit_test_object_.vim_lines) {
993 static void print(
bool b) { std::cout << (b ?
"true" :
"false"); }
995 static void printLast() {
996 std::cout << std::endl;
997 global_unit_test_object_.status =
false;
998 throw UnitTestFailure();
1001 void printPosition(
const char *_file,
int _line) {
1002 if (global_unit_test_object_.vim_lines) {
1003 std::cout << _file <<
':' << _line <<
": (0x" << std::hex << m_ip
1004 << std::dec <<
"): ";
1006 std::cout <<
"at: " << _file <<
':' << _line <<
" (0x" << std::hex << m_ip
1012 template <
typename T>
1013 static inline void printFuzzyInfo(T, T) {}
1014 template <
typename T>
1015 static inline void printFuzzyInfoImpl(T a, T b,
double fuzzyness) {
1016 print(
"\ndistance: ");
1017 print(ulpDiffToReferenceSigned(a, b));
1018 print(
" ulp, allowed distance: ±");
1024 const bool m_failed;
1028 inline void _UnitTest_Compare::printFuzzyInfo(
float a,
float b) {
1029 printFuzzyInfoImpl(a, b, global_unit_test_object_.float_fuzzyness);
1032 inline void _UnitTest_Compare::printFuzzyInfo(
double a,
double b) {
1033 printFuzzyInfoImpl(a, b, global_unit_test_object_.double_fuzzyness);
1039 #define FUZZY_COMPARE(a, b) \ 1040 UnitTest::_UnitTest_Compare(a, b, #a, #b, __FILE__, __LINE__, \ 1041 UnitTest::_UnitTest_Compare::Fuzzy()) \ 1044 #define COMPARE_ABSOLUTE_ERROR(a__, b__, error__) \ 1045 UnitTest::_UnitTest_Compare(a__, b__, #a__, #b__, __FILE__, __LINE__, \ 1046 UnitTest::_UnitTest_Compare::AbsoluteError(), \ 1050 #define COMPARE_RELATIVE_ERROR(a__, b__, error__) \ 1051 UnitTest::_UnitTest_Compare(a__, b__, #a__, #b__, __FILE__, __LINE__, \ 1052 UnitTest::_UnitTest_Compare::RelativeError(), \ 1056 #define COMPARE(a, b) \ 1057 UnitTest::_UnitTest_Compare(a, b, #a, #b, __FILE__, __LINE__) << ' ' 1059 #define VERIFY(cond) \ 1060 UnitTest::_UnitTest_Compare(cond, #cond, __FILE__, __LINE__) << ' ' 1062 #define FAIL() UnitTest::_UnitTest_Compare(__FILE__, __LINE__) << ' ' 1068 ++global_unit_test_object_.passedTests;
1071 ~ADD_PASS() { std::cout << std::endl; }
1072 template <
typename T>
1079 void unittest_assert(
bool cond,
const char *code,
const char *file,
int line) {
1081 if (global_unit_test_object_.expect_assert_failure) {
1082 ++global_unit_test_object_.assert_failure;
1084 _UnitTest_Compare(file, line) <<
"assert(" << code <<
") failed.";
1089 #define EXPECT_ASSERT_FAILURE(code) \ 1090 UnitTest::global_unit_test_object_.expect_assert_failure = true; \ 1091 UnitTest::global_unit_test_object_.assert_failure = 0; \ 1093 if (UnitTest::global_unit_test_object_.assert_failure == 0) { \ 1095 std::cout << " " << #code << " at " << __FILE__ << ":" << __LINE__ \ 1096 << " did not fail as was expected.\n"; \ 1097 UnitTest::global_unit_test_object_.status = false; \ 1098 throw UnitTest::UnitTestFailure(); \ 1101 UnitTest::global_unit_test_object_.expect_assert_failure = false 1103 namespace UnitTest {
1105 template <
typename T>
1106 inline std::string typeToString();
1108 template <
typename T>
1109 inline std::string typeToString_impl(T) {
1110 return typeid(T).name();
1113 template <
typename T>
1114 inline std::string typeToString() {
1115 return typeToString_impl(T());
1118 inline std::string typeToString<void>() {
1123 inline std::string typeToString<long double>() {
1124 return "long double";
1127 inline std::string typeToString<double>() {
1131 inline std::string typeToString<float>() {
1141 inline std::string typeToString<long long>() {
1142 return " long long";
1145 inline std::string typeToString<unsigned long long>() {
1146 return "ulong long";
1149 inline std::string typeToString<long>() {
1153 inline std::string typeToString<unsigned long>() {
1157 inline std::string typeToString<int>() {
1161 inline std::string typeToString<unsigned int>() {
1165 inline std::string typeToString<short>() {
1169 inline std::string typeToString<unsigned short>() {
1173 inline std::string typeToString<char>() {
1177 inline std::string typeToString<unsigned char>() {
1181 inline std::string typeToString<signed char>() {
1185 typedef tuple<TestFunction, std::string> TestData;
1186 vector<TestData> g_allTests;
1188 static void runAll() {
1189 for (
const auto &data : g_allTests) {
1190 global_unit_test_object_.runTestInt(get<0>(data), get<1>(data).c_str());
1194 template <
typename T,
typename Exception =
void,
typename TestImpl =
void>
1195 class Test : TestImpl {
1197 static void wrapper() {
1199 TestImpl::test_function();
1200 }
catch (Exception &e) {
1203 FAIL() <<
"Test was expected to throw, but it didn't";
1207 Test(std::string name) {
1208 if (!std::is_same<T, void>()) {
1209 name +=
'<' + typeToString<T>() +
'>';
1211 g_allTests.emplace_back(wrapper, name);
1214 template <
typename T>
1215 class Test<T, void, void> {
1217 Test(TestFunction fun, std::string name) {
1218 if (!std::is_same<T, void>()) {
1219 name +=
'<' + typeToString<T>() +
'>';
1221 g_allTests.emplace_back(fun, name);
1226 template <
template <
typename V>
class TestFunctor,
typename... TestTypes>
1229 template <
template <
typename V>
class TestFunctor>
1230 class Test2<TestFunctor> {
1232 explicit Test2(
const std::string &) {}
1235 template <
template <
typename V>
class TestFunctor,
typename TestType0,
1236 typename... TestTypes>
1237 class Test2<TestFunctor, TestType0, TestTypes...>
1238 :
public Test2<TestFunctor, TestTypes...> {
1239 typedef Test2<TestFunctor, TestTypes...> Base;
1242 static void call() { TestFunctor<TestType0>()(); }
1244 explicit Test2(std::string name) : Base(name) {
1245 name +=
'<' + typeToString<TestType0>() +
'>';
1246 g_allTests.emplace_back(&call, name);
1250 template <
template <
typename V>
class F,
typename... Typelist>
1251 UnitTest::Test2<F, Typelist...> hackTypelist(
void (*)(Typelist...));
1254 #define TEST_BEGIN(V__, fun__, typelist__) \ 1255 template <typename V__> \ 1257 static auto test_##fun__##__ = decltype( \ 1258 UnitTest::hackTypelist<fun__>(std::declval<void typelist__>()))(#fun__); \ 1259 template <typename V__> \ 1267 #define TEST(fun__) \ 1269 static UnitTest::Test<void> test_##fun__##__(&fun__, #fun__); \ 1272 #define TEST_CATCH(fun__, exception__) \ 1274 static void test_function(); \ 1276 static UnitTest::Test<void, exception__, fun__> test_##fun__##__(#fun__); \ 1277 void fun__::test_function() 1280 int main(
int argc,
char **argv) {
1282 UnitTest::initTest(argc, argv);
1284 return UnitTest::global_unit_test_object_.finalize();
1288 #endif // SRC_TESTS_UNITTEST_H_ int main(int argc, char *argv[])
Main program Smashes Many Accelerated Strongly-Interacting Hadrons :-)
void setup_default_float_traps()
Setup the floating-point traps used throughout SMASH.
#define FAIL()
Call this to fail a test.
static void EXPECT_FAILURE()
Use this to mark that the failure of a following test is expected.
double normal(const T &mean, const T &sigma)
Returns a random number drawn from a normal distribution.
std::ostream & operator<<(std::ostream &out, const ActionPtr &action)
Convenience: dereferences the ActionPtr to Action.