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:
qsr@chromium.org 2012-09-21 07:55:17 +00:00
parent 0e91d185ca
commit c83cd11615
5 changed files with 1257 additions and 601 deletions

View file

@ -21,6 +21,14 @@
#include <AvailabilityMacros.h> #include <AvailabilityMacros.h>
#include <TargetConditionals.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 // Not all MAC_OS_X_VERSION_10_X macros defined in past SDKs
#ifndef MAC_OS_X_VERSION_10_5 #ifndef MAC_OS_X_VERSION_10_5
#define MAC_OS_X_VERSION_10_5 1050 #define MAC_OS_X_VERSION_10_5 1050
@ -28,6 +36,29 @@
#ifndef MAC_OS_X_VERSION_10_6 #ifndef MAC_OS_X_VERSION_10_6
#define MAC_OS_X_VERSION_10_6 1060 #define MAC_OS_X_VERSION_10_6 1060
#endif #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 // 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 // a few different actual definitions, so we're based off of the foundation
// one. // one.
#if !defined(GTM_INLINE) #if !defined(GTM_INLINE)
#if defined (__GNUC__) && (__GNUC__ == 4) #if (defined (__GNUC__) && (__GNUC__ == 4)) || defined (__clang__)
#define GTM_INLINE static __inline__ __attribute__((always_inline)) #define GTM_INLINE static __inline__ __attribute__((always_inline))
#else #else
#define GTM_INLINE static __inline__ #define GTM_INLINE static __inline__
@ -59,8 +90,12 @@
#if !defined (GTM_EXTERN) #if !defined (GTM_EXTERN)
#if defined __cplusplus #if defined __cplusplus
#define GTM_EXTERN extern "C" #define GTM_EXTERN extern "C"
#define GTM_EXTERN_C_BEGIN extern "C" {
#define GTM_EXTERN_C_END }
#else #else
#define GTM_EXTERN extern #define GTM_EXTERN extern
#define GTM_EXTERN_C_BEGIN
#define GTM_EXTERN_C_END
#endif #endif
#endif #endif
@ -70,6 +105,12 @@
#define GTM_EXPORT __attribute__((visibility("default"))) #define GTM_EXPORT __attribute__((visibility("default")))
#endif #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
// //
// _GTMDevLog & _GTMDevAssert are meant to be a very lightweight shell for // _GTMDevLog & _GTMDevAssert are meant to be a very lightweight shell for
@ -100,11 +141,6 @@
#endif // _GTMDevLog #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 #ifndef _GTMDevAssert
// we directly invoke the NSAssert handler so we can pass on the varargs // 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) // (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) ] typedef char _GTMCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ]
#endif // _GTMCompileAssert #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 // 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 // simple things to test against w/o scattering the knowledge of project
@ -183,11 +197,26 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
#else #else
#define GTM_IPHONE_DEVICE 1 #define GTM_IPHONE_DEVICE 1
#endif // TARGET_IPHONE_SIMULATOR #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 #else
// For MacOS specific stuff // For MacOS specific stuff
#define GTM_MACOS_SDK 1 #define GTM_MACOS_SDK 1
#endif #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 // Provide a symbol to include/exclude extra code for GC support. (This mainly
// just controls the inclusion of finalize methods). // just controls the inclusion of finalize methods).
#ifndef GTM_SUPPORT_GC #ifndef GTM_SUPPORT_GC
@ -197,7 +226,7 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
#else #else
// We can't find a symbol to tell if GC is supported/required, so best we // 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. // 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 #define GTM_SUPPORT_GC 0
#else #else
#define GTM_SUPPORT_GC 1 #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 // To simplify support for 64bit (and Leopard in general), we provide the type
// defines for non Leopard SDKs // 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 // NSInteger/NSUInteger and Max/Mins
#ifndef NSINTEGER_DEFINED #ifndef NSINTEGER_DEFINED
#if __LP64__ || NS_BUILD_32_LIKE_64 #if __LP64__ || NS_BUILD_32_LIKE_64
@ -238,4 +267,178 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
#endif /* !defined(__LP64__) || !__LP64__ */ #endif /* !defined(__LP64__) || !__LP64__ */
#define CGFLOAT_DEFINED 1 #define CGFLOAT_DEFINED 1
#endif // CGFLOAT_DEFINED #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__

View file

@ -55,12 +55,6 @@
// Predeclaration of used protocols that are declared later in this file. // Predeclaration of used protocols that are declared later in this file.
@protocol GTMLogWriter, GTMLogFormatter, GTMLogFilter; @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
// //
// GTMLogger is the primary user-facing class for an object-oriented logging // GTMLogger is the primary user-facing class for an object-oriented logging
@ -244,6 +238,10 @@
// Same as +standardLogger, but logs to stderr. // Same as +standardLogger, but logs to stderr.
+ (id)standardLoggerWithStderr; + (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 // Returns a new standard GTMLogger instance with a log writer that will
// write to the file at |path|, and will use the GTMLogStandardFormatter and // write to the file at |path|, and will use the GTMLogStandardFormatter and
// GTMLogLevelFilter classes. If |path| does not exist, it will be created. // GTMLogLevelFilter classes. If |path| does not exist, it will be created.
@ -274,13 +272,13 @@
// //
// Logs a message at the debug level (kGTMLoggerLevelDebug). // 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). // 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). // 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). // 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. // enable the logging of function names.
@interface GTMLogger (GTMLoggerMacroHelpers) @interface GTMLogger (GTMLoggerMacroHelpers)
- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ... - (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, ... - (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, ... - (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, ... - (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ...
CHECK_FORMAT_NSSTRING(2, 3); NS_FORMAT_FUNCTION(2, 3);
@end // GTMLoggerMacroHelpers @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 // Convenience macros that log to the shared GTMLogger instance. These macros
// are how users should typically log to GTMLogger. Notice that GTMLoggerDebug() // are how users should typically log to GTMLogger. Notice that GTMLoggerDebug()
// calls will be compiled out of non-Debug builds. // calls will be compiled out of non-Debug builds.
@ -339,6 +340,8 @@
#define GTMLoggerDebug(...) do {} while(0) #define GTMLoggerDebug(...) do {} while(0)
#endif #endif
#endif // !defined(GTMLoggerInfo)
// Log levels. // Log levels.
typedef enum { typedef enum {
kGTMLoggerLevelUnknown, kGTMLoggerLevelUnknown,
@ -407,7 +410,7 @@ typedef enum {
- (NSString *)stringForFunc:(NSString *)func - (NSString *)stringForFunc:(NSString *)func
withFormat:(NSString *)fmt withFormat:(NSString *)fmt
valist:(va_list)args valist:(va_list)args
level:(GTMLoggerLevel)level; level:(GTMLoggerLevel)level NS_FORMAT_FUNCTION(2, 0);
@end // GTMLogFormatter @end // GTMLogFormatter
@ -415,6 +418,10 @@ typedef enum {
// printf) would. It does not do anything fancy, nor does it add any data of its // printf) would. It does not do anything fancy, nor does it add any data of its
// own. // own.
@interface GTMLogBasicFormatter : NSObject <GTMLogFormatter> @interface GTMLogBasicFormatter : NSObject <GTMLogFormatter>
// Helper method for prettying C99 __func__ and GCC __PRETTY_FUNCTION__
- (NSString *)prettyNameForFunc:(NSString *)func;
@end // GTMLogBasicFormatter @end // GTMLogBasicFormatter
@ -450,9 +457,48 @@ typedef enum {
@interface GTMLogLevelFilter : NSObject <GTMLogFilter> @interface GTMLogLevelFilter : NSObject <GTMLogFilter>
@end // GTMLogLevelFilter @end // GTMLogLevelFilter
// A simple log filter that does NOT filter anything out; // A simple log filter that does NOT filter anything out;
// -filterAllowsMessage:level will always return YES. This can be a convenient // -filterAllowsMessage:level will always return YES. This can be a convenient
// way to enable debug-level logging in release builds (if you so desire). // way to enable debug-level logging in release builds (if you so desire).
@interface GTMLogNoFilter : NSObject <GTMLogFilter> @interface GTMLogNoFilter : NSObject <GTMLogFilter>
@end // GTMLogNoFilter @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

View file

@ -24,23 +24,15 @@
#import <pthread.h> #import <pthread.h>
// Define a trivial assertion macro to avoid dependencies #if !defined(__clang__) && (__GNUC__*10+__GNUC_MINOR__ >= 42)
#ifdef DEBUG // Some versions of GCC (4.2 and below AFAIK) aren't great about supporting
#define GTMLOGGER_ASSERT(expr) assert(expr) // -Wmissing-format-attribute
#else // when the function is anything more complex than foo(NSString *fmt, ...).
#define GTMLOGGER_ASSERT(expr) // You see the error inside the function when you turn ... into va_args and
#endif // 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"
@interface GTMLogger (PrivateMethods) #endif // !__clang__
- (void)logInternalFunc:(const char *)func
format:(NSString *)fmt
valist:(va_list)args
level:(GTMLoggerLevel)level;
@end
// Reference to the shared GTMLogger instance. This is not a singleton, it's // Reference to the shared GTMLogger instance. This is not a singleton, it's
// just an easy reference to one shared instance. // just an easy reference to one shared instance.
@ -56,7 +48,6 @@ static GTMLogger *gSharedLogger = nil;
if (gSharedLogger == nil) { if (gSharedLogger == nil) {
gSharedLogger = [[self standardLogger] retain]; gSharedLogger = [[self standardLogger] retain];
} }
GTMLOGGER_ASSERT(gSharedLogger != nil);
} }
return [[gSharedLogger retain] autorelease]; return [[gSharedLogger retain] autorelease];
} }
@ -69,24 +60,85 @@ static GTMLogger *gSharedLogger = nil;
} }
+ (id)standardLogger { + (id)standardLogger {
id<GTMLogWriter> writer = [NSFileHandle fileHandleWithStandardOutput]; // Don't trust NSFileHandle not to throw
id<GTMLogFormatter> fr = [[[GTMLogStandardFormatter alloc] init] autorelease]; @try {
id<GTMLogFilter> filter = [[[GTMLogLevelFilter alloc] init] autorelease]; id<GTMLogWriter> writer = [NSFileHandle fileHandleWithStandardOutput];
return [self loggerWithWriter:writer formatter:fr filter:filter]; id<GTMLogFormatter> fr = [[[GTMLogStandardFormatter alloc] init]
autorelease];
id<GTMLogFilter> filter = [[[GTMLogLevelFilter alloc] init] autorelease];
return [[[self alloc] initWithWriter:writer
formatter:fr
filter:filter] autorelease];
}
@catch (id e) {
// Ignored
}
return nil;
} }
+ (id)standardLoggerWithStderr { + (id)standardLoggerWithStderr {
id me = [self standardLogger]; // Don't trust NSFileHandle not to throw
[me setWriter:[NSFileHandle fileHandleWithStandardError]]; @try {
return me; 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 { + (id)standardLoggerWithPath:(NSString *)path {
NSFileHandle *fh = [NSFileHandle fileHandleForLoggingAtPath:path mode:0644]; @try {
if (fh == nil) return nil; NSFileHandle *fh = [NSFileHandle fileHandleForLoggingAtPath:path mode:0644];
id me = [self standardLogger]; if (fh == nil) return nil;
[me setWriter:fh]; id me = [self standardLogger];
return me; [me setWriter:fh];
return me;
}
@catch (id e) {
// Ignored
}
return nil;
} }
+ (id)loggerWithWriter:(id<GTMLogWriter>)writer + (id)loggerWithWriter:(id<GTMLogWriter>)writer
@ -112,69 +164,85 @@ static GTMLogger *gSharedLogger = nil;
[self setWriter:writer]; [self setWriter:writer];
[self setFormatter:formatter]; [self setFormatter:formatter];
[self setFilter:filter]; [self setFilter:filter];
GTMLOGGER_ASSERT(formatter_ != nil);
GTMLOGGER_ASSERT(filter_ != nil);
GTMLOGGER_ASSERT(writer_ != nil);
} }
return self; return self;
} }
- (void)dealloc { - (void)dealloc {
GTMLOGGER_ASSERT(writer_ != nil); // Unlikely, but |writer_| may be an NSFileHandle, which can throw
GTMLOGGER_ASSERT(formatter_ != nil); @try {
GTMLOGGER_ASSERT(filter_ != nil); [formatter_ release];
[writer_ release]; [filter_ release];
[formatter_ release]; [writer_ release];
[filter_ release]; }
@catch (id e) {
// Ignored
}
[super dealloc]; [super dealloc];
} }
- (id<GTMLogWriter>)writer { - (id<GTMLogWriter>)writer {
GTMLOGGER_ASSERT(writer_ != nil);
return [[writer_ retain] autorelease]; return [[writer_ retain] autorelease];
} }
- (void)setWriter:(id<GTMLogWriter>)writer { - (void)setWriter:(id<GTMLogWriter>)writer {
@synchronized(self) { @synchronized(self) {
[writer_ autorelease]; [writer_ autorelease];
if (writer == nil) writer_ = nil;
writer_ = [[NSFileHandle fileHandleWithStandardOutput] retain]; if (writer == nil) {
else // Try to use stdout, but don't trust NSFileHandle
@try {
writer_ = [[NSFileHandle fileHandleWithStandardOutput] retain];
}
@catch (id e) {
// Leave |writer_| nil
}
} else {
writer_ = [writer retain]; writer_ = [writer retain];
}
} }
GTMLOGGER_ASSERT(writer_ != nil);
} }
- (id<GTMLogFormatter>)formatter { - (id<GTMLogFormatter>)formatter {
GTMLOGGER_ASSERT(formatter_ != nil);
return [[formatter_ retain] autorelease]; return [[formatter_ retain] autorelease];
} }
- (void)setFormatter:(id<GTMLogFormatter>)formatter { - (void)setFormatter:(id<GTMLogFormatter>)formatter {
@synchronized(self) { @synchronized(self) {
[formatter_ autorelease]; [formatter_ autorelease];
if (formatter == nil) formatter_ = nil;
formatter_ = [[GTMLogBasicFormatter alloc] init]; if (formatter == nil) {
else @try {
formatter_ = [[GTMLogBasicFormatter alloc] init];
}
@catch (id e) {
// Leave |formatter_| nil
}
} else {
formatter_ = [formatter retain]; formatter_ = [formatter retain];
}
} }
GTMLOGGER_ASSERT(formatter_ != nil);
} }
- (id<GTMLogFilter>)filter { - (id<GTMLogFilter>)filter {
GTMLOGGER_ASSERT(filter_ != nil);
return [[filter_ retain] autorelease]; return [[filter_ retain] autorelease];
} }
- (void)setFilter:(id<GTMLogFilter>)filter { - (void)setFilter:(id<GTMLogFilter>)filter {
@synchronized(self) { @synchronized(self) {
[filter_ autorelease]; [filter_ autorelease];
if (filter == nil) filter_ = nil;
filter_ = [[GTMLogNoFilter alloc] init]; if (filter == nil) {
else @try {
filter_ = [[GTMLogNoFilter alloc] init];
}
@catch (id e) {
// Leave |filter_| nil
}
} else {
filter_ = [filter retain]; filter_ = [filter retain];
}
} }
GTMLOGGER_ASSERT(filter_ != nil);
} }
- (void)logDebug:(NSString *)fmt, ... { - (void)logDebug:(NSString *)fmt, ... {
@ -207,7 +275,6 @@ static GTMLogger *gSharedLogger = nil;
@end // GTMLogger @end // GTMLogger
@implementation GTMLogger (GTMLoggerMacroHelpers) @implementation GTMLogger (GTMLoggerMacroHelpers)
- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ... { - (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ... {
@ -240,24 +307,26 @@ static GTMLogger *gSharedLogger = nil;
@end // GTMLoggerMacroHelpers @end // GTMLoggerMacroHelpers
@implementation GTMLogger (PrivateMethods) @implementation GTMLogger (PrivateMethods)
- (void)logInternalFunc:(const char *)func - (void)logInternalFunc:(const char *)func
format:(NSString *)fmt format:(NSString *)fmt
valist:(va_list)args valist:(va_list)args
level:(GTMLoggerLevel)level { level:(GTMLoggerLevel)level {
GTMLOGGER_ASSERT(formatter_ != nil); // Primary point where logging happens, logging should never throw, catch
GTMLOGGER_ASSERT(filter_ != nil); // everything.
GTMLOGGER_ASSERT(writer_ != nil); @try {
NSString *fname = func ? [NSString stringWithUTF8String:func] : nil;
NSString *fname = func ? [NSString stringWithUTF8String:func] : nil; NSString *msg = [formatter_ stringForFunc:fname
NSString *msg = [formatter_ stringForFunc:fname withFormat:fmt
withFormat:fmt valist:args
valist:args level:level];
level:level]; if (msg && [filter_ filterAllowsMessage:msg level:level])
if (msg && [filter_ filterAllowsMessage:msg level:level]) [writer_ logMessage:msg level:level];
[writer_ logMessage:msg level:level]; }
@catch (id e) {
// Ignored
}
} }
@end // PrivateMethods @end // PrivateMethods
@ -278,8 +347,16 @@ static GTMLogger *gSharedLogger = nil;
- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level { - (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
@synchronized(self) { @synchronized(self) {
NSString *line = [NSString stringWithFormat:@"%@\n", msg]; // Closed pipes should not generate exceptions in our caller. Catch here
[self writeData:[line dataUsingEncoding:NSUTF8StringEncoding]]; // 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
}
} }
} }
@ -328,19 +405,32 @@ static GTMLogger *gSharedLogger = nil;
@implementation GTMLogBasicFormatter @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 - (NSString *)stringForFunc:(NSString *)func
withFormat:(NSString *)fmt withFormat:(NSString *)fmt
valist:(va_list)args valist:(va_list)args
level:(GTMLoggerLevel)level { level:(GTMLoggerLevel)level {
// Performance note: since we always have to create a new NSString from the // Performance note: We may want to do a quick check here to see if |fmt|
// returned CFStringRef, we may want to do a quick check here to see if |fmt|
// contains a '%', and if not, simply return 'fmt'. // contains a '%', and if not, simply return 'fmt'.
CFStringRef cfmsg = NULL; if (!(fmt && args)) return nil;
cfmsg = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, return [[[NSString alloc] initWithFormat:fmt arguments:args] autorelease];
NULL, // format options
(CFStringRef)fmt,
args);
return GTMCFAutorelease(cfmsg);
} }
@end // GTMLogBasicFormatter @end // GTMLogBasicFormatter
@ -355,6 +445,10 @@ static GTMLogger *gSharedLogger = nil;
[dateFormatter_ setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSS"]; [dateFormatter_ setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSS"];
pname_ = [[[NSProcessInfo processInfo] processName] copy]; pname_ = [[[NSProcessInfo processInfo] processName] copy];
pid_ = [[NSProcessInfo processInfo] processIdentifier]; pid_ = [[NSProcessInfo processInfo] processIdentifier];
if (!(dateFormatter_ && pname_)) {
[self release];
return nil;
}
} }
return self; return self;
} }
@ -369,15 +463,15 @@ static GTMLogger *gSharedLogger = nil;
withFormat:(NSString *)fmt withFormat:(NSString *)fmt
valist:(va_list)args valist:(va_list)args
level:(GTMLoggerLevel)level { level:(GTMLoggerLevel)level {
GTMLOGGER_ASSERT(dateFormatter_ != nil);
NSString *tstamp = nil; NSString *tstamp = nil;
@synchronized (dateFormatter_) { @synchronized (dateFormatter_) {
tstamp = [dateFormatter_ stringFromDate:[NSDate date]]; tstamp = [dateFormatter_ stringFromDate:[NSDate date]];
} }
return [NSString stringWithFormat:@"%@ %@[%d/%p] [lvl=%d] %@ %@", return [NSString stringWithFormat:@"%@ %@[%d/%p] [lvl=%d] %@ %@",
tstamp, pname_, pid_, pthread_self(), tstamp, pname_, pid_, pthread_self(),
level, (func ? func : @"(no func)"), level, [self prettyNameForFunc:func],
[super stringForFunc:func withFormat:fmt valist:args level:level]]; // |super| has guard for nil |fmt| and |args|
[super stringForFunc:func withFormat:fmt valist:args level:level]];
} }
@end // GTMLogStandardFormatter @end // GTMLogStandardFormatter
@ -391,14 +485,20 @@ static GTMLogger *gSharedLogger = nil;
// COV_NF_START // COV_NF_START
static BOOL IsVerboseLoggingEnabled(void) { static BOOL IsVerboseLoggingEnabled(void) {
static NSString *const kVerboseLoggingKey = @"GTMVerboseLogging"; static NSString *const kVerboseLoggingKey = @"GTMVerboseLogging";
static char *env = NULL; NSString *value = [[[NSProcessInfo processInfo] environment]
if (env == NULL) objectForKey:kVerboseLoggingKey];
env = getenv([kVerboseLoggingKey UTF8String]); if (value) {
// Emulate [NSString boolValue] for pre-10.5
if (env && env[0]) { value = [value stringByTrimmingCharactersInSet:
return (strtol(env, NULL, 10) != 0); [NSCharacterSet whitespaceAndNewlineCharacterSet]];
if ([[value uppercaseString] hasPrefix:@"Y"] ||
[[value uppercaseString] hasPrefix:@"T"] ||
[value intValue]) {
return YES;
} else {
return NO;
}
} }
return [[NSUserDefaults standardUserDefaults] boolForKey:kVerboseLoggingKey]; return [[NSUserDefaults standardUserDefaults] boolForKey:kVerboseLoggingKey];
} }
// COV_NF_END // COV_NF_END
@ -417,7 +517,7 @@ static BOOL IsVerboseLoggingEnabled(void) {
allow = NO; allow = NO;
break; break;
case kGTMLoggerLevelInfo: case kGTMLoggerLevelInfo:
allow = (IsVerboseLoggingEnabled() == YES); allow = IsVerboseLoggingEnabled();
break; break;
case kGTMLoggerLevelError: case kGTMLoggerLevelError:
allow = YES; allow = YES;
@ -443,3 +543,70 @@ static BOOL IsVerboseLoggingEnabled(void) {
} }
@end // GTMLogNoFilter @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__

File diff suppressed because it is too large Load diff

View file

@ -17,13 +17,20 @@
// //
#import "GTMSenTestCase.h" #import "GTMSenTestCase.h"
#import <unistd.h> #import <unistd.h>
#if GTM_IPHONE_SIMULATOR
#import <objc/message.h>
#endif
#import "GTMObjC2Runtime.h"
#import "GTMUnitTestDevLog.h"
#if !GTM_IPHONE_SDK #if !GTM_IPHONE_SDK
#import "GTMGarbageCollection.h" #import "GTMGarbageCollection.h"
#endif // !GTM_IPHONE_SDK #endif // !GTM_IPHONE_SDK
#if GTM_IPHONE_SDK #if GTM_IPHONE_SDK && !GTM_IPHONE_USE_SENTEST
#import <stdarg.h> #import <stdarg.h>
@interface NSException (GTMSenTestPrivateAdditions) @interface NSException (GTMSenTestPrivateAdditions)
@ -84,7 +91,7 @@
} }
NSString *reason = [NSString stringWithFormat:@"'%@' should be %s. %@", 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]; return [self failureInFile:filename atLine:lineNumber reason:reason];
} }
@ -213,6 +220,22 @@ NSString *const SenTestLineNumberKey = @"SenTestLineNumberKey";
@end @end
@implementation SenTestCase @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 { - (void)failWithException:(NSException*)exception {
[exception raise]; [exception raise];
} }
@ -220,17 +243,24 @@ NSString *const SenTestLineNumberKey = @"SenTestLineNumberKey";
- (void)setUp { - (void)setUp {
} }
- (void)performTest:(SEL)sel { - (void)performTest {
currentSelector_ = sel;
@try { @try {
[self invokeTest]; [self invokeTest];
} @catch (NSException *exception) { } @catch (NSException *exception) {
[[self class] printException:exception [[self class] printException:exception
fromTestName:NSStringFromSelector(sel)]; fromTestName:NSStringFromSelector([self selector])];
[exception raise]; [exception raise];
} }
} }
- (NSInvocation *)invocation {
return invocation_;
}
- (SEL)selector {
return [invocation_ selector];
}
+ (void)printException:(NSException *)exception fromTestName:(NSString *)name { + (void)printException:(NSException *)exception fromTestName:(NSString *)name {
NSDictionary *userInfo = [exception userInfo]; NSDictionary *userInfo = [exception userInfo];
NSString *filename = [userInfo objectForKey:SenTestFilenameKey]; NSString *filename = [userInfo objectForKey:SenTestFilenameKey];
@ -261,7 +291,17 @@ NSString *const SenTestLineNumberKey = @"SenTestLineNumberKey";
@try { @try {
[self setUp]; [self setUp];
@try { @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) { } @catch (NSException *exception) {
e = [exception retain]; e = [exception retain];
} }
@ -285,14 +325,83 @@ NSString *const SenTestLineNumberKey = @"SenTestLineNumberKey";
- (NSString *)description { - (NSString *)description {
// This matches the description OCUnit would return to you // This matches the description OCUnit would return to you
return [NSString stringWithFormat:@"-[%@ %@]", [self class], 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 @end
#endif // GTM_IPHONE_SDK #endif // GTM_IPHONE_SDK && !GTM_IPHONE_USE_SENTEST
@implementation GTMTestCase : SenTestCase @implementation GTMTestCase : SenTestCase
- (void)invokeTest { - (void)invokeTest {
NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init];
Class devLogClass = NSClassFromString(@"GTMUnitTestDevLog"); Class devLogClass = NSClassFromString(@"GTMUnitTestDevLog");
if (devLogClass) { if (devLogClass) {
[devLogClass performSelector:@selector(enableTracking)]; [devLogClass performSelector:@selector(enableTracking)];
@ -304,11 +413,26 @@ NSString *const SenTestLineNumberKey = @"SenTestLineNumberKey";
[devLogClass performSelector:@selector(verifyNoMoreLogsExpected)]; [devLogClass performSelector:@selector(verifyNoMoreLogsExpected)];
[devLogClass performSelector:@selector(disableTracking)]; [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 @end
// Leak detection // 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 // Don't want to get leaks on the iPhone Device as the device doesn't
// have 'leaks'. The simulator does though. // have 'leaks'. The simulator does though.
@ -329,13 +453,21 @@ static void _GTMRunLeaks(void) {
[exclusions appendFormat:@"-exclude \"%@\" ", exclusion]; [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 *string
= [NSString stringWithFormat:@"/usr/bin/leaks %@%d" = [NSString stringWithFormat:
@"| /usr/bin/sed -e 's/Leak: /Leaks:0: warning: Leak /'", @"LeakOut=`DYLD_ROOT_PATH='' /usr/bin/leaks %@%d` &&"
@"echo \"$LeakOut\"|/usr/bin/sed -e 's/Leak: /Leaks:0: warning: Leak /'",
exclusions, getpid()]; exclusions, getpid()];
int ret = system([string UTF8String]); int ret = system([string UTF8String]);
if (ret) { 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); __FILE__, __LINE__, ret);
fflush(stderr); fflush(stderr);
} }
@ -355,6 +487,8 @@ static __attribute__((constructor)) void _GTMInstallLeaks(void) {
fprintf(stderr, "Leak Checking Enabled\n"); fprintf(stderr, "Leak Checking Enabled\n");
fflush(stderr); fflush(stderr);
int ret = atexit(&_GTMRunLeaks); int ret = atexit(&_GTMRunLeaks);
// To avoid unused variable warning when _GTMDevAssert is stripped.
(void)ret;
_GTMDevAssert(ret == 0, _GTMDevAssert(ret == 0,
@"Unable to install _GTMRunLeaks as an atexit handler (%d)", @"Unable to install _GTMRunLeaks as an atexit handler (%d)",
errno); errno);
@ -363,4 +497,4 @@ static __attribute__((constructor)) void _GTMInstallLeaks(void) {
} }
} }
#endif // !GTM_IPHONE_DEVICE #endif // !GTM_IPHONE_DEVICE && !GTM_SUPPRESS_RUN_LEAKS_HOOK