Catch/include/internal/catch_objc.hpp

252 lines
8.9 KiB
C++
Raw Normal View History

2010-11-14 17:47:30 -05: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 03:28:37 -05:00
#import <Foundation/Foundation.h>
2010-11-14 17:47:30 -05:00
#import <objc/runtime.h>
2012-02-10 03:28:37 -05:00
2010-11-14 17:47:30 -05:00
#include <string>
2011-04-26 03:32:40 -04: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
#include "internal/catch_test_case_info.hpp"
2010-11-14 17:47:30 -05:00
2012-03-17 14:20:06 -04: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 10:07:11 -04:00
inline void arcSafeRelease( NSObject* obj ) {
[obj release];
}
2012-05-16 10:07:11 -04:00
inline id performOptionalSelector( id obj, SEL sel ) {
if( [obj respondsToSelector: sel] )
return [obj performSelector: sel];
return nil;
}
#define CATCH_UNSAFE_UNRETAINED
2012-03-17 14:20:06 -04:00
#else
inline void arcSafeRelease( NSObject* ){}
2012-05-16 10:07:11 -04:00
inline id performOptionalSelector( id obj, SEL sel ) {
#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 14:20:06 -04:00
#endif
2011-02-08 03:42:05 -05:00
///////////////////////////////////////////////////////////////////////////////
// This protocol is really only here for (self) documenting purposes, since
// all its methods are optional.
2010-11-16 02:00:08 -05:00
@protocol OcFixture
@optional
-(void) setUp;
-(void) tearDown;
@end
2012-05-16 10:07:11 -04:00
namespace Catch {
class OcMethod : public ITestCase {
2010-11-14 17:47:30 -05:00
public:
2012-05-16 10:07:11 -04:00
OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {}
2010-11-14 17:47:30 -05:00
2012-05-16 10:07:11 -04:00
virtual void invoke() const {
2012-03-17 14:20:06 -04:00
id obj = [[m_cls alloc] init];
2010-11-16 02:00:08 -05:00
2012-03-17 14:20:06 -04:00
performOptionalSelector( obj, @selector(setUp) );
performOptionalSelector( obj, m_sel );
performOptionalSelector( obj, @selector(tearDown) );
2010-11-16 02:00:08 -05:00
2012-03-17 14:20:06 -04:00
arcSafeRelease( obj );
2010-11-14 17:47:30 -05:00
}
2012-05-16 10:07:11 -04:00
virtual ITestCase* clone() const {
2010-11-16 02:00:08 -05:00
return new OcMethod( m_cls, m_sel );
2010-11-14 17:47:30 -05:00
}
2012-05-16 10:07:11 -04:00
virtual bool operator == ( const ITestCase& other ) const {
2010-11-14 17:47:30 -05:00
const OcMethod* ocmOther = dynamic_cast<const OcMethod*> ( &other );
return ocmOther && ocmOther->m_sel == m_sel;
}
2012-05-16 10:07:11 -04:00
virtual bool operator < ( const ITestCase& other ) const {
2010-11-14 17:47:30 -05:00
const OcMethod* ocmOther = dynamic_cast<const OcMethod*> ( &other );
return ocmOther && ocmOther->m_sel < m_sel;
}
private:
2010-11-16 02:00:08 -05:00
Class m_cls;
2010-11-14 17:47:30 -05:00
SEL m_sel;
};
2012-05-16 10:07:11 -04:00
namespace Detail{
2010-11-16 02:00:08 -05:00
2012-05-16 10:07:11 -04:00
inline bool startsWith( const std::string& str, const std::string& sub ) {
2010-11-14 17:47:30 -05:00
return str.length() > sub.length() && str.substr( 0, sub.length() ) == sub;
}
2012-05-16 10:07:11 -04:00
inline std::string getAnnotation( Class cls,
const std::string& annotationName,
const std::string& testCaseName ) {
2010-11-14 17:47:30 -05:00
NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()];
SEL sel = NSSelectorFromString( selStr );
2012-03-17 14:20:06 -04:00
arcSafeRelease( selStr );
id value = performOptionalSelector( cls, sel );
if( value )
return [(NSString*)value UTF8String];
2010-11-14 17:47:30 -05:00
return "";
}
2010-11-16 02:00:08 -05:00
}
2012-05-16 10:07:11 -04:00
inline size_t registerTestMethods() {
2010-11-16 02:00:08 -05:00
size_t noTestMethods = 0;
int noClasses = objc_getClassList( NULL, 0 );
Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses);
2012-03-17 14:20:06 -04:00
objc_getClassList( classes, noClasses );
2010-11-14 17:47:30 -05:00
2012-05-16 10:07:11 -04:00
for( int c = 0; c < noClasses; c++ ) {
2010-11-16 02:00:08 -05:00
Class cls = classes[c];
{
u_int count;
Method* methods = class_copyMethodList( cls, &count );
for( u_int m = 0; m < count ; m++ ) {
2010-11-16 02:00:08 -05:00
SEL selector = method_getName(methods[m]);
std::string methodName = sel_getName(selector);
2012-05-16 10:07:11 -04:00
if( Detail::startsWith( methodName, "Catch_TestCase_" ) ) {
2010-11-16 02:00:08 -05:00
std::string testCaseName = methodName.substr( 15 );
2012-03-17 14:20:06 -04:00
std::string name = Detail::getAnnotation( cls, "Name", testCaseName );
std::string desc = Detail::getAnnotation( cls, "Description", testCaseName );
2010-11-16 02:00:08 -05:00
getCurrentContext().getTestCaseRegistry().registerTest( TestCaseInfo( new OcMethod( cls, selector ), name.c_str(), desc.c_str(), SourceLineInfo() ) );
2010-11-16 02:00:08 -05:00
noTestMethods++;
}
}
free(methods);
}
}
return noTestMethods;
}
2012-05-16 10:07:11 -04:00
inline std::string toString( NSString* const& nsstring ) {
return std::string( "@\"" ) + [nsstring UTF8String] + "\"";
}
2012-05-16 10:07:11 -04:00
namespace Matchers {
namespace Impl {
namespace NSStringMatchers {
struct StringHolder {
StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
2012-05-16 10:07:11 -04:00
StringHolder() {
2012-03-17 14:20:06 -04:00
arcSafeRelease( m_substr );
}
NSString* m_substr;
};
2012-05-16 10:07:11 -04:00
struct Equals : StringHolder {
2012-03-14 16:04:50 -04:00
Equals( NSString* substr ) : StringHolder( substr ){}
2012-05-16 10:07:11 -04:00
bool operator()( NSString* str ) const {
2012-03-14 16:04:50 -04:00
return [str isEqualToString:m_substr];
}
2012-05-16 10:07:11 -04:00
friend std::ostream& operator<<( std::ostream& os, const Equals& matcher ) {
2012-03-14 16:04:50 -04:00
os << "equals string: " << Catch::toString( matcher.m_substr );
return os;
}
};
2012-05-16 10:07:11 -04:00
struct Contains : StringHolder {
Contains( NSString* substr ) : StringHolder( substr ){}
2012-05-16 10:07:11 -04:00
bool operator()( NSString* str ) const {
return [str rangeOfString:m_substr].location != NSNotFound;
}
2012-05-16 10:07:11 -04:00
friend std::ostream& operator<<( std::ostream& os, const Contains& matcher ) {
os << "contains: " << Catch::toString( matcher.m_substr );
return os;
}
};
2012-05-16 10:07:11 -04:00
struct StartsWith : StringHolder {
StartsWith( NSString* substr ) : StringHolder( substr ){}
2012-05-16 10:07:11 -04:00
bool operator()( NSString* str ) const {
return [str rangeOfString:m_substr].location == 0;
}
2012-05-16 10:07:11 -04:00
friend std::ostream& operator<<( std::ostream& os, const StartsWith& matcher ) {
os << "starts with: " << Catch::toString( matcher.m_substr );
return os;
}
};
2012-05-16 10:07:11 -04:00
struct EndsWith : StringHolder {
EndsWith( NSString* substr ) : StringHolder( substr ){}
2012-05-16 10:07:11 -04:00
bool operator()( NSString* str ) const {
return [str rangeOfString:m_substr].location == [str length] - [m_substr length];
}
2012-05-16 10:07:11 -04:00
friend std::ostream& operator<<( std::ostream& os, const EndsWith& matcher ) {
os << "ends with: " << Catch::toString( matcher.m_substr );
return os;
}
};
} // namespace NSStringMatchers
} // namespace Impl
2012-03-14 16:04:50 -04:00
inline Impl::NSStringMatchers::Equals
Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); }
inline Impl::NSStringMatchers::Contains
Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); }
2012-03-14 16:04:50 -04:00
inline Impl::NSStringMatchers::StartsWith
StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); }
2012-03-14 16:04:50 -04:00
inline Impl::NSStringMatchers::EndsWith
EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); }
} // namespace Matchers
using namespace Matchers;
} // namespace Catch
2010-11-14 17:47:30 -05:00
2011-02-08 03:42:05 -05:00
///////////////////////////////////////////////////////////////////////////////
2010-11-14 17:47:30 -05:00
#define OC_TEST_CASE( name, desc )\
2012-03-17 14:20:06 -04:00
+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \
2010-11-14 17:47:30 -05:00
{\
2012-03-17 14:20:06 -04:00
return @ name; \
2010-11-14 17:47:30 -05:00
}\
2012-03-17 14:20:06 -04:00
+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \
2010-11-14 17:47:30 -05:00
{ \
2012-03-17 14:20:06 -04:00
return @ desc; \
2010-11-14 17:47:30 -05:00
} \
-(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test )
2012-05-10 16:46:46 -04:00
#endif // TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED