/* * catch_objc.hpp * Catch * * Created by Phil on 14/11/2010. * Copyright 2010 Two Blue Cubes Ltd. All rights reserved. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * */ #ifndef TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED #define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED #import #include #include "catch.hpp" #include "internal/catch_test_case_info.hpp" /////////////////////////////////////////////////////////////////////////////// // This protocol is really only here for (self) documenting purposes, since // all its methods are optional. @protocol OcFixture @optional -(void) setUp; -(void) tearDown; @end namespace Catch { class OcMethod : public ITestCase { public: /////////////////////////////////////////////////////////////////////// OcMethod ( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) { } /////////////////////////////////////////////////////////////////////// virtual void invoke () const { id obj = class_createInstance( m_cls, 0 ); obj = [obj 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)]; [obj release]; } /////////////////////////////////////////////////////////////////////// virtual ITestCase* clone () const { return new OcMethod( m_cls, m_sel ); } /////////////////////////////////////////////////////////////////////// virtual bool operator == ( const ITestCase& other ) const { const OcMethod* ocmOther = dynamic_cast ( &other ); return ocmOther && ocmOther->m_sel == m_sel; } /////////////////////////////////////////////////////////////////////// virtual bool operator < ( const ITestCase& other ) const { const OcMethod* ocmOther = dynamic_cast ( &other ); return ocmOther && ocmOther->m_sel < m_sel; } private: Class m_cls; SEL m_sel; }; namespace Detail { /////////////////////////////////////////////////////////////////////// inline bool startsWith ( const std::string& str, const std::string& sub ) { return str.length() > sub.length() && str.substr( 0, sub.length() ) == sub; } /////////////////////////////////////////////////////////////////////// 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()]; SEL sel = NSSelectorFromString( selStr ); [selStr release]; if( [cls respondsToSelector: sel] ) return (const char*)[cls performSelector: sel]; return ""; } } /////////////////////////////////////////////////////////////////////////// inline size_t registerTestMethods () { size_t noTestMethods = 0; int noClasses = objc_getClassList( NULL, 0 ); std::vector 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 ); const char* name = Detail::getAnnotation( cls, "Name", testCaseName ); const char* desc = Detail::getAnnotation( cls, "Description", testCaseName ); Hub::getTestCaseRegistry().registerTest( TestCaseInfo( new OcMethod( cls, selector ), name, desc ) ); noTestMethods++; } } free(methods); } } return noTestMethods; } } /////////////////////////////////////////////////////////////////////////////// #define OC_TEST_CASE( name, desc )\ +(const char*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \ {\ return name; \ }\ +(const char*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \ { \ return desc; \ } \ -(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test ) #endif // TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED