blob: b43f3352f8d08633508775ca8bed88ff81ca09db [file] [log] [blame]
/*
* 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
#include "catch_objc_arc.hpp"
#import <objc/runtime.h>
#include <string>
// 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 "catch_test_case_info.h"
#include "catch_string_manip.h"
#include "catch_tostring.h"
///////////////////////////////////////////////////////////////////////////////
// This protocol is really only here for (self) documenting purposes, since
// all its methods are optional.
@protocol OcFixture
@optional
-(void) setUp;
-(void) tearDown;
@end
namespace Catch {
class OcMethod : public ITestInvoker {
public:
OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {}
virtual void invoke() const {
id obj = [[m_cls alloc] init];
performOptionalSelector( obj, @selector(setUp) );
performOptionalSelector( obj, m_sel );
performOptionalSelector( obj, @selector(tearDown) );
arcSafeRelease( obj );
}
private:
virtual ~OcMethod() {}
Class m_cls;
SEL m_sel;
};
namespace Detail{
inline std::string getAnnotation( Class cls,
std::string const& annotationName,
std::string const& testCaseName ) {
NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()];
SEL sel = NSSelectorFromString( selStr );
arcSafeRelease( selStr );
id value = performOptionalSelector( cls, sel );
if( value )
return [(NSString*)value UTF8String];
return "";
}
}
inline std::size_t registerTestMethods() {
std::size_t noTestMethods = 0;
int noClasses = objc_getClassList( nullptr, 0 );
Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses);
objc_getClassList( classes, noClasses );
for( int c = 0; c < noClasses; c++ ) {
Class cls = classes[c];
{
u_int count;
Method* methods = class_copyMethodList( cls, &count );
for( u_int m = 0; m < count ; m++ ) {
SEL selector = method_getName(methods[m]);
std::string methodName = sel_getName(selector);
if( startsWith( methodName, "Catch_TestCase_" ) ) {
std::string testCaseName = methodName.substr( 15 );
std::string name = Detail::getAnnotation( cls, "Name", testCaseName );
std::string desc = Detail::getAnnotation( cls, "Description", testCaseName );
const char* className = class_getName( cls );
getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo("",0) ) );
noTestMethods++;
}
}
free(methods);
}
}
return noTestMethods;
}
#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
namespace Matchers {
namespace Impl {
namespace NSStringMatchers {
struct StringHolder : MatcherBase<NSString*>{
StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){}
StringHolder() {
arcSafeRelease( m_substr );
}
bool match( NSString* arg ) const override {
return false;
}
NSString* CATCH_ARC_STRONG m_substr;
};
struct Equals : StringHolder {
Equals( NSString* substr ) : StringHolder( substr ){}
bool match( NSString* str ) const override {
return (str != nil || m_substr == nil ) &&
[str isEqualToString:m_substr];
}
std::string describe() const override {
return "equals string: " + Catch::Detail::stringify( m_substr );
}
};
struct Contains : StringHolder {
Contains( NSString* substr ) : StringHolder( substr ){}
bool match( NSString* str ) const {
return (str != nil || m_substr == nil ) &&
[str rangeOfString:m_substr].location != NSNotFound;
}
std::string describe() const override {
return "contains string: " + Catch::Detail::stringify( m_substr );
}
};
struct StartsWith : StringHolder {
StartsWith( NSString* substr ) : StringHolder( substr ){}
bool match( NSString* str ) const override {
return (str != nil || m_substr == nil ) &&
[str rangeOfString:m_substr].location == 0;
}
std::string describe() const override {
return "starts with: " + Catch::Detail::stringify( m_substr );
}
};
struct EndsWith : StringHolder {
EndsWith( NSString* substr ) : StringHolder( substr ){}
bool match( NSString* str ) const override {
return (str != nil || m_substr == nil ) &&
[str rangeOfString:m_substr].location == [str length] - [m_substr length];
}
std::string describe() const override {
return "ends with: " + Catch::Detail::stringify( m_substr );
}
};
} // namespace NSStringMatchers
} // namespace Impl
inline Impl::NSStringMatchers::Equals
Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); }
inline Impl::NSStringMatchers::Contains
Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); }
inline Impl::NSStringMatchers::StartsWith
StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); }
inline Impl::NSStringMatchers::EndsWith
EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); }
} // namespace Matchers
using namespace Matchers;
#endif // CATCH_CONFIG_DISABLE_MATCHERS
} // namespace Catch
///////////////////////////////////////////////////////////////////////////////
#define OC_MAKE_UNIQUE_NAME( root, uniqueSuffix ) root##uniqueSuffix
#define OC_TEST_CASE2( name, desc, uniqueSuffix ) \
+(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Name_test_, uniqueSuffix ) \
{ \
return @ name; \
} \
+(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Description_test_, uniqueSuffix ) \
{ \
return @ desc; \
} \
-(void) OC_MAKE_UNIQUE_NAME( Catch_TestCase_test_, uniqueSuffix )
#define OC_TEST_CASE( name, desc ) OC_TEST_CASE2( name, desc, __LINE__ )
#endif // TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED