• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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