blob: 353e71cfc67ebca5115aab175d339a22d48211e7 [file] [log] [blame]
Eric Fiselier1e34c762018-04-02 23:03:41 +00001#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
10namespace verbose_assert {
11
12typedef std::basic_ostream<char>&(EndLType)(std::basic_ostream<char>&);
13
14template <class Stream, class Tp,
15 class = decltype(std::declval<Stream&>() << std::declval<Tp const&>())>
16std::true_type IsStreamableImp(int);
17template <class Stream, class Tp> std::false_type IsStreamableImp(long);
18
19template <class Stream, class Tp>
20struct IsStreamable : decltype(IsStreamableImp<Stream, Tp>(0)) {};
21
22template <class Tp, int ST = (IsStreamable<decltype(std::cerr), Tp>::value ? 1
23 : (IsStreamable<decltype(std::wcerr), Tp>::value ? 2 : -1))>
24struct SelectStream {
25 static_assert(ST == -1, "specialization required for ST != -1");
26 static void Print(Tp const&) { std::clog << "Value Not Streamable!\n"; }
27};
28
29template <class Tp>
30struct SelectStream<Tp, 1> {
31 static void Print(Tp const& val) { std::cerr << val; }
32};
33
34template <class Tp>
35struct SelectStream<Tp, 2> {
36 static void Print(Tp const& val) { std::wcerr << val; }
37};
38
39struct 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.
68class AssertHandler {
69public:
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
115private:
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
123private:
124 bool passed;
125};
126
127AssertHandler::LogType AssertHandler::null_log(true);
128AssertHandler::LogType AssertHandler::error_log(false);
129
130template <class It1>
131std::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
144template <class Tp, class Up>
145std::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
152struct EqualToComp {
153 template <class Tp, class Up>
154 bool operator()(Tp const& LHS, Up const& RHS) const {
155 return LHS == RHS;
156 }
157};
158
159template <class It1, class It2, class Comp>
160AssertData 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