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 #include "catch_string_manip.h" 22 #include "catch_tostring.h" 23 24 /////////////////////////////////////////////////////////////////////////////// 25 // This protocol is really only here for (self) documenting purposes, since 26 // all its methods are optional. 27 @protocol OcFixture 28 29 @optional 30 31 -(void) setUp; 32 -(void) tearDown; 33 34 @end 35 36 namespace Catch { 37 38 class OcMethod : public ITestInvoker { 39 40 public: OcMethod(Class cls,SEL sel)41 OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} 42 invoke() const43 virtual void invoke() const { 44 id obj = [[m_cls alloc] init]; 45 46 performOptionalSelector( obj, @selector(setUp) ); 47 performOptionalSelector( obj, m_sel ); 48 performOptionalSelector( obj, @selector(tearDown) ); 49 50 arcSafeRelease( obj ); 51 } 52 private: ~OcMethod()53 virtual ~OcMethod() {} 54 55 Class m_cls; 56 SEL m_sel; 57 }; 58 59 namespace Detail{ 60 61 getAnnotation(Class cls,std::string const & annotationName,std::string const & testCaseName)62 inline std::string getAnnotation( Class cls, 63 std::string const& annotationName, 64 std::string const& testCaseName ) { 65 NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; 66 SEL sel = NSSelectorFromString( selStr ); 67 arcSafeRelease( selStr ); 68 id value = performOptionalSelector( cls, sel ); 69 if( value ) 70 return [(NSString*)value UTF8String]; 71 return ""; 72 } 73 } 74 registerTestMethods()75 inline std::size_t registerTestMethods() { 76 std::size_t noTestMethods = 0; 77 int noClasses = objc_getClassList( nullptr, 0 ); 78 79 Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); 80 objc_getClassList( classes, noClasses ); 81 82 for( int c = 0; c < noClasses; c++ ) { 83 Class cls = classes[c]; 84 { 85 u_int count; 86 Method* methods = class_copyMethodList( cls, &count ); 87 for( u_int m = 0; m < count ; m++ ) { 88 SEL selector = method_getName(methods[m]); 89 std::string methodName = sel_getName(selector); 90 if( startsWith( methodName, "Catch_TestCase_" ) ) { 91 std::string testCaseName = methodName.substr( 15 ); 92 std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); 93 std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); 94 const char* className = class_getName( cls ); 95 96 getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, NameAndTags( name.c_str(), desc.c_str() ), SourceLineInfo("",0) ) ); 97 noTestMethods++; 98 } 99 } 100 free(methods); 101 } 102 } 103 return noTestMethods; 104 } 105 106 #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) 107 108 namespace Matchers { 109 namespace Impl { 110 namespace NSStringMatchers { 111 112 struct StringHolder : MatcherBase<NSString*>{ StringHolderCatch::Matchers::Impl::NSStringMatchers::StringHolder113 StringHolder( NSString* substr ) : m_substr( [substr copy] ){} StringHolderCatch::Matchers::Impl::NSStringMatchers::StringHolder114 StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} StringHolderCatch::Matchers::Impl::NSStringMatchers::StringHolder115 StringHolder() { 116 arcSafeRelease( m_substr ); 117 } 118 matchCatch::Matchers::Impl::NSStringMatchers::StringHolder119 bool match( NSString* arg ) const override { 120 return false; 121 } 122 123 NSString* CATCH_ARC_STRONG m_substr; 124 }; 125 126 struct Equals : StringHolder { EqualsCatch::Matchers::Impl::NSStringMatchers::Equals127 Equals( NSString* substr ) : StringHolder( substr ){} 128 matchCatch::Matchers::Impl::NSStringMatchers::Equals129 bool match( NSString* str ) const override { 130 return (str != nil || m_substr == nil ) && 131 [str isEqualToString:m_substr]; 132 } 133 describeCatch::Matchers::Impl::NSStringMatchers::Equals134 std::string describe() const override { 135 return "equals string: " + Catch::Detail::stringify( m_substr ); 136 } 137 }; 138 139 struct Contains : StringHolder { ContainsCatch::Matchers::Impl::NSStringMatchers::Contains140 Contains( NSString* substr ) : StringHolder( substr ){} 141 matchCatch::Matchers::Impl::NSStringMatchers::Contains142 bool match( NSString* str ) const { 143 return (str != nil || m_substr == nil ) && 144 [str rangeOfString:m_substr].location != NSNotFound; 145 } 146 describeCatch::Matchers::Impl::NSStringMatchers::Contains147 std::string describe() const override { 148 return "contains string: " + Catch::Detail::stringify( m_substr ); 149 } 150 }; 151 152 struct StartsWith : StringHolder { StartsWithCatch::Matchers::Impl::NSStringMatchers::StartsWith153 StartsWith( NSString* substr ) : StringHolder( substr ){} 154 matchCatch::Matchers::Impl::NSStringMatchers::StartsWith155 bool match( NSString* str ) const override { 156 return (str != nil || m_substr == nil ) && 157 [str rangeOfString:m_substr].location == 0; 158 } 159 describeCatch::Matchers::Impl::NSStringMatchers::StartsWith160 std::string describe() const override { 161 return "starts with: " + Catch::Detail::stringify( m_substr ); 162 } 163 }; 164 struct EndsWith : StringHolder { EndsWithCatch::Matchers::Impl::NSStringMatchers::EndsWith165 EndsWith( NSString* substr ) : StringHolder( substr ){} 166 matchCatch::Matchers::Impl::NSStringMatchers::EndsWith167 bool match( NSString* str ) const override { 168 return (str != nil || m_substr == nil ) && 169 [str rangeOfString:m_substr].location == [str length] - [m_substr length]; 170 } 171 describeCatch::Matchers::Impl::NSStringMatchers::EndsWith172 std::string describe() const override { 173 return "ends with: " + Catch::Detail::stringify( m_substr ); 174 } 175 }; 176 177 } // namespace NSStringMatchers 178 } // namespace Impl 179 180 inline Impl::NSStringMatchers::Equals Equals(NSString * substr)181 Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); } 182 183 inline Impl::NSStringMatchers::Contains Contains(NSString * substr)184 Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); } 185 186 inline Impl::NSStringMatchers::StartsWith StartsWith(NSString * substr)187 StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); } 188 189 inline Impl::NSStringMatchers::EndsWith EndsWith(NSString * substr)190 EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); } 191 192 } // namespace Matchers 193 194 using namespace Matchers; 195 196 #endif // CATCH_CONFIG_DISABLE_MATCHERS 197 198 } // namespace Catch 199 200 /////////////////////////////////////////////////////////////////////////////// 201 #define OC_MAKE_UNIQUE_NAME( root, uniqueSuffix ) root##uniqueSuffix 202 #define OC_TEST_CASE2( name, desc, uniqueSuffix ) \ 203 +(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Name_test_, uniqueSuffix ) \ 204 { \ 205 return @ name; \ 206 } \ 207 +(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Description_test_, uniqueSuffix ) \ 208 { \ 209 return @ desc; \ 210 } \ 211 -(void) OC_MAKE_UNIQUE_NAME( Catch_TestCase_test_, uniqueSuffix ) 212 213 #define OC_TEST_CASE( name, desc ) OC_TEST_CASE2( name, desc, __LINE__ ) 214 215 #endif // TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED 216