blob: f8044a0c34af11fae0bc18499ebee0fa0cf05212 [file] [log] [blame]
Phil Nashd52f61c2010-11-14 22:47:30 +00001/*
2 * catch_objc.hpp
Phil Nash58e9a8b2011-02-08 08:42:05 +00003 * Catch
Phil Nashd52f61c2010-11-14 22:47:30 +00004 *
5 * Created by Phil on 14/11/2010.
6 * Copyright 2010 Two Blue Cubes Ltd. All rights reserved.
7 *
8 * Distributed under the Boost Software License, Version 1.0. (See accompanying
9 * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
10 *
11 */
12
13#ifndef TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED
14#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED
15
Phil Nash6abf7022012-02-10 08:28:37 +000016#import <Foundation/Foundation.h>
Phil Nashd52f61c2010-11-14 22:47:30 +000017#import <objc/runtime.h>
Phil Nash6abf7022012-02-10 08:28:37 +000018
Phil Nashd52f61c2010-11-14 22:47:30 +000019#include <string>
Phil Nash823ea3e2011-04-26 08:32:40 +010020
Phil Nash89d1e6c2011-05-24 08:23:02 +010021// NB. Any general catch headers included here must be included
22// in catch.hpp first to make sure they are included by the single
23// header for non obj-usage
Phil Nash0847a0f2011-02-01 16:09:18 +000024#include "internal/catch_test_case_info.hpp"
Phil Nashd52f61c2010-11-14 22:47:30 +000025
Phil Nash58e9a8b2011-02-08 08:42:05 +000026///////////////////////////////////////////////////////////////////////////////
27// This protocol is really only here for (self) documenting purposes, since
28// all its methods are optional.
Phil Nashf59ecbc2010-11-16 07:00:08 +000029@protocol OcFixture
30
31@optional
32
33-(void) setUp;
34-(void) tearDown;
35
36@end
37
Phil Nashd52f61c2010-11-14 22:47:30 +000038namespace Catch
39{
Phil Nash0847a0f2011-02-01 16:09:18 +000040 class OcMethod : public ITestCase
Phil Nashd52f61c2010-11-14 22:47:30 +000041 {
42 public:
Phil Nash58e9a8b2011-02-08 08:42:05 +000043 ///////////////////////////////////////////////////////////////////////
44 OcMethod
45 (
46 Class cls,
47 SEL sel
48 )
49 : m_cls( cls ),
50 m_sel( sel )
Phil Nashd52f61c2010-11-14 22:47:30 +000051 {
52 }
53
Phil Nash58e9a8b2011-02-08 08:42:05 +000054 ///////////////////////////////////////////////////////////////////////
55 virtual void invoke
56 ()
57 const
Phil Nashd52f61c2010-11-14 22:47:30 +000058 {
Phil Nashf59ecbc2010-11-16 07:00:08 +000059 id obj = class_createInstance( m_cls, 0 );
60 obj = [obj init];
61
62 if( [obj respondsToSelector: @selector(setUp) ] )
63 [obj performSelector: @selector(setUp)];
64
Phil Nashd52f61c2010-11-14 22:47:30 +000065 if( [obj respondsToSelector: m_sel] )
66 [obj performSelector: m_sel];
Phil Nashf59ecbc2010-11-16 07:00:08 +000067
68 if( [obj respondsToSelector: @selector(tearDown) ] )
69 [obj performSelector: @selector(tearDown)];
70
Phil Nashd52f61c2010-11-14 22:47:30 +000071 [obj release];
72 }
73
Phil Nash58e9a8b2011-02-08 08:42:05 +000074 ///////////////////////////////////////////////////////////////////////
75 virtual ITestCase* clone
76 ()
77 const
Phil Nashd52f61c2010-11-14 22:47:30 +000078 {
Phil Nashf59ecbc2010-11-16 07:00:08 +000079 return new OcMethod( m_cls, m_sel );
Phil Nashd52f61c2010-11-14 22:47:30 +000080 }
81
Phil Nash58e9a8b2011-02-08 08:42:05 +000082 ///////////////////////////////////////////////////////////////////////
83 virtual bool operator ==
84 (
85 const ITestCase& other
86 )
87 const
Phil Nashd52f61c2010-11-14 22:47:30 +000088 {
89 const OcMethod* ocmOther = dynamic_cast<const OcMethod*> ( &other );
90 return ocmOther && ocmOther->m_sel == m_sel;
91 }
92
Phil Nash58e9a8b2011-02-08 08:42:05 +000093 ///////////////////////////////////////////////////////////////////////
94 virtual bool operator <
95 (
96 const ITestCase& other
97 )
98 const
Phil Nashd52f61c2010-11-14 22:47:30 +000099 {
100 const OcMethod* ocmOther = dynamic_cast<const OcMethod*> ( &other );
101 return ocmOther && ocmOther->m_sel < m_sel;
102 }
103
104 private:
Phil Nashf59ecbc2010-11-16 07:00:08 +0000105 Class m_cls;
Phil Nashd52f61c2010-11-14 22:47:30 +0000106 SEL m_sel;
107 };
108
Phil Nashf59ecbc2010-11-16 07:00:08 +0000109 namespace Detail
Phil Nashd52f61c2010-11-14 22:47:30 +0000110 {
Phil Nashf59ecbc2010-11-16 07:00:08 +0000111
Phil Nash58e9a8b2011-02-08 08:42:05 +0000112 ///////////////////////////////////////////////////////////////////////
113 inline bool startsWith
114 (
115 const std::string& str,
116 const std::string& sub
117 )
Phil Nashd52f61c2010-11-14 22:47:30 +0000118 {
119 return str.length() > sub.length() && str.substr( 0, sub.length() ) == sub;
120 }
121
Phil Nash58e9a8b2011-02-08 08:42:05 +0000122 ///////////////////////////////////////////////////////////////////////
123 inline const char* getAnnotation
124 (
125 Class cls,
126 const std::string& annotationName,
127 const std::string& testCaseName
128 )
Phil Nashd52f61c2010-11-14 22:47:30 +0000129 {
130 NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()];
131 SEL sel = NSSelectorFromString( selStr );
132 [selStr release];
Phil Nashf59ecbc2010-11-16 07:00:08 +0000133 if( [cls respondsToSelector: sel] )
134 return (const char*)[cls performSelector: sel];
Phil Nashd52f61c2010-11-14 22:47:30 +0000135 return "";
136 }
Phil Nashf59ecbc2010-11-16 07:00:08 +0000137 }
138
Phil Nash58e9a8b2011-02-08 08:42:05 +0000139 ///////////////////////////////////////////////////////////////////////////
140 inline size_t registerTestMethods
141 ()
Phil Nashf59ecbc2010-11-16 07:00:08 +0000142 {
143 size_t noTestMethods = 0;
144 int noClasses = objc_getClassList( NULL, 0 );
Phil Nashd52f61c2010-11-14 22:47:30 +0000145
Phil Nashf59ecbc2010-11-16 07:00:08 +0000146 std::vector<Class> classes( noClasses );
147 objc_getClassList( &classes[0], noClasses );
148
149 for( int c = 0; c < noClasses; c++ )
150 {
151 Class cls = classes[c];
152 {
153 u_int count;
154 Method* methods = class_copyMethodList( cls, &count );
155 for( int m = 0; m < count ; m++ )
156 {
157 SEL selector = method_getName(methods[m]);
158 std::string methodName = sel_getName(selector);
159 if( Detail::startsWith( methodName, "Catch_TestCase_" ) )
160 {
161 std::string testCaseName = methodName.substr( 15 );
Phil Nash0847a0f2011-02-01 16:09:18 +0000162 const char* name = Detail::getAnnotation( cls, "Name", testCaseName );
163 const char* desc = Detail::getAnnotation( cls, "Description", testCaseName );
Phil Nashf59ecbc2010-11-16 07:00:08 +0000164
Phil Nash823ea3e2011-04-26 08:32:40 +0100165 Hub::getTestCaseRegistry().registerTest( TestCaseInfo( new OcMethod( cls, selector ), name, desc, "", 0 ) );
Phil Nashf59ecbc2010-11-16 07:00:08 +0000166 noTestMethods++;
167
168 }
169 }
170 free(methods);
171 }
172 }
173 return noTestMethods;
Phil Nash966f5db2012-03-04 21:18:46 +0000174 }
175
176 template<>
177 inline std::string toString<NSString*>( NSString* const& nsstring )
178 {
179 return std::string( "@\"" ) + [nsstring UTF8String] + "\"";
180 }
181
182 namespace Matchers
183 {
184 namespace Impl
185 {
186 namespace NSStringMatchers
187 {
188 struct StringHolder
189 {
190 StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
191 StringHolder()
192 {
193 [m_substr release];
194 }
195
196 NSString* m_substr;
197 };
198
Phil Nashdb837a12012-03-14 20:04:50 +0000199 struct Equals : StringHolder
200 {
201 Equals( NSString* substr ) : StringHolder( substr ){}
202
203 bool operator()( NSString* str ) const
204 {
205 return [str isEqualToString:m_substr];
206 }
207
208 friend std::ostream& operator<<( std::ostream& os, const Equals& matcher )
209 {
210 os << "equals string: " << Catch::toString( matcher.m_substr );
211 return os;
212 }
213 };
214
Phil Nash966f5db2012-03-04 21:18:46 +0000215 struct Contains : StringHolder
216 {
217 Contains( NSString* substr ) : StringHolder( substr ){}
218
219 bool operator()( NSString* str ) const
220 {
221 return [str rangeOfString:m_substr].location != NSNotFound;
222 }
223
224 friend std::ostream& operator<<( std::ostream& os, const Contains& matcher )
225 {
226 os << "contains: " << Catch::toString( matcher.m_substr );
227 return os;
228 }
229 };
230
231 struct StartsWith : StringHolder
232 {
233 StartsWith( NSString* substr ) : StringHolder( substr ){}
234
235 bool operator()( NSString* str ) const
236 {
237 return [str rangeOfString:m_substr].location == 0;
238 }
239
240 friend std::ostream& operator<<( std::ostream& os, const StartsWith& matcher )
241 {
242 os << "starts with: " << Catch::toString( matcher.m_substr );
243 return os;
244 }
245 };
246 struct EndsWith : StringHolder
247 {
248 EndsWith( NSString* substr ) : StringHolder( substr ){}
249
250 bool operator()( NSString* str ) const
251 {
252 return [str rangeOfString:m_substr].location == [str length] - [m_substr length];
253 }
254
255 friend std::ostream& operator<<( std::ostream& os, const EndsWith& matcher )
256 {
257 os << "ends with: " << Catch::toString( matcher.m_substr );
258 return os;
259 }
260 };
261
262 } // namespace NSStringMatchers
263 } // namespace Impl
264
Phil Nashdb837a12012-03-14 20:04:50 +0000265 inline Impl::NSStringMatchers::Equals
266 Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); }
267
Phil Nash966f5db2012-03-04 21:18:46 +0000268 inline Impl::NSStringMatchers::Contains
269 Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); }
Phil Nashdb837a12012-03-14 20:04:50 +0000270
Phil Nash966f5db2012-03-04 21:18:46 +0000271 inline Impl::NSStringMatchers::StartsWith
272 StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); }
Phil Nashdb837a12012-03-14 20:04:50 +0000273
Phil Nash966f5db2012-03-04 21:18:46 +0000274 inline Impl::NSStringMatchers::EndsWith
275 EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); }
276
277 } // namespace Matchers
278
279 using namespace Matchers;
280
281} // namespace Catch
Phil Nashd52f61c2010-11-14 22:47:30 +0000282
Phil Nash58e9a8b2011-02-08 08:42:05 +0000283///////////////////////////////////////////////////////////////////////////////
Phil Nashd52f61c2010-11-14 22:47:30 +0000284#define OC_TEST_CASE( name, desc )\
285+(const char*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \
286{\
287return name; \
288}\
289+(const char*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \
290{ \
291return desc; \
292} \
293-(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test )
294
295#endif // TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED