Version: SMASH-1.5
unittest.h
Go to the documentation of this file.
1 /* TODO(mkretz): license?
2 
3  Copyright (C) 2009-2014 Matthias Kretz <kretz@kde.org>
4 
5  Vc is free software: you can redistribute it and/or modify
6  it under the terms of the GNU Lesser General Public License as
7  published by the Free Software Foundation, either version 3 of
8  the License, or (at your option) any later version.
9 
10  Vc is distributed in the hope that it will be useful, but
11  WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU Lesser General Public License for more details.
14 
15  You should have received a copy of the GNU Lesser General Public
16  License along with Vc. If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #ifndef SRC_TESTS_UNITTEST_H_
20 #define SRC_TESTS_UNITTEST_H_
21 
22 #include "assert.h"
23 
24 #include <cmath>
25 #include <cstdlib>
26 #include <cstring>
27 #include <functional>
28 #include <iomanip>
29 #include <iostream>
30 #include <limits>
31 #include <memory>
32 #include <sstream>
33 #include <string>
34 #include <tuple>
35 #include <typeinfo>
36 #include <vector>
37 
38 #include "../include/smash/fpenvironment.h"
39 
40 #if defined(__GNUC__) && !defined(_WIN32) && defined(_GLIBCXX_OSTREAM)
41 #define HACK_OSTREAM_FOR_TTY 1
42 #endif
43 
44 #ifdef HACK_OSTREAM_FOR_TTY
45 #include <unistd.h>
46 #include <ext/stdio_sync_filebuf.h>
47 #endif
48 
49 #include "macros.h"
50 #include "ulp.h"
51 
52 #ifdef DOXYGEN
53 
282 #define TEST(function_name)
283 
291 #define TEST_CATCH(function_name, ExceptionType)
292 
300 #define TEST_BEGIN(T, function_name, typelist)
301 
305 #define TEST_END
306 
310 #define VERIFY(condition)
311 
315 #define COMPARE(test_value, reference)
316 
325 #define COMPARE_ABSOLUTE_ERROR(test_value, reference, allowed_difference)
326 
344 #define COMPARE_RELATIVE_ERROR(test_value, reference, \
345  allowed_relative_difference)
346 
393 #define FUZZY_COMPARE(test_value, reference)
394 
398 #define FAIL()
399 
403 #define EXPECT_ASSERT_FAILURE(code)
404 
409 #else
410 
411 namespace UnitTest {
412 
413 using std::get;
414 using std::tuple;
415 using std::vector;
416 
417 namespace AnsiColor {
418 struct Type {
419  const char *data;
420 };
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"};
425 } // namespace AnsiColor
426 
427 #ifdef HACK_OSTREAM_FOR_TTY
428 class hacked_ostream : public std::ostream {
429  public:
430  using std::ostream::_M_streambuf;
431 };
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);
437  if (!hack) {
438  return false;
439  }
440  FILE *file = hack->file();
441  return 1 == isatty(fileno(file));
442 }
443 #else
444 static bool mayUseColor(const std::ostream &) { return false; }
445 #endif
446 
447 static std::ostream &operator<<(std::ostream &s, const AnsiColor::Type &color) {
448  if (mayUseColor(s)) {
449  return s << color.data;
450  } else {
451  return s;
452  }
453 }
454 
455 static inline void printPass() {
456  std::cout << AnsiColor::green << " PASS: " << AnsiColor::normal;
457 }
458 
459 class UnitTestFailure {};
460 
461 using TestFunction = void (*)(void);
462 
463 class UnitTester { // {{{1
464  public:
465  UnitTester()
466  : status(true),
467  expect_failure(false),
468  assert_failure(0),
469  expect_assert_failure(false),
470  float_fuzzyness(1.f),
471  double_fuzzyness(1.),
472  only_name(0),
473  m_finalized(false),
474  failedTests(0),
475  passedTests(0) {}
476 
477  int finalize() {
478  m_finalized = true;
479  std::cout << "\n Testing done. " << passedTests << " tests passed. "
480  << failedTests << " tests failed." << std::endl;
481  return failedTests;
482  }
483 
484  void runTestInt(TestFunction fun, const char *name);
485 
486  bool status;
487  bool expect_failure;
488  int assert_failure;
489  bool expect_assert_failure;
490  float float_fuzzyness;
491  double double_fuzzyness;
492  const char *only_name;
493  bool vim_lines = false;
494 
495  private:
496  bool m_finalized;
497  int failedTests;
498 
499  public:
500  int passedTests;
501 };
502 
503 static UnitTester global_unit_test_object_;
504 #endif // make this visible to doxygen:
505 
524 static inline void EXPECT_FAILURE() { // {{{1
525  global_unit_test_object_.expect_failure = true;
526 }
527 #ifndef DOXYGEN // make the following invisible to DOXYGEN
528 
529 static const char *_unittest_fail() { // {{{1
530  if (global_unit_test_object_.expect_failure) {
531  return "XFAIL: ";
532  }
533  static const char *str = 0;
534  if (str == 0) {
535  if (mayUseColor(std::cout)) {
536  static const char *fail = " \033[1;40;31mFAIL:\033[0m ";
537  str = fail;
538  } else {
539  static const char *fail = " FAIL: ";
540  str = fail;
541  }
542  }
543  return str;
544 }
545 static void initTest(int argc, char **argv) { // {{{1
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";
551  exit(0);
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;
557  }
558  }
559 }
560 // setFuzzyness {{{1
561 template <typename T>
562 static inline void setFuzzyness(T);
563 template <>
564 inline void setFuzzyness<float>(float fuzz) {
565  global_unit_test_object_.float_fuzzyness = fuzz;
566 }
567 template <>
568 inline void setFuzzyness<double>(double fuzz) {
569  global_unit_test_object_.double_fuzzyness = fuzz;
570 }
571 void UnitTester::runTestInt(TestFunction fun, const char *name) { // {{{1
572  if (global_unit_test_object_.only_name &&
573  0 != std::strcmp(name, global_unit_test_object_.only_name)) {
574  return;
575  }
576  global_unit_test_object_.status = true;
577  global_unit_test_object_.expect_failure = false;
578  try {
579  setFuzzyness<float>(1);
580  setFuzzyness<double>(1);
581  fun();
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;
588  } catch (...) {
589  std::cout << _unittest_fail() << "┍ " << name
590  << " threw an unexpected exception, of unknown type\n";
591  global_unit_test_object_.status = false;
592  }
593  if (global_unit_test_object_.expect_failure) {
594  if (!global_unit_test_object_.status) {
595  std::cout << "XFAIL: " << name << std::endl;
596  } else {
597  std::cout
598  << "unexpected PASS: " << name
599  << "\n This test should have failed but didn't. Check the code!"
600  << std::endl;
601  ++failedTests;
602  }
603  } else {
604  if (!global_unit_test_object_.status) {
605  std::cout << _unittest_fail();
606  if (!vim_lines) {
607  std::cout << "┕ ";
608  }
609  std::cout << name << std::endl;
610  if (vim_lines) {
611  std::cout << '\n';
612  }
613  ++failedTests;
614  } else {
615  UnitTest::printPass();
616  std::cout << name;
617  std::cout << std::endl;
618  ++passedTests;
619  }
620  }
621 }
622 
623 // ulpDiffToReferenceWrapper {{{1
624 template <typename T>
625 T ulpDiffToReferenceWrapper(T a, T b) {
626  const T diff = ulpDiffToReference(a, b);
627  return diff;
628 }
629 // unittest_fuzzyCompareHelper {{{1
630 template <typename T>
631 static inline bool unittest_fuzzyCompareHelper(const T &a, const T &b) {
632  return a == b;
633 }
634 template <>
635 inline bool unittest_fuzzyCompareHelper<float>(const float &a, const float &b) {
636  return ulpDiffToReferenceWrapper(a, b) <=
637  global_unit_test_object_.float_fuzzyness;
638 }
639 template <>
640 inline bool unittest_fuzzyCompareHelper<double>(const double &a,
641  const double &b) {
642  return ulpDiffToReferenceWrapper(a, b) <=
643  global_unit_test_object_.double_fuzzyness;
644 }
645 
646 // unitttest_comparePrintHelper {{{1
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) << ") -> "
655  << m;
656  if (fuzzyness > 0.) {
657  std::cout << " with fuzzyness " << fuzzyness;
658  }
659  std::cout << " at " << file << ":" << line << " failed.\n";
660 }
661 
662 // unittest_fuzzynessHelper {{{1
663 template <typename T>
664 inline double unittest_fuzzynessHelper(const T &) {
665  return 0.;
666 }
667 template <>
668 inline double unittest_fuzzynessHelper<float>(const float &) {
669  return global_unit_test_object_.float_fuzzyness;
670 }
671 template <>
672 inline double unittest_fuzzynessHelper<double>(const double &) {
673  return global_unit_test_object_.double_fuzzyness;
674 }
675 
676 class _UnitTest_Compare { // {{{1
677  template <typename T, typename ET>
678  static bool absoluteErrorTest(const T &a, const T &b, ET error) {
679  if (a > b) { // don't use abs(a - b) because it doesn't work for unsigned
680  // integers
681  return a - b > error;
682  } else {
683  return b - a > error;
684  }
685  }
686 
687  template <typename T, typename ET>
688  static bool relativeErrorTest(const T &a, const T &b, ET error) {
689  if (b > 0) {
690  error *= b;
691  } else if (b < 0) {
692  error *= -b;
693  } else if (std::is_floating_point<T>::value) {
694  // if the reference value is 0 then use the smallest normalized number
695  error *= std::numeric_limits<T>::min();
696  } else {
697  // error *= 1; // the smallest non-zero positive number is 1...
698  }
699  if (a > b) { // don't use abs(a - b) because it doesn't work for unsigned
700  // integers
701  return a - b > error;
702  } else {
703  return b - a > error;
704  }
705  }
706 
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)) {};
715 
716  public:
717  struct Fuzzy {};
718  struct AbsoluteError {};
719  struct RelativeError {};
720 
721  // Normal Compare ctor {{{2
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)) {
727  printFirst();
728  printPosition(_file, _line);
729  print(_a);
730  print(" (");
731  print(std::setprecision(10));
732  print(a);
733  print(") == ");
734  print(_b);
735  print(" (");
736  print(std::setprecision(10));
737  print(b);
738  print(std::setprecision(6));
739  print(") -> ");
740  print(a == b);
741  }
742  }
743 
744  // Fuzzy Compare ctor {{{2
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,
748  Fuzzy)
749  : m_ip(getIp()), m_failed(!unittest_fuzzyCompareHelper(a, b)) {
750  if (IS_UNLIKELY(m_failed)) {
751  printFirst();
752  printPosition(_file, _line);
753  print(_a);
754  print(" (");
755  print(std::setprecision(10));
756  print(a);
757  print(") ≈ ");
758  print(_b);
759  print(" (");
760  print(std::setprecision(10));
761  print(b);
762  print(std::setprecision(6));
763  print(") -> ");
764  print(a == b);
765  printFuzzyInfo(a, b);
766  }
767  }
768 
769  // Absolute Error Compare ctor {{{2
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)) {
776  printFirst();
777  printPosition(_file, _line);
778  print(_a);
779  print(" (");
780  print(std::setprecision(10));
781  print(a);
782  print(") ≈ ");
783  print(_b);
784  print(" (");
785  print(std::setprecision(10));
786  print(b);
787  print(std::setprecision(6));
788  print(") -> ");
789  print(a == b);
790  print("\ndifference: ");
791  if (a > b) {
792  print(a - b);
793  } else {
794  print('-');
795  print(b - a);
796  }
797  print(", allowed difference: ±");
798  print(error);
799  print("\ndistance: ");
800  print(ulpDiffToReferenceSigned(a, b));
801  print(" ulp");
802  }
803  }
804 
805  // Relative Error Compare ctor {{{2
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)) {
812  printFirst();
813  printPosition(_file, _line);
814  print(_a);
815  print(" (");
816  print(std::setprecision(10));
817  print(a);
818  print(") ≈ ");
819  print(_b);
820  print(" (");
821  print(std::setprecision(10));
822  print(b);
823  print(std::setprecision(6));
824  print(") -> ");
825  print(a == b);
826  print("\nrelative difference: ");
827  if (a > b) {
828  print((a - b) / (b > 0 ? b : -b));
829  } else {
830  print('-');
831  print((b - a) / (b > 0 ? b : -b));
832  }
833  print(", allowed: ±");
834  print(error);
835  print("\nabsolute difference: ");
836  if (a > b) {
837  print(a - b);
838  } else {
839  print('-');
840  print(b - a);
841  }
842  print(", allowed: ±");
843  print(error * (b > 0 ? b : -b));
844  print("\ndistance: ");
845  print(ulpDiffToReferenceSigned(a, b));
846  print(" ulp");
847  }
848  }
849  // VERIFY ctor {{{2
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)) {
854  printFirst();
855  printPosition(_file, _line);
856  print(cond);
857  }
858  }
859 
860  // FAIL ctor {{{2
861  ALWAYS_INLINE _UnitTest_Compare(const char *_file, int _line)
862  : m_ip(getIp()), m_failed(true) {
863  printFirst();
864  printPosition(_file, _line);
865  }
866 
867  // stream operators {{{2
868  template <typename T>
869  ALWAYS_INLINE const _UnitTest_Compare &operator<<(const T &x) const {
870  if (IS_UNLIKELY(m_failed)) {
871  print(x);
872  }
873  return *this;
874  }
875 
876  ALWAYS_INLINE const _UnitTest_Compare &operator<<(const char *str) const {
877  if (IS_UNLIKELY(m_failed)) {
878  print(str);
879  }
880  return *this;
881  }
882 
883  ALWAYS_INLINE const _UnitTest_Compare &operator<<(const char ch) const {
884  if (IS_UNLIKELY(m_failed)) {
885  print(ch);
886  }
887  return *this;
888  }
889 
890  ALWAYS_INLINE const _UnitTest_Compare &operator<<(bool b) const {
891  if (IS_UNLIKELY(m_failed)) {
892  print(b);
893  }
894  return *this;
895  }
896 
897  ALWAYS_INLINE ~_UnitTest_Compare() // {{{2
898 #ifdef _NO_NOEXCEPT
899  throw(UnitTestFailure)
900 #else
901  noexcept(false)
902 #endif
903  {
904  if (IS_UNLIKELY(m_failed)) {
905  printLast();
906  }
907  }
908 
909  // }}}2
910  private:
911  static ALWAYS_INLINE size_t getIp() { // {{{2
912  size_t _ip;
913 #ifdef __GNUC__
914 #ifdef __x86_64__
915  asm volatile("lea 0(%%rip),%0" : "=r"(_ip));
916 #else
917  // asm volatile("call 1f\n\t1: pop %0" : "=r"(_ip));
918  asm volatile("1: movl $1b,%0" : "=r"(_ip));
919 #endif
920 #else
921  _ip = 0;
922 #endif
923  return _ip;
924  }
925  static char hexChar(char x) { return x + (x > 9 ? 87 : 48); }
926  template <typename T>
927  static void printMem(const T &x) // {{{2
928  {
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';
933  s[length] = '\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);
938  }
939  std::cout << s.get();
940  }
941  static void printFirst() { // {{{2
942  if (!global_unit_test_object_.vim_lines) {
943  std::cout << _unittest_fail() << "┍ ";
944  }
945  }
946  // print overloads {{{2
947  template <typename T>
948  static inline
949  typename std::enable_if<has_ostream_operator<T>::value, void>::type
950  print(const T &x) {
951  std::cout << x;
952  }
953  template <typename T>
954  static inline
955  typename std::enable_if<!has_ostream_operator<T>::value, void>::type
956  print(const T &x) {
957  printMem(x);
958  }
959  static void print(const std::type_info &x) { std::cout << x.name(); }
960  static void print(const char *str) {
961  const char *pos = 0;
962  if (0 != (pos = std::strchr(str, '\n'))) {
963  if (pos == str) {
964  std::cout << '\n' << _unittest_fail();
965  if (!global_unit_test_object_.vim_lines) {
966  std::cout << "│ ";
967  }
968  print(&str[1]);
969  } else {
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) {
974  std::cout << "│ ";
975  }
976  free(left);
977  print(&pos[1]);
978  }
979  } else {
980  std::cout << str;
981  }
982  }
983  static void print(const char ch) {
984  if (ch == '\n') {
985  std::cout << '\n' << _unittest_fail();
986  if (!global_unit_test_object_.vim_lines) {
987  std::cout << "│ ";
988  }
989  } else {
990  std::cout << ch;
991  }
992  }
993  static void print(bool b) { std::cout << (b ? "true" : "false"); }
994  // printLast {{{2
995  static void printLast() {
996  std::cout << std::endl;
997  global_unit_test_object_.status = false;
998  throw UnitTestFailure();
999  }
1000  // printPosition {{{2
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 << "): ";
1005  } else {
1006  std::cout << "at: " << _file << ':' << _line << " (0x" << std::hex << m_ip
1007  << std::dec;
1008  print("):\n");
1009  }
1010  }
1011  // printFuzzy... {{{2
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: ±");
1019  print(fuzzyness);
1020  print(" ulp");
1021  }
1022  // member variables {{{2
1023  const size_t m_ip;
1024  const bool m_failed;
1025 };
1026 // printFuzzyInfo specializations for float and double {{{1
1027 template <>
1028 inline void _UnitTest_Compare::printFuzzyInfo(float a, float b) {
1029  printFuzzyInfoImpl(a, b, global_unit_test_object_.float_fuzzyness);
1030 }
1031 template <>
1032 inline void _UnitTest_Compare::printFuzzyInfo(double a, double b) {
1033  printFuzzyInfoImpl(a, b, global_unit_test_object_.double_fuzzyness);
1034 }
1035 
1036 // FUZZY_COMPARE {{{1
1037 // Workaround for clang: The "<< ' '" is only added to silence the warnings
1038 // about unused return values.
1039 #define FUZZY_COMPARE(a, b) \
1040  UnitTest::_UnitTest_Compare(a, b, #a, #b, __FILE__, __LINE__, \
1041  UnitTest::_UnitTest_Compare::Fuzzy()) \
1042  << ' '
1043 // COMPARE_ABSOLUTE_ERROR {{{1
1044 #define COMPARE_ABSOLUTE_ERROR(a__, b__, error__) \
1045  UnitTest::_UnitTest_Compare(a__, b__, #a__, #b__, __FILE__, __LINE__, \
1046  UnitTest::_UnitTest_Compare::AbsoluteError(), \
1047  error__) \
1048  << ' '
1049 // COMPARE_RELATIVE_ERROR {{{1
1050 #define COMPARE_RELATIVE_ERROR(a__, b__, error__) \
1051  UnitTest::_UnitTest_Compare(a__, b__, #a__, #b__, __FILE__, __LINE__, \
1052  UnitTest::_UnitTest_Compare::RelativeError(), \
1053  error__) \
1054  << ' '
1055 // COMPARE {{{1
1056 #define COMPARE(a, b) \
1057  UnitTest::_UnitTest_Compare(a, b, #a, #b, __FILE__, __LINE__) << ' '
1058 // VERIFY {{{1
1059 #define VERIFY(cond) \
1060  UnitTest::_UnitTest_Compare(cond, #cond, __FILE__, __LINE__) << ' '
1061 // FAIL {{{1
1062 #define FAIL() UnitTest::_UnitTest_Compare(__FILE__, __LINE__) << ' '
1063 
1064 // ADD_PASS() << "text" {{{1
1065 class ADD_PASS {
1066  public:
1067  ADD_PASS() {
1068  ++global_unit_test_object_.passedTests;
1069  printPass();
1070  }
1071  ~ADD_PASS() { std::cout << std::endl; }
1072  template <typename T>
1073  ADD_PASS &operator<<(const T &x) {
1074  std::cout << x;
1075  return *this;
1076  }
1077 };
1078 // unittest_assert (called from assert macro) {{{1
1079 void unittest_assert(bool cond, const char *code, const char *file, int line) {
1080  if (!cond) {
1081  if (global_unit_test_object_.expect_assert_failure) {
1082  ++global_unit_test_object_.assert_failure;
1083  } else {
1084  _UnitTest_Compare(file, line) << "assert(" << code << ") failed.";
1085  }
1086  }
1087 }
1088 // EXPECT_ASSERT_FAILURE {{{1
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; \
1092  code; \
1093  if (UnitTest::global_unit_test_object_.assert_failure == 0) { \
1094  /* failure expected but it didn't fail */ \
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(); \
1099  return; \
1100  } \
1101  UnitTest::global_unit_test_object_.expect_assert_failure = false
1102 } // namespace UnitTest
1103 namespace UnitTest { // {{{1
1104 // typeToString {{{2
1105 template <typename T>
1106 inline std::string typeToString();
1107 
1108 template <typename T>
1109 inline std::string typeToString_impl(T) {
1110  return typeid(T).name();
1111 }
1112 
1113 template <typename T>
1114 inline std::string typeToString() {
1115  return typeToString_impl(T());
1116 }
1117 template <>
1118 inline std::string typeToString<void>() {
1119  return "";
1120 }
1121 
1122 template <>
1123 inline std::string typeToString<long double>() {
1124  return "long double";
1125 }
1126 template <>
1127 inline std::string typeToString<double>() {
1128  return "double";
1129 }
1130 template <>
1131 inline std::string typeToString<float>() {
1132  return " float";
1133 }
1134 /* NOLINT(runtime/int):
1135  * The warning is disabled here because this code is not about using integers
1136  * but about recognizing integer types. And the following types are the
1137  * different builtin types. It doesn't make sense to check for aliased types
1138  * here.
1139  */
1140 template <>
1141 inline std::string typeToString<long long>() { // NOLINT(runtime/int)
1142  return " long long";
1143 }
1144 template <>
1145 inline std::string typeToString<unsigned long long>() { // NOLINT(runtime/int)
1146  return "ulong long";
1147 }
1148 template <>
1149 inline std::string typeToString<long>() { // NOLINT(runtime/int)
1150  return " long";
1151 }
1152 template <>
1153 inline std::string typeToString<unsigned long>() { // NOLINT(runtime/int)
1154  return " ulong";
1155 }
1156 template <>
1157 inline std::string typeToString<int>() {
1158  return " int";
1159 }
1160 template <>
1161 inline std::string typeToString<unsigned int>() {
1162  return " uint";
1163 }
1164 template <>
1165 inline std::string typeToString<short>() { // NOLINT(runtime/int)
1166  return " short";
1167 }
1168 template <>
1169 inline std::string typeToString<unsigned short>() { // NOLINT(runtime/int)
1170  return "ushort";
1171 }
1172 template <>
1173 inline std::string typeToString<char>() {
1174  return " char";
1175 }
1176 template <>
1177 inline std::string typeToString<unsigned char>() {
1178  return " uchar";
1179 }
1180 template <>
1181 inline std::string typeToString<signed char>() {
1182  return " schar";
1183 }
1184 // runAll and TestData {{{2
1185 typedef tuple<TestFunction, std::string> TestData;
1186 vector<TestData> g_allTests;
1187 
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());
1191  }
1192 }
1193 // class Test {{{2
1194 template <typename T, typename Exception = void, typename TestImpl = void>
1195 class Test : TestImpl {
1196  private:
1197  static void wrapper() {
1198  try {
1199  TestImpl::test_function();
1200  } catch (Exception &e) {
1201  return;
1202  }
1203  FAIL() << "Test was expected to throw, but it didn't";
1204  }
1205 
1206  public:
1207  Test(std::string name) {
1208  if (!std::is_same<T, void>()) {
1209  name += '<' + typeToString<T>() + '>';
1210  }
1211  g_allTests.emplace_back(wrapper, name);
1212  }
1213 };
1214 template <typename T>
1215 class Test<T, void, void> {
1216  public:
1217  Test(TestFunction fun, std::string name) {
1218  if (!std::is_same<T, void>()) {
1219  name += '<' + typeToString<T>() + '>';
1220  }
1221  g_allTests.emplace_back(fun, name);
1222  }
1223 };
1224 
1225 // class Test2 {{{2
1226 template <template <typename V> class TestFunctor, typename... TestTypes>
1227 class Test2;
1228 
1229 template <template <typename V> class TestFunctor>
1230 class Test2<TestFunctor> {
1231  protected:
1232  explicit Test2(const std::string &) {}
1233 };
1234 
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;
1240 
1241  public:
1242  static void call() { TestFunctor<TestType0>()(); }
1243 
1244  explicit Test2(std::string name) : Base(name) {
1245  name += '<' + typeToString<TestType0>() + '>';
1246  g_allTests.emplace_back(&call, name);
1247  }
1248 };
1249 // hackTypelist {{{2
1250 template <template <typename V> class F, typename... Typelist>
1251 UnitTest::Test2<F, Typelist...> hackTypelist(void (*)(Typelist...));
1252 } // namespace UnitTest
1253 // TEST_BEGIN / TEST_END / TEST macros {{{1
1254 #define TEST_BEGIN(V__, fun__, typelist__) \
1255  template <typename V__> \
1256  struct fun__; \
1257  static auto test_##fun__##__ = decltype( \
1258  UnitTest::hackTypelist<fun__>(std::declval<void typelist__>()))(#fun__); \
1259  template <typename V__> \
1260  struct fun__ { \
1261  void operator()() {
1262 #define TEST_END \
1263  } \
1264  } \
1265  ;
1266 
1267 #define TEST(fun__) \
1268  void fun__(); \
1269  static UnitTest::Test<void> test_##fun__##__(&fun__, #fun__); \
1270  void fun__()
1271 
1272 #define TEST_CATCH(fun__, exception__) \
1273  struct fun__ { \
1274  static void test_function(); \
1275  }; \
1276  static UnitTest::Test<void, exception__, fun__> test_##fun__##__(#fun__); \
1277  void fun__::test_function()
1278 
1279 // main {{{1
1280 int main(int argc, char **argv) {
1282  UnitTest::initTest(argc, argv);
1283  UnitTest::runAll();
1284  return UnitTest::global_unit_test_object_.finalize();
1285 }
1286 // }}}1
1287 #endif // DOXYGEN
1288 #endif // SRC_TESTS_UNITTEST_H_
1289 
1290 // vim: foldmethod=marker sw=2
int main(int argc, char *argv[])
Main program Smashes Many Accelerated Strongly-Interacting Hadrons :-)
Definition: smash.cc:333
void setup_default_float_traps()
Setup the floating-point traps used throughout SMASH.
#define FAIL()
Call this to fail a test.
Definition: unittest.h:398
static void EXPECT_FAILURE()
Use this to mark that the failure of a following test is expected.
Definition: unittest.h:524
double normal(const T &mean, const T &sigma)
Returns a random number drawn from a normal distribution.
Definition: random.h:247
std::ostream & operator<<(std::ostream &out, const ActionPtr &action)
Convenience: dereferences the ActionPtr to Action.
Definition: action.h:457