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 <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__

View file

@ -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

View file

@ -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__

View file

@ -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

View file

@ -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