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* str ) 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 override {
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