1 /* 2 * Created by Phil on 14/11/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_OBJC_HPP_INCLUDED 9 #define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED 10 11 #include "catch_objc_arc.hpp" 12 13 #import <objc/runtime.h> 14 15 #include <string> 16 17 // NB. Any general catch headers included here must be included 18 // in catch.hpp first to make sure they are included by the single 19 // header for non obj-usage 20 #include "catch_test_case_info.h" 21 22 /////////////////////////////////////////////////////////////////////////////// 23 // This protocol is really only here for (self) documenting purposes, since 24 // all its methods are optional. 25 @protocol OcFixture 26 27 @optional 28 29 -(void) setUp; 30 -(void) tearDown; 31 32 @end 33 34 namespace Catch { 35 36 class OcMethod : public SharedImpl<ITestCase> { 37 38 public: OcMethod(Class cls,SEL sel)39 OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} 40 invoke() const41 virtual void invoke() const { 42 id obj = [[m_cls alloc] init]; 43 44 performOptionalSelector( obj, @selector(setUp) ); 45 performOptionalSelector( obj, m_sel ); 46 performOptionalSelector( obj, @selector(tearDown) ); 47 48 arcSafeRelease( obj ); 49 } 50 private: ~OcMethod()51 virtual ~OcMethod() {} 52 53 Class m_cls; 54 SEL m_sel; 55 }; 56 57 namespace Detail{ 58 59 getAnnotation(Class cls,std::string const & annotationName,std::string const & testCaseName)60 inline std::string getAnnotation( Class cls, 61 std::string const& annotationName, 62 std::string const& testCaseName ) { 63 NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; 64 SEL sel = NSSelectorFromString( selStr ); 65 arcSafeRelease( selStr ); 66 id value = performOptionalSelector( cls, sel ); 67 if( value ) 68 return [(NSString*)value UTF8String]; 69 return ""; 70 } 71 } 72 registerTestMethods()73 inline size_t registerTestMethods() { 74 size_t noTestMethods = 0; 75 int noClasses = objc_getClassList( CATCH_NULL, 0 ); 76 77 Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); 78 objc_getClassList( classes, noClasses ); 79 80 for( int c = 0; c < noClasses; c++ ) { 81 Class cls = classes[c]; 82 { 83 u_int count; 84 Method* methods = class_copyMethodList( cls, &count ); 85 for( u_int m = 0; m < count ; m++ ) { 86 SEL selector = method_getName(methods[m]); 87 std::string methodName = sel_getName(selector); 88 if( startsWith( methodName, "Catch_TestCase_" ) ) { 89 std::string testCaseName = methodName.substr( 15 ); 90 std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); 91 std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); 92 const char* className = class_getName( cls ); 93 94 getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) ); 95 noTestMethods++; 96 } 97 } 98 free(methods); 99 } 100 } 101 return noTestMethods; 102 } 103 104 namespace Matchers { 105 namespace Impl { 106 namespace NSStringMatchers { 107 108 template<typename MatcherT> 109 struct StringHolder : MatcherImpl<MatcherT, NSString*>{ StringHolderCatch::Matchers::Impl::NSStringMatchers::StringHolder110 StringHolder( NSString* substr ) : m_substr( [substr copy] ){} StringHolderCatch::Matchers::Impl::NSStringMatchers::StringHolder111 StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} StringHolderCatch::Matchers::Impl::NSStringMatchers::StringHolder112 StringHolder() { 113 arcSafeRelease( m_substr ); 114 } 115 116 NSString* m_substr; 117 }; 118 119 struct Equals : StringHolder<Equals> { EqualsCatch::Matchers::Impl::NSStringMatchers::Equals120 Equals( NSString* substr ) : StringHolder( substr ){} 121 matchCatch::Matchers::Impl::NSStringMatchers::Equals122 virtual bool match( ExpressionType const& str ) const { 123 return (str != nil || m_substr == nil ) && 124 [str isEqualToString:m_substr]; 125 } 126 toStringCatch::Matchers::Impl::NSStringMatchers::Equals127 virtual std::string toString() const { 128 return "equals string: " + Catch::toString( m_substr ); 129 } 130 }; 131 132 struct Contains : StringHolder<Contains> { ContainsCatch::Matchers::Impl::NSStringMatchers::Contains133 Contains( NSString* substr ) : StringHolder( substr ){} 134 matchCatch::Matchers::Impl::NSStringMatchers::Contains135 virtual bool match( ExpressionType const& str ) const { 136 return (str != nil || m_substr == nil ) && 137 [str rangeOfString:m_substr].location != NSNotFound; 138 } 139 toStringCatch::Matchers::Impl::NSStringMatchers::Contains140 virtual std::string toString() const { 141 return "contains string: " + Catch::toString( m_substr ); 142 } 143 }; 144 145 struct StartsWith : StringHolder<StartsWith> { StartsWithCatch::Matchers::Impl::NSStringMatchers::StartsWith146 StartsWith( NSString* substr ) : StringHolder( substr ){} 147 matchCatch::Matchers::Impl::NSStringMatchers::StartsWith148 virtual bool match( ExpressionType const& str ) const { 149 return (str != nil || m_substr == nil ) && 150 [str rangeOfString:m_substr].location == 0; 151 } 152 toStringCatch::Matchers::Impl::NSStringMatchers::StartsWith153 virtual std::string toString() const { 154 return "starts with: " + Catch::toString( m_substr ); 155 } 156 }; 157 struct EndsWith : StringHolder<EndsWith> { EndsWithCatch::Matchers::Impl::NSStringMatchers::EndsWith158 EndsWith( NSString* substr ) : StringHolder( substr ){} 159 matchCatch::Matchers::Impl::NSStringMatchers::EndsWith160 virtual bool match( ExpressionType const& str ) const { 161 return (str != nil || m_substr == nil ) && 162 [str rangeOfString:m_substr].location == [str length] - [m_substr length]; 163 } 164 toStringCatch::Matchers::Impl::NSStringMatchers::EndsWith165 virtual std::string toString() const { 166 return "ends with: " + Catch::toString( m_substr ); 167 } 168 }; 169 170 } // namespace NSStringMatchers 171 } // namespace Impl 172 173 inline Impl::NSStringMatchers::Equals Equals(NSString * substr)174 Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); } 175 176 inline Impl::NSStringMatchers::Contains Contains(NSString * substr)177 Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); } 178 179 inline Impl::NSStringMatchers::StartsWith StartsWith(NSString * substr)180 StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); } 181 182 inline Impl::NSStringMatchers::EndsWith EndsWith(NSString * substr)183 EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); } 184 185 } // namespace Matchers 186 187 using namespace Matchers; 188 189 } // namespace Catch 190 191 /////////////////////////////////////////////////////////////////////////////// 192 #define OC_TEST_CASE( name, desc )\ 193 +(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \ 194 {\ 195 return @ name; \ 196 }\ 197 +(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \ 198 { \ 199 return @ desc; \ 200 } \ 201 -(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test ) 202 203 #endif // TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED 204