aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/mac/GTMDefines.h279
-rw-r--r--src/common/mac/GTMLogger.h128
-rw-r--r--src/common/mac/GTMLogger.m371
-rw-r--r--src/common/mac/testing/GTMSenTestCase.h904
-rw-r--r--src/common/mac/testing/GTMSenTestCase.m174
5 files changed, 1256 insertions, 600 deletions
diff --git a/src/common/mac/GTMDefines.h b/src/common/mac/GTMDefines.h
index b88193cd..b970d69c 100644
--- a/src/common/mac/GTMDefines.h
+++ b/src/common/mac/GTMDefines.h
@@ -1,4 +1,4 @@
-//
+//
// GTMDefines.h
//
// Copyright 2008 Google Inc.
@@ -6,21 +6,29 @@
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at
-//
+//
// http://www.apache.org/licenses/LICENSE-2.0
-//
+//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
//
-
+
// ============================================================================
#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
@@ -35,7 +66,7 @@
// ----------------------------------------------------------------------------
-// By setting the GTM_CONTAINERS_VALIDATION_FAILED_LOG and
+// By setting the GTM_CONTAINERS_VALIDATION_FAILED_LOG and
// GTM_CONTAINERS_VALIDATION_FAILED_ASSERT macros you can control what happens
// when a validation fails. If you implement your own validators, you may want
// to control their internals using the same macros for consistency.
@@ -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
@@ -82,12 +123,12 @@
// _GTMDevLog log some error/problem in debug builds
// _GTMDevAssert assert if conditon isn't met w/in a method/function
// in all builds.
-//
+//
// To replace this system, just provide different macro definitions in your
// prefix header. Remember, any implementation you provide *must* be thread
// safe since this could be called by anything in what ever situtation it has
// been placed in.
-//
+//
// We only define the simple macros if nothing else has defined this.
#ifndef _GTMDevLog
@@ -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__
diff --git a/src/common/mac/GTMLogger.h b/src/common/mac/GTMLogger.h
index 7d52f01e..c4fd1402 100644
--- a/src/common/mac/GTMLogger.h
+++ b/src/common/mac/GTMLogger.h
@@ -6,9 +6,9 @@
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at
-//
+//
// http://www.apache.org/licenses/LICENSE-2.0
-//
+//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -18,15 +18,15 @@
// Key Abstractions
// ----------------
-//
-// This file declares multiple classes and protocols that are used by the
+//
+// This file declares multiple classes and protocols that are used by the
// GTMLogger logging system. The 4 main abstractions used in this file are the
// following:
//
// * logger (GTMLogger) - The main logging class that users interact with. It
// has methods for logging at different levels and uses a log writer, a log
// formatter, and a log filter to get the job done.
-//
+//
// * log writer (GTMLogWriter) - Writes a given string to some log file, where
// a "log file" can be a physical file on disk, a POST over HTTP to some URL,
// or even some in-memory structure (e.g., a ring buffer).
@@ -44,7 +44,7 @@
// flexibility to dynamically enable debug logging in Release builds.
//
// This file also declares some classes to handle the common log writer, log
-// formatter, and log filter cases. Callers can also create their own writers,
+// formatter, and log filter cases. Callers can also create their own writers,
// formatters, and filters and they can even build them on top of the ones
// declared here. Keep in mind that your custom writer/formatter/filter may be
// called from multiple threads, so it must be thread-safe.
@@ -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
@@ -69,7 +63,7 @@
// sent to a GTMLogger to log a message, the message is formatted using the log
// formatter, then the log filter is consulted to see if the message should be
// logged, and if so, the message is sent to the log writer to be written out.
-//
+//
// GTMLogger is intended to be a flexible and thread-safe logging solution. Its
// flexibility comes from the fact that GTMLogger instances can be customized
// with user defined formatters, filters, and writers. And these writers,
@@ -77,7 +71,7 @@
// ways to suit the needs at hand. For example, multiple writers can be used at
// the same time, and a GTMLogger instance can even be used as another
// GTMLogger's writer. This allows for arbitrarily deep logging trees.
-//
+//
// A standard GTMLogger uses a writer that sends messages to standard out, a
// formatter that smacks a timestamp and a few other bits of interesting
// information on the message, and a filter that filters out debug messages from
@@ -85,13 +79,13 @@
// the following:
//
// 2007-12-30 10:29:24.177 myapp[4588/0xa07d0f60] [lvl=1] foo=<Foo: 0x123>
-//
+//
// The output contains the date and time of the log message, the name of the
// process followed by its process ID/thread ID, the log level at which the
// message was logged (in the previous example the level was 1:
// kGTMLoggerLevelDebug), and finally, the user-specified log message itself (in
// this case, the log message was @"foo=%@", foo).
-//
+//
// Multiple instances of GTMLogger can be created, each configured their own
// way. Though GTMLogger is not a singleton (in the GoF sense), it does provide
// access to a shared (i.e., globally accessible) GTMLogger instance. This makes
@@ -113,10 +107,10 @@
// with behavior that many developers are currently used to. Note that this
// means that GTMLoggerDebug(@"hi") will be compiled out of Release builds, but
// [[GTMLogger sharedLogger] logDebug:@"hi"] will NOT be compiled out.
-//
+//
// Standard loggers are created with the GTMLogLevelFilter log filter, which
// filters out certain log messages based on log level, and some other settings.
-//
+//
// In addition to the -logDebug:, -logInfo:, and -logError: methods defined on
// GTMLogger itself, there are also C macros that make usage of the shared
// GTMLogger instance very convenient. These macros are:
@@ -146,7 +140,7 @@
// GTMLogger class directly in order to configure the shared logger, which all
// of the code using the macros will be using. Again, this is just the typical
// situation.
-//
+//
// To be complete, there are cases where you may want to use GTMLogger directly,
// or even create separate GTMLogger instances for some reason. That's fine,
// too.
@@ -160,14 +154,14 @@
//
// GTMLoggerDebug(@"foo = %@", foo);
//
-// 2. The previous example is similar to the following. The major difference is
+// 2. The previous example is similar to the following. The major difference is
// that the previous call (example 1) will be compiled out of Release builds
// but this statement will not be compiled out.
//
// [[GTMLogger sharedLogger] logDebug:@"foo = %@", foo];
//
// 3. Send all logging output from the shared logger to a file. We do this by
-// creating an NSFileHandle for writing associated with a file, and setting
+// creating an NSFileHandle for writing associated with a file, and setting
// that file handle as the logger's writer.
//
// NSFileHandle *f = [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log"
@@ -185,12 +179,12 @@
// 5. Create a logger that writes to stdout and does NOT do any formatting to
// the log message. This might be useful, for example, when writing a help
// screen for a command-line tool to standard output.
-//
+//
// GTMLogger *logger = [GTMLogger logger];
// [logger logInfo:@"%@ version 0.1 usage", progName];
//
-// 6. Send log output to stdout AND to a log file. The trick here is that
-// NSArrays function as composite log writers, which means when an array is
+// 6. Send log output to stdout AND to a log file. The trick here is that
+// NSArrays function as composite log writers, which means when an array is
// set as the log writer, it forwards all logging messages to all of its
// contained GTMLogWriters.
//
@@ -198,7 +192,7 @@
// NSArray *writers = [NSArray arrayWithObjects:
// [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log" create:YES],
// [NSFileHandle fileHandleWithStandardOutput], nil];
-//
+//
// GTMLogger *logger = [GTMLogger standardLogger];
// [logger setWriter:writers];
// [logger logInfo:@"hi"]; // Output goes to stdout and /tmp/f.log
@@ -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,20 +272,20 @@
//
// 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);
//
// Accessors
//
-// Accessor methods for the log writer. If the log writer is set to nil,
+// Accessor methods for the log writer. If the log writer is set to nil,
// [NSFileHandle fileHandleWithStandardOutput] is used.
- (id<GTMLogWriter>)writer;
- (void)setWriter:(id<GTMLogWriter>)writer;
@@ -306,20 +304,23 @@
@end // GTMLogger
-// Helper functions that are used by the convenience GTMLogger*() macros that
+// Helper functions that are used by the convenience GTMLogger*() macros that
// 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,
@@ -365,7 +368,7 @@ typedef enum {
// now becomes a valid log writer. Log messages are written to the file handle
// with a newline appended.
@interface NSFileHandle (GTMFileHandleLogWriter) <GTMLogWriter>
-// Opens the file at |path| in append mode, and creates the file with |mode|
+// Opens the file at |path| in append mode, and creates the file with |mode|
// if it didn't previously exist.
+ (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode;
@end // NSFileHandle
@@ -379,7 +382,7 @@ typedef enum {
//
// This is useful in situations where you would like to send log output to
// multiple log writers at the same time. Simply create an NSArray of the log
-// writers you wish to use, then set the array as the "writer" for your
+// writers you wish to use, then set the array as the "writer" for your
// GTMLogger instance.
@interface NSArray (GTMArrayCompositeLogWriter) <GTMLogWriter>
@end // GTMArrayCompositeLogWriter
@@ -390,7 +393,7 @@ typedef enum {
//
// This is useful when you want to configure a logger to log to a specific
// writer with a specific formatter and/or filter. But you want to also compose
-// that with a different log writer that may have its own formatter and/or
+// that with a different log writer that may have its own formatter and/or
// filter.
@interface GTMLogger (GTMLoggerLogWriter) <GTMLogWriter>
@end // GTMLoggerLogWriter
@@ -407,14 +410,18 @@ 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
-// A basic log formatter that formats a string the same way that NSLog (or
+// A basic log formatter that formats a string the same way that NSLog (or
// 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
+
diff --git a/src/common/mac/GTMLogger.m b/src/common/mac/GTMLogger.m
index de941d2e..4b40747b 100644
--- a/src/common/mac/GTMLogger.m
+++ b/src/common/mac/GTMLogger.m
@@ -6,9 +6,9 @@
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at
-//
+//
// http://www.apache.org/licenses/LICENSE-2.0
-//
+//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -24,39 +24,30 @@
#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
+// Reference to the shared GTMLogger instance. This is not a singleton, it's
// just an easy reference to one shared instance.
static GTMLogger *gSharedLogger = nil;
@implementation GTMLogger
-// Returns a pointer to the shared logger instance. If none exists, a standard
+// Returns a pointer to the shared logger instance. If none exists, a standard
// logger is created and returned.
+ (id)sharedLogger {
@synchronized(self) {
if (gSharedLogger == nil) {
gSharedLogger = [[self standardLogger] retain];
}
- GTMLOGGER_ASSERT(gSharedLogger != nil);
}
return [[gSharedLogger retain] autorelease];
}
@@ -69,24 +60,85 @@ static GTMLogger *gSharedLogger = nil;
}
+ (id)standardLogger {
- id<GTMLogWriter> writer = [NSFileHandle fileHandleWithStandardOutput];
- id<GTMLogFormatter> fr = [[[GTMLogStandardFormatter alloc] init] autorelease];
- id<GTMLogFilter> filter = [[[GTMLogLevelFilter alloc] init] autorelease];
- return [self loggerWithWriter:writer formatter:fr filter:filter];
+ // Don't trust NSFileHandle not to throw
+ @try {
+ id<GTMLogWriter> writer = [NSFileHandle fileHandleWithStandardOutput];
+ 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 me = [self standardLogger];
- [me setWriter:[NSFileHandle fileHandleWithStandardError]];
- return me;
+ // 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 {
- NSFileHandle *fh = [NSFileHandle fileHandleForLoggingAtPath:path mode:0644];
- if (fh == nil) return nil;
- id me = [self standardLogger];
- [me setWriter:fh];
- return me;
+ @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
@@ -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];
- [formatter_ release];
- [filter_ 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_ = [[NSFileHandle fileHandleWithStandardOutput] retain];
- else
+ writer_ = nil;
+ if (writer == nil) {
+ // Try to use stdout, but don't trust NSFileHandle
+ @try {
+ writer_ = [[NSFileHandle fileHandleWithStandardOutput] retain];
+ }
+ @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_ = [[GTMLogBasicFormatter alloc] init];
- else
+ formatter_ = nil;
+ if (formatter == nil) {
+ @try {
+ formatter_ = [[GTMLogBasicFormatter alloc] init];
+ }
+ @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_ = [[GTMLogNoFilter alloc] init];
- else
+ filter_ = nil;
+ if (filter == nil) {
+ @try {
+ filter_ = [[GTMLogNoFilter alloc] init];
+ }
+ @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,24 +307,26 @@ static GTMLogger *gSharedLogger = nil;
@end // GTMLoggerMacroHelpers
-
@implementation GTMLogger (PrivateMethods)
- (void)logInternalFunc:(const char *)func
format:(NSString *)fmt
- valist:(va_list)args
+ valist:(va_list)args
level:(GTMLoggerLevel)level {
- GTMLOGGER_ASSERT(formatter_ != nil);
- GTMLOGGER_ASSERT(filter_ != nil);
- GTMLOGGER_ASSERT(writer_ != nil);
-
- NSString *fname = func ? [NSString stringWithUTF8String:func] : nil;
- NSString *msg = [formatter_ stringForFunc:fname
- withFormat:fmt
- valist:args
- level:level];
- if (msg && [filter_ filterAllowsMessage:msg level:level])
- [writer_ logMessage:msg level:level];
+ // 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
+ valist:args
+ level:level];
+ if (msg && [filter_ filterAllowsMessage:msg level:level])
+ [writer_ logMessage:msg level:level];
+ }
+ @catch (id e) {
+ // Ignored
+ }
}
@end // PrivateMethods
@@ -278,8 +347,16 @@ static GTMLogger *gSharedLogger = nil;
- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
@synchronized(self) {
- NSString *line = [NSString stringWithFormat:@"%@\n", msg];
- [self writeData:[line dataUsingEncoding:NSUTF8StringEncoding]];
+ // 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
+ }
}
}
@@ -306,18 +383,18 @@ static GTMLogger *gSharedLogger = nil;
- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
switch (level) {
case kGTMLoggerLevelDebug:
- [self logDebug:@"%@", msg];
+ [self logDebug:@"%@", msg];
break;
case kGTMLoggerLevelInfo:
[self logInfo:@"%@", msg];
break;
- case kGTMLoggerLevelError:
+ case kGTMLoggerLevelError:
[self logError:@"%@", msg];
break;
case kGTMLoggerLevelAssert:
[self logAssert:@"%@", msg];
break;
- default:
+ default:
// Ignore the message.
break;
}
@@ -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
+ 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|
- // contains a '%', and if not, simply return 'fmt'.
- CFStringRef cfmsg = NULL;
- cfmsg = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault,
- NULL, // format options
- (CFStringRef)fmt,
- args);
- return GTMCFAutorelease(cfmsg);
+ // Performance note: We may want to do a quick check here to see if |fmt|
+ // contains a '%', and if not, simply return 'fmt'.
+ 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;
}
@@ -367,17 +461,17 @@ static GTMLogger *gSharedLogger = nil;
- (NSString *)stringForFunc:(NSString *)func
withFormat:(NSString *)fmt
- valist:(va_list)args
+ 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)"),
- [super stringForFunc:func withFormat:fmt valist:args level:level]];
+ tstamp, pname_, pid_, pthread_self(),
+ level, [self prettyNameForFunc:func],
+ // |super| has guard for nil |fmt| and |args|
+ [super stringForFunc:func withFormat:fmt valist:args level:level]];
}
@end // GTMLogStandardFormatter
@@ -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
@@ -409,15 +509,15 @@ static BOOL IsVerboseLoggingEnabled(void) {
#if DEBUG
return YES;
#endif
-
+
BOOL allow = YES;
-
+
switch (level) {
case kGTMLoggerLevelDebug:
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__
+
diff --git a/src/common/mac/testing/GTMSenTestCase.h b/src/common/mac/testing/GTMSenTestCase.h
index d425f597..ce3d9022 100644
--- a/src/common/mac/testing/GTMSenTestCase.h
+++ b/src/common/mac/testing/GTMSenTestCase.h
@@ -6,9 +6,9 @@
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at
-//
+//
// http://www.apache.org/licenses/LICENSE-2.0
-//
+//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -22,44 +22,60 @@
// Copyright (c) 1997-2005, Sen:te (Sente SA). All rights reserved.
//
// Use of this source code is governed by the following license:
-//
-// Redistribution and use in source and binary forms, with or without modification,
+//
+// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
-//
-// (1) Redistributions of source code must retain the above copyright notice,
+//
+// (1) Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
-//
-// (2) Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
+//
+// (2) Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-// IN NO EVENT SHALL Sente SA OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
-// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL Sente SA OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
+//
// Note: this license is equivalent to the FreeBSD license.
-//
+//
// This notice may not be removed from this file.
-// Some extra test case macros that would have been convenient for SenTestingKit
-// to provide. I didn't stick GTM in front of the Macro names, so that they would
+// Some extra test case macros that would have been convenient for SenTestingKit
+// to provide. I didn't stick GTM in front of the Macro names, so that they would
// be easy to remember.
#import "GTMDefines.h"
-#if (!GTM_IPHONE_SDK)
+#if (!GTM_IPHONE_SDK) || (GTM_IPHONE_USE_SENTEST)
#import <SenTestingKit/SenTestingKit.h>
#else
#import <Foundation/Foundation.h>
+#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
@@ -68,25 +84,24 @@ NSString *STComposeString(NSString *, ...);
// ...: A variable number of arguments to the format string. Can be absent.
#define STAssertNoErr(a1, description, ...) \
do { \
- @try {\
+ @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__] \
- atLine:__LINE__ \
- withDescription:_expression]]; \
+ 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:@"%@", STComposeString(description, ##__VA_ARGS__)])]; \
} \
- }\
- @catch (id anException) {\
+ } \
+ @catch (id anException) { \
[self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == noErr fails", #a1] \
exception:anException \
inFile:[NSString stringWithUTF8String:__FILE__] \
atLine:__LINE__ \
- withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
- }\
+ withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
} while(0)
// Generates a failure when a1 != a2
@@ -98,26 +113,25 @@ do { \
// ...: A variable number of arguments to the format string. Can be absent.
#define STAssertErr(a1, a2, description, ...) \
do { \
- @try {\
+ @try { \
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__] \
- atLine:__LINE__ \
- withDescription:_expression]]; \
+ 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:@"%@", STComposeString(description, ##__VA_ARGS__)])]; \
} \
- }\
- @catch (id anException) {\
+ } \
+ @catch (id anException) { \
[self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == (%s) fails", #a1, #a2] \
exception:anException \
inFile:[NSString stringWithUTF8String:__FILE__] \
atLine:__LINE__ \
- withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
- }\
+ withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
} while(0)
@@ -129,25 +143,24 @@ do { \
// ...: A variable number of arguments to the format string. Can be absent.
#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__] \
- atLine:__LINE__ \
- withDescription:_expression]]; \
+ @try { \
+ __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:@"%@", STComposeString(description, ##__VA_ARGS__)])]; \
} \
- }\
- @catch (id anException) {\
+ } \
+ @catch (id anException) { \
[self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) != NULL fails", #a1] \
exception:anException \
inFile:[NSString stringWithUTF8String:__FILE__] \
atLine:__LINE__ \
- withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
- }\
+ withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
} while(0)
// Generates a failure when a1 is not NULL
@@ -158,28 +171,27 @@ do { \
// ...: A variable number of arguments to the format string. Can be absent.
#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__] \
- atLine:__LINE__ \
- withDescription:_expression]]; \
+ @try { \
+ __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:@"%@", STComposeString(description, ##__VA_ARGS__)])]; \
} \
- }\
- @catch (id anException) {\
+ } \
+ @catch (id anException) { \
[self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == NULL fails", #a1] \
exception:anException \
inFile:[NSString stringWithUTF8String:__FILE__] \
atLine:__LINE__ \
- withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
- }\
+ withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
} while(0)
-// Generates a failure when a1 is equal to a2. This test is for C scalars,
+// Generates a failure when a1 is equal to a2. This test is for C scalars,
// structs and unions.
// Args:
// a1: argument 1
@@ -189,34 +201,33 @@ do { \
// ...: A variable number of arguments to the format string. Can be absent.
#define STAssertNotEquals(a1, a2, description, ...) \
do { \
- @try {\
- if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \
+ @try { \
+ 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__] \
- atLine:__LINE__ \
- withDescription:_expression]]; \
- } \
+ NSString *_expression = [NSString stringWithFormat:@"((%s) != (%s))", #a1, #a2]; \
+ [self failWithException:([NSException failureInCondition:_expression \
+ isTrue:NO \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)])]; \
+ }\
} \
} \
- @catch (id anException) {\
+ @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)
// Generates a failure when a1 is equal to a2. This test is for objects.
@@ -226,32 +237,30 @@ 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__] \
- atLine:__LINE__ \
- withDescription:_expression]]; \
+ 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__)])]; \
}\
@catch (id anException) {\
- [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) != (%s)", #a1, #a2] \
- exception:anException \
- inFile:[NSString stringWithUTF8String:__FILE__] \
- atLine:__LINE__ \
- withDescription:STComposeString(desc, ##__VA_ARGS__)]]; \
+ [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)
-// Generates a failure when a1 is not 'op' to a2. This test is for C scalars.
+// Generates a failure when a1 is not 'op' to a2. This test is for C scalars.
// Args:
// a1: argument 1
// a2: argument 2
@@ -261,38 +270,37 @@ do { \
// ...: A variable number of arguments to the format string. Can be absent.
#define STAssertOperation(a1, a2, op, description, ...) \
do { \
- @try {\
- if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \
+ @try { \
+ 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__] \
- atLine:__LINE__ \
- withDescription:_expression]]; \
+ 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:@"%@", STComposeString(description, ##__VA_ARGS__)])]; \
} \
} \
} \
- @catch (id anException) {\
+ @catch (id anException) { \
[self failWithException:[NSException \
failureInRaise:[NSString stringWithFormat:@"(%s) %s (%s)", #a1, #op, #a2] \
exception:anException \
inFile:[NSString stringWithUTF8String:__FILE__] \
atLine:__LINE__ \
- withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
- }\
+ withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
} while(0)
-// Generates a failure when a1 is not > a2. This test is for C scalars.
+// Generates a failure when a1 is not > a2. This test is for C scalars.
// Args:
// a1: argument 1
// a2: argument 2
@@ -303,7 +311,7 @@ do { \
#define STAssertGreaterThan(a1, a2, description, ...) \
STAssertOperation(a1, a2, >, description, ##__VA_ARGS__)
-// Generates a failure when a1 is not >= a2. This test is for C scalars.
+// Generates a failure when a1 is not >= a2. This test is for C scalars.
// Args:
// a1: argument 1
// a2: argument 2
@@ -314,7 +322,7 @@ do { \
#define STAssertGreaterThanOrEqual(a1, a2, description, ...) \
STAssertOperation(a1, a2, >=, description, ##__VA_ARGS__)
-// Generates a failure when a1 is not < a2. This test is for C scalars.
+// Generates a failure when a1 is not < a2. This test is for C scalars.
// Args:
// a1: argument 1
// a2: argument 2
@@ -325,7 +333,7 @@ do { \
#define STAssertLessThan(a1, a2, description, ...) \
STAssertOperation(a1, a2, <, description, ##__VA_ARGS__)
-// Generates a failure when a1 is not <= a2. This test is for C scalars.
+// Generates a failure when a1 is not <= a2. This test is for C scalars.
// Args:
// a1: argument 1
// a2: argument 2
@@ -349,26 +357,26 @@ do { \
// ...: A variable number of arguments to the format string. Can be absent.
#define STAssertEqualStrings(a1, a2, description, ...) \
do { \
- @try {\
+ @try { \
id a1value = (a1); \
id a2value = (a2); \
if (a1value == a2value) continue; \
if ([a1value isKindOfClass:[NSString class]] && \
[a2value isKindOfClass:[NSString class]] && \
[a1value compare:a2value options:0] == NSOrderedSame) continue; \
- [self failWithException:[NSException failureInEqualityBetweenObject: a1value \
- andObject: a2value \
- inFile: [NSString stringWithUTF8String:__FILE__] \
- atLine: __LINE__ \
- withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
- }\
- @catch (id anException) {\
- [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \
+ [self failWithException:[NSException failureInEqualityBetweenObject:a1value \
+ andObject:a2value \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ 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)
// Generates a failure when string a1 is equal to string a2. This call
@@ -384,25 +392,25 @@ do { \
// ...: A variable number of arguments to the format string. Can be absent.
#define STAssertNotEqualStrings(a1, a2, description, ...) \
do { \
- @try {\
+ @try { \
id a1value = (a1); \
id a2value = (a2); \
if ([a1value isKindOfClass:[NSString class]] && \
[a2value isKindOfClass:[NSString class]] && \
[a1value compare:a2value options:0] != NSOrderedSame) continue; \
- [self failWithException:[NSException failureInEqualityBetweenObject: a1value \
- andObject: a2value \
- inFile: [NSString stringWithUTF8String:__FILE__] \
- atLine: __LINE__ \
- withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
- }\
- @catch (id anException) {\
- [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) != (%s)", #a1, #a2] \
+ [self failWithException:[NSException failureInEqualityBetweenObject:a1value \
+ andObject:a2value \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ 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)
// Generates a failure when c-string a1 is not equal to c-string a2.
@@ -414,24 +422,24 @@ do { \
// ...: A variable number of arguments to the format string. Can be absent.
#define STAssertEqualCStrings(a1, a2, description, ...) \
do { \
- @try {\
+ @try { \
const char* a1value = (a1); \
const char* a2value = (a2); \
if (a1value == a2value) continue; \
if (strcmp(a1value, a2value) == 0) continue; \
- [self failWithException:[NSException failureInEqualityBetweenObject: [NSString stringWithUTF8String:a1value] \
- andObject: [NSString stringWithUTF8String:a2value] \
- inFile: [NSString stringWithUTF8String:__FILE__] \
- atLine: __LINE__ \
- withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
- }\
- @catch (id anException) {\
- [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \
+ [self failWithException:[NSException failureInEqualityBetweenObject:[NSString stringWithUTF8String:a1value] \
+ andObject:[NSString stringWithUTF8String:a2value] \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ 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)
// Generates a failure when c-string a1 is equal to c-string a2.
@@ -443,30 +451,97 @@ do { \
// ...: A variable number of arguments to the format string. Can be absent.
#define STAssertNotEqualCStrings(a1, a2, description, ...) \
do { \
- @try {\
+ @try { \
const char* a1value = (a1); \
const char* a2value = (a2); \
if (strcmp(a1value, a2value) != 0) continue; \
- [self failWithException:[NSException failureInEqualityBetweenObject: [NSString stringWithUTF8String:a1value] \
- andObject: [NSString stringWithUTF8String:a2value] \
- inFile: [NSString stringWithUTF8String:__FILE__] \
- atLine: __LINE__ \
- withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
- }\
- @catch (id anException) {\
- [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) != (%s)", #a1, #a2] \
+ [self failWithException:[NSException failureInEqualityBetweenObject:[NSString stringWithUTF8String:a1value] \
+ andObject:[NSString stringWithUTF8String:a2value] \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ 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)
+
+/*" 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)
-#if GTM_IPHONE_SDK
+#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
- (or one is nil and the other is not).
+/*" Generates a failure when !{ [a1 isEqualTo:a2] } is false
+ (or one is nil and the other is not).
_{a1 The object on the left.}
_{a2 The object on the right.}
_{description A format string as in the printf() function. Can be nil or
@@ -475,26 +550,26 @@ do { \
"*/
#define STAssertEqualObjects(a1, a2, description, ...) \
do { \
- @try {\
+ @try { \
id a1value = (a1); \
id a2value = (a2); \
if (a1value == a2value) continue; \
- if ( (@encode(__typeof__(a1value)) == @encode(id)) && \
- (@encode(__typeof__(a2value)) == @encode(id)) && \
- [(id)a1value isEqual: (id)a2value] ) continue; \
- [self failWithException:[NSException failureInEqualityBetweenObject: a1value \
- andObject: a2value \
- inFile: [NSString stringWithUTF8String:__FILE__] \
- atLine: __LINE__ \
- withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
- }\
- @catch (id anException) {\
- [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \
+ 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__)]]; \
+ } \
+ @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)
@@ -508,40 +583,40 @@ do { \
"*/
#define STAssertEquals(a1, a2, description, ...) \
do { \
- @try {\
- if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \
+ @try { \
+ 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))]; \
+ NSValue *a1encoded = [NSValue value:&a1value withObjCType:@encode(__typeof__(a1))]; \
+ NSValue *a2encoded = [NSValue value:&a2value withObjCType:@encode(__typeof__(a2))]; \
if (![a1encoded isEqualToValue:a2encoded]) { \
- [self failWithException:[NSException failureInEqualityBetweenValue: a1encoded \
- andValue: a2encoded \
- withAccuracy: nil \
- inFile: [NSString stringWithUTF8String:__FILE__] \
- atLine: __LINE__ \
- withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ [self failWithException:[NSException failureInEqualityBetweenValue:a1encoded \
+ andValue:a2encoded \
+ withAccuracy:nil \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
} \
} \
} \
- @catch (id anException) {\
- [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \
+ @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)
#define STAbsoluteDifference(left,right) (MAX(left,right)-MIN(left,right))
-/*" Generates a failure when a1 is not equal to a2 within + or - accuracy is false.
- This test is for scalars such as floats and doubles where small differences
+/*" Generates a failure when a1 is not equal to a2 within + or - accuracy is false.
+ This test is for scalars such as floats and doubles where small differences
could make these items not exactly equal, but also works for all scalars.
_{a1 The scalar on the left.}
_{a2 The scalar on the right.}
@@ -554,11 +629,11 @@ do { \
#define STAssertEqualsWithAccuracy(a1, a2, accuracy, description, ...) \
do { \
- @try {\
- if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \
+ @try { \
+ 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); \
@@ -567,35 +642,35 @@ do { \
NSValue *a1encoded = [NSValue value:&a1value withObjCType:@encode(__typeof__(a1))]; \
NSValue *a2encoded = [NSValue value:&a2value withObjCType:@encode(__typeof__(a2))]; \
NSValue *accuracyencoded = [NSValue value:&accuracyvalue withObjCType:@encode(__typeof__(accuracy))]; \
- [self failWithException:[NSException failureInEqualityBetweenValue: a1encoded \
- andValue: a2encoded \
- withAccuracy: accuracyencoded \
- inFile: [NSString stringWithUTF8String:__FILE__] \
- atLine: __LINE__ \
- withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ [self failWithException:[NSException failureInEqualityBetweenValue:a1encoded \
+ andValue:a2encoded \
+ withAccuracy:accuracyencoded \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
} \
} \
} \
- @catch (id anException) {\
- [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \
+ @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)
-/*" Generates a failure unconditionally.
+/*" Generates a failure unconditionally.
_{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 STFail(description, ...) \
-[self failWithException:[NSException failureInFile: [NSString stringWithUTF8String:__FILE__] \
- atLine: __LINE__ \
- withDescription: STComposeString(description, ##__VA_ARGS__)]]
+[self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]
@@ -607,25 +682,25 @@ do { \
"*/
#define STAssertNil(a1, description, ...) \
do { \
- @try {\
+ @try { \
id a1value = (a1); \
if (a1value != nil) { \
- NSString *_a1 = [NSString stringWithUTF8String: #a1]; \
+ NSString *_a1 = [NSString stringWithUTF8String:#a1]; \
NSString *_expression = [NSString stringWithFormat:@"((%@) == nil)", _a1]; \
- [self failWithException:[NSException failureInCondition: _expression \
- isTrue: NO \
- inFile: [NSString stringWithUTF8String:__FILE__] \
- atLine: __LINE__ \
- withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ [self failWithException:[NSException failureInCondition:_expression \
+ isTrue:NO \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
} \
- }\
- @catch (id anException) {\
- [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == nil fails", #a1] \
+ } \
+ @catch (id anException) { \
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == nil fails", #a1] \
exception:anException \
inFile:[NSString stringWithUTF8String:__FILE__] \
atLine:__LINE__ \
- withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
- }\
+ withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
} while(0)
@@ -637,29 +712,29 @@ do { \
"*/
#define STAssertNotNil(a1, description, ...) \
do { \
- @try {\
+ @try { \
id a1value = (a1); \
if (a1value == nil) { \
- NSString *_a1 = [NSString stringWithUTF8String: #a1]; \
+ NSString *_a1 = [NSString stringWithUTF8String:#a1]; \
NSString *_expression = [NSString stringWithFormat:@"((%@) != nil)", _a1]; \
- [self failWithException:[NSException failureInCondition: _expression \
- isTrue: NO \
- inFile: [NSString stringWithUTF8String:__FILE__] \
- atLine: __LINE__ \
- withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ [self failWithException:[NSException failureInCondition:_expression \
+ isTrue:NO \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
} \
- }\
- @catch (id anException) {\
- [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) != nil fails", #a1] \
+ } \
+ @catch (id anException) { \
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) != nil fails", #a1] \
exception:anException \
inFile:[NSString stringWithUTF8String:__FILE__] \
atLine:__LINE__ \
- withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
- }\
+ withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
} while(0)
-/*" Generates a failure when expression evaluates to false.
+/*" Generates a failure when expression evaluates to false.
_{expr The expression that is tested.}
_{description A format string as in the printf() function. Can be nil or
an empty string but must be present.}
@@ -667,20 +742,20 @@ do { \
"*/
#define STAssertTrue(expr, description, ...) \
do { \
- BOOL _evaluatedExpression = (expr);\
- if (!_evaluatedExpression) {\
- NSString *_expression = [NSString stringWithUTF8String: #expr];\
- [self failWithException:[NSException failureInCondition: _expression \
- isTrue: NO \
- inFile: [NSString stringWithUTF8String:__FILE__] \
- atLine: __LINE__ \
- withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ BOOL _evaluatedExpression = (expr); \
+ if (!_evaluatedExpression) { \
+ NSString *_expression = [NSString stringWithUTF8String:#expr]; \
+ [self failWithException:[NSException failureInCondition:_expression \
+ isTrue:NO \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
} \
} while (0)
-/*" Generates a failure when expression evaluates to false and in addition will
- generate error messages if an exception is encountered.
+/*" Generates a failure when expression evaluates to false and in addition will
+ generate error messages if an exception is encountered.
_{expr The expression that is tested.}
_{description A format string as in the printf() function. Can be nil or
an empty string but must be present.}
@@ -688,28 +763,28 @@ do { \
"*/
#define STAssertTrueNoThrow(expr, description, ...) \
do { \
- @try {\
- BOOL _evaluatedExpression = (expr);\
- if (!_evaluatedExpression) {\
- NSString *_expression = [NSString stringWithUTF8String: #expr];\
- [self failWithException:[NSException failureInCondition: _expression \
- isTrue: NO \
- inFile: [NSString stringWithUTF8String:__FILE__] \
- atLine: __LINE__ \
- withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ @try { \
+ BOOL _evaluatedExpression = (expr); \
+ if (!_evaluatedExpression) { \
+ NSString *_expression = [NSString stringWithUTF8String:#expr]; \
+ [self failWithException:[NSException failureInCondition:_expression \
+ isTrue:NO \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
} \
} \
- @catch (id anException) {\
- [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) ", #expr] \
+ @catch (id anException) { \
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) ", #expr] \
exception:anException \
inFile:[NSString stringWithUTF8String:__FILE__] \
atLine:__LINE__ \
- withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
- }\
+ withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
} while (0)
-/*" Generates a failure when the expression evaluates to true.
+/*" Generates a failure when the expression evaluates to true.
_{expr The expression that is tested.}
_{description A format string as in the printf() function. Can be nil or
an empty string but must be present.}
@@ -717,19 +792,19 @@ do { \
"*/
#define STAssertFalse(expr, description, ...) \
do { \
- BOOL _evaluatedExpression = (expr);\
- if (_evaluatedExpression) {\
- NSString *_expression = [NSString stringWithUTF8String: #expr];\
- [self failWithException:[NSException failureInCondition: _expression \
- isTrue: YES \
- inFile: [NSString stringWithUTF8String:__FILE__] \
- atLine: __LINE__ \
- withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ BOOL _evaluatedExpression = (expr); \
+ if (_evaluatedExpression) { \
+ NSString *_expression = [NSString stringWithUTF8String:#expr]; \
+ [self failWithException:[NSException failureInCondition:_expression \
+ isTrue:YES \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
} \
} while (0)
-/*" Generates a failure when the expression evaluates to true and in addition
+/*" Generates a failure when the expression evaluates to true and in addition
will generate error messages if an exception is encountered.
_{expr The expression that is tested.}
_{description A format string as in the printf() function. Can be nil or
@@ -738,28 +813,28 @@ do { \
"*/
#define STAssertFalseNoThrow(expr, description, ...) \
do { \
- @try {\
- BOOL _evaluatedExpression = (expr);\
- if (_evaluatedExpression) {\
- NSString *_expression = [NSString stringWithUTF8String: #expr];\
- [self failWithException:[NSException failureInCondition: _expression \
- isTrue: YES \
- inFile: [NSString stringWithUTF8String:__FILE__] \
- atLine: __LINE__ \
- withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ @try { \
+ BOOL _evaluatedExpression = (expr); \
+ if (_evaluatedExpression) { \
+ NSString *_expression = [NSString stringWithUTF8String:#expr]; \
+ [self failWithException:[NSException failureInCondition:_expression \
+ isTrue:YES \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
} \
} \
- @catch (id anException) {\
- [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"!(%s) ", #expr] \
+ @catch (id anException) { \
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"!(%s) ", #expr] \
exception:anException \
inFile:[NSString stringWithUTF8String:__FILE__] \
atLine:__LINE__ \
- withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
- }\
+ withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
} while (0)
-/*" Generates a failure when expression does not throw an exception.
+/*" Generates a failure when expression does not throw an exception.
_{expression The expression that is evaluated.}
_{description A format string as in the printf() function. Can be nil or
an empty string but must be present.}
@@ -768,21 +843,21 @@ do { \
#define STAssertThrows(expr, description, ...) \
do { \
@try { \
- (expr);\
+ (expr); \
} \
@catch (id anException) { \
continue; \
- }\
- [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \
- exception: nil \
- inFile: [NSString stringWithUTF8String:__FILE__] \
- atLine: __LINE__ \
- withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
+ [self failWithException:[NSException failureInRaise:[NSString stringWithUTF8String:#expr] \
+ exception:nil \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
} while (0)
-/*" Generates a failure when expression does not throw an exception of a
- specific class.
+/*" Generates a failure when expression does not throw an exception of a
+ specific class.
_{expression The expression that is evaluated.}
_{specificException The specified class of the exception.}
_{description A format string as in the printf() function. Can be nil or
@@ -792,32 +867,32 @@ do { \
#define STAssertThrowsSpecific(expr, specificException, description, ...) \
do { \
@try { \
- (expr);\
+ (expr); \
} \
@catch (specificException *anException) { \
continue; \
- }\
- @catch (id anException) {\
- NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\
- [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \
- exception: anException \
- inFile: [NSString stringWithUTF8String:__FILE__] \
- atLine: __LINE__ \
- withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \
+ } \
+ @catch (id anException) { \
+ NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description); \
+ [self failWithException:[NSException failureInRaise:[NSString stringWithUTF8String:#expr] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:@"%@", STComposeString(_descrip, ##__VA_ARGS__)]]; \
continue; \
- }\
- NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\
- [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \
- exception: nil \
- inFile: [NSString stringWithUTF8String:__FILE__] \
- atLine: __LINE__ \
- withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \
+ } \
+ NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description); \
+ [self failWithException:[NSException failureInRaise:[NSString stringWithUTF8String:#expr] \
+ exception:nil \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:@"%@", STComposeString(_descrip, ##__VA_ARGS__)]]; \
} while (0)
-/*" Generates a failure when expression does not throw an exception of a
+/*" Generates a failure when expression does not throw an exception of a
specific class with a specific name. Useful for those frameworks like
- AppKit or Foundation that throw generic NSException w/specific names
+ AppKit or Foundation that throw generic NSException w/specific names
(NSInvalidArgumentException, etc).
_{expression The expression that is evaluated.}
_{specificException The specified class of the exception.}
@@ -830,40 +905,40 @@ do { \
#define STAssertThrowsSpecificNamed(expr, specificException, aName, description, ...) \
do { \
@try { \
- (expr);\
+ (expr); \
} \
@catch (specificException *anException) { \
- if ([aName isEqualToString: [anException name]]) continue; \
- NSString *_descrip = STComposeString(@"(Expected exception: %@ (name: %@)) %@", NSStringFromClass([specificException class]), aName, description);\
+ if ([aName isEqualToString:[anException name]]) continue; \
+ NSString *_descrip = STComposeString(@"(Expected exception: %@ (name: %@)) %@", NSStringFromClass([specificException class]), aName, description); \
[self failWithException: \
- [NSException failureInRaise: [NSString stringWithUTF8String:#expr] \
- exception: anException \
- inFile: [NSString stringWithUTF8String:__FILE__] \
- atLine: __LINE__ \
- withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \
+ [NSException failureInRaise:[NSString stringWithUTF8String:#expr] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:@"%@", STComposeString(_descrip, ##__VA_ARGS__)]]; \
continue; \
- }\
- @catch (id anException) {\
- NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\
+ } \
+ @catch (id anException) { \
+ NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description); \
[self failWithException: \
- [NSException failureInRaise: [NSString stringWithUTF8String:#expr] \
- exception: anException \
- inFile: [NSString stringWithUTF8String:__FILE__] \
- atLine: __LINE__ \
- withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \
+ [NSException failureInRaise:[NSString stringWithUTF8String:#expr] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:@"%@", STComposeString(_descrip, ##__VA_ARGS__)]]; \
continue; \
- }\
- NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\
+ } \
+ NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description); \
[self failWithException: \
- [NSException failureInRaise: [NSString stringWithUTF8String:#expr] \
- exception: nil \
- inFile: [NSString stringWithUTF8String:__FILE__] \
- atLine: __LINE__ \
- withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \
+ [NSException failureInRaise:[NSString stringWithUTF8String:#expr] \
+ exception:nil \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:@"%@", STComposeString(_descrip, ##__VA_ARGS__)]]; \
} while (0)
-/*" Generates a failure when expression does throw an exception.
+/*" Generates a failure when expression does throw an exception.
_{expression The expression that is evaluated.}
_{description A format string as in the printf() function. Can be nil or
an empty string but must be present.}
@@ -872,15 +947,15 @@ do { \
#define STAssertNoThrow(expr, description, ...) \
do { \
@try { \
- (expr);\
+ (expr); \
} \
@catch (id anException) { \
- [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \
- exception: anException \
- inFile: [NSString stringWithUTF8String:__FILE__] \
- atLine: __LINE__ \
- withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
- }\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithUTF8String:#expr] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
} while (0)
@@ -895,24 +970,24 @@ do { \
#define STAssertNoThrowSpecific(expr, specificException, description, ...) \
do { \
@try { \
- (expr);\
+ (expr); \
} \
@catch (specificException *anException) { \
- [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \
- exception: anException \
- inFile: [NSString stringWithUTF8String:__FILE__] \
- atLine: __LINE__ \
- withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
- }\
- @catch (id anythingElse) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithUTF8String:#expr] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:@"%@", STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
+ @catch (id anythingElse) { \
; \
- }\
+ } \
} while (0)
-/*" Generates a failure when expression does throw an exception of a
+/*" Generates a failure when expression does throw an exception of a
specific class with a specific name. Useful for those frameworks like
- AppKit or Foundation that throw generic NSException w/specific names
+ AppKit or Foundation that throw generic NSException w/specific names
(NSInvalidArgumentException, etc).
_{expression The expression that is evaluated.}
_{specificException The specified class of the exception.}
@@ -925,69 +1000,77 @@ do { \
#define STAssertNoThrowSpecificNamed(expr, specificException, aName, description, ...) \
do { \
@try { \
- (expr);\
+ (expr); \
} \
@catch (specificException *anException) { \
- if ([aName isEqualToString: [anException name]]) { \
- NSString *_descrip = STComposeString(@"(Expected exception: %@ (name: %@)) %@", NSStringFromClass([specificException class]), aName, description);\
+ if ([aName isEqualToString:[anException name]]) { \
+ NSString *_descrip = STComposeString(@"(Expected exception: %@ (name: %@)) %@", NSStringFromClass([specificException class]), aName, description); \
[self failWithException: \
- [NSException failureInRaise: [NSString stringWithUTF8String:#expr] \
- exception: anException \
- inFile: [NSString stringWithUTF8String:__FILE__] \
- atLine: __LINE__ \
- withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \
+ [NSException failureInRaise:[NSString stringWithUTF8String:#expr] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:@"%@", STComposeString(_descrip, ##__VA_ARGS__)]]; \
} \
continue; \
- }\
- @catch (id anythingElse) {\
+ } \
+ @catch (id anythingElse) { \
; \
- }\
+ } \
} while (0)
@interface NSException (GTMSenTestAdditions)
-+ (NSException *)failureInFile:(NSString *)filename
- atLine:(int)lineNumber
- withDescription:(NSString *)formatString, ...;
-+ (NSException *)failureInCondition:(NSString *)condition
- isTrue:(BOOL)isTrue
- inFile:(NSString *)filename
- atLine:(int)lineNumber
- withDescription:(NSString *)formatString, ...;
++ (NSException *)failureInFile:(NSString *)filename
+ atLine:(int)lineNumber
+ withDescription:(NSString *)formatString, ... NS_FORMAT_FUNCTION(3, 4);
++ (NSException *)failureInCondition:(NSString *)condition
+ isTrue:(BOOL)isTrue
+ inFile:(NSString *)filename
+ atLine:(int)lineNumber
+ withDescription:(NSString *)formatString, ... NS_FORMAT_FUNCTION(5, 6);
+ (NSException *)failureInEqualityBetweenObject:(id)left
andObject:(id)right
inFile:(NSString *)filename
atLine:(int)lineNumber
- withDescription:(NSString *)formatString, ...;
-+ (NSException *)failureInEqualityBetweenValue:(NSValue *)left
- andValue:(NSValue *)right
- withAccuracy:(NSValue *)accuracy
- inFile:(NSString *)filename
+ 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, ...;
-+ (NSException *)failureInRaise:(NSString *)expression
- inFile:(NSString *)filename
+ withDescription:(NSString *)formatString, ... NS_FORMAT_FUNCTION(6, 7);
++ (NSException *)failureInRaise:(NSString *)expression
+ inFile:(NSString *)filename
atLine:(int)lineNumber
- withDescription:(NSString *)formatString, ...;
-+ (NSException *)failureInRaise:(NSString *)expression
- exception:(NSException *)exception
- 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, ... 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
diff --git a/src/common/mac/testing/GTMSenTestCase.m b/src/common/mac/testing/GTMSenTestCase.m
index 99b9db07..5607c316 100644
--- a/src/common/mac/testing/GTMSenTestCase.m
+++ b/src/common/mac/testing/GTMSenTestCase.m
@@ -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];
}
@@ -284,15 +324,84 @@ NSString *const SenTestLineNumberKey = @"SenTestLineNumberKey";
- (NSString *)description {
// This matches the description OCUnit would return to you
- return [NSString stringWithFormat:@"-[%@ %@]", [self class],
- NSStringFromSelector(currentSelector_)];
+ return [NSString stringWithFormat:@"-[%@ %@]", [self class],
+ 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,19 +413,34 @@ 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.
// COV_NF_START
// We don't have leak checking on by default, so this won't be hit.
static void _GTMRunLeaks(void) {
- // This is an atexit handler. It runs leaks for us to check if we are
- // leaking anything in our tests.
+ // This is an atexit handler. It runs leaks for us to check if we are
+ // leaking anything in our tests.
const char* cExclusionsEnv = getenv("GTM_LEAKS_SYMBOLS_TO_IGNORE");
NSMutableString *exclusions = [NSMutableString string];
if (cExclusionsEnv) {
@@ -329,13 +453,21 @@ static void _GTMRunLeaks(void) {
[exclusions appendFormat:@"-exclude \"%@\" ", exclusion];
}
}
- NSString *string
- = [NSString stringWithFormat:@"/usr/bin/leaks %@%d"
- @"| /usr/bin/sed -e 's/Leak: /Leaks:0: warning: Leak /'",
+ // 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:
+ @"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,12 +487,14 @@ static __attribute__((constructor)) void _GTMInstallLeaks(void) {
fprintf(stderr, "Leak Checking Enabled\n");
fflush(stderr);
int ret = atexit(&_GTMRunLeaks);
- _GTMDevAssert(ret == 0,
- @"Unable to install _GTMRunLeaks as an atexit handler (%d)",
+ // To avoid unused variable warning when _GTMDevAssert is stripped.
+ (void)ret;
+ _GTMDevAssert(ret == 0,
+ @"Unable to install _GTMRunLeaks as an atexit handler (%d)",
errno);
// COV_NF_END
- }
+ }
}
}
-#endif // !GTM_IPHONE_DEVICE
+#endif // !GTM_IPHONE_DEVICE && !GTM_SUPPRESS_RUN_LEAKS_HOOK