1 #ifndef TEST_SUPPORT_VERBOSE_ASSERT 2 #define TEST_SUPPORT_VERBOSE_ASSERT 3 4 #include <iostream> 5 #include <cstdio> 6 #include <sstream> 7 #include <string> 8 #include "test_macros.h" 9 10 namespace verbose_assert { 11 12 typedef std::basic_ostream<char>&(EndLType)(std::basic_ostream<char>&); 13 14 template <class Stream, class Tp, 15 class = decltype(std::declval<Stream&>() << std::declval<Tp const&>())> 16 std::true_type IsStreamableImp(int); 17 template <class Stream, class Tp> std::false_type IsStreamableImp(long); 18 19 template <class Stream, class Tp> 20 struct IsStreamable : decltype(IsStreamableImp<Stream, Tp>(0)) {}; 21 22 template <class Tp, int ST = (IsStreamable<decltype(std::cerr), Tp>::value ? 1 23 : (IsStreamable<decltype(std::wcerr), Tp>::value ? 2 : -1))> 24 struct SelectStream { 25 static_assert(ST == -1, "specialization required for ST != -1"); PrintSelectStream26 static void Print(Tp const&) { std::clog << "Value Not Streamable!\n"; } 27 }; 28 29 template <class Tp> 30 struct SelectStream<Tp, 1> { 31 static void Print(Tp const& val) { std::cerr << val; } 32 }; 33 34 template <class Tp> 35 struct SelectStream<Tp, 2> { 36 static void Print(Tp const& val) { std::wcerr << val; } 37 }; 38 39 struct AssertData { 40 AssertData(const char* xcheck, const char* xfile, const char* xfunc, 41 unsigned long xline, bool xpassed = true) 42 : passed(xpassed), check(xcheck), file(xfile), func(xfunc), line(xline), 43 msg() {} 44 45 AssertData& SetFailed(std::string xmsg = std::string()) { 46 msg = xmsg; 47 passed = false; 48 return *this; 49 } 50 51 void PrintFailed() const { 52 std::fprintf(stderr, "%s:%lu %s: Assertion '%s' failed.\n", file, line, 53 func, check); 54 if (!msg.empty()) 55 std::fprintf(stderr, "%s\n", msg.data()); 56 } 57 58 bool passed; 59 const char* check; 60 const char* file; 61 const char* func; 62 unsigned long line; 63 std::string msg; 64 }; 65 66 // AssertHandler is the class constructed by failing CHECK macros. AssertHandler 67 // will log information about the failures and abort when it is destructed. 68 class AssertHandler { 69 public: 70 AssertHandler(AssertData const& Data) 71 : passed(Data.passed) { 72 if (!passed) 73 Data.PrintFailed(); 74 } 75 76 ~AssertHandler() TEST_NOEXCEPT_FALSE { 77 if (!passed) { 78 error_log << std::endl; 79 std::abort(); 80 } 81 } 82 83 class LogType { 84 friend class AssertHandler; 85 86 template <class Tp> 87 friend LogType& operator<<(LogType& log, Tp const& value) { 88 if (!log.is_disabled) { 89 SelectStream<Tp>::Print(value); 90 } 91 return log; 92 } 93 94 friend LogType& operator<<(LogType& log, EndLType* m) { 95 if (!log.is_disabled) { 96 SelectStream<EndLType*>::Print(m); 97 } 98 return log; 99 } 100 101 private: 102 LogType(bool disable) : is_disabled(disable) {} 103 bool is_disabled; 104 105 LogType(LogType const&); 106 LogType& operator=(LogType const&); 107 }; 108 109 LogType& GetLog() { 110 if (passed) 111 return null_log; 112 return error_log; 113 } 114 115 private: 116 static LogType null_log; 117 static LogType error_log; 118 119 AssertHandler& operator=(const AssertHandler&) = delete; 120 AssertHandler(const AssertHandler&) = delete; 121 AssertHandler() = delete; 122 123 private: 124 bool passed; 125 }; 126 127 AssertHandler::LogType AssertHandler::null_log(true); 128 AssertHandler::LogType AssertHandler::error_log(false); 129 130 template <class It1> 131 std::string PrintRange(const char* Name, It1 F, It1 E) { 132 std::stringstream ss; 133 ss << " " << Name << " = ["; 134 while (F != E) { 135 ss << *F; 136 ++F; 137 if (F != E) 138 ss << ", "; 139 } 140 ss << "]\n"; 141 return ss.str(); 142 } 143 144 template <class Tp, class Up> 145 std::string PrintMismatch(Tp const& LHS, Up const& RHS, int Elem) { 146 std::stringstream ss; 147 ss << " Element " << Elem << " mismatched: `" << LHS << "` != `" << RHS 148 << "`!\n"; 149 return ss.str(); 150 }; 151 152 struct EqualToComp { 153 template <class Tp, class Up> 154 bool operator()(Tp const& LHS, Up const& RHS) const { 155 return LHS == RHS; 156 } 157 }; 158 159 template <class It1, class It2, class Comp> 160 AssertData CheckCollectionsEqual(It1 F1, It1 E1, It2 F2, It2 E2, 161 AssertData Data, Comp C = EqualToComp()) { 162 const It1 F1Orig = F1; 163 const It2 F2Orig = F2; 164 bool Failed = false; 165 std::string ErrorMsg; 166 int Idx = 0; 167 while (F1 != E1 && F2 != E2) { 168 if (!(C(*F1, *F2))) { 169 ErrorMsg += PrintMismatch(*F1, *F2, Idx); 170 Failed = true; 171 break; 172 } 173 ++Idx; 174 ++F1; 175 ++F2; 176 } 177 if (!Failed && (F1 != E1 || F2 != E2)) { 178 ErrorMsg += " Ranges have different sizes!\n"; 179 Failed = true; 180 } 181 if (Failed) { 182 ErrorMsg += PrintRange("LHS", F1Orig, E1); 183 ErrorMsg += PrintRange("RHS", F2Orig, E2); 184 Data.SetFailed(ErrorMsg); 185 } 186 return Data; 187 } 188 } // namespace verbose_assert 189 190 #ifdef __GNUC__ 191 #define ASSERT_FN_NAME() __PRETTY_FUNCTION__ 192 #else 193 #define ASSERT_FN_NAME() __func__ 194 #endif 195 196 #define DISPLAY(...) " " #__VA_ARGS__ " = " << (__VA_ARGS__) << "\n" 197 198 #define ASSERT(...) \ 199 ::verbose_assert::AssertHandler(::verbose_assert::AssertData( \ 200 #__VA_ARGS__, __FILE__, ASSERT_FN_NAME(), __LINE__,(__VA_ARGS__))).GetLog() 201 202 #define ASSERT_EQ(LHS, RHS) \ 203 ASSERT(LHS == RHS) << DISPLAY(LHS) << DISPLAY(RHS) 204 #define ASSERT_NEQ(LHS, RHS) \ 205 ASSERT(LHS != RHS) << DISPLAY(LHS) << DISPLAY(RHS) 206 #define ASSERT_PRED(PRED, LHS, RHS) \ 207 ASSERT(PRED(LHS, RHS)) << DISPLAY(LHS) << DISPLAY(RHS) 208 209 #define ASSERT_COLLECTION_EQ_COMP(F1, E1, F2, E2, Comp) \ 210 (::verbose_assert::AssertHandler( \ 211 ::verbose_assert::CheckCollectionsEqual( \ 212 F1, E1, F2, E2, \ 213 ::verbose_assert::AssertData("CheckCollectionsEqual(" #F1 ", " #E1 \ 214 ", " #F2 ", " #E2 ")", \ 215 __FILE__, ASSERT_FN_NAME(), __LINE__), \ 216 Comp)) \ 217 .GetLog()) 218 219 #define ASSERT_COLLECTION_EQ(F1, E1, F2, E2) \ 220 ASSERT_COLLECTION_EQ_COMP(F1, E1, F2, E2, ::verbose_assert::EqualToComp()) 221 222 #endif 223