• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #ifndef RAPID_CXX_TEST_H
2 #define RAPID_CXX_TEST_H
3 
4 # include <cstddef>
5 # include <cstdlib>
6 # include <cstdio>
7 # include <cstring>
8 # include <cassert>
9 
10 #include "test_macros.h"
11 
12 #if !defined(RAPID_CXX_TEST_NO_SYSTEM_HEADER) || !defined(__GNUC__)
13 #pragma GCC system_header
14 #endif
15 
16 # define RAPID_CXX_TEST_PP_CAT(x, y) RAPID_CXX_TEST_PP_CAT_2(x, y)
17 # define RAPID_CXX_TEST_PP_CAT_2(x, y) x##y
18 
19 # define RAPID_CXX_TEST_PP_STR(...) RAPID_CXX_TEST_PP_STR_2(__VA_ARGS__)
20 # define RAPID_CXX_TEST_PP_STR_2(...) #__VA_ARGS__
21 
22 # if defined(__GNUC__)
23 #   define TEST_FUNC_NAME() __PRETTY_FUNCTION__
24 #   define RAPID_CXX_TEST_UNUSED __attribute__((unused))
25 # else
26 #   define TEST_FUNC_NAME() __func__
27 #   define RAPID_CXX_TEST_UNUSED
28 # endif
29 
30 ////////////////////////////////////////////////////////////////////////////////
31 //                          TEST_SUITE
32 ////////////////////////////////////////////////////////////////////////////////
33 # define TEST_SUITE(Name)                                           \
34 namespace Name                                                      \
35 {                                                                   \
36     inline ::rapid_cxx_test::test_suite & get_test_suite()          \
37     {                                                               \
38         static ::rapid_cxx_test::test_suite m_suite(#Name);         \
39         return m_suite;                                             \
40     }                                                               \
41                                                                     \
42     inline int unit_test_main(int, char**)                          \
43     {                                                               \
44         ::rapid_cxx_test::test_runner runner(get_test_suite());     \
45         return runner.run();                                        \
46     }                                                               \
47 }                                                                   \
48 int main(int argc, char **argv)                                     \
49 {                                                                   \
50     return Name::unit_test_main(argc, argv);                        \
51 }                                                                   \
52 namespace Name                                                      \
53 { /* namespace closed in TEST_SUITE_END */
54 #
55 
56 ////////////////////////////////////////////////////////////////////////////////
57 //                         TEST_SUITE_END
58 ////////////////////////////////////////////////////////////////////////////////
59 # define TEST_SUITE_END()                                       \
60 } /* namespace opened in TEST_SUITE(...) */
61 #
62 
63 ////////////////////////////////////////////////////////////////////////////////
64 //                          TEST_CASE
65 ////////////////////////////////////////////////////////////////////////////////
66 
67 # if !defined(__clang__)
68 #
69 # define TEST_CASE(Name)                                                                                \
70     void Name();                                                                                        \
71     static void RAPID_CXX_TEST_PP_CAT(Name, _invoker)()                                                 \
72     {                                                                                                   \
73         Name();                                                                                         \
74     }                                                                                                   \
75     static ::rapid_cxx_test::registrar                                                                  \
76     RAPID_CXX_TEST_PP_CAT(rapid_cxx_test_registrar_, Name)(                                         \
77         get_test_suite()                                                                                \
78       , ::rapid_cxx_test::test_case(__FILE__, #Name, __LINE__, & RAPID_CXX_TEST_PP_CAT(Name, _invoker)) \
79       );                                                                                                \
80     void Name()
81 #
82 # else /* __clang__ */
83 #
84 # define TEST_CASE(Name)                                                                                \
85     void Name();                                                                                        \
86     static void RAPID_CXX_TEST_PP_CAT(Name, _invoker)()                                                 \
87     {                                                                                                   \
88         Name();                                                                                         \
89     }                                                                                                   \
90     _Pragma("clang diagnostic push")                                                                    \
91     _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"")                                       \
92     static ::rapid_cxx_test::registrar                                                                  \
93     RAPID_CXX_TEST_PP_CAT(rapid_cxx_test_registrar_, Name)(                                         \
94         get_test_suite()                                                                                \
95       , ::rapid_cxx_test::test_case(__FILE__, #Name, __LINE__, & RAPID_CXX_TEST_PP_CAT(Name, _invoker)) \
96       );                                                                                                \
97     _Pragma("clang diagnostic pop")                                                                     \
98     void Name()
99 #
100 # endif /* !defined(__clang__) */
101 
102 
103 # define TEST_SET_CHECKPOINT() ::rapid_cxx_test::set_checkpoint(__FILE__, TEST_FUNC_NAME(), __LINE__)
104 
105 #define RAPID_CXX_TEST_OUTCOME()
106 
107 ////////////////////////////////////////////////////////////////////////////////
108 //                              TEST_UNSUPPORTED
109 ////////////////////////////////////////////////////////////////////////////////
110 # define TEST_UNSUPPORTED()                                                                 \
111     do {                                                                                    \
112         TEST_SET_CHECKPOINT();                                                              \
113         ::rapid_cxx_test::test_outcome m_f(                                                 \
114           ::rapid_cxx_test::failure_type::unsupported, __FILE__, TEST_FUNC_NAME(), __LINE__ \
115           , "", ""                                                                          \
116         );                                                                                  \
117         ::rapid_cxx_test::get_reporter().report(m_f);                                       \
118         return;                                                                             \
119     } while (false)
120 #
121 
122 
123 ////////////////////////////////////////////////////////////////////////////////
124 //                            BASIC ASSERTIONS
125 ////////////////////////////////////////////////////////////////////////////////
126 # define TEST_WARN(...)                                                                \
127     do {                                                                               \
128         TEST_SET_CHECKPOINT();                                                         \
129         ::rapid_cxx_test::test_outcome m_f(                                            \
130             ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
131             , "TEST_WARN(" #__VA_ARGS__ ")", ""                                        \
132             );                                                                         \
133         if (not (__VA_ARGS__)) {                                                       \
134             m_f.type = ::rapid_cxx_test::failure_type::warn;                           \
135         }                                                                              \
136         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
137     } while (false)
138 #
139 
140 # define TEST_CHECK(...)                                                               \
141     do {                                                                               \
142         TEST_SET_CHECKPOINT();                                                         \
143         ::rapid_cxx_test::test_outcome m_f(                                            \
144             ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
145             , "TEST_CHECK(" #__VA_ARGS__ ")", ""                                       \
146             );                                                                         \
147         if (not (__VA_ARGS__)) {                                                       \
148             m_f.type = ::rapid_cxx_test::failure_type::check;                          \
149         }                                                                              \
150         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
151     } while (false)
152 #
153 
154 # define TEST_REQUIRE(...)                                                             \
155     do {                                                                               \
156         TEST_SET_CHECKPOINT();                                                         \
157         ::rapid_cxx_test::test_outcome m_f(                                            \
158             ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
159             , "TEST_REQUIRE(" #__VA_ARGS__ ")", ""                                     \
160             );                                                                         \
161         if (not (__VA_ARGS__)) {                                                       \
162             m_f.type = ::rapid_cxx_test::failure_type::require;                        \
163         }                                                                              \
164         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
165         if (m_f.type != ::rapid_cxx_test::failure_type::none) {                        \
166             return;                                                                    \
167         }                                                                              \
168     } while (false)
169 #
170 
171 # define TEST_ASSERT(...)                                                              \
172     do {                                                                               \
173         TEST_SET_CHECKPOINT();                                                         \
174         ::rapid_cxx_test::test_outcome m_f(                                            \
175             ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
176             , "TEST_ASSERT(" #__VA_ARGS__ ")", ""                                      \
177             );                                                                         \
178         if (not (__VA_ARGS__)) {                                                       \
179             m_f.type = ::rapid_cxx_test::failure_type::assert;                         \
180         }                                                                              \
181         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
182         if (m_f.type != ::rapid_cxx_test::failure_type::none) {                        \
183             std::abort();                                                              \
184         }                                                                              \
185     } while (false)
186 #
187 
188 ////////////////////////////////////////////////////////////////////////////////
189 //                    TEST_CHECK_NO_THROW / TEST_CHECK_THROW
190 ////////////////////////////////////////////////////////////////////////////////
191 #ifndef TEST_HAS_NO_EXCEPTIONS
192 
193 # define TEST_CHECK_NO_THROW(...)                                                      \
194     do {                                                                               \
195         TEST_SET_CHECKPOINT();                                                         \
196         ::rapid_cxx_test::test_outcome m_f(                                            \
197             ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
198             , "TEST_CHECK_NO_THROW(" #__VA_ARGS__ ")", ""                              \
199             );                                                                         \
200         try {                                                                          \
201             (static_cast<void>(__VA_ARGS__));                                          \
202         } catch (...) {                                                                \
203             m_f.type = ::rapid_cxx_test::failure_type::check;                          \
204         }                                                                              \
205         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
206     } while (false)
207 #
208 
209 # define TEST_CHECK_THROW(Except, ...)                                                 \
210     do {                                                                               \
211         TEST_SET_CHECKPOINT();                                                         \
212         ::rapid_cxx_test::test_outcome m_f(                                            \
213             ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
214             , "TEST_CHECK_THROW(" #Except "," #__VA_ARGS__ ")", ""                     \
215             );                                                                         \
216         try {                                                                          \
217             (static_cast<void>(__VA_ARGS__));                                          \
218             m_f.type = ::rapid_cxx_test::failure_type::check;                          \
219         } catch (Except const &) {}                                                    \
220         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
221     } while (false)
222 #
223 
224 #define TEST_CHECK_THROW_RESULT(Except, Checker, ...)                          \
225   do {                                                                         \
226     TEST_SET_CHECKPOINT();                                                     \
227     ::rapid_cxx_test::test_outcome m_f(::rapid_cxx_test::failure_type::none,   \
228                                        __FILE__, TEST_FUNC_NAME(), __LINE__,   \
229                                        "TEST_CHECK_THROW_RESULT(" #Except      \
230                                        "," #Checker "," #__VA_ARGS__ ")",      \
231                                        "");                                    \
232     try {                                                                      \
233       (static_cast<void>(__VA_ARGS__));                                        \
234       m_f.type = ::rapid_cxx_test::failure_type::check;                        \
235     } catch (Except const& Caught) {                                           \
236       Checker(Caught);                                                         \
237     }                                                                          \
238     ::rapid_cxx_test::get_reporter().report(m_f);                              \
239   } while (false)
240 #
241 
242 #else // TEST_HAS_NO_EXCEPTIONS
243 
244 # define TEST_CHECK_NO_THROW(...)                                                      \
245     do {                                                                               \
246         TEST_SET_CHECKPOINT();                                                         \
247         ::rapid_cxx_test::test_outcome m_f(                                            \
248             ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
249             , "TEST_CHECK_NO_THROW(" #__VA_ARGS__ ")", ""                              \
250             );                                                                         \
251         (static_cast<void>(__VA_ARGS__));                                              \
252         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
253     } while (false)
254 #
255 
256 #define TEST_CHECK_THROW(Except, ...) ((void)0)
257 #define TEST_CHECK_THROW_RESULT(Except, Checker, ...) ((void)0)
258 
259 #endif // TEST_HAS_NO_EXCEPTIONS
260 
261 
262 ////////////////////////////////////////////////////////////////////////////////
263 //                    TEST_REQUIRE_NO_THROW / TEST_REQUIRE_THROWs
264 ////////////////////////////////////////////////////////////////////////////////
265 #ifndef TEST_HAS_NO_EXCEPTIONS
266 
267 # define TEST_REQUIRE_NO_THROW(...)                                                    \
268     do {                                                                               \
269         TEST_SET_CHECKPOINT();                                                         \
270         ::rapid_cxx_test::test_outcome m_f(                                            \
271             ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
272             , "TEST_REQUIRE_NO_THROW(" #__VA_ARGS__ ")", ""                            \
273             );                                                                         \
274         try {                                                                          \
275             (static_cast<void>(__VA_ARGS__));                                          \
276         } catch (...) {                                                                \
277             m_f.type = ::rapid_cxx_test::failure_type::require;                        \
278         }                                                                              \
279         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
280         if (m_f.type != ::rapid_cxx_test::failure_type::none) {                        \
281             return;                                                                    \
282         }                                                                              \
283     } while (false)
284 #
285 
286 # define TEST_REQUIRE_THROW(Except, ...)                                               \
287     do {                                                                               \
288         TEST_SET_CHECKPOINT();                                                         \
289         ::rapid_cxx_test::test_outcome m_f(                                            \
290             ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
291             , "TEST_REQUIRE_THROW(" #Except "," #__VA_ARGS__ ")", ""                   \
292             );                                                                         \
293         try {                                                                          \
294             (static_cast<void>(__VA_ARGS__));                                          \
295             m_f.type = ::rapid_cxx_test::failure_type::require;                        \
296         } catch (Except const &) {}                                                    \
297         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
298         if (m_f.type != ::rapid_cxx_test::failure_type::none) {                        \
299             return;                                                                    \
300         }                                                                              \
301     } while (false)
302 #
303 
304 #else // TEST_HAS_NO_EXCEPTIONS
305 
306 # define TEST_REQUIRE_NO_THROW(...)                                                    \
307     do {                                                                               \
308         TEST_SET_CHECKPOINT();                                                         \
309         ::rapid_cxx_test::test_outcome m_f(                                            \
310             ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
311             , "TEST_REQUIRE_NO_THROW(" #__VA_ARGS__ ")", ""                            \
312             );                                                                         \
313         (static_cast<void>(__VA_ARGS__));                                              \
314         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
315     } while (false)
316 #
317 
318 #define TEST_REQUIRE_THROW(Except, ...) ((void)0)
319 
320 #endif // TEST_HAS_NO_EXCEPTIONS
321 
322 ////////////////////////////////////////////////////////////////////////////////
323 //                    TEST_ASSERT_NO_THROW / TEST_ASSERT_THROW
324 ////////////////////////////////////////////////////////////////////////////////
325 #ifndef TEST_HAS_NO_EXCEPTIONS
326 
327 # define TEST_ASSERT_NO_THROW(...)                                                     \
328     do {                                                                               \
329         TEST_SET_CHECKPOINT();                                                         \
330         ::rapid_cxx_test::test_outcome m_f(                                            \
331             ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
332             , "TEST_ASSERT_NO_THROW(" #__VA_ARGS__ ")", ""                             \
333             );                                                                         \
334         try {                                                                          \
335             (static_cast<void>(__VA_ARGS__));                                          \
336         } catch (...) {                                                                \
337             m_f.type = ::rapid_cxx_test::failure_type::assert;                         \
338         }                                                                              \
339         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
340         if (m_f.type != ::rapid_cxx_test::failure_type::none) {                        \
341             std::abort();                                                              \
342         }                                                                              \
343     } while (false)
344 #
345 
346 # define TEST_ASSERT_THROW(Except, ...)                                                \
347     do {                                                                               \
348         TEST_SET_CHECKPOINT();                                                         \
349         ::rapid_cxx_test::test_outcome m_f(                                            \
350             ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
351             , "TEST_ASSERT_THROW(" #Except "," #__VA_ARGS__ ")", ""                    \
352             );                                                                         \
353         try {                                                                          \
354             (static_cast<void>(__VA_ARGS__));                                          \
355             m_f.type = ::rapid_cxx_test::failure_type::assert;                         \
356         } catch (Except const &) {}                                                    \
357         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
358         if (m_f.type != ::rapid_cxx_test::failure_type::none) {                        \
359             std::abort();                                                              \
360         }                                                                              \
361     } while (false)
362 #
363 
364 #else // TEST_HAS_NO_EXCEPTIONS
365 
366 # define TEST_ASSERT_NO_THROW(...)                                                     \
367     do {                                                                               \
368         TEST_SET_CHECKPOINT();                                                         \
369         ::rapid_cxx_test::test_outcome m_f(                                            \
370             ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
371             , "TEST_ASSERT_NO_THROW(" #__VA_ARGS__ ")", ""                             \
372             );                                                                         \
373         (static_cast<void>(__VA_ARGS__));                                              \
374         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
375     } while (false)
376 #
377 
378 #define TEST_ASSERT_THROW(Except, ...) ((void)0)
379 
380 #endif // TEST_HAS_NO_EXCEPTIONS
381 
382 ////////////////////////////////////////////////////////////////////////////////
383 //
384 ////////////////////////////////////////////////////////////////////////////////
385 
386 # define TEST_WARN_EQUAL_COLLECTIONS(...)                                              \
387     do {                                                                               \
388         TEST_SET_CHECKPOINT();                                                         \
389         ::rapid_cxx_test::test_outcome m_f(                                            \
390           ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__   \
391           , "TEST_WARN_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", ""                        \
392         );                                                                             \
393         if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \
394             m_f.type = ::rapid_cxx_test::failure_type::warn;                           \
395         }                                                                              \
396         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
397     } while (false)
398 #
399 
400 # define TEST_CHECK_EQUAL_COLLECTIONS(...)                                             \
401     do {                                                                               \
402         TEST_SET_CHECKPOINT();                                                         \
403         ::rapid_cxx_test::test_outcome m_f(                                            \
404           ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__   \
405           , "TEST_CHECK_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", ""                       \
406         );                                                                             \
407         if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \
408             m_f.type = ::rapid_cxx_test::failure_type::check;                          \
409         }                                                                              \
410         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
411     } while (false)
412 #
413 
414 # define TEST_REQUIRE_EQUAL_COLLECTIONS(...)                                           \
415     do {                                                                               \
416         TEST_SET_CHECKPOINT();                                                         \
417         ::rapid_cxx_test::test_outcome m_f(                                            \
418           ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__   \
419           , "TEST_REQUIRE_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", ""                     \
420         );                                                                             \
421         if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \
422             m_f.type = ::rapid_cxx_test::failure_type::require;                        \
423         }                                                                              \
424         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
425         if (m_f.type != ::rapid_cxx_test::failure_type::none) {                        \
426             return;                                                                    \
427         }                                                                              \
428     } while (false)
429 #
430 
431 # define TEST_ASSERT_EQUAL_COLLECTIONS(...)                                            \
432     do {                                                                               \
433         TEST_SET_CHECKPOINT();                                                         \
434         ::rapid_cxx_test::test_outcome m_f(                                            \
435           ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__   \
436           , "TEST_ASSERT_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", ""                      \
437         );                                                                             \
438         if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \
439             m_f.type = ::rapid_cxx_test::failure_type::assert;                         \
440         }                                                                              \
441         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
442         if (m_f.type != ::rapid_cxx_test::failure_type::none) {                        \
443           ::std::abort();                                                              \
444         }                                                                              \
445     } while (false)
446 #
447 
448 namespace rapid_cxx_test
449 {
450     typedef void (*invoker_t)();
451 
452     ////////////////////////////////////////////////////////////////////////////
453     struct test_case
454     {
test_casetest_case455         test_case()
456             : file(""), func(""), line(0), invoke(NULL)
457         {}
458 
test_casetest_case459         test_case(const char* file1, const char* func1, std::size_t line1,
460                   invoker_t invoke1)
461             : file(file1), func(func1), line(line1), invoke(invoke1)
462         {}
463 
464         const char *file;
465         const char *func;
466         std::size_t line;
467         invoker_t invoke;
468     };
469 
470     ////////////////////////////////////////////////////////////////////////////
471     struct failure_type
472     {
473         enum enum_type {
474             none,
475             unsupported,
476             warn,
477             check,
478             require,
479             assert,
480             uncaught_exception
481         };
482     };
483 
484     typedef failure_type::enum_type failure_type_t;
485 
486     ////////////////////////////////////////////////////////////////////////////
487     struct test_outcome
488     {
test_outcometest_outcome489         test_outcome()
490             : type(failure_type::none),
491               file(""), func(""), line(0),
492               expression(""), message("")
493         {}
494 
test_outcometest_outcome495         test_outcome(failure_type_t type1, const char* file1, const char* func1,
496                      std::size_t line1, const char* expression1,
497                      const char* message1)
498             : type(type1), file(file1), func(func1), line(line1),
499               expression(expression1), message(message1)
500         {
501             trim_func_string();
502         }
503 
504         failure_type_t type;
505         const char *file;
506         const char *func;
507         std::size_t line;
508         const char *expression;
509         const char *message;
510 
511     private:
trim_file_stringtest_outcome512         void trim_file_string() {
513             const char* f_start  = file;
514             const char* prev_start = f_start;
515             const char* last_start = f_start;
516             char last;
517             while ((last = *f_start) != '\0') {
518                 ++f_start;
519                 if (last == '/' && *f_start) {
520                     prev_start = last_start;
521                     last_start = f_start;
522                 }
523             }
524             file = prev_start;
525         }
trim_func_stringtest_outcome526       void trim_func_string() {
527           const char* void_loc = ::strstr(func, "void ");
528           if (void_loc == func) {
529               func += strlen("void ");
530           }
531           const char* namespace_loc = ::strstr(func, "::");
532           if (namespace_loc) {
533               func = namespace_loc + 2;
534           }
535       }
536     };
537 
538     ////////////////////////////////////////////////////////////////////////////
539     struct checkpoint
540     {
541         const char *file;
542         const char *func;
543         std::size_t line;
544     };
545 
546     namespace detail
547     {
global_checkpoint()548         inline checkpoint & global_checkpoint()
549         {
550             static checkpoint cp = {"", "", 0};
551             return cp;
552         }
553     }
554 
555     ////////////////////////////////////////////////////////////////////////////
set_checkpoint(const char * file,const char * func,std::size_t line)556     inline void set_checkpoint(const char* file, const char* func, std::size_t line)
557     {
558         checkpoint& cp = detail::global_checkpoint();
559         cp.file = file;
560         cp.func = func;
561         cp.line = line;
562     }
563 
564     ////////////////////////////////////////////////////////////////////////////
get_checkpoint()565     inline checkpoint const & get_checkpoint()
566     {
567         return detail::global_checkpoint();
568     }
569 
570     ////////////////////////////////////////////////////////////////////////////
571     class test_suite
572     {
573     public:
574         typedef test_case const* iterator;
575         typedef iterator const_iterator;
576 
577     public:
test_suite(const char * xname)578         test_suite(const char *xname)
579           : m_name(xname), m_tests(), m_size(0)
580         {
581             assert(xname);
582         }
583 
584     public:
name()585         const char *name() const { return m_name; }
586 
size()587         std::size_t size() const { return m_size; }
588 
589         test_case const & operator[](std::size_t i) const
590         {
591             assert(i < m_size);
592             return m_tests[i];
593         }
594 
begin()595         const_iterator begin() const
596         { return m_tests; }
597 
end()598         const_iterator end() const
599         {
600             return m_tests + m_size;
601         }
602 
603     public:
register_test(test_case tc)604         std::size_t register_test(test_case tc)
605         {
606             static std::size_t test_case_max = sizeof(m_tests) / sizeof(test_case);
607             assert(m_size < test_case_max);
608             m_tests[m_size] = tc;
609             return m_size++;
610         }
611 
612     private:
613         test_suite(test_suite const &);
614         test_suite & operator=(test_suite const &);
615 
616     private:
617         const char* m_name;
618         // Since fast compile times in a priority, we use simple containers
619         // with hard limits.
620         test_case m_tests[256];
621         std::size_t m_size;
622     };
623 
624     ////////////////////////////////////////////////////////////////////////////
625     class registrar
626     {
627     public:
registrar(test_suite & st,test_case tc)628         registrar(test_suite & st, test_case tc)
629         {
630             st.register_test(tc);
631         }
632     };
633 
634     ////////////////////////////////////////////////////////////////////////////
635     class test_reporter
636     {
637     public:
test_reporter()638         test_reporter()
639             : m_testcases(0), m_testcase_failures(0), m_unsupported(0),
640               m_assertions(0), m_warning_failures(0), m_check_failures(0),
641               m_require_failures(0), m_uncaught_exceptions(0), m_failure()
642         {
643         }
644 
test_case_begin()645         void test_case_begin()
646         {
647             ++m_testcases;
648             clear_failure();
649         }
650 
test_case_end()651         void test_case_end()
652         {
653             if (m_failure.type != failure_type::none
654                 && m_failure.type !=  failure_type::unsupported) {
655                 ++m_testcase_failures;
656             }
657         }
658 
659 # if defined(__GNUC__)
660 #   pragma GCC diagnostic push
661 #   pragma GCC diagnostic ignored "-Wswitch-default"
662 # endif
663         // Each assertion and failure is reported through this function.
report(test_outcome o)664         void report(test_outcome o)
665         {
666             ++m_assertions;
667             switch (o.type)
668             {
669             case failure_type::none:
670                 break;
671             case failure_type::unsupported:
672                 ++m_unsupported;
673                 m_failure = o;
674                 break;
675             case failure_type::warn:
676                 ++m_warning_failures;
677                 report_error(o);
678                 break;
679             case failure_type::check:
680                 ++m_check_failures;
681                 report_error(o);
682                 m_failure = o;
683                 break;
684             case failure_type::require:
685                 ++m_require_failures;
686                 report_error(o);
687                 m_failure = o;
688                 break;
689             case failure_type::assert:
690                 report_error(o);
691                 break;
692             case failure_type::uncaught_exception:
693                 ++m_uncaught_exceptions;
694                 std::fprintf(stderr
695                     , "Test case FAILED with uncaught exception:\n"
696                       "    last checkpoint near %s::%lu %s\n\n"
697                     , o.file, o.line, o.func
698                     );
699                 m_failure = o;
700                 break;
701             }
702         }
703 # if defined(__GNUC__)
704 #   pragma GCC diagnostic pop
705 # endif
706 
current_failure()707         test_outcome current_failure() const
708         {
709             return m_failure;
710         }
711 
clear_failure()712         void clear_failure()
713         {
714             m_failure.type = failure_type::none;
715             m_failure.file = "";
716             m_failure.func = "";
717             m_failure.line = 0;
718             m_failure.expression = "";
719             m_failure.message = "";
720         }
721 
test_case_count()722         std::size_t test_case_count() const
723         { return m_testcases; }
724 
test_case_failure_count()725         std::size_t test_case_failure_count() const
726         { return m_testcase_failures; }
727 
unsupported_count()728         std::size_t unsupported_count() const
729         { return m_unsupported; }
730 
assertion_count()731         std::size_t assertion_count() const
732         { return m_assertions; }
733 
warning_failure_count()734         std::size_t warning_failure_count() const
735         { return m_warning_failures; }
736 
check_failure_count()737         std::size_t check_failure_count() const
738         { return m_check_failures; }
739 
require_failure_count()740         std::size_t require_failure_count() const
741         { return m_require_failures; }
742 
failure_count()743         std::size_t failure_count() const
744         { return m_check_failures + m_require_failures + m_uncaught_exceptions; }
745 
746         // Print a summary of what was run and the outcome.
print_summary(const char * suitename)747         void print_summary(const char* suitename) const
748         {
749             FILE* out = failure_count() ? stderr : stdout;
750             std::size_t testcases_run = m_testcases - m_unsupported;
751             std::fprintf(out, "Summary for testsuite %s:\n", suitename);
752             std::fprintf(out, "    %lu of %lu test cases passed.\n", testcases_run - m_testcase_failures, testcases_run);
753             std::fprintf(out, "    %lu of %lu assertions passed.\n", m_assertions - (m_warning_failures + m_check_failures + m_require_failures), m_assertions);
754             std::fprintf(out, "    %lu unsupported test case%s.\n", m_unsupported, (m_unsupported != 1 ? "s" : ""));
755         }
756 
757     private:
758         test_reporter(test_reporter const &);
759         test_reporter const & operator=(test_reporter const &);
760 
report_error(test_outcome o)761         void report_error(test_outcome o) const
762         {
763             std::fprintf(stderr, "In %s:%lu Assertion %s failed.\n    in file: %s\n    %s\n"
764                 , o.func, o.line, o.expression, o.file,  o.message ? o.message : ""
765               );
766         }
767 
768     private:
769         // counts of testcases, failed testcases, and unsupported testcases.
770         std::size_t m_testcases;
771         std::size_t m_testcase_failures;
772         std::size_t m_unsupported;
773 
774         // counts of assertions and assertion failures.
775         std::size_t m_assertions;
776         std::size_t m_warning_failures;
777         std::size_t m_check_failures;
778         std::size_t m_require_failures;
779         std::size_t m_uncaught_exceptions;
780 
781         // The last failure. This is cleared between testcases.
782         test_outcome m_failure;
783     };
784 
785     ////////////////////////////////////////////////////////////////////////////
get_reporter()786     inline test_reporter & get_reporter()
787     {
788         static test_reporter o;
789         return o;
790     }
791 
792     ////////////////////////////////////////////////////////////////////////////
793     class test_runner
794     {
795     public:
test_runner(test_suite & ts)796         test_runner(test_suite & ts)
797           : m_ts(ts)
798         {}
799 
800     public:
run()801         int run()
802         {
803             // for each testcase
804             for (test_suite::const_iterator b = m_ts.begin(), e = m_ts.end();
805                  b != e; ++b)
806             {
807                 test_case const& tc = *b;
808                 set_checkpoint(tc.file, tc.func, tc.line);
809                 get_reporter().test_case_begin();
810 #ifndef TEST_HAS_NO_EXCEPTIONS
811                 try {
812 #endif
813                     tc.invoke();
814 #ifndef TEST_HAS_NO_EXCEPTIONS
815                 } catch (...) {
816                     test_outcome o;
817                     o.type = failure_type::uncaught_exception;
818                     o.file = get_checkpoint().file;
819                     o.func = get_checkpoint().func;
820                     o.line = get_checkpoint().line;
821                     o.expression = "";
822                     o.message = "";
823                     get_reporter().report(o);
824                 }
825 #endif
826                 get_reporter().test_case_end();
827             }
828             auto exit_code = get_reporter().failure_count() ? EXIT_FAILURE : EXIT_SUCCESS;
829             if (exit_code == EXIT_FAILURE || get_reporter().unsupported_count())
830               get_reporter().print_summary(m_ts.name());
831             return exit_code;
832         }
833 
834     private:
835         test_runner(test_runner const &);
836         test_runner operator=(test_runner const &);
837 
838         test_suite & m_ts;
839     };
840 
841     namespace detail
842     {
843         template <class Iter1, class Iter2>
check_equal_collections_impl(Iter1 start1,Iter1 const end1,Iter2 start2,Iter2 const end2)844         bool check_equal_collections_impl(
845             Iter1 start1, Iter1 const end1
846           , Iter2 start2, Iter2 const end2
847           )
848         {
849             while (start1 != end1 && start2 != end2) {
850                 if (*start1 != *start2) {
851                     return false;
852                 }
853                 ++start1; ++start2;
854             }
855             return (start1 == end1 && start2 == end2);
856         }
857     }                                                       // namespace detail
858 
859 }                                                    // namespace rapid_cxx_test
860 
861 
862 # if defined(__GNUC__)
863 #   pragma GCC diagnostic pop
864 # endif
865 
866 #endif /* RAPID_CXX_TEST_H */
867