1 /*
2  *  Created by Phil on 18/10/2010.
3  *  Copyright 2010 Two Blue Cubes Ltd. All rights reserved.
4  *
5  *  Distributed under the Boost Software License, Version 1.0. (See accompanying
6  *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7  */
8 #ifndef TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED
9 #define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED
10 
11 #include "catch_common.h"
12 #include "catch_interfaces_testcase.h"
13 #include "catch_compiler_capabilities.h"
14 #include "catch_stringref.h"
15 #include "catch_type_traits.hpp"
16 #include "catch_preprocessor.hpp"
17 #include "catch_meta.hpp"
18 
19 namespace Catch {
20 
21 template<typename C>
22 class TestInvokerAsMethod : public ITestInvoker {
23     void (C::*m_testAsMethod)();
24 public:
TestInvokerAsMethod(void (C::* testAsMethod)())25     TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {}
26 
invoke()27     void invoke() const override {
28         C obj;
29         (obj.*m_testAsMethod)();
30     }
31 };
32 
33 auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker*;
34 
35 template<typename C>
36 auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* {
37     return new(std::nothrow) TestInvokerAsMethod<C>( testAsMethod );
38 }
39 
40 struct NameAndTags {
41     NameAndTags( StringRef const& name_ = StringRef(), StringRef const& tags_ = StringRef() ) noexcept;
42     StringRef name;
43     StringRef tags;
44 };
45 
46 struct AutoReg : NonCopyable {
47     AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept;
48     ~AutoReg();
49 };
50 
51 } // end namespace Catch
52 
53 #if defined(CATCH_CONFIG_DISABLE)
54     #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \
55         static void TestName()
56     #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \
57         namespace{                        \
58             struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
59                 void test();              \
60             };                            \
61         }                                 \
62         void TestName::test()
63     #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION( TestName, ... )  \
64         template<typename TestType>                                             \
65         static void TestName()
66     #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... )    \
67         namespace{                                                                                  \
68             template<typename TestType>                                                             \
69             struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) {     \
70                 void test();                                                                        \
71             };                                                                                      \
72         }                                                                                           \
73         template<typename TestType>                                                                 \
74         void TestName::test()
75 #endif
76 
77     ///////////////////////////////////////////////////////////////////////////////
78     #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
79         static void TestName(); \
80         CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
81         namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
82         CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
83         static void TestName()
84     #define INTERNAL_CATCH_TESTCASE( ... ) \
85         INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ )
86 
87     ///////////////////////////////////////////////////////////////////////////////
88     #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
89         CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
90         namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
91         CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
92 
93     ///////////////////////////////////////////////////////////////////////////////
94     #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
95         CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
96         namespace{ \
97             struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
98                 void test(); \
99             }; \
100             Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
101         } \
102         CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
103         void TestName::test()
104     #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
105         INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ )
106 
107     ///////////////////////////////////////////////////////////////////////////////
108     #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
109         CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
110         Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
111         CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
112 
113     ///////////////////////////////////////////////////////////////////////////////
114     #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, ... )\
115         CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
116         template<typename TestType> \
117         static void TestFunc();\
118         namespace {\
119             template<typename...Types> \
120             struct TestName{\
121                 template<typename...Ts> \
122                 TestName(Ts...names){\
123                     CATCH_INTERNAL_CHECK_UNIQUE_TYPES(CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)) \
124                     using expander = int[];\
125                     (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ names, Tags } ), 0)... };/* NOLINT */ \
126                 }\
127             };\
128             INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestName, Name, __VA_ARGS__) \
129         }\
130         CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
131         template<typename TestType> \
132         static void TestFunc()
133 
134 #if defined(CATCH_CPP17_OR_GREATER)
135 #define CATCH_INTERNAL_CHECK_UNIQUE_TYPES(...) static_assert(Catch::is_unique<__VA_ARGS__>,"Duplicate type detected in declaration of template test case");
136 #else
137 #define CATCH_INTERNAL_CHECK_UNIQUE_TYPES(...) static_assert(Catch::is_unique<__VA_ARGS__>::value,"Duplicate type detected in declaration of template test case");
138 #endif
139 
140 #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
141     #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
142         INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, __VA_ARGS__ )
143 #else
144     #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
145         INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, __VA_ARGS__ ) )
146 #endif
147 
148     #define INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestName, Name, ...)\
149         static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
150             TestName<CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)>(CATCH_REC_LIST_UD(INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME,Name, __VA_ARGS__));\
151             return 0;\
152         }();
153 
154     #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, TmplTypes, TypesList) \
155         CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS                      \
156         template<typename TestType> static void TestFuncName();       \
157         namespace {                                                   \
158             template<typename... Types>                               \
159             struct TestName {                                         \
160                 TestName() {                                          \
161                     CATCH_INTERNAL_CHECK_UNIQUE_TYPES(Types...)       \
162                     int index = 0;                                    \
163                     using expander = int[];                           \
164                     constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
165                     constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
166                     constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
167                     (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++, 0)... };/* NOLINT */\
168                 }                                                     \
169             };                                                        \
170             static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
171                 using TestInit = Catch::combine<INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)> \
172                             ::with_types<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(TypesList)>::into<TestName>::type; \
173                 TestInit();                                           \
174                 return 0;                                             \
175             }();                                                      \
176         }                                                             \
177         CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS                    \
178         template<typename TestType>                                   \
179         static void TestFuncName()
180 
181 #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
182     #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\
183         INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ),Name,Tags,__VA_ARGS__)
184 #else
185     #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\
186         INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, __VA_ARGS__ ) )
187 #endif
188 
189     #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, ... ) \
190         CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
191         namespace{ \
192             template<typename TestType> \
193             struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
194                 void test();\
195             };\
196             template<typename...Types> \
197             struct TestNameClass{\
198                 template<typename...Ts> \
199                 TestNameClass(Ts...names){\
200                     CATCH_INTERNAL_CHECK_UNIQUE_TYPES(CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)) \
201                     using expander = int[];\
202                     (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ names, Tags } ), 0)... };/* NOLINT */ \
203                 }\
204             };\
205             INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestNameClass, Name, __VA_ARGS__)\
206         }\
207         CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS\
208         template<typename TestType> \
209         void TestName<TestType>::test()
210 
211 #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
212     #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
213         INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, __VA_ARGS__ )
214 #else
215     #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
216         INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, __VA_ARGS__ ) )
217 #endif
218 
219     #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, TmplTypes, TypesList)\
220         CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
221         template<typename TestType> \
222             struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
223                 void test();\
224             };\
225         namespace {\
226             template<typename...Types>\
227             struct TestNameClass{\
228                 TestNameClass(){\
229                     CATCH_INTERNAL_CHECK_UNIQUE_TYPES(Types...)\
230                     int index = 0;\
231                     using expander = int[];\
232                     constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
233                     constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
234                     constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
235                     (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++, 0)... };/* NOLINT */ \
236                 }\
237             };\
238             static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
239                 using TestInit = Catch::combine<INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>\
240                             ::with_types<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(TypesList)>::into<TestNameClass>::type;\
241                 TestInit();\
242                 return 0;\
243             }(); \
244         }\
245         CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
246         template<typename TestType> \
247         void TestName<TestType>::test()
248 
249 #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
250     #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
251         INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, __VA_ARGS__ )
252 #else
253     #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
254         INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, __VA_ARGS__ ) )
255 #endif
256 
257 #endif // TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED
258