blob: 6db1b707b3b4c4b524cd7367740f97cb5438274b [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 Nash44fbbb02012-05-16 15:07:11 +010031 inline void arcSafeRelease( NSObject* obj ) {
Phil Nash861a1e72012-04-28 12:29:52 +010032 [obj release];
33 }
Phil Nash44fbbb02012-05-16 15:07:11 +010034 inline id performOptionalSelector( id obj, SEL sel ) {
Phil Nash861a1e72012-04-28 12:29:52 +010035 if( [obj respondsToSelector: sel] )
36 return [obj performSelector: sel];
37 return nil;
38 }
39 #define CATCH_UNSAFE_UNRETAINED
Phil Nash53c990a2012-03-17 18:20:06 +000040#else
Phil Nash861a1e72012-04-28 12:29:52 +010041 inline void arcSafeRelease( NSObject* ){}
Phil Nash44fbbb02012-05-16 15:07:11 +010042 inline id performOptionalSelector( id obj, SEL sel ) {
Phil Nash861a1e72012-04-28 12:29:52 +010043 #pragma clang diagnostic push
44 #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
45 if( [obj respondsToSelector: sel] )
46 return [obj performSelector: sel];
47 #pragma clang diagnostic pop
48 return nil;
49 }
50 #define CATCH_UNSAFE_UNRETAINED __unsafe_unretained
Phil Nash53c990a2012-03-17 18:20:06 +000051#endif
52
Phil Nash58e9a8b2011-02-08 08:42:05 +000053///////////////////////////////////////////////////////////////////////////////
54// This protocol is really only here for (self) documenting purposes, since
55// all its methods are optional.
Phil Nashf59ecbc2010-11-16 07:00:08 +000056@protocol OcFixture
57
58@optional
59
60-(void) setUp;
61-(void) tearDown;
62
63@end
64
Phil Nash44fbbb02012-05-16 15:07:11 +010065namespace Catch {
66
67 class OcMethod : public ITestCase {
68
Phil Nashd52f61c2010-11-14 22:47:30 +000069 public:
Phil Nash44fbbb02012-05-16 15:07:11 +010070 OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {}
Phil Nashd52f61c2010-11-14 22:47:30 +000071
Phil Nash44fbbb02012-05-16 15:07:11 +010072 virtual void invoke() const {
Phil Nash53c990a2012-03-17 18:20:06 +000073 id obj = [[m_cls alloc] init];
Phil Nashf59ecbc2010-11-16 07:00:08 +000074
Phil Nash53c990a2012-03-17 18:20:06 +000075 performOptionalSelector( obj, @selector(setUp) );
76 performOptionalSelector( obj, m_sel );
77 performOptionalSelector( obj, @selector(tearDown) );
Phil Nashf59ecbc2010-11-16 07:00:08 +000078
Phil Nash53c990a2012-03-17 18:20:06 +000079 arcSafeRelease( obj );
Phil Nashd52f61c2010-11-14 22:47:30 +000080 }
81
Phil Nash44fbbb02012-05-16 15:07:11 +010082 virtual ITestCase* clone() const {
Phil Nashf59ecbc2010-11-16 07:00:08 +000083 return new OcMethod( m_cls, m_sel );
Phil Nashd52f61c2010-11-14 22:47:30 +000084 }
85
Phil Nash44fbbb02012-05-16 15:07:11 +010086 virtual bool operator == ( const ITestCase& other ) const {
Phil Nashd52f61c2010-11-14 22:47:30 +000087 const OcMethod* ocmOther = dynamic_cast<const OcMethod*> ( &other );
88 return ocmOther && ocmOther->m_sel == m_sel;
89 }
90
Phil Nash44fbbb02012-05-16 15:07:11 +010091 virtual bool operator < ( const ITestCase& other ) const {
Phil Nashd52f61c2010-11-14 22:47:30 +000092 const OcMethod* ocmOther = dynamic_cast<const OcMethod*> ( &other );
93 return ocmOther && ocmOther->m_sel < m_sel;
94 }
95
96 private:
Phil Nashf59ecbc2010-11-16 07:00:08 +000097 Class m_cls;
Phil Nashd52f61c2010-11-14 22:47:30 +000098 SEL m_sel;
99 };
100
Phil Nash44fbbb02012-05-16 15:07:11 +0100101 namespace Detail{
Phil Nashf59ecbc2010-11-16 07:00:08 +0000102
Phil Nash44fbbb02012-05-16 15:07:11 +0100103 inline bool startsWith( const std::string& str, const std::string& sub ) {
Phil Nashd52f61c2010-11-14 22:47:30 +0000104 return str.length() > sub.length() && str.substr( 0, sub.length() ) == sub;
105 }
106
Phil Nash44fbbb02012-05-16 15:07:11 +0100107 inline std::string getAnnotation( Class cls,
108 const std::string& annotationName,
109 const std::string& testCaseName ) {
Phil Nashd52f61c2010-11-14 22:47:30 +0000110 NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()];
111 SEL sel = NSSelectorFromString( selStr );
Phil Nash53c990a2012-03-17 18:20:06 +0000112 arcSafeRelease( selStr );
113 id value = performOptionalSelector( cls, sel );
114 if( value )
115 return [(NSString*)value UTF8String];
Phil Nashd52f61c2010-11-14 22:47:30 +0000116 return "";
117 }
Phil Nashf59ecbc2010-11-16 07:00:08 +0000118 }
119
Phil Nash44fbbb02012-05-16 15:07:11 +0100120 inline size_t registerTestMethods() {
Phil Nashf59ecbc2010-11-16 07:00:08 +0000121 size_t noTestMethods = 0;
122 int noClasses = objc_getClassList( NULL, 0 );
Phil Nashd52f61c2010-11-14 22:47:30 +0000123
Phil Nash861a1e72012-04-28 12:29:52 +0100124 Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses);
Phil Nash53c990a2012-03-17 18:20:06 +0000125 objc_getClassList( classes, noClasses );
Phil Nashf59ecbc2010-11-16 07:00:08 +0000126
Phil Nash44fbbb02012-05-16 15:07:11 +0100127 for( int c = 0; c < noClasses; c++ ) {
Phil Nashf59ecbc2010-11-16 07:00:08 +0000128 Class cls = classes[c];
129 {
130 u_int count;
131 Method* methods = class_copyMethodList( cls, &count );
Phil Nash44fbbb02012-05-16 15:07:11 +0100132 for( int m = 0; m < count ; m++ ) {
Phil Nashf59ecbc2010-11-16 07:00:08 +0000133 SEL selector = method_getName(methods[m]);
134 std::string methodName = sel_getName(selector);
Phil Nash44fbbb02012-05-16 15:07:11 +0100135 if( Detail::startsWith( methodName, "Catch_TestCase_" ) ) {
Phil Nashf59ecbc2010-11-16 07:00:08 +0000136 std::string testCaseName = methodName.substr( 15 );
Phil Nash53c990a2012-03-17 18:20:06 +0000137 std::string name = Detail::getAnnotation( cls, "Name", testCaseName );
138 std::string desc = Detail::getAnnotation( cls, "Description", testCaseName );
Phil Nashf59ecbc2010-11-16 07:00:08 +0000139
Phil Nashb84444c2012-05-10 08:16:30 +0100140 Context::getTestCaseRegistry().registerTest( TestCaseInfo( new OcMethod( cls, selector ), name.c_str(), desc.c_str(), SourceLineInfo() ) );
Phil Nashf59ecbc2010-11-16 07:00:08 +0000141 noTestMethods++;
Phil Nashf59ecbc2010-11-16 07:00:08 +0000142 }
143 }
144 free(methods);
145 }
146 }
147 return noTestMethods;
Phil Nash966f5db2012-03-04 21:18:46 +0000148 }
149
Phil Nash44fbbb02012-05-16 15:07:11 +0100150 inline std::string toString( NSString* const& nsstring ) {
Phil Nash966f5db2012-03-04 21:18:46 +0000151 return std::string( "@\"" ) + [nsstring UTF8String] + "\"";
152 }
153
Phil Nash44fbbb02012-05-16 15:07:11 +0100154 namespace Matchers {
155 namespace Impl {
156 namespace NSStringMatchers {
157
158 struct StringHolder {
Phil Nash966f5db2012-03-04 21:18:46 +0000159 StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
Phil Nash44fbbb02012-05-16 15:07:11 +0100160 StringHolder() {
Phil Nash53c990a2012-03-17 18:20:06 +0000161 arcSafeRelease( m_substr );
Phil Nash966f5db2012-03-04 21:18:46 +0000162 }
163
164 NSString* m_substr;
165 };
166
Phil Nash44fbbb02012-05-16 15:07:11 +0100167 struct Equals : StringHolder {
Phil Nashdb837a12012-03-14 20:04:50 +0000168 Equals( NSString* substr ) : StringHolder( substr ){}
169
Phil Nash44fbbb02012-05-16 15:07:11 +0100170 bool operator()( NSString* str ) const {
Phil Nashdb837a12012-03-14 20:04:50 +0000171 return [str isEqualToString:m_substr];
172 }
173
Phil Nash44fbbb02012-05-16 15:07:11 +0100174 friend std::ostream& operator<<( std::ostream& os, const Equals& matcher ) {
Phil Nashdb837a12012-03-14 20:04:50 +0000175 os << "equals string: " << Catch::toString( matcher.m_substr );
176 return os;
177 }
178 };
179
Phil Nash44fbbb02012-05-16 15:07:11 +0100180 struct Contains : StringHolder {
Phil Nash966f5db2012-03-04 21:18:46 +0000181 Contains( NSString* substr ) : StringHolder( substr ){}
182
Phil Nash44fbbb02012-05-16 15:07:11 +0100183 bool operator()( NSString* str ) const {
Phil Nash966f5db2012-03-04 21:18:46 +0000184 return [str rangeOfString:m_substr].location != NSNotFound;
185 }
186
Phil Nash44fbbb02012-05-16 15:07:11 +0100187 friend std::ostream& operator<<( std::ostream& os, const Contains& matcher ) {
Phil Nash966f5db2012-03-04 21:18:46 +0000188 os << "contains: " << Catch::toString( matcher.m_substr );
189 return os;
190 }
191 };
192
Phil Nash44fbbb02012-05-16 15:07:11 +0100193 struct StartsWith : StringHolder {
Phil Nash966f5db2012-03-04 21:18:46 +0000194 StartsWith( NSString* substr ) : StringHolder( substr ){}
195
Phil Nash44fbbb02012-05-16 15:07:11 +0100196 bool operator()( NSString* str ) const {
Phil Nash966f5db2012-03-04 21:18:46 +0000197 return [str rangeOfString:m_substr].location == 0;
198 }
199
Phil Nash44fbbb02012-05-16 15:07:11 +0100200 friend std::ostream& operator<<( std::ostream& os, const StartsWith& matcher ) {
Phil Nash966f5db2012-03-04 21:18:46 +0000201 os << "starts with: " << Catch::toString( matcher.m_substr );
202 return os;
203 }
204 };
Phil Nash44fbbb02012-05-16 15:07:11 +0100205 struct EndsWith : StringHolder {
Phil Nash966f5db2012-03-04 21:18:46 +0000206 EndsWith( NSString* substr ) : StringHolder( substr ){}
207
Phil Nash44fbbb02012-05-16 15:07:11 +0100208 bool operator()( NSString* str ) const {
Phil Nash966f5db2012-03-04 21:18:46 +0000209 return [str rangeOfString:m_substr].location == [str length] - [m_substr length];
210 }
211
Phil Nash44fbbb02012-05-16 15:07:11 +0100212 friend std::ostream& operator<<( std::ostream& os, const EndsWith& matcher ) {
Phil Nash966f5db2012-03-04 21:18:46 +0000213 os << "ends with: " << Catch::toString( matcher.m_substr );
214 return os;
215 }
216 };
217
218 } // namespace NSStringMatchers
219 } // namespace Impl
220
Phil Nashdb837a12012-03-14 20:04:50 +0000221 inline Impl::NSStringMatchers::Equals
222 Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); }
223
Phil Nash966f5db2012-03-04 21:18:46 +0000224 inline Impl::NSStringMatchers::Contains
225 Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); }
Phil Nashdb837a12012-03-14 20:04:50 +0000226
Phil Nash966f5db2012-03-04 21:18:46 +0000227 inline Impl::NSStringMatchers::StartsWith
228 StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); }
Phil Nashdb837a12012-03-14 20:04:50 +0000229
Phil Nash966f5db2012-03-04 21:18:46 +0000230 inline Impl::NSStringMatchers::EndsWith
231 EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); }
232
233 } // namespace Matchers
234
235 using namespace Matchers;
236
237} // namespace Catch
Phil Nashd52f61c2010-11-14 22:47:30 +0000238
Phil Nash58e9a8b2011-02-08 08:42:05 +0000239///////////////////////////////////////////////////////////////////////////////
Phil Nashd52f61c2010-11-14 22:47:30 +0000240#define OC_TEST_CASE( name, desc )\
Phil Nash53c990a2012-03-17 18:20:06 +0000241+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \
Phil Nashd52f61c2010-11-14 22:47:30 +0000242{\
Phil Nash53c990a2012-03-17 18:20:06 +0000243return @ name; \
Phil Nashd52f61c2010-11-14 22:47:30 +0000244}\
Phil Nash53c990a2012-03-17 18:20:06 +0000245+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \
Phil Nashd52f61c2010-11-14 22:47:30 +0000246{ \
Phil Nash53c990a2012-03-17 18:20:06 +0000247return @ desc; \
Phil Nashd52f61c2010-11-14 22:47:30 +0000248} \
249-(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test )
250
Phil Nashd10d2d32012-05-10 21:46:46 +0100251#endif // TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED