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             struct StringHolder : MatcherBase<NSString*>{
StringHolderCatch::Matchers::Impl::NSStringMatchers::StringHolder109                 StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
StringHolderCatch::Matchers::Impl::NSStringMatchers::StringHolder110                 StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){}
StringHolderCatch::Matchers::Impl::NSStringMatchers::StringHolder111                 StringHolder() {
112                     arcSafeRelease( m_substr );
113                 }
114 
matchCatch::Matchers::Impl::NSStringMatchers::StringHolder115                 virtual bool match( NSString* arg ) const CATCH_OVERRIDE {
116                     return false;
117                 }
118 
119                 NSString* m_substr;
120             };
121 
122             struct Equals : StringHolder {
EqualsCatch::Matchers::Impl::NSStringMatchers::Equals123                 Equals( NSString* substr ) : StringHolder( substr ){}
124 
matchCatch::Matchers::Impl::NSStringMatchers::Equals125                 virtual bool match( NSString* str ) const CATCH_OVERRIDE {
126                     return  (str != nil || m_substr == nil ) &&
127                             [str isEqualToString:m_substr];
128                 }
129 
describeCatch::Matchers::Impl::NSStringMatchers::Equals130                 virtual std::string describe() const CATCH_OVERRIDE {
131                     return "equals string: " + Catch::toString( m_substr );
132                 }
133             };
134 
135             struct Contains : StringHolder {
ContainsCatch::Matchers::Impl::NSStringMatchers::Contains136                 Contains( NSString* substr ) : StringHolder( substr ){}
137 
matchCatch::Matchers::Impl::NSStringMatchers::Contains138                 virtual bool match( NSString* str ) const {
139                     return  (str != nil || m_substr == nil ) &&
140                             [str rangeOfString:m_substr].location != NSNotFound;
141                 }
142 
describeCatch::Matchers::Impl::NSStringMatchers::Contains143                 virtual std::string describe() const CATCH_OVERRIDE {
144                     return "contains string: " + Catch::toString( m_substr );
145                 }
146             };
147 
148             struct StartsWith : StringHolder {
StartsWithCatch::Matchers::Impl::NSStringMatchers::StartsWith149                 StartsWith( NSString* substr ) : StringHolder( substr ){}
150 
matchCatch::Matchers::Impl::NSStringMatchers::StartsWith151                 virtual bool match( NSString* str ) const {
152                     return  (str != nil || m_substr == nil ) &&
153                             [str rangeOfString:m_substr].location == 0;
154                 }
155 
describeCatch::Matchers::Impl::NSStringMatchers::StartsWith156                 virtual std::string describe() const CATCH_OVERRIDE {
157                     return "starts with: " + Catch::toString( m_substr );
158                 }
159             };
160             struct EndsWith : StringHolder {
EndsWithCatch::Matchers::Impl::NSStringMatchers::EndsWith161                 EndsWith( NSString* substr ) : StringHolder( substr ){}
162 
matchCatch::Matchers::Impl::NSStringMatchers::EndsWith163                 virtual bool match( NSString* str ) const {
164                     return  (str != nil || m_substr == nil ) &&
165                             [str rangeOfString:m_substr].location == [str length] - [m_substr length];
166                 }
167 
describeCatch::Matchers::Impl::NSStringMatchers::EndsWith168                 virtual std::string describe() const CATCH_OVERRIDE {
169                     return "ends with: " + Catch::toString( m_substr );
170                 }
171             };
172 
173         } // namespace NSStringMatchers
174         } // namespace Impl
175 
176         inline Impl::NSStringMatchers::Equals
Equals(NSString * substr)177             Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); }
178 
179         inline Impl::NSStringMatchers::Contains
Contains(NSString * substr)180             Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); }
181 
182         inline Impl::NSStringMatchers::StartsWith
StartsWith(NSString * substr)183             StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); }
184 
185         inline Impl::NSStringMatchers::EndsWith
EndsWith(NSString * substr)186             EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); }
187 
188     } // namespace Matchers
189 
190     using namespace Matchers;
191 
192 } // namespace Catch
193 
194 ///////////////////////////////////////////////////////////////////////////////
195 #define OC_TEST_CASE( name, desc )\
196 +(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \
197 {\
198 return @ name; \
199 }\
200 +(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \
201 { \
202 return @ desc; \
203 } \
204 -(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test )
205 
206 #endif // TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED
207