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