blob: 4920ebdc651ffc686ecaf5d8c6be8d1560d26ae8 [file] [log] [blame]
Phil Nashd52f61c2010-11-14 22:47:30 +00001/*
Phil Nashd52f61c2010-11-14 22:47:30 +00002 * 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)
Phil Nashd52f61c2010-11-14 22:47:30 +00007 */
Phil Nashd52f61c2010-11-14 22:47:30 +00008#ifndef TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED
9#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED
10
Phil Nash6abf7022012-02-10 08:28:37 +000011#import <Foundation/Foundation.h>
Phil Nashd52f61c2010-11-14 22:47:30 +000012#import <objc/runtime.h>
Phil Nash6abf7022012-02-10 08:28:37 +000013
Phil Nashd52f61c2010-11-14 22:47:30 +000014#include <string>
Phil Nash823ea3e2011-04-26 08:32:40 +010015
Phil Nash89d1e6c2011-05-24 08:23:02 +010016// NB. Any general catch headers included here must be included
17// in catch.hpp first to make sure they are included by the single
18// header for non obj-usage
Phil Nash0847a0f2011-02-01 16:09:18 +000019#include "internal/catch_test_case_info.hpp"
Phil Nashd52f61c2010-11-14 22:47:30 +000020
Phil Nash53c990a2012-03-17 18:20:06 +000021#ifdef __has_feature
22#define CATCH_ARC_ENABLED __has_feature(objc_arc)
23#else
24#define CATCH_ARC_ENABLED 0
25#endif
26
27void arcSafeRelease( NSObject* obj );
28id performOptionalSelector( id obj, SEL sel );
29
30#if !CATCH_ARC_ENABLED
Phil Nash861a1e72012-04-28 12:29:52 +010031 inline void arcSafeRelease( NSObject* obj )
32 {
33 [obj release];
34 }
35 inline id performOptionalSelector( id obj, SEL sel )
36 {
37 if( [obj respondsToSelector: sel] )
38 return [obj performSelector: sel];
39 return nil;
40 }
41 #define CATCH_UNSAFE_UNRETAINED
Phil Nash53c990a2012-03-17 18:20:06 +000042#else
Phil Nash861a1e72012-04-28 12:29:52 +010043 inline void arcSafeRelease( NSObject* ){}
44 inline id performOptionalSelector( id obj, SEL sel )
45 {
46 #pragma clang diagnostic push
47 #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
48 if( [obj respondsToSelector: sel] )
49 return [obj performSelector: sel];
50 #pragma clang diagnostic pop
51 return nil;
52 }
53 #define CATCH_UNSAFE_UNRETAINED __unsafe_unretained
Phil Nash53c990a2012-03-17 18:20:06 +000054#endif
55
Phil Nash58e9a8b2011-02-08 08:42:05 +000056///////////////////////////////////////////////////////////////////////////////
57// This protocol is really only here for (self) documenting purposes, since
58// all its methods are optional.
Phil Nashf59ecbc2010-11-16 07:00:08 +000059@protocol OcFixture
60
61@optional
62
63-(void) setUp;
64-(void) tearDown;
65
66@end
67
Phil Nashd52f61c2010-11-14 22:47:30 +000068namespace Catch
69{
Phil Nash0847a0f2011-02-01 16:09:18 +000070 class OcMethod : public ITestCase
Phil Nashd52f61c2010-11-14 22:47:30 +000071 {
72 public:
Phil Nash58e9a8b2011-02-08 08:42:05 +000073 ///////////////////////////////////////////////////////////////////////
74 OcMethod
75 (
76 Class cls,
77 SEL sel
78 )
79 : m_cls( cls ),
80 m_sel( sel )
Phil Nashd52f61c2010-11-14 22:47:30 +000081 {
82 }
83
Phil Nash58e9a8b2011-02-08 08:42:05 +000084 ///////////////////////////////////////////////////////////////////////
85 virtual void invoke
86 ()
87 const
Phil Nashd52f61c2010-11-14 22:47:30 +000088 {
Phil Nash53c990a2012-03-17 18:20:06 +000089 id obj = [[m_cls alloc] init];
Phil Nashf59ecbc2010-11-16 07:00:08 +000090
Phil Nash53c990a2012-03-17 18:20:06 +000091 performOptionalSelector( obj, @selector(setUp) );
92 performOptionalSelector( obj, m_sel );
93 performOptionalSelector( obj, @selector(tearDown) );
Phil Nashf59ecbc2010-11-16 07:00:08 +000094
Phil Nash53c990a2012-03-17 18:20:06 +000095 arcSafeRelease( obj );
Phil Nashd52f61c2010-11-14 22:47:30 +000096 }
97
Phil Nash58e9a8b2011-02-08 08:42:05 +000098 ///////////////////////////////////////////////////////////////////////
99 virtual ITestCase* clone
100 ()
101 const
Phil Nashd52f61c2010-11-14 22:47:30 +0000102 {
Phil Nashf59ecbc2010-11-16 07:00:08 +0000103 return new OcMethod( m_cls, m_sel );
Phil Nashd52f61c2010-11-14 22:47:30 +0000104 }
105
Phil Nash58e9a8b2011-02-08 08:42:05 +0000106 ///////////////////////////////////////////////////////////////////////
107 virtual bool operator ==
108 (
109 const ITestCase& other
110 )
111 const
Phil Nashd52f61c2010-11-14 22:47:30 +0000112 {
113 const OcMethod* ocmOther = dynamic_cast<const OcMethod*> ( &other );
114 return ocmOther && ocmOther->m_sel == m_sel;
115 }
116
Phil Nash58e9a8b2011-02-08 08:42:05 +0000117 ///////////////////////////////////////////////////////////////////////
118 virtual bool operator <
119 (
120 const ITestCase& other
121 )
122 const
Phil Nashd52f61c2010-11-14 22:47:30 +0000123 {
124 const OcMethod* ocmOther = dynamic_cast<const OcMethod*> ( &other );
125 return ocmOther && ocmOther->m_sel < m_sel;
126 }
127
128 private:
Phil Nashf59ecbc2010-11-16 07:00:08 +0000129 Class m_cls;
Phil Nashd52f61c2010-11-14 22:47:30 +0000130 SEL m_sel;
131 };
132
Phil Nashf59ecbc2010-11-16 07:00:08 +0000133 namespace Detail
Phil Nashd52f61c2010-11-14 22:47:30 +0000134 {
Phil Nashf59ecbc2010-11-16 07:00:08 +0000135
Phil Nash58e9a8b2011-02-08 08:42:05 +0000136 ///////////////////////////////////////////////////////////////////////
137 inline bool startsWith
138 (
139 const std::string& str,
140 const std::string& sub
141 )
Phil Nashd52f61c2010-11-14 22:47:30 +0000142 {
143 return str.length() > sub.length() && str.substr( 0, sub.length() ) == sub;
144 }
145
Phil Nash58e9a8b2011-02-08 08:42:05 +0000146 ///////////////////////////////////////////////////////////////////////
Phil Nash53c990a2012-03-17 18:20:06 +0000147 inline std::string getAnnotation
Phil Nash58e9a8b2011-02-08 08:42:05 +0000148 (
149 Class cls,
150 const std::string& annotationName,
151 const std::string& testCaseName
152 )
Phil Nashd52f61c2010-11-14 22:47:30 +0000153 {
154 NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()];
155 SEL sel = NSSelectorFromString( selStr );
Phil Nash53c990a2012-03-17 18:20:06 +0000156 arcSafeRelease( selStr );
157 id value = performOptionalSelector( cls, sel );
158 if( value )
159 return [(NSString*)value UTF8String];
Phil Nashd52f61c2010-11-14 22:47:30 +0000160 return "";
161 }
Phil Nashf59ecbc2010-11-16 07:00:08 +0000162 }
163
Phil Nash58e9a8b2011-02-08 08:42:05 +0000164 ///////////////////////////////////////////////////////////////////////////
165 inline size_t registerTestMethods
166 ()
Phil Nashf59ecbc2010-11-16 07:00:08 +0000167 {
168 size_t noTestMethods = 0;
169 int noClasses = objc_getClassList( NULL, 0 );
Phil Nashd52f61c2010-11-14 22:47:30 +0000170
Phil Nash861a1e72012-04-28 12:29:52 +0100171 Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses);
Phil Nash53c990a2012-03-17 18:20:06 +0000172 objc_getClassList( classes, noClasses );
Phil Nashf59ecbc2010-11-16 07:00:08 +0000173
174 for( int c = 0; c < noClasses; c++ )
175 {
176 Class cls = classes[c];
177 {
178 u_int count;
179 Method* methods = class_copyMethodList( cls, &count );
180 for( int m = 0; m < count ; m++ )
181 {
182 SEL selector = method_getName(methods[m]);
183 std::string methodName = sel_getName(selector);
184 if( Detail::startsWith( methodName, "Catch_TestCase_" ) )
185 {
186 std::string testCaseName = methodName.substr( 15 );
Phil Nash53c990a2012-03-17 18:20:06 +0000187 std::string name = Detail::getAnnotation( cls, "Name", testCaseName );
188 std::string desc = Detail::getAnnotation( cls, "Description", testCaseName );
Phil Nashf59ecbc2010-11-16 07:00:08 +0000189
Phil Nashb84444c2012-05-10 08:16:30 +0100190 Context::getTestCaseRegistry().registerTest( TestCaseInfo( new OcMethod( cls, selector ), name.c_str(), desc.c_str(), SourceLineInfo() ) );
Phil Nashf59ecbc2010-11-16 07:00:08 +0000191 noTestMethods++;
192
193 }
194 }
195 free(methods);
196 }
197 }
198 return noTestMethods;
Phil Nash966f5db2012-03-04 21:18:46 +0000199 }
200
Phil Nash53c990a2012-03-17 18:20:06 +0000201 inline std::string toString( NSString* const& nsstring )
Phil Nash966f5db2012-03-04 21:18:46 +0000202 {
203 return std::string( "@\"" ) + [nsstring UTF8String] + "\"";
204 }
205
206 namespace Matchers
207 {
208 namespace Impl
209 {
210 namespace NSStringMatchers
211 {
212 struct StringHolder
213 {
214 StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
215 StringHolder()
216 {
Phil Nash53c990a2012-03-17 18:20:06 +0000217 arcSafeRelease( m_substr );
Phil Nash966f5db2012-03-04 21:18:46 +0000218 }
219
220 NSString* m_substr;
221 };
222
Phil Nashdb837a12012-03-14 20:04:50 +0000223 struct Equals : StringHolder
224 {
225 Equals( NSString* substr ) : StringHolder( substr ){}
226
227 bool operator()( NSString* str ) const
228 {
229 return [str isEqualToString:m_substr];
230 }
231
232 friend std::ostream& operator<<( std::ostream& os, const Equals& matcher )
233 {
234 os << "equals string: " << Catch::toString( matcher.m_substr );
235 return os;
236 }
237 };
238
Phil Nash966f5db2012-03-04 21:18:46 +0000239 struct Contains : StringHolder
240 {
241 Contains( NSString* substr ) : StringHolder( substr ){}
242
243 bool operator()( NSString* str ) const
244 {
245 return [str rangeOfString:m_substr].location != NSNotFound;
246 }
247
248 friend std::ostream& operator<<( std::ostream& os, const Contains& matcher )
249 {
250 os << "contains: " << Catch::toString( matcher.m_substr );
251 return os;
252 }
253 };
254
255 struct StartsWith : StringHolder
256 {
257 StartsWith( NSString* substr ) : StringHolder( substr ){}
258
259 bool operator()( NSString* str ) const
260 {
261 return [str rangeOfString:m_substr].location == 0;
262 }
263
264 friend std::ostream& operator<<( std::ostream& os, const StartsWith& matcher )
265 {
266 os << "starts with: " << Catch::toString( matcher.m_substr );
267 return os;
268 }
269 };
270 struct EndsWith : StringHolder
271 {
272 EndsWith( NSString* substr ) : StringHolder( substr ){}
273
274 bool operator()( NSString* str ) const
275 {
276 return [str rangeOfString:m_substr].location == [str length] - [m_substr length];
277 }
278
279 friend std::ostream& operator<<( std::ostream& os, const EndsWith& matcher )
280 {
281 os << "ends with: " << Catch::toString( matcher.m_substr );
282 return os;
283 }
284 };
285
286 } // namespace NSStringMatchers
287 } // namespace Impl
288
Phil Nashdb837a12012-03-14 20:04:50 +0000289 inline Impl::NSStringMatchers::Equals
290 Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); }
291
Phil Nash966f5db2012-03-04 21:18:46 +0000292 inline Impl::NSStringMatchers::Contains
293 Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); }
Phil Nashdb837a12012-03-14 20:04:50 +0000294
Phil Nash966f5db2012-03-04 21:18:46 +0000295 inline Impl::NSStringMatchers::StartsWith
296 StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); }
Phil Nashdb837a12012-03-14 20:04:50 +0000297
Phil Nash966f5db2012-03-04 21:18:46 +0000298 inline Impl::NSStringMatchers::EndsWith
299 EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); }
300
301 } // namespace Matchers
302
303 using namespace Matchers;
304
305} // namespace Catch
Phil Nashd52f61c2010-11-14 22:47:30 +0000306
Phil Nash58e9a8b2011-02-08 08:42:05 +0000307///////////////////////////////////////////////////////////////////////////////
Phil Nashd52f61c2010-11-14 22:47:30 +0000308#define OC_TEST_CASE( name, desc )\
Phil Nash53c990a2012-03-17 18:20:06 +0000309+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \
Phil Nashd52f61c2010-11-14 22:47:30 +0000310{\
Phil Nash53c990a2012-03-17 18:20:06 +0000311return @ name; \
Phil Nashd52f61c2010-11-14 22:47:30 +0000312}\
Phil Nash53c990a2012-03-17 18:20:06 +0000313+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \
Phil Nashd52f61c2010-11-14 22:47:30 +0000314{ \
Phil Nash53c990a2012-03-17 18:20:06 +0000315return @ desc; \
Phil Nashd52f61c2010-11-14 22:47:30 +0000316} \
317-(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test )
318
Phil Nashd10d2d32012-05-10 21:46:46 +0100319#endif // TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED