• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #ifndef RAPID_CXX_TEST_HPP
2 #define RAPID_CXX_TEST_HPP
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 #else // TEST_HAS_NO_EXCEPTIONS
225 
226 # define TEST_CHECK_NO_THROW(...)                                                      \
227     do {                                                                               \
228         TEST_SET_CHECKPOINT();                                                         \
229         ::rapid_cxx_test::test_outcome m_f(                                            \
230             ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
231             , "TEST_CHECK_NO_THROW(" #__VA_ARGS__ ")", ""                              \
232             );                                                                         \
233         (static_cast<void>(__VA_ARGS__));                                              \
234         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
235     } while (false)
236 #
237 
238 #define TEST_CHECK_THROW(Except, ...) ((void)0)
239 
240 #endif // TEST_HAS_NO_EXCEPTIONS
241 
242 
243 ////////////////////////////////////////////////////////////////////////////////
244 //                    TEST_REQUIRE_NO_THROW / TEST_REQUIRE_THROWs
245 ////////////////////////////////////////////////////////////////////////////////
246 #ifndef TEST_HAS_NO_EXCEPTIONS
247 
248 # define TEST_REQUIRE_NO_THROW(...)                                                    \
249     do {                                                                               \
250         TEST_SET_CHECKPOINT();                                                         \
251         ::rapid_cxx_test::test_outcome m_f(                                            \
252             ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
253             , "TEST_REQUIRE_NO_THROW(" #__VA_ARGS__ ")", ""                            \
254             );                                                                         \
255         try {                                                                          \
256             (static_cast<void>(__VA_ARGS__));                                          \
257         } catch (...) {                                                                \
258             m_f.type = ::rapid_cxx_test::failure_type::require;                        \
259         }                                                                              \
260         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
261         if (m_f.type != ::rapid_cxx_test::failure_type::none) {                        \
262             return;                                                                    \
263         }                                                                              \
264     } while (false)
265 #
266 
267 # define TEST_REQUIRE_THROW(Except, ...)                                               \
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_THROW(" #Except "," #__VA_ARGS__ ")", ""                   \
273             );                                                                         \
274         try {                                                                          \
275             (static_cast<void>(__VA_ARGS__));                                          \
276             m_f.type = ::rapid_cxx_test::failure_type::require;                        \
277         } catch (Except const &) {}                                                    \
278         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
279         if (m_f.type != ::rapid_cxx_test::failure_type::none) {                        \
280             return;                                                                    \
281         }                                                                              \
282     } while (false)
283 #
284 
285 #else // TEST_HAS_NO_EXCEPTIONS
286 
287 # define TEST_REQUIRE_NO_THROW(...)                                                    \
288     do {                                                                               \
289         TEST_SET_CHECKPOINT();                                                         \
290         ::rapid_cxx_test::test_outcome m_f(                                            \
291             ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
292             , "TEST_REQUIRE_NO_THROW(" #__VA_ARGS__ ")", ""                            \
293             );                                                                         \
294         (static_cast<void>(__VA_ARGS__));                                              \
295         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
296     } while (false)
297 #
298 
299 #define TEST_REQUIRE_THROW(Except, ...) ((void)0)
300 
301 #endif // TEST_HAS_NO_EXCEPTIONS
302 
303 ////////////////////////////////////////////////////////////////////////////////
304 //                    TEST_ASSERT_NO_THROW / TEST_ASSERT_THROW
305 ////////////////////////////////////////////////////////////////////////////////
306 #ifndef TEST_HAS_NO_EXCEPTIONS
307 
308 # define TEST_ASSERT_NO_THROW(...)                                                     \
309     do {                                                                               \
310         TEST_SET_CHECKPOINT();                                                         \
311         ::rapid_cxx_test::test_outcome m_f(                                            \
312             ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
313             , "TEST_ASSERT_NO_THROW(" #__VA_ARGS__ ")", ""                             \
314             );                                                                         \
315         try {                                                                          \
316             (static_cast<void>(__VA_ARGS__));                                          \
317         } catch (...) {                                                                \
318             m_f.type = ::rapid_cxx_test::failure_type::assert;                         \
319         }                                                                              \
320         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
321         if (m_f.type != ::rapid_cxx_test::failure_type::none) {                        \
322             std::abort();                                                              \
323         }                                                                              \
324     } while (false)
325 #
326 
327 # define TEST_ASSERT_THROW(Except, ...)                                                \
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_THROW(" #Except "," #__VA_ARGS__ ")", ""                    \
333             );                                                                         \
334         try {                                                                          \
335             (static_cast<void>(__VA_ARGS__));                                          \
336             m_f.type = ::rapid_cxx_test::failure_type::assert;                         \
337         } catch (Except const &) {}                                                    \
338         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
339         if (m_f.type != ::rapid_cxx_test::failure_type::none) {                        \
340             std::abort();                                                              \
341         }                                                                              \
342     } while (false)
343 #
344 
345 #else // TEST_HAS_NO_EXCEPTIONS
346 
347 # define TEST_ASSERT_NO_THROW(...)                                                     \
348     do {                                                                               \
349         TEST_SET_CHECKPOINT();                                                         \
350         ::rapid_cxx_test::test_outcome m_f(                                            \
351             ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
352             , "TEST_ASSERT_NO_THROW(" #__VA_ARGS__ ")", ""                             \
353             );                                                                         \
354         (static_cast<void>(__VA_ARGS__));                                              \
355         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
356     } while (false)
357 #
358 
359 #define TEST_ASSERT_THROW(Except, ...) ((void)0)
360 
361 #endif // TEST_HAS_NO_EXCEPTIONS
362 
363 ////////////////////////////////////////////////////////////////////////////////
364 //
365 ////////////////////////////////////////////////////////////////////////////////
366 
367 # define TEST_WARN_EQUAL_COLLECTIONS(...)                                              \
368     do {                                                                               \
369         TEST_SET_CHECKPOINT();                                                         \
370         ::rapid_cxx_test::test_outcome m_f(                                            \
371           ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__   \
372           , "TEST_WARN_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", ""                        \
373         );                                                                             \
374         if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \
375             m_f.type = ::rapid_cxx_test::failure_type::warn;                           \
376         }                                                                              \
377         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
378     } while (false)
379 #
380 
381 # define TEST_CHECK_EQUAL_COLLECTIONS(...)                                             \
382     do {                                                                               \
383         TEST_SET_CHECKPOINT();                                                         \
384         ::rapid_cxx_test::test_outcome m_f(                                            \
385           ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__   \
386           , "TEST_CHECK_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", ""                       \
387         );                                                                             \
388         if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \
389             m_f.type = ::rapid_cxx_test::failure_type::check;                          \
390         }                                                                              \
391         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
392     } while (false)
393 #
394 
395 # define TEST_REQUIRE_EQUAL_COLLECTIONS(...)                                           \
396     do {                                                                               \
397         TEST_SET_CHECKPOINT();                                                         \
398         ::rapid_cxx_test::test_outcome m_f(                                            \
399           ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__   \
400           , "TEST_REQUIRE_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", ""                     \
401         );                                                                             \
402         if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \
403             m_f.type = ::rapid_cxx_test::failure_type::require;                        \
404         }                                                                              \
405         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
406         if (m_f.type != ::rapid_cxx_test::failure_type::none) {                        \
407             return;                                                                    \
408         }                                                                              \
409     } while (false)
410 #
411 
412 # define TEST_ASSERT_EQUAL_COLLECTIONS(...)                                            \
413     do {                                                                               \
414         TEST_SET_CHECKPOINT();                                                         \
415         ::rapid_cxx_test::test_outcome m_f(                                            \
416           ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__   \
417           , "TEST_ASSERT_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", ""                      \
418         );                                                                             \
419         if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \
420             m_f.type = ::rapid_cxx_test::failure_type::assert;                         \
421         }                                                                              \
422         ::rapid_cxx_test::get_reporter().report(m_f);                                  \
423         if (m_f.type != ::rapid_cxx_test::failure_type::none) {                        \
424           ::std::abort();                                                              \
425         }                                                                              \
426     } while (false)
427 #
428 
429 namespace rapid_cxx_test
430 {
431     typedef void (*invoker_t)();
432 
433     ////////////////////////////////////////////////////////////////////////////
434     struct test_case
435     {
test_caserapid_cxx_test::test_case436         test_case()
437             : file(""), func(""), line(0), invoke(NULL)
438         {}
439 
test_caserapid_cxx_test::test_case440         test_case(const char* file1, const char* func1, std::size_t line1,
441                   invoker_t invoke1)
442             : file(file1), func(func1), line(line1), invoke(invoke1)
443         {}
444 
445         const char *file;
446         const char *func;
447         std::size_t line;
448         invoker_t invoke;
449     };
450 
451     ////////////////////////////////////////////////////////////////////////////
452     struct failure_type
453     {
454         enum enum_type {
455             none,
456             unsupported,
457             warn,
458             check,
459             require,
460             assert,
461             uncaught_exception
462         };
463     };
464 
465     typedef failure_type::enum_type failure_type_t;
466 
467     ////////////////////////////////////////////////////////////////////////////
468     struct test_outcome
469     {
test_outcomerapid_cxx_test::test_outcome470         test_outcome()
471             : type(failure_type::none),
472               file(""), func(""), line(0),
473               expression(""), message("")
474         {}
475 
test_outcomerapid_cxx_test::test_outcome476         test_outcome(failure_type_t type1, const char* file1, const char* func1,
477                      std::size_t line1, const char* expression1,
478                      const char* message1)
479             : type(type1), file(file1), func(func1), line(line1),
480               expression(expression1), message(message1)
481         {
482             trim_func_string();
483         }
484 
485         failure_type_t type;
486         const char *file;
487         const char *func;
488         std::size_t line;
489         const char *expression;
490         const char *message;
491 
492     private:
trim_file_stringrapid_cxx_test::test_outcome493         void trim_file_string() {
494             const char* f_start  = file;
495             const char* prev_start = f_start;
496             const char* last_start = f_start;
497             char last;
498             while ((last = *f_start) != '\0') {
499                 ++f_start;
500                 if (last == '/' && *f_start) {
501                     prev_start = last_start;
502                     last_start = f_start;
503                 }
504             }
505             file = prev_start;
506         }
trim_func_stringrapid_cxx_test::test_outcome507       void trim_func_string() {
508           const char* void_loc = ::strstr(func, "void ");
509           if (void_loc == func) {
510               func += strlen("void ");
511           }
512           const char* namespace_loc = ::strstr(func, "::");
513           if (namespace_loc) {
514               func = namespace_loc + 2;
515           }
516       }
517     };
518 
519     ////////////////////////////////////////////////////////////////////////////
520     struct checkpoint
521     {
522         const char *file;
523         const char *func;
524         std::size_t line;
525     };
526 
527     namespace detail
528     {
global_checkpoint()529         inline checkpoint & global_checkpoint()
530         {
531             static checkpoint cp = {"", "", 0};
532             return cp;
533         }
534     }
535 
536     ////////////////////////////////////////////////////////////////////////////
set_checkpoint(const char * file,const char * func,std::size_t line)537     inline void set_checkpoint(const char* file, const char* func, std::size_t line)
538     {
539         checkpoint& cp = detail::global_checkpoint();
540         cp.file = file;
541         cp.func = func;
542         cp.line = line;
543     }
544 
545     ////////////////////////////////////////////////////////////////////////////
get_checkpoint()546     inline checkpoint const & get_checkpoint()
547     {
548         return detail::global_checkpoint();
549     }
550 
551     ////////////////////////////////////////////////////////////////////////////
552     class test_suite
553     {
554     public:
555         typedef test_case const* iterator;
556         typedef iterator const_iterator;
557 
558     public:
test_suite(const char * xname)559         test_suite(const char *xname)
560           : m_name(xname), m_tests(), m_size(0)
561         {
562             assert(xname);
563         }
564 
565     public:
name() const566         const char *name() const { return m_name; }
567 
size() const568         std::size_t size() const { return m_size; }
569 
operator [](std::size_t i) const570         test_case const & operator[](std::size_t i) const
571         {
572             assert(i < m_size);
573             return m_tests[i];
574         }
575 
begin() const576         const_iterator begin() const
577         { return m_tests; }
578 
end() const579         const_iterator end() const
580         {
581             return m_tests + m_size;
582         }
583 
584     public:
register_test(test_case tc)585         std::size_t register_test(test_case tc)
586         {
587             static std::size_t test_case_max = sizeof(m_tests) / sizeof(test_case);
588             assert(m_size < test_case_max);
589             m_tests[m_size] = tc;
590             return m_size++;
591         }
592 
593     private:
594         test_suite(test_suite const &);
595         test_suite & operator=(test_suite const &);
596 
597     private:
598         const char* m_name;
599         // Since fast compile times in a priority, we use simple containers
600         // with hard limits.
601         test_case m_tests[256];
602         std::size_t m_size;
603     };
604 
605     ////////////////////////////////////////////////////////////////////////////
606     class registrar
607     {
608     public:
registrar(test_suite & st,test_case tc)609         registrar(test_suite & st, test_case tc)
610         {
611             st.register_test(tc);
612         }
613     };
614 
615     ////////////////////////////////////////////////////////////////////////////
616     class test_reporter
617     {
618     public:
test_reporter()619         test_reporter()
620             : m_testcases(0), m_testcase_failures(0), m_unsupported(0),
621               m_assertions(0), m_warning_failures(0), m_check_failures(0),
622               m_require_failures(0), m_uncaught_exceptions(0), m_failure()
623         {
624         }
625 
test_case_begin()626         void test_case_begin()
627         {
628             ++m_testcases;
629             clear_failure();
630         }
631 
test_case_end()632         void test_case_end()
633         {
634             if (m_failure.type != failure_type::none
635                 && m_failure.type !=  failure_type::unsupported) {
636                 ++m_testcase_failures;
637             }
638         }
639 
640 # if defined(__GNUC__)
641 #   pragma GCC diagnostic push
642 #   pragma GCC diagnostic ignored "-Wswitch-default"
643 # endif
644         // Each assertion and failure is reported through this function.
report(test_outcome o)645         void report(test_outcome o)
646         {
647             ++m_assertions;
648             switch (o.type)
649             {
650             case failure_type::none:
651                 break;
652             case failure_type::unsupported:
653                 ++m_unsupported;
654                 m_failure = o;
655                 break;
656             case failure_type::warn:
657                 ++m_warning_failures;
658                 report_error(o);
659                 break;
660             case failure_type::check:
661                 ++m_check_failures;
662                 report_error(o);
663                 m_failure = o;
664                 break;
665             case failure_type::require:
666                 ++m_require_failures;
667                 report_error(o);
668                 m_failure = o;
669                 break;
670             case failure_type::assert:
671                 report_error(o);
672                 break;
673             case failure_type::uncaught_exception:
674                 ++m_uncaught_exceptions;
675                 std::fprintf(stderr
676                     , "Test case FAILED with uncaught exception:\n"
677                       "    last checkpoint near %s::%lu %s\n\n"
678                     , o.file, o.line, o.func
679                     );
680                 m_failure = o;
681                 break;
682             }
683         }
684 # if defined(__GNUC__)
685 #   pragma GCC diagnostic pop
686 # endif
687 
current_failure() const688         test_outcome current_failure() const
689         {
690             return m_failure;
691         }
692 
clear_failure()693         void clear_failure()
694         {
695             m_failure.type = failure_type::none;
696             m_failure.file = "";
697             m_failure.func = "";
698             m_failure.line = 0;
699             m_failure.expression = "";
700             m_failure.message = "";
701         }
702 
test_case_count() const703         std::size_t test_case_count() const
704         { return m_testcases; }
705 
test_case_failure_count() const706         std::size_t test_case_failure_count() const
707         { return m_testcase_failures; }
708 
unsupported_count() const709         std::size_t unsupported_count() const
710         { return m_unsupported; }
711 
assertion_count() const712         std::size_t assertion_count() const
713         { return m_assertions; }
714 
warning_failure_count() const715         std::size_t warning_failure_count() const
716         { return m_warning_failures; }
717 
check_failure_count() const718         std::size_t check_failure_count() const
719         { return m_check_failures; }
720 
require_failure_count() const721         std::size_t require_failure_count() const
722         { return m_require_failures; }
723 
failure_count() const724         std::size_t failure_count() const
725         { return m_check_failures + m_require_failures + m_uncaught_exceptions; }
726 
727         // Print a summary of what was run and the outcome.
print_summary(const char * suitename) const728         void print_summary(const char* suitename) const
729         {
730             FILE* out = failure_count() ? stderr : stdout;
731             std::size_t testcases_run = m_testcases - m_unsupported;
732             std::fprintf(out, "Summary for testsuite %s:\n", suitename);
733             std::fprintf(out, "    %lu of %lu test cases passed.\n", testcases_run - m_testcase_failures, testcases_run);
734             std::fprintf(out, "    %lu of %lu assertions passed.\n", m_assertions - (m_warning_failures + m_check_failures + m_require_failures), m_assertions);
735             std::fprintf(out, "    %lu unsupported test case%s.\n", m_unsupported, (m_unsupported != 1 ? "s" : ""));
736         }
737 
738     private:
739         test_reporter(test_reporter const &);
740         test_reporter const & operator=(test_reporter const &);
741 
report_error(test_outcome o) const742         void report_error(test_outcome o) const
743         {
744             std::fprintf(stderr, "In %s:%lu Assertion %s failed.\n    in file: %s\n    %s\n"
745                 , o.func, o.line, o.expression, o.file,  o.message ? o.message : ""
746               );
747         }
748 
749     private:
750         // counts of testcases, failed testcases, and unsupported testcases.
751         std::size_t m_testcases;
752         std::size_t m_testcase_failures;
753         std::size_t m_unsupported;
754 
755         // counts of assertions and assertion failures.
756         std::size_t m_assertions;
757         std::size_t m_warning_failures;
758         std::size_t m_check_failures;
759         std::size_t m_require_failures;
760         std::size_t m_uncaught_exceptions;
761 
762         // The last failure. This is cleared between testcases.
763         test_outcome m_failure;
764     };
765 
766     ////////////////////////////////////////////////////////////////////////////
get_reporter()767     inline test_reporter & get_reporter()
768     {
769         static test_reporter o;
770         return o;
771     }
772 
773     ////////////////////////////////////////////////////////////////////////////
774     class test_runner
775     {
776     public:
test_runner(test_suite & ts)777         test_runner(test_suite & ts)
778           : m_ts(ts)
779         {}
780 
781     public:
run()782         int run()
783         {
784             // for each testcase
785             for (test_suite::const_iterator b = m_ts.begin(), e = m_ts.end();
786                  b != e; ++b)
787             {
788                 test_case const& tc = *b;
789                 set_checkpoint(tc.file, tc.func, tc.line);
790                 get_reporter().test_case_begin();
791 #ifndef TEST_HAS_NO_EXCEPTIONS
792                 try {
793 #endif
794                     tc.invoke();
795 #ifndef TEST_HAS_NO_EXCEPTIONS
796                 } catch (...) {
797                     test_outcome o;
798                     o.type = failure_type::uncaught_exception;
799                     o.file = get_checkpoint().file;
800                     o.func = get_checkpoint().func;
801                     o.line = get_checkpoint().line;
802                     o.expression = "";
803                     o.message = "";
804                     get_reporter().report(o);
805                 }
806 #endif
807                 get_reporter().test_case_end();
808             }
809             auto exit_code = get_reporter().failure_count() ? EXIT_FAILURE : EXIT_SUCCESS;
810             if (exit_code == EXIT_FAILURE)
811                 get_reporter().print_summary(m_ts.name());
812             return exit_code;
813         }
814 
815     private:
816         test_runner(test_runner const &);
817         test_runner operator=(test_runner const &);
818 
819         test_suite & m_ts;
820     };
821 
822     namespace detail
823     {
824         template <class Iter1, class Iter2>
check_equal_collections_impl(Iter1 start1,Iter1 const end1,Iter2 start2,Iter2 const end2)825         bool check_equal_collections_impl(
826             Iter1 start1, Iter1 const end1
827           , Iter2 start2, Iter2 const end2
828           )
829         {
830             while (start1 != end1 && start2 != end2) {
831                 if (*start1 != *start2) {
832                     return false;
833                 }
834                 ++start1; ++start2;
835             }
836             return (start1 == end1 && start2 == end2);
837         }
838     }                                                       // namespace detail
839 
840 }                                                    // namespace rapid_cxx_test
841 
842 
843 # if defined(__GNUC__)
844 #   pragma GCC diagnostic pop
845 # endif
846 
847 #endif /* RAPID_CXX_TEST_HPP */
848