forked from catchorg/Catch2
		
	
		
			
				
	
	
		
			324 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			324 lines
		
	
	
		
			10 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 <Foundation/Foundation.h>
 | 
						|
#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"
 | 
						|
 | 
						|
#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
 | 
						|
    inline void arcSafeRelease( NSObject* obj )
 | 
						|
    {
 | 
						|
        [obj release];
 | 
						|
    }
 | 
						|
    inline id performOptionalSelector( id obj, SEL sel )
 | 
						|
    {
 | 
						|
        if( [obj respondsToSelector: sel] )
 | 
						|
            return [obj performSelector: sel];
 | 
						|
        return nil;
 | 
						|
    }
 | 
						|
    #define CATCH_UNSAFE_UNRETAINED
 | 
						|
#else
 | 
						|
    inline void arcSafeRelease( NSObject* ){}
 | 
						|
    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
 | 
						|
#endif
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////////////////////////
 | 
						|
// 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 = [[m_cls alloc] init];
 | 
						|
            
 | 
						|
            performOptionalSelector( obj, @selector(setUp)  );
 | 
						|
            performOptionalSelector( obj, m_sel );
 | 
						|
            performOptionalSelector( obj, @selector(tearDown)  );
 | 
						|
            
 | 
						|
            arcSafeRelease( obj );
 | 
						|
        }
 | 
						|
        
 | 
						|
        ///////////////////////////////////////////////////////////////////////
 | 
						|
        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 std::string 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 );
 | 
						|
            arcSafeRelease( selStr );
 | 
						|
            id value = performOptionalSelector( cls, sel );
 | 
						|
            if( value )
 | 
						|
                return [(NSString*)value UTF8String];
 | 
						|
            return "";
 | 
						|
        }        
 | 
						|
    }
 | 
						|
    
 | 
						|
    ///////////////////////////////////////////////////////////////////////////
 | 
						|
    inline size_t registerTestMethods
 | 
						|
    ()
 | 
						|
    {
 | 
						|
        size_t noTestMethods = 0;
 | 
						|
        int noClasses = objc_getClassList( NULL, 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( 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 );
 | 
						|
                        std::string name = Detail::getAnnotation( cls, "Name", testCaseName );
 | 
						|
                        std::string desc = Detail::getAnnotation( cls, "Description", testCaseName );
 | 
						|
                        
 | 
						|
                        Hub::getTestCaseRegistry().registerTest( TestCaseInfo( new OcMethod( cls, selector ), name.c_str(), desc.c_str(), "", 0 ) );
 | 
						|
                        noTestMethods++;
 | 
						|
                        
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                free(methods);              
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return noTestMethods;
 | 
						|
    }
 | 
						|
    
 | 
						|
    inline std::string toString( NSString* const& nsstring )
 | 
						|
    {
 | 
						|
        return std::string( "@\"" ) + [nsstring UTF8String] + "\"";
 | 
						|
    }
 | 
						|
    
 | 
						|
    namespace Matchers
 | 
						|
    {
 | 
						|
        namespace Impl
 | 
						|
        {
 | 
						|
        namespace NSStringMatchers
 | 
						|
        {
 | 
						|
            struct StringHolder
 | 
						|
            {
 | 
						|
                StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
 | 
						|
                StringHolder()
 | 
						|
                {
 | 
						|
                    arcSafeRelease( m_substr );
 | 
						|
                }
 | 
						|
                
 | 
						|
                NSString* m_substr;            
 | 
						|
            };
 | 
						|
            
 | 
						|
            struct Equals : StringHolder
 | 
						|
            {
 | 
						|
                Equals( NSString* substr ) : StringHolder( substr ){}
 | 
						|
                
 | 
						|
                bool operator()( NSString* str ) const
 | 
						|
                {
 | 
						|
                    return [str isEqualToString:m_substr];
 | 
						|
                }
 | 
						|
                
 | 
						|
                friend std::ostream& operator<<( std::ostream& os, const Equals& matcher )
 | 
						|
                {
 | 
						|
                    os << "equals string: " << Catch::toString( matcher.m_substr );
 | 
						|
                    return os;
 | 
						|
                }
 | 
						|
            };
 | 
						|
            
 | 
						|
            struct Contains : StringHolder
 | 
						|
            {
 | 
						|
                Contains( NSString* substr ) : StringHolder( substr ){}
 | 
						|
                
 | 
						|
                bool operator()( NSString* str ) const
 | 
						|
                {
 | 
						|
                    return [str rangeOfString:m_substr].location != NSNotFound;
 | 
						|
                }
 | 
						|
                
 | 
						|
                friend std::ostream& operator<<( std::ostream& os, const Contains& matcher )
 | 
						|
                {
 | 
						|
                    os << "contains: " << Catch::toString( matcher.m_substr );
 | 
						|
                    return os;
 | 
						|
                }
 | 
						|
            };
 | 
						|
 | 
						|
            struct StartsWith : StringHolder
 | 
						|
            {
 | 
						|
                StartsWith( NSString* substr ) : StringHolder( substr ){}
 | 
						|
                
 | 
						|
                bool operator()( NSString* str ) const
 | 
						|
                {
 | 
						|
                    return [str rangeOfString:m_substr].location == 0;
 | 
						|
                }
 | 
						|
                
 | 
						|
                friend std::ostream& operator<<( std::ostream& os, const StartsWith& matcher )
 | 
						|
                {
 | 
						|
                    os << "starts with: " << Catch::toString( matcher.m_substr );
 | 
						|
                    return os;
 | 
						|
                }
 | 
						|
            };
 | 
						|
            struct EndsWith : StringHolder
 | 
						|
            {
 | 
						|
                EndsWith( NSString* substr ) : StringHolder( substr ){}
 | 
						|
                
 | 
						|
                bool operator()( NSString* str ) const
 | 
						|
                {
 | 
						|
                    return [str rangeOfString:m_substr].location == [str length] - [m_substr length];
 | 
						|
                }
 | 
						|
                
 | 
						|
                friend std::ostream& operator<<( std::ostream& os, const EndsWith& matcher )
 | 
						|
                {
 | 
						|
                    os << "ends with: " << Catch::toString( matcher.m_substr );
 | 
						|
                    return os;
 | 
						|
                }
 | 
						|
            };
 | 
						|
            
 | 
						|
        } // 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;
 | 
						|
    
 | 
						|
} // namespace Catch
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////////////////////////
 | 
						|
#define OC_TEST_CASE( name, desc )\
 | 
						|
+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \
 | 
						|
{\
 | 
						|
return @ name; \
 | 
						|
}\
 | 
						|
+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \
 | 
						|
{ \
 | 
						|
return @ desc; \
 | 
						|
} \
 | 
						|
-(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test )
 | 
						|
 | 
						|
#endif // TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED
 |