MT Core (C++)
Core library for replacing C++ standard in project usage
Loading...
Searching...
No Matches
mttest.hpp
Go to the documentation of this file.
1/*
2
3Copyright 2025 Matthew Tolman
4
5Licensed under the Apache License, Version 2.0 (the "License");
6you may not use this file except in compliance with the License.
7You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11Unless required by applicable law or agreed to in writing, software
12distributed under the License is distributed on an "AS IS" BASIS,
13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14See the License for the specific language governing permissions and
15limitations under the License.
16
17*/
18
19#ifndef MTTEST_HPP
20#define MTTEST_HPP
21
22#include <chrono>
23#include <functional>
24#include <iostream>
25#include <map>
26#include <sstream>
27#include <string>
28#include <vector>
29#include <cstring>
30#include "mtcore.hpp"
31
32template<mtcore::DefaultFormattable T>
33std::ostream &operator<<(std::ostream &os, const T &v) {
34 auto writer = mtcore::io::ostream_writer<char>(os);
35 mtcore::io::print(writer, "{}", v);
36 return os;
37}
38
44
57namespace mttest {
58
60template <typename T> class IsStreamable {
61 template <typename TT>
62 static auto test(std::stringstream &&s, TT &&t) -> decltype(s << std::forward<TT>(t));
63
64 struct dummy_t {};
65 static dummy_t test(...);
66
67 using return_type = decltype(test(std::declval<std::stringstream>(), std::declval<T>()));
68
69public:
70 static const bool value = !std::is_same<return_type, dummy_t>::value;
71};
72
74template <typename T> std::string as_string(const T &val) {
75 if constexpr (IsStreamable<T>::value) {
76 std::stringstream ss;
77 ss << val;
78 return ss.str();
79 } else {
80#if __cpp_constexpr >= 201907L
81 return std::string{typeid(T).name()} + "{?}";
82#else
83 return "{?}";
84#endif
85 }
86}
87
89template <typename L, typename R> struct CmpRes {
90 const L &left;
91 const R &right;
92 bool res;
93};
94
96inline std::string op_name(const std::string &op) {
97 if (op == "eq") return " == ";
98 if (op == "ne") return " != ";
99 if (op == "lt") return " < ";
100 if (op == "le") return " <= ";
101 if (op == "gt") return " > ";
102 if (op == "ge") return " >= ";
103 if (op == "check") return "";
104 return " ??? ";
105}
106
109 struct test_err {
110 std::string cond;
111 std::string err;
112 std::string file;
113 size_t line;
114 };
115
116 struct test_fail {
117 std::string cond;
118 std::string file;
119 size_t line;
120 std::string message;
121 };
122
123 std::vector<test_err> errors = {};
124 std::vector<test_fail> failures = {};
125
126 void add_error(const std::string &cond, const std::runtime_error &err, const std::string &file, size_t line) {
127 errors.push_back({cond, err.what(), file, line});
128 }
129
130 void add_error(const std::string &cond, int err, const std::string &file, size_t line) {
131 errors.push_back({cond, std::to_string(err), file, line});
132 }
133
134 void add_error(const std::string &cond, const std::string &file, size_t line) {
135 errors.push_back({cond, "UNKNOWN", file, line});
136 }
137
138 void add_failure(const std::string &cond, const std::string &file, size_t line) {
139 failures.push_back({cond, file, line});
140 }
141
142 void add_failure(const std::string &cond, const std::string &msg, const std::string &file, size_t line) {
143 failures.push_back({cond, file, line, msg});
144 }
145
146 template <typename L, typename R>
147 void add_failure(const std::string &comp, const std::string &raw,
148 const L &left, const R &right, const std::string &file,
149 size_t line) {
150 failures.push_back({"CHECK_EQ(" + raw + ")", file, line,
151 "RECEIVED '" + as_string(left) + "' " + op_name(comp) +
152 " '" + as_string(right) + "'"});
153 }
154};
155
157struct TestCase {
158 std::function<void(TestContext &)> case_impl;
159 std::string suite;
160 std::string name;
161};
162
169
172 std::map<std::string, std::vector<TestCase>> cases = {};
173
174 bool operator+=(const TestCase &tc) {
175 cases[tc.suite].push_back(tc);
176 return true;
177 }
178
179 SuiteStatus run_suite(bool verbose, const std::string &suite) {
180 auto startTime = std::chrono::system_clock::now();
181 auto test_cases = cases[suite];
182 std::cout << suite << std::flush;
183 bool suite_nl = false;
184 auto print_suite_nl = [&] {
185 if (suite_nl)
186 return;
187 suite_nl = true;
188 std::cout << "\n";
189 };
191 for (const auto &tc : test_cases) {
192 auto tcStart = std::chrono::system_clock::now();
193 bool printed = false;
194 auto print_case = [&] {
195 print_suite_nl();
196 if (printed) return;
197 printed = true;
198 std::cout << "\tCASE " << tc.name << std::flush;
199 };
200 if (verbose) print_case();
201
202 auto context = mttest::TestContext{};
203
204#if __cpp_exceptions
205 try {
206 tc.case_impl(context);
207 } catch (const std::runtime_error &err) {
208 context.add_error("Uncaught Exception", err, tc.name, 0);
209 } catch (int err) {
210 context.add_error("Uncaught Exception", err, tc.name, 0);
211 } catch (...) {
212 context.add_error("Uncaught Exception", tc.name, 0);
213 }
214#else
215 tc.case_impl(context);
216#endif
217 if (context.errors.empty() && context.failures.empty() && verbose) {
218 std::cout << " [PASS]";
219 } else if (!context.errors.empty()) {
220 print_case();
221 std::cout << " [ERROR]";
222 status = std::max(status, mttest::ERROR);
223 } else if (!context.failures.empty()) {
224 print_case();
225 std::cout << " [FAILED]";
226 status = std::max(status, mttest::FAIL);
227 }
228
229 if (printed) {
230 std::cout << " (" <<
231 std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - tcStart).count()
232 << "ms)" << std::endl;
233 }
234
235 for (const auto &err : context.errors) {
236 std::cout << "\t\t" << err.cond << " ERRED WITH " << err.err << "\n"
237 << "\t\t\tWHERE: " << err.file << ":" << err.line << "\n";
238 }
239
240 for (const auto &fail : context.failures) {
241 std::cout << "\t\t" << fail.cond << " FAILED! " << fail.message << "\n"
242 << "\t\t\tWHERE: " << fail.file << ":" << fail.line << "\n";
243 }
244 }
245
246 if (!suite_nl) {
247 std::cout << " ";
248 } else {
249 std::cout << suite << " ";
250 }
251
252 switch (status) {
253 case OK:
254 std::cout << "[PASSED]";
255 break;
256 case FAIL:
257 std::cout << "[FAILED]";
258 break;
259 case ERROR:
260 std::cout << "[ERRED]";
261 break;
262 }
263 std::cout << " (" << std::chrono::duration_cast<std::chrono::milliseconds>(
264 std::chrono::system_clock::now() - startTime).count() << "ms)" << std::endl;
265 return status;
266 }
267
268 SuiteStatus run(int argc, char **argv) {
269 std::vector<std::string> suites;
270 bool verbose = false;
271 for (int i = 1; i < argc; ++i) {
272 if (strcmp("-v", argv[i]) == 0) {
273 verbose = true;
274 } else {
275 suites.emplace_back(argv[i]);
276 }
277 }
278
279 SuiteStatus status = OK;
280 if (suites.empty()) {
281 for (const auto &[suite, cases] : cases) {
282 status = std::max(status, run_suite(verbose, suite));
283 }
284 return status;
285 }
286 for (const auto &s : suites) {
287 status = std::max(status, run_suite(verbose, s));
288 }
289 return status;
290 }
291};
292
294inline bool check(bool l) { return l; }
296inline bool check(bool l, const std::string &) { return l; }
298inline std::string check_msg(bool, std::string message = "") { return message; }
300template <typename L, typename R>
301auto eq(const L &left, const R &right) { return CmpRes<L, R>{left, right, left == right}; }
303template <typename L, typename R>
304auto ne(const L &left, const R &right) { return CmpRes<L, R>{left, right, left != right}; }
306template <typename L, typename R>
307auto lt(const L &left, const R &right) { return CmpRes<L, R>{left, right, left < right}; }
309template <typename L, typename R>
310auto le(const L &left, const R &right) { return CmpRes<L, R>{left, right, left <= right}; }
312template <typename L, typename R>
313auto gt(const L &left, const R &right) { return CmpRes<L, R>{left, right, left > right}; }
315template <typename L, typename R>
316auto ge(const L &left, const R &right) { return CmpRes<L, R>{left, right, left >= right}; }
317} // namespace mttest
318
319#ifdef MTTEST_DEFINE_MAIN
320#undef MTTEST_DEFINE_MAIN
322
323int main(int argc, char **argv) {
324 if (auto run_status = test_suites.run(argc, argv); run_status != mttest::OK) {
325 exit(-1);
326 }
327 return 0;
328}
329#else
331#endif
332
333#define TEST_IMPL_NAME1(X, Y, S, N, SEP) X##SEP##Y##SEP##S##SEP##N
334#define TEST_IMPL_NAME(X, Y, S, N) TEST_IMPL_NAME1(X, Y, S, N, _)
335#define TEST_SHORT_NAME(X, Y) TEST_IMPL_NAME1(X, Y, S, N, _)
336
337#define TEST_STRINGIFY(...) #__VA_ARGS__
338
345#define TEST_CASE(SUITE, NAME) \
346 static void TEST_IMPL_NAME(tc_impl, __LINE__, SUITE, NAME)( \
347 mttest::TestContext & mttest__context_ctx); \
348 static const auto TEST_IMPL_NAME(tc_decl, __LINE__, SUITE, NAME) = \
349 test_suites += \
350 mttest::TestCase{TEST_IMPL_NAME(tc_impl, __LINE__, SUITE, NAME), \
351 TEST_STRINGIFY(SUITE), TEST_STRINGIFY(NAME)}; \
352 static void TEST_IMPL_NAME(tc_impl, __LINE__, SUITE, \
353 NAME)(mttest::TestContext & mttest__context_ctx)
354
355#define MT_TEST_NARGS(...) \
356 MT_TEST_NARGS_(__VA_ARGS__, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
357#define MT_TEST_NARGS_(_11, _10, _9, _8, _7, _6, _5, _4, _3, _2, _1, N, ...) N
358
359#define MT_TEST_CONC(A, B) MT_TEST_CONC_(A, B)
360#define MT_TEST_CONC_(A, B) A##B
361
362#define MT_TEST_GET_ELEMS(N, ...) \
363 MT_TEST_CONC(MT_TEST_GET_ELEMS_, N)(__VA_ARGS__)
364#define MT_TEST_GET_ELEMS_0(_0)
365#define MT_TEST_GET_ELEMS_1(_0, _1)
366#define MT_TEST_GET_ELEMS_2(_0, _1, _2) #_1
367#define MT_TEST_GET_ELEMS_3(_0, _1, _2, _3) #_1 #_2
368#define MT_TEST_GET_ELEMS_4(_0, _1, _2, _3, _4) #_1 #_2 #_3
369#define MT_TEST_GET_ELEMS_5(_0, _1, _2, _3, _4, _5) #_1 #_2 #_3 #_4
370#define MT_TEST_GET_ELEMS_6(_0, _1, _2, _3, _4, _5, _6) #_1 #_2 #_3 #_4 #_5
371#define MT_TEST_GET_ELEMS_7(_0, _1, _2, _3, _4, _5, _6, _7) \
372 #_1 #_2 #_3 #_4 #_5 #_6
373#define MT_TEST_GET_ELEMS_8(_0, _1, _2, _3, _4, _5, _6, _7, _8) \
374 #_1 #_2 #_3 #_4 #_5 #_6 #_7
375#define MT_TEST_GET_ELEMS_9(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \
376 #_1 #_2 #_3 #_4 #_5 #_6 #_7 #_8
377#define MT_TEST_GET_ELEMS_10(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \
378 #_1 #_2 #_3 #_4 #_5 #_6 #_7 #_8 #_9
379#define MT_TEST_GET_ELEMS_11(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \
380 #_1 #_2 #_3 #_4 #_5 #_6 #_7 #_8 #_9 #_10
381
382// Placeholder to get all but last (and _)
383// Used to get condition for check message
384#define MT_TEST_GET_NOT_LAST(...) \
385 MT_TEST_GET_ELEMS(MT_TEST_NARGS(__VA_ARGS__), _, __VA_ARGS__)
386
387#define MT_TEST_COND(...) MT_TEST_GET_NOT_LAST(__VA_ARGS__)
388
389#if __cpp_exceptions
394#define CHECK_MESSAGE(...) \
395 try { \
396 auto TEST_SHORT_NAME(mttest_res, __LINE__) = mttest::check(__VA_ARGS__); \
397 if (!TEST_SHORT_NAME(mttest_res, __LINE__)) { \
398 mttest__context_ctx.add_failure(MT_TEST_COND(__VA_ARGS__), \
399 mttest::check_msg(__VA_ARGS__), \
400 __FILE__, __LINE__); \
401 } \
402 } catch (const std::runtime_error &err) { \
403 mttest__context_ctx.add_error(MT_TEST_COND(__VA_ARGS__), err, __FILE__, \
404 __LINE__); \
405 } catch (int err) { \
406 mttest__context_ctx.add_error(MT_TEST_COND(__VA_ARGS__), err, __FILE__, \
407 __LINE__); \
408 } catch (...) { \
409 mttest__context_ctx.add_error(MT_TEST_COND(__VA_ARGS__), __FILE__, \
410 __LINE__); \
411 }
412
417#define CHECK(...) \
418 try { \
419 auto TEST_SHORT_NAME(mttest_res, __LINE__) = __VA_ARGS__; \
420 if (!TEST_SHORT_NAME(mttest_res, __LINE__)) { \
421 mttest__context_ctx.add_failure(TEST_STRINGIFY(__VA_ARGS__), __FILE__, \
422 __LINE__); \
423 } \
424 } catch (const std::runtime_error &err) { \
425 mttest__context_ctx.add_error(#__VA_ARGS__, err, __FILE__, __LINE__); \
426 } catch (int err) { \
427 mttest__context_ctx.add_error(#__VA_ARGS__, err, __FILE__, __LINE__); \
428 } catch (...) { \
429 mttest__context_ctx.add_error(#__VA_ARGS__, __FILE__, __LINE__); \
430 }
431
432#define MTTEST_CHECK_CMP(COMP, ...) \
433 try { \
434 auto TEST_SHORT_NAME(mttest_res, __LINE__) = mttest::COMP(__VA_ARGS__); \
435 if (!TEST_SHORT_NAME(mttest_res, __LINE__).res) { \
436 mttest__context_ctx.add_failure( \
437 #COMP, #__VA_ARGS__, TEST_SHORT_NAME(mttest_res, __LINE__).left, \
438 TEST_SHORT_NAME(mttest_res, __LINE__).right, __FILE__, __LINE__); \
439 } \
440 } catch (const std::runtime_error &err) { \
441 mttest__context_ctx.add_error(#__VA_ARGS__, err, __FILE__, __LINE__); \
442 } catch (int err) { \
443 mttest__context_ctx.add_error(#__VA_ARGS__, err, __FILE__, __LINE__); \
444 } catch (...) { \
445 mttest__context_ctx.add_error(#__VA_ARGS__, __FILE__, __LINE__); \
446 }
447
452#define CHECK_EQ_APPROX(LEFT, RIGHT, EPSILON) \
453 try { \
454 auto TEST_IMPL_NAME(mttest_check_res_left, __LINE__, _L_, _O_) = LEFT; \
455 auto TEST_IMPL_NAME(mttest_check_res_left_minus, __LINE__, _L_, _M_) = \
456 TEST_IMPL_NAME(mttest_check_res_left, __LINE__, _L_, _O_) - EPSILON; \
457 auto TEST_IMPL_NAME(mttest_check_res_left_plus, __LINE__, _L_, _P_) = \
458 TEST_IMPL_NAME(mttest_check_res_left, __LINE__, _L_, _O_) + EPSILON; \
459 auto TEST_IMPL_NAME(mttest_check_res_right, __LINE__, _R_, _O_) = RIGHT; \
460 if (TEST_IMPL_NAME(mttest_check_res_left_minus, __LINE__, _L_, _M_) > \
461 TEST_IMPL_NAME(mttest_check_res_right, __LINE__, _R_, _O_) || \
462 TEST_IMPL_NAME(mttest_check_res_right, __LINE__, _R_, _O_) > \
463 TEST_IMPL_NAME(mttest_check_res_left_plus, __LINE__, _L_, _P_)) { \
464 mttest__context_ctx.add_failure( \
465 TEST_STRINGIFY((LEFT - EPSILON) <= (RIGHT) <= (LEFT + EPSILON)), \
466 std::string{"Got "} + \
467 mttest::as_string( \
468 TEST_IMPL_NAME(mttest_check_res_left_minus, __LINE__, _L_, _M_)) + \
469 " <= " + \
470 mttest::as_string( \
471 TEST_IMPL_NAME(mttest_check_res_right, __LINE__, _R_, _O_)) + " <= " + \
472 mttest::as_string( \
473 TEST_IMPL_NAME(mttest_check_res_left_plus, __LINE__, _L_, _P_)), \
474 __FILE__, __LINE__); \
475 } \
476 } catch (const std::runtime_error &err) { \
477 mttest__context_ctx.add_error( \
478 TEST_STRINGIFY((LEFT - EPSILON) <= (RIGHT) <= (LEFT + EPSILON)), err, \
479 __FILE__, __LINE__); \
480 } catch (int err) { \
481 mttest__context_ctx.add_error( \
482 TEST_STRINGIFY((LEFT - EPSILON) <= (RIGHT) <= (LEFT + EPSILON)), err, \
483 __FILE__, __LINE__); \
484 } catch (...) { \
485 mttest__context_ctx.add_error( \
486 TEST_STRINGIFY((LEFT - EPSILON) <= (RIGHT) <= (LEFT + EPSILON)), \
487 __FILE__, __LINE__); \
488 }
489#else
493#define CHECK_MESSAGE(...) \
494 { \
495 auto TEST_SHORT_NAME(mttest_res, __LINE__) = mttest::check(__VA_ARGS__); \
496 if (!TEST_SHORT_NAME(mttest_res, __LINE__)) { \
497 mttest__context_ctx.add_failure(MT_TEST_COND(__VA_ARGS__), \
498 mttest::check_msg(__VA_ARGS__), \
499 __FILE__, __LINE__); \
500 } \
501 }
502
506#define CHECK(...) \
507 { \
508 auto TEST_SHORT_NAME(mttest_res, __LINE__) = __VA_ARGS__; \
509 if (!TEST_SHORT_NAME(mttest_res, __LINE__)) { \
510 mttest__context_ctx.add_failure(TEST_STRINGIFY(__VA_ARGS__), __FILE__, \
511 __LINE__); \
512 } \
513 }
514
515#define MTTEST_CHECK_CMP(COMP, ...) \
516 { \
517 auto TEST_SHORT_NAME(mttest_res, __LINE__) = mttest::COMP(__VA_ARGS__); \
518 if (!TEST_SHORT_NAME(mttest_res, __LINE__).res) { \
519 mttest__context_ctx.add_failure( \
520 #COMP, #__VA_ARGS__, TEST_SHORT_NAME(mttest_res, __LINE__).left, \
521 TEST_SHORT_NAME(mttest_res, __LINE__).right, __FILE__, __LINE__); \
522 } \
523 }
524
528#define CHECK_EQ_APPROX(LEFT, RIGHT, EPSILON) \
529 { \
530 auto TEST_IMPL_NAME(mttest_check_res_left, __LINE__) = LEFT; \
531 auto TEST_IMPL_NAME(mttest_check_res_left_minus, __LINE__) = \
532 TEST_IMPL_NAME(mttest_check_res_left, __LINE__) - EPSILON; \
533 auto TEST_IMPL_NAME(mttest_check_res_left_plus, __LINE__) = \
534 TEST_IMPL_NAME(mttest_check_res_left, __LINE__) + EPSILON; \
535 auto TEST_IMPL_NAME(mttest_check_res_right, __LINE__) = RIGHT; \
536 if (TEST_IMPL_NAME(mttest_check_res_left_minus, __LINE__) > \
537 TEST_IMPL_NAME(mttest_check_res_right, __LINE__) || \
538 TEST_IMPL_NAME(mttest_check_res_right, __LINE__) > \
539 TEST_IMPL_NAME(mttest_check_res_left_plus, __LINE__)) { \
540 mttest__context_ctx.add_failure( \
541 TEST_STRINGIFY((LEFT - EPSILON) <= (RIGHT) <= (LEFT + EPSILON)), \
542 std::string{"Got "} + \
543 mttest::as_string( \
544 TEST_IMPL_NAME(mttest_check_res_left_minus, __LINE__)) + \
545 " <= " + \
546 mttest::as_string( \
547 TEST_IMPL_NAME(mttest_check_res_right, __LINE__)) + " <= " + \
548 mttest::as_string( \
549 TEST_IMPL_NAME(mttest_check_res_left_plus, __LINE__)), \
550 __FILE__, __LINE__); \
551 } \
552 }
553
554#endif
555
560#define CHECK_EQ(...) MTTEST_CHECK_CMP(eq, __VA_ARGS__);
565#define CHECK_NE(...) MTTEST_CHECK_CMP(ne, __VA_ARGS__);
570#define CHECK_LT(...) MTTEST_CHECK_CMP(lt, __VA_ARGS__);
575#define CHECK_LE(...) MTTEST_CHECK_CMP(le, __VA_ARGS__);
580#define CHECK_GT(...) MTTEST_CHECK_CMP(gt, __VA_ARGS__);
585#define CHECK_GE(...) MTTEST_CHECK_CMP(ge, __VA_ARGS__);
586
587#endif // MTTEST_HPP
Checks if a class can be streamd to ostream.
Definition mttest.hpp:60
static const bool value
Definition mttest.hpp:70
Result< size_t, typename Writer< WI >::ErrType > print(Writer< WI > &writer, const char *fmt, const Args &...args)
Prints arguments using a format string Element insert points are designated by a pair of curly braces...
auto ostream_writer(std::ostream &os)
Creates a writer which passes its output to an ostream Useful for compatibility with the C++ standard...
mttest::TestSuites test_suites
std::ostream & operator<<(std::ostream &os, const T &v)
Definition mttest.hpp:33
MT Unit testing framework.
Definition mttest.hpp:57
auto eq(const L &left, const R &right)
Equality comparison (used by macros)
Definition mttest.hpp:301
auto lt(const L &left, const R &right)
Less than comparison (used by macros)
Definition mttest.hpp:307
auto ne(const L &left, const R &right)
Inequality comparison (used by macros)
Definition mttest.hpp:304
std::string as_string(const T &val)
Gets a value as a string (for debug printing)
Definition mttest.hpp:74
SuiteStatus
Test suite status.
Definition mttest.hpp:164
@ ERROR
Definition mttest.hpp:167
auto le(const L &left, const R &right)
Less than or equal to comparison (used by macros)
Definition mttest.hpp:310
bool check(bool l)
Does a check (used by macros)
Definition mttest.hpp:294
std::string check_msg(bool, std::string message="")
Gets the message from a check (used by macros)
Definition mttest.hpp:298
auto ge(const L &left, const R &right)
Greater than or equal to comparison (used by macros)
Definition mttest.hpp:316
auto gt(const L &left, const R &right)
Greater than comparison (used by macros)
Definition mttest.hpp:313
std::string op_name(const std::string &op)
Gets an operation name's output symbol.
Definition mttest.hpp:96
Result of comparing left and right (retains value references for debug printing)
Definition mttest.hpp:89
const R & right
Definition mttest.hpp:91
const L & left
Definition mttest.hpp:90
Test case metadata.
Definition mttest.hpp:157
std::function< void(TestContext &)> case_impl
Definition mttest.hpp:158
std::string name
Definition mttest.hpp:160
std::string suite
Definition mttest.hpp:159
Context for unit tests.
Definition mttest.hpp:108
std::vector< test_fail > failures
Definition mttest.hpp:124
void add_failure(const std::string &comp, const std::string &raw, const L &left, const R &right, const std::string &file, size_t line)
Definition mttest.hpp:147
void add_failure(const std::string &cond, const std::string &file, size_t line)
Definition mttest.hpp:138
void add_error(const std::string &cond, const std::runtime_error &err, const std::string &file, size_t line)
Definition mttest.hpp:126
std::vector< test_err > errors
Definition mttest.hpp:123
void add_error(const std::string &cond, int err, const std::string &file, size_t line)
Definition mttest.hpp:130
void add_failure(const std::string &cond, const std::string &msg, const std::string &file, size_t line)
Definition mttest.hpp:142
void add_error(const std::string &cond, const std::string &file, size_t line)
Definition mttest.hpp:134
Test suite metadata and runner.
Definition mttest.hpp:171
bool operator+=(const TestCase &tc)
Definition mttest.hpp:174
SuiteStatus run_suite(bool verbose, const std::string &suite)
Definition mttest.hpp:179
std::map< std::string, std::vector< TestCase > > cases
Definition mttest.hpp:172
SuiteStatus run(int argc, char **argv)
Definition mttest.hpp:268