diff --git a/include/internal/catch_capture.hpp b/include/internal/catch_capture.hpp index dbc1ffb6..358f0362 100644 --- a/include/internal/catch_capture.hpp +++ b/include/internal/catch_capture.hpp @@ -639,6 +639,25 @@ public: return *this; } + /////////////////////////////////////////////////////////////////////////// + template + ResultBuilder& acceptMatcher + ( + const MatcherT& matcher, + ArgT* arg, + const std::string& matcherCallAsString + ) + { + std::string matcherAsString = Catch::toString( matcher ); + if( matcherAsString == "{?}" ) + matcherAsString = matcherCallAsString; + m_result.setLhs( Catch::toString( arg ) ); + m_result.setRhs( matcherAsString ); + m_result.setOp( "matches" ); + m_result.setResultType( matcher( arg ) ? ResultWas::Ok : ResultWas::ExpressionFailed ); + return *this; + } + /////////////////////////////////////////////////////////////////////////// ResultBuilder& setResultType ( diff --git a/include/internal/catch_default_main.hpp b/include/internal/catch_default_main.hpp index 01d7105e..86e66391 100644 --- a/include/internal/catch_default_main.hpp +++ b/include/internal/catch_default_main.hpp @@ -17,13 +17,17 @@ int main (int argc, char * const argv[]) { #ifdef __OBJC__ +#if !CATCH_ARC_ENABLED NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; +#endif Catch::registerTestMethods(); int result = Catch::Main( argc, (char* const*)argv ); +#if !CATCH_ARC_ENABLED [pool drain]; +#endif #else diff --git a/include/internal/catch_objc.hpp b/include/internal/catch_objc.hpp index f8044a0c..8d6d23b6 100644 --- a/include/internal/catch_objc.hpp +++ b/include/internal/catch_objc.hpp @@ -23,6 +23,39 @@ // header for non obj-usage #include "internal/catch_test_case_info.hpp" +#ifdef __has_feature +#define CATCH_ARC_ENABLED __has_feature(objc_arc) +#else +#define CATCH_ARC_ENABLED 0 +#endif + +void arcSafeRelease( NSObject* obj ); +id performOptionalSelector( id obj, SEL sel ); + +#if !CATCH_ARC_ENABLED +inline void arcSafeRelease( NSObject* obj ) +{ + [obj release]; +} +inline id performOptionalSelector( id obj, SEL sel ) +{ + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; + return nil; +} +#else +inline void arcSafeRelease( NSObject* ){} +inline id performOptionalSelector( id obj, SEL sel ) +{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; +#pragma clang diagnostic pop + return nil; +} +#endif + /////////////////////////////////////////////////////////////////////////////// // This protocol is really only here for (self) documenting purposes, since // all its methods are optional. @@ -56,19 +89,13 @@ namespace Catch () const { - id obj = class_createInstance( m_cls, 0 ); - obj = [obj init]; + id obj = [[m_cls alloc] init]; - if( [obj respondsToSelector: @selector(setUp) ] ) - [obj performSelector: @selector(setUp)]; - - if( [obj respondsToSelector: m_sel] ) - [obj performSelector: m_sel]; - - if( [obj respondsToSelector: @selector(tearDown) ] ) - [obj performSelector: @selector(tearDown)]; + performOptionalSelector( obj, @selector(setUp) ); + performOptionalSelector( obj, m_sel ); + performOptionalSelector( obj, @selector(tearDown) ); - [obj release]; + arcSafeRelease( obj ); } /////////////////////////////////////////////////////////////////////// @@ -120,7 +147,7 @@ namespace Catch } /////////////////////////////////////////////////////////////////////// - inline const char* getAnnotation + inline std::string getAnnotation ( Class cls, const std::string& annotationName, @@ -129,9 +156,10 @@ namespace Catch { NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; SEL sel = NSSelectorFromString( selStr ); - [selStr release]; - if( [cls respondsToSelector: sel] ) - return (const char*)[cls performSelector: sel]; + arcSafeRelease( selStr ); + id value = performOptionalSelector( cls, sel ); + if( value ) + return [(NSString*)value UTF8String]; return ""; } } @@ -143,8 +171,8 @@ namespace Catch size_t noTestMethods = 0; int noClasses = objc_getClassList( NULL, 0 ); - std::vector classes( noClasses ); - objc_getClassList( &classes[0], noClasses ); + Class* classes = (__unsafe_unretained Class *)malloc( sizeof(Class) * noClasses); + objc_getClassList( classes, noClasses ); for( int c = 0; c < noClasses; c++ ) { @@ -159,10 +187,10 @@ namespace Catch if( Detail::startsWith( methodName, "Catch_TestCase_" ) ) { std::string testCaseName = methodName.substr( 15 ); - const char* name = Detail::getAnnotation( cls, "Name", testCaseName ); - const char* desc = Detail::getAnnotation( cls, "Description", testCaseName ); + std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); + std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); - Hub::getTestCaseRegistry().registerTest( TestCaseInfo( new OcMethod( cls, selector ), name, desc, "", 0 ) ); + Hub::getTestCaseRegistry().registerTest( TestCaseInfo( new OcMethod( cls, selector ), name.c_str(), desc.c_str(), "", 0 ) ); noTestMethods++; } @@ -173,8 +201,7 @@ namespace Catch return noTestMethods; } - template<> - inline std::string toString( NSString* const& nsstring ) + inline std::string toString( NSString* const& nsstring ) { return std::string( "@\"" ) + [nsstring UTF8String] + "\""; } @@ -190,7 +217,7 @@ namespace Catch StringHolder( NSString* substr ) : m_substr( [substr copy] ){} StringHolder() { - [m_substr release]; + arcSafeRelease( m_substr ); } NSString* m_substr; @@ -282,13 +309,13 @@ namespace Catch /////////////////////////////////////////////////////////////////////////////// #define OC_TEST_CASE( name, desc )\ -+(const char*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \ ++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \ {\ -return name; \ +return @ name; \ }\ -+(const char*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \ ++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \ { \ -return desc; \ +return @ desc; \ } \ -(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test ) diff --git a/projects/SelfTest/MiscTests.cpp b/projects/SelfTest/MiscTests.cpp index b73b1aaf..d4b814a5 100644 --- a/projects/SelfTest/MiscTests.cpp +++ b/projects/SelfTest/MiscTests.cpp @@ -194,7 +194,7 @@ inline const char* testStringForMatching() using namespace Catch::Matchers; TEST_CASE("./succeeding/matchers", "") -{ +{ REQUIRE_THAT( testStringForMatching(), Contains( "string" ) ); CHECK_THAT( testStringForMatching(), Contains( "abc" ) ); diff --git a/projects/XCode4/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj b/projects/XCode4/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj index b8dc32ef..d3cc91a0 100644 --- a/projects/XCode4/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj +++ b/projects/XCode4/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj @@ -34,6 +34,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 4A3D7DD01503869D005F9203 /* catch_matchers.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = catch_matchers.hpp; sourceTree = ""; }; 4A6D0C20149B3D3B00DB3EAA /* CatchSelfTest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = CatchSelfTest; sourceTree = BUILT_PRODUCTS_DIR; }; 4A6D0C26149B3D3B00DB3EAA /* CatchSelfTest.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = CatchSelfTest.1; sourceTree = ""; }; 4A6D0C2D149B3D9E00DB3EAA /* ApproxTests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ApproxTests.cpp; path = ../../../SelfTest/ApproxTests.cpp; sourceTree = ""; }; @@ -193,6 +194,7 @@ 4A7ADB4314F631E10094FE10 /* catch_totals.hpp */, 4AB1C73514F97BDA00F31DF7 /* catch_console_colour_impl.hpp */, 4AB1C73714F97C1300F31DF7 /* catch_console_colour.hpp */, + 4A3D7DD01503869D005F9203 /* catch_matchers.hpp */, ); name = internal; path = ../../../../include/internal; diff --git a/projects/XCode4/OCTest/OCTest.xcodeproj/project.pbxproj b/projects/XCode4/OCTest/OCTest.xcodeproj/project.pbxproj index 3df3f0dd..2c30f383 100644 --- a/projects/XCode4/OCTest/OCTest.xcodeproj/project.pbxproj +++ b/projects/XCode4/OCTest/OCTest.xcodeproj/project.pbxproj @@ -193,6 +193,7 @@ 4A63D2B014E3C1A900F615CB /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_OBJC_ARC = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -200,6 +201,7 @@ 4A63D2B114E3C1A900F615CB /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_OBJC_ARC = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -223,6 +225,7 @@ 4A63D2B114E3C1A900F615CB /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; diff --git a/projects/XCode4/OCTest/OCTest/CatchOCTestCase.mm b/projects/XCode4/OCTest/OCTest/CatchOCTestCase.mm index 008ac875..149e51ff 100644 --- a/projects/XCode4/OCTest/OCTest/CatchOCTestCase.mm +++ b/projects/XCode4/OCTest/OCTest/CatchOCTestCase.mm @@ -21,7 +21,7 @@ -(void) tearDown { - [obj release]; + arcSafeRelease( obj ); } OC_TEST_CASE( "OCTest/test1", "This is a test case" ) @@ -41,6 +41,12 @@ OC_TEST_CASE( "OCTest/test2", "This is another test case" ) REQUIRE( obj.int_val == 2 ); } +/////////////////////////////////////////////////////////////////////////// +template +void useObject( const T& object ){} + +template +void useObject( const T* object ){} using namespace Catch::Matchers; OC_TEST_CASE( "OCTest/matchers", "Matches work with OC types (NSString so far)" ) diff --git a/projects/XCode4/OCTest/OCTest/OCTest.mm b/projects/XCode4/OCTest/OCTest/OCTest.mm index 12b789eb..bfb2ca26 100644 --- a/projects/XCode4/OCTest/OCTest/OCTest.mm +++ b/projects/XCode4/OCTest/OCTest/OCTest.mm @@ -24,5 +24,5 @@ TEST_CASE( "OCTest/TestObj", "tests TestObj" ) REQUIRE( obj.int_val == 1 ); - [obj release]; + arcSafeRelease( obj ); } diff --git a/single_include/catch.hpp b/single_include/catch.hpp index baeb1934..045f10f4 100644 --- a/single_include/catch.hpp +++ b/single_include/catch.hpp @@ -1913,6 +1913,25 @@ public: return *this; } + /////////////////////////////////////////////////////////////////////////// + template + ResultBuilder& acceptMatcher + ( + const MatcherT& matcher, + ArgT* arg, + const std::string& matcherCallAsString + ) + { + std::string matcherAsString = Catch::toString( matcher ); + if( matcherAsString == "{?}" ) + matcherAsString = matcherCallAsString; + m_result.setLhs( Catch::toString( arg ) ); + m_result.setRhs( matcherAsString ); + m_result.setOp( "matches" ); + m_result.setResultType( matcher( arg ) ? ResultWas::Ok : ResultWas::ExpressionFailed ); + return *this; + } + /////////////////////////////////////////////////////////////////////////// ResultBuilder& setResultType ( @@ -3034,6 +3053,39 @@ using namespace Matchers; // in catch.hpp first to make sure they are included by the single // header for non obj-usage +#ifdef __has_feature +#define CATCH_ARC_ENABLED __has_feature(objc_arc) +#else +#define CATCH_ARC_ENABLED 0 +#endif + +void arcSafeRelease( NSObject* obj ); +id performOptionalSelector( id obj, SEL sel ); + +#if !CATCH_ARC_ENABLED +inline void arcSafeRelease( NSObject* obj ) +{ + [obj release]; +} +inline id performOptionalSelector( id obj, SEL sel ) +{ + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; + return nil; +} +#else +inline void arcSafeRelease( NSObject* ){} +inline id performOptionalSelector( id obj, SEL sel ) +{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; +#pragma clang diagnostic pop + return nil; +} +#endif + /////////////////////////////////////////////////////////////////////////////// // This protocol is really only here for (self) documenting purposes, since // all its methods are optional. @@ -3067,19 +3119,13 @@ namespace Catch () const { - id obj = class_createInstance( m_cls, 0 ); - obj = [obj init]; + id obj = [[m_cls alloc] init]; - if( [obj respondsToSelector: @selector(setUp) ] ) - [obj performSelector: @selector(setUp)]; + performOptionalSelector( obj, @selector(setUp) ); + performOptionalSelector( obj, m_sel ); + performOptionalSelector( obj, @selector(tearDown) ); - if( [obj respondsToSelector: m_sel] ) - [obj performSelector: m_sel]; - - if( [obj respondsToSelector: @selector(tearDown) ] ) - [obj performSelector: @selector(tearDown)]; - - [obj release]; + arcSafeRelease( obj ); } /////////////////////////////////////////////////////////////////////// @@ -3131,7 +3177,7 @@ namespace Catch } /////////////////////////////////////////////////////////////////////// - inline const char* getAnnotation + inline std::string getAnnotation ( Class cls, const std::string& annotationName, @@ -3140,9 +3186,10 @@ namespace Catch { NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; SEL sel = NSSelectorFromString( selStr ); - [selStr release]; - if( [cls respondsToSelector: sel] ) - return (const char*)[cls performSelector: sel]; + arcSafeRelease( selStr ); + id value = performOptionalSelector( cls, sel ); + if( value ) + return [(NSString*)value UTF8String]; return ""; } } @@ -3154,8 +3201,8 @@ namespace Catch size_t noTestMethods = 0; int noClasses = objc_getClassList( NULL, 0 ); - std::vector classes( noClasses ); - objc_getClassList( &classes[0], noClasses ); + Class* classes = (__unsafe_unretained Class *)malloc( sizeof(Class) * noClasses); + objc_getClassList( classes, noClasses ); for( int c = 0; c < noClasses; c++ ) { @@ -3170,10 +3217,10 @@ namespace Catch if( Detail::startsWith( methodName, "Catch_TestCase_" ) ) { std::string testCaseName = methodName.substr( 15 ); - const char* name = Detail::getAnnotation( cls, "Name", testCaseName ); - const char* desc = Detail::getAnnotation( cls, "Description", testCaseName ); + std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); + std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); - Hub::getTestCaseRegistry().registerTest( TestCaseInfo( new OcMethod( cls, selector ), name, desc, "", 0 ) ); + Hub::getTestCaseRegistry().registerTest( TestCaseInfo( new OcMethod( cls, selector ), name.c_str(), desc.c_str(), "", 0 ) ); noTestMethods++; } @@ -3184,8 +3231,7 @@ namespace Catch return noTestMethods; } - template<> - inline std::string toString( NSString* const& nsstring ) + inline std::string toString( NSString* const& nsstring ) { return std::string( "@\"" ) + [nsstring UTF8String] + "\""; } @@ -3201,7 +3247,7 @@ namespace Catch StringHolder( NSString* substr ) : m_substr( [substr copy] ){} StringHolder() { - [m_substr release]; + arcSafeRelease( m_substr ); } NSString* m_substr; @@ -3293,13 +3339,13 @@ namespace Catch /////////////////////////////////////////////////////////////////////////////// #define OC_TEST_CASE( name, desc )\ -+(const char*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \ ++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \ {\ -return name; \ +return @ name; \ }\ -+(const char*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \ ++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \ { \ -return desc; \ +return @ desc; \ } \ -(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test ) @@ -6804,13 +6850,17 @@ int main (int argc, char * const argv[]) { #ifdef __OBJC__ +#if !CATCH_ARC_ENABLED NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; +#endif Catch::registerTestMethods(); int result = Catch::Main( argc, (char* const*)argv ); +#if !CATCH_ARC_ENABLED [pool drain]; +#endif #else