From 4fd103b5de98d2f469b982677ea389e7ee7d64b3 Mon Sep 17 00:00:00 2001 From: "thomasvl@gmail.com" Date: Fri, 9 Jan 2009 20:34:30 +0000 Subject: - turned off _debug framework support in tests since we now capture a lot more in log validation. - Added GTM_SUPPORT_GC for controlling the inclusion of GC related code. - If you are using GTMUnitTestDevLog, it also tries to capture logs from NSAssert. - Added GTM_FOREACH_OBJECT/GTM_FOREACH_KEY that uses NSEnumerator and objectEnumerator/keyEnumerator on 10.4, but on 10.5+/iPhone uses FastEnumeration. - GTMNSWorkspace+Running gives a variety of ways of determining the attributes of running processes. --- AppKit/GTMCarbonEvent.m | 5 +- AppKit/GTMCarbonEventTest.m | 6 +- AppKit/GTMGetURLHandler.m | 16 +- AppKit/GTMHotKeyTextField.m | 8 +- AppKit/GTMLinearRGBShading.m | 3 + AppKit/GTMLoginItems.m | 3 +- AppKit/GTMLoginItemsTest.m | 3 +- AppKit/GTMNSImage+Scaling.m | 20 ++- AppKit/GTMNSWorkspace+Running.h | 80 ++++++++++ AppKit/GTMNSWorkspace+Running.m | 171 +++++++++++++++++++++ AppKit/GTMNSWorkspace+RunningTest.m | 78 ++++++++++ Foundation/GTMCalculatedRange.m | 9 +- Foundation/GTMFileSystemKQueue.m | 2 + Foundation/GTMHTTPFetcher.m | 6 +- Foundation/GTMHTTPServer.m | 2 + Foundation/GTMLightweightProxy.m | 4 +- Foundation/GTMLogger+ASL.m | 3 + Foundation/GTMLogger.m | 3 +- Foundation/GTMNSAppleEventDescriptor+Foundation.m | 32 ++-- .../GTMNSAppleEventDescriptor+FoundationTest.m | 2 +- Foundation/GTMNSArray+Merge.m | 3 +- Foundation/GTMNSDictionary+URLArguments.m | 4 +- Foundation/GTMNSFileManager+Path.m | 7 +- Foundation/GTMRegex.h | 4 +- Foundation/GTMRegex.m | 5 +- Foundation/GTMSignalHandler.m | 2 + GTM.xcodeproj/project.pbxproj | 12 ++ GTMDefines.h | 37 +++++ ReleaseNotes.txt | 18 ++- UnitTesting/GTMAppKit+UnitTesting.m | 14 +- UnitTesting/GTMNSObject+BindingUnitTesting.h | 3 +- UnitTesting/GTMNSObject+BindingUnitTesting.m | 6 +- UnitTesting/GTMNSObject+UnitTesting.m | 13 +- UnitTesting/GTMSenTestCase.m | 6 +- UnitTesting/GTMUIKit+UnitTesting.m | 6 +- UnitTesting/GTMUnitTestDevLog.m | 71 ++++++++- UnitTesting/GTMUnitTestingBindingTest.m | 3 +- UnitTesting/RunMacOSUnitTests.sh | 9 -- iPhone/GTMABAddressBook.m | 6 +- 39 files changed, 577 insertions(+), 108 deletions(-) create mode 100644 AppKit/GTMNSWorkspace+Running.h create mode 100644 AppKit/GTMNSWorkspace+Running.m create mode 100644 AppKit/GTMNSWorkspace+RunningTest.m diff --git a/AppKit/GTMCarbonEvent.m b/AppKit/GTMCarbonEvent.m index 270a078..16c46c8 100644 --- a/AppKit/GTMCarbonEvent.m +++ b/AppKit/GTMCarbonEvent.m @@ -141,6 +141,7 @@ return carbonEvent; } +#if GTM_SUPPORT_GC - (void)finalize { if (event_) { ReleaseEvent(event_); @@ -148,6 +149,7 @@ } [super finalize]; } +#endif // releases our retained event // @@ -596,9 +598,8 @@ CantRegisterHotkey: BOOL handled = [event getEventHotKeyIDParameterNamed:kEventParamDirectObject data:&keyID]; if (handled) { - NSEnumerator *dictEnumerator = [hotkeys_ objectEnumerator]; GTMCarbonHotKey *hotkey; - while ((hotkey = [dictEnumerator nextObject])) { + GTM_FOREACH_OBJECT(hotkey, [hotkeys_ allValues]) { if ([hotkey matchesHotKeyID:keyID]) { EventKind kind = [event eventKind]; BOOL onKeyDown = [hotkey onKeyDown]; diff --git a/AppKit/GTMCarbonEventTest.m b/AppKit/GTMCarbonEventTest.m index a1a8b2b..f7b4d54 100644 --- a/AppKit/GTMCarbonEventTest.m +++ b/AppKit/GTMCarbonEventTest.m @@ -274,7 +274,11 @@ static const UInt32 kTestParameterValue = 'bam '; whenPressed:YES], @"Shouldn't have created hotkey"); #if DEBUG - // This test debug selector validation, so we only can do it in debug. + // This tests debug selector validation, so we only can do it in debug. + [GTMUnitTestDevLogDebug expectPattern:@"RecordedNSAssert in " + @"GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments - " + @"\"GTMCarbonEventDispatcherHandlerTest\" selector \"badSelector:\" is " + @"unimplemented or misnamed \\(.*/GTMDebugSelectorValidation.h:[0-9]*\\)"]; STAssertThrowsSpecificNamed([dispatcher registerHotKey:0x5 modifiers:keyMods target:self diff --git a/AppKit/GTMGetURLHandler.m b/AppKit/GTMGetURLHandler.m index 66c02eb..dcac292 100644 --- a/AppKit/GTMGetURLHandler.m +++ b/AppKit/GTMGetURLHandler.m @@ -149,9 +149,8 @@ withReplyEvent:(NSAppleEventDescriptor *)replyEvent { // classes properly. We check here instead of at init in case some of the // handlers are being handled by plugins or other imported code that are // loaded after we have been initialized. - NSEnumerator *enumerator = [urlTypes_ objectEnumerator]; NSDictionary *urlType; - while ((urlType = [enumerator nextObject])) { + GTM_FOREACH_OBJECT(urlType, urlTypes_) { NSString *className = [urlType objectForKey:kGTMBundleURLClassKey]; if ([className length]) { Class cls = NSClassFromString(className); @@ -215,18 +214,21 @@ withReplyEvent:(NSAppleEventDescriptor *)replyEvent { - (Class)getClassForScheme:(NSString *)scheme withReplyEvent:(NSAppleEventDescriptor*)replyEvent { - NSEnumerator *typeEnumerator = [urlTypes_ objectEnumerator]; NSDictionary *urlType; Class cls = nil; NSString *typeScheme = nil; - while (!typeScheme && (urlType = [typeEnumerator nextObject])) { + GTM_FOREACH_OBJECT(urlType, urlTypes_) { NSArray *schemes = [urlType objectForKey:kGTMCFBundleURLSchemesKey]; - NSEnumerator *schemeEnumerator = [schemes objectEnumerator]; - while ((typeScheme = [schemeEnumerator nextObject])) { - if ([typeScheme caseInsensitiveCompare:scheme] == NSOrderedSame) { + NSString *aScheme; + GTM_FOREACH_OBJECT(aScheme, schemes) { + if ([aScheme caseInsensitiveCompare:scheme] == NSOrderedSame) { + typeScheme = aScheme; break; } } + if (typeScheme) { + break; + } } if (typeScheme) { NSString *class = [urlType objectForKey:kGTMBundleURLClassKey]; diff --git a/AppKit/GTMHotKeyTextField.m b/AppKit/GTMHotKeyTextField.m index 3a604be..0f14e48 100644 --- a/AppKit/GTMHotKeyTextField.m +++ b/AppKit/GTMHotKeyTextField.m @@ -48,12 +48,14 @@ static CFStringRef kGTM_TISPropertyUnicodeKeyLayoutData = NULL; @implementation GTMHotKeyTextField +#if GTM_SUPPORT_GC - (void)finalize { if (boundObject_ && boundKeyPath_) { [boundObject_ removeObserver:self forKeyPath:boundKeyPath_]; } [super finalize]; } +#endif - (void)dealloc { @@ -690,10 +692,12 @@ static CFStringRef kGTM_TISPropertyUnicodeKeyLayoutData = NULL; sizeof(uchrChars) / sizeof(UniChar), &uchrCharLength, uchrChars); - if (err != noErr) { // COV_NF_START + if (err != noErr) { + // COV_NF_START _GTMDevLog(@"failed to translate the keycode, err=%d", err); return nil; - } // COV_NF_END + // COV_NF_END + } if (uchrCharLength < 1) return nil; keystrokeString = [NSString stringWithCharacters:uchrChars length:uchrCharLength]; diff --git a/AppKit/GTMLinearRGBShading.m b/AppKit/GTMLinearRGBShading.m index f0216bf..af70086 100644 --- a/AppKit/GTMLinearRGBShading.m +++ b/AppKit/GTMLinearRGBShading.m @@ -17,6 +17,7 @@ // #import "GTMLinearRGBShading.h" +#import "GTMDefines.h" // Carbon callback function required for CoreGraphics static void cShadeFunction(void *info, const CGFloat *inPos, CGFloat *outVals); @@ -57,6 +58,7 @@ static void cShadeFunction(void *info, const CGFloat *inPos, CGFloat *outVals); return self; } +#if GTM_SUPPORT_GC - (void)finalize { if (nil != function_) { CGFunctionRelease(function_); @@ -66,6 +68,7 @@ static void cShadeFunction(void *info, const CGFloat *inPos, CGFloat *outVals); } [super finalize]; } +#endif - (void)dealloc { if (nil != function_) { diff --git a/AppKit/GTMLoginItems.m b/AppKit/GTMLoginItems.m index 1e59e70..c3dedfe 100644 --- a/AppKit/GTMLoginItems.m +++ b/AppKit/GTMLoginItems.m @@ -42,9 +42,8 @@ NSString * const kGTMLoginItemsHiddenKey = @"Hide"; loginItems:(NSArray *)items { if (!value || !key || !items) return NSNotFound; NSDictionary *item = nil; - NSEnumerator *itemsEnum = [items objectEnumerator]; NSInteger found = -1; - while ((item = [itemsEnum nextObject])) { + GTM_FOREACH_OBJECT(item, items) { ++found; id itemValue = [item objectForKey:key]; if (itemValue && [itemValue isEqual:value]) { diff --git a/AppKit/GTMLoginItemsTest.m b/AppKit/GTMLoginItemsTest.m index 980f772..50b7482 100644 --- a/AppKit/GTMLoginItemsTest.m +++ b/AppKit/GTMLoginItemsTest.m @@ -31,8 +31,7 @@ static BOOL ItemsListHasPath(NSArray *items, NSString *path) { NSDictionary *item = nil; - NSEnumerator *itemsEnum = [items objectEnumerator]; - while ((item = [itemsEnum nextObject])) { + GTM_FOREACH_OBJECT(item, items) { NSString *itemPath = [item objectForKey:kGTMLoginItemsPathKey]; if (itemPath && [itemPath isEqual:path]) { return YES; diff --git a/AppKit/GTMNSImage+Scaling.m b/AppKit/GTMNSImage+Scaling.m index 5f31190..8283b29 100644 --- a/AppKit/GTMNSImage+Scaling.m +++ b/AppKit/GTMNSImage+Scaling.m @@ -36,8 +36,7 @@ CGFloat repDistance = CGFLOAT_MAX; NSImageRep *thisRep; - NSEnumerator *repEnum = [reps objectEnumerator]; - while ((thisRep = [repEnum nextObject])) { + GTM_FOREACH_OBJECT(thisRep, reps) { CGFloat thisDistance; thisDistance = MIN(size.width - [thisRep size].width, size.height - [thisRep size].height); @@ -62,8 +61,7 @@ NSArray *reps = [self representations]; NSImageRep *thisRep; - NSEnumerator *repEnum = [reps objectEnumerator]; - while ((thisRep = [repEnum nextObject])) { + GTM_FOREACH_OBJECT(thisRep, reps) { if (NSEqualSizes([thisRep size], size)) { return thisRep; } @@ -155,12 +153,18 @@ } - (void)gtm_removeRepresentationsLargerThanSize:(NSSize)size { - NSEnumerator *e = [[self representations] reverseObjectEnumerator]; + NSMutableArray *repsToRemove = [NSMutableArray array]; NSImageRep *thisRep; - while((thisRep = [e nextObject]) ) { + // Remove them in a second loop so we don't change things will doing the + // initial loop. + GTM_FOREACH_OBJECT(thisRep, [self representations]) { if ([thisRep size].width > size.width - && [thisRep size].height > size.height) - [self removeRepresentation:thisRep]; + && [thisRep size].height > size.height) { + [repsToRemove addObject:thisRep]; + } + } + GTM_FOREACH_OBJECT(thisRep, repsToRemove) { + [self removeRepresentation:thisRep]; } } diff --git a/AppKit/GTMNSWorkspace+Running.h b/AppKit/GTMNSWorkspace+Running.h new file mode 100644 index 0000000..7e9dc6d --- /dev/null +++ b/AppKit/GTMNSWorkspace+Running.h @@ -0,0 +1,80 @@ +// +// GTMNSWorkspace+Running.h +// +// Copyright 2007-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 +// License for the specific language governing permissions and limitations under +// the License. +// + +#import +#import "GTMDefines.h" + +// Process Dictionary keys +// Make sure to use numberToProcessSerialNumber: on the return values +// of these keys to get valid PSNs on both Leopard and Tiger. +// Numeric types come back as a NSNumber. +GTM_EXTERN NSString *const kGTMWorkspaceRunningPSN; // long long +GTM_EXTERN NSString *const kGTMWorkspaceRunningParentPSN; // long long + +GTM_EXTERN NSString *const kGTMWorkspaceRunningFlavor; // SInt32 +GTM_EXTERN NSString *const kGTMWorkspaceRunningAttributes; // SInt32 +GTM_EXTERN NSString *const kGTMWorkspaceRunningFileType; // NSString +GTM_EXTERN NSString *const kGTMWorkspaceRunningFileCreator; // NSString +GTM_EXTERN NSString *const kGTMWorkspaceRunningPID; // long +GTM_EXTERN NSString *const kGTMWorkspaceRunningLSBackgroundOnly; // bool +GTM_EXTERN NSString *const kGTMWorkspaceRunningLSUIElement; // bool +GTM_EXTERN NSString *const kGTMWorkspaceRunningIsHidden; // bool +GTM_EXTERN NSString *const kGTMWorkspaceRunningCheckedIn; // bool +GTM_EXTERN NSString *const kGTMWorkspaceRunningLSUIPresentationMode; // Short +GTM_EXTERN NSString *const kGTMWorkspaceRunningBundlePath; // NSString +GTM_EXTERN NSString *const kGTMWorkspaceRunningBundleExecutable; // NSString +GTM_EXTERN NSString *const kGTMWorkspaceRunningBundleName; // NSString +GTM_EXTERN NSString *const kGTMWorkspaceRunningBundleIdentifier; // NSString +GTM_EXTERN NSString *const kGTMWorkspaceRunningBundleVersion; // NSString + +// A category for getting information about other running processes +@interface NSWorkspace (GTMWorkspaceRunningAdditions) + +// Returns a YES/NO if a process w/ the given identifier is running +- (BOOL)gtm_isAppWithIdentifierRunning:(NSString *)identifier; + +// Returns a dictionary with info for our process. +//See Process Dictionary Keys above for values +- (NSDictionary *)gtm_processInfoDictionary; + +// Returns a dictionary with info for the active process. +// See Process Dictionary Keys above for values +- (NSDictionary *)gtm_processInfoDictionaryForActiveApp; + +// Returns a dictionary with info for the process. +//See Process Dictionary Keys above for values +- (NSDictionary *)gtm_processInfoDictionaryForPID:(pid_t)pid; + +// Returns a dictionary with info for the process. +// See Process Dictionary Keys above for values +- (NSDictionary *)gtm_processInfoDictionaryForPSN:(const ProcessSerialNumberPtr)psn; + +// Returns true if we were launched as a login item. +- (BOOL)gtm_wasLaunchedAsLoginItem; + +// Returns true if we were launched by a given bundleid +- (BOOL)gtm_wasLaunchedByProcess:(NSString*)bundleid; + +// Returns true if the PSN was found for the running app with bundleID +- (BOOL)gtm_processSerialNumber:(ProcessSerialNumber*)outPSN + withBundleID:(NSString*)bundleID; + +// Converts PSNs stored in NSNumbers to real PSNs +- (ProcessSerialNumber)gtm_numberToProcessSerialNumber:(NSNumber*)number; + +@end diff --git a/AppKit/GTMNSWorkspace+Running.m b/AppKit/GTMNSWorkspace+Running.m new file mode 100644 index 0000000..a031043 --- /dev/null +++ b/AppKit/GTMNSWorkspace+Running.m @@ -0,0 +1,171 @@ +// +// GTMNSWorkspace+Running.m +// +// Copyright 2007-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 +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMNSWorkspace+Running.h" +#import +#import +#import "GTMGarbageCollection.h" +#import "GTMSystemVersion.h" + +NSString *const kGTMWorkspaceRunningPSN = @"PSN"; +NSString *const kGTMWorkspaceRunningFlavor = @"Flavor"; +NSString *const kGTMWorkspaceRunningAttributes = @"Attributes"; +NSString *const kGTMWorkspaceRunningParentPSN = @"ParentPSN"; +NSString *const kGTMWorkspaceRunningFileType = @"FileType"; +NSString *const kGTMWorkspaceRunningFileCreator = @"FileCreator"; +NSString *const kGTMWorkspaceRunningPID = @"pid"; +NSString *const kGTMWorkspaceRunningLSBackgroundOnly = @"LSBackgroundOnly"; +NSString *const kGTMWorkspaceRunningLSUIElement = @"LSUIElement"; +NSString *const kGTMWorkspaceRunningIsHidden = @"IsHiddenAttr"; +NSString *const kGTMWorkspaceRunningCheckedIn = @"IsCheckedInAttr"; +NSString *const kGTMWorkspaceRunningLSUIPresentationMode + = @"LSUIPresentationMode"; +NSString *const kGTMWorkspaceRunningBundlePath = @"BundlePath"; +NSString *const kGTMWorkspaceRunningBundleExecutable = @"CFBundleExecutable"; +NSString *const kGTMWorkspaceRunningBundleName = @"CFBundleName"; +NSString *const kGTMWorkspaceRunningBundleIdentifier = @"CFBundleIdentifier"; +NSString *const kGTMWorkspaceRunningBundleVersion = @"CFBundleVersion"; + +@implementation NSWorkspace (GTMWorkspaceRunningAdditions) + +/// Returns a YES/NO if a process w/ the given identifier is running +- (BOOL)gtm_isAppWithIdentifierRunning:(NSString *)identifier { + if ([identifier length] == 0) return NO; + NSArray *launchedApps = [self launchedApplications]; + NSArray *buildIDs + = [launchedApps valueForKey:@"NSApplicationBundleIdentifier"]; + return [buildIDs containsObject:identifier]; +} + +- (NSDictionary *)gtm_processInfoDictionaryForPID:(pid_t)pid { + NSDictionary *dict = nil; + ProcessSerialNumber psn; + if (GetProcessForPID(pid, &psn) == noErr) { + dict = [self gtm_processInfoDictionaryForPSN:&psn]; + } + return dict; +} + +- (NSDictionary *)gtm_processInfoDictionaryForPSN:(ProcessSerialNumberPtr)psn { + NSDictionary *dict = nil; + if (psn) { + CFDictionaryRef cfDict + = ProcessInformationCopyDictionary(psn, + kProcessDictionaryIncludeAllInformationMask); + dict = GTMCFAutorelease(cfDict); + } + return dict; +} + +- (NSDictionary *)gtm_processInfoDictionary { + NSDictionary *dict = nil; + ProcessSerialNumber selfNumber; + if (MacGetCurrentProcess(&selfNumber) == noErr) { + dict = [self gtm_processInfoDictionaryForPSN:&selfNumber]; + } + return dict; +} + +- (NSDictionary *)gtm_processInfoDictionaryForActiveApp { + NSDictionary *processDict = nil; + ProcessSerialNumber psn; + OSStatus status = GetFrontProcess(&psn); + if (status == noErr) { + processDict = [self gtm_processInfoDictionaryForPSN:&psn]; + } + return processDict; +} + +- (BOOL)gtm_wasLaunchedAsLoginItem { + // If the launching process was 'loginwindow', we were launched as a login + // item + return [self gtm_wasLaunchedByProcess:@"com.apple.loginwindow"]; +} + +- (BOOL)gtm_wasLaunchedByProcess:(NSString*)bundleid { + BOOL wasLaunchedByProcess = NO; + NSDictionary *processInfo = [self gtm_processInfoDictionary]; + if (processInfo) { + NSNumber *processNumber + = [processInfo objectForKey:kGTMWorkspaceRunningParentPSN]; + ProcessSerialNumber parentPSN + = [self gtm_numberToProcessSerialNumber:processNumber]; + NSDictionary *parentProcessInfo + = [self gtm_processInfoDictionaryForPSN:&parentPSN]; + NSString *parentBundle + = [parentProcessInfo objectForKey:kGTMWorkspaceRunningBundleIdentifier]; + wasLaunchedByProcess + = [parentBundle isEqualToString:bundleid]; + } + return wasLaunchedByProcess; +} + +- (BOOL)gtm_processSerialNumber:(ProcessSerialNumber*)outPSN + withBundleID:(NSString*)bundleID { + if (!outPSN || [bundleID length] == 0) { + return NO; + } + + NSArray *apps = [self launchedApplications]; + + NSEnumerator *enumerator = [apps objectEnumerator]; + NSDictionary *dict; + + while ((dict = [enumerator nextObject])) { + NSString *nextID = [dict objectForKey:@"NSApplicationBundleIdentifier"]; + + if ([nextID isEqualToString:bundleID]) { + NSNumber *psn + = [dict objectForKey:@"NSApplicationProcessSerialNumberLow"]; + outPSN->lowLongOfPSN = [psn unsignedIntValue]; + + psn = [dict objectForKey:@"NSApplicationProcessSerialNumberHigh"]; + outPSN->highLongOfPSN = [psn unsignedIntValue]; + + return YES; + } + } + + return NO; +} + +- (ProcessSerialNumber)gtm_numberToProcessSerialNumber:(NSNumber*)number { + // There is a bug in Tiger where they were packing ProcessSerialNumbers + // incorrectly into the longlong that they stored in the dictionary. + // This fixes it. + ProcessSerialNumber outPSN = { kNoProcess, kNoProcess}; + if (number) { + long long temp = [number longLongValue]; + UInt32 hi = (UInt32)((temp >> 32) & 0x00000000FFFFFFFFLL); + UInt32 lo = (UInt32)((temp >> 0) & 0x00000000FFFFFFFFLL); + if ([GTMSystemVersion isLeopardOrGreater]) { + outPSN.highLongOfPSN = hi; + outPSN.lowLongOfPSN = lo; + } else { +#if TARGET_RT_BIG_ENDIAN + outPSN.highLongOfPSN = hi; + outPSN.lowLongOfPSN = lo; +#else + outPSN.highLongOfPSN = lo; + outPSN.lowLongOfPSN = hi; +#endif + } + } + return outPSN; +} +@end diff --git a/AppKit/GTMNSWorkspace+RunningTest.m b/AppKit/GTMNSWorkspace+RunningTest.m new file mode 100644 index 0000000..abd20a6 --- /dev/null +++ b/AppKit/GTMNSWorkspace+RunningTest.m @@ -0,0 +1,78 @@ +// +// GTMNSWorkspace+RunningTest.m +// +// Copyright 2007-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 +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMSenTestCase.h" +#import "GTMNSWorkspace+Running.h" +#import + +@interface GTMNSWorkspace_RunningTest : GTMTestCase +@end + +@implementation GTMNSWorkspace_RunningTest + +- (void)testBasics { + NSWorkspace *ws = [NSWorkspace sharedWorkspace]; + STAssertTrue([ws gtm_isAppWithIdentifierRunning:@"com.apple.finder"], nil); + STAssertFalse([ws gtm_isAppWithIdentifierRunning:@"com.google.nothing"], nil); + + NSDictionary *processInfo = [ws gtm_processInfoDictionary]; + STAssertNotNil(processInfo, nil); + + BOOL wasLaunchedAsLoginItem = [ws gtm_wasLaunchedAsLoginItem]; + STAssertFalse(wasLaunchedAsLoginItem, nil); + + pid_t pid = getpid(); + NSDictionary *processInfo2 = [ws gtm_processInfoDictionaryForPID:pid]; + STAssertNotNil(processInfo2, nil); + STAssertEqualObjects(processInfo, processInfo2, nil); + + ProcessSerialNumber num = { 0, 0 }; + BOOL gotPSN = [ws gtm_processSerialNumber:&num + withBundleID:@"com.apple.finder"]; + STAssertTrue(gotPSN, nil); + STAssertGreaterThan(num.highLongOfPSN + num.lowLongOfPSN, (UInt32)0, nil); + gotPSN = [ws gtm_processSerialNumber:&num + withBundleID:@"bad.bundle.id"]; + STAssertFalse(gotPSN, nil); + + gotPSN = [ws gtm_processSerialNumber:NULL + withBundleID:nil]; + STAssertFalse(gotPSN, nil); + + processInfo = [ws gtm_processInfoDictionaryForActiveApp]; + STAssertNotNil(processInfo, nil); + + NSString *const keys[] = { + kGTMWorkspaceRunningPSN, kGTMWorkspaceRunningParentPSN, + kGTMWorkspaceRunningFlavor, kGTMWorkspaceRunningAttributes, + kGTMWorkspaceRunningFileType, kGTMWorkspaceRunningFileCreator, + kGTMWorkspaceRunningPID, kGTMWorkspaceRunningLSBackgroundOnly, + kGTMWorkspaceRunningLSUIElement, kGTMWorkspaceRunningIsHidden, + kGTMWorkspaceRunningCheckedIn, kGTMWorkspaceRunningBundleIdentifier, + kGTMWorkspaceRunningBundleVersion, kGTMWorkspaceRunningBundleName, + kGTMWorkspaceRunningLSUIPresentationMode, kGTMWorkspaceRunningBundlePath, + kGTMWorkspaceRunningBundleExecutable + }; + for (size_t i = 0; i < sizeof(keys) / sizeof(NSString *); ++i) { + NSString *const key = keys[i]; + STAssertNotNil([processInfo objectForKey:key], + @"Couldn't get %@ from %@", key, processInfo); + } +} + +@end diff --git a/Foundation/GTMCalculatedRange.m b/Foundation/GTMCalculatedRange.m index ef49d83..5ab491d 100644 --- a/Foundation/GTMCalculatedRange.m +++ b/Foundation/GTMCalculatedRange.m @@ -81,9 +81,8 @@ GTM_INLINE BOOL FPEqual(CGFloat a, CGFloat b) { - (void)insertStop:(id)item atPosition:(CGFloat)position { NSUInteger positionIndex = 0; - NSEnumerator *theEnumerator = [storage_ objectEnumerator]; GTMCalculatedRangeStopPrivate *theStop; - while (nil != (theStop = [theEnumerator nextObject])) { + GTM_FOREACH_OBJECT(theStop, storage_) { if ([theStop position] < position) { positionIndex += 1; } @@ -100,9 +99,8 @@ GTM_INLINE BOOL FPEqual(CGFloat a, CGFloat b) { - (BOOL)removeStopAtPosition:(CGFloat)position { NSUInteger positionIndex = 0; BOOL foundStop = NO; - NSEnumerator *theEnumerator = [storage_ objectEnumerator]; GTMCalculatedRangeStopPrivate *theStop; - while (nil != (theStop = [theEnumerator nextObject])) { + GTM_FOREACH_OBJECT(theStop, storage_) { if (FPEqual([theStop position], position)) { break; } else { @@ -135,8 +133,7 @@ GTM_INLINE BOOL FPEqual(CGFloat a, CGFloat b) { - (id)valueAtPosition:(CGFloat)position { id theValue = nil; GTMCalculatedRangeStopPrivate *theStop; - NSEnumerator *theEnumerator = [storage_ objectEnumerator]; - while (nil != (theStop = [theEnumerator nextObject])) { + GTM_FOREACH_OBJECT(theStop, storage_) { if (FPEqual([theStop position], position)) { theValue = [theStop item]; break; diff --git a/Foundation/GTMFileSystemKQueue.m b/Foundation/GTMFileSystemKQueue.m index 050d68f..ebb2ec8 100644 --- a/Foundation/GTMFileSystemKQueue.m +++ b/Foundation/GTMFileSystemKQueue.m @@ -85,11 +85,13 @@ static CFSocketRef gRunLoopSocket = NULL; return self; } +#if GTM_SUPPORT_GC - (void)finalize { [self unregisterWithKQueue]; [super finalize]; } +#endif - (void)dealloc { [self unregisterWithKQueue]; diff --git a/Foundation/GTMHTTPFetcher.m b/Foundation/GTMHTTPFetcher.m index 34821f2..c3be384 100644 --- a/Foundation/GTMHTTPFetcher.m +++ b/Foundation/GTMHTTPFetcher.m @@ -398,9 +398,8 @@ CannotBeginFetch: // any headers in the redirect override headers in the original. NSDictionary *redirectHeaders = [redirectRequest allHTTPHeaderFields]; if (redirectHeaders) { - NSEnumerator *enumerator = [redirectHeaders keyEnumerator]; NSString *key; - while (nil != (key = [enumerator nextObject])) { + GTM_FOREACH_KEY(key, redirectHeaders) { NSString *value = [redirectHeaders objectForKey:key]; [newRequest setValue:value forHTTPHeaderField:key]; } @@ -1198,10 +1197,9 @@ CannotBeginFetch: [self removeExpiredCookiesInArray:cookieStorageArray]; - NSEnumerator *newCookieEnum = [newCookies objectEnumerator]; NSHTTPCookie *newCookie; - while ((newCookie = [newCookieEnum nextObject]) != nil) { + GTM_FOREACH_OBJECT(newCookie, newCookies) { if ([[newCookie name] length] > 0 && [[newCookie domain] length] > 0 diff --git a/Foundation/GTMHTTPServer.m b/Foundation/GTMHTTPServer.m index ecd649c..4ecc9f7 100644 --- a/Foundation/GTMHTTPServer.m +++ b/Foundation/GTMHTTPServer.m @@ -94,10 +94,12 @@ static NSString *kResponse = @"Response"; [super dealloc]; } +#if GTM_SUPPORT_GC - (void)finalize { [self stop]; [super finalize]; } +#endif - (id)delegate { return delegate_; diff --git a/Foundation/GTMLightweightProxy.m b/Foundation/GTMLightweightProxy.m index 39f5f5c..c00e44b 100644 --- a/Foundation/GTMLightweightProxy.m +++ b/Foundation/GTMLightweightProxy.m @@ -17,7 +17,7 @@ // #import "GTMLightweightProxy.h" - +#import "GTMDefines.h" @implementation GTMLightweightProxy @@ -28,12 +28,14 @@ } #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 +#if GTM_SUPPORT_GC // -[NSProxy finalize] is only in 10.5 or later - (void)finalize { representedObject_ = nil; [super finalize]; } #endif +#endif - (void)dealloc { // it's weak, we don't release diff --git a/Foundation/GTMLogger+ASL.m b/Foundation/GTMLogger+ASL.m index 2213df6..90ea7e5 100644 --- a/Foundation/GTMLogger+ASL.m +++ b/Foundation/GTMLogger+ASL.m @@ -17,6 +17,7 @@ // #import "GTMLogger+ASL.h" +#import "GTMDefines.h" @implementation GTMLogger (GTMLoggerASLAdditions) @@ -107,10 +108,12 @@ [super dealloc]; } +#if GTM_SUPPORT_GC - (void)finalize { if (client_) asl_close(client_); [super finalize]; } +#endif // We don't test this one line because we don't want to pollute actual system // logs with test messages. diff --git a/Foundation/GTMLogger.m b/Foundation/GTMLogger.m index 4da1034..de941d2 100644 --- a/Foundation/GTMLogger.m +++ b/Foundation/GTMLogger.m @@ -291,8 +291,7 @@ static GTMLogger *gSharedLogger = nil; - (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level { @synchronized(self) { id child = nil; - NSEnumerator *childEnumerator = [self objectEnumerator]; - while ((child = [childEnumerator nextObject])) { + GTM_FOREACH_OBJECT(child, self) { if ([child conformsToProtocol:@protocol(GTMLogWriter)]) [child logMessage:msg level:level]; } diff --git a/Foundation/GTMNSAppleEventDescriptor+Foundation.m b/Foundation/GTMNSAppleEventDescriptor+Foundation.m index d0c26ec..b355607 100644 --- a/Foundation/GTMNSAppleEventDescriptor+Foundation.m +++ b/Foundation/GTMNSAppleEventDescriptor+Foundation.m @@ -100,15 +100,22 @@ static NSMutableDictionary *gTypeMap = nil; NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; NSAppleEventDescriptor *userRecord = [self descriptorForKeyword:keyASUserRecordFields]; if (userRecord) { - NSEnumerator *userItems = [[userRecord gtm_arrayValue] objectEnumerator]; - NSString *key; - while ((key = [userItems nextObject])) { - NSString *value = [userItems nextObject]; - if (!value) { - _GTMDevLog(@"Got a key %@ with no value in %@", key, userItems); - return nil; + NSArray *userItems = [userRecord gtm_arrayValue]; + NSString *key = nil; + NSString *item; + GTM_FOREACH_OBJECT(item, userItems) { + if (key) { + // Save the pair and reset our state + [dictionary setObject:item forKey:key]; + key = nil; + } else { + // Save it for the next pair + key = item; } - [dictionary setObject:value forKey:key]; + } + if (key) { + _GTMDevLog(@"Got a key %@ with no value in %@", key, userItems); + return nil; } } else { NSUInteger count = [self numberOfItems]; @@ -291,10 +298,9 @@ static NSMutableDictionary *gTypeMap = nil; } - (NSAppleEventDescriptor*)gtm_appleEventDescriptor { - NSEnumerator* keys = [self keyEnumerator]; Class keyClass = nil; id key = nil; - while ((key = [keys nextObject])) { + GTM_FOREACH_KEY(key, self) { if (!keyClass) { if ([key isKindOfClass:[GTMFourCharCode class]]) { keyClass = [GTMFourCharCode class]; @@ -314,8 +320,7 @@ static NSMutableDictionary *gTypeMap = nil; NSAppleEventDescriptor *desc = [NSAppleEventDescriptor recordDescriptor]; if ([keyClass isEqual:[NSString class]]) { NSMutableArray *array = [NSMutableArray arrayWithCapacity:[self count] * 2]; - keys = [self keyEnumerator]; - while ((key = [keys nextObject])) { + GTM_FOREACH_KEY(key, self) { [array addObject:key]; [array addObject:[self objectForKey:key]]; } @@ -325,8 +330,7 @@ static NSMutableDictionary *gTypeMap = nil; } [desc setDescriptor:userRecord forKeyword:keyASUserRecordFields]; } else { - keys = [self keyEnumerator]; - while ((key = [keys nextObject])) { + GTM_FOREACH_KEY(key, self) { id value = [self objectForKey:key]; NSAppleEventDescriptor *valDesc = [value gtm_appleEventDescriptor]; if (!valDesc) { diff --git a/Foundation/GTMNSAppleEventDescriptor+FoundationTest.m b/Foundation/GTMNSAppleEventDescriptor+FoundationTest.m index 6df19ab..f346e99 100644 --- a/Foundation/GTMNSAppleEventDescriptor+FoundationTest.m +++ b/Foundation/GTMNSAppleEventDescriptor+FoundationTest.m @@ -242,7 +242,7 @@ NSAppleEventDescriptor *userRecord = [array gtm_appleEventDescriptor]; STAssertNotNil(userRecord, @""); [desc setDescriptor:userRecord forKeyword:keyASUserRecordFields]; - [GTMUnitTestDevLog expectPattern:@"Got a key bam with no value in <.*"]; + [GTMUnitTestDevLog expectPattern:@"Got a key bam with no value in \\(.*"]; dictionary = [desc gtm_objectValue]; STAssertNil(dictionary, @"Should be nil"); } diff --git a/Foundation/GTMNSArray+Merge.m b/Foundation/GTMNSArray+Merge.m index da58a02..0c1582d 100644 --- a/Foundation/GTMNSArray+Merge.m +++ b/Foundation/GTMNSArray+Merge.m @@ -56,8 +56,7 @@ : nil; id newItem = nil; - NSEnumerator *itemEnum = [sortedNewArray objectEnumerator]; - while ((newItem = [itemEnum nextObject])) { + GTM_FOREACH_OBJECT(newItem, sortedNewArray) { BOOL stillLooking = YES; while (oldIndex < oldCount && stillLooking) { // We must take care here, since Intel leaves junk in high bytes of diff --git a/Foundation/GTMNSDictionary+URLArguments.m b/Foundation/GTMNSDictionary+URLArguments.m index d67572c..89610e4 100644 --- a/Foundation/GTMNSDictionary+URLArguments.m +++ b/Foundation/GTMNSDictionary+URLArguments.m @@ -19,6 +19,7 @@ #import "GTMNSDictionary+URLArguments.h" #import "GTMNSString+URLArguments.h" #import "GTMMethodCheck.h" +#import "GTMDefines.h" @implementation NSDictionary (GTMNSDictionaryURLArgumentsAdditions) @@ -26,9 +27,8 @@ GTM_METHOD_CHECK(NSString, gtm_stringByEscapingForURLArgument); - (NSString *)gtm_httpArgumentsString { NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:[self count]]; - NSEnumerator* keyEnumerator = [self keyEnumerator]; NSString* key; - while ((key = [keyEnumerator nextObject])) { + GTM_FOREACH_KEY(key, self) { [arguments addObject:[NSString stringWithFormat:@"%@=%@", [key gtm_stringByEscapingForURLArgument], [[[self objectForKey:key] description] gtm_stringByEscapingForURLArgument]]]; diff --git a/Foundation/GTMNSFileManager+Path.m b/Foundation/GTMNSFileManager+Path.m index 2d71729..195cd9e 100644 --- a/Foundation/GTMNSFileManager+Path.m +++ b/Foundation/GTMNSFileManager+Path.m @@ -17,6 +17,7 @@ // #import "GTMNSFileManager+Path.h" +#import "GTMDefines.h" @implementation NSFileManager (GMFileManagerPathAdditions) @@ -34,10 +35,9 @@ return YES; NSString *actualPath = @"/"; - NSEnumerator *directoryEnumerator = [[path pathComponents] objectEnumerator]; NSString *directory; - while ((directory = [directoryEnumerator nextObject])) { + GTM_FOREACH_OBJECT(directory, [path pathComponents]) { actualPath = [actualPath stringByAppendingPathComponent:directory]; if ([self fileExistsAtPath:actualPath isDirectory:&isDir] && isDir) { @@ -84,10 +84,9 @@ NSMutableArray *paths = [NSMutableArray arrayWithCapacity:[basenames count]]; NSString *basename; - NSEnumerator *basenamesEnumerator = [basenames objectEnumerator]; // Convert all the |basenames| to full paths. - while ((basename = [basenamesEnumerator nextObject])) { + GTM_FOREACH_OBJECT(basename, basenames) { NSString *fullPath = [directoryPath stringByAppendingPathComponent:basename]; [paths addObject:fullPath]; } diff --git a/Foundation/GTMRegex.h b/Foundation/GTMRegex.h index c32eee2..3ef5604 100644 --- a/Foundation/GTMRegex.h +++ b/Foundation/GTMRegex.h @@ -84,12 +84,10 @@ _EXTERN NSString* kGTMRegexPatternErrorErrorString _INITIALIZE_AS(@"patternError // Example usage: // // NSArray *inputArrayOfStrings = ... -// NSEnumerator *enumerator = [inputArrayOfString objectEnumerator]; -// NSString *curStr = nil; // NSArray *matches = [NSMutableArray array]; // // GTMRegex *regex = [GTMRegex regexWithPattern:@"foo.*bar"]; -// while ((curStr = [enumerator nextObject]) != nil) { +// for (NSString *curStr in inputArrayOfStrings) { // if ([regex matchesString:curStr]) // [matches addObject:curStr]; // } diff --git a/Foundation/GTMRegex.m b/Foundation/GTMRegex.m index 71d5050..1027224 100644 --- a/Foundation/GTMRegex.m +++ b/Foundation/GTMRegex.m @@ -190,6 +190,7 @@ static NSString *const kReplacementPattern = return self; } +#if GTM_SUPPORT_GC - (void)finalize { // we used pattern_ as our flag that we initialized the regex_t if (pattern_) { @@ -200,6 +201,7 @@ static NSString *const kReplacementPattern = } [super finalize]; } +#endif - (void)dealloc { // we used pattern_ as our flag that we initialized the regex_t @@ -390,9 +392,8 @@ static NSString *const kReplacementPattern = // no replacements, they want to eat matches, nothing to do } else { // spin over the split up replacement - NSEnumerator *replacementEnumerator = [replacements objectEnumerator]; GTMRegexStringSegment *replacementSegment = nil; - while ((replacementSegment = [replacementEnumerator nextObject]) != nil) { + GTM_FOREACH_OBJECT(replacementSegment, replacements) { if (![replacementSegment isMatch]) { // not a match, raw text to put in [result appendString:[replacementSegment string]]; diff --git a/Foundation/GTMSignalHandler.m b/Foundation/GTMSignalHandler.m index edfff7d..557d8be 100644 --- a/Foundation/GTMSignalHandler.m +++ b/Foundation/GTMSignalHandler.m @@ -86,12 +86,14 @@ static CFSocketRef gRunLoopSocket = NULL; return self; } +#if GTM_SUPPORT_GC - (void)finalize { [self unregisterWithKQueue]; [super finalize]; } +#endif - (void)dealloc { [self unregisterWithKQueue]; diff --git a/GTM.xcodeproj/project.pbxproj b/GTM.xcodeproj/project.pbxproj index 1faf2b1..dff1850 100644 --- a/GTM.xcodeproj/project.pbxproj +++ b/GTM.xcodeproj/project.pbxproj @@ -135,6 +135,9 @@ 8B8EC87D0EF17C270044D13F /* GTMNSFileManager+Carbon.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B8EC87B0EF17C270044D13F /* GTMNSFileManager+Carbon.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8B8EC87E0EF17C270044D13F /* GTMNSFileManager+Carbon.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B8EC87C0EF17C270044D13F /* GTMNSFileManager+Carbon.m */; }; 8B8EC8800EF17C2F0044D13F /* GTMNSFileManager+CarbonTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B8EC87F0EF17C2F0044D13F /* GTMNSFileManager+CarbonTest.m */; }; + 8BA01B5D0F144BD800926923 /* GTMNSWorkspace+Running.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BA01B5B0F144BD800926923 /* GTMNSWorkspace+Running.m */; }; + 8BA01B5E0F144BD800926923 /* GTMNSWorkspace+Running.h in Headers */ = {isa = PBXBuildFile; fileRef = 8BA01B5C0F144BD800926923 /* GTMNSWorkspace+Running.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8BA01B600F144BE500926923 /* GTMNSWorkspace+RunningTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BA01B5F0F144BE500926923 /* GTMNSWorkspace+RunningTest.m */; }; 8BC045C20DAE899100C2D1CA /* GTMGeometryUtilsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE2800D198D0E009257D2 /* GTMGeometryUtilsTest.m */; }; 8BC046B90DAE8C4B00C2D1CA /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BC046B80DAE8C4B00C2D1CA /* ApplicationServices.framework */; }; 8BC04CD80DB003D800C2D1CA /* GTMMethodCheck.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B6F31F40DA3489B0052CA40 /* GTMMethodCheck.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -412,6 +415,9 @@ 8B8EC87B0EF17C270044D13F /* GTMNSFileManager+Carbon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSFileManager+Carbon.h"; sourceTree = ""; }; 8B8EC87C0EF17C270044D13F /* GTMNSFileManager+Carbon.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSFileManager+Carbon.m"; sourceTree = ""; }; 8B8EC87F0EF17C2F0044D13F /* GTMNSFileManager+CarbonTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSFileManager+CarbonTest.m"; sourceTree = ""; }; + 8BA01B5B0F144BD800926923 /* GTMNSWorkspace+Running.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSWorkspace+Running.m"; sourceTree = ""; }; + 8BA01B5C0F144BD800926923 /* GTMNSWorkspace+Running.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSWorkspace+Running.h"; sourceTree = ""; }; + 8BA01B5F0F144BE500926923 /* GTMNSWorkspace+RunningTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSWorkspace+RunningTest.m"; sourceTree = ""; }; 8BC046B80DAE8C4B00C2D1CA /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = /System/Library/Frameworks/ApplicationServices.framework; sourceTree = ""; }; 8BC04D140DB0061300C2D1CA /* RunMacOSUnitTests.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = RunMacOSUnitTests.sh; sourceTree = ""; }; 8BE2836B0DED0F130035B3F8 /* GTMFourCharCode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMFourCharCode.m; sourceTree = ""; }; @@ -772,6 +778,9 @@ 7F3EB38C0E5E09C700A7A75E /* GTMNSImage+Scaling.h */, 7F3EB38D0E5E09C700A7A75E /* GTMNSImage+Scaling.m */, 7F3EB3930E5E0A2100A7A75E /* GTMNSImage+ScalingTest.m */, + 8BA01B5B0F144BD800926923 /* GTMNSWorkspace+Running.m */, + 8BA01B5C0F144BD800926923 /* GTMNSWorkspace+Running.h */, + 8BA01B5F0F144BE500926923 /* GTMNSWorkspace+RunningTest.m */, F47F1C740D490E5C00925B8F /* GTMShading.h */, F435E4840DC8F3DC0069CDE8 /* TestData */, ); @@ -1028,6 +1037,7 @@ 8B3E292F0EEB53F8000681D8 /* GTMCarbonEvent.h in Headers */, F49FA8440EEF2AB700077669 /* GTMFileSystemKQueue.h in Headers */, 8B8EC87D0EF17C270044D13F /* GTMNSFileManager+Carbon.h in Headers */, + 8BA01B5E0F144BD800926923 /* GTMNSWorkspace+Running.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1426,6 +1436,7 @@ 8B3E292E0EEB53F8000681D8 /* GTMCarbonEvent.m in Sources */, F49FA8450EEF2AB700077669 /* GTMFileSystemKQueue.m in Sources */, 8B8EC87E0EF17C270044D13F /* GTMNSFileManager+Carbon.m in Sources */, + 8BA01B5D0F144BD800926923 /* GTMNSWorkspace+Running.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1449,6 +1460,7 @@ 8B8B102A0EEB8B2900E543D0 /* GTMCarbonEventTest.m in Sources */, 8B8B10FD0EEB8BC300E543D0 /* GTMUnitTestingUtilities.m in Sources */, 8B8B11000EEB8CD000E543D0 /* GTMGetURLHandlerTest.m in Sources */, + 8BA01B600F144BE500926923 /* GTMNSWorkspace+RunningTest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/GTMDefines.h b/GTMDefines.h index 5242e88..b41a6c8 100644 --- a/GTMDefines.h +++ b/GTMDefines.h @@ -155,6 +155,26 @@ 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 defined(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 + // ============================================================================ // ---------------------------------------------------------------------------- @@ -179,6 +199,23 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...); #define GTM_MACOS_SDK 1 #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 + #if GTM_IPHONE_SDK + // iPhone never needs GC + #define GTM_SUPPORT_GC 0 + #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 + #define GTM_SUPPORT_GC 0 + #else + #define GTM_SUPPORT_GC 1 + #endif + #endif +#endif + // 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 diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index cd07252..b592a31 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -188,6 +188,9 @@ Changes since 1.5.1 use GTMUnitTestDevLog, the log routines now go through _GTMDevLog so that they can be caught in GTMUnitTestDevLog and verified like any _GTMDevLog calls you may make. For an example of this in action see GTMCarbonEventTest.m. + Since we have turned this on, we have turned off using _debug frameworks + from the RunUnitTests.sh because it was reporting a pile of uninteresting + issues that were interfering with unittests. - Added GTMFileSystemKQueue. It provides a simple wrapper for kqueuing something in the file system and tracking changes to it. @@ -204,6 +207,18 @@ Changes since 1.5.1 for the messages in debug builds, to make it easier to validate messages that are only present in debug builds. +- Added GTM_SUPPORT_GC for controlling the inclusion of GC related code. + +- If you are using GTMUnitTestDevLog, it also tries to capture logs from + NSAssert. + +- Added GTM_FOREACH_OBJECT/GTM_FOREACH_KEY that uses NSEnumerator and + objectEnumerator/keyEnumerator on 10.4, but on 10.5+/iPhone uses + FastEnumeration. + +- GTMNSWorkspace+Running gives a variety of ways of determining the attributes + of running processes. + Release 1.5.1 Changes since 1.5.0 @@ -291,8 +306,7 @@ Changes since 1.0.0 - Added RunMacOSUnitTests shell script. We run this script for starting up our unittests because it turns on a variety of "enhancements" (such as zombies, - scribbling etc) to encourage our unittests to fail for us. It also will run - the unittests using the _debug frameworks if you have them. + scribbling etc) to encourage our unittests to fail for us. https://connect.apple.com/cgi-bin/WebObjects/MemberSite.woa/wa/getSoftware?bundleID=19915 diff --git a/UnitTesting/GTMAppKit+UnitTesting.m b/UnitTesting/GTMAppKit+UnitTesting.m index 0e21491..23c44e3 100644 --- a/UnitTesting/GTMAppKit+UnitTesting.m +++ b/UnitTesting/GTMAppKit+UnitTesting.m @@ -37,10 +37,9 @@ GTM_METHOD_CHECK(NSObject, gtm_unitTestEncodeState:); ENCODE_NSINTEGER(inCoder, [[self mainWindow] windowNumber], @"ApplicationMainWindow"); // Descend down into the windows allowing them to store their state - NSEnumerator *windowEnum = [[self windows] objectEnumerator]; NSWindow *window = nil; int i = 0; - while ((window = [windowEnum nextObject])) { + GTM_FOREACH_OBJECT(window, [self windows]) { if ([window isVisible]) { // Only record visible windows because invisible windows may be closing on us // This appears to happen differently in 64 bit vs 32 bit, and items @@ -190,10 +189,12 @@ GTM_METHOD_CHECK(NSObject, gtm_unitTestEncodeState:); } } // Descend down into the menuitems allowing them to store their state - NSEnumerator *menuItemEnum = [[self itemArray] objectEnumerator]; NSMenuItem *menuItem = nil; - for (int i = 0; (menuItem = [menuItemEnum nextObject]); ++i) { - [inCoder encodeObject:menuItem forKey:[NSString stringWithFormat:@"MenuItem %d", i]]; + int i = 0; + GTM_FOREACH_OBJECT(menuItem, [self itemArray]) { + [inCoder encodeObject:menuItem + forKey:[NSString stringWithFormat:@"MenuItem %d", i]]; + ++i; } } @@ -324,10 +325,9 @@ GTM_METHOD_CHECK(NSObject, gtm_unitTestEncodeState:); [super gtm_unitTestEncodeState:inCoder]; [inCoder encodeBool:[self isHidden] forKey:@"ViewIsHidden"]; if ([self gtm_shouldEncodeStateForSubviews]) { - NSEnumerator *subviewEnum = [[self subviews] objectEnumerator]; NSView *subview = nil; int i = 0; - while ((subview = [subviewEnum nextObject])) { + GTM_FOREACH_OBJECT(subview, [self subviews]) { [inCoder encodeObject:subview forKey:[NSString stringWithFormat:@"ViewSubView %d", i]]; i = i + 1; } diff --git a/UnitTesting/GTMNSObject+BindingUnitTesting.h b/UnitTesting/GTMNSObject+BindingUnitTesting.h index 462bb9b..55c3dfe 100644 --- a/UnitTesting/GTMNSObject+BindingUnitTesting.h +++ b/UnitTesting/GTMNSObject+BindingUnitTesting.h @@ -46,9 +46,8 @@ do { \ NSArray *errors = nil; \ BOOL isGood = GTMDoExposedBindingsFunctionCorrectly(a1Object, &errors); \ if (!isGood) { \ - NSEnumerator *errorEnum = [errors objectEnumerator]; \ NSString *failString; \ - while ((failString = [errorEnum nextObject])) { \ + GTM_FOREACH_OBJECT(failString, errors) { \ if (description) { \ STFail(@"%@: %@", failString, STComposeString(description, ##__VA_ARGS__)); \ } else { \ diff --git a/UnitTesting/GTMNSObject+BindingUnitTesting.m b/UnitTesting/GTMNSObject+BindingUnitTesting.m index 7a14ad6..d064a47 100644 --- a/UnitTesting/GTMNSObject+BindingUnitTesting.m +++ b/UnitTesting/GTMNSObject+BindingUnitTesting.m @@ -31,9 +31,8 @@ BOOL GTMDoExposedBindingsFunctionCorrectly(NSObject *object, NSArray *bindings = [object exposedBindings]; if ([bindings count]) { NSArray *bindingsToIgnore = [object gtm_unitTestExposedBindingsToIgnore]; - NSEnumerator *bindingsEnum = [bindings objectEnumerator]; NSString *bindingKey; - while ((bindingKey = [bindingsEnum nextObject])) { + GTM_FOREACH_OBJECT(bindingKey, bindings) { if (![bindingsToIgnore containsObject:bindingKey]) { Class theClass = [object valueClassForBinding:bindingKey]; if (!theClass) { @@ -54,9 +53,8 @@ BOOL GTMDoExposedBindingsFunctionCorrectly(NSObject *object, } // COV_NF_LINE - compiler bug NSArray *testValues = [object gtm_unitTestExposedBindingsTestValues:bindingKey]; - NSEnumerator *testEnum = [testValues objectEnumerator]; GTMBindingUnitTestData *testData; - while ((testData = [testEnum nextObject])) { + GTM_FOREACH_OBJECT(testData, testValues) { id valueToSet = [testData valueToSet]; [object setValue:valueToSet forKey:bindingKey]; id valueReceived = [object valueForKey:bindingKey]; diff --git a/UnitTesting/GTMNSObject+UnitTesting.m b/UnitTesting/GTMNSObject+UnitTesting.m index bc94729..f3bfbb1 100644 --- a/UnitTesting/GTMNSObject+UnitTesting.m +++ b/UnitTesting/GTMNSObject+UnitTesting.m @@ -417,11 +417,14 @@ static NSString *gGTMUnitTestSaveToDirectory = nil; // we're on an automated build system, so use the build products dir as an // override instead of writing on the desktop. NSDictionary *env = [[NSProcessInfo processInfo] environment]; - NSEnumerator *enumerator = [env keyEnumerator]; - NSString *key = nil; - while (((key = [enumerator nextObject]) != nil) && - ![key hasSuffix:@"BUILD_NUMBER"]) - ; + BOOL foundBuildNumber = NO; + NSString *key; + GTM_FOREACH_KEY(key, env) { + if ([key hasSuffix:@"BUILD_NUMBER"]) { + foundBuildNumber = YES; + break; + } + } if (key) { result = [env objectForKey:@"BUILT_PRODUCTS_DIR"]; } diff --git a/UnitTesting/GTMSenTestCase.m b/UnitTesting/GTMSenTestCase.m index 8d45dab..dbd137a 100644 --- a/UnitTesting/GTMSenTestCase.m +++ b/UnitTesting/GTMSenTestCase.m @@ -314,10 +314,9 @@ static void _GTMRunLeaks(void) { if (cExclusionsEnv) { NSString *exclusionsEnv = [NSString stringWithUTF8String:cExclusionsEnv]; NSArray *exclusionsArray = [exclusionsEnv componentsSeparatedByString:@","]; - NSEnumerator *exclusionsEnum = [exclusionsArray objectEnumerator]; NSString *exclusion; NSCharacterSet *wcSet = [NSCharacterSet whitespaceCharacterSet]; - while ((exclusion = [exclusionsEnum nextObject])) { + GTM_FOREACH_OBJECT(exclusion, exclusionsArray) { exclusion = [exclusion stringByTrimmingCharactersInSet:wcSet]; [exclusions appendFormat:@"-exclude \"%@\" ", exclusion]; } @@ -345,7 +344,8 @@ static __attribute__((constructor)) void _GTMInstallLeaks(void) { if (checkLeaks) { fprintf(stderr, "Leak Checking Enabled\n"); fflush(stderr); - _GTMDevAssert(atexit(&_GTMRunLeaks) == 0, + int ret = atexit(&_GTMRunLeaks); + _GTMDevAssert(ret == 0, @"Unable to install _GTMRunLeaks as an atexit handler (%d)", errno); } diff --git a/UnitTesting/GTMUIKit+UnitTesting.m b/UnitTesting/GTMUIKit+UnitTesting.m index d1cf2b2..56460be 100644 --- a/UnitTesting/GTMUIKit+UnitTesting.m +++ b/UnitTesting/GTMUIKit+UnitTesting.m @@ -93,13 +93,11 @@ [layer gtm_unitTestEncodeState:inCoder]; } if ([self gtm_shouldEncodeStateForSubviews]) { - NSEnumerator *subviewEnum = [[self subviews] objectEnumerator]; - UIView *subview = nil; int i = 0; - while ((subview = [subviewEnum nextObject])) { + for (UIView *subview in [self subviews]) { [inCoder encodeObject:subview forKey:[NSString stringWithFormat:@"ViewSubView %d", i]]; - i = i + 1; + i++; } } } diff --git a/UnitTesting/GTMUnitTestDevLog.m b/UnitTesting/GTMUnitTestDevLog.m index 227eb11..35e29ae 100644 --- a/UnitTesting/GTMUnitTestDevLog.m +++ b/UnitTesting/GTMUnitTestDevLog.m @@ -38,7 +38,7 @@ static void GTMDevLogDebugAssert(OSType componentSignature, length:StrLength(outputMsg) encoding:NSMacOSRomanStringEncoding] autorelease]; - _GTMDevLog(outLog); + _GTMDevLog(@"%@", outLog); // Don't want any percents in outLog honored } static inline void GTMInstallDebugAssertOutputHandler(void) { InstallDebugAssertOutputHandler(GTMDevLogDebugAssert); @@ -51,6 +51,57 @@ static inline void GTMInstallDebugAssertOutputHandler(void) {}; static inline void GTMUninstallDebugAssertOutputHandler(void) {}; #endif // GTM_IPHONE_SDK +@interface GTMUnttestDevLogAssertionHandler : NSAssertionHandler +@end + +@implementation GTMUnttestDevLogAssertionHandler +- (void)handleFailureInMethod:(SEL)selector + object:(id)object + file:(NSString *)fileName + lineNumber:(NSInteger)line + description:(NSString *)format, ... { + va_list argList; + va_start(argList, format); + NSString *descStr + = [[[NSString alloc] initWithFormat:format arguments:argList] autorelease]; + va_end(argList); + + // You need a format that will be useful in logs, but won't trip up Xcode or + // any other build systems parsing of the output. + NSString *outLog + = [NSString stringWithFormat:@"RecordedNSAssert in %@ - %@ (%@:%ld)", + NSStringFromSelector(selector), + descStr, + fileName, (long)line]; + _GTMDevLog(@"%@", outLog); // Don't want any percents in outLog honored + [NSException raise:NSInternalInconsistencyException + format:@"NSAssert raised"]; +} + +- (void)handleFailureInFunction:(NSString *)functionName + file:(NSString *)fileName + lineNumber:(NSInteger)line + description:(NSString *)format, ... { + va_list argList; + va_start(argList, format); + NSString *descStr + = [[[NSString alloc] initWithFormat:format arguments:argList] autorelease]; + va_end(argList); + + // You need a format that will be useful in logs, but won't trip up Xcode or + // any other build systems parsing of the output. + NSString *outLog + = [NSString stringWithFormat:@"RecordedNSAssert in %@ - %@ (%@:%ld)", + functionName, + descStr, + fileName, (long)line]; + _GTMDevLog(@"%@", outLog); // Don't want any percents in outLog honored + [NSException raise:NSInternalInconsistencyException + format:@"NSAssert raised"]; +} + +@end + @implementation GTMUnitTestDevLog // If unittests are ever being run on separate threads, this may need to be // made a thread local variable. @@ -70,11 +121,29 @@ static BOOL gTrackingEnabled = NO; + (void)enableTracking { GTMInstallDebugAssertOutputHandler(); + + NSMutableDictionary *threadDictionary + = [[NSThread currentThread] threadDictionary]; + if ([threadDictionary objectForKey:@"NSAssertionHandler"] != nil) { + NSLog(@"Warning: replacing NSAssertionHandler to capture assertions"); + } + + // Install an assertion handler to capture those. + GTMUnttestDevLogAssertionHandler *handler = + [[[GTMUnttestDevLogAssertionHandler alloc] init] autorelease]; + [threadDictionary setObject:handler forKey:@"NSAssertionHandler"]; + gTrackingEnabled = YES; } + (void)disableTracking { GTMUninstallDebugAssertOutputHandler(); + + // Clear our assertion handler back out. + NSMutableDictionary *threadDictionary + = [[NSThread currentThread] threadDictionary]; + [threadDictionary removeObjectForKey:@"NSAssertionHandler"]; + gTrackingEnabled = NO; } diff --git a/UnitTesting/GTMUnitTestingBindingTest.m b/UnitTesting/GTMUnitTestingBindingTest.m index aabac43..19ab5b0 100644 --- a/UnitTesting/GTMUnitTestingBindingTest.m +++ b/UnitTesting/GTMUnitTestingBindingTest.m @@ -33,9 +33,8 @@ // Iterates through all of our subviews testing the exposed bindings - (void)doSubviewBindingTest:(NSView*)view { NSArray *subviews = [view subviews]; - NSEnumerator *subviewEnum = [subviews objectEnumerator]; NSView *subview; - while ((subview = [subviewEnum nextObject])) { + GTM_FOREACH_OBJECT(subview, subviews) { GTMTestExposedBindings(subview, @"testing %@", subview); [self doSubviewBindingTest:subview]; } diff --git a/UnitTesting/RunMacOSUnitTests.sh b/UnitTesting/RunMacOSUnitTests.sh index f35092b..23897f0 100755 --- a/UnitTesting/RunMacOSUnitTests.sh +++ b/UnitTesting/RunMacOSUnitTests.sh @@ -189,15 +189,6 @@ if [ ! $GTM_DISABLE_ZOMBIES ]; then export NSZombieEnabled=YES fi -# If we have debug libraries on the machine, we'll use them -# unless a target has specifically turned them off -if [ ! $GTM_NO_DEBUG_FRAMEWORKS ]; then - if [ -f "/System/Library/Frameworks/CoreFoundation.framework/Versions/Current/CoreFoundation_debug" ]; then - GTMXcodeNote ${LINENO} "Using _debug frameworks" - export DYLD_IMAGE_SUFFIX=_debug - fi -fi - # If leaks testing is enabled, we have to go through our convoluted path # to handle architectures that don't allow us to do leak testing. if [ GTM_ENABLE_LEAKS ]; then diff --git a/iPhone/GTMABAddressBook.m b/iPhone/GTMABAddressBook.m index 05a5523..05dc27f 100644 --- a/iPhone/GTMABAddressBook.m +++ b/iPhone/GTMABAddressBook.m @@ -441,10 +441,8 @@ typedef struct { NSArray *people = GTMCFAutorelease(ABGroupCopyArrayOfAllMembers([self recordRef])); NSMutableArray *gtmPeople = [NSMutableArray arrayWithCapacity:[people count]]; - NSEnumerator *enumerator = [people objectEnumerator]; - ABRecordRef person; - while ((person = [enumerator nextObject])) { - [gtmPeople addObject:[GTMABPerson recordWithRecord:person]]; + for (id person in people) { + [gtmPeople addObject:[GTMABPerson recordWithRecord:(ABRecordRef)person]]; } return gtmPeople; } -- cgit v1.2.3