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