Update GTM files to latest from
http://google-toolbox-for-mac.googlecode.com/svn/trunk/ Patch by: jakerr@google.com Review: https://breakpad.appspot.com/452002/ git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1045 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
parent
0e91d185ca
commit
c83cd11615
5 changed files with 1257 additions and 601 deletions
|
@ -21,6 +21,14 @@
|
|||
#include <AvailabilityMacros.h>
|
||||
#include <TargetConditionals.h>
|
||||
|
||||
#ifdef __OBJC__
|
||||
#include <Foundation/NSObjCRuntime.h>
|
||||
#endif // __OBJC__
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#include <Availability.h>
|
||||
#endif // TARGET_OS_IPHONE
|
||||
|
||||
// Not all MAC_OS_X_VERSION_10_X macros defined in past SDKs
|
||||
#ifndef MAC_OS_X_VERSION_10_5
|
||||
#define MAC_OS_X_VERSION_10_5 1050
|
||||
|
@ -28,6 +36,29 @@
|
|||
#ifndef MAC_OS_X_VERSION_10_6
|
||||
#define MAC_OS_X_VERSION_10_6 1060
|
||||
#endif
|
||||
#ifndef MAC_OS_X_VERSION_10_7
|
||||
#define MAC_OS_X_VERSION_10_7 1070
|
||||
#endif
|
||||
|
||||
// Not all __IPHONE_X macros defined in past SDKs
|
||||
#ifndef __IPHONE_3_0
|
||||
#define __IPHONE_3_0 30000
|
||||
#endif
|
||||
#ifndef __IPHONE_3_1
|
||||
#define __IPHONE_3_1 30100
|
||||
#endif
|
||||
#ifndef __IPHONE_3_2
|
||||
#define __IPHONE_3_2 30200
|
||||
#endif
|
||||
#ifndef __IPHONE_4_0
|
||||
#define __IPHONE_4_0 40000
|
||||
#endif
|
||||
#ifndef __IPHONE_4_3
|
||||
#define __IPHONE_4_3 40300
|
||||
#endif
|
||||
#ifndef __IPHONE_5_0
|
||||
#define __IPHONE_5_0 50000
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// CPP symbols that can be overridden in a prefix to control how the toolbox
|
||||
|
@ -47,7 +78,7 @@
|
|||
// a few different actual definitions, so we're based off of the foundation
|
||||
// one.
|
||||
#if !defined(GTM_INLINE)
|
||||
#if defined (__GNUC__) && (__GNUC__ == 4)
|
||||
#if (defined (__GNUC__) && (__GNUC__ == 4)) || defined (__clang__)
|
||||
#define GTM_INLINE static __inline__ __attribute__((always_inline))
|
||||
#else
|
||||
#define GTM_INLINE static __inline__
|
||||
|
@ -59,8 +90,12 @@
|
|||
#if !defined (GTM_EXTERN)
|
||||
#if defined __cplusplus
|
||||
#define GTM_EXTERN extern "C"
|
||||
#define GTM_EXTERN_C_BEGIN extern "C" {
|
||||
#define GTM_EXTERN_C_END }
|
||||
#else
|
||||
#define GTM_EXTERN extern
|
||||
#define GTM_EXTERN_C_BEGIN
|
||||
#define GTM_EXTERN_C_END
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
@ -70,6 +105,12 @@
|
|||
#define GTM_EXPORT __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
// Give ourselves a consistent way of declaring something as unused. This
|
||||
// doesn't use __unused because that is only supported in gcc 4.2 and greater.
|
||||
#if !defined (GTM_UNUSED)
|
||||
#define GTM_UNUSED(x) ((void)(x))
|
||||
#endif
|
||||
|
||||
// _GTMDevLog & _GTMDevAssert
|
||||
//
|
||||
// _GTMDevLog & _GTMDevAssert are meant to be a very lightweight shell for
|
||||
|
@ -100,11 +141,6 @@
|
|||
|
||||
#endif // _GTMDevLog
|
||||
|
||||
// Declared here so that it can easily be used for logging tracking if
|
||||
// necessary. See GTMUnitTestDevLog.h for details.
|
||||
@class NSString;
|
||||
GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
|
||||
|
||||
#ifndef _GTMDevAssert
|
||||
// we directly invoke the NSAssert handler so we can pass on the varargs
|
||||
// (NSAssert doesn't have a macro we can use that takes varargs)
|
||||
|
@ -145,28 +181,6 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
|
|||
typedef char _GTMCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ]
|
||||
#endif // _GTMCompileAssert
|
||||
|
||||
// Macro to allow fast enumeration when building for 10.5 or later, and
|
||||
// reliance on NSEnumerator for 10.4. Remember, NSDictionary w/ FastEnumeration
|
||||
// does keys, so pick the right thing, nothing is done on the FastEnumeration
|
||||
// side to be sure you're getting what you wanted.
|
||||
#ifndef GTM_FOREACH_OBJECT
|
||||
#if TARGET_OS_IPHONE || (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
|
||||
#define GTM_FOREACH_OBJECT(element, collection) \
|
||||
for (element in collection)
|
||||
#define GTM_FOREACH_KEY(element, collection) \
|
||||
for (element in collection)
|
||||
#else
|
||||
#define GTM_FOREACH_OBJECT(element, collection) \
|
||||
for (NSEnumerator * _ ## element ## _enum = [collection objectEnumerator]; \
|
||||
(element = [_ ## element ## _enum nextObject]) != nil; )
|
||||
#define GTM_FOREACH_KEY(element, collection) \
|
||||
for (NSEnumerator * _ ## element ## _enum = [collection keyEnumerator]; \
|
||||
(element = [_ ## element ## _enum nextObject]) != nil; )
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// ============================================================================
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// CPP symbols defined based on the project settings so the GTM code has
|
||||
// simple things to test against w/o scattering the knowledge of project
|
||||
|
@ -183,11 +197,26 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
|
|||
#else
|
||||
#define GTM_IPHONE_DEVICE 1
|
||||
#endif // TARGET_IPHONE_SIMULATOR
|
||||
// By default, GTM has provided it's own unittesting support, define this
|
||||
// to use the support provided by Xcode, especially for the Xcode4 support
|
||||
// for unittesting.
|
||||
#ifndef GTM_IPHONE_USE_SENTEST
|
||||
#define GTM_IPHONE_USE_SENTEST 0
|
||||
#endif
|
||||
#else
|
||||
// For MacOS specific stuff
|
||||
#define GTM_MACOS_SDK 1
|
||||
#endif
|
||||
|
||||
// Some of our own availability macros
|
||||
#if GTM_MACOS_SDK
|
||||
#define GTM_AVAILABLE_ONLY_ON_IPHONE UNAVAILABLE_ATTRIBUTE
|
||||
#define GTM_AVAILABLE_ONLY_ON_MACOS
|
||||
#else
|
||||
#define GTM_AVAILABLE_ONLY_ON_IPHONE
|
||||
#define GTM_AVAILABLE_ONLY_ON_MACOS UNAVAILABLE_ATTRIBUTE
|
||||
#endif
|
||||
|
||||
// Provide a symbol to include/exclude extra code for GC support. (This mainly
|
||||
// just controls the inclusion of finalize methods).
|
||||
#ifndef GTM_SUPPORT_GC
|
||||
|
@ -197,7 +226,7 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
|
|||
#else
|
||||
// We can't find a symbol to tell if GC is supported/required, so best we
|
||||
// do on Mac targets is include it if we're on 10.5 or later.
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
|
||||
#define GTM_SUPPORT_GC 0
|
||||
#else
|
||||
#define GTM_SUPPORT_GC 1
|
||||
|
@ -207,7 +236,7 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
|
|||
|
||||
// To simplify support for 64bit (and Leopard in general), we provide the type
|
||||
// defines for non Leopard SDKs
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
|
||||
#if !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
|
||||
// NSInteger/NSUInteger and Max/Mins
|
||||
#ifndef NSINTEGER_DEFINED
|
||||
#if __LP64__ || NS_BUILD_32_LIKE_64
|
||||
|
@ -238,4 +267,178 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
|
|||
#endif /* !defined(__LP64__) || !__LP64__ */
|
||||
#define CGFLOAT_DEFINED 1
|
||||
#endif // CGFLOAT_DEFINED
|
||||
#endif // MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
|
||||
#endif // MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
|
||||
|
||||
// Some support for advanced clang static analysis functionality
|
||||
// See http://clang-analyzer.llvm.org/annotations.html
|
||||
#ifndef __has_feature // Optional.
|
||||
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
|
||||
#endif
|
||||
|
||||
#ifndef NS_RETURNS_RETAINED
|
||||
#if __has_feature(attribute_ns_returns_retained)
|
||||
#define NS_RETURNS_RETAINED __attribute__((ns_returns_retained))
|
||||
#else
|
||||
#define NS_RETURNS_RETAINED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef NS_RETURNS_NOT_RETAINED
|
||||
#if __has_feature(attribute_ns_returns_not_retained)
|
||||
#define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained))
|
||||
#else
|
||||
#define NS_RETURNS_NOT_RETAINED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef CF_RETURNS_RETAINED
|
||||
#if __has_feature(attribute_cf_returns_retained)
|
||||
#define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
|
||||
#else
|
||||
#define CF_RETURNS_RETAINED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef CF_RETURNS_NOT_RETAINED
|
||||
#if __has_feature(attribute_cf_returns_not_retained)
|
||||
#define CF_RETURNS_NOT_RETAINED __attribute__((cf_returns_not_retained))
|
||||
#else
|
||||
#define CF_RETURNS_NOT_RETAINED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef NS_CONSUMED
|
||||
#if __has_feature(attribute_ns_consumed)
|
||||
#define NS_CONSUMED __attribute__((ns_consumed))
|
||||
#else
|
||||
#define NS_CONSUMED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef CF_CONSUMED
|
||||
#if __has_feature(attribute_cf_consumed)
|
||||
#define CF_CONSUMED __attribute__((cf_consumed))
|
||||
#else
|
||||
#define CF_CONSUMED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef NS_CONSUMES_SELF
|
||||
#if __has_feature(attribute_ns_consumes_self)
|
||||
#define NS_CONSUMES_SELF __attribute__((ns_consumes_self))
|
||||
#else
|
||||
#define NS_CONSUMES_SELF
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Defined on 10.6 and above.
|
||||
#ifndef NS_FORMAT_ARGUMENT
|
||||
#define NS_FORMAT_ARGUMENT(A)
|
||||
#endif
|
||||
|
||||
// Defined on 10.6 and above.
|
||||
#ifndef NS_FORMAT_FUNCTION
|
||||
#define NS_FORMAT_FUNCTION(F,A)
|
||||
#endif
|
||||
|
||||
// Defined on 10.6 and above.
|
||||
#ifndef CF_FORMAT_ARGUMENT
|
||||
#define CF_FORMAT_ARGUMENT(A)
|
||||
#endif
|
||||
|
||||
// Defined on 10.6 and above.
|
||||
#ifndef CF_FORMAT_FUNCTION
|
||||
#define CF_FORMAT_FUNCTION(F,A)
|
||||
#endif
|
||||
|
||||
#ifndef GTM_NONNULL
|
||||
#define GTM_NONNULL(x) __attribute__((nonnull(x)))
|
||||
#endif
|
||||
|
||||
// Invalidates the initializer from which it's called.
|
||||
#ifndef GTMInvalidateInitializer
|
||||
#if __has_feature(objc_arc)
|
||||
#define GTMInvalidateInitializer() \
|
||||
do { \
|
||||
[self class]; /* Avoid warning of dead store to |self|. */ \
|
||||
_GTMDevAssert(NO, @"Invalid initializer."); \
|
||||
return nil; \
|
||||
} while (0)
|
||||
#else
|
||||
#define GTMInvalidateInitializer() \
|
||||
do { \
|
||||
[self release]; \
|
||||
_GTMDevAssert(NO, @"Invalid initializer."); \
|
||||
return nil; \
|
||||
} while (0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __OBJC__
|
||||
|
||||
// Declared here so that it can easily be used for logging tracking if
|
||||
// necessary. See GTMUnitTestDevLog.h for details.
|
||||
@class NSString;
|
||||
GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...) NS_FORMAT_FUNCTION(1, 2);
|
||||
|
||||
// Macro to allow you to create NSStrings out of other macros.
|
||||
// #define FOO foo
|
||||
// NSString *fooString = GTM_NSSTRINGIFY(FOO);
|
||||
#if !defined (GTM_NSSTRINGIFY)
|
||||
#define GTM_NSSTRINGIFY_INNER(x) @#x
|
||||
#define GTM_NSSTRINGIFY(x) GTM_NSSTRINGIFY_INNER(x)
|
||||
#endif
|
||||
|
||||
// Macro to allow fast enumeration when building for 10.5 or later, and
|
||||
// reliance on NSEnumerator for 10.4. Remember, NSDictionary w/ FastEnumeration
|
||||
// does keys, so pick the right thing, nothing is done on the FastEnumeration
|
||||
// side to be sure you're getting what you wanted.
|
||||
#ifndef GTM_FOREACH_OBJECT
|
||||
#if TARGET_OS_IPHONE || !(MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5)
|
||||
#define GTM_FOREACH_ENUMEREE(element, enumeration) \
|
||||
for (element in enumeration)
|
||||
#define GTM_FOREACH_OBJECT(element, collection) \
|
||||
for (element in collection)
|
||||
#define GTM_FOREACH_KEY(element, collection) \
|
||||
for (element in collection)
|
||||
#else
|
||||
#define GTM_FOREACH_ENUMEREE(element, enumeration) \
|
||||
for (NSEnumerator *_ ## element ## _enum = enumeration; \
|
||||
(element = [_ ## element ## _enum nextObject]) != nil; )
|
||||
#define GTM_FOREACH_OBJECT(element, collection) \
|
||||
GTM_FOREACH_ENUMEREE(element, [collection objectEnumerator])
|
||||
#define GTM_FOREACH_KEY(element, collection) \
|
||||
GTM_FOREACH_ENUMEREE(element, [collection keyEnumerator])
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// ============================================================================
|
||||
|
||||
// To simplify support for both Leopard and Snow Leopard we declare
|
||||
// the Snow Leopard protocols that we need here.
|
||||
#if !defined(GTM_10_6_PROTOCOLS_DEFINED) && !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
|
||||
#define GTM_10_6_PROTOCOLS_DEFINED 1
|
||||
@protocol NSConnectionDelegate
|
||||
@end
|
||||
@protocol NSAnimationDelegate
|
||||
@end
|
||||
@protocol NSImageDelegate
|
||||
@end
|
||||
@protocol NSTabViewDelegate
|
||||
@end
|
||||
#endif // !defined(GTM_10_6_PROTOCOLS_DEFINED) && !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
|
||||
|
||||
// GTM_SEL_STRING is for specifying selector (usually property) names to KVC
|
||||
// or KVO methods.
|
||||
// In debug it will generate warnings for undeclared selectors if
|
||||
// -Wunknown-selector is turned on.
|
||||
// In release it will have no runtime overhead.
|
||||
#ifndef GTM_SEL_STRING
|
||||
#ifdef DEBUG
|
||||
#define GTM_SEL_STRING(selName) NSStringFromSelector(@selector(selName))
|
||||
#else
|
||||
#define GTM_SEL_STRING(selName) @#selName
|
||||
#endif // DEBUG
|
||||
#endif // GTM_SEL_STRING
|
||||
|
||||
#endif // __OBJC__
|
||||
|
|
|
@ -55,12 +55,6 @@
|
|||
// Predeclaration of used protocols that are declared later in this file.
|
||||
@protocol GTMLogWriter, GTMLogFormatter, GTMLogFilter;
|
||||
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
|
||||
#define CHECK_FORMAT_NSSTRING(a, b) __attribute__((format(__NSString__, a, b)))
|
||||
#else
|
||||
#define CHECK_FORMAT_NSSTRING(a, b)
|
||||
#endif
|
||||
|
||||
// GTMLogger
|
||||
//
|
||||
// GTMLogger is the primary user-facing class for an object-oriented logging
|
||||
|
@ -244,6 +238,10 @@
|
|||
// Same as +standardLogger, but logs to stderr.
|
||||
+ (id)standardLoggerWithStderr;
|
||||
|
||||
// Same as +standardLogger but levels >= kGTMLoggerLevelError are routed to
|
||||
// stderr, everything else goes to stdout.
|
||||
+ (id)standardLoggerWithStdoutAndStderr;
|
||||
|
||||
// Returns a new standard GTMLogger instance with a log writer that will
|
||||
// write to the file at |path|, and will use the GTMLogStandardFormatter and
|
||||
// GTMLogLevelFilter classes. If |path| does not exist, it will be created.
|
||||
|
@ -274,13 +272,13 @@
|
|||
//
|
||||
|
||||
// Logs a message at the debug level (kGTMLoggerLevelDebug).
|
||||
- (void)logDebug:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2);
|
||||
- (void)logDebug:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
|
||||
// Logs a message at the info level (kGTMLoggerLevelInfo).
|
||||
- (void)logInfo:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2);
|
||||
- (void)logInfo:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
|
||||
// Logs a message at the error level (kGTMLoggerLevelError).
|
||||
- (void)logError:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2);
|
||||
- (void)logError:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
|
||||
// Logs a message at the assert level (kGTMLoggerLevelAssert).
|
||||
- (void)logAssert:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2);
|
||||
- (void)logAssert:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
|
||||
|
||||
|
||||
//
|
||||
|
@ -310,16 +308,19 @@
|
|||
// enable the logging of function names.
|
||||
@interface GTMLogger (GTMLoggerMacroHelpers)
|
||||
- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ...
|
||||
CHECK_FORMAT_NSSTRING(2, 3);
|
||||
NS_FORMAT_FUNCTION(2, 3);
|
||||
- (void)logFuncInfo:(const char *)func msg:(NSString *)fmt, ...
|
||||
CHECK_FORMAT_NSSTRING(2, 3);
|
||||
NS_FORMAT_FUNCTION(2, 3);
|
||||
- (void)logFuncError:(const char *)func msg:(NSString *)fmt, ...
|
||||
CHECK_FORMAT_NSSTRING(2, 3);
|
||||
NS_FORMAT_FUNCTION(2, 3);
|
||||
- (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ...
|
||||
CHECK_FORMAT_NSSTRING(2, 3);
|
||||
NS_FORMAT_FUNCTION(2, 3);
|
||||
@end // GTMLoggerMacroHelpers
|
||||
|
||||
|
||||
// The convenience macros are only defined if they haven't already been defined.
|
||||
#ifndef GTMLoggerInfo
|
||||
|
||||
// Convenience macros that log to the shared GTMLogger instance. These macros
|
||||
// are how users should typically log to GTMLogger. Notice that GTMLoggerDebug()
|
||||
// calls will be compiled out of non-Debug builds.
|
||||
|
@ -339,6 +340,8 @@
|
|||
#define GTMLoggerDebug(...) do {} while(0)
|
||||
#endif
|
||||
|
||||
#endif // !defined(GTMLoggerInfo)
|
||||
|
||||
// Log levels.
|
||||
typedef enum {
|
||||
kGTMLoggerLevelUnknown,
|
||||
|
@ -407,7 +410,7 @@ typedef enum {
|
|||
- (NSString *)stringForFunc:(NSString *)func
|
||||
withFormat:(NSString *)fmt
|
||||
valist:(va_list)args
|
||||
level:(GTMLoggerLevel)level;
|
||||
level:(GTMLoggerLevel)level NS_FORMAT_FUNCTION(2, 0);
|
||||
@end // GTMLogFormatter
|
||||
|
||||
|
||||
|
@ -415,6 +418,10 @@ typedef enum {
|
|||
// printf) would. It does not do anything fancy, nor does it add any data of its
|
||||
// own.
|
||||
@interface GTMLogBasicFormatter : NSObject <GTMLogFormatter>
|
||||
|
||||
// Helper method for prettying C99 __func__ and GCC __PRETTY_FUNCTION__
|
||||
- (NSString *)prettyNameForFunc:(NSString *)func;
|
||||
|
||||
@end // GTMLogBasicFormatter
|
||||
|
||||
|
||||
|
@ -450,9 +457,48 @@ typedef enum {
|
|||
@interface GTMLogLevelFilter : NSObject <GTMLogFilter>
|
||||
@end // GTMLogLevelFilter
|
||||
|
||||
|
||||
// A simple log filter that does NOT filter anything out;
|
||||
// -filterAllowsMessage:level will always return YES. This can be a convenient
|
||||
// way to enable debug-level logging in release builds (if you so desire).
|
||||
@interface GTMLogNoFilter : NSObject <GTMLogFilter>
|
||||
@end // GTMLogNoFilter
|
||||
|
||||
|
||||
// Base class for custom level filters. Not for direct use, use the minimum
|
||||
// or maximum level subclasses below.
|
||||
@interface GTMLogAllowedLevelFilter : NSObject <GTMLogFilter> {
|
||||
@private
|
||||
NSIndexSet *allowedLevels_;
|
||||
}
|
||||
@end
|
||||
|
||||
// A log filter that allows you to set a minimum log level. Messages below this
|
||||
// level will be filtered.
|
||||
@interface GTMLogMininumLevelFilter : GTMLogAllowedLevelFilter
|
||||
|
||||
// Designated initializer, logs at levels < |level| will be filtered.
|
||||
- (id)initWithMinimumLevel:(GTMLoggerLevel)level;
|
||||
|
||||
@end
|
||||
|
||||
// A log filter that allows you to set a maximum log level. Messages whose level
|
||||
// exceeds this level will be filtered. This is really only useful if you have
|
||||
// a composite GTMLogger that is sending the other messages elsewhere.
|
||||
@interface GTMLogMaximumLevelFilter : GTMLogAllowedLevelFilter
|
||||
|
||||
// Designated initializer, logs at levels > |level| will be filtered.
|
||||
- (id)initWithMaximumLevel:(GTMLoggerLevel)level;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
// For subclasses only
|
||||
@interface GTMLogger (PrivateMethods)
|
||||
|
||||
- (void)logInternalFunc:(const char *)func
|
||||
format:(NSString *)fmt
|
||||
valist:(va_list)args
|
||||
level:(GTMLoggerLevel)level NS_FORMAT_FUNCTION(2, 0);
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -24,23 +24,15 @@
|
|||
#import <pthread.h>
|
||||
|
||||
|
||||
// Define a trivial assertion macro to avoid dependencies
|
||||
#ifdef DEBUG
|
||||
#define GTMLOGGER_ASSERT(expr) assert(expr)
|
||||
#else
|
||||
#define GTMLOGGER_ASSERT(expr)
|
||||
#endif
|
||||
|
||||
|
||||
@interface GTMLogger (PrivateMethods)
|
||||
|
||||
- (void)logInternalFunc:(const char *)func
|
||||
format:(NSString *)fmt
|
||||
valist:(va_list)args
|
||||
level:(GTMLoggerLevel)level;
|
||||
|
||||
@end
|
||||
|
||||
#if !defined(__clang__) && (__GNUC__*10+__GNUC_MINOR__ >= 42)
|
||||
// Some versions of GCC (4.2 and below AFAIK) aren't great about supporting
|
||||
// -Wmissing-format-attribute
|
||||
// when the function is anything more complex than foo(NSString *fmt, ...).
|
||||
// You see the error inside the function when you turn ... into va_args and
|
||||
// attempt to call another function (like vsprintf for example).
|
||||
// So we just shut off the warning for this file. We reenable it at the end.
|
||||
#pragma GCC diagnostic ignored "-Wmissing-format-attribute"
|
||||
#endif // !__clang__
|
||||
|
||||
// Reference to the shared GTMLogger instance. This is not a singleton, it's
|
||||
// just an easy reference to one shared instance.
|
||||
|
@ -56,7 +48,6 @@ static GTMLogger *gSharedLogger = nil;
|
|||
if (gSharedLogger == nil) {
|
||||
gSharedLogger = [[self standardLogger] retain];
|
||||
}
|
||||
GTMLOGGER_ASSERT(gSharedLogger != nil);
|
||||
}
|
||||
return [[gSharedLogger retain] autorelease];
|
||||
}
|
||||
|
@ -69,25 +60,86 @@ static GTMLogger *gSharedLogger = nil;
|
|||
}
|
||||
|
||||
+ (id)standardLogger {
|
||||
// Don't trust NSFileHandle not to throw
|
||||
@try {
|
||||
id<GTMLogWriter> writer = [NSFileHandle fileHandleWithStandardOutput];
|
||||
id<GTMLogFormatter> fr = [[[GTMLogStandardFormatter alloc] init] autorelease];
|
||||
id<GTMLogFormatter> fr = [[[GTMLogStandardFormatter alloc] init]
|
||||
autorelease];
|
||||
id<GTMLogFilter> filter = [[[GTMLogLevelFilter alloc] init] autorelease];
|
||||
return [self loggerWithWriter:writer formatter:fr filter:filter];
|
||||
return [[[self alloc] initWithWriter:writer
|
||||
formatter:fr
|
||||
filter:filter] autorelease];
|
||||
}
|
||||
@catch (id e) {
|
||||
// Ignored
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (id)standardLoggerWithStderr {
|
||||
// Don't trust NSFileHandle not to throw
|
||||
@try {
|
||||
id me = [self standardLogger];
|
||||
[me setWriter:[NSFileHandle fileHandleWithStandardError]];
|
||||
return me;
|
||||
}
|
||||
@catch (id e) {
|
||||
// Ignored
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (id)standardLoggerWithStdoutAndStderr {
|
||||
// We're going to take advantage of the GTMLogger to GTMLogWriter adaptor
|
||||
// and create a composite logger that an outer "standard" logger can use
|
||||
// as a writer. Our inner loggers should apply no formatting since the main
|
||||
// logger does that and we want the caller to be able to change formatters
|
||||
// or add writers without knowing the inner structure of our composite.
|
||||
|
||||
// Don't trust NSFileHandle not to throw
|
||||
@try {
|
||||
GTMLogBasicFormatter *formatter = [[[GTMLogBasicFormatter alloc] init]
|
||||
autorelease];
|
||||
GTMLogger *stdoutLogger =
|
||||
[self loggerWithWriter:[NSFileHandle fileHandleWithStandardOutput]
|
||||
formatter:formatter
|
||||
filter:[[[GTMLogMaximumLevelFilter alloc]
|
||||
initWithMaximumLevel:kGTMLoggerLevelInfo]
|
||||
autorelease]];
|
||||
GTMLogger *stderrLogger =
|
||||
[self loggerWithWriter:[NSFileHandle fileHandleWithStandardError]
|
||||
formatter:formatter
|
||||
filter:[[[GTMLogMininumLevelFilter alloc]
|
||||
initWithMinimumLevel:kGTMLoggerLevelError]
|
||||
autorelease]];
|
||||
GTMLogger *compositeWriter =
|
||||
[self loggerWithWriter:[NSArray arrayWithObjects:
|
||||
stdoutLogger, stderrLogger, nil]
|
||||
formatter:formatter
|
||||
filter:[[[GTMLogNoFilter alloc] init] autorelease]];
|
||||
GTMLogger *outerLogger = [self standardLogger];
|
||||
[outerLogger setWriter:compositeWriter];
|
||||
return outerLogger;
|
||||
}
|
||||
@catch (id e) {
|
||||
// Ignored
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (id)standardLoggerWithPath:(NSString *)path {
|
||||
@try {
|
||||
NSFileHandle *fh = [NSFileHandle fileHandleForLoggingAtPath:path mode:0644];
|
||||
if (fh == nil) return nil;
|
||||
id me = [self standardLogger];
|
||||
[me setWriter:fh];
|
||||
return me;
|
||||
}
|
||||
@catch (id e) {
|
||||
// Ignored
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (id)loggerWithWriter:(id<GTMLogWriter>)writer
|
||||
formatter:(id<GTMLogFormatter>)formatter
|
||||
|
@ -112,69 +164,85 @@ static GTMLogger *gSharedLogger = nil;
|
|||
[self setWriter:writer];
|
||||
[self setFormatter:formatter];
|
||||
[self setFilter:filter];
|
||||
GTMLOGGER_ASSERT(formatter_ != nil);
|
||||
GTMLOGGER_ASSERT(filter_ != nil);
|
||||
GTMLOGGER_ASSERT(writer_ != nil);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
GTMLOGGER_ASSERT(writer_ != nil);
|
||||
GTMLOGGER_ASSERT(formatter_ != nil);
|
||||
GTMLOGGER_ASSERT(filter_ != nil);
|
||||
[writer_ release];
|
||||
// Unlikely, but |writer_| may be an NSFileHandle, which can throw
|
||||
@try {
|
||||
[formatter_ release];
|
||||
[filter_ release];
|
||||
[writer_ release];
|
||||
}
|
||||
@catch (id e) {
|
||||
// Ignored
|
||||
}
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (id<GTMLogWriter>)writer {
|
||||
GTMLOGGER_ASSERT(writer_ != nil);
|
||||
return [[writer_ retain] autorelease];
|
||||
}
|
||||
|
||||
- (void)setWriter:(id<GTMLogWriter>)writer {
|
||||
@synchronized(self) {
|
||||
[writer_ autorelease];
|
||||
if (writer == nil)
|
||||
writer_ = nil;
|
||||
if (writer == nil) {
|
||||
// Try to use stdout, but don't trust NSFileHandle
|
||||
@try {
|
||||
writer_ = [[NSFileHandle fileHandleWithStandardOutput] retain];
|
||||
else
|
||||
}
|
||||
@catch (id e) {
|
||||
// Leave |writer_| nil
|
||||
}
|
||||
} else {
|
||||
writer_ = [writer retain];
|
||||
}
|
||||
GTMLOGGER_ASSERT(writer_ != nil);
|
||||
}
|
||||
}
|
||||
|
||||
- (id<GTMLogFormatter>)formatter {
|
||||
GTMLOGGER_ASSERT(formatter_ != nil);
|
||||
return [[formatter_ retain] autorelease];
|
||||
}
|
||||
|
||||
- (void)setFormatter:(id<GTMLogFormatter>)formatter {
|
||||
@synchronized(self) {
|
||||
[formatter_ autorelease];
|
||||
if (formatter == nil)
|
||||
formatter_ = nil;
|
||||
if (formatter == nil) {
|
||||
@try {
|
||||
formatter_ = [[GTMLogBasicFormatter alloc] init];
|
||||
else
|
||||
}
|
||||
@catch (id e) {
|
||||
// Leave |formatter_| nil
|
||||
}
|
||||
} else {
|
||||
formatter_ = [formatter retain];
|
||||
}
|
||||
GTMLOGGER_ASSERT(formatter_ != nil);
|
||||
}
|
||||
}
|
||||
|
||||
- (id<GTMLogFilter>)filter {
|
||||
GTMLOGGER_ASSERT(filter_ != nil);
|
||||
return [[filter_ retain] autorelease];
|
||||
}
|
||||
|
||||
- (void)setFilter:(id<GTMLogFilter>)filter {
|
||||
@synchronized(self) {
|
||||
[filter_ autorelease];
|
||||
if (filter == nil)
|
||||
filter_ = nil;
|
||||
if (filter == nil) {
|
||||
@try {
|
||||
filter_ = [[GTMLogNoFilter alloc] init];
|
||||
else
|
||||
}
|
||||
@catch (id e) {
|
||||
// Leave |filter_| nil
|
||||
}
|
||||
} else {
|
||||
filter_ = [filter retain];
|
||||
}
|
||||
GTMLOGGER_ASSERT(filter_ != nil);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)logDebug:(NSString *)fmt, ... {
|
||||
|
@ -207,7 +275,6 @@ static GTMLogger *gSharedLogger = nil;
|
|||
|
||||
@end // GTMLogger
|
||||
|
||||
|
||||
@implementation GTMLogger (GTMLoggerMacroHelpers)
|
||||
|
||||
- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ... {
|
||||
|
@ -240,17 +307,15 @@ static GTMLogger *gSharedLogger = nil;
|
|||
|
||||
@end // GTMLoggerMacroHelpers
|
||||
|
||||
|
||||
@implementation GTMLogger (PrivateMethods)
|
||||
|
||||
- (void)logInternalFunc:(const char *)func
|
||||
format:(NSString *)fmt
|
||||
valist:(va_list)args
|
||||
level:(GTMLoggerLevel)level {
|
||||
GTMLOGGER_ASSERT(formatter_ != nil);
|
||||
GTMLOGGER_ASSERT(filter_ != nil);
|
||||
GTMLOGGER_ASSERT(writer_ != nil);
|
||||
|
||||
// Primary point where logging happens, logging should never throw, catch
|
||||
// everything.
|
||||
@try {
|
||||
NSString *fname = func ? [NSString stringWithUTF8String:func] : nil;
|
||||
NSString *msg = [formatter_ stringForFunc:fname
|
||||
withFormat:fmt
|
||||
|
@ -259,6 +324,10 @@ static GTMLogger *gSharedLogger = nil;
|
|||
if (msg && [filter_ filterAllowsMessage:msg level:level])
|
||||
[writer_ logMessage:msg level:level];
|
||||
}
|
||||
@catch (id e) {
|
||||
// Ignored
|
||||
}
|
||||
}
|
||||
|
||||
@end // PrivateMethods
|
||||
|
||||
|
@ -278,9 +347,17 @@ static GTMLogger *gSharedLogger = nil;
|
|||
|
||||
- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
|
||||
@synchronized(self) {
|
||||
// Closed pipes should not generate exceptions in our caller. Catch here
|
||||
// as well [GTMLogger logInternalFunc:...] so that an exception in this
|
||||
// writer does not prevent other writers from having a chance.
|
||||
@try {
|
||||
NSString *line = [NSString stringWithFormat:@"%@\n", msg];
|
||||
[self writeData:[line dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
}
|
||||
@catch (id e) {
|
||||
// Ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end // GTMFileHandleLogWriter
|
||||
|
@ -328,19 +405,32 @@ static GTMLogger *gSharedLogger = nil;
|
|||
|
||||
@implementation GTMLogBasicFormatter
|
||||
|
||||
- (NSString *)prettyNameForFunc:(NSString *)func {
|
||||
NSString *name = [func stringByTrimmingCharactersInSet:
|
||||
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
NSString *function = @"(unknown)";
|
||||
if ([name length]) {
|
||||
if (// Objective C __func__ and __PRETTY_FUNCTION__
|
||||
[name hasPrefix:@"-["] || [name hasPrefix:@"+["] ||
|
||||
// C++ __PRETTY_FUNCTION__ and other preadorned formats
|
||||
[name hasSuffix:@")"]) {
|
||||
function = name;
|
||||
} else {
|
||||
// Assume C99 __func__
|
||||
function = [NSString stringWithFormat:@"%@()", name];
|
||||
}
|
||||
}
|
||||
return function;
|
||||
}
|
||||
|
||||
- (NSString *)stringForFunc:(NSString *)func
|
||||
withFormat:(NSString *)fmt
|
||||
valist:(va_list)args
|
||||
level:(GTMLoggerLevel)level {
|
||||
// Performance note: since we always have to create a new NSString from the
|
||||
// returned CFStringRef, we may want to do a quick check here to see if |fmt|
|
||||
// Performance note: We may want to do a quick check here to see if |fmt|
|
||||
// contains a '%', and if not, simply return 'fmt'.
|
||||
CFStringRef cfmsg = NULL;
|
||||
cfmsg = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault,
|
||||
NULL, // format options
|
||||
(CFStringRef)fmt,
|
||||
args);
|
||||
return GTMCFAutorelease(cfmsg);
|
||||
if (!(fmt && args)) return nil;
|
||||
return [[[NSString alloc] initWithFormat:fmt arguments:args] autorelease];
|
||||
}
|
||||
|
||||
@end // GTMLogBasicFormatter
|
||||
|
@ -355,6 +445,10 @@ static GTMLogger *gSharedLogger = nil;
|
|||
[dateFormatter_ setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSS"];
|
||||
pname_ = [[[NSProcessInfo processInfo] processName] copy];
|
||||
pid_ = [[NSProcessInfo processInfo] processIdentifier];
|
||||
if (!(dateFormatter_ && pname_)) {
|
||||
[self release];
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
@ -369,14 +463,14 @@ static GTMLogger *gSharedLogger = nil;
|
|||
withFormat:(NSString *)fmt
|
||||
valist:(va_list)args
|
||||
level:(GTMLoggerLevel)level {
|
||||
GTMLOGGER_ASSERT(dateFormatter_ != nil);
|
||||
NSString *tstamp = nil;
|
||||
@synchronized (dateFormatter_) {
|
||||
tstamp = [dateFormatter_ stringFromDate:[NSDate date]];
|
||||
}
|
||||
return [NSString stringWithFormat:@"%@ %@[%d/%p] [lvl=%d] %@ %@",
|
||||
tstamp, pname_, pid_, pthread_self(),
|
||||
level, (func ? func : @"(no func)"),
|
||||
level, [self prettyNameForFunc:func],
|
||||
// |super| has guard for nil |fmt| and |args|
|
||||
[super stringForFunc:func withFormat:fmt valist:args level:level]];
|
||||
}
|
||||
|
||||
|
@ -391,14 +485,20 @@ static GTMLogger *gSharedLogger = nil;
|
|||
// COV_NF_START
|
||||
static BOOL IsVerboseLoggingEnabled(void) {
|
||||
static NSString *const kVerboseLoggingKey = @"GTMVerboseLogging";
|
||||
static char *env = NULL;
|
||||
if (env == NULL)
|
||||
env = getenv([kVerboseLoggingKey UTF8String]);
|
||||
|
||||
if (env && env[0]) {
|
||||
return (strtol(env, NULL, 10) != 0);
|
||||
NSString *value = [[[NSProcessInfo processInfo] environment]
|
||||
objectForKey:kVerboseLoggingKey];
|
||||
if (value) {
|
||||
// Emulate [NSString boolValue] for pre-10.5
|
||||
value = [value stringByTrimmingCharactersInSet:
|
||||
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
if ([[value uppercaseString] hasPrefix:@"Y"] ||
|
||||
[[value uppercaseString] hasPrefix:@"T"] ||
|
||||
[value intValue]) {
|
||||
return YES;
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
return [[NSUserDefaults standardUserDefaults] boolForKey:kVerboseLoggingKey];
|
||||
}
|
||||
// COV_NF_END
|
||||
|
@ -417,7 +517,7 @@ static BOOL IsVerboseLoggingEnabled(void) {
|
|||
allow = NO;
|
||||
break;
|
||||
case kGTMLoggerLevelInfo:
|
||||
allow = (IsVerboseLoggingEnabled() == YES);
|
||||
allow = IsVerboseLoggingEnabled();
|
||||
break;
|
||||
case kGTMLoggerLevelError:
|
||||
allow = YES;
|
||||
|
@ -443,3 +543,70 @@ static BOOL IsVerboseLoggingEnabled(void) {
|
|||
}
|
||||
|
||||
@end // GTMLogNoFilter
|
||||
|
||||
|
||||
@implementation GTMLogAllowedLevelFilter
|
||||
|
||||
// Private designated initializer
|
||||
- (id)initWithAllowedLevels:(NSIndexSet *)levels {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
allowedLevels_ = [levels retain];
|
||||
// Cap min/max level
|
||||
if (!allowedLevels_ ||
|
||||
// NSIndexSet is unsigned so only check the high bound, but need to
|
||||
// check both first and last index because NSIndexSet appears to allow
|
||||
// wraparound.
|
||||
([allowedLevels_ firstIndex] > kGTMLoggerLevelAssert) ||
|
||||
([allowedLevels_ lastIndex] > kGTMLoggerLevelAssert)) {
|
||||
[self release];
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
// Allow all levels in default init
|
||||
return [self initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange:
|
||||
NSMakeRange(kGTMLoggerLevelUnknown,
|
||||
(kGTMLoggerLevelAssert - kGTMLoggerLevelUnknown + 1))]];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[allowedLevels_ release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level {
|
||||
return [allowedLevels_ containsIndex:level];
|
||||
}
|
||||
|
||||
@end // GTMLogAllowedLevelFilter
|
||||
|
||||
|
||||
@implementation GTMLogMininumLevelFilter
|
||||
|
||||
- (id)initWithMinimumLevel:(GTMLoggerLevel)level {
|
||||
return [super initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange:
|
||||
NSMakeRange(level,
|
||||
(kGTMLoggerLevelAssert - level + 1))]];
|
||||
}
|
||||
|
||||
@end // GTMLogMininumLevelFilter
|
||||
|
||||
|
||||
@implementation GTMLogMaximumLevelFilter
|
||||
|
||||
- (id)initWithMaximumLevel:(GTMLoggerLevel)level {
|
||||
return [super initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange:
|
||||
NSMakeRange(kGTMLoggerLevelUnknown, level + 1)]];
|
||||
}
|
||||
|
||||
@end // GTMLogMaximumLevelFilter
|
||||
|
||||
#if !defined(__clang__) && (__GNUC__*10+__GNUC_MINOR__ >= 42)
|
||||
// See comment at top of file.
|
||||
#pragma GCC diagnostic error "-Wmissing-format-attribute"
|
||||
#endif // !__clang__
|
||||
|
||||
|
|
|
@ -53,13 +53,29 @@
|
|||
|
||||
#import "GTMDefines.h"
|
||||
|
||||
#if (!GTM_IPHONE_SDK)
|
||||
#if (!GTM_IPHONE_SDK) || (GTM_IPHONE_USE_SENTEST)
|
||||
#import <SenTestingKit/SenTestingKit.h>
|
||||
#else
|
||||
#import <Foundation/Foundation.h>
|
||||
NSString *STComposeString(NSString *, ...);
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined __clang__
|
||||
// gcc and gcc-llvm do not allow you to use STAssert(blah, nil) with nil
|
||||
// as a description if you have the NS_FORMAT_FUNCTION on.
|
||||
// clang however will not compile without warnings if you don't have it.
|
||||
NSString *STComposeString(NSString *, ...) NS_FORMAT_FUNCTION(1, 2);
|
||||
#else
|
||||
NSString *STComposeString(NSString *, ...);
|
||||
#endif // __clang__
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // !GTM_IPHONE_SDK || GTM_IPHONE_USE_SENTEST
|
||||
|
||||
// Generates a failure when a1 != noErr
|
||||
// Args:
|
||||
// a1: should be either an OSErr or an OSStatus
|
||||
|
@ -71,13 +87,12 @@ do { \
|
|||
@try { \
|
||||
OSStatus a1value = (a1); \
|
||||
if (a1value != noErr) { \
|
||||
NSString *_expression = [NSString stringWithFormat:@"Expected noErr, got %ld for (%s)", a1value, #a1]; \
|
||||
if (description) { \
|
||||
_expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \
|
||||
} \
|
||||
[self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
NSString *_expression = [NSString stringWithFormat:@"Expected noErr, got %ld for (%s)", (long)a1value, #a1]; \
|
||||
[self failWithException:([NSException failureInCondition:_expression \
|
||||
isTrue:NO \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:_expression]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)])]; \
|
||||
} \
|
||||
} \
|
||||
@catch (id anException) { \
|
||||
|
@ -85,7 +100,7 @@ do { \
|
|||
exception:anException \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
@ -102,13 +117,12 @@ do { \
|
|||
OSStatus a1value = (a1); \
|
||||
OSStatus a2value = (a2); \
|
||||
if (a1value != a2value) { \
|
||||
NSString *_expression = [NSString stringWithFormat:@"Expected %s(%ld) but got %ld for (%s)", #a2, a2value, a1value, #a1]; \
|
||||
if (description) { \
|
||||
_expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \
|
||||
} \
|
||||
[self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
NSString *_expression = [NSString stringWithFormat:@"Expected %s(%ld) but got %ld for (%s)", #a2, (long)a2value, (long)a1value, #a1]; \
|
||||
[self failWithException:([NSException failureInCondition:_expression \
|
||||
isTrue:NO \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:_expression]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)])]; \
|
||||
} \
|
||||
} \
|
||||
@catch (id anException) { \
|
||||
|
@ -116,7 +130,7 @@ do { \
|
|||
exception:anException \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
@ -130,15 +144,14 @@ do { \
|
|||
#define STAssertNotNULL(a1, description, ...) \
|
||||
do { \
|
||||
@try { \
|
||||
const void* a1value = (a1); \
|
||||
if (a1value == NULL) { \
|
||||
NSString *_expression = [NSString stringWithFormat:@"(%s) != NULL", #a1]; \
|
||||
if (description) { \
|
||||
_expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \
|
||||
} \
|
||||
[self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
__typeof__(a1) a1value = (a1); \
|
||||
if (a1value == (__typeof__(a1))NULL) { \
|
||||
NSString *_expression = [NSString stringWithFormat:@"((%s) != NULL)", #a1]; \
|
||||
[self failWithException:([NSException failureInCondition:_expression \
|
||||
isTrue:NO \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:_expression]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)])]; \
|
||||
} \
|
||||
} \
|
||||
@catch (id anException) { \
|
||||
|
@ -146,7 +159,7 @@ do { \
|
|||
exception:anException \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
@ -159,15 +172,14 @@ do { \
|
|||
#define STAssertNULL(a1, description, ...) \
|
||||
do { \
|
||||
@try { \
|
||||
const void* a1value = (a1); \
|
||||
if (a1value != NULL) { \
|
||||
NSString *_expression = [NSString stringWithFormat:@"(%s) == NULL", #a1]; \
|
||||
if (description) { \
|
||||
_expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \
|
||||
} \
|
||||
[self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
__typeof__(a1) a1value = (a1); \
|
||||
if (a1value != (__typeof__(a1))NULL) { \
|
||||
NSString *_expression = [NSString stringWithFormat:@"((%s) == NULL)", #a1]; \
|
||||
[self failWithException:([NSException failureInCondition:_expression \
|
||||
isTrue:NO \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:_expression]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)])]; \
|
||||
} \
|
||||
} \
|
||||
@catch (id anException) { \
|
||||
|
@ -175,7 +187,7 @@ do { \
|
|||
exception:anException \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
@ -190,23 +202,22 @@ do { \
|
|||
#define STAssertNotEquals(a1, a2, description, ...) \
|
||||
do { \
|
||||
@try { \
|
||||
if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \
|
||||
if (strcmp(@encode(__typeof__(a1)), @encode(__typeof__(a2)))) { \
|
||||
[self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:[[[NSString stringWithFormat:@"Type mismatch (%@/%@) -- ",@encode(__typeof__(a1)),@encode(__typeof__(a2))] stringByAppendingString:STComposeString(description, ##__VA_ARGS__)]]]; \
|
||||
withDescription:@"Type mismatch -- %@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} else { \
|
||||
__typeof__(a1) a1value = (a1); \
|
||||
__typeof__(a2) a2value = (a2); \
|
||||
NSValue *a1encoded = [NSValue value:&a1value withObjCType:@encode(__typeof__(a1))]; \
|
||||
NSValue *a2encoded = [NSValue value:&a2value withObjCType:@encode(__typeof__(a2))]; \
|
||||
if ([a1encoded isEqualToValue:a2encoded]) { \
|
||||
NSString *_expression = [NSString stringWithFormat:@"(%s) != (%s)", #a1, #a2]; \
|
||||
if (description) { \
|
||||
_expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \
|
||||
} \
|
||||
[self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
NSString *_expression = [NSString stringWithFormat:@"((%s) != (%s))", #a1, #a2]; \
|
||||
[self failWithException:([NSException failureInCondition:_expression \
|
||||
isTrue:NO \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:_expression]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)])]; \
|
||||
}\
|
||||
} \
|
||||
} \
|
||||
|
@ -215,7 +226,7 @@ do { \
|
|||
exception:anException \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
@ -226,28 +237,26 @@ do { \
|
|||
// description: A format string as in the printf() function. Can be nil or
|
||||
// an empty string but must be present.
|
||||
// ...: A variable number of arguments to the format string. Can be absent.
|
||||
#define STAssertNotEqualObjects(a1, a2, desc, ...) \
|
||||
#define STAssertNotEqualObjects(a1, a2, description, ...) \
|
||||
do { \
|
||||
@try {\
|
||||
id a1value = (a1); \
|
||||
id a2value = (a2); \
|
||||
if ( (@encode(__typeof__(a1value)) == @encode(id)) && \
|
||||
(@encode(__typeof__(a2value)) == @encode(id)) && \
|
||||
![(id)a1value isEqual:(id)a2value] ) continue; \
|
||||
NSString *_expression = [NSString stringWithFormat:@"%s('%@') != %s('%@')", #a1, [a1 description], #a2, [a2 description]]; \
|
||||
if (desc) { \
|
||||
_expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(desc, ##__VA_ARGS__)]; \
|
||||
} \
|
||||
[self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
if ( (strcmp(@encode(__typeof__(a1value)), @encode(id)) == 0) && \
|
||||
(strcmp(@encode(__typeof__(a2value)), @encode(id)) == 0) && \
|
||||
(![(id)a1value isEqual:(id)a2value]) ) continue; \
|
||||
[self failWithException:([NSException failureInEqualityBetweenObject:a1value \
|
||||
andObject:a2value \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:_expression]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)])]; \
|
||||
}\
|
||||
@catch (id anException) {\
|
||||
[self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) != (%s)", #a1, #a2] \
|
||||
[self failWithException:([NSException failureInRaise:[NSString stringWithFormat:@"(%s) != (%s)", #a1, #a2] \
|
||||
exception:anException \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:STComposeString(desc, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)])]; \
|
||||
}\
|
||||
} while(0)
|
||||
|
||||
|
@ -262,23 +271,22 @@ do { \
|
|||
#define STAssertOperation(a1, a2, op, description, ...) \
|
||||
do { \
|
||||
@try { \
|
||||
if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \
|
||||
if (strcmp(@encode(__typeof__(a1)), @encode(__typeof__(a2)))) { \
|
||||
[self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:[[[NSString stringWithFormat:@"Type mismatch (%@/%@) -- ",@encode(__typeof__(a1)),@encode(__typeof__(a2))] stringByAppendingString:STComposeString(description, ##__VA_ARGS__)]]]; \
|
||||
withDescription:@"Type mismatch -- %@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} else { \
|
||||
__typeof__(a1) a1value = (a1); \
|
||||
__typeof__(a2) a2value = (a2); \
|
||||
if (!(a1value op a2value)) { \
|
||||
double a1DoubleValue = a1value; \
|
||||
double a2DoubleValue = a2value; \
|
||||
NSString *_expression = [NSString stringWithFormat:@"%s (%lg) %s %s (%lg)", #a1, a1DoubleValue, #op, #a2, a2DoubleValue]; \
|
||||
if (description) { \
|
||||
_expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \
|
||||
} \
|
||||
[self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
NSString *_expression = [NSString stringWithFormat:@"(%s (%lg) %s %s (%lg))", #a1, a1DoubleValue, #op, #a2, a2DoubleValue]; \
|
||||
[self failWithException:([NSException failureInCondition:_expression \
|
||||
isTrue:NO \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:_expression]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)])]; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
|
@ -288,7 +296,7 @@ do { \
|
|||
exception:anException \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
@ -360,14 +368,14 @@ do { \
|
|||
andObject:a2value \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
@catch (id anException) { \
|
||||
[self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == (%s)", #a1, #a2] \
|
||||
exception:anException \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
@ -394,14 +402,14 @@ do { \
|
|||
andObject:a2value \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
@catch (id anException) { \
|
||||
[self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) != (%s)", #a1, #a2] \
|
||||
exception:anException \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
@ -423,14 +431,14 @@ do { \
|
|||
andObject:[NSString stringWithUTF8String:a2value] \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
@catch (id anException) { \
|
||||
[self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == (%s)", #a1, #a2] \
|
||||
exception:anException \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
@ -451,18 +459,85 @@ do { \
|
|||
andObject:[NSString stringWithUTF8String:a2value] \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
@catch (id anException) { \
|
||||
[self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) != (%s)", #a1, #a2] \
|
||||
exception:anException \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#if GTM_IPHONE_SDK
|
||||
/*" Generates a failure when a1 is not equal to a2 within + or - accuracy is false.
|
||||
This test is for GLKit types (GLKVector, GLKMatrix) where small differences
|
||||
could make these items not exactly equal. Do not use this version directly.
|
||||
Use the explicit STAssertEqualGLKVectors and STAssertEqualGLKMatrices defined
|
||||
below.
|
||||
_{a1 The GLKType on the left.}
|
||||
_{a2 The GLKType on the right.}
|
||||
_{accuracy The maximum difference between a1 and a2 for these values to be
|
||||
considered equal.}
|
||||
_{description A format string as in the printf() function. Can be nil or
|
||||
an empty string but must be present.}
|
||||
_{... A variable number of arguments to the format string. Can be absent.}
|
||||
"*/
|
||||
|
||||
#define STInternalAssertEqualGLKVectorsOrMatrices(a1, a2, accuracy, description, ...) \
|
||||
do { \
|
||||
@try { \
|
||||
if (strcmp(@encode(__typeof__(a1)), @encode(__typeof__(a2)))) { \
|
||||
[self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:@"Type mismatch -- %@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} else { \
|
||||
__typeof__(a1) a1GLKValue = (a1); \
|
||||
__typeof__(a2) a2GLKValue = (a2); \
|
||||
__typeof__(accuracy) accuracyvalue = (accuracy); \
|
||||
float *a1FloatValue = ((float*)&a1GLKValue); \
|
||||
float *a2FloatValue = ((float*)&a2GLKValue); \
|
||||
for (size_t i = 0; i < sizeof(__typeof__(a1)) / sizeof(float); ++i) { \
|
||||
float a1value = a1FloatValue[i]; \
|
||||
float a2value = a2FloatValue[i]; \
|
||||
if (STAbsoluteDifference(a1value, a2value) > accuracyvalue) { \
|
||||
NSMutableArray *strings = [NSMutableArray arrayWithCapacity:sizeof(a1) / sizeof(float)]; \
|
||||
NSString *string; \
|
||||
for (size_t j = 0; j < sizeof(__typeof__(a1)) / sizeof(float); ++j) { \
|
||||
string = [NSString stringWithFormat:@"(%0.3f == %0.3f)", a1FloatValue[j], a2FloatValue[j]]; \
|
||||
[strings addObject:string]; \
|
||||
} \
|
||||
string = [strings componentsJoinedByString:@", "]; \
|
||||
NSString *desc = STComposeString(description, ##__VA_ARGS__); \
|
||||
desc = [NSString stringWithFormat:@"%@ With Accuracy %0.3f: %@", string, (float)accuracyvalue, desc]; \
|
||||
[self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:@"%@", desc]]; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
@catch (id anException) { \
|
||||
[self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == (%s)", #a1, #a2] \
|
||||
exception:anException \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define STAssertEqualGLKVectors(a1, a2, accuracy, description, ...) \
|
||||
STInternalAssertEqualGLKVectorsOrMatrices(a1, a2, accuracy, description, ##__VA_ARGS__)
|
||||
|
||||
#define STAssertEqualGLKMatrices(a1, a2, accuracy, description, ...) \
|
||||
STInternalAssertEqualGLKVectorsOrMatrices(a1, a2, accuracy, description, ##__VA_ARGS__)
|
||||
|
||||
#define STAssertEqualGLKQuaternions(a1, a2, accuracy, description, ...) \
|
||||
STInternalAssertEqualGLKVectorsOrMatrices(a1, a2, accuracy, description, ##__VA_ARGS__)
|
||||
|
||||
#if GTM_IPHONE_SDK && !GTM_IPHONE_USE_SENTEST
|
||||
// When not using the Xcode provided version, define everything ourselves.
|
||||
|
||||
// SENTE_BEGIN
|
||||
/*" Generates a failure when !{ [a1 isEqualTo:a2] } is false
|
||||
|
@ -479,21 +554,21 @@ do { \
|
|||
id a1value = (a1); \
|
||||
id a2value = (a2); \
|
||||
if (a1value == a2value) continue; \
|
||||
if ( (@encode(__typeof__(a1value)) == @encode(id)) && \
|
||||
(@encode(__typeof__(a2value)) == @encode(id)) && \
|
||||
if ((strcmp(@encode(__typeof__(a1value)), @encode(id)) == 0) && \
|
||||
(strcmp(@encode(__typeof__(a2value)), @encode(id)) == 0) && \
|
||||
[(id)a1value isEqual:(id)a2value]) continue; \
|
||||
[self failWithException:[NSException failureInEqualityBetweenObject:a1value \
|
||||
andObject:a2value \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
@catch (id anException) { \
|
||||
[self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == (%s)", #a1, #a2] \
|
||||
exception:anException \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
@ -509,10 +584,10 @@ do { \
|
|||
#define STAssertEquals(a1, a2, description, ...) \
|
||||
do { \
|
||||
@try { \
|
||||
if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \
|
||||
if (strcmp(@encode(__typeof__(a1)), @encode(__typeof__(a2)))) { \
|
||||
[self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:[[NSString stringWithFormat:@"Type mismatch (%@/%@) -- ",@encode(__typeof__(a1)),@encode(__typeof__(a2))] stringByAppendingString:STComposeString(description, ##__VA_ARGS__)]]]; \
|
||||
withDescription:@"Type mismatch -- %@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} else { \
|
||||
__typeof__(a1) a1value = (a1); \
|
||||
__typeof__(a2) a2value = (a2); \
|
||||
|
@ -524,7 +599,7 @@ do { \
|
|||
withAccuracy:nil \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
|
@ -533,7 +608,7 @@ do { \
|
|||
exception:anException \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
@ -555,10 +630,10 @@ do { \
|
|||
#define STAssertEqualsWithAccuracy(a1, a2, accuracy, description, ...) \
|
||||
do { \
|
||||
@try { \
|
||||
if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \
|
||||
if (strcmp(@encode(__typeof__(a1)), @encode(__typeof__(a2)))) { \
|
||||
[self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:[[[NSString stringWithFormat:@"Type mismatch (%@/%@) -- ",@encode(__typeof__(a1)),@encode(__typeof__(a2))] stringByAppendingString:STComposeString(description, ##__VA_ARGS__)]]]; \
|
||||
withDescription:@"Type mismatch -- %@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} else { \
|
||||
__typeof__(a1) a1value = (a1); \
|
||||
__typeof__(a2) a2value = (a2); \
|
||||
|
@ -572,7 +647,7 @@ do { \
|
|||
withAccuracy:accuracyencoded \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
|
@ -581,7 +656,7 @@ do { \
|
|||
exception:anException \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
@ -595,7 +670,7 @@ do { \
|
|||
#define STFail(description, ...) \
|
||||
[self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription: STComposeString(description, ##__VA_ARGS__)]]
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]
|
||||
|
||||
|
||||
|
||||
|
@ -616,7 +691,7 @@ do { \
|
|||
isTrue:NO \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
} \
|
||||
@catch (id anException) { \
|
||||
|
@ -624,7 +699,7 @@ do { \
|
|||
exception:anException \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
@ -646,7 +721,7 @@ do { \
|
|||
isTrue:NO \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
} \
|
||||
@catch (id anException) { \
|
||||
|
@ -654,7 +729,7 @@ do { \
|
|||
exception:anException \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
@ -674,7 +749,7 @@ do { \
|
|||
isTrue:NO \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
@ -696,7 +771,7 @@ do { \
|
|||
isTrue:NO \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
} \
|
||||
@catch (id anException) { \
|
||||
|
@ -704,7 +779,7 @@ do { \
|
|||
exception:anException \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
@ -724,7 +799,7 @@ do { \
|
|||
isTrue:YES \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
@ -746,7 +821,7 @@ do { \
|
|||
isTrue:YES \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
} \
|
||||
@catch (id anException) { \
|
||||
|
@ -754,7 +829,7 @@ do { \
|
|||
exception:anException \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
@ -777,7 +852,7 @@ do { \
|
|||
exception:nil \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} while (0)
|
||||
|
||||
|
||||
|
@ -803,7 +878,7 @@ do { \
|
|||
exception:anException \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(_descrip, ##__VA_ARGS__)]]; \
|
||||
continue; \
|
||||
} \
|
||||
NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description); \
|
||||
|
@ -811,7 +886,7 @@ do { \
|
|||
exception:nil \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(_descrip, ##__VA_ARGS__)]]; \
|
||||
} while (0)
|
||||
|
||||
|
||||
|
@ -840,7 +915,7 @@ do { \
|
|||
exception:anException \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(_descrip, ##__VA_ARGS__)]]; \
|
||||
continue; \
|
||||
} \
|
||||
@catch (id anException) { \
|
||||
|
@ -850,7 +925,7 @@ do { \
|
|||
exception:anException \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(_descrip, ##__VA_ARGS__)]]; \
|
||||
continue; \
|
||||
} \
|
||||
NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description); \
|
||||
|
@ -859,7 +934,7 @@ do { \
|
|||
exception:nil \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(_descrip, ##__VA_ARGS__)]]; \
|
||||
} while (0)
|
||||
|
||||
|
||||
|
@ -879,7 +954,7 @@ do { \
|
|||
exception:anException \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
@ -902,7 +977,7 @@ do { \
|
|||
exception:anException \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
@catch (id anythingElse) { \
|
||||
; \
|
||||
|
@ -935,7 +1010,7 @@ do { \
|
|||
exception:anException \
|
||||
inFile:[NSString stringWithUTF8String:__FILE__] \
|
||||
atLine:__LINE__ \
|
||||
withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \
|
||||
withDescription:@"%@", STComposeString(_descrip, ##__VA_ARGS__)]]; \
|
||||
} \
|
||||
continue; \
|
||||
} \
|
||||
|
@ -949,45 +1024,53 @@ do { \
|
|||
@interface NSException (GTMSenTestAdditions)
|
||||
+ (NSException *)failureInFile:(NSString *)filename
|
||||
atLine:(int)lineNumber
|
||||
withDescription:(NSString *)formatString, ...;
|
||||
withDescription:(NSString *)formatString, ... NS_FORMAT_FUNCTION(3, 4);
|
||||
+ (NSException *)failureInCondition:(NSString *)condition
|
||||
isTrue:(BOOL)isTrue
|
||||
inFile:(NSString *)filename
|
||||
atLine:(int)lineNumber
|
||||
withDescription:(NSString *)formatString, ...;
|
||||
withDescription:(NSString *)formatString, ... NS_FORMAT_FUNCTION(5, 6);
|
||||
+ (NSException *)failureInEqualityBetweenObject:(id)left
|
||||
andObject:(id)right
|
||||
inFile:(NSString *)filename
|
||||
atLine:(int)lineNumber
|
||||
withDescription:(NSString *)formatString, ...;
|
||||
withDescription:(NSString *)formatString, ... NS_FORMAT_FUNCTION(5, 6);
|
||||
+ (NSException *)failureInEqualityBetweenValue:(NSValue *)left
|
||||
andValue:(NSValue *)right
|
||||
withAccuracy:(NSValue *)accuracy
|
||||
inFile:(NSString *)filename
|
||||
atLine:(int) ineNumber
|
||||
withDescription:(NSString *)formatString, ...;
|
||||
withDescription:(NSString *)formatString, ... NS_FORMAT_FUNCTION(6, 7);
|
||||
+ (NSException *)failureInRaise:(NSString *)expression
|
||||
inFile:(NSString *)filename
|
||||
atLine:(int)lineNumber
|
||||
withDescription:(NSString *)formatString, ...;
|
||||
withDescription:(NSString *)formatString, ... NS_FORMAT_FUNCTION(4, 5);
|
||||
+ (NSException *)failureInRaise:(NSString *)expression
|
||||
exception:(NSException *)exception
|
||||
inFile:(NSString *)filename
|
||||
atLine:(int)lineNumber
|
||||
withDescription:(NSString *)formatString, ...;
|
||||
withDescription:(NSString *)formatString, ... NS_FORMAT_FUNCTION(5, 6);
|
||||
@end
|
||||
|
||||
// SENTE_END
|
||||
|
||||
@interface SenTestCase : NSObject {
|
||||
SEL currentSelector_;
|
||||
}
|
||||
|
||||
@protocol SenTestCase
|
||||
+ (id)testCaseWithInvocation:(NSInvocation *)anInvocation;
|
||||
- (id)initWithInvocation:(NSInvocation *)anInvocation;
|
||||
- (void)setUp;
|
||||
- (void)invokeTest;
|
||||
- (void)tearDown;
|
||||
- (void)performTest:(SEL)sel;
|
||||
- (void)performTest;
|
||||
- (void)failWithException:(NSException*)exception;
|
||||
- (NSInvocation *)invocation;
|
||||
- (SEL)selector;
|
||||
+ (NSArray *)testInvocations;
|
||||
@end
|
||||
|
||||
@interface SenTestCase : NSObject<SenTestCase> {
|
||||
@private
|
||||
NSInvocation *invocation_;
|
||||
}
|
||||
@end
|
||||
|
||||
GTM_EXTERN NSString *const SenTestFailureException;
|
||||
|
@ -995,10 +1078,33 @@ GTM_EXTERN NSString *const SenTestFailureException;
|
|||
GTM_EXTERN NSString *const SenTestFilenameKey;
|
||||
GTM_EXTERN NSString *const SenTestLineNumberKey;
|
||||
|
||||
#endif // GTM_IPHONE_SDK
|
||||
#endif // GTM_IPHONE_SDK && !GTM_IPHONE_USE_SENTEST
|
||||
|
||||
// All unittest cases in GTM should inherit from GTMTestCase. It makes sure
|
||||
// to set up our logging system correctly to verify logging calls.
|
||||
// See GTMUnitTestDevLog.h for details
|
||||
@interface GTMTestCase : SenTestCase
|
||||
|
||||
// Returns YES if this is an abstract testCase class as opposed to a concrete
|
||||
// testCase class that you want tests run against. SenTestCase is not designed
|
||||
// out of the box to handle an abstract class hierarchy descending from it with
|
||||
// some concrete subclasses. In some cases we want all the "concrete"
|
||||
// subclasses of an abstract subclass of SenTestCase to run a test, but we don't
|
||||
// want that test to be run against an instance of an abstract subclass itself.
|
||||
// By returning "YES" here, the tests defined by this class won't be run against
|
||||
// an instance of this class. As an example class hierarchy:
|
||||
//
|
||||
// FooExtensionTestCase
|
||||
// GTMTestCase <- ExtensionAbstractTestCase <
|
||||
// BarExtensionTestCase
|
||||
//
|
||||
// So FooExtensionTestCase and BarExtensionTestCase inherit from
|
||||
// ExtensionAbstractTestCase (and probably FooExtension and BarExtension inherit
|
||||
// from a class named Extension). We want the tests in ExtensionAbstractTestCase
|
||||
// to be run as part of FooExtensionTestCase and BarExtensionTestCase, but we
|
||||
// don't want them run against ExtensionAbstractTestCase. The default
|
||||
// implementation checks to see if the name of the class contains the word
|
||||
// "AbstractTest" (case sensitive).
|
||||
+ (BOOL)isAbstractTestCase;
|
||||
|
||||
@end
|
||||
|
|
|
@ -17,13 +17,20 @@
|
|||
//
|
||||
|
||||
#import "GTMSenTestCase.h"
|
||||
|
||||
#import <unistd.h>
|
||||
#if GTM_IPHONE_SIMULATOR
|
||||
#import <objc/message.h>
|
||||
#endif
|
||||
|
||||
#import "GTMObjC2Runtime.h"
|
||||
#import "GTMUnitTestDevLog.h"
|
||||
|
||||
#if !GTM_IPHONE_SDK
|
||||
#import "GTMGarbageCollection.h"
|
||||
#endif // !GTM_IPHONE_SDK
|
||||
|
||||
#if GTM_IPHONE_SDK
|
||||
#if GTM_IPHONE_SDK && !GTM_IPHONE_USE_SENTEST
|
||||
#import <stdarg.h>
|
||||
|
||||
@interface NSException (GTMSenTestPrivateAdditions)
|
||||
|
@ -84,7 +91,7 @@
|
|||
}
|
||||
|
||||
NSString *reason = [NSString stringWithFormat:@"'%@' should be %s. %@",
|
||||
condition, isTrue ? "TRUE" : "FALSE", testDescription];
|
||||
condition, isTrue ? "false" : "true", testDescription];
|
||||
|
||||
return [self failureInFile:filename atLine:lineNumber reason:reason];
|
||||
}
|
||||
|
@ -213,6 +220,22 @@ NSString *const SenTestLineNumberKey = @"SenTestLineNumberKey";
|
|||
@end
|
||||
|
||||
@implementation SenTestCase
|
||||
+ (id)testCaseWithInvocation:(NSInvocation *)anInvocation {
|
||||
return [[[self alloc] initWithInvocation:anInvocation] autorelease];
|
||||
}
|
||||
|
||||
- (id)initWithInvocation:(NSInvocation *)anInvocation {
|
||||
if ((self = [super init])) {
|
||||
invocation_ = [anInvocation retain];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[invocation_ release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)failWithException:(NSException*)exception {
|
||||
[exception raise];
|
||||
}
|
||||
|
@ -220,17 +243,24 @@ NSString *const SenTestLineNumberKey = @"SenTestLineNumberKey";
|
|||
- (void)setUp {
|
||||
}
|
||||
|
||||
- (void)performTest:(SEL)sel {
|
||||
currentSelector_ = sel;
|
||||
- (void)performTest {
|
||||
@try {
|
||||
[self invokeTest];
|
||||
} @catch (NSException *exception) {
|
||||
[[self class] printException:exception
|
||||
fromTestName:NSStringFromSelector(sel)];
|
||||
fromTestName:NSStringFromSelector([self selector])];
|
||||
[exception raise];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSInvocation *)invocation {
|
||||
return invocation_;
|
||||
}
|
||||
|
||||
- (SEL)selector {
|
||||
return [invocation_ selector];
|
||||
}
|
||||
|
||||
+ (void)printException:(NSException *)exception fromTestName:(NSString *)name {
|
||||
NSDictionary *userInfo = [exception userInfo];
|
||||
NSString *filename = [userInfo objectForKey:SenTestFilenameKey];
|
||||
|
@ -261,7 +291,17 @@ NSString *const SenTestLineNumberKey = @"SenTestLineNumberKey";
|
|||
@try {
|
||||
[self setUp];
|
||||
@try {
|
||||
[self performSelector:currentSelector_];
|
||||
NSInvocation *invocation = [self invocation];
|
||||
#if GTM_IPHONE_SIMULATOR
|
||||
// We don't call [invocation invokeWithTarget:self]; because of
|
||||
// Radar 8081169: NSInvalidArgumentException can't be caught
|
||||
// It turns out that on iOS4 (and 3.2) exceptions thrown inside an
|
||||
// [invocation invoke] on the simulator cannot be caught.
|
||||
// http://openradar.appspot.com/8081169
|
||||
objc_msgSend(self, [invocation selector]);
|
||||
#else
|
||||
[invocation invokeWithTarget:self];
|
||||
#endif
|
||||
} @catch (NSException *exception) {
|
||||
e = [exception retain];
|
||||
}
|
||||
|
@ -285,14 +325,83 @@ NSString *const SenTestLineNumberKey = @"SenTestLineNumberKey";
|
|||
- (NSString *)description {
|
||||
// This matches the description OCUnit would return to you
|
||||
return [NSString stringWithFormat:@"-[%@ %@]", [self class],
|
||||
NSStringFromSelector(currentSelector_)];
|
||||
NSStringFromSelector([self selector])];
|
||||
}
|
||||
|
||||
// Used for sorting methods below
|
||||
static int MethodSort(id a, id b, void *context) {
|
||||
NSInvocation *invocationA = a;
|
||||
NSInvocation *invocationB = b;
|
||||
const char *nameA = sel_getName([invocationA selector]);
|
||||
const char *nameB = sel_getName([invocationB selector]);
|
||||
return strcmp(nameA, nameB);
|
||||
}
|
||||
|
||||
|
||||
+ (NSArray *)testInvocations {
|
||||
NSMutableArray *invocations = nil;
|
||||
// Need to walk all the way up the parent classes collecting methods (in case
|
||||
// a test is a subclass of another test).
|
||||
Class senTestCaseClass = [SenTestCase class];
|
||||
for (Class currentClass = self;
|
||||
currentClass && (currentClass != senTestCaseClass);
|
||||
currentClass = class_getSuperclass(currentClass)) {
|
||||
unsigned int methodCount;
|
||||
Method *methods = class_copyMethodList(currentClass, &methodCount);
|
||||
if (methods) {
|
||||
// This handles disposing of methods for us even if an exception should fly.
|
||||
[NSData dataWithBytesNoCopy:methods
|
||||
length:sizeof(Method) * methodCount];
|
||||
if (!invocations) {
|
||||
invocations = [NSMutableArray arrayWithCapacity:methodCount];
|
||||
}
|
||||
for (size_t i = 0; i < methodCount; ++i) {
|
||||
Method currMethod = methods[i];
|
||||
SEL sel = method_getName(currMethod);
|
||||
char *returnType = NULL;
|
||||
const char *name = sel_getName(sel);
|
||||
// If it starts with test, takes 2 args (target and sel) and returns
|
||||
// void run it.
|
||||
if (strstr(name, "test") == name) {
|
||||
returnType = method_copyReturnType(currMethod);
|
||||
if (returnType) {
|
||||
// This handles disposing of returnType for us even if an
|
||||
// exception should fly. Length +1 for the terminator, not that
|
||||
// the length really matters here, as we never reference inside
|
||||
// the data block.
|
||||
[NSData dataWithBytesNoCopy:returnType
|
||||
length:strlen(returnType) + 1];
|
||||
}
|
||||
}
|
||||
// TODO: If a test class is a subclass of another, and they reuse the
|
||||
// same selector name (ie-subclass overrides it), this current loop
|
||||
// and test here will cause cause it to get invoked twice. To fix this
|
||||
// the selector would have to be checked against all the ones already
|
||||
// added, so it only gets done once.
|
||||
if (returnType // True if name starts with "test"
|
||||
&& strcmp(returnType, @encode(void)) == 0
|
||||
&& method_getNumberOfArguments(currMethod) == 2) {
|
||||
NSMethodSignature *sig = [self instanceMethodSignatureForSelector:sel];
|
||||
NSInvocation *invocation
|
||||
= [NSInvocation invocationWithMethodSignature:sig];
|
||||
[invocation setSelector:sel];
|
||||
[invocations addObject:invocation];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Match SenTestKit and run everything in alphbetical order.
|
||||
[invocations sortUsingFunction:MethodSort context:nil];
|
||||
return invocations;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif // GTM_IPHONE_SDK
|
||||
#endif // GTM_IPHONE_SDK && !GTM_IPHONE_USE_SENTEST
|
||||
|
||||
@implementation GTMTestCase : SenTestCase
|
||||
- (void)invokeTest {
|
||||
NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init];
|
||||
Class devLogClass = NSClassFromString(@"GTMUnitTestDevLog");
|
||||
if (devLogClass) {
|
||||
[devLogClass performSelector:@selector(enableTracking)];
|
||||
|
@ -304,11 +413,26 @@ NSString *const SenTestLineNumberKey = @"SenTestLineNumberKey";
|
|||
[devLogClass performSelector:@selector(verifyNoMoreLogsExpected)];
|
||||
[devLogClass performSelector:@selector(disableTracking)];
|
||||
}
|
||||
[localPool drain];
|
||||
}
|
||||
|
||||
+ (BOOL)isAbstractTestCase {
|
||||
NSString *name = NSStringFromClass(self);
|
||||
return [name rangeOfString:@"AbstractTest"].location != NSNotFound;
|
||||
}
|
||||
|
||||
+ (NSArray *)testInvocations {
|
||||
NSArray *invocations = nil;
|
||||
if (![self isAbstractTestCase]) {
|
||||
invocations = [super testInvocations];
|
||||
}
|
||||
return invocations;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// Leak detection
|
||||
#if !GTM_IPHONE_DEVICE
|
||||
#if !GTM_IPHONE_DEVICE && !GTM_SUPPRESS_RUN_LEAKS_HOOK
|
||||
// Don't want to get leaks on the iPhone Device as the device doesn't
|
||||
// have 'leaks'. The simulator does though.
|
||||
|
||||
|
@ -329,13 +453,21 @@ static void _GTMRunLeaks(void) {
|
|||
[exclusions appendFormat:@"-exclude \"%@\" ", exclusion];
|
||||
}
|
||||
}
|
||||
// Clearing out DYLD_ROOT_PATH because iPhone Simulator framework libraries
|
||||
// are different from regular OS X libraries and leaks will fail to run
|
||||
// because of missing symbols. Also capturing the output of leaks and then
|
||||
// pipe rather than a direct pipe, because otherwise if leaks failed,
|
||||
// the system() call will still be successful. Bug:
|
||||
// http://code.google.com/p/google-toolbox-for-mac/issues/detail?id=56
|
||||
NSString *string
|
||||
= [NSString stringWithFormat:@"/usr/bin/leaks %@%d"
|
||||
@"| /usr/bin/sed -e 's/Leak: /Leaks:0: warning: Leak /'",
|
||||
= [NSString stringWithFormat:
|
||||
@"LeakOut=`DYLD_ROOT_PATH='' /usr/bin/leaks %@%d` &&"
|
||||
@"echo \"$LeakOut\"|/usr/bin/sed -e 's/Leak: /Leaks:0: warning: Leak /'",
|
||||
exclusions, getpid()];
|
||||
int ret = system([string UTF8String]);
|
||||
if (ret) {
|
||||
fprintf(stderr, "%s:%d: Error: Unable to run leaks. 'system' returned: %d",
|
||||
fprintf(stderr,
|
||||
"%s:%d: Error: Unable to run leaks. 'system' returned: %d\n",
|
||||
__FILE__, __LINE__, ret);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
@ -355,6 +487,8 @@ static __attribute__((constructor)) void _GTMInstallLeaks(void) {
|
|||
fprintf(stderr, "Leak Checking Enabled\n");
|
||||
fflush(stderr);
|
||||
int ret = atexit(&_GTMRunLeaks);
|
||||
// To avoid unused variable warning when _GTMDevAssert is stripped.
|
||||
(void)ret;
|
||||
_GTMDevAssert(ret == 0,
|
||||
@"Unable to install _GTMRunLeaks as an atexit handler (%d)",
|
||||
errno);
|
||||
|
@ -363,4 +497,4 @@ static __attribute__((constructor)) void _GTMInstallLeaks(void) {
|
|||
}
|
||||
}
|
||||
|
||||
#endif // !GTM_IPHONE_DEVICE
|
||||
#endif // !GTM_IPHONE_DEVICE && !GTM_SUPPRESS_RUN_LEAKS_HOOK
|
||||
|
|
Loading…
Reference in a new issue