Catch/include/internal/catch_objc.hpp
Phil Nash 89d1e6c4f1 Tweaks to allow headers to be glued together.
Added Python script to generate single header.
Added new XCode project that runs self test against single header
2011-05-24 08:23:02 +01:00

187 lines
5.6 KiB
C++

/*
* catch_objc.hpp
* Catch
*
* 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
#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 "internal/catch_test_case_info.hpp"
///////////////////////////////////////////////////////////////////////////////
// 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 ITestCase
{
public:
///////////////////////////////////////////////////////////////////////
OcMethod
(
Class cls,
SEL sel
)
: m_cls( cls ),
m_sel( sel )
{
}
///////////////////////////////////////////////////////////////////////
virtual void invoke
()
const
{
id obj = class_createInstance( m_cls, 0 );
obj = [obj init];
if( [obj respondsToSelector: @selector(setUp) ] )
[obj performSelector: @selector(setUp)];
if( [obj respondsToSelector: m_sel] )
[obj performSelector: m_sel];
if( [obj respondsToSelector: @selector(tearDown) ] )
[obj performSelector: @selector(tearDown)];
[obj release];
}
///////////////////////////////////////////////////////////////////////
virtual ITestCase* clone
()
const
{
return new OcMethod( m_cls, m_sel );
}
///////////////////////////////////////////////////////////////////////
virtual bool operator ==
(
const ITestCase& other
)
const
{
const OcMethod* ocmOther = dynamic_cast<const OcMethod*> ( &other );
return ocmOther && ocmOther->m_sel == m_sel;
}
///////////////////////////////////////////////////////////////////////
virtual bool operator <
(
const ITestCase& other
)
const
{
const OcMethod* ocmOther = dynamic_cast<const OcMethod*> ( &other );
return ocmOther && ocmOther->m_sel < m_sel;
}
private:
Class m_cls;
SEL m_sel;
};
namespace Detail
{
///////////////////////////////////////////////////////////////////////
inline bool startsWith
(
const std::string& str,
const std::string& sub
)
{
return str.length() > sub.length() && str.substr( 0, sub.length() ) == sub;
}
///////////////////////////////////////////////////////////////////////
inline const char* getAnnotation
(
Class cls,
const std::string& annotationName,
const std::string& testCaseName
)
{
NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()];
SEL sel = NSSelectorFromString( selStr );
[selStr release];
if( [cls respondsToSelector: sel] )
return (const char*)[cls performSelector: sel];
return "";
}
}
///////////////////////////////////////////////////////////////////////////
inline size_t registerTestMethods
()
{
size_t noTestMethods = 0;
int noClasses = objc_getClassList( NULL, 0 );
std::vector<Class> classes( noClasses );
objc_getClassList( &classes[0], noClasses );
for( int c = 0; c < noClasses; c++ )
{
Class cls = classes[c];
{
u_int count;
Method* methods = class_copyMethodList( cls, &count );
for( int m = 0; m < count ; m++ )
{
SEL selector = method_getName(methods[m]);
std::string methodName = sel_getName(selector);
if( Detail::startsWith( methodName, "Catch_TestCase_" ) )
{
std::string testCaseName = methodName.substr( 15 );
const char* name = Detail::getAnnotation( cls, "Name", testCaseName );
const char* desc = Detail::getAnnotation( cls, "Description", testCaseName );
Hub::getTestCaseRegistry().registerTest( TestCaseInfo( new OcMethod( cls, selector ), name, desc, "", 0 ) );
noTestMethods++;
}
}
free(methods);
}
}
return noTestMethods;
}
}
///////////////////////////////////////////////////////////////////////////////
#define OC_TEST_CASE( name, desc )\
+(const char*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \
{\
return name; \
}\
+(const char*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \
{ \
return desc; \
} \
-(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test )
#endif // TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED