ARC support for Object-C projects

This commit is contained in:
Phil Nash 2012-03-17 18:20:06 +00:00
parent 8d18d1648a
commit 53c990a7e1
9 changed files with 168 additions and 57 deletions

View File

@ -639,6 +639,25 @@ public:
return *this; return *this;
} }
///////////////////////////////////////////////////////////////////////////
template<typename MatcherT, typename ArgT>
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 ResultBuilder& setResultType
( (

View File

@ -17,13 +17,17 @@ int main (int argc, char * const argv[])
{ {
#ifdef __OBJC__ #ifdef __OBJC__
#if !CATCH_ARC_ENABLED
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
#endif
Catch::registerTestMethods(); Catch::registerTestMethods();
int result = Catch::Main( argc, (char* const*)argv ); int result = Catch::Main( argc, (char* const*)argv );
#if !CATCH_ARC_ENABLED
[pool drain]; [pool drain];
#endif
#else #else

View File

@ -23,6 +23,39 @@
// header for non obj-usage // header for non obj-usage
#include "internal/catch_test_case_info.hpp" #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 // This protocol is really only here for (self) documenting purposes, since
// all its methods are optional. // all its methods are optional.
@ -56,19 +89,13 @@ namespace Catch
() ()
const const
{ {
id obj = class_createInstance( m_cls, 0 ); id obj = [[m_cls alloc] init];
obj = [obj init];
if( [obj respondsToSelector: @selector(setUp) ] ) performOptionalSelector( obj, @selector(setUp) );
[obj performSelector: @selector(setUp)]; performOptionalSelector( obj, m_sel );
performOptionalSelector( obj, @selector(tearDown) );
if( [obj respondsToSelector: m_sel] ) arcSafeRelease( obj );
[obj performSelector: m_sel];
if( [obj respondsToSelector: @selector(tearDown) ] )
[obj performSelector: @selector(tearDown)];
[obj release];
} }
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
@ -120,7 +147,7 @@ namespace Catch
} }
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
inline const char* getAnnotation inline std::string getAnnotation
( (
Class cls, Class cls,
const std::string& annotationName, const std::string& annotationName,
@ -129,9 +156,10 @@ namespace Catch
{ {
NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()];
SEL sel = NSSelectorFromString( selStr ); SEL sel = NSSelectorFromString( selStr );
[selStr release]; arcSafeRelease( selStr );
if( [cls respondsToSelector: sel] ) id value = performOptionalSelector( cls, sel );
return (const char*)[cls performSelector: sel]; if( value )
return [(NSString*)value UTF8String];
return ""; return "";
} }
} }
@ -143,8 +171,8 @@ namespace Catch
size_t noTestMethods = 0; size_t noTestMethods = 0;
int noClasses = objc_getClassList( NULL, 0 ); int noClasses = objc_getClassList( NULL, 0 );
std::vector<Class> classes( noClasses ); Class* classes = (__unsafe_unretained Class *)malloc( sizeof(Class) * noClasses);
objc_getClassList( &classes[0], noClasses ); objc_getClassList( classes, noClasses );
for( int c = 0; c < noClasses; c++ ) for( int c = 0; c < noClasses; c++ )
{ {
@ -159,10 +187,10 @@ namespace Catch
if( Detail::startsWith( methodName, "Catch_TestCase_" ) ) if( Detail::startsWith( methodName, "Catch_TestCase_" ) )
{ {
std::string testCaseName = methodName.substr( 15 ); std::string testCaseName = methodName.substr( 15 );
const char* name = Detail::getAnnotation( cls, "Name", testCaseName ); std::string name = Detail::getAnnotation( cls, "Name", testCaseName );
const char* desc = Detail::getAnnotation( cls, "Description", 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++; noTestMethods++;
} }
@ -173,8 +201,7 @@ namespace Catch
return noTestMethods; return noTestMethods;
} }
template<> inline std::string toString( NSString* const& nsstring )
inline std::string toString<NSString*>( NSString* const& nsstring )
{ {
return std::string( "@\"" ) + [nsstring UTF8String] + "\""; return std::string( "@\"" ) + [nsstring UTF8String] + "\"";
} }
@ -190,7 +217,7 @@ namespace Catch
StringHolder( NSString* substr ) : m_substr( [substr copy] ){} StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
StringHolder() StringHolder()
{ {
[m_substr release]; arcSafeRelease( m_substr );
} }
NSString* m_substr; NSString* m_substr;
@ -282,13 +309,13 @@ namespace Catch
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#define OC_TEST_CASE( name, desc )\ #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 ) -(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test )

View File

@ -34,6 +34,7 @@
/* End PBXCopyFilesBuildPhase section */ /* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
4A3D7DD01503869D005F9203 /* catch_matchers.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = catch_matchers.hpp; sourceTree = "<group>"; };
4A6D0C20149B3D3B00DB3EAA /* CatchSelfTest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = CatchSelfTest; sourceTree = BUILT_PRODUCTS_DIR; }; 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 = "<group>"; }; 4A6D0C26149B3D3B00DB3EAA /* CatchSelfTest.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = CatchSelfTest.1; sourceTree = "<group>"; };
4A6D0C2D149B3D9E00DB3EAA /* ApproxTests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ApproxTests.cpp; path = ../../../SelfTest/ApproxTests.cpp; sourceTree = "<group>"; }; 4A6D0C2D149B3D9E00DB3EAA /* ApproxTests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ApproxTests.cpp; path = ../../../SelfTest/ApproxTests.cpp; sourceTree = "<group>"; };
@ -193,6 +194,7 @@
4A7ADB4314F631E10094FE10 /* catch_totals.hpp */, 4A7ADB4314F631E10094FE10 /* catch_totals.hpp */,
4AB1C73514F97BDA00F31DF7 /* catch_console_colour_impl.hpp */, 4AB1C73514F97BDA00F31DF7 /* catch_console_colour_impl.hpp */,
4AB1C73714F97C1300F31DF7 /* catch_console_colour.hpp */, 4AB1C73714F97C1300F31DF7 /* catch_console_colour.hpp */,
4A3D7DD01503869D005F9203 /* catch_matchers.hpp */,
); );
name = internal; name = internal;
path = ../../../../include/internal; path = ../../../../include/internal;

View File

@ -193,6 +193,7 @@
4A63D2B014E3C1A900F615CB /* Debug */ = { 4A63D2B014E3C1A900F615CB /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
}; };
name = Debug; name = Debug;
@ -200,6 +201,7 @@
4A63D2B114E3C1A900F615CB /* Release */ = { 4A63D2B114E3C1A900F615CB /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
}; };
name = Release; name = Release;
@ -223,6 +225,7 @@
4A63D2B114E3C1A900F615CB /* Release */, 4A63D2B114E3C1A900F615CB /* Release */,
); );
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
}; };
/* End XCConfigurationList section */ /* End XCConfigurationList section */
}; };

View File

@ -21,7 +21,7 @@
-(void) tearDown -(void) tearDown
{ {
[obj release]; arcSafeRelease( obj );
} }
OC_TEST_CASE( "OCTest/test1", "This is a test case" ) 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 ); REQUIRE( obj.int_val == 2 );
} }
///////////////////////////////////////////////////////////////////////////
template<typename T>
void useObject( const T& object ){}
template<typename T>
void useObject( const T* object ){}
using namespace Catch::Matchers; using namespace Catch::Matchers;
OC_TEST_CASE( "OCTest/matchers", "Matches work with OC types (NSString so far)" ) OC_TEST_CASE( "OCTest/matchers", "Matches work with OC types (NSString so far)" )

View File

@ -24,5 +24,5 @@ TEST_CASE( "OCTest/TestObj", "tests TestObj" )
REQUIRE( obj.int_val == 1 ); REQUIRE( obj.int_val == 1 );
[obj release]; arcSafeRelease( obj );
} }

View File

@ -1913,6 +1913,25 @@ public:
return *this; return *this;
} }
///////////////////////////////////////////////////////////////////////////
template<typename MatcherT, typename ArgT>
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 ResultBuilder& setResultType
( (
@ -3034,6 +3053,39 @@ using namespace Matchers;
// in catch.hpp first to make sure they are included by the single // in catch.hpp first to make sure they are included by the single
// header for non obj-usage // 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 // This protocol is really only here for (self) documenting purposes, since
// all its methods are optional. // all its methods are optional.
@ -3067,19 +3119,13 @@ namespace Catch
() ()
const const
{ {
id obj = class_createInstance( m_cls, 0 ); id obj = [[m_cls alloc] init];
obj = [obj init];
if( [obj respondsToSelector: @selector(setUp) ] ) performOptionalSelector( obj, @selector(setUp) );
[obj performSelector: @selector(setUp)]; performOptionalSelector( obj, m_sel );
performOptionalSelector( obj, @selector(tearDown) );
if( [obj respondsToSelector: m_sel] ) arcSafeRelease( obj );
[obj performSelector: m_sel];
if( [obj respondsToSelector: @selector(tearDown) ] )
[obj performSelector: @selector(tearDown)];
[obj release];
} }
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
@ -3131,7 +3177,7 @@ namespace Catch
} }
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
inline const char* getAnnotation inline std::string getAnnotation
( (
Class cls, Class cls,
const std::string& annotationName, const std::string& annotationName,
@ -3140,9 +3186,10 @@ namespace Catch
{ {
NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()];
SEL sel = NSSelectorFromString( selStr ); SEL sel = NSSelectorFromString( selStr );
[selStr release]; arcSafeRelease( selStr );
if( [cls respondsToSelector: sel] ) id value = performOptionalSelector( cls, sel );
return (const char*)[cls performSelector: sel]; if( value )
return [(NSString*)value UTF8String];
return ""; return "";
} }
} }
@ -3154,8 +3201,8 @@ namespace Catch
size_t noTestMethods = 0; size_t noTestMethods = 0;
int noClasses = objc_getClassList( NULL, 0 ); int noClasses = objc_getClassList( NULL, 0 );
std::vector<Class> classes( noClasses ); Class* classes = (__unsafe_unretained Class *)malloc( sizeof(Class) * noClasses);
objc_getClassList( &classes[0], noClasses ); objc_getClassList( classes, noClasses );
for( int c = 0; c < noClasses; c++ ) for( int c = 0; c < noClasses; c++ )
{ {
@ -3170,10 +3217,10 @@ namespace Catch
if( Detail::startsWith( methodName, "Catch_TestCase_" ) ) if( Detail::startsWith( methodName, "Catch_TestCase_" ) )
{ {
std::string testCaseName = methodName.substr( 15 ); std::string testCaseName = methodName.substr( 15 );
const char* name = Detail::getAnnotation( cls, "Name", testCaseName ); std::string name = Detail::getAnnotation( cls, "Name", testCaseName );
const char* desc = Detail::getAnnotation( cls, "Description", 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++; noTestMethods++;
} }
@ -3184,8 +3231,7 @@ namespace Catch
return noTestMethods; return noTestMethods;
} }
template<> inline std::string toString( NSString* const& nsstring )
inline std::string toString<NSString*>( NSString* const& nsstring )
{ {
return std::string( "@\"" ) + [nsstring UTF8String] + "\""; return std::string( "@\"" ) + [nsstring UTF8String] + "\"";
} }
@ -3201,7 +3247,7 @@ namespace Catch
StringHolder( NSString* substr ) : m_substr( [substr copy] ){} StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
StringHolder() StringHolder()
{ {
[m_substr release]; arcSafeRelease( m_substr );
} }
NSString* m_substr; NSString* m_substr;
@ -3293,13 +3339,13 @@ namespace Catch
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#define OC_TEST_CASE( name, desc )\ #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 ) -(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test )
@ -6804,13 +6850,17 @@ int main (int argc, char * const argv[])
{ {
#ifdef __OBJC__ #ifdef __OBJC__
#if !CATCH_ARC_ENABLED
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
#endif
Catch::registerTestMethods(); Catch::registerTestMethods();
int result = Catch::Main( argc, (char* const*)argv ); int result = Catch::Main( argc, (char* const*)argv );
#if !CATCH_ARC_ENABLED
[pool drain]; [pool drain];
#endif
#else #else