aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--DebugUtils/GTMMethodCheck.m195
-rw-r--r--Foundation/GTMLogger.h1
-rw-r--r--Foundation/GTMLogger.m45
-rw-r--r--UnitTesting/GTMUnitTestDevLog.h33
-rw-r--r--UnitTesting/GTMUnitTestDevLog.m9
5 files changed, 120 insertions, 163 deletions
diff --git a/DebugUtils/GTMMethodCheck.m b/DebugUtils/GTMMethodCheck.m
index ac43a49..0ff795e 100644
--- a/DebugUtils/GTMMethodCheck.m
+++ b/DebugUtils/GTMMethodCheck.m
@@ -23,65 +23,6 @@
#import "GTMObjC2Runtime.h"
#import <dlfcn.h>
-// Checks to see if the cls passed in (or one of its superclasses) conforms
-// to NSObject protocol. Inheriting from NSObject is the easiest way to do this
-// but not all classes (i.e. NSProxy) inherit from NSObject. Also, some classes
-// inherit from Object instead of NSObject which is fine, and we'll count as
-// conforming to NSObject for our needs.
-static BOOL ConformsToNSObjectProtocol(Class cls) {
- // If we get nil, obviously doesn't conform.
- if (!cls) return NO;
- const char *className = class_getName(cls);
- if (!className) return NO;
-
- // We're going to assume that all Apple classes will work
- // (and aren't being checked)
- // Note to apple: why doesn't obj-c have real namespaces instead of two
- // letter hacks? If you name your own classes starting with NS this won't
- // work for you.
- // Some classes (like _NSZombie) start with _NS.
- // On Leopard we have to look for CFObject as well.
- // On iPhone we check Object as well
- if ((strncmp(className, "NS", 2) == 0)
- || (strncmp(className, "_NS", 3) == 0)
- || (strncmp(className, "__NS", 4) == 0)
- || (strcmp(className, "CFObject") == 0)
- || (strcmp(className, "_CNZombie_") == 0)
- || (strcmp(className, "FigIrisAutoTrimmerMotionSampleExport") == 0)
- || (strcmp(className, "__IncompleteProtocol") == 0)
- || (strcmp(className, "__ARCLite__") == 0)
- || (strcmp(className, "WebMIMETypeRegistry") == 0)
- || (strcmp(className, "Object") == 0)
-#if GTM_IPHONE_SDK
- || (strcmp(className, "UIKeyboardCandidateUtilities") == 0)
- || (strcmp(className, "JSExport") == 0)
-#endif
- ) {
- return YES;
- }
-
- // iPhone and Mac OS X 10.8 with Obj-C 2 SDKs do not define the |Object|
- // class, so we instead test for the |NSObject| class.
-#if GTM_IPHONE_SDK || \
- (__OBJC2__ && defined(MAC_OS_X_VERSION_10_8) && \
- MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8)
- // Iterate through all the protocols |cls| supports looking for NSObject.
- if (cls == [NSObject class]
- || class_conformsToProtocol(cls, @protocol(NSObject))) {
- return YES;
- }
-#else
- // Iterate through all the protocols |cls| supports looking for NSObject.
- if (cls == [Object class]
- || class_conformsToProtocol(cls, @protocol(NSObject))) {
- return YES;
- }
-#endif
-
- // Recursively check the superclasses.
- return ConformsToNSObjectProtocol(class_getSuperclass(cls));
-}
-
void GTMMethodCheckMethodChecker(void) {
// Run through all the classes looking for class methods that are
// prefixed with xxGMMethodCheckMethod. If it finds one, it calls it.
@@ -95,84 +36,78 @@ void GTMMethodCheckMethodChecker(void) {
// the copy in our local image. This will give us access to our local image
// in the methodCheckerInfo structure.
Dl_info methodCheckerInfo;
- if (dladdr(GTMMethodCheckMethodChecker,
- &methodCheckerInfo)) {
- int numClasses = 0;
- int newNumClasses = objc_getClassList(NULL, 0);
- int i;
- Class *classes = NULL;
- while (numClasses < newNumClasses) {
- numClasses = newNumClasses;
- classes = (Class *)realloc(classes, sizeof(Class) * numClasses);
- _GTMDevAssert(classes, @"Unable to allocate memory for classes");
- newNumClasses = objc_getClassList(classes, numClasses);
+ int foundMethodChecker = dladdr(GTMMethodCheckMethodChecker,
+ &methodCheckerInfo);
+ _GTMDevAssert(foundMethodChecker, @"GTMMethodCheckMethodChecker: Unable to "
+ @"get dladdr for GTMMethodCheckMethodChecker");
+ int numClasses = 0;
+ int newNumClasses = objc_getClassList(NULL, 0);
+ int i;
+ Class *classes = NULL;
+ while (numClasses < newNumClasses) {
+ numClasses = newNumClasses;
+ classes = (Class *)realloc(classes, sizeof(Class) * numClasses);
+ _GTMDevAssert(classes, @"Unable to allocate memory for classes");
+ newNumClasses = objc_getClassList(classes, numClasses);
+ }
+ for (i = 0; i < numClasses && classes; ++i) {
+ Class cls = classes[i];
+ const char *className = class_getName(cls);
+ _GTMDevAssert(className, @"GTMMethodCheckMethodChecker: Unable to "
+ @"get className for class %d", i);
+ // Since we are looking for a class method (+xxGMMethodCheckMethod...)
+ // we need to query the isa pointer to see what methods it support, but
+ // send the method (if it's supported) to the class itself.
+ if (strcmp(className, "__ARCLite__") == 0) {
+ // __ARCLite__ is "magic" and does not have a metaClass.
+ continue;
+ }
+ Class metaClass = objc_getMetaClass(className);
+ _GTMDevAssert(metaClass, @"GTMMethodCheckMethodChecker: Unable to "
+ @"get metaClass for %s", className);
+ unsigned int count;
+ Method *methods = class_copyMethodList(metaClass, &count);
+ if (count == 0) {
+ continue;
}
- for (i = 0; i < numClasses && classes; ++i) {
- Class cls = classes[i];
+ _GTMDevAssert(methods, @"GTMMethodCheckMethodChecker: Unable to "
+ @"get methods for class %s", className);
- // Since we are directly calling objc_msgSend, we need to conform to
- // @protocol(NSObject), or else we will tumble into a _objc_msgForward
- // recursive loop when we try and call a function by name.
- if (!ConformsToNSObjectProtocol(cls)) {
- // COV_NF_START
- _GTMDevLog(@"GTMMethodCheckMethodChecker: Class %s does not conform "
- "to @protocol(NSObject), so won't be checked",
- class_getName(cls));
- continue;
- // COV_NF_END
- }
- // Since we are looking for a class method (+xxGMMethodCheckMethod...)
- // we need to query the isa pointer to see what methods it support, but
- // send the method (if it's supported) to the class itself.
- unsigned int count;
- Class metaClass = objc_getMetaClass(class_getName(cls));
- Method *methods = class_copyMethodList(metaClass, &count);
- unsigned int j;
- for (j = 0; j < count; ++j) {
- SEL selector = method_getName(methods[j]);
- const char *name = sel_getName(selector);
- if (strstr(name, "xxGTMMethodCheckMethod") == name) {
- Dl_info methodInfo;
- if (!dladdr(method_getImplementation(methods[j]),
- &methodInfo)) {
- // COV_NF_START
- // Don't know how to force this case in a unittest
- // Certainly hope we never see it.
- _GTMDevLog(@"GTMMethodCheckMethodChecker: Unable to get dladdr "
- "info for %s while introspecting +[%s %s]]", name,
- class_getName(cls), name);
- continue;
- // COV_NF_END
- }
+ unsigned int j;
+ for (j = 0; j < count; ++j) {
+ SEL selector = method_getName(methods[j]);
+ _GTMDevAssert(selector, @"GTMMethodCheckMethodChecker: Unable to "
+ @"get selector for method %d of %s", j, className);
+ const char *name = sel_getName(selector);
+ _GTMDevAssert(selector, @"GTMMethodCheckMethodChecker: Unable to "
+ @"get name for method %d of %s", j, className);
+ if (strstr(name, "xxGTMMethodCheckMethod") == name) {
+ Dl_info methodInfo;
+ IMP imp = method_getImplementation(methods[j]);
+ _GTMDevAssert(selector, @"GTMMethodCheckMethodChecker: Unable to "
+ @"get IMP for method %s of %s", name, className);
+ int foundMethod = dladdr(imp, &methodInfo);
+ _GTMDevAssert(foundMethod, @"GTMMethodCheckMethodChecker: Unable to "
+ @"get dladdr for method %s of %s", name, className);
- // Check to make sure that the method we are checking comes from the
- // same image that we are in. We compare the address of the local
- // image (stored in |methodCheckerInfo| as noted above) with the
- // address of the image which implements the method we want to
- // check. If they match we continue. This does two things:
- // a) minimizes the amount of calls we make to the xxxGTMMethodCheck
- // methods. They should only be called once.
- // b) prevents initializers for various classes being called too
- // early
- if (methodCheckerInfo.dli_fbase == methodInfo.dli_fbase) {
- typedef void (*GTMMethodCheckMethod)(Class, SEL);
- GTMMethodCheckMethod func = (GTMMethodCheckMethod)objc_msgSend;
- func(cls, selector);
- }
+ // Check to make sure that the method we are checking comes from the
+ // same image that we are in. We compare the address of the local
+ // image (stored in |methodCheckerInfo| as noted above) with the
+ // address of the image which implements the method we want to
+ // check. If they match we continue. This does two things:
+ // a) minimizes the amount of calls we make to the xxxGTMMethodCheck
+ // methods. They should only be called once.
+ // b) prevents initializers for various classes being called too
+ // early
+ if (methodCheckerInfo.dli_fbase == methodInfo.dli_fbase) {
+ void (*func)(id, SEL) = (void *)imp;
+ func(cls, selector);
}
}
- free(methods);
}
- free(classes);
- } else {
- // COV_NF_START
- // This means that we didn't find the GTMMethodCheckMethodChecker method
- // Don't know how to force this case in a unittest.
- // Certainly hope we never see it.
- _GTMDevLog(@"GTMMethodCheckMethodChecker: Unable to get dladdr info "
- "for GTMMethodCheckMethodChecker");
- // COV_NF_END
+ free(methods);
}
+ free(classes);
#if !defined(__has_feature) || !__has_feature(objc_arc)
[pool drain];
#else
diff --git a/Foundation/GTMLogger.h b/Foundation/GTMLogger.h
index a776922..16f0eaf 100644
--- a/Foundation/GTMLogger.h
+++ b/Foundation/GTMLogger.h
@@ -457,6 +457,7 @@ typedef enum {
@interface GTMLogLevelFilter : NSObject <GTMLogFilter> {
@private
BOOL verboseLoggingEnabled_;
+ NSUserDefaults *userDefaults_;
}
@end // GTMLogLevelFilter
diff --git a/Foundation/GTMLogger.m b/Foundation/GTMLogger.m
index 7c2a543..3ace20f 100644
--- a/Foundation/GTMLogger.m
+++ b/Foundation/GTMLogger.m
@@ -97,7 +97,7 @@ static GTMLogger *gSharedLogger = nil;
// Don't trust NSFileHandle not to throw
@try {
- GTMLogBasicFormatter *formatter = [[[GTMLogBasicFormatter alloc] init]
+ GTMLogBasicFormatter *formatter = [[[GTMLogBasicFormatter alloc] init]
autorelease];
GTMLogger *stdoutLogger =
[self loggerWithWriter:[NSFileHandle fileHandleWithStandardOutput]
@@ -475,12 +475,13 @@ static GTMLogger *gSharedLogger = nil;
@end // GTMLogStandardFormatter
+static NSString *const kVerboseLoggingKey = @"GTMVerboseLogging";
+
// Check the environment and the user preferences for the GTMVerboseLogging key
// to see if verbose logging has been enabled. The environment variable will
// override the defaults setting, so check the environment first.
// COV_NF_START
-static BOOL IsVerboseLoggingEnabled(void) {
- static NSString *const kVerboseLoggingKey = @"GTMVerboseLogging";
+static BOOL IsVerboseLoggingEnabled(NSUserDefaults *userDefaults) {
NSString *value = [[[NSProcessInfo processInfo] environment]
objectForKey:kVerboseLoggingKey];
if (value) {
@@ -495,7 +496,7 @@ static BOOL IsVerboseLoggingEnabled(void) {
return NO;
}
}
- return [[NSUserDefaults standardUserDefaults] boolForKey:kVerboseLoggingKey];
+ return [userDefaults boolForKey:kVerboseLoggingKey];
}
// COV_NF_END
@@ -504,21 +505,28 @@ static BOOL IsVerboseLoggingEnabled(void) {
- (id)init {
self = [super init];
if (self) {
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(defaultsChanged:)
- name:NSUserDefaultsDidChangeNotification
- object:nil];
-
- verboseLoggingEnabled_ = IsVerboseLoggingEnabled();
+ // Keep a reference to standardUserDefaults, avoiding a crash if client code calls
+ // "NSUserDefaults resetStandardUserDefaults" which releases it from memory. We are still
+ // notified of changes through our instance. Note: resetStandardUserDefaults does not actually
+ // clear settings:
+ // https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSUserDefaults_Class/index.html#//apple_ref/occ/clm/NSUserDefaults/resetStandardUserDefaults
+ // and so should only be called in test code if necessary.
+ userDefaults_ = [[NSUserDefaults standardUserDefaults] retain];
+ [userDefaults_ addObserver:self
+ forKeyPath:kVerboseLoggingKey
+ options:NSKeyValueObservingOptionNew
+ context:nil];
+
+ verboseLoggingEnabled_ = IsVerboseLoggingEnabled(userDefaults_);
}
return self;
}
- (void)dealloc {
- [[NSNotificationCenter defaultCenter] removeObserver:self
- name:NSUserDefaultsDidChangeNotification
- object:nil];
+ [userDefaults_ removeObserver:self forKeyPath:kVerboseLoggingKey];
+ [userDefaults_ release];
+
[super dealloc];
}
@@ -552,8 +560,14 @@ static BOOL IsVerboseLoggingEnabled(void) {
return allow;
}
-- (void)defaultsChanged:(NSNotification *)note {
- verboseLoggingEnabled_ = IsVerboseLoggingEnabled();
+- (void)observeValueForKeyPath:(NSString *)keyPath
+ ofObject:(id)object
+ change:(NSDictionary *)change
+ context:(void *)context
+{
+ if([keyPath isEqual:kVerboseLoggingKey]) {
+ verboseLoggingEnabled_ = IsVerboseLoggingEnabled(userDefaults_);
+ }
}
@end // GTMLogLevelFilter
@@ -632,4 +646,3 @@ static BOOL IsVerboseLoggingEnabled(void) {
// See comment at top of file.
#pragma GCC diagnostic error "-Wmissing-format-attribute"
#endif // !__clang__
-
diff --git a/UnitTesting/GTMUnitTestDevLog.h b/UnitTesting/GTMUnitTestDevLog.h
index f80743a..2be231e 100644
--- a/UnitTesting/GTMUnitTestDevLog.h
+++ b/UnitTesting/GTMUnitTestDevLog.h
@@ -1,14 +1,14 @@
//
// GTMUnitTestDevLog.h
-//
+//
// Copyright 2008 Google Inc.
//
// 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
@@ -25,7 +25,7 @@
// to find unexpected logs in your output when running unittests.
// In your unittests you tell GTMUnitTestDevLog what messages you expect your
// test to spit out, and it will cause any that don't match to appear as errors
-// in your unittest run output. You can match on exact strings or standard
+// in your unittest run output. You can match on exact strings or standard
// regexps.
// Set GTM_SHOW_UNITTEST_DEVLOGS in the environment to show the logs that that
// are expected and encountered. Otherwise they aren't display to keep the
@@ -33,8 +33,8 @@
@interface GTMUnitTestDevLog : NSObject
// Log a message
-+ (void)log:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2);
-+ (void)log:(NSString*)format args:(va_list)args NS_FORMAT_FUNCTION(1,0);
++ (void)log:(NSString*)format, ... NS_FORMAT_FUNCTION(1, 2);
++ (void)log:(NSString*)format args:(va_list)args NS_FORMAT_FUNCTION(1, 0);
// Turn tracking on/off
+ (void)enableTracking;
@@ -43,24 +43,25 @@
// Note that you are expecting a string that has an exact match. No need to
// escape any pattern characters.
-+ (void)expectString:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2);
++ (void)expectString:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2);
// Note that you are expecting a pattern. Pattern characters that you want
// exact matches on must be escaped. See [GTMRegex escapedPatternForString].
// Patterns match across newlines (kGTMRegexOptionSupressNewlineSupport) making
// it easier to match output from the descriptions of NS collection types such
// as NSArray and NSDictionary.
-+ (void)expectPattern:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2);
++ (void)expectPattern:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2);
// Note that you are expecting exactly 'n' strings
-+ (void)expect:(NSUInteger)n
- casesOfString:(NSString *)format, ... NS_FORMAT_FUNCTION(2,3);
++ (void)expect:(NSUInteger)n
+ casesOfString:(NSString *)format, ... NS_FORMAT_FUNCTION(2, 3);
// Note that you are expecting exactly 'n' patterns
-+ (void)expect:(NSUInteger)n
- casesOfPattern:(NSString*)format, ... NS_FORMAT_FUNCTION(2,3);
-+ (void)expect:(NSUInteger)n
- casesOfPattern:(NSString*)format args:(va_list)args NS_FORMAT_FUNCTION(2,0);
++ (void)expect:(NSUInteger)n
+ casesOfPattern:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3);
++ (void)expect:(NSUInteger)n
+ casesOfPattern:(NSString*)format
+ args:(va_list)args NS_FORMAT_FUNCTION(2, 0);
// Call when you want to verify that you have matched all the logs you expect
// to match. If your unittests inherit from GTMTestcase (like they should) you
@@ -77,3 +78,7 @@
// ie-the expect requests don't count in release builds.
@interface GTMUnitTestDevLogDebug : GTMUnitTestDevLog
@end
+
+// The name of the exception that is raised when GTMUnitTestDevLog
+// finds a problem.
+GTM_EXTERN NSString *const GTMLogFailureException;
diff --git a/UnitTesting/GTMUnitTestDevLog.m b/UnitTesting/GTMUnitTestDevLog.m
index fd90396..7a12bb0 100644
--- a/UnitTesting/GTMUnitTestDevLog.m
+++ b/UnitTesting/GTMUnitTestDevLog.m
@@ -61,6 +61,8 @@ static inline void GTMInstallDebugAssertOutputHandler(void) {};
static inline void GTMUninstallDebugAssertOutputHandler(void) {};
#endif // GTM_IPHONE_SDK
+NSString *const GTMLogFailureException = @"GTMLogFailureException";
+
@interface GTMUnttestDevLogAssertionHandler : NSAssertionHandler
@end
@@ -182,12 +184,13 @@ static BOOL gTrackingEnabled = NO;
[patterns removeObjectAtIndex:0];
}
if (logError) {
+
if (regex) {
- [NSException raise:SenTestFailureException
+ [NSException raise:GTMLogFailureException
format:@"Unexpected log: %@\nExpected: %@",
logString, regex];
} else {
- [NSException raise:SenTestFailureException
+ [NSException raise:GTMLogFailureException
format:@"Unexpected log: %@", logString];
}
} else {
@@ -262,7 +265,7 @@ casesOfPattern:(NSString*)format
if ([patterns count] > 0) {
NSMutableArray *patternsCopy = [[patterns copy] autorelease];
[self resetExpectedLogs];
- [NSException raise:SenTestFailureException
+ [NSException raise:GTMLogFailureException
format:@"Logs still expected %@", patternsCopy];
}
}