Auto registers OC Classes

This commit is contained in:
Phil Nash 2010-11-16 07:00:08 +00:00
parent d52f61cc67
commit f59ecbcad6
4 changed files with 71 additions and 53 deletions

View File

@ -11,14 +11,9 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import "TestObj.h" #import "TestObj.h"
@interface TestFixture : NSObject @interface TestFixture : NSObject <OcFixture>
{ {
TestObj* obj; TestObj* obj;
} }
@end @end
CATCH_REGISTER_CLASS( TestFixture )

View File

@ -12,20 +12,14 @@
@implementation TestFixture @implementation TestFixture
- (id) init -(void) setUp
{ {
self = [super init]; obj = [[TestObj alloc] init];
if (self != nil)
{
obj = [[TestObj alloc] init];
}
return self;
} }
- (void) dealloc -(void) tearDown
{ {
[obj release]; [obj release];
[super dealloc];
} }
OC_TEST_CASE( "OCTest/test1", "This is a test case" ) OC_TEST_CASE( "OCTest/test1", "This is a test case" )

View File

@ -3,9 +3,12 @@
#import "CatchOCTestCase.h" #import "CatchOCTestCase.h"
int main (int argc, const char * argv[]) { int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Catch::registerTestMethods();
// insert code here... // insert code here...
int result = Catch::Main( argc, (char* const*)argv ); int result = Catch::Main( argc, (char* const*)argv );

View File

@ -17,27 +17,44 @@
#include <string> #include <string>
#include "catch.hpp" #include "catch.hpp"
@protocol OcFixture
@optional
-(void) setUp;
-(void) tearDown;
@end
namespace Catch namespace Catch
{ {
template<typename T>
class OcMethod : public TestCase class OcMethod : public TestCase
{ {
public: public:
OcMethod( SEL sel ) : m_sel( sel ) OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel )
{ {
} }
virtual void invoke() const virtual void invoke() const
{ {
T* obj = [[T alloc] init]; id obj = class_createInstance( m_cls, 0 );
obj = [obj init];
if( [obj respondsToSelector: @selector(setUp) ] )
[obj performSelector: @selector(setUp)];
if( [obj respondsToSelector: m_sel] ) if( [obj respondsToSelector: m_sel] )
[obj performSelector: m_sel]; [obj performSelector: m_sel];
if( [obj respondsToSelector: @selector(tearDown) ] )
[obj performSelector: @selector(tearDown)];
[obj release]; [obj release];
} }
virtual TestCase* clone() const virtual TestCase* clone() const
{ {
return new OcMethod<T>( m_sel ); return new OcMethod( m_cls, m_sel );
} }
virtual bool operator == ( const TestCase& other ) const virtual bool operator == ( const TestCase& other ) const
@ -53,56 +70,65 @@ namespace Catch
} }
private: private:
Class m_cls;
SEL m_sel; SEL m_sel;
}; };
template<typename T> namespace Detail
struct OcAutoReg
{ {
OcAutoReg()
{ inline bool startsWith( const std::string& str, const std::string& sub )
u_int count;
Method* methods = class_copyMethodList([T class], &count);
for( int i = 0; i < count ; i++ )
{
SEL selector = method_getName(methods[i]);
std::string methodName = sel_getName(selector);
if( startsWith( methodName, "Catch_TestCase_" ) )
{
std::string testCaseName = methodName.substr( 15 );
std::string name = getAnnotation( "Name", testCaseName );
std::string desc = getAnnotation( "Description", testCaseName );
TestRegistry::instance().registerTest( TestCaseInfo( new OcMethod<T>( selector ), name, desc ) );
}
}
free(methods);
}
private:
bool startsWith( const std::string& str, const std::string& sub )
{ {
return str.length() > sub.length() && str.substr( 0, sub.length() ) == sub; return str.length() > sub.length() && str.substr( 0, sub.length() ) == sub;
} }
const char* getAnnotation( const std::string& annotationName, const std::string& testCaseName ) inline const char* getAnnotation( Class cls, const std::string& annotationName, const std::string& testCaseName )
{ {
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]; [selStr release];
if( [[T class] respondsToSelector: sel] ) if( [cls respondsToSelector: sel] )
return (const char*)[[T class] performSelector: sel]; return (const char*)[cls performSelector: sel];
return ""; return "";
} }
}
inline size_t registerTestMethods()
{
size_t noTestMethods = 0;
int noClasses = objc_getClassList( NULL, 0 );
}; std::vector<Class> classes( noClasses );
objc_getClassList( &classes[0], noClasses );
for( int c = 0; c < noClasses; c++ )
{
Class cls = classes[c];
{
u_int count;
Method* methods = class_copyMethodList( cls, &count );
for( int m = 0; m < count ; m++ )
{
SEL selector = method_getName(methods[m]);
std::string methodName = sel_getName(selector);
if( Detail::startsWith( methodName, "Catch_TestCase_" ) )
{
std::string testCaseName = methodName.substr( 15 );
std::string name = Detail::getAnnotation( cls, "Name", testCaseName );
std::string desc = Detail::getAnnotation( cls, "Description", testCaseName );
TestRegistry::instance().registerTest( TestCaseInfo( new OcMethod( cls, selector ), name, desc ) );
noTestMethods++;
}
}
free(methods);
}
}
return noTestMethods;
}
} }
#define CATCH_REGISTER_CLASS( className ) namespace{ Catch::OcAutoReg<className> reg; }
#define OC_TEST_CASE( name, desc )\ #define OC_TEST_CASE( name, desc )\
+(const char*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \ +(const char*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \
{\ {\