2010-11-14 23:47:30 +01:00
|
|
|
/*
|
|
|
|
* Created by Phil on 14/11/2010.
|
|
|
|
* Copyright 2010 Two Blue Cubes Ltd. All rights reserved.
|
|
|
|
*
|
|
|
|
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
|
|
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
*/
|
|
|
|
#ifndef TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED
|
|
|
|
#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED
|
|
|
|
|
2012-02-10 09:28:37 +01:00
|
|
|
#import <Foundation/Foundation.h>
|
2010-11-14 23:47:30 +01:00
|
|
|
#import <objc/runtime.h>
|
2012-02-10 09:28:37 +01:00
|
|
|
|
2010-11-14 23:47:30 +01:00
|
|
|
#include <string>
|
2011-04-26 09:32:40 +02:00
|
|
|
|
2011-05-24 09:23:02 +02:00
|
|
|
// NB. Any general catch headers included here must be included
|
|
|
|
// in catch.hpp first to make sure they are included by the single
|
|
|
|
// header for non obj-usage
|
2011-02-01 17:09:18 +01:00
|
|
|
#include "internal/catch_test_case_info.hpp"
|
2010-11-14 23:47:30 +01:00
|
|
|
|
2012-03-17 19:20:06 +01:00
|
|
|
#ifdef __has_feature
|
|
|
|
#define CATCH_ARC_ENABLED __has_feature(objc_arc)
|
|
|
|
#else
|
|
|
|
#define CATCH_ARC_ENABLED 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void arcSafeRelease( NSObject* obj );
|
|
|
|
id performOptionalSelector( id obj, SEL sel );
|
|
|
|
|
|
|
|
#if !CATCH_ARC_ENABLED
|
2012-05-16 16:07:11 +02:00
|
|
|
inline void arcSafeRelease( NSObject* obj ) {
|
2012-04-28 13:29:52 +02:00
|
|
|
[obj release];
|
|
|
|
}
|
2012-05-16 16:07:11 +02:00
|
|
|
inline id performOptionalSelector( id obj, SEL sel ) {
|
2012-04-28 13:29:52 +02:00
|
|
|
if( [obj respondsToSelector: sel] )
|
|
|
|
return [obj performSelector: sel];
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
#define CATCH_UNSAFE_UNRETAINED
|
2012-03-17 19:20:06 +01:00
|
|
|
#else
|
2012-04-28 13:29:52 +02:00
|
|
|
inline void arcSafeRelease( NSObject* ){}
|
2012-05-16 16:07:11 +02:00
|
|
|
inline id performOptionalSelector( id obj, SEL sel ) {
|
2012-04-28 13:29:52 +02:00
|
|
|
#pragma clang diagnostic push
|
|
|
|
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
|
|
|
if( [obj respondsToSelector: sel] )
|
|
|
|
return [obj performSelector: sel];
|
|
|
|
#pragma clang diagnostic pop
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained
|
2012-03-17 19:20:06 +01:00
|
|
|
#endif
|
|
|
|
|
2011-02-08 09:42:05 +01:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// This protocol is really only here for (self) documenting purposes, since
|
|
|
|
// all its methods are optional.
|
2010-11-16 08:00:08 +01:00
|
|
|
@protocol OcFixture
|
|
|
|
|
|
|
|
@optional
|
|
|
|
|
|
|
|
-(void) setUp;
|
|
|
|
-(void) tearDown;
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
2012-05-16 16:07:11 +02:00
|
|
|
namespace Catch {
|
|
|
|
|
|
|
|
class OcMethod : public ITestCase {
|
|
|
|
|
2010-11-14 23:47:30 +01:00
|
|
|
public:
|
2012-05-16 16:07:11 +02:00
|
|
|
OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {}
|
2010-11-14 23:47:30 +01:00
|
|
|
|
2012-05-16 16:07:11 +02:00
|
|
|
virtual void invoke() const {
|
2012-03-17 19:20:06 +01:00
|
|
|
id obj = [[m_cls alloc] init];
|
2010-11-16 08:00:08 +01:00
|
|
|
|
2012-03-17 19:20:06 +01:00
|
|
|
performOptionalSelector( obj, @selector(setUp) );
|
|
|
|
performOptionalSelector( obj, m_sel );
|
|
|
|
performOptionalSelector( obj, @selector(tearDown) );
|
2010-11-16 08:00:08 +01:00
|
|
|
|
2012-03-17 19:20:06 +01:00
|
|
|
arcSafeRelease( obj );
|
2010-11-14 23:47:30 +01:00
|
|
|
}
|
|
|
|
|
2012-05-16 16:07:11 +02:00
|
|
|
virtual ITestCase* clone() const {
|
2010-11-16 08:00:08 +01:00
|
|
|
return new OcMethod( m_cls, m_sel );
|
2010-11-14 23:47:30 +01:00
|
|
|
}
|
|
|
|
|
2012-05-16 16:07:11 +02:00
|
|
|
virtual bool operator == ( const ITestCase& other ) const {
|
2010-11-14 23:47:30 +01:00
|
|
|
const OcMethod* ocmOther = dynamic_cast<const OcMethod*> ( &other );
|
|
|
|
return ocmOther && ocmOther->m_sel == m_sel;
|
|
|
|
}
|
|
|
|
|
2012-05-16 16:07:11 +02:00
|
|
|
virtual bool operator < ( const ITestCase& other ) const {
|
2010-11-14 23:47:30 +01:00
|
|
|
const OcMethod* ocmOther = dynamic_cast<const OcMethod*> ( &other );
|
|
|
|
return ocmOther && ocmOther->m_sel < m_sel;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2010-11-16 08:00:08 +01:00
|
|
|
Class m_cls;
|
2010-11-14 23:47:30 +01:00
|
|
|
SEL m_sel;
|
|
|
|
};
|
|
|
|
|
2012-05-16 16:07:11 +02:00
|
|
|
namespace Detail{
|
2010-11-16 08:00:08 +01:00
|
|
|
|
2012-05-16 16:07:11 +02:00
|
|
|
inline bool startsWith( const std::string& str, const std::string& sub ) {
|
2010-11-14 23:47:30 +01:00
|
|
|
return str.length() > sub.length() && str.substr( 0, sub.length() ) == sub;
|
|
|
|
}
|
|
|
|
|
2012-05-16 16:07:11 +02:00
|
|
|
inline std::string getAnnotation( Class cls,
|
|
|
|
const std::string& annotationName,
|
|
|
|
const std::string& testCaseName ) {
|
2010-11-14 23:47:30 +01:00
|
|
|
NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()];
|
|
|
|
SEL sel = NSSelectorFromString( selStr );
|
2012-03-17 19:20:06 +01:00
|
|
|
arcSafeRelease( selStr );
|
|
|
|
id value = performOptionalSelector( cls, sel );
|
|
|
|
if( value )
|
|
|
|
return [(NSString*)value UTF8String];
|
2010-11-14 23:47:30 +01:00
|
|
|
return "";
|
|
|
|
}
|
2010-11-16 08:00:08 +01:00
|
|
|
}
|
|
|
|
|
2012-05-16 16:07:11 +02:00
|
|
|
inline size_t registerTestMethods() {
|
2010-11-16 08:00:08 +01:00
|
|
|
size_t noTestMethods = 0;
|
|
|
|
int noClasses = objc_getClassList( NULL, 0 );
|
|
|
|
|
2012-04-28 13:29:52 +02:00
|
|
|
Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses);
|
2012-03-17 19:20:06 +01:00
|
|
|
objc_getClassList( classes, noClasses );
|
2010-11-14 23:47:30 +01:00
|
|
|
|
2012-05-16 16:07:11 +02:00
|
|
|
for( int c = 0; c < noClasses; c++ ) {
|
2010-11-16 08:00:08 +01:00
|
|
|
Class cls = classes[c];
|
|
|
|
{
|
|
|
|
u_int count;
|
|
|
|
Method* methods = class_copyMethodList( cls, &count );
|
2012-05-21 18:11:55 +02:00
|
|
|
for( u_int m = 0; m < count ; m++ ) {
|
2010-11-16 08:00:08 +01:00
|
|
|
SEL selector = method_getName(methods[m]);
|
|
|
|
std::string methodName = sel_getName(selector);
|
2012-05-16 16:07:11 +02:00
|
|
|
if( Detail::startsWith( methodName, "Catch_TestCase_" ) ) {
|
2010-11-16 08:00:08 +01:00
|
|
|
std::string testCaseName = methodName.substr( 15 );
|
2012-03-17 19:20:06 +01:00
|
|
|
std::string name = Detail::getAnnotation( cls, "Name", testCaseName );
|
|
|
|
std::string desc = Detail::getAnnotation( cls, "Description", testCaseName );
|
2010-11-16 08:00:08 +01:00
|
|
|
|
2012-05-10 09:16:30 +02:00
|
|
|
Context::getTestCaseRegistry().registerTest( TestCaseInfo( new OcMethod( cls, selector ), name.c_str(), desc.c_str(), SourceLineInfo() ) );
|
2010-11-16 08:00:08 +01:00
|
|
|
noTestMethods++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free(methods);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return noTestMethods;
|
2012-03-04 22:18:46 +01:00
|
|
|
}
|
|
|
|
|
2012-05-16 16:07:11 +02:00
|
|
|
inline std::string toString( NSString* const& nsstring ) {
|
2012-03-04 22:18:46 +01:00
|
|
|
return std::string( "@\"" ) + [nsstring UTF8String] + "\"";
|
|
|
|
}
|
|
|
|
|
2012-05-16 16:07:11 +02:00
|
|
|
namespace Matchers {
|
|
|
|
namespace Impl {
|
|
|
|
namespace NSStringMatchers {
|
|
|
|
|
|
|
|
struct StringHolder {
|
2012-03-04 22:18:46 +01:00
|
|
|
StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
|
2012-05-16 16:07:11 +02:00
|
|
|
StringHolder() {
|
2012-03-17 19:20:06 +01:00
|
|
|
arcSafeRelease( m_substr );
|
2012-03-04 22:18:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
NSString* m_substr;
|
|
|
|
};
|
|
|
|
|
2012-05-16 16:07:11 +02:00
|
|
|
struct Equals : StringHolder {
|
2012-03-14 21:04:50 +01:00
|
|
|
Equals( NSString* substr ) : StringHolder( substr ){}
|
|
|
|
|
2012-05-16 16:07:11 +02:00
|
|
|
bool operator()( NSString* str ) const {
|
2012-03-14 21:04:50 +01:00
|
|
|
return [str isEqualToString:m_substr];
|
|
|
|
}
|
|
|
|
|
2012-05-16 16:07:11 +02:00
|
|
|
friend std::ostream& operator<<( std::ostream& os, const Equals& matcher ) {
|
2012-03-14 21:04:50 +01:00
|
|
|
os << "equals string: " << Catch::toString( matcher.m_substr );
|
|
|
|
return os;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-05-16 16:07:11 +02:00
|
|
|
struct Contains : StringHolder {
|
2012-03-04 22:18:46 +01:00
|
|
|
Contains( NSString* substr ) : StringHolder( substr ){}
|
|
|
|
|
2012-05-16 16:07:11 +02:00
|
|
|
bool operator()( NSString* str ) const {
|
2012-03-04 22:18:46 +01:00
|
|
|
return [str rangeOfString:m_substr].location != NSNotFound;
|
|
|
|
}
|
|
|
|
|
2012-05-16 16:07:11 +02:00
|
|
|
friend std::ostream& operator<<( std::ostream& os, const Contains& matcher ) {
|
2012-03-04 22:18:46 +01:00
|
|
|
os << "contains: " << Catch::toString( matcher.m_substr );
|
|
|
|
return os;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-05-16 16:07:11 +02:00
|
|
|
struct StartsWith : StringHolder {
|
2012-03-04 22:18:46 +01:00
|
|
|
StartsWith( NSString* substr ) : StringHolder( substr ){}
|
|
|
|
|
2012-05-16 16:07:11 +02:00
|
|
|
bool operator()( NSString* str ) const {
|
2012-03-04 22:18:46 +01:00
|
|
|
return [str rangeOfString:m_substr].location == 0;
|
|
|
|
}
|
|
|
|
|
2012-05-16 16:07:11 +02:00
|
|
|
friend std::ostream& operator<<( std::ostream& os, const StartsWith& matcher ) {
|
2012-03-04 22:18:46 +01:00
|
|
|
os << "starts with: " << Catch::toString( matcher.m_substr );
|
|
|
|
return os;
|
|
|
|
}
|
|
|
|
};
|
2012-05-16 16:07:11 +02:00
|
|
|
struct EndsWith : StringHolder {
|
2012-03-04 22:18:46 +01:00
|
|
|
EndsWith( NSString* substr ) : StringHolder( substr ){}
|
|
|
|
|
2012-05-16 16:07:11 +02:00
|
|
|
bool operator()( NSString* str ) const {
|
2012-03-04 22:18:46 +01:00
|
|
|
return [str rangeOfString:m_substr].location == [str length] - [m_substr length];
|
|
|
|
}
|
|
|
|
|
2012-05-16 16:07:11 +02:00
|
|
|
friend std::ostream& operator<<( std::ostream& os, const EndsWith& matcher ) {
|
2012-03-04 22:18:46 +01:00
|
|
|
os << "ends with: " << Catch::toString( matcher.m_substr );
|
|
|
|
return os;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace NSStringMatchers
|
|
|
|
} // namespace Impl
|
|
|
|
|
2012-03-14 21:04:50 +01:00
|
|
|
inline Impl::NSStringMatchers::Equals
|
|
|
|
Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); }
|
|
|
|
|
2012-03-04 22:18:46 +01:00
|
|
|
inline Impl::NSStringMatchers::Contains
|
|
|
|
Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); }
|
2012-03-14 21:04:50 +01:00
|
|
|
|
2012-03-04 22:18:46 +01:00
|
|
|
inline Impl::NSStringMatchers::StartsWith
|
|
|
|
StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); }
|
2012-03-14 21:04:50 +01:00
|
|
|
|
2012-03-04 22:18:46 +01:00
|
|
|
inline Impl::NSStringMatchers::EndsWith
|
|
|
|
EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); }
|
|
|
|
|
|
|
|
} // namespace Matchers
|
|
|
|
|
|
|
|
using namespace Matchers;
|
|
|
|
|
|
|
|
} // namespace Catch
|
2010-11-14 23:47:30 +01:00
|
|
|
|
2011-02-08 09:42:05 +01:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2010-11-14 23:47:30 +01:00
|
|
|
#define OC_TEST_CASE( name, desc )\
|
2012-03-17 19:20:06 +01:00
|
|
|
+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \
|
2010-11-14 23:47:30 +01:00
|
|
|
{\
|
2012-03-17 19:20:06 +01:00
|
|
|
return @ name; \
|
2010-11-14 23:47:30 +01:00
|
|
|
}\
|
2012-03-17 19:20:06 +01:00
|
|
|
+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \
|
2010-11-14 23:47:30 +01:00
|
|
|
{ \
|
2012-03-17 19:20:06 +01:00
|
|
|
return @ desc; \
|
2010-11-14 23:47:30 +01:00
|
|
|
} \
|
|
|
|
-(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test )
|
|
|
|
|
2012-05-10 22:46:46 +02:00
|
|
|
#endif // TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED
|