diff options
author | thomasvl@gmail.com <thomasvl@gmail.com@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3> | 2008-08-12 17:21:32 +0000 |
---|---|---|
committer | thomasvl@gmail.com <thomasvl@gmail.com@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3> | 2008-08-12 17:21:32 +0000 |
commit | 7063d76a007fbf636250d7199d6f24ec487163b1 (patch) | |
tree | 5a1f2f0a4b597f62df3e2fe858d76d37b22bbe89 | |
parent | 43982f07ba6a0a9839e32e774855c9d2068e9d5e (diff) |
- Added GTMNSMakeUncollectable for forcing objects to survive in a GC world.
- Added GTMCFAutorelease to make the [GTMNSMakeCollectable(cfFoo) autorelease] simpler and clearer, it's now just GTMCFAutorelease(cfFoo), and works in both GC and non-GC world.
- Added GTMIsGarbageCollectionEnabled to GTMGarbageCollection.h. See the note there for it's usage.
- Disabled the unittests for things on top of NSAppleScript in a GC world since Apple has bugs and it can crash. See the unittest for a note about it.
- GTMStackTrace now can figure out ObjC symbols. Downside it is now ObjC only.
- GTMFourCharCode can now be used with NSAppleEventDescriptors easily. typeType, typeKeyword, typeApplSignature, and typeEnumerated all get turned into GTMFourCharCodes.
- Fixed up crash in GTMLoggerRingBufferWriter when used with GC on.
- Significant updates to GTMNSAppleScript+Handler allowing you to list all handlers and properties (including inherited) and cleans up several errors in how scripting was being handled.
52 files changed, 2050 insertions, 601 deletions
diff --git a/AppKit/GTMLargeTypeWindow.h b/AppKit/GTMLargeTypeWindow.h index 9bb6b85..52316fc 100644 --- a/AppKit/GTMLargeTypeWindow.h +++ b/AppKit/GTMLargeTypeWindow.h @@ -28,6 +28,15 @@ // = [[[GTMLargeTypeWindow alloc] initWithString:@"Foo"] autorelease]; // [window makeKeyAndOrderFront:nil]; +// NB This class appears to have a problem with GC on 10.5.4 and below. +// Radar 6137322 CIFilter crashing when run with GC enabled +// This appears to be an Apple bug with GC. +// We do a copy animation that causes things to crash, but only with GC +// on. Currently I have left this enabled in GTMLargeTypeWindow pending +// info from Apple on the bug. It's hard to reproduce, and only appears +// at this time on our test machines. +// Dual-Core Intel Xeon with ATI Radeon X1300 + // Amount of time to fade the window in or out const NSTimeInterval kGTMLargeTypeWindowFadeTime; diff --git a/AppKit/GTMLargeTypeWindow.m b/AppKit/GTMLargeTypeWindow.m index d84ce42..709099d 100644 --- a/AppKit/GTMLargeTypeWindow.m +++ b/AppKit/GTMLargeTypeWindow.m @@ -16,10 +16,12 @@ // the License. // +#import <QuartzCore/QuartzCore.h> + #import "GTMLargeTypeWindow.h" #import "GTMGeometryUtils.h" #import "GTMNSBezierPath+RoundRect.h" - +#import "GTMMethodCheck.h" // Amount of time to fade the window in or out const NSTimeInterval kGTMLargeTypeWindowFadeTime = 0.333; @@ -30,15 +32,23 @@ static const CGFloat kEdgeInset = 16.0; // Give us an alpha value for our backing window static const CGFloat kTwoThirdsAlpha = 0.66; -@interface GTMLargeTypeBackgroundView : NSView +@interface GTMLargeTypeCopyAnimation : NSAnimation +@end + +@interface GTMLargeTypeBackgroundView : NSView { + CIFilter *transition_; + GTMLargeTypeCopyAnimation *animation_; +} +- (void)animateCopy; @end @interface GTMLargeTypeWindow (GTMLargeTypeWindowPrivate) + (CGFloat)displayWidth; -- (void)startFadeInAnimation; +- (void)animateWithEffect:(NSString*)effect; @end @implementation GTMLargeTypeWindow + - (id)initWithString:(NSString *)string { if ([string length] == 0) { _GTMDevLog(@"GTMLargeTypeWindow got an empty string"); @@ -49,18 +59,44 @@ static const CGFloat kTwoThirdsAlpha = 0.66; NSMutableAttributedString *attrString = [[[NSMutableAttributedString alloc] initWithString:string] autorelease]; + NSRange fullRange = NSMakeRange(0, [string length]); + [attrString addAttribute:NSForegroundColorAttributeName + value:[NSColor whiteColor] + range:fullRange]; + + NSMutableParagraphStyle *style + = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] autorelease]; + [style setAlignment:NSCenterTextAlignment]; + [attrString addAttribute:NSParagraphStyleAttributeName + value:style + range:fullRange]; + + NSShadow *textShadow = [[[NSShadow alloc] init] autorelease]; + [textShadow setShadowOffset:NSMakeSize( 5, -5 )]; + [textShadow setShadowBlurRadius:10]; + [textShadow setShadowColor:[NSColor colorWithCalibratedWhite:0 + alpha:kTwoThirdsAlpha]]; + [attrString addAttribute:NSShadowAttributeName + value:textShadow + range:fullRange]; + // Try and find a size that fits without iterating too many times. // We start going 50 pixels at a time, then 10, then 1 int size = -26; // start at 24 (-26 + 50) int offsets[] = { 50, 10, 1 }; + NSSize bigSize = NSMakeSize(MAXFLOAT, MAXFLOAT); + NSStringDrawingOptions options = (NSStringDrawingUsesDeviceMetrics | + NSStringDrawingOneShot); for (size_t i = 0; i < sizeof(offsets) / sizeof(int); ++i) { for(size = size + offsets[i]; size >= 24 && size < 300; size += offsets[i]) { - NSFont *textFont = [NSFont boldSystemFontOfSize:size]; - NSDictionary *fontAttr - = [NSDictionary dictionaryWithObject:textFont - forKey:NSFontAttributeName]; - NSSize textSize = [string sizeWithAttributes:fontAttr]; - if (textSize.width > displayWidth) { + NSFont *font = [NSFont boldSystemFontOfSize:size] ; + [attrString addAttribute:NSFontAttributeName + value:font + range:fullRange]; + NSRect textSize = [attrString boundingRectWithSize:bigSize + options:options]; + NSSize maxAdvanceSize = [font maximumAdvancement]; + if (textSize.size.width + maxAdvanceSize.width > displayWidth) { size = size - offsets[i]; break; } @@ -73,30 +109,9 @@ static const CGFloat kTwoThirdsAlpha = 0.66; } else if (size < 24) { size = 24; } - - NSRange fullRange = NSMakeRange(0, [string length]); [attrString addAttribute:NSFontAttributeName value:[NSFont boldSystemFontOfSize:size] range:fullRange]; - [attrString addAttribute:NSForegroundColorAttributeName - value:[NSColor whiteColor] - range:fullRange]; - - NSMutableParagraphStyle *style - = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] autorelease]; - [style setAlignment:NSCenterTextAlignment]; - [attrString addAttribute:NSParagraphStyleAttributeName - value:style - range:fullRange]; - - NSShadow *textShadow = [[[NSShadow alloc] init] autorelease]; - [textShadow setShadowOffset:NSMakeSize( 5, -5 )]; - [textShadow setShadowBlurRadius:10]; - [textShadow setShadowColor:[NSColor colorWithCalibratedWhite:0 - alpha:kTwoThirdsAlpha]]; - [attrString addAttribute:NSShadowAttributeName - value:textShadow - range:fullRange]; return [self initWithAttributedString:attrString]; } @@ -186,12 +201,26 @@ static const CGFloat kTwoThirdsAlpha = 0.66; [pb setString:[[firstResponder textStorage] string] forType:NSStringPboardType]; } + + // Give the user some feedback that a copy has occurred + [(GTMLargeTypeBackgroundView*)[self contentView] animateCopy]; } - (BOOL)canBecomeKeyWindow { return YES; } +- (BOOL)performKeyEquivalent:(NSEvent *)theEvent { + NSString *chars = [theEvent charactersIgnoringModifiers]; + NSUInteger flags = ([theEvent modifierFlags] & + NSDeviceIndependentModifierFlagsMask); + BOOL isValid = (flags == NSCommandKeyMask) && [chars isEqualToString:@"c"]; + if (isValid) { + [self copy:self]; + } + return isValid; +} + - (void)keyDown:(NSEvent *)theEvent { [self close]; } @@ -204,30 +233,18 @@ static const CGFloat kTwoThirdsAlpha = 0.66; } - (void)makeKeyAndOrderFront:(id)sender { - [self startFadeInAnimation]; [super makeKeyAndOrderFront:sender]; + [self animateWithEffect:NSViewAnimationFadeInEffect]; } - (void)orderFront:(id)sender { - [self startFadeInAnimation]; [super orderFront:sender]; + [self animateWithEffect:NSViewAnimationFadeInEffect]; } - (void)orderOut:(id)sender { - NSDictionary *fadeOut = [NSDictionary dictionaryWithObjectsAndKeys: - self, NSViewAnimationTargetKey, - NSViewAnimationFadeOutEffect, NSViewAnimationEffectKey, - nil]; - NSArray *animation = [NSArray arrayWithObject:fadeOut]; - NSViewAnimation *viewAnim - = [[[NSViewAnimation alloc] initWithViewAnimations:animation] autorelease]; - [viewAnim setDuration:kGTMLargeTypeWindowFadeTime]; - [viewAnim startAnimation]; - NSDate *fadeOutDate - = [NSDate dateWithTimeIntervalSinceNow:kGTMLargeTypeWindowFadeTime]; - // We have a local run loop because if this is called as part of a close - // our window will be hidden immediately before it has a chance to fade. - [[NSRunLoop currentRunLoop] runUntilDate:fadeOutDate]; + [self animateWithEffect:NSViewAnimationFadeOutEffect]; + [super orderOut:sender]; } + (CGFloat)displayWidth { @@ -237,40 +254,105 @@ static const CGFloat kTwoThirdsAlpha = 0.66; return NSWidth( screenRect ) * 11.0 / 12.0 - 2.0 * kEdgeInset; } -- (void)startFadeInAnimation { - // If we aren't already fully visible, start fading us in. - if ([self alphaValue] < 1.0) { - NSDictionary *fadeIn = [NSDictionary dictionaryWithObjectsAndKeys: - self, NSViewAnimationTargetKey, - NSViewAnimationFadeInEffect, NSViewAnimationEffectKey, - nil]; - NSArray *animation = [NSArray arrayWithObject:fadeIn]; - NSViewAnimation *viewAnim - = [[[NSViewAnimation alloc] initWithViewAnimations:animation] autorelease]; - [viewAnim setDuration:kGTMLargeTypeWindowFadeTime]; - [viewAnim startAnimation]; - } +- (void)animateWithEffect:(NSString*)effect { + NSDictionary *fadeIn = [NSDictionary dictionaryWithObjectsAndKeys: + self, NSViewAnimationTargetKey, + effect, NSViewAnimationEffectKey, + nil]; + NSArray *animation = [NSArray arrayWithObject:fadeIn]; + NSViewAnimation *viewAnim + = [[[NSViewAnimation alloc] initWithViewAnimations:animation] autorelease]; + [viewAnim setDuration:kGTMLargeTypeWindowFadeTime]; + [viewAnim setAnimationBlockingMode:NSAnimationBlocking]; + [viewAnim startAnimation]; } @end @implementation GTMLargeTypeBackgroundView +GTM_METHOD_CHECK(NSBezierPath, gtm_appendBezierPathWithRoundRect:cornerRadius:); - (BOOL)isOpaque { return NO; } - (void)drawRect:(NSRect)rect { - [[NSColor colorWithDeviceWhite:0 alpha:kTwoThirdsAlpha] set]; rect = [self bounds]; - NSBezierPath *roundRect = [NSBezierPath bezierPath]; CGFloat minRadius = MIN(NSWidth(rect), NSHeight(rect)) * 0.5f; [roundRect gtm_appendBezierPathWithRoundRect:rect cornerRadius:MIN(minRadius, 32)]; - [roundRect addClip]; - NSRectFill(rect); - [super drawRect:rect]; + [roundRect addClip]; + if (transition_) { + NSNumber *val = [NSNumber numberWithFloat:[animation_ currentValue]]; + [transition_ setValue:val forKey:@"inputTime"]; + CIImage *outputCIImage = [transition_ valueForKey:@"outputImage"]; + [outputCIImage drawInRect:rect + fromRect:rect + operation:NSCompositeSourceOver + fraction:1.0]; + } else { + [[NSColor colorWithDeviceWhite:0 alpha:kTwoThirdsAlpha] set]; + + NSRectFill(rect); + } } +- (void)animateCopy { + // This does a photocopy swipe to show folks that their copy has succceeded + // Store off a copy of our background + NSRect bounds = [self bounds]; + NSBitmapImageRep *rep = [self bitmapImageRepForCachingDisplayInRect:bounds]; + NSGraphicsContext *context = [NSGraphicsContext graphicsContextWithBitmapImageRep:rep]; + [NSGraphicsContext saveGraphicsState]; + [NSGraphicsContext setCurrentContext:context]; + [self drawRect:bounds]; + [NSGraphicsContext restoreGraphicsState]; + CIVector *extent = [CIVector vectorWithX:bounds.origin.x + Y:bounds.origin.y + Z:bounds.size.width + W:bounds.size.height]; + CIFilter *transition = [CIFilter filterWithName:@"CICopyMachineTransition"]; + [transition setDefaults]; + [transition setValue:extent + forKey:@"inputExtent"]; + CIImage *image = [[CIImage alloc] initWithBitmapImageRep:rep]; + + [transition setValue:image forKey:@"inputImage"]; + [transition setValue:image forKey:@"inputTargetImage"]; + [transition setValue:[NSNumber numberWithInt:0] + forKey:@"inputTime"]; + [transition valueForKey:@"outputImage"]; + [image release]; + transition_ = [transition retain]; + animation_ = [[GTMLargeTypeCopyAnimation alloc] initWithDuration:0.5 + animationCurve:NSAnimationLinear]; + [animation_ setFrameRate:0.0f]; + [animation_ setDelegate:self]; + [animation_ setAnimationBlockingMode:NSAnimationBlocking]; + [animation_ startAnimation]; +} + +- (void)animationDidEnd:(NSAnimation*)animation { + [animation_ release]; + animation_ = nil; + [transition_ release]; + transition_ = nil; + [self display]; +} + +- (float)animation:(NSAnimation*)animation + valueForProgress:(NSAnimationProgress)progress { + // This gives us half the copy animation, so we don't swing back + // Don't want too much gratuitous effect + // 0.6 is required by experimentation. 0.5 doesn't do it + return progress * 0.6f; +} +@end + +@implementation GTMLargeTypeCopyAnimation +- (void)setCurrentProgress:(NSAnimationProgress)progress { + [super setCurrentProgress:progress]; + [[self delegate] display]; +} @end diff --git a/AppKit/GTMLargeTypeWindowTest.m b/AppKit/GTMLargeTypeWindowTest.m index b4a9fe4..d9e56c2 100644 --- a/AppKit/GTMLargeTypeWindowTest.m +++ b/AppKit/GTMLargeTypeWindowTest.m @@ -20,6 +20,8 @@ #import "GTMLargeTypeWindow.h" #import "GTMNSObject+UnitTesting.h" #import "GTMUnitTestDevLog.h" +#import "GTMGarbageCollection.h" +#import "GTMSystemVersion.h" NSString *const kLongTextBlock = @"`Twas brillig, and the slithy toves " @@ -49,11 +51,31 @@ NSString *const kLongTextBlock = NSString *const kMediumTextBlock = @"For the Snark was a Boojum, you see."; +NSString *const kShortTextBlock = @"Short"; + @interface GTMLargeTypeWindowTest : GTMTestCase @end @implementation GTMLargeTypeWindowTest -- (void)testLargeTypeWindow { +- (BOOL)shouldDoAnimateCopy { + // NOTE: Animated copy tests are disabled when GC is on. + // See the comment/warning in the GTMLargeTypeWindow.h for more details, + // but we disable them to avoid the tests failing (crashing) when it's Apple's + // bug. Please bump the system check as appropriate when new systems are + // tested. Currently broken on 10.5.4 and below. + // Radar 6137322 CIFilter crashing when run with GC enabled + long major, minor, bugfix; + [GTMSystemVersion getMajor:&major minor:&minor bugFix:&bugfix]; + if (!(GTMIsGarbageCollectionEnabled() + && major <= 10 && minor <= 5 && bugfix <= 4)) { + return YES; + } else { + NSLog(@"--- animated copy not run because of GC incompatibilites ---"); + return NO; + } +} + +- (void)testLargeTypeWindowIllegalInits { [GTMUnitTestDevLog expectString:@"GTMLargeTypeWindow got an empty string"]; GTMLargeTypeWindow *window = [[[GTMLargeTypeWindow alloc] initWithString:@""] autorelease]; @@ -82,9 +104,11 @@ NSString *const kMediumTextBlock = @"For the Snark was a Boojum, you see."; [GTMUnitTestDevLog expectString:@"GTMLargeTypeWindow got an empty image"]; window = [[[GTMLargeTypeWindow alloc] initWithImage:nil] autorelease]; STAssertNil(window, nil); - - window = [[[GTMLargeTypeWindow alloc] - initWithString:kMediumTextBlock] autorelease]; +} + +- (void)testLargeTypeWindowMediumText { + GTMLargeTypeWindow *window = [[[GTMLargeTypeWindow alloc] + initWithString:kMediumTextBlock] autorelease]; STAssertNotNil(window, nil); STAssertTrue([window canBecomeKeyWindow], nil); [window makeKeyAndOrderFront:nil]; @@ -94,52 +118,80 @@ NSString *const kMediumTextBlock = @"For the Snark was a Boojum, you see."; GTMAssertObjectStateEqualToStateNamed(window, @"GTMLargeTypeWindowMediumTextTest", nil); - [window copy:nil]; - NSPasteboard *pb = [NSPasteboard generalPasteboard]; - NSString *pbString = [pb stringForType:NSStringPboardType]; - STAssertEqualObjects(pbString, kMediumTextBlock, nil); + if ([self shouldDoAnimateCopy]) { + [window copy:nil]; + NSPasteboard *pb = [NSPasteboard generalPasteboard]; + NSString *pbString = [pb stringForType:NSStringPboardType]; + STAssertEqualObjects(pbString, kMediumTextBlock, nil); + } [window keyDown:nil]; - - window = [[[GTMLargeTypeWindow alloc] initWithString:@"Short"] autorelease]; +} + +- (void)testLargeTypeWindowShortText { + GTMLargeTypeWindow *window = [[[GTMLargeTypeWindow alloc] + initWithString:kShortTextBlock] autorelease]; STAssertNotNil(window, nil); STAssertTrue([window canBecomeKeyWindow], nil); [window makeKeyAndOrderFront:nil]; - endDate = [NSDate dateWithTimeIntervalSinceNow:kGTMLargeTypeWindowFadeTime]; + NSDate *endDate + = [NSDate dateWithTimeIntervalSinceNow:kGTMLargeTypeWindowFadeTime]; [[NSRunLoop currentRunLoop] runUntilDate:endDate]; GTMAssertObjectStateEqualToStateNamed(window, @"GTMLargeTypeWindowShortTextTest", nil); - [window copy:nil]; - pbString = [pb stringForType:NSStringPboardType]; - STAssertEqualObjects(pbString, @"Short", nil); + if ([self shouldDoAnimateCopy]) { + [window copy:nil]; + NSPasteboard *pb = [NSPasteboard generalPasteboard]; + NSString *pbString = [pb stringForType:NSStringPboardType]; + STAssertEqualObjects(pbString, kShortTextBlock, nil); + } [window resignKeyWindow]; +} - window = [[[GTMLargeTypeWindow alloc] - initWithString:kLongTextBlock] autorelease]; +- (void)testLargeTypeWindowLongText { + GTMLargeTypeWindow *window = [[[GTMLargeTypeWindow alloc] + initWithString:kLongTextBlock] autorelease]; STAssertNotNil(window, nil); [window orderFront:nil]; - endDate = [NSDate dateWithTimeIntervalSinceNow:kGTMLargeTypeWindowFadeTime]; + NSDate *endDate + = [NSDate dateWithTimeIntervalSinceNow:kGTMLargeTypeWindowFadeTime]; [[NSRunLoop currentRunLoop] runUntilDate:endDate]; // Can't do state for long text as it will wrap differently on different // sized screens. GTMAssertObjectStateEqualToStateNamed(window, @"GTMLargeTypeWindowLongTextTest", nil); + if ([self shouldDoAnimateCopy]) { + [window copy:nil]; + NSPasteboard *pb = [NSPasteboard generalPasteboard]; + NSString *pbString = [pb stringForType:NSStringPboardType]; + STAssertEqualObjects(pbString, kLongTextBlock, nil); + } [window keyDown:nil]; - +} + +- (void)testLargeTypeWindowImageText { NSImage *image = [NSApp applicationIconImage]; - window = [[[GTMLargeTypeWindow alloc] initWithImage:image] autorelease]; + GTMLargeTypeWindow *window = [[[GTMLargeTypeWindow alloc] + initWithImage:image] autorelease]; STAssertNotNil(window, nil); [window makeKeyAndOrderFront:nil]; - endDate = [NSDate dateWithTimeIntervalSinceNow:kGTMLargeTypeWindowFadeTime]; + NSDate *endDate + = [NSDate dateWithTimeIntervalSinceNow:kGTMLargeTypeWindowFadeTime]; [[NSRunLoop currentRunLoop] runUntilDate:endDate]; GTMAssertObjectStateEqualToStateNamed(window, @"GTMLargeTypeWindowImageTest", nil); - [window copy:nil]; - // Pasteboard should not change for an image - pbString = [pb stringForType:NSStringPboardType]; - STAssertEqualObjects(pbString, @"Short", nil); + NSString *testString = @"TestString"; + NSPasteboard *pb = [NSPasteboard generalPasteboard]; + [pb declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:self]; + [pb setString:testString forType:NSStringPboardType]; + if ([self shouldDoAnimateCopy]) { + [window copy:nil]; + // Pasteboard should not change for an image + NSString *pbString = [pb stringForType:NSStringPboardType]; + STAssertEqualObjects(pbString, testString, nil); + } [window resignKeyWindow]; } diff --git a/AppKit/GTMLinearRGBShadingTest.m b/AppKit/GTMLinearRGBShadingTest.m index cb65572..d70b878 100644 --- a/AppKit/GTMLinearRGBShadingTest.m +++ b/AppKit/GTMLinearRGBShadingTest.m @@ -69,6 +69,24 @@ STAssertEqualsWithAccuracy(theColor[2], newValue, 0.001, nil); STAssertEqualsWithAccuracy(theColor[3], newValue, 0.001, nil); } + // Create a shading with 1 color to test that special handling + NSColor *purple = [NSColor purpleColor]; + NSColor *singleColor[1] = { purple }; + CGFloat singlePosition[1] = { 0.5 }; + theShading = + [GTMLinearRGBShading shadingWithColors:singleColor + fromSpaceNamed:NSCalibratedRGBColorSpace + atPositions:singlePosition + count:1]; + // test over a range to make sure we always get the same color + for (NSUInteger i = 0; i < kColorCount; i++) { + CGFloat newValue = kColorIncrement * i; + CGFloat *theColor = (CGFloat*)[theShading valueAtPosition:newValue]; + STAssertEqualsWithAccuracy(theColor[0], [purple redComponent], 0.001, nil); + STAssertEqualsWithAccuracy(theColor[1], [purple greenComponent], 0.001, nil); + STAssertEqualsWithAccuracy(theColor[2], [purple blueComponent], 0.001, nil); + STAssertEqualsWithAccuracy(theColor[3], [purple alphaComponent], 0.001, nil); + } } - (void)testShadeFunction { @@ -83,6 +101,7 @@ } - (void)testColorSpace { + // Calibrated RGB GTMLinearRGBShading *theShading = [GTMLinearRGBShading shadingWithColors:nil fromSpaceNamed:NSCalibratedRGBColorSpace @@ -91,5 +110,23 @@ CGColorSpaceRef theColorSpace = [theShading colorSpace]; STAssertNotNULL(theColorSpace, nil); STAssertEquals(CFGetTypeID(theColorSpace), CGColorSpaceGetTypeID(), nil); + + // Device RGB + theShading = + [GTMLinearRGBShading shadingWithColors:nil + fromSpaceNamed:NSDeviceRGBColorSpace + atPositions:nil + count:0]; + theColorSpace = [theShading colorSpace]; + STAssertNotNULL(theColorSpace, nil); + STAssertEquals(CFGetTypeID(theColorSpace), CGColorSpaceGetTypeID(), nil); + + // Device CMYK (not supported) + theShading = + [GTMLinearRGBShading shadingWithColors:nil + fromSpaceNamed:NSDeviceCMYKColorSpace + atPositions:nil + count:0]; + STAssertNULL(theShading, nil); } @end diff --git a/AppKit/GTMLoginItems.m b/AppKit/GTMLoginItems.m index 61a2120..1e59e70 100644 --- a/AppKit/GTMLoginItems.m +++ b/AppKit/GTMLoginItems.m @@ -57,22 +57,28 @@ NSString * const kGTMLoginItemsHiddenKey = @"Hide"; + (BOOL)compileAndRunScript:(NSString *)script withError:(NSError **)errorInfo { if ([script length] == 0) { + // COV_NF_START - no real way to test this if (errorInfo) *errorInfo = [NSError errorWithDomain:@"GTMLoginItems" code:-90 userInfo:nil]; return NO; + // COV_NF_END } NSAppleScript *query = [[[NSAppleScript alloc] initWithSource:script] autorelease]; NSDictionary *errDict = nil; if ( ![query compileAndReturnError:&errDict]) { + // COV_NF_START - no real way to test this if (errorInfo) *errorInfo = [NSError errorWithDomain:@"GTMLoginItems" code:-91 userInfo:errDict]; return NO; + // COV_NF_END } NSAppleEventDescriptor *scriptResult = [query executeAndReturnError:&errDict]; if (!scriptResult) { + // COV_NF_START - no real way to test this if (*errorInfo) *errorInfo = [NSError errorWithDomain:@"GTMLoginItems" code:-92 userInfo:errDict]; return NO; + // COV_NF_END } // we don't process the result return YES; @@ -90,19 +96,23 @@ NSString * const kGTMLoginItemsHiddenKey = @"Hide"; NSString *querySource = @"tell application \"System Events\" to get properties of login items"; query = [[NSAppleScript alloc] initWithSource:querySource]; if ( ![query compileAndReturnError:&errDict]) { + // COV_NF_START - no real way to test this if (errorInfo) *errorInfo = [NSError errorWithDomain:@"GTMLoginItems" code:-1 userInfo:errDict]; [query release]; query = nil; return nil; + // COV_NF_END } } // run the script NSAppleEventDescriptor *scriptResult = [query executeAndReturnError:&errDict]; if (!scriptResult) { + // COV_NF_START - no real way to test this if (*errorInfo) *errorInfo = [NSError errorWithDomain:@"GTMLoginItems" code:-2 userInfo:errDict]; return nil; + // COV_NF_END } // build our results NSMutableArray *result = [NSMutableArray array]; diff --git a/AppKit/GTMLoginItemsTest.m b/AppKit/GTMLoginItemsTest.m index ec08a85..980f772 100644 --- a/AppKit/GTMLoginItemsTest.m +++ b/AppKit/GTMLoginItemsTest.m @@ -23,14 +23,12 @@ // we don't really run this test because if someone had it in some automated // tests, then if something did fail, it could leave things in the login items // on the computer which could be a nasty surprise. -#define TESTS_ENABLED 0 +#define MODIFICATION_TESTS_ENABLED 0 @interface GTMLoginItemsTest : GTMTestCase @end -#if TESTS_ENABLED - static BOOL ItemsListHasPath(NSArray *items, NSString *path) { NSDictionary *item = nil; NSEnumerator *itemsEnum = [items objectEnumerator]; @@ -43,14 +41,42 @@ static BOOL ItemsListHasPath(NSArray *items, NSString *path) { return NO; } -#endif // TESTS_ENABLED - @implementation GTMLoginItemsTest -- (void)testGTMLoginItems { +- (void)testNoModification { -#if TESTS_ENABLED + NSError *error = nil; + NSString *bogusAppPath = @"/Applications/AppThatDoesNotExist.app"; + NSString *bogusAppName = @"AppThatDoesNotExist"; + + // fetch the starting values + NSArray *initialItems = [GTMLoginItems loginItems:&error]; + STAssertNotNil(initialItems, @"shouldn't be nil (%@)", error); + STAssertFalse(ItemsListHasPath(initialItems, bogusAppPath), + @"bogusApp shouldn't be in list to start for test (%@)", initialItems); + + // check by path + STAssertFalse([GTMLoginItems pathInLoginItems:bogusAppPath], nil); + + // check by name + STAssertFalse([GTMLoginItems itemWithNameInLoginItems:bogusAppName], nil); + + // remove it by path + [GTMLoginItems removePathFromLoginItems:bogusAppPath]; + NSArray *curItems = [GTMLoginItems loginItems:nil]; + STAssertEqualObjects(initialItems, curItems, nil); + + // remove it by name + [GTMLoginItems removeItemWithNameFromLoginItems:bogusAppName]; + curItems = [GTMLoginItems loginItems:nil]; + STAssertEqualObjects(initialItems, curItems, nil); + +} +- (void)testModification { + +#if MODIFICATION_TESTS_ENABLED + NSError *error = nil; NSString *textEditPath = @"/Applications/TextEdit.app"; NSString *textEditName = @"TextEdit"; @@ -79,7 +105,7 @@ static BOOL ItemsListHasPath(NSArray *items, NSString *path) { // check by path STAssertFalse([GTMLoginItems pathInLoginItems:textEditPath], nil); - + // check by name STAssertFalse([GTMLoginItems itemWithNameInLoginItems:textEditName], nil); @@ -105,8 +131,8 @@ static BOOL ItemsListHasPath(NSArray *items, NSString *path) { // check by name STAssertFalse([GTMLoginItems itemWithNameInLoginItems:textEditName], nil); -#endif // TESTS_ENABLED - +#endif // MODIFICATION_TESTS_ENABLED + } @end diff --git a/AppKit/GTMNSBezierPath+CGPath.m b/AppKit/GTMNSBezierPath+CGPath.m index 3bc6de9..656a139 100644 --- a/AppKit/GTMNSBezierPath+CGPath.m +++ b/AppKit/GTMNSBezierPath+CGPath.m @@ -58,9 +58,9 @@ case NSClosePathBezierPathElement: CGPathCloseSubpath(thePath); break; - default: + default: // COV_NF_START _GTMDevLog(@"Unknown element at [NSBezierPath (GTMBezierPathCGPathAdditions) cgPath]"); - break; + break; // COV_NF_END }; } return thePath; diff --git a/AppKit/GTMNSBezierPath+RoundRectTest.m b/AppKit/GTMNSBezierPath+RoundRectTest.m index ad64fd4..5bdf3a3 100644 --- a/AppKit/GTMNSBezierPath+RoundRectTest.m +++ b/AppKit/GTMNSBezierPath+RoundRectTest.m @@ -1,5 +1,5 @@ // -// NSBezierPath+RoundRectTest.m +// GTMNSBezierPath+RoundRectTest.m // // Copyright 2006-2008 Google Inc. // @@ -28,7 +28,7 @@ @implementation GTMNSBezierPath_RoundRectTest - (void)testRoundRects { - GTMAssertDrawingEqualToImageNamed(self, NSMakeSize(330, 430), + GTMAssertDrawingEqualToImageNamed(self, NSMakeSize(490, 430), @"GTMNSBezierPath+RoundRectTest", nil, nil); } @@ -39,7 +39,8 @@ NSMakeRect(50.0, 10.0, 30.0, 30.0), //Square Test NSMakeRect(100.0, 10.0, 1.0, 2.0), //Small Test NSMakeRect(120.0, 10.0, 15.0, 20.0), //Medium Test - NSMakeRect(140.0, 10.0, 150.0, 30.0) //Large Test + NSMakeRect(140.0, 10.0, 150.0, 30.0), //Large Test + NSMakeRect(300.0, 10.0, 150.0, 30.0) //Large Test 2 (for different radius) }; const NSUInteger theRectCount = sizeof(theRects) / sizeof(NSRect); @@ -50,8 +51,9 @@ for (i = 0; i < theLineWidthCount; ++i) { for (j = 0; j < theRectCount; ++j) { + CGFloat cornerRadius = ( (j < (theRectCount - 1)) ? 20.0 : 0.0 ); NSBezierPath *roundRect = [NSBezierPath gtm_bezierPathWithRoundRect:theRects[j] - cornerRadius:20.0]; + cornerRadius:cornerRadius]; [roundRect setLineWidth: theLineWidths[i]]; [roundRect stroke]; CGFloat newWidth = 35.0; @@ -71,8 +73,9 @@ for (i = 0; i < theColorCount; ++i) { for (j = 0; j < theRectCount; ++j) { + CGFloat cornerRadius = ( (j < (theRectCount - 1)) ? 10.0 : 0.0 ); NSBezierPath *roundRect = [NSBezierPath gtm_bezierPathWithRoundRect:theRects[j] - cornerRadius:10.0]; + cornerRadius:cornerRadius]; [theColors[i] setFill]; [roundRect fill]; theRects[j].origin.y += 35.0; @@ -85,8 +88,9 @@ for (i = 0; i < theFlatnessCount; i++) { for (j = 0; j < theRectCount; ++j) { + CGFloat cornerRadius = ( (j < (theRectCount - 1)) ? 6.0 : 0.0 ); NSBezierPath *roundRect = [NSBezierPath gtm_bezierPathWithRoundRect:theRects[j] - cornerRadius:6.0]; + cornerRadius:cornerRadius]; [roundRect setFlatness:theFlatness[i]]; [roundRect stroke]; theRects[j].origin.y += 35.0; diff --git a/AppKit/GTMNSBezierPath+ShadingTest.m b/AppKit/GTMNSBezierPath+ShadingTest.m index 5634cf3..a9dfbef 100644 --- a/AppKit/GTMNSBezierPath+ShadingTest.m +++ b/AppKit/GTMNSBezierPath+ShadingTest.m @@ -31,7 +31,7 @@ - (void)testShadings { GTMAssertDrawingEqualToImageNamed(self, - NSMakeSize(200, 200), + NSMakeSize(310, 410), @"GTMNSBezierPath+ShadingTest", nil, nil); } @@ -50,7 +50,7 @@ count:sizeof(theFloatArray)/sizeof(CGFloat)]; NSBezierPath *shadedPath; - // axialStrokeRect test + // axial stroke rect - diagonal fill NSRect axialStrokeRect = NSMakeRect(10.0f, 10.0f, 90.0f, 90.0f); shadedPath = [NSBezierPath bezierPathWithRect:axialStrokeRect]; [shadedPath setLineWidth: 10.0f]; @@ -59,8 +59,28 @@ NSPoint endPoint = NSMakePoint(axialStrokeRect.origin.x + axialStrokeRect.size.width - 20.0f, axialStrokeRect.origin.y + axialStrokeRect.size.height - 20.0f); [shadedPath gtm_strokeAxiallyFrom:startPoint to:endPoint extendingStart:YES extendingEnd:YES shading:shading]; + + // axial stroke rect - v line fill + axialStrokeRect = NSMakeRect(110.0f, 10.0f, 90.0f, 90.0f); + shadedPath = [NSBezierPath bezierPathWithRect:axialStrokeRect]; + [shadedPath setLineWidth: 10.0f]; + startPoint = NSMakePoint(axialStrokeRect.origin.x + axialStrokeRect.size.width / 2.0f, + axialStrokeRect.origin.y + 20.0f); + endPoint = NSMakePoint(axialStrokeRect.origin.x + axialStrokeRect.size.width / 2.0f, + axialStrokeRect.origin.y + axialStrokeRect.size.height - 20.0f); + [shadedPath gtm_strokeAxiallyFrom:startPoint to:endPoint extendingStart:YES extendingEnd:YES shading:shading]; + + // axial stroke rect - h line fill + axialStrokeRect = NSMakeRect(210.0f, 10.0f, 90.0f, 90.0f); + shadedPath = [NSBezierPath bezierPathWithRect:axialStrokeRect]; + [shadedPath setLineWidth: 10.0f]; + startPoint = NSMakePoint(axialStrokeRect.origin.x + 20.0f, + axialStrokeRect.origin.y + axialStrokeRect.size.height / 2.0f); + endPoint = NSMakePoint(axialStrokeRect.origin.x + axialStrokeRect.size.width - 20.0f, + axialStrokeRect.origin.y + axialStrokeRect.size.height / 2.0f); + [shadedPath gtm_strokeAxiallyFrom:startPoint to:endPoint extendingStart:YES extendingEnd:YES shading:shading]; - // axial fill + // axial fill rect - diagonal fill NSRect axialFillRect = NSMakeRect(10.0f, 110.0f, 90.0f, 90.0f); shadedPath = [NSBezierPath bezierPathWithRect:axialFillRect]; startPoint = NSMakePoint(axialFillRect.origin.x + 20.0f, @@ -69,8 +89,26 @@ axialFillRect.origin.y + axialFillRect.size.height - 20.0f); [shadedPath gtm_fillAxiallyFrom:startPoint to:endPoint extendingStart:YES extendingEnd:YES shading:shading]; - // radial stroke - NSRect radialStrokeRect = NSMakeRect(110.0f, 110.0f, 90.0f, 90.0f); + // axial fill rect - v line fill + axialFillRect = NSMakeRect(110.0f, 110.0f, 90.0f, 90.0f); + shadedPath = [NSBezierPath bezierPathWithRect:axialFillRect]; + startPoint = NSMakePoint(axialFillRect.origin.x + axialFillRect.size.width / 2.0f, + axialFillRect.origin.y + 20.0f); + endPoint = NSMakePoint(axialFillRect.origin.x + axialFillRect.size.width / 2.0f, + axialFillRect.origin.y + axialFillRect.size.height - 20.0f); + [shadedPath gtm_fillAxiallyFrom:startPoint to:endPoint extendingStart:YES extendingEnd:YES shading:shading]; + + // axial fill rect - h line fill + axialFillRect = NSMakeRect(210.0f, 110.0f, 90.0f, 90.0f); + shadedPath = [NSBezierPath bezierPathWithRect:axialFillRect]; + startPoint = NSMakePoint(axialFillRect.origin.x + 20.0f, + axialFillRect.origin.y + axialFillRect.size.height / 2.0f); + endPoint = NSMakePoint(axialFillRect.origin.x + axialFillRect.size.width - 20.0f, + axialFillRect.origin.y + axialFillRect.size.height / 2.0f); + [shadedPath gtm_fillAxiallyFrom:startPoint to:endPoint extendingStart:YES extendingEnd:YES shading:shading]; + + // radial stroke rect - diagonal fill + NSRect radialStrokeRect = NSMakeRect(10.0f, 210.0f, 90.0f, 90.0f); shadedPath = [NSBezierPath bezierPathWithRect:radialStrokeRect]; startPoint = NSMakePoint(radialStrokeRect.origin.x + 20.0f, radialStrokeRect.origin.y + 20.0f); @@ -80,8 +118,30 @@ to:endPoint toRadius:20.0f extendingStart:YES extendingEnd:YES shading:shading]; - // radial fill - NSRect radialFillRect = NSMakeRect(110.0f, 10.0f, 90.0f, 90.0f); + // radial stroke rect - v line fill + radialStrokeRect = NSMakeRect(110.0f, 210.0f, 90.0f, 90.0f); + shadedPath = [NSBezierPath bezierPathWithRect:radialStrokeRect]; + startPoint = NSMakePoint(radialStrokeRect.origin.x + radialStrokeRect.size.width / 2.0f, + radialStrokeRect.origin.y + 20.0f); + endPoint = NSMakePoint(radialStrokeRect.origin.x + radialStrokeRect.size.width / 2.0f, + radialStrokeRect.origin.y + radialStrokeRect.size.height - 20.0f); + [shadedPath gtm_strokeRadiallyFrom:startPoint fromRadius:60.0f + to:endPoint toRadius:20.0f + extendingStart:YES extendingEnd:YES shading:shading]; + + // radial stroke rect - h line fill + radialStrokeRect = NSMakeRect(210.0f, 210.0f, 90.0f, 90.0f); + shadedPath = [NSBezierPath bezierPathWithRect:radialStrokeRect]; + startPoint = NSMakePoint(radialStrokeRect.origin.x + 20.0f, + radialStrokeRect.origin.y + radialStrokeRect.size.height / 2.0f); + endPoint = NSMakePoint(radialStrokeRect.origin.x + radialStrokeRect.size.width - 20.0f, + radialStrokeRect.origin.y + radialStrokeRect.size.height / 2.0f); + [shadedPath gtm_strokeRadiallyFrom:startPoint fromRadius:60.0f + to:endPoint toRadius:20.0f + extendingStart:YES extendingEnd:YES shading:shading]; + + // radial fill rect - diagonal fill + NSRect radialFillRect = NSMakeRect(10.0f, 310.0f, 90.0f, 90.0f); shadedPath = [NSBezierPath bezierPathWithRect:radialFillRect]; startPoint = NSMakePoint(radialFillRect.origin.x + 20.0f, radialFillRect.origin.y + 20.0f); @@ -90,6 +150,28 @@ [shadedPath gtm_fillRadiallyFrom:startPoint fromRadius:10.0f to:endPoint toRadius:20.0f extendingStart:YES extendingEnd:YES shading:shading]; + + // radial fill rect - v line fill + radialFillRect = NSMakeRect(110.0f, 310.0f, 90.0f, 90.0f); + shadedPath = [NSBezierPath bezierPathWithRect:radialFillRect]; + startPoint = NSMakePoint(radialFillRect.origin.x + radialFillRect.size.width / 2.0f, + radialFillRect.origin.y + 20.0f); + endPoint = NSMakePoint(radialFillRect.origin.x + radialFillRect.size.width / 2.0f, + radialFillRect.origin.y + radialFillRect.size.height - 20.0f); + [shadedPath gtm_fillRadiallyFrom:startPoint fromRadius:10.0f + to:endPoint toRadius:20.0f + extendingStart:YES extendingEnd:YES shading:shading]; + + // radial fill rect - h line fill + radialFillRect = NSMakeRect(210.0f, 310.0f, 90.0f, 90.0f); + shadedPath = [NSBezierPath bezierPathWithRect:radialFillRect]; + startPoint = NSMakePoint(radialFillRect.origin.x + 20.0f, + radialFillRect.origin.y + radialFillRect.size.height / 2.0f); + endPoint = NSMakePoint(radialFillRect.origin.x + radialFillRect.size.width - 20.0f, + radialFillRect.origin.y + radialFillRect.size.height / 2.0f); + [shadedPath gtm_fillRadiallyFrom:startPoint fromRadius:10.0f + to:endPoint toRadius:20.0f + extendingStart:YES extendingEnd:YES shading:shading]; } @end diff --git a/AppKit/TestData/GTMNSBezierPath+RoundRectTest.ppc64.tiff b/AppKit/TestData/GTMNSBezierPath+RoundRectTest.ppc64.tiff Binary files differindex a4df8f8..ef22b6b 100644 --- a/AppKit/TestData/GTMNSBezierPath+RoundRectTest.ppc64.tiff +++ b/AppKit/TestData/GTMNSBezierPath+RoundRectTest.ppc64.tiff diff --git a/AppKit/TestData/GTMNSBezierPath+RoundRectTest.tiff b/AppKit/TestData/GTMNSBezierPath+RoundRectTest.tiff Binary files differindex c41ab04..b165acf 100644 --- a/AppKit/TestData/GTMNSBezierPath+RoundRectTest.tiff +++ b/AppKit/TestData/GTMNSBezierPath+RoundRectTest.tiff diff --git a/AppKit/TestData/GTMNSBezierPath+RoundRectTest.x86_64.tiff b/AppKit/TestData/GTMNSBezierPath+RoundRectTest.x86_64.tiff Binary files differindex a4df8f8..ef22b6b 100644 --- a/AppKit/TestData/GTMNSBezierPath+RoundRectTest.x86_64.tiff +++ b/AppKit/TestData/GTMNSBezierPath+RoundRectTest.x86_64.tiff diff --git a/AppKit/TestData/GTMNSBezierPath+ShadingTest.10.5.tiff b/AppKit/TestData/GTMNSBezierPath+ShadingTest.10.5.tiff Binary files differindex 040cb0d..b44b5bf 100644 --- a/AppKit/TestData/GTMNSBezierPath+ShadingTest.10.5.tiff +++ b/AppKit/TestData/GTMNSBezierPath+ShadingTest.10.5.tiff diff --git a/Foundation/GTMFourCharCode.m b/Foundation/GTMFourCharCode.m index 6b67cae..f5a08bf 100644 --- a/Foundation/GTMFourCharCode.m +++ b/Foundation/GTMFourCharCode.m @@ -25,7 +25,7 @@ @implementation GTMFourCharCode + (id)stringWithFourCharCode:(FourCharCode)code { - return [GTMNSMakeCollectable(UTCreateStringForOSType(code)) autorelease]; + return GTMCFAutorelease(UTCreateStringForOSType(code)); } + (id)fourCharCodeWithString:(NSString*)string { @@ -88,7 +88,7 @@ } - (NSString*)stringValue { - return [GTMNSMakeCollectable(UTCreateStringForOSType(code_)) autorelease]; + return GTMCFAutorelease(UTCreateStringForOSType(code_)); } - (NSNumber*)numberValue { diff --git a/Foundation/GTMGarbageCollection.h b/Foundation/GTMGarbageCollection.h index 4a7ac56..7f2873c 100644 --- a/Foundation/GTMGarbageCollection.h +++ b/Foundation/GTMGarbageCollection.h @@ -18,21 +18,40 @@ #import <Foundation/Foundation.h> +#import "GTMDefines.h" + // This allows us to easily move our code from GC to non GC. // They are no-ops unless we are require Leopard or above. // See // http://developer.apple.com/documentation/Cocoa/Conceptual/GarbageCollection/index.html +// and +// http://developer.apple.com/documentation/Cocoa/Conceptual/GarbageCollection/Articles/gcCoreFoundation.html#//apple_ref/doc/uid/TP40006687-SW1 // for details. -// General use would be -// CFTypeRef type = ... -// return [GTMNSMakeCollectable(type) autorelease]; - -#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 +#if (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050) && !GTM_IPHONE_SDK +// General use would be to call this through GTMCFAutorelease +// but there may be a reason the you want to make something collectable +// but not autoreleased, especially in pure GC code where you don't +// want to bother with the nop autorelease. FOUNDATION_STATIC_INLINE id GTMNSMakeCollectable(CFTypeRef cf) { return NSMakeCollectable(cf); } +// GTMNSMakeUncollectable is for global maps, etc. that we don't +// want released ever. You should still retain these in non-gc code. +FOUNDATION_STATIC_INLINE void GTMNSMakeUncollectable(id object) { + [[NSGarbageCollector defaultCollector] disableCollectorForPointer:object]; +} + +// Hopefully no code really needs this, but GTMIsGarbageCollectionEnabled is +// a common way to check at runtime if GC is on. +// There are some places where GC doesn't work w/ things w/in Apple's +// frameworks, so this is here so GTM unittests and detect it, and not run +// individual tests to work around bugs in Apple's frameworks. +FOUNDATION_STATIC_INLINE BOOL GTMIsGarbageCollectionEnabled(void) { + return ([NSGarbageCollector defaultCollector] != nil); +} + #else FOUNDATION_STATIC_INLINE id GTMNSMakeCollectable(CFTypeRef cf) { @@ -40,4 +59,19 @@ FOUNDATION_STATIC_INLINE id GTMNSMakeCollectable(CFTypeRef cf) { return (id)cf; } +FOUNDATION_STATIC_INLINE void GTMNSMakeUncollectable(id object) { +} + +FOUNDATION_STATIC_INLINE BOOL GTMIsGarbageCollectionEnabled(void) { + return NO; +} + #endif + +// GTMCFAutorelease makes a CF object collectable in GC mode, or adds it +// to the autorelease pool in non-GC mode. Either way it is taken care +// of. +FOUNDATION_STATIC_INLINE id GTMCFAutorelease(CFTypeRef cf) { + return [GTMNSMakeCollectable(cf) autorelease]; +} + diff --git a/Foundation/GTMGeometryUtils.h b/Foundation/GTMGeometryUtils.h index e8a078c..9691ffc 100644 --- a/Foundation/GTMGeometryUtils.h +++ b/Foundation/GTMGeometryUtils.h @@ -386,8 +386,6 @@ CG_INLINE NSRect GTMNSRectScale(NSRect inRect, CGFloat xScale, CGFloat yScale) { inRect.size.width * xScale, inRect.size.height * yScale); } - - /// Align rectangles // // Args: diff --git a/Foundation/GTMGeometryUtilsTest.m b/Foundation/GTMGeometryUtilsTest.m index ca817f8..5a59c8b 100644 --- a/Foundation/GTMGeometryUtilsTest.m +++ b/Foundation/GTMGeometryUtilsTest.m @@ -24,7 +24,7 @@ @implementation GTMGeometryUtilsTest -#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) +#if !GTM_IPHONE_SDK - (void)testGTMCGPointToNSPoint { CGPoint cgPoint = CGPointMake(15.1,6.2); NSPoint nsPoint = GTMCGPointToNSPoint(cgPoint); @@ -43,7 +43,6 @@ STAssertTrue(CGRectEqualToRect(cgRect, *(CGRect*)&nsRect), nil); } - - (void)testGTMNSRectToCGRect { NSRect nsRect = NSMakeRect(4.6,3.2,22.1,45.0); CGRect cgRect = GTMNSRectToCGRect(nsRect); @@ -80,6 +79,99 @@ point = GTMNSMidMinY(rect); STAssertEqualsWithAccuracy(point.y, (CGFloat)0.0, (CGFloat)0.01, nil); STAssertEqualsWithAccuracy(point.x, (CGFloat)1.0, (CGFloat)0.01, nil); + + point = GTMNSCenter(rect); + STAssertEqualsWithAccuracy(point.y, (CGFloat)1.0, (CGFloat)0.01, nil); + STAssertEqualsWithAccuracy(point.x, (CGFloat)1.0, (CGFloat)0.01, nil); +} + +- (void)testGTMNSRectSize { + NSSize nsSize = GTMNSRectSize(NSMakeRect(1, 1, 10, 5)); + STAssertEqualsWithAccuracy(nsSize.width, (CGFloat)10.0, (CGFloat)0.01, nil); + STAssertEqualsWithAccuracy(nsSize.height, (CGFloat)5.0, (CGFloat)0.01, nil); +} + +- (void)testGTMNSRectOfSize { + NSRect outRect = GTMNSRectOfSize(NSMakeSize(10, 5)); + NSRect expectedRect = NSMakeRect(0, 0, 10, 5); + STAssertEquals(outRect, expectedRect, nil); +} + +- (void)testGTMNSAlignRectangles { + typedef struct { + NSPoint expectedOrigin; + GTMRectAlignment alignment; + } TestData; + + TestData data[] = { + { {1,2}, GTMRectAlignTop }, + { {0,2}, GTMRectAlignTopLeft }, + { {2,2}, GTMRectAlignTopRight }, + { {0,1}, GTMRectAlignLeft }, + { {1,0}, GTMRectAlignBottom }, + { {0,0}, GTMRectAlignBottomLeft }, + { {2,0}, GTMRectAlignBottomRight }, + { {2,1}, GTMRectAlignRight }, + { {1,1}, GTMRectAlignCenter }, + }; + + NSRect rect1 = NSMakeRect(0, 0, 4, 4); + NSRect rect2 = NSMakeRect(0, 0, 2, 2); + + NSRect expectedRect; + expectedRect.size = NSMakeSize(2, 2); + + for (size_t i = 0; i < sizeof(data) / sizeof(TestData); i++) { + expectedRect.origin = data[i].expectedOrigin; + NSRect outRect = GTMNSAlignRectangles(rect2, rect1, data[i].alignment); + STAssertEquals(outRect, expectedRect, nil); + } +} + +- (void)testGTMNSScaleRectangleToSize { + NSRect rect = NSMakeRect(0.0f, 0.0f, 10.0f, 10.0f); + typedef struct { + NSSize size_; + NSSize newSize_; + } Test; + Test tests[] = { + { { 5.0, 10.0 }, { 5.0, 5.0 } }, + { { 10.0, 5.0 }, { 5.0, 5.0 } }, + { { 10.0, 10.0 }, { 10.0, 10.0 } }, + { { 11.0, 11.0, }, { 10.0, 10.0 } }, + { { 5.0, 2.0 }, { 2.0, 2.0 } }, + { { 2.0, 5.0 }, { 2.0, 2.0 } }, + { { 2.0, 2.0 }, { 2.0, 2.0 } }, + { { 0.0, 10.0 }, { 0.0, 0.0 } } + }; + + for (size_t i = 0; i < sizeof(tests) / sizeof(Test); ++i) { + NSRect result = GTMNSScaleRectangleToSize(rect, tests[i].size_, + GTMScaleProportionally); + STAssertEquals(result, GTMNSRectOfSize(tests[i].newSize_), @"failed on test %zd", i); + } + + NSRect result = GTMNSScaleRectangleToSize(NSZeroRect, tests[0].size_, + GTMScaleProportionally); + STAssertEquals(result, NSZeroRect, nil); + + result = GTMNSScaleRectangleToSize(rect, tests[0].size_, + GTMScaleToFit); + STAssertEquals(result, GTMNSRectOfSize(tests[0].size_), nil); + + result = GTMNSScaleRectangleToSize(rect, tests[0].size_, + GTMScaleNone); + STAssertEquals(result, rect, nil); +} + +- (void)testGTMNSDistanceBetweenPoints { + NSPoint pt1 = NSMakePoint(0, 0); + NSPoint pt2 = NSMakePoint(3, 4); + STAssertEquals(GTMNSDistanceBetweenPoints(pt1, pt2), (CGFloat)5.0, nil); + STAssertEquals(GTMNSDistanceBetweenPoints(pt2, pt1), (CGFloat)5.0, nil); + pt1 = NSMakePoint(1, 1); + pt2 = NSMakePoint(1, 1); + STAssertEquals(GTMNSDistanceBetweenPoints(pt1, pt2), (CGFloat)0.0, nil); } - (void)testGTMNSRectScaling { @@ -89,9 +181,9 @@ rect2, nil); } -#endif // #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) +#endif // !GTM_IPHONE_SDK -- (void)testGTMDistanceBetweenPoints { +- (void)testGTMCGDistanceBetweenPoints { CGPoint pt1 = CGPointMake(0, 0); CGPoint pt2 = CGPointMake(3, 4); STAssertEquals(GTMCGDistanceBetweenPoints(pt1, pt2), (CGFloat)5.0, nil); @@ -101,7 +193,7 @@ STAssertEquals(GTMCGDistanceBetweenPoints(pt1, pt2), (CGFloat)0.0, nil); } -- (void)testGTMAlignRectangles { +- (void)testGTMCGAlignRectangles { typedef struct { CGPoint expectedOrigin; GTMRectAlignment alignment; @@ -150,6 +242,22 @@ point = GTMCGMidMinY(rect); STAssertEqualsWithAccuracy(point.y, (CGFloat)0.0, (CGFloat)0.01, nil); STAssertEqualsWithAccuracy(point.x, (CGFloat)1.0, (CGFloat)0.01, nil); + + point = GTMCGCenter(rect); + STAssertEqualsWithAccuracy(point.y, (CGFloat)1.0, (CGFloat)0.01, nil); + STAssertEqualsWithAccuracy(point.x, (CGFloat)1.0, (CGFloat)0.01, nil); +} + +- (void)testGTMCGRectSize { + CGSize cgSize = GTMCGRectSize(CGRectMake(1, 1, 10, 5)); + STAssertEqualsWithAccuracy(cgSize.width, (CGFloat)10.0, (CGFloat)0.01, nil); + STAssertEqualsWithAccuracy(cgSize.height, (CGFloat)5.0, (CGFloat)0.01, nil); +} + +- (void)testGTMCGRectOfSize { + CGRect outRect = GTMCGRectOfSize(CGSizeMake(10, 5)); + CGRect expectedRect = CGRectMake(0, 0, 10, 5); + STAssertEquals(outRect, expectedRect, nil); } - (void)testGTMCGRectScaling { @@ -158,8 +266,8 @@ STAssertEquals(GTMCGRectScale(rect, (CGFloat)0.2, (CGFloat)1.2), rect2, nil); } - -- (void)testGTMScaleRectangleToSize { + +- (void)testGTMCGScaleRectangleToSize { CGRect rect = CGRectMake(0.0f, 0.0f, 10.0f, 10.0f); typedef struct { CGSize size_; @@ -194,4 +302,5 @@ GTMScaleNone); STAssertEquals(result, rect, nil); } + @end diff --git a/Foundation/GTMHTTPFetcher.m b/Foundation/GTMHTTPFetcher.m index 96fa337..a73d752 100644 --- a/Foundation/GTMHTTPFetcher.m +++ b/Foundation/GTMHTTPFetcher.m @@ -20,6 +20,7 @@ #import "GTMHTTPFetcher.h" #import "GTMDebugSelectorValidation.h" +#import "GTMGarbageCollection.h" @interface GTMHTTPFetcher (GTMHTTPFetcherLoggingInternal) - (void)logFetchWithError:(NSError *)error; @@ -53,9 +54,9 @@ NSString* const kGTMLastModifiedHeader = @"Last-Modified"; NSString* const kGTMIfModifiedSinceHeader = @"If-Modified-Since"; -NSMutableArray* gGTMFetcherStaticCookies = nil; -Class gGTMFetcherConnectionClass = nil; -NSArray *gGTMFetcherDefaultRunLoopModes = nil; +static NSMutableArray* gGTMFetcherStaticCookies = nil; +static Class gGTMFetcherConnectionClass = nil; +static NSArray *gGTMFetcherDefaultRunLoopModes = nil; const NSTimeInterval kDefaultMaxRetryInterval = 60. * 10.; // 10 minutes @@ -81,6 +82,7 @@ const NSTimeInterval kDefaultMaxRetryInterval = 60. * 10.; // 10 minutes + (void)initialize { if (!gGTMFetcherStaticCookies) { gGTMFetcherStaticCookies = [[NSMutableArray alloc] init]; + GTMNSMakeUncollectable(gGTMFetcherStaticCookies); } } @@ -140,14 +142,16 @@ const NSTimeInterval kDefaultMaxRetryInterval = 60. * 10.; // 10 minutes GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments(delegate, retrySEL_, @encode(BOOL), @encode(GTMHTTPFetcher *), @encode(BOOL), @encode(NSError *), NULL); if (connection_ != nil) { + // COV_NF_START - since we want the assert, we can't really test this _GTMDevAssert(connection_ != nil, @"fetch object %@ being reused; this should never happen", self); goto CannotBeginFetch; + // COV_NF_END } if (request_ == nil) { - _GTMDevAssert(request_ != nil, @"beginFetchWithDelegate requires a request"); + _GTMDevLog(@"beginFetchWithDelegate requires a request"); goto CannotBeginFetch; } @@ -254,9 +258,10 @@ const NSTimeInterval kDefaultMaxRetryInterval = 60. * 10.; // 10 minutes } if (!connection_) { - _GTMDevAssert(connection_ != nil, - @"beginFetchWithDelegate could not create a connection"); + // COV_NF_START - can't really create this case + _GTMDevLog(@"beginFetchWithDelegate could not create a connection"); goto CannotBeginFetch; + // COV_NF_END } // we'll retain the delegate only during the outstanding connection (similar @@ -1045,11 +1050,12 @@ CannotBeginFetch: + (void)setDefaultRunLoopModes:(NSArray *)modes { [gGTMFetcherDefaultRunLoopModes autorelease]; gGTMFetcherDefaultRunLoopModes = [modes retain]; + GTMNSMakeUncollectable(gGTMFetcherDefaultRunLoopModes); } + (Class)connectionClass { if (gGTMFetcherConnectionClass == nil) { - gGTMFetcherConnectionClass = [NSURLConnection class]; + gGTMFetcherConnectionClass = [NSURLConnection class]; } return gGTMFetcherConnectionClass; } @@ -1214,7 +1220,7 @@ CannotBeginFetch: } } else { - _GTMDevAssert(NO, @"Cookie incomplete: %@", newCookie); + _GTMDevAssert(NO, @"Cookie incomplete: %@", newCookie); // COV_NF_LINE } } } diff --git a/Foundation/GTMHTTPFetcherTest.m b/Foundation/GTMHTTPFetcherTest.m index 6bb99a1..3c461e0 100644 --- a/Foundation/GTMHTTPFetcherTest.m +++ b/Foundation/GTMHTTPFetcherTest.m @@ -51,8 +51,8 @@ - (BOOL)fixRequestFetcher:(GTMHTTPFetcher *)fetcher willRetry:(BOOL)suggestedWillRetry forError:(NSError *)error; -- (void)testFetcher:(GTMHTTPFetcher *)fetcher finishedWithData:(NSData *)data; -- (void)testFetcher:(GTMHTTPFetcher *)fetcher failedWithError:(NSError *)error; +- (void)fetcher:(GTMHTTPFetcher *)fetcher finishedWithData:(NSData *)data; +- (void)fetcher:(GTMHTTPFetcher *)fetcher failedWithError:(NSError *)error; @end @implementation GTMHTTPFetcherTest @@ -105,7 +105,6 @@ static NSString *const kValidFileName = @"GTMHTTPFetcherTestPage.html"; } - (void)testValidFetch { - NSString *urlString = [self fileURLStringToTestFileName:kValidFileName]; GTMHTTPFetcher *fetcher = @@ -252,7 +251,6 @@ static NSString *const kValidFileName = @"GTMHTTPFetcherTestPage.html"; } - (void)testRetryFetches { - GTMHTTPFetcher *fetcher; NSString *invalidFile = [kValidFileName stringByAppendingString:@"?status=503"]; @@ -362,8 +360,8 @@ static NSString *const kValidFileName = @"GTMHTTPFetcherTestPage.html"; BOOL isFetching = [fetcher beginFetchWithDelegate:self - didFinishSelector:@selector(testFetcher:finishedWithData:) - didFailSelector:@selector(testFetcher:failedWithError:)]; + didFinishSelector:@selector(fetcher:finishedWithData:) + didFailSelector:@selector(fetcher:failedWithError:)]; STAssertTrue(isFetching, @"Begin fetch failed"); if (isFetching) { @@ -419,14 +417,14 @@ static NSString *const kValidFileName = @"GTMHTTPFetcherTestPage.html"; -- (void)testFetcher:(GTMHTTPFetcher *)fetcher finishedWithData:(NSData *)data { +- (void)fetcher:(GTMHTTPFetcher *)fetcher finishedWithData:(NSData *)data { fetchedData_ = [data copy]; fetchedStatus_ = [fetcher statusCode]; // this implicitly tests that the fetcher has kept the response fetchedRequest_ = [[fetcher request] retain]; fetchedResponse_ = [[fetcher response] retain]; } -- (void)testFetcher:(GTMHTTPFetcher *)fetcher failedWithError:(NSError *)error { +- (void)fetcher:(GTMHTTPFetcher *)fetcher failedWithError:(NSError *)error { // if it's a status error, don't hang onto the error, just the status/data if ([[error domain] isEqual:kGTMHTTPFetcherStatusDomain]) { fetchedData_ = [[[error userInfo] objectForKey:kGTMHTTPFetcherStatusDataKey] copy]; diff --git a/Foundation/GTMHTTPServer.h b/Foundation/GTMHTTPServer.h index c70178c..71522df 100644 --- a/Foundation/GTMHTTPServer.h +++ b/Foundation/GTMHTTPServer.h @@ -62,7 +62,7 @@ enum { // See comment at top of file for the intened use of this class. @interface GTMHTTPServer : NSObject { @private - id delegate_; + __weak id delegate_; uint16_t port_; BOOL localhostOnly_; NSFileHandle *listenHandle_; diff --git a/Foundation/GTMHTTPServer.m b/Foundation/GTMHTTPServer.m index daa6a4e..ffa294c 100644 --- a/Foundation/GTMHTTPServer.m +++ b/Foundation/GTMHTTPServer.m @@ -331,7 +331,7 @@ startFailed: response = [delegate_ httpServer:self handleRequest:request]; } @catch (NSException *e) { _GTMDevLog(@"Exception trying to handle http request: %@", e); - } + } // COV_NF_LINE - radar 5851992 only reachable w/ an uncaught exception which isn't testable if (!response) { [self closeConnection:connDict]; @@ -350,14 +350,16 @@ startFailed: } - (NSMutableDictionary *)lookupConnection:(NSFileHandle *)fileHandle { + NSMutableDictionary *result = nil; NSUInteger max = [connections_ count]; for (NSUInteger x = 0; x < max; ++x) { NSMutableDictionary *connDict = [connections_ objectAtIndex:x]; if (fileHandle == [connDict objectForKey:kFileHandle]) { - return connDict; + result = connDict; + break; } } - return nil; + return result; } - (void)closeConnection:(NSMutableDictionary *)connDict { @@ -367,6 +369,10 @@ startFailed: [center removeObserver:self name:NSFileHandleReadCompletionNotification object:connectionHandle]; + // in a non GC world, we're fine just letting the connect get closed when + // the object is release when it comes out of connections_, but in a GC world + // it won't get cleaned up + [connectionHandle closeFile]; // remove it from the list [connections_ removeObject:connDict]; @@ -380,11 +386,11 @@ startFailed: NSFileHandle *connectionHandle = [connDict objectForKey:kFileHandle]; NSData *serialized = [response serializedData]; [connectionHandle writeData:serialized]; - } @catch (NSException *e) { + } @catch (NSException *e) { // COV_NF_START - causing an exception here is to hard in a test // TODO: let the delegate know about the exception (but do it on the main // thread) _GTMDevLog(@"exception while sending reply: %@", e); - } + } // COV_NF_END // back to the main thread to close things down [self performSelectorOnMainThread:@selector(sentResponse:) @@ -421,28 +427,30 @@ startFailed: } - (void)dealloc { - CFRelease(message_); + if (message_) { + CFRelease(message_); + } [super dealloc]; } - (NSString *)version { - return [GTMNSMakeCollectable(CFHTTPMessageCopyVersion(message_)) autorelease]; + return GTMCFAutorelease(CFHTTPMessageCopyVersion(message_)); } - (NSURL *)URL { - return [GTMNSMakeCollectable(CFHTTPMessageCopyRequestURL(message_)) autorelease]; + return GTMCFAutorelease(CFHTTPMessageCopyRequestURL(message_)); } - (NSString *)method { - return [GTMNSMakeCollectable(CFHTTPMessageCopyRequestMethod(message_)) autorelease]; + return GTMCFAutorelease(CFHTTPMessageCopyRequestMethod(message_)); } - (NSData *)body { - return [GTMNSMakeCollectable(CFHTTPMessageCopyBody(message_)) autorelease]; + return GTMCFAutorelease(CFHTTPMessageCopyBody(message_)); } - (NSDictionary *)allHeaderFieldValues { - return GTMNSMakeCollectable(CFHTTPMessageCopyAllHeaderFields(message_)); + return GTMCFAutorelease(CFHTTPMessageCopyAllHeaderFields(message_)); } - (NSString *)description { @@ -471,7 +479,7 @@ startFailed: if (key) { value = CFHTTPMessageCopyHeaderFieldValue(message_, (CFStringRef)key); } - return [GTMNSMakeCollectable(value) autorelease]; + return GTMCFAutorelease(value); } - (UInt32)contentLength { @@ -578,7 +586,7 @@ startFailed: } - (NSData *)serializedData { - return [GTMNSMakeCollectable(CFHTTPMessageCopySerializedMessage(message_)) autorelease]; + return GTMCFAutorelease(CFHTTPMessageCopySerializedMessage(message_)); } @end diff --git a/Foundation/GTMHTTPServerTest.m b/Foundation/GTMHTTPServerTest.m index d96d54e..fd962af 100644 --- a/Foundation/GTMHTTPServerTest.m +++ b/Foundation/GTMHTTPServerTest.m @@ -52,9 +52,8 @@ // helper that throws while handling its request @interface TestThrowingServerDelegate : TestServerDelegate -// since this method ALWAYS throws, we can mark it as noreturn - (GTMHTTPResponseMessage *)httpServer:(GTMHTTPServer *)server - handleRequest:(GTMHTTPRequestMessage *)request __attribute__ ((noreturn)); + handleRequest:(GTMHTTPRequestMessage *)request; @end // The timings used for waiting for replies @@ -369,8 +368,10 @@ const NSTimeInterval kSendChunkInterval = 0.05; // make sure we see the request at this point STAssertEquals([server activeRequestCount], (NSUInteger)1, @"should have started the request by now"); - // drop the pool to close the connection - [localPool release]; + // force the connection closed and drop the pool to get all the cleanup to + // happen. + [handle closeFile]; + [localPool drain]; // spin the run loop so it should see the close loopIntervalDate = [NSDate dateWithTimeIntervalSinceNow:kRunLoopInterval]; [[NSRunLoop currentRunLoop] runUntilDate:loopIntervalDate]; diff --git a/Foundation/GTMLogger+ASLTest.m b/Foundation/GTMLogger+ASLTest.m index 97f7e29..cd32484 100644 --- a/Foundation/GTMLogger+ASLTest.m +++ b/Foundation/GTMLogger+ASLTest.m @@ -68,7 +68,6 @@ static NSMutableArray *gDummyLog; // weak // dummy writer will save the messages w/ @level concatenated. The "level" // will be the ASL level, not the GTMLogger level. GTMLogASLWriter will log // all - NSArray *log = gDummyLog; NSArray *expected = [NSArray arrayWithObjects: @"unknown@5", @"debug@5", @@ -77,7 +76,7 @@ static NSMutableArray *gDummyLog; // weak @"assert@1", nil]; - STAssertEqualObjects(log, expected, nil); + STAssertEqualObjects(gDummyLog, expected, nil); gDummyLog = nil; } diff --git a/Foundation/GTMLogger.m b/Foundation/GTMLogger.m index cf1d2c8..4da1034 100644 --- a/Foundation/GTMLogger.m +++ b/Foundation/GTMLogger.m @@ -341,7 +341,7 @@ static GTMLogger *gSharedLogger = nil; NULL, // format options (CFStringRef)fmt, args); - return [GTMNSMakeCollectable(cfmsg) autorelease]; + return GTMCFAutorelease(cfmsg); } @end // GTMLogBasicFormatter diff --git a/Foundation/GTMLoggerRingBufferWriter.h b/Foundation/GTMLoggerRingBufferWriter.h index e749a33..30e58ff 100644 --- a/Foundation/GTMLoggerRingBufferWriter.h +++ b/Foundation/GTMLoggerRingBufferWriter.h @@ -17,6 +17,7 @@ // #import "GTMLogger.h" +#import "GTMDefines.h" typedef struct GTMRingBufferPair GTMRingBufferPair; @@ -51,39 +52,39 @@ typedef struct GTMRingBufferPair GTMRingBufferPair; @private id<GTMLogWriter> writer_; GTMRingBufferPair *buffer_; - int capacity_; - int nextIndex_; // Index of the next element of |buffer_| to fill. - int totalLogged_; // This > 0 and |nextIndex_| == 0 means we've wrapped. + NSUInteger capacity_; + NSUInteger nextIndex_; // Index of the next element of |buffer_| to fill. + NSUInteger totalLogged_; // This > 0 and |nextIndex_| == 0 means we've wrapped. } -// Returns an autoreleased ring buffer writer. If |capacity| is -// non-positive, or |writer| is nil, then nil is returned. -+ (id)ringBufferWriterWithCapacity:(int)capacity +// Returns an autoreleased ring buffer writer. If |writer| is nil, +// then nil is returned. ++ (id)ringBufferWriterWithCapacity:(NSUInteger)capacity writer:(id<GTMLogWriter>)loggerWriter; -// Designated initializer. If |capacity| is non-positive, or |writer| -// is nil, then nil is returned. If you just use -init, nil will be returned. -- (id)initWithCapacity:(int)capacity +// Designated initializer. If |writer| is nil, then nil is returned. +// If you just use -init, nil will be returned. +- (id)initWithCapacity:(NSUInteger)capacity writer:(id<GTMLogWriter>)loggerWriter; // How many messages will be logged before older messages get dropped // on the floor. -- (int)capacity; +- (NSUInteger)capacity; // The log writer that will get the buffered log messages if/when they // need to be displayed. - (id<GTMLogWriter>)writer; // How many log messages are currently in the buffer. -- (int)count; +- (NSUInteger)count; // How many have been dropped on the floor since creation, or the last // reset. -- (int)droppedLogCount; +- (NSUInteger)droppedLogCount; // The total number of messages processed since creation, or the last // reset. -- (int)totalLogged; +- (NSUInteger)totalLogged; // Purge the contents and reset the counters. - (void)reset; diff --git a/Foundation/GTMLoggerRingBufferWriter.m b/Foundation/GTMLoggerRingBufferWriter.m index f5502b8..4a8c7c6 100644 --- a/Foundation/GTMLoggerRingBufferWriter.m +++ b/Foundation/GTMLoggerRingBufferWriter.m @@ -18,16 +18,13 @@ #import "GTMLoggerRingBufferWriter.h" -// Define a trivial assertion macro to avoid dependencies -#ifdef DEBUG -#define GTMLOGGER_ASSERT(expr) assert(expr) -#else -#define GTMLOGGER_ASSERT(expr) -#endif - // Holds a message and a level. struct GTMRingBufferPair { - NSString *logMessage_; + // Explicitly using CFStringRef instead of NSString because in a GC world, the + // NSString will be collected because there is no way for the GC to know that + // there is a strong reference to the NSString in this data structure. By + // using a CFStringRef we can CFRetain it, and avoid the problem. + CFStringRef logMessage_; GTMLoggerLevel level_; }; @@ -52,7 +49,7 @@ typedef void (GTMRingBufferPairCallback)(GTMLoggerRingBufferWriter *rbw, @implementation GTMLoggerRingBufferWriter -+ (id)ringBufferWriterWithCapacity:(int)capacity ++ (id)ringBufferWriterWithCapacity:(NSUInteger)capacity writer:(id<GTMLogWriter>)writer { GTMLoggerRingBufferWriter *rbw = [[[self alloc] initWithCapacity:capacity @@ -62,24 +59,21 @@ typedef void (GTMRingBufferPairCallback)(GTMLoggerRingBufferWriter *rbw, } // ringBufferWriterWithCapacity -- (id)initWithCapacity:(int)capacity +- (id)initWithCapacity:(NSUInteger)capacity writer:(id<GTMLogWriter>)writer { if ((self = [super init])) { - if (capacity > 0) { - writer_ = [writer retain]; - capacity_ = capacity; + writer_ = [writer retain]; + capacity_ = capacity; - buffer_ = calloc(capacity_, sizeof(GTMRingBufferPair)); + buffer_ = (GTMRingBufferPair *)calloc(capacity_, sizeof(GTMRingBufferPair)); - nextIndex_ = 0; - } + nextIndex_ = 0; - if (buffer_ == NULL || writer_ == nil) { + if (capacity_ == 0 || !buffer_ || !writer_) { [self release]; - return nil; + self = nil; } } - return self; } // initWithCapacity @@ -94,14 +88,16 @@ typedef void (GTMRingBufferPairCallback)(GTMLoggerRingBufferWriter *rbw, [self reset]; [writer_ release]; - free(buffer_); + if (buffer_) { + free(buffer_); + } [super dealloc]; } // dealloc -- (int)capacity { +- (NSUInteger)capacity { return capacity_; } // capacity @@ -111,8 +107,8 @@ typedef void (GTMRingBufferPairCallback)(GTMLoggerRingBufferWriter *rbw, } // writer -- (int)count { - int count = 0; +- (NSUInteger)count { + NSUInteger count = 0; @synchronized(self) { if ((nextIndex_ == 0 && totalLogged_ > 0) || totalLogged_ >= capacity_) { @@ -128,21 +124,23 @@ typedef void (GTMRingBufferPairCallback)(GTMLoggerRingBufferWriter *rbw, } // count -- (int)droppedLogCount { - int droppedCount = 0; +- (NSUInteger)droppedLogCount { + NSUInteger droppedCount = 0; @synchronized(self) { - droppedCount = totalLogged_ - capacity_; + if (capacity_ > totalLogged_) { + droppedCount = 0; + } else { + droppedCount = totalLogged_ - capacity_; + } } - if (droppedCount < 0) droppedCount = 0; - return droppedCount; } // droppedLogCount -- (int)totalLogged { +- (NSUInteger)totalLogged { return totalLogged_; } // totalLogged @@ -179,9 +177,11 @@ typedef void (GTMRingBufferPairCallback)(GTMLoggerRingBufferWriter *rbw, // the structure. static void ResetCallback(GTMLoggerRingBufferWriter *rbw, GTMRingBufferPair *pair) { - [pair->logMessage_ release]; + if (pair->logMessage_) { + CFRelease(pair->logMessage_); + } pair->logMessage_ = nil; - pair->level_ = 0; + pair->level_ = kGTMLoggerLevelUnknown; } // ResetCallback @@ -200,7 +200,7 @@ static void ResetCallback(GTMLoggerRingBufferWriter *rbw, // ring buffer's |writer_|. static void PrintContentsCallback(GTMLoggerRingBufferWriter *rbw, GTMRingBufferPair *pair) { - [[rbw writer] logMessage:pair->logMessage_ level:pair->level_]; + [[rbw writer] logMessage:(NSString*)pair->logMessage_ level:pair->level_]; } // PrintContentsCallback @@ -213,21 +213,21 @@ static void PrintContentsCallback(GTMLoggerRingBufferWriter *rbw, // Assumes caller will do any necessary synchronization. - (void)addMessage:(NSString *)message level:(GTMLoggerLevel)level { - int newIndex = nextIndex_; - nextIndex_ = (nextIndex_ + 1) % capacity_; - - ++totalLogged_; - - // Sanity check - GTMLOGGER_ASSERT(buffer_ != NULL); - GTMLOGGER_ASSERT(nextIndex_ >= 0 && nextIndex_ < capacity_); - GTMLOGGER_ASSERT(newIndex >= 0 && newIndex < capacity_); - - // Now store the goodies. - GTMRingBufferPair *pair = buffer_ + newIndex; - [pair->logMessage_ release]; - pair->logMessage_ = [message copy]; - pair->level_ = level; + NSUInteger newIndex = nextIndex_; + nextIndex_ = (nextIndex_ + 1) % capacity_; + + ++totalLogged_; + + // Now store the goodies. + GTMRingBufferPair *pair = buffer_ + newIndex; + if (pair->logMessage_) { + CFRelease(pair->logMessage_); + pair->logMessage_ = nil; + } + if (message) { + pair->logMessage_ = CFStringCreateCopy(kCFAllocatorDefault, (CFStringRef)message); + } + pair->level_ = level; } // addMessage @@ -235,7 +235,7 @@ static void PrintContentsCallback(GTMLoggerRingBufferWriter *rbw, // From the GTMLogWriter protocol. - (void)logMessage:(NSString *)message level:(GTMLoggerLevel)level { @synchronized(self) { - [self addMessage:message level:level]; + [self addMessage:(NSString*)message level:level]; if (level >= kGTMLoggerLevelError) { [self dumpContents]; diff --git a/Foundation/GTMLoggerRingBufferWriterTest.m b/Foundation/GTMLoggerRingBufferWriterTest.m index 6e64cc0..1c5d72b 100644 --- a/Foundation/GTMLoggerRingBufferWriterTest.m +++ b/Foundation/GTMLoggerRingBufferWriterTest.m @@ -19,7 +19,7 @@ #import "GTMSenTestCase.h" #import "GTMLoggerRingBufferWriter.h" #import "GTMLogger.h" - +#import "GTMUnitTestDevLog.h" // -------------------------------------------------- // CountingWriter keeps a count of the number of times it has been @@ -31,16 +31,17 @@ NSMutableArray *loggedContents_; } -- (int)count; +- (NSUInteger)count; - (NSArray *)loggedContents; - (void)reset; @end // CountingWriter @implementation CountingWriter - - (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level { - if (loggedContents_ == nil) loggedContents_ = [[NSMutableArray alloc] init]; + if (!loggedContents_) { + loggedContents_ = [[NSMutableArray alloc] init]; + } [loggedContents_ addObject:msg]; } // logMessage @@ -54,8 +55,8 @@ loggedContents_ = nil; } // reset -- (int)count { - return (int)[loggedContents_ count]; +- (NSUInteger)count { + return [loggedContents_ count]; } // count - (NSArray *)loggedContents { @@ -68,7 +69,7 @@ @interface GTMLoggerRingBufferWriterTest : GTMTestCase { @private GTMLogger *logger_; - __weak CountingWriter *countingWriter_; + CountingWriter *countingWriter_; } @end // GTMLoggerRingBufferWriterTest @@ -100,12 +101,13 @@ - (void)setUp { - countingWriter_ = [[[CountingWriter alloc] init] autorelease]; + countingWriter_ = [[CountingWriter alloc] init]; logger_ = [[GTMLogger alloc] init]; } // setUp - (void)tearDown { + [countingWriter_ release]; [logger_ release]; } // tearDown @@ -116,21 +118,18 @@ GTMLoggerRingBufferWriter *writer = [GTMLoggerRingBufferWriter ringBufferWriterWithCapacity:32 writer:countingWriter_]; - STAssertEquals([writer capacity], 32, nil); + STAssertEquals([writer capacity], (NSUInteger)32, nil); STAssertTrue([writer writer] == countingWriter_, nil); - STAssertEquals([writer count], 0, nil); - STAssertEquals([writer droppedLogCount], 0, nil); - STAssertEquals([writer totalLogged], 0, nil); + STAssertEquals([writer count], (NSUInteger)0, nil); + STAssertEquals([writer droppedLogCount], (NSUInteger)0, nil); + STAssertEquals([writer totalLogged], (NSUInteger)0, nil); // Try with invalid arguments. Should always get nil back. writer = [GTMLoggerRingBufferWriter ringBufferWriterWithCapacity:0 writer:countingWriter_]; STAssertNil(writer, nil); - writer = - [GTMLoggerRingBufferWriter ringBufferWriterWithCapacity:-1 - writer:countingWriter_]; - STAssertNil(writer, nil); + writer = [GTMLoggerRingBufferWriter ringBufferWriterWithCapacity:32 writer:nil]; STAssertNil(writer, nil); @@ -149,28 +148,28 @@ // Shouldn't do anything if there are no contents. [writer dumpContents]; - STAssertEquals([writer count], 0, nil); - STAssertEquals([countingWriter_ count], 0, nil); + STAssertEquals([writer count], (NSUInteger)0, nil); + STAssertEquals([countingWriter_ count], (NSUInteger)0, nil); // Log a single item. Make sure the counts are accurate. [logger_ logDebug:@"oop"]; - STAssertEquals([writer count], 1, nil); - STAssertEquals([writer totalLogged], 1, nil); - STAssertEquals([writer droppedLogCount], 0, nil); - STAssertEquals([countingWriter_ count], 0, nil); + STAssertEquals([writer count], (NSUInteger)1, nil); + STAssertEquals([writer totalLogged], (NSUInteger)1, nil); + STAssertEquals([writer droppedLogCount], (NSUInteger)0, nil); + STAssertEquals([countingWriter_ count], (NSUInteger)0, nil); // Log a second item. Also make sure counts are accurate. [logger_ logDebug:@"ack"]; - STAssertEquals([writer count], 2, nil); - STAssertEquals([writer totalLogged], 2, nil); - STAssertEquals([writer droppedLogCount], 0, nil); - STAssertEquals([countingWriter_ count], 0, nil); + STAssertEquals([writer count], (NSUInteger)2, nil); + STAssertEquals([writer totalLogged], (NSUInteger)2, nil); + STAssertEquals([writer droppedLogCount], (NSUInteger)0, nil); + STAssertEquals([countingWriter_ count], (NSUInteger)0, nil); // Print them, and make sure the countingWriter sees the right stuff. [writer dumpContents]; - STAssertEquals([countingWriter_ count], 2, nil); - STAssertEquals([writer count], 2, nil); // Should not be zeroed. - STAssertEquals([writer totalLogged], 2, nil); + STAssertEquals([countingWriter_ count], (NSUInteger)2, nil); + STAssertEquals([writer count], (NSUInteger)2, nil); // Should not be zeroed. + STAssertEquals([writer totalLogged], (NSUInteger)2, nil); [self compareWriter:countingWriter_ withExpectedLogging:[NSArray arrayWithObjects:@"oop",@"ack", nil] @@ -180,18 +179,18 @@ // Wipe the slates clean. [writer reset]; [countingWriter_ reset]; - STAssertEquals([writer count], 0, nil); - STAssertEquals([writer totalLogged], 0, nil); + STAssertEquals([writer count], (NSUInteger)0, nil); + STAssertEquals([writer totalLogged], (NSUInteger)0, nil); // An error log level should print the buffer and empty it. [logger_ logDebug:@"oop"]; [logger_ logInfo:@"ack"]; - STAssertEquals([writer droppedLogCount], 0, nil); - STAssertEquals([writer totalLogged], 2, nil); + STAssertEquals([writer droppedLogCount], (NSUInteger)0, nil); + STAssertEquals([writer totalLogged], (NSUInteger)2, nil); [logger_ logError:@"blargh"]; - STAssertEquals([countingWriter_ count], 3, nil); - STAssertEquals([writer droppedLogCount], 0, nil); + STAssertEquals([countingWriter_ count], (NSUInteger)3, nil); + STAssertEquals([writer droppedLogCount], (NSUInteger)0, nil); [self compareWriter:countingWriter_ withExpectedLogging:[NSArray arrayWithObjects:@"oop", @"ack", @@ -205,13 +204,13 @@ [logger_ logDebug:@"oop"]; [logger_ logInfo:@"ack"]; [logger_ logDebug:@"blargh"]; - STAssertEquals([writer droppedLogCount], 0, nil); - STAssertEquals([writer count], 3, nil); - STAssertEquals([writer totalLogged], 3, nil); + STAssertEquals([writer droppedLogCount], (NSUInteger)0, nil); + STAssertEquals([writer count], (NSUInteger)3, nil); + STAssertEquals([writer totalLogged], (NSUInteger)3, nil); [logger_ logAssert:@"ouch"]; - STAssertEquals([countingWriter_ count], 4, nil); - STAssertEquals([writer droppedLogCount], 0, nil); + STAssertEquals([countingWriter_ count], (NSUInteger)4, nil); + STAssertEquals([writer droppedLogCount], (NSUInteger)0, nil); [self compareWriter:countingWriter_ withExpectedLogging:[NSArray arrayWithObjects:@"oop", @"ack", @"blargh", @"ouch", nil] @@ -224,11 +223,11 @@ [logger_ logDebug:@"oop"]; [logger_ logDebug:@"blargh"]; [logger_ logDebug:@"flong"]; // Fills buffer - STAssertEquals([writer droppedLogCount], 0, nil); - STAssertEquals([writer count], 4, nil); + STAssertEquals([writer droppedLogCount], (NSUInteger)0, nil); + STAssertEquals([writer count], (NSUInteger)4, nil); [logger_ logAssert:@"ouch"]; // should drop "ack" - STAssertEquals([countingWriter_ count], 4, nil); + STAssertEquals([countingWriter_ count], (NSUInteger)4, nil); [self compareWriter:countingWriter_ withExpectedLogging:[NSArray arrayWithObjects:@"oop", @"blargh", @@ -243,11 +242,11 @@ [logger_ logDebug:@"blargh"]; [logger_ logDebug:@"flong"]; // Fills buffer [logger_ logDebug:@"bloogie"]; // should drop "ack" - STAssertEquals([writer droppedLogCount], 1, nil); - STAssertEquals([writer count], 4, nil); + STAssertEquals([writer droppedLogCount], (NSUInteger)1, nil); + STAssertEquals([writer count], (NSUInteger)4, nil); [logger_ logAssert:@"ouch"]; // should drop "oop" - STAssertEquals([countingWriter_ count], 4, nil); + STAssertEquals([countingWriter_ count], (NSUInteger)4, nil); [self compareWriter:countingWriter_ withExpectedLogging:[NSArray arrayWithObjects:@"blargh", @@ -265,22 +264,22 @@ [logger_ setWriter:writer]; [logger_ logInfo:@"ack"]; - STAssertEquals([countingWriter_ count], 0, nil); - STAssertEquals([writer count], 1, nil); + STAssertEquals([countingWriter_ count], (NSUInteger)0, nil); + STAssertEquals([writer count], (NSUInteger)1, nil); [writer dumpContents]; - STAssertEquals([countingWriter_ count], 1, nil); + STAssertEquals([countingWriter_ count], (NSUInteger)1, nil); [self compareWriter:countingWriter_ withExpectedLogging:[NSArray arrayWithObjects:@"ack", nil] line:__LINE__]; [logger_ logDebug:@"oop"]; // should drop "ack" - STAssertEquals([writer count], 1, nil); - STAssertEquals([writer droppedLogCount], 1, nil); + STAssertEquals([writer count], (NSUInteger)1, nil); + STAssertEquals([writer droppedLogCount], (NSUInteger)1, nil); [countingWriter_ reset]; [logger_ logError:@"snoogy"]; // should drop "oop" - STAssertEquals([countingWriter_ count], 1, nil); + STAssertEquals([countingWriter_ count], (NSUInteger)1, nil); [self compareWriter:countingWriter_ withExpectedLogging:[NSArray arrayWithObjects:@"snoogy", nil] @@ -292,7 +291,7 @@ // Run 10 threads, all logging through the same logger. -static volatile int gStoppedThreads = 0; // Total number that have stopped. +static volatile NSUInteger gStoppedThreads = 0; // Total number that have stopped. - (void)bangMe:(id)info { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; @@ -320,15 +319,15 @@ static volatile int gStoppedThreads = 0; // Total number that have stopped. - (void)testThreading { - const int kThreadCount = 10; - const int kCapacity = 10; + const NSUInteger kThreadCount = 10; + const NSUInteger kCapacity = 10; GTMLoggerRingBufferWriter *writer = [GTMLoggerRingBufferWriter ringBufferWriterWithCapacity:kCapacity writer:countingWriter_]; [logger_ setWriter:writer]; - for (int i = 0; i < kThreadCount; i++) { + for (NSUInteger i = 0; i < kThreadCount; i++) { [NSThread detachNewThreadSelector:@selector(bangMe:) toTarget:self withObject:logger_]; @@ -345,8 +344,8 @@ static volatile int gStoppedThreads = 0; // Total number that have stopped. // Now make sure we get back what's expected. STAssertEquals([writer count], kThreadCount, nil); - STAssertEquals([countingWriter_ count], 0, nil); // Nothing should be logged - STAssertEquals([writer totalLogged], 420, nil); + STAssertEquals([countingWriter_ count], (NSUInteger)0, nil); // Nothing should be logged + STAssertEquals([writer totalLogged], (NSUInteger)420, nil); [logger_ logError:@"bork"]; STAssertEquals([countingWriter_ count], kCapacity, nil); diff --git a/Foundation/GTMNSAppleEvent+HandlerTest.applescript b/Foundation/GTMNSAppleEvent+HandlerTest.applescript index ec99433..a0822f9 100644 --- a/Foundation/GTMNSAppleEvent+HandlerTest.applescript +++ b/Foundation/GTMNSAppleEvent+HandlerTest.applescript @@ -14,6 +14,21 @@ -- the License. -- +script parentTestScript + property parentTestScriptProperty : 6 + on parentTestScriptFunc() + return "parent" + end parentTestScriptFunc +end script + +script testScript + property parent : parentTestScript + property testScriptProperty : 5 + on testScriptFunc() + return "child" + end testScriptFunc +end script + property foo : 1 on test() @@ -35,5 +50,9 @@ on testAdd of a onto b given otherValue:d return a + b + d end testAdd +on testGetScript() + return testScript +end testGetScript + on open end open diff --git a/Foundation/GTMNSAppleEventDescriptor+Foundation.h b/Foundation/GTMNSAppleEventDescriptor+Foundation.h index 51a724f..fe3e1f0 100644 --- a/Foundation/GTMNSAppleEventDescriptor+Foundation.h +++ b/Foundation/GTMNSAppleEventDescriptor+Foundation.h @@ -1,5 +1,5 @@ // -// NSAppleEventDescriptor+Foundation.h +// GTMNSAppleEventDescriptor+Foundation.h // // Copyright 2008 Google Inc. // @@ -18,15 +18,16 @@ #import <Foundation/Foundation.h> #import "GTMDefines.h" +#import "GTMFourCharCode.h" // A category for dealing with NSAppleEventDescriptors and NSArrays. @interface NSAppleEventDescriptor (GTMAppleEventDescriptorArrayAdditions) // Used to register the types you know how to convert into // NSAppleEventDescriptors. -// See examples in NSAppleEventDescriptor+String, NSAppleEventDescriptor+Number -// etc. +// See examples in GTMNSAppleEventDescriptor+Foundation. // Args: // selector - selector to call for any of the types in |types| +// -(NSAppleEventDesc *)selector_name; // types - an std c array of types of length |count| // count - number of types in |types| + (void)gtm_registerSelector:(SEL)selector @@ -89,3 +90,11 @@ timeOut:(NSTimeInterval)timeout reply:(NSAppleEventDescriptor**)reply; @end + +@interface GTMFourCharCode (GTMAppleEventDescriptorObjectAdditions) + +// if you call gtm_appleEventDescriptor on GTMFourCharCode it will be of +// type typeType. If you need something different (like typeProperty) this +// allows you to define the type you want. +- (NSAppleEventDescriptor*)gtm_appleEventDescriptorOfType:(DescType)type; +@end diff --git a/Foundation/GTMNSAppleEventDescriptor+Foundation.m b/Foundation/GTMNSAppleEventDescriptor+Foundation.m index 2b6acd4..deb375f 100644 --- a/Foundation/GTMNSAppleEventDescriptor+Foundation.m +++ b/Foundation/GTMNSAppleEventDescriptor+Foundation.m @@ -17,7 +17,7 @@ // #import "GTMNSAppleEventDescriptor+Foundation.h" -#import "GTMFourCharCode.h" +#import "GTMDebugSelectorValidation.h" #import <Carbon/Carbon.h> // Needed Solely For keyASUserRecordFields // Map of types to selectors. @@ -29,6 +29,14 @@ static NSMutableDictionary *gTypeMap = nil; forTypes:(DescType*)types count:(NSUInteger)count { if (selector && types && count > 0) { +#if DEBUG + NSAppleEventDescriptor *desc + = [[[NSAppleEventDescriptor alloc] initListDescriptor] autorelease]; + GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments(desc, + selector, + @encode(id), + NULL); +#endif @synchronized(self) { if (!gTypeMap) { gTypeMap = [[NSMutableDictionary alloc] init]; @@ -222,6 +230,10 @@ static NSMutableDictionary *gTypeMap = nil; return value; } +- (GTMFourCharCode*)gtm_fourCharCodeValue { + return [GTMFourCharCode fourCharCodeWithFourCharCode:[self typeCodeValue]]; +} + @end @implementation NSObject (GTMAppleEventDescriptorObjectAdditions) @@ -458,6 +470,35 @@ static NSMutableDictionary *gTypeMap = nil; @end +@implementation GTMFourCharCode (GTMAppleEventDescriptorObjectAdditions) + ++ (void)load { + DescType types[] = { + typeType, + typeKeyword, + typeApplSignature, + typeEnumerated, + }; + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + [NSAppleEventDescriptor gtm_registerSelector:@selector(gtm_fourCharCodeValue) + forTypes:types + count:sizeof(types)/sizeof(DescType)]; + [pool release]; +} + +- (NSAppleEventDescriptor*)gtm_appleEventDescriptor { + return [self gtm_appleEventDescriptorOfType:typeType]; +} + +- (NSAppleEventDescriptor*)gtm_appleEventDescriptorOfType:(DescType)type { + FourCharCode code = [self fourCharCode]; + return [NSAppleEventDescriptor descriptorWithDescriptorType:type + bytes:&code + length:sizeof(code)]; +} +@end + @implementation NSAppleEventDescriptor (GTMAppleEventDescriptorAdditions) - (BOOL)gtm_sendEventWithMode:(AESendMode)mode diff --git a/Foundation/GTMNSAppleEventDescriptor+FoundationTest.m b/Foundation/GTMNSAppleEventDescriptor+FoundationTest.m index c35c352..833fd5a 100644 --- a/Foundation/GTMNSAppleEventDescriptor+FoundationTest.m +++ b/Foundation/GTMNSAppleEventDescriptor+FoundationTest.m @@ -52,16 +52,16 @@ DescType type; [NSAppleEventDescriptor gtm_registerSelector:nil forTypes:&type count:1]; - [NSAppleEventDescriptor gtm_registerSelector:@selector(retain) + [NSAppleEventDescriptor gtm_registerSelector:@selector(initListDescriptor) forTypes:nil count:1]; - [NSAppleEventDescriptor gtm_registerSelector:@selector(retain) + [NSAppleEventDescriptor gtm_registerSelector:@selector(initListDescriptor) forTypes:&type count:0]; // Test the duplicate case - [NSAppleEventDescriptor gtm_registerSelector:@selector(retain) + [NSAppleEventDescriptor gtm_registerSelector:@selector(initListDescriptor) forTypes:&type count:1]; - [GTMUnitTestDevLog expectPattern:@"retain being replaced with retain exists " - "for type: [0-9]+"]; - [NSAppleEventDescriptor gtm_registerSelector:@selector(retain) + [GTMUnitTestDevLog expectPattern:@"initListDescriptor being replaced with " + "initListDescriptor exists for type: [0-9]+"]; + [NSAppleEventDescriptor gtm_registerSelector:@selector(initListDescriptor) forTypes:&type count:1]; } @@ -558,6 +558,23 @@ } } +- (void)testDescriptorWithGTMFourCharCode { + GTMFourCharCode *fcc = [GTMFourCharCode fourCharCodeWithFourCharCode:'APPL']; + STAssertNotNil(fcc, nil); + NSAppleEventDescriptor *desc = [fcc gtm_appleEventDescriptor]; + STAssertNotNil(desc, nil); + GTMFourCharCode *fcc2 = [desc gtm_objectValue]; + STAssertNotNil(fcc2, nil); + STAssertEqualObjects(fcc, fcc2, nil); + STAssertEquals([desc descriptorType], (DescType)typeType, nil); + desc = [fcc gtm_appleEventDescriptorOfType:typeKeyword]; + STAssertNotNil(desc, nil); + fcc2 = [desc gtm_objectValue]; + STAssertNotNil(fcc2, nil); + STAssertEqualObjects(fcc, fcc2, nil); + STAssertEquals([desc descriptorType], (DescType)typeKeyword, nil); +} + - (void)handleEvent:(NSAppleEventDescriptor*)event withReply:(NSAppleEventDescriptor*)reply { gotEvent_ = YES; diff --git a/Foundation/GTMNSAppleEventDescriptor+Handler.m b/Foundation/GTMNSAppleEventDescriptor+Handler.m index b5b2974..da0197b 100644 --- a/Foundation/GTMNSAppleEventDescriptor+Handler.m +++ b/Foundation/GTMNSAppleEventDescriptor+Handler.m @@ -1,5 +1,5 @@ // -// NSAppleEventDescriptor+Handler.m +// GTMNSAppleEventDescriptor+Handler.m // // Copyright 2008 Google Inc. // diff --git a/Foundation/GTMNSAppleEventDescriptor+HandlerTest.m b/Foundation/GTMNSAppleEventDescriptor+HandlerTest.m index 769e014..a137d8a 100644 --- a/Foundation/GTMNSAppleEventDescriptor+HandlerTest.m +++ b/Foundation/GTMNSAppleEventDescriptor+HandlerTest.m @@ -1,5 +1,5 @@ // -// NSAppleEventDescriptor+HandlerTest.m +// GTNNSAppleEventDescriptor+HandlerTest.m // // Copyright 2008 Google Inc. // diff --git a/Foundation/GTMNSAppleScript+Handler.h b/Foundation/GTMNSAppleScript+Handler.h index ca3d6d2..2c38f36 100644 --- a/Foundation/GTMNSAppleScript+Handler.h +++ b/Foundation/GTMNSAppleScript+Handler.h @@ -19,9 +19,23 @@ #import <Foundation/Foundation.h> #import "GTMDefines.h" +// :::WARNING::: NSAppleScript and Garbage Collect (GC) +// +// As of 10.5.4 (and below) Apple has bugs in NSAppleScript when running with +// GC; ie-things crash that have nothing to do w/ this or your code. See +// http://rails.wincent.com/issues/640 for a good amount of detail about the +// problems and simple cases that show it. + // A category for calling handlers in NSAppleScript + +enum { + // Data type is OSAID. These will generally be representing + // scripts. + typeGTMOSAID = 'GTMO' +}; + @interface NSAppleScript(GTMAppleScriptHandlerAdditions) -// This method allows us to call a specific handler in an AppleScript. +// Allows us to call a specific handler in an AppleScript. // parameters are passed in left-right order 0-n. // // Args: @@ -36,24 +50,73 @@ error:(NSDictionary**)error; +// Allows us to call a specific labeled handler in an AppleScript. +// Parameters for a labeled handler can be in any order, as long as the +// order of the params array corresponds to the order of the labels array +// such that labels are associated with their correct parameter values. +// +// Args: +// handler - name of the handler to call in the Applescript +// labels - the labels to associate with the parameters +// params - the parameters to pass to the handler +// count - number of labels/parameters +// error - in non-nil returns any error that may have occurred. +// +// Returns: +// The result of the handler being called. nil on failure. - (NSAppleEventDescriptor*)gtm_executeLabeledHandler:(NSString*)handler labels:(AEKeyword*)labels parameters:(id*)params count:(NSUInteger)count error:(NSDictionary **)error; + +// Same as executeAppleEvent:error: except that it handles return values of +// script correctly. Return values containing scripts will have the +// typeGTMOSAID. Calling gtm_objectValue on a NSAppleEventDescriptor of +// typeGTMOSAID will resolve correctly to a script value. We don't use +// typeScript because that actually copies the script instead of returning the +// actual value. Therefore if you called executeAppleEvent:error: (instead of +// the GTM version) to execute an event that returns a script, you will +// get a completely new Applescript, instead of the actual script you wanted. If +// you are working with script information, use gtm_executeAppleEvent:error +// instead of executeAppleEvent:error: to avoid the problem. +- (NSAppleEventDescriptor *)gtm_executeAppleEvent:(NSAppleEventDescriptor *)event + error:(NSDictionary **)error; + +// The set of all handlers that are defined in this script and its parents. +// Remember that handlers that are defined in an sdef will have their +// eventclass/eventid as their handler instead of the name seen in the script. +// So: +// on open(a) +// blah +// end open +// won't be "open" it will be "aevtodoc". - (NSSet*)gtm_handlers; + +// The set of all properties that are defined in this script and its parents. +// Note that properties can be strings or GTMNSFourCharCodes, so expect both +// coming back in the set. - (NSSet*)gtm_properties; -- (BOOL)gtm_setValue:(id)value forProperty:(NSString*)property; -- (id)gtm_valueForProperty:(NSString*)property; -@end +// Return a value for a property. Will look up the inheritence tree. +// Property must be an NSString or a GTMFourCharCode. +- (id)gtm_valueForProperty:(id)property; -@interface NSAppleEventDescriptor(GTMAppleEventDescriptorScriptAdditions) +// Return a value for a property by type (eg pASParent). Will look up the +// inheritence tree +- (id)gtm_valueForPropertyEnum:(DescType)property; -// Return an NSAppleScript for a desc of typeScript -// Returns nil on failure. -- (NSAppleScript*)gtm_scriptValue; +// Set a script property value. Returns YES/NO on success/failure. +// Property must be of kind NSString or GTMFourCharCode. +// If addingDefinition is YES, it will add a definition to the script +// if the value doesn't exist in the script or one of it's parents. +- (BOOL)gtm_setValue:(id)value + forProperty:(id)property + addingDefinition:(BOOL)adding; -// Return a NSString with [eventClass][eventID] for typeEvent 'evnt' -- (NSString*)gtm_eventValue; +// Set a value for a property by type (eg pASParent). See note above +// for gtm_setValue:forProperty. +- (BOOL)gtm_setValue:(id)value + forPropertyEnum:(DescType)property + addingDefinition:(BOOL)adding; @end diff --git a/Foundation/GTMNSAppleScript+Handler.m b/Foundation/GTMNSAppleScript+Handler.m index d3eeffd..ec6f5d0 100644 --- a/Foundation/GTMNSAppleScript+Handler.m +++ b/Foundation/GTMNSAppleScript+Handler.m @@ -1,5 +1,5 @@ // -// NSAppleScript+Handler.m +// GTMNSAppleScript+Handler.m // // Copyright 2008 Google Inc. // @@ -28,12 +28,47 @@ + (ComponentInstance)_defaultScriptingComponent; - (OSAID) _compiledScriptID; - (id)_initWithData:(NSData*)data error:(NSDictionary**)error; +- (id)_initWithScriptIDNoCopy:(OSAID)osaID; ++ (id)_infoForOSAError:(OSAError)error; @end @interface NSMethodSignature (NSPrivate) + (id)signatureWithObjCTypes:(const char *)fp8; @end +// Our own private interfaces. +@interface NSAppleScript (GTMAppleScriptHandlerAdditionsPrivate) + +// Return an descriptor for a property. Properties are only supposed to be +// of type NSString or GTMFourCharCode. GTMFourCharCode's need special handling +// as they must be turned into NSAppleEventDescriptors of typeProperty. +- (NSAppleEventDescriptor*)gtm_descriptorForPropertyValue:(id)property; + +// Return an NSAppleEventDescriptor for a given property. +// |property| must be kind of class GTMFourCharCode +- (NSAppleEventDescriptor*)gtm_valueDescriptorForProperty:(id)property; + +// Utility routine for extracting multiple values in scripts and their +// parents. +- (NSSet*)gtm_allValuesUsingSelector:(SEL)selector; + +// Utility routine for extracting the handlers for a specific script without +// referring to parent scripts. +- (NSSet*)gtm_scriptHandlers; + +// Utility routine for extracting the properties for a specific script without +// referring to parent scripts. +- (NSSet*)gtm_scriptProperties; + +// Handles creating an NSAppleEventDescriptor from an OSAID +- (NSAppleEventDescriptor*)descForScriptID:(OSAID)scriptID + component:(ComponentInstance)component; + +// Utility methods for converting between real and generic OSAIDs. +- (OSAID)gtm_genericID:(OSAID)osaID forComponent:(ComponentInstance)component; +- (OSAID)gtm_realIDAndComponent:(ComponentInstance*)component; +@end + @implementation NSAppleScript(GTMAppleScriptHandlerAdditions) GTM_METHOD_CHECK(NSAppleEventDescriptor, gtm_descriptorWithPositionalHandler:parametersArray:); // COV_NF_LINE GTM_METHOD_CHECK(NSAppleEventDescriptor, gtm_descriptorWithLabeledHandler:labels:parameters:count:); // COV_NF_LINE @@ -56,25 +91,41 @@ GTM_METHOD_CHECK(NSAppleEventDescriptor, gtm_registerSelector:forTypes:count:); [NSAppleEventDescriptor gtm_registerSelector:@selector(gtm_eventValue) forTypes:types2 count:sizeof(types2)/sizeof(DescType)]; + + DescType types3[] = { + typeGTMOSAID + }; + + [NSAppleEventDescriptor gtm_registerSelector:@selector(gtm_osaIDValue) + forTypes:types3 + count:sizeof(types3)/sizeof(DescType)]; [pool release]; } -- (OSAID)gtm_realIDAndComponent:(ComponentInstance*)component { +- (NSAppleEventDescriptor *)gtm_executeAppleEvent:(NSAppleEventDescriptor *)event + error:(NSDictionary **)error { if (![self isCompiled]) { - NSDictionary *error; - if (![self compileAndReturnError:&error]) { - _GTMDevLog(@"Unable to compile script: %@ %@", self, error); - return kOSANullScript; + if (![self compileAndReturnError:error]) { + return nil; } } - OSAID genericID = [self _compiledScriptID]; - ComponentInstance genericComponent = [NSAppleScript _defaultScriptingComponent]; - OSAError err = OSAGenericToRealID(genericComponent, &genericID, component); - if (err) { - _GTMDevLog(@"Unable to get real id script: %@ %@", self, err); // COV_NF_LINE - genericID = kOSANullScript; // COV_NF_LINE + NSAppleEventDescriptor *desc = nil; + ComponentInstance component; + OSAID scriptID = [self gtm_realIDAndComponent:&component]; + OSAID valueID; + OSAError err = OSAExecuteEvent(component, [event aeDesc], scriptID, + kOSAModeNull, &valueID); + if (err == noErr) { + // descForScriptID:component: is what sets this apart from the + // standard executeAppelEvent:error: in that it handles + // taking script results and turning them into AEDescs of typeGTMOSAID + // instead of typeScript. + desc = [self descForScriptID:valueID component:component]; } - return genericID; + if (err && error) { + *error = [NSAppleScript _infoForOSAError:err]; + } + return desc; } - (NSAppleEventDescriptor*)gtm_executePositionalHandler:(NSString*)handler @@ -83,7 +134,7 @@ GTM_METHOD_CHECK(NSAppleEventDescriptor, gtm_registerSelector:forTypes:count:); NSAppleEventDescriptor *event = [NSAppleEventDescriptor gtm_descriptorWithPositionalHandler:handler parametersArray:params]; - return [self executeAppleEvent:event error:error]; + return [self gtm_executeAppleEvent:event error:error]; } - (NSAppleEventDescriptor*)gtm_executeLabeledHandler:(NSString*)handler @@ -96,60 +147,50 @@ GTM_METHOD_CHECK(NSAppleEventDescriptor, gtm_registerSelector:forTypes:count:); labels:labels parameters:params count:count]; - return [self executeAppleEvent:event error:error]; + return [self gtm_executeAppleEvent:event error:error]; } - (NSSet*)gtm_handlers { - AEDescList names = { typeNull, NULL }; - NSArray *array = nil; - ComponentInstance component; - OSAID osaID = [self gtm_realIDAndComponent:&component]; - OSAError err = OSAGetHandlerNames(component, kOSAModeNull, osaID, &names); - if (!err) { - NSAppleEventDescriptor *desc - = [[[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&names] autorelease]; - array = [desc gtm_objectValue]; - } - if (err) { - _GTMDevLog(@"Error getting handlers: %d", err); - } - return [NSSet setWithArray:array]; + return [self gtm_allValuesUsingSelector:@selector(gtm_scriptHandlers)]; } - + - (NSSet*)gtm_properties { - AEDescList names = { typeNull, NULL }; - NSArray *array = nil; - ComponentInstance component; - OSAID osaID = [self gtm_realIDAndComponent:&component]; - OSAError err = OSAGetPropertyNames(component, kOSAModeNull, osaID, &names); - if (!err) { - NSAppleEventDescriptor *desc - = [[[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&names] autorelease]; - array = [desc gtm_objectValue]; - } - if (err) { - _GTMDevLog(@"Error getting properties: %d", err); - } - return [NSSet setWithArray:array]; + return [self gtm_allValuesUsingSelector:@selector(gtm_scriptProperties)]; } -- (BOOL)gtm_setValue:(id)value forProperty:(NSString*)property { +// Set a value for a property by type (eg pASTopLevelScript) +- (BOOL)gtm_setValue:(id)value + forPropertyEnum:(DescType)property + addingDefinition:(BOOL)adding { + GTMFourCharCode *fcc + = [GTMFourCharCode fourCharCodeWithFourCharCode:property]; + return [self gtm_setValue:value forProperty:fcc addingDefinition:adding]; +} + +- (BOOL)gtm_setValue:(id)value + forProperty:(id)property + addingDefinition:(BOOL)adding{ + OSAError error = paramErr; BOOL wasGood = NO; + NSAppleEventDescriptor *propertyName + = [self gtm_descriptorForPropertyValue:property]; NSAppleEventDescriptor *desc = [value gtm_appleEventDescriptor]; - NSAppleEventDescriptor *propertyName = [property gtm_appleEventDescriptor]; - OSAError error = paramErr; - if (desc && propertyName) { + if (propertyName && desc) { + NSAppleScript *script = self; OSAID valueID = kOSANullScript; ComponentInstance component; - OSAID scriptID = [self gtm_realIDAndComponent:&component]; + OSAID scriptID = [script gtm_realIDAndComponent:&component]; error = OSACoerceFromDesc(component, [desc aeDesc], kOSAModeNull, &valueID); - if (!error) { - error = OSASetProperty(component, kOSAModeNull, - scriptID, [propertyName aeDesc], valueID); - if (!error) { + if (error == noErr) { + error = OSASetProperty(component, + adding ? kOSAModeNull : kOSAModeDontDefine, + scriptID, + [propertyName aeDesc], + valueID); + if (error == noErr) { wasGood = YES; } } @@ -161,38 +202,13 @@ GTM_METHOD_CHECK(NSAppleEventDescriptor, gtm_registerSelector:forTypes:count:); return wasGood; } -- (id)gtm_valueForProperty:(NSString*)property { - id value = nil; - NSAppleEventDescriptor *propertyName = [property gtm_appleEventDescriptor]; - OSAError error = paramErr; - if (propertyName) { - ComponentInstance component; - OSAID scriptID = [self gtm_realIDAndComponent:&component]; - OSAID valueID = kOSANullScript; - error = OSAGetProperty(component, - kOSAModeNull, - scriptID, - [propertyName aeDesc], - &valueID); - if (!error) { - AEDesc aeDesc; - error = OSACoerceToDesc(component, - valueID, - typeWildCard, - kOSAModeNull, - &aeDesc); - if (!error) { - NSAppleEventDescriptor *desc - = [[[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&aeDesc] autorelease]; - value = [desc gtm_objectValue]; - } - } - } - if (error) { - _GTMDevLog(@"Unable to get valueForProperty:%@ from %@ (%d)", - property, self, error); - } - return value; +- (id)gtm_valueForProperty:(id)property { + return [[self gtm_valueDescriptorForProperty:property] gtm_objectValue]; +} + +- (id)gtm_valueForPropertyEnum:(DescType)property { + GTMFourCharCode *fcc = [GTMFourCharCode fourCharCodeWithFourCharCode:property]; + return [self gtm_valueForProperty:fcc]; } - (NSAppleEventDescriptor*)gtm_appleEventDescriptor { @@ -200,15 +216,16 @@ GTM_METHOD_CHECK(NSAppleEventDescriptor, gtm_registerSelector:forTypes:count:); OSAID osaID = [self gtm_realIDAndComponent:&component]; AEDesc result = { typeNull, NULL }; NSAppleEventDescriptor *desc = nil; - OSAError err = OSACoerceToDesc(component, - osaID, - typeScript, - kOSAModeNull, - &result); - if (!err) { - desc = [[[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&result] autorelease]; + OSAError error = OSACoerceToDesc(component, + osaID, + typeScript, + kOSAModeNull, + &result); + if (error == noErr) { + desc = [[[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&result] + autorelease]; } else { - _GTMDevLog(@"Unable to coerce script %d", err); // COV_NF_LINE + _GTMDevLog(@"Unable to coerce script %d", error); // COV_NF_LINE } return desc; } @@ -255,7 +272,195 @@ GTM_METHOD_CHECK(NSAppleEventDescriptor, gtm_registerSelector:forTypes:count:); } @end -@implementation NSAppleEventDescriptor(GMAppleEventDescriptorScriptAdditions) +@implementation NSAppleScript (GTMAppleScriptHandlerAdditionsPrivate) + +- (NSAppleEventDescriptor*)gtm_descriptorForPropertyValue:(id)property { + NSAppleEventDescriptor *propDesc = nil; + if ([property isKindOfClass:[GTMFourCharCode class]]) { + propDesc = [property gtm_appleEventDescriptorOfType:typeProperty]; + } else if ([property isKindOfClass:[NSString class]]) { + propDesc = [property gtm_appleEventDescriptor]; + } + return propDesc; +} + +- (NSAppleEventDescriptor*)gtm_valueDescriptorForProperty:(id)property { + OSAError error = paramErr; + NSAppleEventDescriptor *desc = nil; + NSAppleEventDescriptor *propertyName + = [self gtm_descriptorForPropertyValue:property]; + if (propertyName) { + ComponentInstance component; + OSAID scriptID = [self gtm_realIDAndComponent:&component]; + OSAID valueID = kOSANullScript; + error = OSAGetProperty(component, + kOSAModeNull, + scriptID, + [propertyName aeDesc], + &valueID); + if (error == noErr) { + desc = [self descForScriptID:valueID component:component]; + } + } + if (error) { + _GTMDevLog(@"Unable to get valueForProperty:%@ from %@ (%d)", + property, self, error); + } + return desc; +} + +- (NSSet*)gtm_allValuesUsingSelector:(SEL)selector { + NSMutableSet *resultSet = [NSMutableSet set]; + NSAppleEventDescriptor *scriptDesc = [self gtm_appleEventDescriptor]; + NSMutableSet *scriptDescsWeveSeen = [NSMutableSet set]; + GTMFourCharCode *fcc = [GTMFourCharCode fourCharCodeWithFourCharCode:pASParent]; + Class appleScriptClass = [NSAppleScript class]; + while (scriptDesc) { + NSAppleScript *script = [scriptDesc gtm_objectValue]; + if ([script isKindOfClass:appleScriptClass]) { + NSData *data = [scriptDesc data]; + if (!data || [scriptDescsWeveSeen containsObject:data]) { + break; + } else { + [scriptDescsWeveSeen addObject:data]; + } + NSSet *newSet = [script performSelector:selector]; + [resultSet unionSet:newSet]; + scriptDesc = [script gtm_valueDescriptorForProperty:fcc]; + } else { + break; + } + } + return resultSet; +} + +- (NSSet*)gtm_scriptHandlers { + AEDescList names = { typeNull, NULL }; + NSArray *array = nil; + ComponentInstance component; + OSAID osaID = [self gtm_realIDAndComponent:&component]; + OSAError error = OSAGetHandlerNames(component, kOSAModeNull, osaID, &names); + if (error == noErr) { + NSAppleEventDescriptor *desc + = [[[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&names] + autorelease]; + array = [desc gtm_objectValue]; + } + if (error != noErr) { + _GTMDevLog(@"Error getting handlers: %d", error); // COV_NF_LINE + } + return [NSSet setWithArray:array]; +} + +- (NSSet*)gtm_scriptProperties { + AEDescList names = { typeNull, NULL }; + NSArray *array = nil; + ComponentInstance component; + OSAID osaID = [self gtm_realIDAndComponent:&component]; + OSAError error = OSAGetPropertyNames(component, kOSAModeNull, osaID, &names); + if (error == noErr) { + NSAppleEventDescriptor *desc + = [[[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&names] + autorelease]; + array = [desc gtm_objectValue]; + } + if (error != noErr) { + _GTMDevLog(@"Error getting properties: %d", error); // COV_NF_LINE + } + return [NSSet setWithArray:array]; +} + +- (OSAID)gtm_genericID:(OSAID)osaID forComponent:(ComponentInstance)component { + ComponentInstance genericComponent = [NSAppleScript _defaultScriptingComponent]; + OSAID exactID = osaID; + OSAError error = OSARealToGenericID(genericComponent, &exactID, component); + if (error != noErr) { + _GTMDevLog(@"Unable to get real id script: %@ %d", self, error); // COV_NF_LINE + exactID = kOSANullScript; // COV_NF_LINE + } + return exactID; +} + +- (NSAppleEventDescriptor*)descForScriptID:(OSAID)osaID + component:(ComponentInstance)component { + NSAppleEventDescriptor *desc = nil; + // If we have a script, return a typeGTMOSAID, otherwise convert it to + // it's default AEDesc using OSACoerceToDesc with typeWildCard. + long value = 0; + OSAError err = noErr; + if (osaID == 0) { + desc = [NSAppleEventDescriptor nullDescriptor]; + } else { + err = OSAGetScriptInfo(component, + osaID, + kOSAScriptBestType, + &value); + if (err == noErr) { + if (value == typeScript) { + osaID = [self gtm_genericID:osaID forComponent:component]; + desc = [NSAppleEventDescriptor descriptorWithDescriptorType:typeGTMOSAID + bytes:&osaID + length:sizeof(osaID)]; + } else { + AEDesc aeDesc; + err = OSACoerceToDesc(component, + osaID, + typeWildCard, + kOSAModeNull, + &aeDesc); + if (err == noErr) { + desc = [[[NSAppleEventDescriptor alloc] + initWithAEDescNoCopy:&aeDesc] autorelease]; + } + } + } + } + if (err != noErr) { + _GTMDevLog(@"Unable to create desc for id:%d (%d)", osaID, err); // COV_NF_LINE + } + return desc; +} + +- (OSAID)gtm_realIDAndComponent:(ComponentInstance*)component { + if (![self isCompiled]) { + NSDictionary *error; + if (![self compileAndReturnError:&error]) { + _GTMDevLog(@"Unable to compile script: %@ %@", self, error); + return kOSANullScript; + } + } + OSAID genericID = [self _compiledScriptID]; + ComponentInstance genericComponent = [NSAppleScript _defaultScriptingComponent]; + OSAError error = OSAGenericToRealID(genericComponent, &genericID, component); + if (error != noErr) { + _GTMDevLog(@"Unable to get real id script: %@ %d", self, error); // COV_NF_LINE + genericID = kOSANullScript; // COV_NF_LINE + } + return genericID; +} + +@end + +// Private methods for dealing with Scripts/Events and NSAppleEventDescriptors +@interface NSAppleEventDescriptor (GTMAppleEventDescriptorScriptAdditions) + +// Return an NSAppleScript for a desc of typeScript. This will create a new +// Applescript that is a copy of the script that you want. +// Returns nil on failure. +- (NSAppleScript*)gtm_scriptValue; + +// Return an NSAppleScript for a desc of typeGTMOSAID. This will not copy the +// script, but will create an NSAppleScript wrapping the script represented +// by the OSAID. +// Returns nil on failure. +- (NSAppleScript*)gtm_osaIDValue; + +// Return a NSString with [eventClass][eventID] for typeEvent 'evnt' +- (NSString*)gtm_eventValue; +@end + + +@implementation NSAppleEventDescriptor (GMAppleEventDescriptorScriptAdditions) - (NSAppleScript*)gtm_scriptValue { NSDictionary *error; @@ -267,6 +472,12 @@ GTM_METHOD_CHECK(NSAppleEventDescriptor, gtm_registerSelector:forTypes:count:); return script; } +- (NSAppleScript*)gtm_osaIDValue { + _GTMDevAssert([[self data] length] == sizeof(OSAID), nil); + OSAID osaID = *(const OSAID*)[[self data] bytes]; + return [[[NSAppleScript alloc] _initWithScriptIDNoCopy:osaID] autorelease]; +} + - (NSString*)gtm_eventValue { struct AEEventRecordStruct { AEEventClass eventClass; diff --git a/Foundation/GTMNSAppleScript+HandlerTest.m b/Foundation/GTMNSAppleScript+HandlerTest.m index 5a9d52b..d4bb9bc 100644 --- a/Foundation/GTMNSAppleScript+HandlerTest.m +++ b/Foundation/GTMNSAppleScript+HandlerTest.m @@ -1,5 +1,5 @@ // -// NSAppleScript+HandlerTest.m +// GTMNSAppleScript+HandlerTest.m // // Copyright 2008 Google Inc. // @@ -21,6 +21,9 @@ #import "GTMNSAppleScript+Handler.h" #import "GTMNSAppleEventDescriptor+Foundation.h" #import "GTMUnitTestDevLog.h" +#import "GTMGarbageCollection.h" +#import "GTMSystemVersion.h" +#import "GTMFourCharCode.h" @interface GTMNSAppleScript_HandlerTest : GTMTestCase { NSAppleScript *script_; @@ -28,16 +31,34 @@ @end @implementation GTMNSAppleScript_HandlerTest +- (void)invokeTest { + // NOTE: These tests are disabled in GC is on. See the comment/warning in the + // GTMNSAppleScript+Handler.h for more details, but we disable them to avoid + // the tests failing (crashing) when it's Apple's bug. Please bump the system + // check as appropriate when new systems are tested. Currently broken on + // 10.5.4 and below. Radar 6126682. + long major, minor, bugfix; + [GTMSystemVersion getMajor:&major minor:&minor bugFix:&bugfix]; + if (!(GTMIsGarbageCollectionEnabled() + && major <= 10 && minor <= 5 && bugfix <= 4)) { + [super invokeTest]; + } else { + NSLog(@"--- %@ NOT run because of GC incompatibilites ---", [self name]); + } +} + - (void)setUp { - NSBundle *bundle = [NSBundle bundleForClass:[GTMNSAppleScript_HandlerTest class]]; + NSBundle *bundle + = [NSBundle bundleForClass:[GTMNSAppleScript_HandlerTest class]]; STAssertNotNil(bundle, nil); NSString *path = [bundle pathForResource:@"GTMNSAppleEvent+HandlerTest" ofType:@"scpt" inDirectory:@"Scripts"]; STAssertNotNil(path, [bundle description]); NSDictionary *error = nil; - script_ = [[NSAppleScript alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] - error:&error]; + script_ + = [[NSAppleScript alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] + error:&error]; STAssertNotNil(script_, [error description]); STAssertNil(error, @"Error should be nil. Error = %@", [error description]); } @@ -47,6 +68,30 @@ script_ = nil; } +- (void)testExecuteAppleEvent { + NSString *source = @"on test()\nreturn 1\nend test"; + NSAppleScript *script + = [[[NSAppleScript alloc] initWithSource:source] autorelease]; + STAssertNotNil(script, nil); + NSDictionary *error = nil; + NSAppleEventDescriptor *desc = [script gtm_executePositionalHandler:@"test" + parameters:nil + error:&error]; + STAssertNotNil(desc, [error description]); + STAssertNil(error, @"Error should be nil. Error = %@", [error description]); + STAssertEquals([desc gtm_objectValue], [NSNumber numberWithInt:1], nil); + + // bogus script + source = @"adf872345ba asdf asdf gr"; + script = [[[NSAppleScript alloc] initWithSource:source] autorelease]; + STAssertNotNil(script, nil); + desc = [script gtm_executePositionalHandler:@"test" + parameters:nil + error:&error]; + STAssertNil(desc, nil); + STAssertNotNil(error, @"Error should not be nil"); +} + - (void)testHandlerNoParamsNoReturn { NSDictionary *error = nil; NSAppleEventDescriptor *desc = [script_ gtm_executePositionalHandler:@"test" @@ -73,9 +118,10 @@ - (void)testHandlerNoParamsWithReturn { NSDictionary *error = nil; - NSAppleEventDescriptor *desc = [script_ gtm_executePositionalHandler:@"testReturnOne" - parameters:nil - error:&error]; + NSAppleEventDescriptor *desc + = [script_ gtm_executePositionalHandler:@"testReturnOne" + parameters:nil + error:&error]; STAssertNotNil(desc, [error description]); STAssertNil(error, @"Error should be nil. Error = %@", [error description]); STAssertEquals([desc descriptorType], (DescType)typeSInt32, nil); @@ -101,9 +147,10 @@ - (void)testHandlerOneParamWithReturn { NSDictionary *error = nil; // Note case change in executeHandler call - NSAppleEventDescriptor *desc = [script_ gtm_executePositionalHandler:@"testreturnParam" - parameters:nil - error:&error]; + NSAppleEventDescriptor *desc + = [script_ gtm_executePositionalHandler:@"testreturnParam" + parameters:nil + error:&error]; STAssertNil(desc, @"Desc should by nil %@", desc); STAssertNotNil(error, nil); error = nil; @@ -128,9 +175,10 @@ NSDictionary *error = nil; // Note case change in executeHandler call // Test case and empty params - NSAppleEventDescriptor *desc = [script_ gtm_executePositionalHandler:@"testADDPArams" - parameters:nil - error:&error]; + NSAppleEventDescriptor *desc + = [script_ gtm_executePositionalHandler:@"testADDPArams" + parameters:nil + error:&error]; STAssertNil(desc, @"Desc should by nil %@", desc); STAssertNotNil(error, nil); @@ -193,10 +241,11 @@ params[2] = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:4] forKey:@"othervalue"]; - NSAppleEventDescriptor *desc = [script_ gtm_executeLabeledHandler:@"testAdd" - labels:labels - parameters:params - count:sizeof(params) / sizeof(id) + NSAppleEventDescriptor *desc + = [script_ gtm_executeLabeledHandler:@"testAdd" + labels:labels + parameters:params + count:sizeof(params) / sizeof(id) error:&error]; STAssertNotNil(desc, [error description]); STAssertNil(error, @"Error should be nil. Error = %@", [error description]); @@ -235,39 +284,136 @@ @"testreturnparam", @"testaddparams", @"testadd", + @"testgetscript", nil]; STAssertEqualObjects(handlers, expected, @"Unexpected handlers?"); } +- (void)testInheritedHandlers { + NSDictionary *error = nil; + NSAppleEventDescriptor *desc + = [script_ gtm_executePositionalHandler:@"testGetScript" + parameters:nil + error:&error]; + STAssertNil(error, nil); + STAssertNotNil(desc, nil); + NSAppleScript *script = [desc gtm_objectValue]; + STAssertTrue([script isKindOfClass:[NSAppleScript class]], nil); + error = nil; + desc = [script gtm_executePositionalHandler:@"parentTestScriptFunc" + parameters:nil error:&error]; + STAssertNil(error, nil); + STAssertNotNil(desc, nil); + NSString *value = [desc gtm_objectValue]; + STAssertEqualObjects(value, @"parent", nil); +} + - (void)testProperties { - NSSet *properties = [script_ gtm_properties]; - NSSet *expected = [NSSet setWithObjects: - @"foo", - @"asdscriptuniqueidentifier", - nil]; + NSDictionary *error = nil; + NSAppleEventDescriptor *desc + = [script_ gtm_executePositionalHandler:@"testGetScript" + parameters:nil + error:&error]; + STAssertNil(error, nil); + STAssertNotNil(desc, nil); + NSAppleScript *script = [desc gtm_objectValue]; + STAssertTrue([script isKindOfClass:[NSAppleScript class]], nil); + + NSSet *properties = [script gtm_properties]; + NSSet *expected + = [NSSet setWithObjects: + @"testscriptproperty", + @"parenttestscriptproperty", + @"foo", + @"testscript", + @"parenttestscript", + @"asdscriptuniqueidentifier", + [GTMFourCharCode fourCharCodeWithFourCharCode:pVersion], + [GTMFourCharCode fourCharCodeWithFourCharCode:pASPrintDepth], + [GTMFourCharCode fourCharCodeWithFourCharCode:pASTopLevelScript], + [GTMFourCharCode fourCharCodeWithFourCharCode:pASResult], + [GTMFourCharCode fourCharCodeWithFourCharCode:pASMinutes], + [GTMFourCharCode fourCharCodeWithFourCharCode:pASDays], + // No constant for linefeed in the 10.5 sdk + // Radar 6132775 Need a constant for the Applescript Property 'lnfd' + [GTMFourCharCode fourCharCodeWithFourCharCode:'lnfd'], + [GTMFourCharCode fourCharCodeWithFourCharCode:pASPi], + [GTMFourCharCode fourCharCodeWithFourCharCode:pASReturn], + [GTMFourCharCode fourCharCodeWithFourCharCode:pASSpace], + [GTMFourCharCode fourCharCodeWithFourCharCode:pASPrintLength], + [GTMFourCharCode fourCharCodeWithFourCharCode:pASQuote], + [GTMFourCharCode fourCharCodeWithFourCharCode:pASWeeks], + [GTMFourCharCode fourCharCodeWithFourCharCode:pTextItemDelimiters], + // Applescript properties should be pASSeconds, but + // on 10.5.4 it is actually using cSeconds. + // Radar 6132696 Applescript root level property is cSeconds + // instead of pASSeconds + [GTMFourCharCode fourCharCodeWithFourCharCode:cSeconds], + [GTMFourCharCode fourCharCodeWithFourCharCode:pASHours], + [GTMFourCharCode fourCharCodeWithFourCharCode:pASTab], + nil]; STAssertEqualObjects(properties, expected, @"Unexpected properties?"); - id value = [script_ gtm_valueForProperty:@"foo"]; - STAssertEqualObjects(value, [NSNumber numberWithInt:1], @"bad property?"); - BOOL goodSet = [script_ gtm_setValue:@"bar" forProperty:@"foo"]; + id value = [script gtm_valueForProperty:@"testScriptProperty"]; + STAssertEqualObjects(value, [NSNumber numberWithInt:5], @"bad property?"); + BOOL goodSet = [script gtm_setValue:@"bar" + forProperty:@"foo" + addingDefinition:NO]; STAssertTrue(goodSet, @"Couldn't set property"); + + // Test local set + value = [script gtm_valueForProperty:@"foo"]; + STAssertEqualObjects(value, @"bar", @"bad property?"); + + // Test inherited set value = [script_ gtm_valueForProperty:@"foo"]; STAssertEqualObjects(value, @"bar", @"bad property?"); - + [GTMUnitTestDevLog expectPattern:@"Unable to setValue:bar forProperty:" "\\(null\\) from <NSAppleScript: 0x[0-9a-f]+> \\(-50\\)"]; - goodSet = [script_ gtm_setValue:@"bar" forProperty:nil]; + goodSet = [script gtm_setValue:@"bar" + forProperty:nil + addingDefinition:NO]; + STAssertFalse(goodSet, @"Set property?"); + + [GTMUnitTestDevLog expectPattern:@"Unable to setValue:bar forProperty:3" + " from <NSAppleScript: 0x[0-9a-f]+> \\(-50\\)"]; + goodSet = [script gtm_setValue:@"bar" + forProperty:[NSNumber numberWithInt:3] + addingDefinition:YES]; STAssertFalse(goodSet, @"Set property?"); + + [GTMUnitTestDevLog expectPattern:@"Unable to get valueForProperty:gargle " "from <NSAppleScript: 0x[0-9a-f]+> \\(-1753\\)"]; - value = [script_ gtm_valueForProperty:@"gargle"]; + value = [script gtm_valueForProperty:@"gargle"]; STAssertNil(value, @"Property named gargle?"); + + goodSet = [script_ gtm_setValue:@"wow" + forProperty:@"addedProperty" + addingDefinition:YES]; + STAssertTrue(goodSet, @"Unable to addProperty"); + + value = [script gtm_valueForProperty:@"addedProperty"]; + STAssertNotNil(value, nil); + STAssertEqualObjects(value, @"wow", nil); + + // http://www.straightdope.com/classics/a3_341.html + NSNumber *newPI = [NSNumber numberWithInt:3]; + goodSet = [script gtm_setValue:newPI + forPropertyEnum:pASPi + addingDefinition:NO]; + STAssertTrue(goodSet, @"Unable to set property"); + value = [script_ gtm_valueForPropertyEnum:pASPi]; + STAssertNotNil(value, nil); + STAssertEqualObjects(value, newPI, @"bad property"); } - (void)testFailures { NSDictionary *error = nil; - NSAppleEventDescriptor *desc = [script_ gtm_executePositionalHandler:@"noSuchTest" - parameters:nil - error:&error]; + NSAppleEventDescriptor *desc + = [script_ gtm_executePositionalHandler:@"noSuchTest" + parameters:nil + error:&error]; STAssertNil(desc, nil); STAssertNotNil(error, nil); @@ -294,15 +440,18 @@ STAssertNil(desc, nil); // Test with a bad script - NSAppleScript *script = [[[NSAppleScript alloc] initWithSource:@"david hasselhoff"] autorelease]; + NSAppleScript *script + = [[[NSAppleScript alloc] initWithSource:@"david hasselhoff"] autorelease]; [GTMUnitTestDevLog expectPattern:@"Unable to compile script: .*"]; - [GTMUnitTestDevLog expectPattern:@"Error getting handlers: -[0-9]+"]; + [GTMUnitTestDevLog expectPattern:@"Unable to coerce script -2147450879"]; NSSet *handlers = [script gtm_handlers]; STAssertEquals([handlers count], (NSUInteger)0, @"Should have no handlers"); [GTMUnitTestDevLog expectPattern:@"Unable to compile script: .*"]; - [GTMUnitTestDevLog expectPattern:@"Error getting properties: -[0-9]+"]; + [GTMUnitTestDevLog expectPattern:@"Unable to coerce script -2147450879"]; NSSet *properties = [script gtm_properties]; - STAssertEquals([properties count], (NSUInteger)0, @"Should have no properties"); + STAssertEquals([properties count], + (NSUInteger)0, + @"Should have no properties"); } - (void)testScriptDescriptors { @@ -325,7 +474,8 @@ [foo test]; NSNumber *val = [foo testReturnParam:[NSNumber numberWithInt:2]]; STAssertEquals([val intValue], 2, @"should be 2"); - val = [foo testAddParams:[NSNumber numberWithInt:2] :[NSNumber numberWithInt:3]]; + val = [foo testAddParams:[NSNumber numberWithInt:2] + :[NSNumber numberWithInt:3]]; STAssertEquals([val intValue], 5, @"should be 5"); } @end diff --git a/Foundation/GTMNSString+FindFolder.m b/Foundation/GTMNSString+FindFolder.m index 7d2f3b5..3a44174 100644 --- a/Foundation/GTMNSString+FindFolder.m +++ b/Foundation/GTMNSString+FindFolder.m @@ -31,12 +31,11 @@ OSErr err = FSFindFolder(theDomain, theFolderType, doCreate, &folderRef); if (err == noErr) { - CFURLRef folderURL = CFURLCreateFromFSRef(kCFAllocatorSystemDefault, &folderRef); + CFURLRef folderURL = CFURLCreateFromFSRef(kCFAllocatorSystemDefault, + &folderRef); if (folderURL) { - - folderPath = GTMNSMakeCollectable(CFURLCopyFileSystemPath(folderURL, kCFURLPOSIXPathStyle)); - [folderPath autorelease]; - + folderPath = GTMCFAutorelease(CFURLCopyFileSystemPath(folderURL, + kCFURLPOSIXPathStyle)); CFRelease(folderURL); } } diff --git a/Foundation/GTMNSString+URLArguments.m b/Foundation/GTMNSString+URLArguments.m index 564b943..46d2c99 100644 --- a/Foundation/GTMNSString+URLArguments.m +++ b/Foundation/GTMNSString+URLArguments.m @@ -30,7 +30,7 @@ NULL, (CFStringRef)@"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8); - return [GTMNSMakeCollectable(escaped) autorelease]; + return GTMCFAutorelease(escaped); } - (NSString*)gtm_stringByUnescapingFromURLArgument { diff --git a/Foundation/GTMNSString+XMLTest.m b/Foundation/GTMNSString+XMLTest.m index dc157dc..4788690 100644 --- a/Foundation/GTMNSString+XMLTest.m +++ b/Foundation/GTMNSString+XMLTest.m @@ -1,5 +1,5 @@ // -// NSString+XMLTest.m +// GTMNSString+XMLTest.m // // Copyright 2007-2008 Google Inc. // diff --git a/Foundation/GTMObjC2Runtime.h b/Foundation/GTMObjC2Runtime.h index ab34cfb..f94e680 100644 --- a/Foundation/GTMObjC2Runtime.h +++ b/Foundation/GTMObjC2Runtime.h @@ -50,17 +50,25 @@ #if MAC_OS_X_VERSION_MIN_REQUIRED < 1050 #import "objc/Protocol.h" -Class object_getClass(id obj); -const char *class_getName(Class cls); -BOOL class_conformsToProtocol(Class cls, Protocol *protocol); -Class class_getSuperclass(Class cls); -Method *class_copyMethodList(Class cls, unsigned int *outCount); -SEL method_getName(Method m); -void method_exchangeImplementations(Method m1, Method m2); -IMP method_getImplementation(Method method); -IMP method_setImplementation(Method method, IMP imp); -struct objc_method_description protocol_getMethodDescription(Protocol *p, - SEL aSel, - BOOL isRequiredMethod, - BOOL isInstanceMethod); +#ifdef __cplusplus +extern "C" { +#endif + +OBJC_EXPORT Class object_getClass(id obj); +OBJC_EXPORT const char *class_getName(Class cls); +OBJC_EXPORT BOOL class_conformsToProtocol(Class cls, Protocol *protocol); +OBJC_EXPORT Class class_getSuperclass(Class cls); +OBJC_EXPORT Method *class_copyMethodList(Class cls, unsigned int *outCount); +OBJC_EXPORT SEL method_getName(Method m); +OBJC_EXPORT void method_exchangeImplementations(Method m1, Method m2); +OBJC_EXPORT IMP method_getImplementation(Method method); +OBJC_EXPORT IMP method_setImplementation(Method method, IMP imp); +OBJC_EXPORT struct objc_method_description protocol_getMethodDescription(Protocol *p, + SEL aSel, + BOOL isRequiredMethod, + BOOL isInstanceMethod); +#ifdef __cplusplus +} +#endif + #endif // OBJC2_UNAVAILABLE diff --git a/Foundation/GTMProgressMonitorInputStream.m b/Foundation/GTMProgressMonitorInputStream.m index 2336268..94212dd 100644 --- a/Foundation/GTMProgressMonitorInputStream.m +++ b/Foundation/GTMProgressMonitorInputStream.m @@ -59,13 +59,40 @@ inputStream_ = [input retain]; dataSize_ = length; + + if (!inputStream_) { + [self release]; + self = nil; + } } return self; } +#pragma mark - + - (id)init { - _GTMDevAssert(NO, @"should call initWithStream:length:"); - return nil; + return [self initWithStream:nil length:0]; +} + +- (id)initWithData:(NSData *)data { + unsigned long long dataLength = [data length]; + NSInputStream *inputStream = nil; + if (data) { + inputStream = [NSInputStream inputStreamWithData:data]; + } + return [self initWithStream:inputStream length:dataLength]; +} + +- (id)initWithFileAtPath:(NSString *)path { + NSDictionary *fileAttrs = + [[NSFileManager defaultManager] fileAttributesAtPath:path + traverseLink:YES]; + unsigned long long fileSize = [fileAttrs fileSize]; + NSInputStream *inputStream = nil; + if (fileSize) { + inputStream = [NSInputStream inputStreamWithFileAtPath:path]; + } + return [self initWithStream:inputStream length:fileSize]; } - (void)dealloc { @@ -75,7 +102,6 @@ #pragma mark - - - (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len { NSInteger numRead = [inputStream_ read:buffer maxLength:len]; @@ -103,6 +129,9 @@ } - (BOOL)getBuffer:(uint8_t **)buffer length:(NSUInteger *)len { + // TODO: doesn't this advance the stream so we should warn the progress + // callback? NSInputStream w/ a file and NSData both seem to return NO for + // this so I'm not sure how to test it. return [inputStream_ getBuffer:buffer length:len]; } @@ -135,6 +164,7 @@ - (id)propertyForKey:(NSString *)key { return [inputStream_ propertyForKey:key]; } + - (BOOL)setProperty:(id)property forKey:(NSString *)key { return [inputStream_ setProperty:property forKey:key]; } diff --git a/Foundation/GTMProgressMonitorInputStreamTest.m b/Foundation/GTMProgressMonitorInputStreamTest.m new file mode 100644 index 0000000..1d0d1aa --- /dev/null +++ b/Foundation/GTMProgressMonitorInputStreamTest.m @@ -0,0 +1,257 @@ +// +// GTMProgressMonitorInputStreamTest.m +// +// 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 +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMSenTestCase.h" +#import "GTMProgressMonitorInputStream.h" +#import "GTMUnitTestDevLog.h" + + +@interface GTMProgressMonitorInputStreamTest : GTMTestCase +@end + +@interface TestStreamMonitor : NSObject { + @private + NSMutableArray *reportedDeliverySizesArray_; + NSMutableSet *reportedTotalSizesSet_; +} +- (NSArray *)reportedSizes; +- (NSSet *)reportedTotals; +- (void)inputStream:(GTMProgressMonitorInputStream *)stream + hasDeliveredBytes:(unsigned long long)numRead + ofTotalBytes:(unsigned long long)total; +@end + +@implementation GTMProgressMonitorInputStreamTest + +static const unsigned long long kSourceDataByteCount = (10000*10); + +- (void)testInit { + + // bad inputs + + // init + STAssertNil([[GTMProgressMonitorInputStream alloc] init], nil); + STAssertNil([[GTMProgressMonitorInputStream alloc] initWithStream:nil length:0], nil); + STAssertNil([[GTMProgressMonitorInputStream alloc] initWithData:nil], nil); + STAssertNil([[GTMProgressMonitorInputStream alloc] initWithFileAtPath:nil], nil); + + // class helpers + STAssertNil([GTMProgressMonitorInputStream inputStreamWithStream:nil length:0], nil); + STAssertNil([GTMProgressMonitorInputStream inputStreamWithData:nil], nil); + STAssertNil([GTMProgressMonitorInputStream inputStreamWithFileAtPath:nil], nil); + + // some data for next round + NSData *data = [@"some data" dataUsingEncoding:NSUTF8StringEncoding]; + STAssertNotNil(data, nil); + GTMProgressMonitorInputStream *monStream; + + // good inputs + + NSInputStream *inputStream = [NSInputStream inputStreamWithData:data]; + STAssertNotNil(inputStream, nil); + monStream = + [GTMProgressMonitorInputStream inputStreamWithStream:inputStream + length:[data length]]; + STAssertNotNil(monStream, nil); + + monStream = [GTMProgressMonitorInputStream inputStreamWithData:data]; + STAssertNotNil(monStream, nil); + + monStream = + [GTMProgressMonitorInputStream inputStreamWithFileAtPath:@"/etc/services"]; + STAssertNotNil(monStream, nil); + +} + +- (void)testMonitorAccessors { + + NSData *data = [@"some data" dataUsingEncoding:NSUTF8StringEncoding]; + STAssertNotNil(data, nil); + GTMProgressMonitorInputStream *monStream = + [GTMProgressMonitorInputStream inputStreamWithData:data]; + STAssertNotNil(monStream, nil); + + TestStreamMonitor *monitor = [[[TestStreamMonitor alloc] init] autorelease]; + STAssertNotNil(monitor, nil); + + SEL monSel = @selector(inputStream:hasDeliveredBytes:ofTotalBytes:); + [monStream setMonitorDelegate:monitor selector:monSel]; + STAssertEquals([monStream monitorDelegate], monitor, nil); + STAssertEquals([monStream monitorSelector], monSel, nil); + + [monStream setMonitorSource:data]; + STAssertEquals([monStream monitorSource], data, nil); +} + +- (void)testInputStreamAccessors { + + GTMProgressMonitorInputStream *monStream = + [GTMProgressMonitorInputStream inputStreamWithFileAtPath:@"/etc/services"]; + STAssertNotNil(monStream, nil); + + // delegate + + [monStream setDelegate:self]; + STAssertEquals([monStream delegate], self, nil); + [monStream setDelegate:nil]; + STAssertNil([monStream delegate], nil); + + // error (we get unknown error before we open things) + + NSError *err = [monStream streamError]; + STAssertEqualObjects([err domain], @"NSUnknownErrorDomain", nil); + + // status and properties + + // pre open + STAssertEquals([monStream streamStatus], + (NSStreamStatus)NSStreamStatusNotOpen, nil); + [monStream open]; + // post open + STAssertEquals([monStream streamStatus], + (NSStreamStatus)NSStreamStatusOpen, nil); + STAssertEqualObjects([monStream propertyForKey:NSStreamFileCurrentOffsetKey], + [NSNumber numberWithInt:0], nil); + // read some + uint8_t buf[8]; + long bytesRead = [monStream read:buf maxLength:sizeof(buf)]; + STAssertGreaterThanOrEqual(bytesRead, (long)sizeof(buf), nil); + // post read + STAssertEqualObjects([monStream propertyForKey:NSStreamFileCurrentOffsetKey], + [NSNumber numberWithLong:bytesRead], nil); + [monStream close]; + // post close + STAssertEquals([monStream streamStatus], + (NSStreamStatus)NSStreamStatusClosed, nil); + +} + +- (void)testProgressMessagesViaRead { + + // make a big data buffer (sourceData) + NSMutableData *sourceData = + [NSMutableData dataWithCapacity:kSourceDataByteCount]; + for (int idx = 0; idx < 10000; idx++) { + [sourceData appendBytes:"0123456789" length:10]; + } + STAssertEquals([sourceData length], (NSUInteger)kSourceDataByteCount, nil); + + // make a buffer to hold the data as read from the stream, and an array + // to hold the size of each read + NSMutableData *resultData = [NSMutableData data]; + NSMutableArray *deliverySizesArray = [NSMutableArray array]; + + TestStreamMonitor *monitor = [[[TestStreamMonitor alloc] init] autorelease]; + STAssertNotNil(monitor, nil); + + // create the stream; set self as the monitor + GTMProgressMonitorInputStream* monStream = + [GTMProgressMonitorInputStream inputStreamWithData:sourceData]; + [monStream setMonitorDelegate:monitor + selector:@selector(inputStream:hasDeliveredBytes:ofTotalBytes:)]; + [monStream open]; + + // we'll read random-sized chunks of data from our stream, adding the chunk + // size to deliverySizesArray and the data itself to resultData + srandomdev(); + + NSUInteger bytesReadSoFar = 0; + uint8_t readBuffer[2048]; + while (1) { + NSStreamStatus status = [monStream streamStatus]; + if (bytesReadSoFar < kSourceDataByteCount) { + STAssertTrue([monStream hasBytesAvailable], nil); + STAssertEquals(status, (NSStreamStatus)NSStreamStatusOpen, nil); + } else { + STAssertFalse([monStream hasBytesAvailable], nil); + STAssertEquals(status, (NSStreamStatus)NSStreamStatusAtEnd, nil); + } + + // read a random block size between 1 and 2048 bytes + NSUInteger bytesToRead = (random() % sizeof(readBuffer)) + 1; + NSInteger bytesRead = [monStream read:readBuffer maxLength:bytesToRead]; + + // done? + if (bytesRead <= 0) { + break; + } + + // save the data we just read, and the size of the read + [resultData appendBytes:readBuffer length:bytesRead]; + bytesReadSoFar += bytesRead; + NSNumber *bytesReadSoFarNumber = + [NSNumber numberWithUnsignedLongLong:(unsigned long long)bytesReadSoFar]; + [deliverySizesArray addObject:bytesReadSoFarNumber]; + } + + [monStream close]; + + // compare deliverySizesArray to the array built by our callback, and + // resultData to the sourceData + STAssertEqualObjects(deliverySizesArray, [monitor reportedSizes], + @"unexpected size deliveries"); + NSNumber *sourceNumber = + [NSNumber numberWithUnsignedLongLong:kSourceDataByteCount]; + STAssertEqualObjects([NSSet setWithObject:sourceNumber], + [monitor reportedTotals], + @"unexpected total sizes"); + + // STAssertEqualObjects on the actual NSDatas is hanging when they are unequal + // here so I'll just assert True + STAssertTrue([sourceData isEqualToData:resultData], + @"unexpected data read"); +} + +@end + +@implementation TestStreamMonitor + +- (id)init { + self = [super init]; + if (self) { + reportedDeliverySizesArray_ = [[NSMutableArray alloc] init]; + reportedTotalSizesSet_ = [[NSMutableSet alloc] init]; + } + return self; +} + +- (void) dealloc { + [reportedDeliverySizesArray_ release]; + [reportedTotalSizesSet_ release]; + [super dealloc]; +} + +- (NSArray *)reportedSizes { + return reportedDeliverySizesArray_; +} + +- (NSSet *)reportedTotals { + return reportedTotalSizesSet_; +} + +- (void)inputStream:(GTMProgressMonitorInputStream *)stream + hasDeliveredBytes:(unsigned long long)numRead + ofTotalBytes:(unsigned long long)total { + // add the number read so far to the array + [reportedDeliverySizesArray_ addObject: + [NSNumber numberWithUnsignedLongLong:numRead]]; + [reportedTotalSizesSet_ addObject: + [NSNumber numberWithUnsignedLongLong:total]]; +} + +@end diff --git a/Foundation/GTMRegex.m b/Foundation/GTMRegex.m index fb6e3a0..c142c62 100644 --- a/Foundation/GTMRegex.m +++ b/Foundation/GTMRegex.m @@ -276,7 +276,7 @@ static NSString *const kReplacementPattern = } result = buildResult; - } // COV_NF_LINE - radar 5851992 not all brackets reachable w/ obj-c exceptions and coverage + } // COV_NF_LINE - radar 5851992 only reachable w/ an uncaught exception which isn't testable @finally { free(regMatches); } @@ -649,8 +649,7 @@ static NSString *const kReplacementPattern = isMatch:isMatch] autorelease]; nextMatches = nil; } - } // COV_NF_START - no real way to force this in a test - @catch (id e) { + } @catch (id e) { // COV_NF_START - no real way to force this in a test _GTMDevLog(@"Exceptions while trying to advance enumeration (%@)", e); // if we still have something in our temp, free it if (nextMatches) diff --git a/Foundation/GTMScriptRunnerTest.m b/Foundation/GTMScriptRunnerTest.m index 45378bc..9545045 100644 --- a/Foundation/GTMScriptRunnerTest.m +++ b/Foundation/GTMScriptRunnerTest.m @@ -207,7 +207,13 @@ STAssertEquals([output intValue], numVars, @"should be back down to %d vars", numVars); - NSDictionary *currVars = [[NSProcessInfo processInfo] environment]; + NSMutableDictionary *currVars + = [[[[NSProcessInfo processInfo] environment] mutableCopy] autorelease]; + + // When debugging a release build _ was not in the processInfo environment + // causing the assert below to fail. Not sure why, but it appeared + // to be harmless, and easy to account for. + [currVars setObject:@"/usr/bin/env" forKey:@"_"]; [sr setEnvironment:currVars]; output = [sr run:@"/usr/bin/env | wc -l"]; diff --git a/Foundation/GTMStackTrace.c b/Foundation/GTMStackTrace.c deleted file mode 100644 index 68e5c7d..0000000 --- a/Foundation/GTMStackTrace.c +++ /dev/null @@ -1,92 +0,0 @@ -// -// GTMStackTrace.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. -// - -#include <stdlib.h> -#include <dlfcn.h> -#include <mach-o/nlist.h> -#include "GTMStackTrace.h" - -// Structure representing a small portion of a stack, starting from the saved -// frame pointer, and continuing through the saved program counter. -struct StackFrame { - void *saved_fp; -#if defined (__ppc__) || defined(__ppc64__) - void *padding; -#endif - void *saved_pc; -}; - -// __builtin_frame_address(0) is a gcc builtin that returns a pointer to the -// current frame pointer. We then use the frame pointer to walk the stack -// picking off program counters and other saved frame pointers. This works -// great on i386, but PPC requires a little more work because the PC (or link -// register) isn't always stored on the stack. -// -int GTMGetStackProgramCounters(void *outPcs[], int size) { - if (!outPcs || (size < 1)) return 0; - - struct StackFrame *fp; -#if defined (__ppc__) || defined(__ppc64__) - outPcs[0] = __builtin_return_address(0); - fp = (struct StackFrame *)__builtin_frame_address(1); -#elif defined (__i386__) || defined(__x86_64__) - fp = (struct StackFrame *)__builtin_frame_address(0); -#else -#error architecture not supported -#endif - - int level = 0; - while (level < size) { - if (fp == NULL) { - level--; - break; - } - outPcs[level] = fp->saved_pc; - level++; - fp = (struct StackFrame *)fp->saved_fp; - } - - return level; -} - -CFStringRef GTMStackTraceCreate(void) { - // The maximum number of stack frames that we will walk. We limit this so - // that super-duper recursive functions (or bugs) don't send us for an - // infinite loop. - static const int kMaxStackTraceDepth = 100; - void *pcs[kMaxStackTraceDepth]; - int depth = kMaxStackTraceDepth; - depth = GTMGetStackProgramCounters(pcs, depth); - - CFMutableStringRef trace = CFStringCreateMutable(kCFAllocatorDefault, 0); - - for (int i = 0; i < depth; i++) { - Dl_info info = { NULL, NULL, NULL, NULL }; - dladdr(pcs[i], &info); - const char *symbol = info.dli_sname; - const char *fname = info.dli_fname; - - CFStringAppendFormat(trace, NULL, - CFSTR("#%-2d 0x%08lx %s () [%s]\n"), - i, pcs[i], - (symbol ? symbol : "??"), - (fname ? fname : "??")); - } - - return trace; -} diff --git a/Foundation/GTMStackTrace.h b/Foundation/GTMStackTrace.h index cfb9c95..a77ca93 100644 --- a/Foundation/GTMStackTrace.h +++ b/Foundation/GTMStackTrace.h @@ -22,20 +22,20 @@ extern "C" { #endif -/// Returns a string containing a nicely formatted stack trace. -// The caller owns the returned CFStringRef and is responsible for releasing it. -// -// ***************************************************************************** -// The symbolic names returned for Objective-C methods will be INCORRECT. This -// is because dladdr() doesn't properly resolve Objective-C names. The symbol's -// address will be CORRECT, so will be able to use atos or gdb to get a properly -// resolved Objective-C name. -- 5/15/2007 -// TODO: write dladdr() replacement that works with Objective-C symbols. -// ***************************************************************************** +struct GTMAddressDescriptor { + const void *address; // address + const char *symbol; // nearest symbol to address + const char *class_name; // if it is an obj-c method, the method's class + BOOL is_class_method; // if it is an obj-c method, type of method + const char *filename; // file that the method came from. +}; + +// Returns a string containing a nicely formatted stack trace. // -// This function gets the stack trace for the current thread, and is safe to -// use in production multi-threaded code. Typically this function will be used -// along with some loggins, as in the following: +// This function gets the stack trace for the current thread. It will +// be from the caller of GTMStackTrace upwards to the top the calling stack. +// Typically this function will be used along with some logging, +// as in the following: // // MyAppLogger(@"Should never get here:\n%@", GTMStackTrace()); // @@ -49,34 +49,43 @@ extern "C" { // #5 0x00002692 tart () [/Users/me/./StackLog] // #6 0x000025b9 tart () [/Users/me/./StackLog] // -// If you're using this with Objective-C, you may want to use the GTMStackTrace() -// variant that autoreleases the returned string. -// -CFStringRef GTMStackTraceCreate(void); - -/// Wrapper that autoreleases the returned CFStringRef. -// This is simply for the convenience of those using Objective-C. -// -#if __OBJC__ -#include "GTMGarbageCollection.h" -#define GTMStackTrace() [GTMNSMakeCollectable(GTMStackTraceCreate()) autorelease] -#endif -/// Returns an array of program counters from the current thread's stack. +NSString *GTMStackTrace(void); + +// Returns an array of program counters from the current thread's stack. // *** You should probably use GTMStackTrace() instead of this function *** // However, if you actually want all the PCs in "void *" form, then this -// funtion is more convenient. +// funtion is more convenient. This will include PCs of GTMStaceTrace and +// its inner utility functions that you may want to strip out. // // Args: // outPcs - an array of "void *" pointers to the program counters found on the // current thread's stack. -// size - the size of outPcs +// count - the number of entries in the outPcs array // // Returns: // The number of program counters actually added to outPcs. // -int GTMGetStackProgramCounters(void *outPcs[], int size); +int GTMGetStackProgramCounters(void *outPcs[], int count); +// Returns an array of GTMAddressDescriptors from the current thread's stack. +// *** You should probably use GTMStackTrace() instead of this function *** +// However, if you actually want all the PCs with symbols, this is the way +// to get them. There is no memory allocations done, so no clean up is required +// except for the caller to free outDescs if they allocated it themselves. +// This will include PCs of GTMStaceTrace and its inner utility functions that +// you may want to strip out. +// +// Args: +// outDescs - an array of "struct GTMAddressDescriptor" pointers corresponding +// to the program counters found on the current thread's stack. +// count - the number of entries in the outDescs array +// +// Returns: +// The number of program counters actually added to outPcs. +// +int GTMGetStackAddressDescriptors(struct GTMAddressDescriptor outDescs[], + int count); #ifdef __cplusplus } #endif diff --git a/Foundation/GTMStackTrace.m b/Foundation/GTMStackTrace.m new file mode 100644 index 0000000..5dc1c0b --- /dev/null +++ b/Foundation/GTMStackTrace.m @@ -0,0 +1,228 @@ +// +// GTMStackTrace.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. +// + +#include <stdlib.h> +#include <dlfcn.h> +#include <mach-o/nlist.h> +#include "GTMStackTrace.h" +#include "GTMObjC2Runtime.h" + +// Structure representing a small portion of a stack, starting from the saved +// frame pointer, and continuing through the saved program counter. +struct GTMStackFrame { + void *saved_fp; +#if defined (__ppc__) || defined(__ppc64__) + void *padding; +#endif + void *saved_pc; +}; + +struct GTMClassDescription { + const char *class_name; + Method *class_methods; + unsigned int class_method_count; + Method *instance_methods; + unsigned int instance_method_count; +}; + +#pragma mark Private utility functions + +static struct GTMClassDescription *GTMClassDescriptions(int *total_count) { + int class_count = objc_getClassList(nil, 0); + struct GTMClassDescription *class_descs + = calloc(class_count, sizeof(struct GTMClassDescription)); + if (class_descs) { + Class *classes = calloc(class_count, sizeof(Class)); + if (classes) { + objc_getClassList(classes, class_count); + for (int i = 0; i < class_count; ++i) { + class_descs[i].class_methods + = class_copyMethodList(object_getClass(classes[i]), + &class_descs[i].class_method_count); + class_descs[i].instance_methods + = class_copyMethodList(classes[i], + &class_descs[i].instance_method_count); + class_descs[i].class_name = class_getName(classes[i]); + } + free(classes); + } else { + free(class_descs); + class_count = 0; + } + } + if (total_count) { + *total_count = class_count; + } + return class_descs; +} + +static void GTMFreeClassDescriptions(struct GTMClassDescription *class_descs, + int count) { + if (!class_descs || count < 0) return; + for (int i = 0; i < count; ++i) { + if (class_descs[i].instance_methods) { + free(class_descs[i].instance_methods); + } + if (class_descs[i].class_methods) { + free(class_descs[i].class_methods); + } + } + free(class_descs); +} + +#pragma mark Public functions + +// __builtin_frame_address(0) is a gcc builtin that returns a pointer to the +// current frame pointer. We then use the frame pointer to walk the stack +// picking off program counters and other saved frame pointers. This works +// great on i386, but PPC requires a little more work because the PC (or link +// register) isn't always stored on the stack. +// +int GTMGetStackProgramCounters(void *outPcs[], int count) { + if (!outPcs || (count < 1)) return 0; + + struct GTMStackFrame *fp; +#if defined (__ppc__) || defined(__ppc64__) + outPcs[0] = __builtin_return_address(0); + fp = (struct GTMStackFrame *)__builtin_frame_address(1); +#elif defined (__i386__) || defined(__x86_64__) + fp = (struct GTMStackFrame *)__builtin_frame_address(0); +#else +#error architecture not supported +#endif + + int level = 0; + while (level < count) { + if (fp == NULL) { + level--; + break; + } + outPcs[level] = fp->saved_pc; + level++; + fp = (struct GTMStackFrame *)fp->saved_fp; + } + + return level; +} + +int GTMGetStackAddressDescriptors(struct GTMAddressDescriptor outDescs[], + int count) { + if (count < 1) return 0; + + void **pcs = calloc(count, sizeof(void*)); + if (!pcs) return 0; + + int newSize = GTMGetStackProgramCounters(pcs, count); + int class_desc_count; + + // Get our obj-c class descriptions. This is expensive, so we do it once + // at the top. We go through this because dladdr doesn't work with + // obj methods. + struct GTMClassDescription *class_descs + = GTMClassDescriptions(&class_desc_count); + + // Iterate through the stack. + for (int i = 0; i < newSize; ++i) { + const char *class_name = NULL; + Boolean is_class_method = FALSE; + size_t smallest_diff = SIZE_MAX; + struct GTMAddressDescriptor *currDesc = &outDescs[i]; + currDesc->address = pcs[i]; + Method best_method = NULL; + // Iterate through all the classes we know of. + for (int j = 0; j < class_desc_count; ++j) { + // First check the class methods. + for (unsigned int k = 0; k < class_descs[j].class_method_count; ++k) { + IMP imp = method_getImplementation(class_descs[j].class_methods[k]); + if (imp <= (IMP)currDesc->address) { + size_t diff = (size_t)currDesc->address - (size_t)imp; + if (diff < smallest_diff) { + best_method = class_descs[j].class_methods[k]; + class_name = class_descs[j].class_name; + is_class_method = TRUE; + smallest_diff = diff; + } + } + } + // Then check the instance methods. + for (unsigned int k = 0; k < class_descs[j].instance_method_count; ++k) { + IMP imp = method_getImplementation(class_descs[j].instance_methods[k]); + if (imp <= (IMP)currDesc->address) { + size_t diff = (size_t)currDesc->address - (size_t)imp; + if (diff < smallest_diff) { + best_method = class_descs[j].instance_methods[k]; + class_name = class_descs[j].class_name; + is_class_method = TRUE; + smallest_diff = diff; + } + } + } + } + + // If we have one, store it off. + if (best_method) { + currDesc->symbol = sel_getName(method_getName(best_method)); + currDesc->is_class_method = is_class_method; + currDesc->class_name = class_name; + } + Dl_info info = { NULL, NULL, NULL, NULL }; + + // Check to see if the one returned by dladdr is better. + dladdr(currDesc->address, &info); + if ((size_t)currDesc->address - (size_t)info.dli_saddr < smallest_diff) { + currDesc->symbol = info.dli_sname; + currDesc->is_class_method = FALSE; + currDesc->class_name = NULL; + } + currDesc->filename = info.dli_fname; + } + GTMFreeClassDescriptions(class_descs, class_desc_count); + return newSize; +} + +NSString *GTMStackTrace(void) { + // The maximum number of stack frames that we will walk. We limit this so + // that super-duper recursive functions (or bugs) don't send us for an + // infinite loop. + const int kMaxStackTraceDepth = 100; + struct GTMAddressDescriptor descs[kMaxStackTraceDepth]; + int depth = kMaxStackTraceDepth; + depth = GTMGetStackAddressDescriptors(descs, depth); + + NSMutableString *trace = [NSMutableString string]; + + // Start at the second item so that GTMStackTrace and it's utility calls (of + // which there is currently 1) is not included in the output. + const int kTracesToStrip = 2; + for (int i = kTracesToStrip; i < depth; i++) { + if (descs[i].class_name) { + [trace appendFormat:@"#%-2d 0x%08lx %s[%s %s] (%s)\n", + i - kTracesToStrip, descs[i].address, + (descs[i].is_class_method ? "+" : "-"), + descs[i].class_name, + (descs[i].symbol ? descs[i].symbol : "??"), + (descs[i].filename ? descs[i].filename : "??")]; + } else { + [trace appendFormat:@"#%-2d 0x%08lx %s() (%s)\n", + i - kTracesToStrip, descs[i].address, + (descs[i].symbol ? descs[i].symbol : "??"), + (descs[i].filename ? descs[i].filename : "??")]; + } + } + return trace; +} diff --git a/Foundation/GTMStackTraceTest.m b/Foundation/GTMStackTraceTest.m index edcb851..dd92fe9 100644 --- a/Foundation/GTMStackTraceTest.m +++ b/Foundation/GTMStackTraceTest.m @@ -35,9 +35,13 @@ @"stack trace must have < 25 lines"); NSString *firstFrame = [stacklines objectAtIndex:0]; - NSRange range = [firstFrame rangeOfString:@"GTMStackTrace"]; + NSRange range = [firstFrame rangeOfString:@"testStackTraceBasic"]; STAssertNotEquals(range.location, (NSUInteger)NSNotFound, - @"First frame should contain GTMStackTrace, stack trace: %@", + @"First frame should contain testStackTraceBasic," + " stack trace: %@", stacktrace); + range = [firstFrame rangeOfString:@"#0"]; + STAssertNotEquals(range.location, (NSUInteger)NSNotFound, + @"First frame should contain #0, stack trace: %@", stacktrace); } diff --git a/GTM.xcodeproj/project.pbxproj b/GTM.xcodeproj/project.pbxproj index 84e068a..94623d9 100644 --- a/GTM.xcodeproj/project.pbxproj +++ b/GTM.xcodeproj/project.pbxproj @@ -49,9 +49,6 @@ 8B1801B20E25341B00280961 /* GTMLargeTypeWindowLongTextTest.gtmUTState in Resources */ = {isa = PBXBuildFile; fileRef = 8B1801AC0E25341B00280961 /* GTMLargeTypeWindowLongTextTest.gtmUTState */; }; 8B1801B30E25341B00280961 /* GTMLargeTypeWindowShortTextTest.gtmUTState in Resources */ = {isa = PBXBuildFile; fileRef = 8B1801AD0E25341B00280961 /* GTMLargeTypeWindowShortTextTest.gtmUTState */; }; 8B1802420E25592200280961 /* GTMLargeTypeWindowMediumTextTest.gtmUTState in Resources */ = {isa = PBXBuildFile; fileRef = 8B1802410E25592200280961 /* GTMLargeTypeWindowMediumTextTest.gtmUTState */; }; - 8B2C21B50E00883F00B5ECB1 /* GTMObjC2Runtime.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F32040DA34A1B0052CA40 /* GTMObjC2Runtime.m */; }; - 8B2C21B60E00884000B5ECB1 /* GTMObjC2Runtime.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F32040DA34A1B0052CA40 /* GTMObjC2Runtime.m */; }; - 8B2C21B70E00885600B5ECB1 /* GTMObjC2Runtime.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F32040DA34A1B0052CA40 /* GTMObjC2Runtime.m */; }; 8B3344210DBF7A36009FD32C /* GTMNSAppleScript+HandlerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3344170DBF7A36009FD32C /* GTMNSAppleScript+HandlerTest.m */; }; 8B3344230DBF7A36009FD32C /* GTMNSAppleEventDescriptor+HandlerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B33441A0DBF7A36009FD32C /* GTMNSAppleEventDescriptor+HandlerTest.m */; }; 8B3344250DBF7A36009FD32C /* GTMNSAppleEventDescriptor+FoundationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B33441D0DBF7A36009FD32C /* GTMNSAppleEventDescriptor+FoundationTest.m */; }; @@ -67,7 +64,6 @@ 8B45A19A0DA46AAA001148C5 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B45A1990DA46AAA001148C5 /* QuartzCore.framework */; }; 8B45A2040DA46DF6001148C5 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; }; 8B45A21A0DA46E1D001148C5 /* GTMGeometryUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE27F0D198D0E009257D2 /* GTMGeometryUtils.m */; }; - 8B45A21D0DA46E2C001148C5 /* GTMObjC2Runtime.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F32040DA34A1B0052CA40 /* GTMObjC2Runtime.m */; }; 8B45A21E0DA46E34001148C5 /* GTMObjC2Runtime.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F32040DA34A1B0052CA40 /* GTMObjC2Runtime.m */; }; 8B45A22C0DA46E51001148C5 /* GTMSystemVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE2930D198D24009257D2 /* GTMSystemVersion.m */; }; 8B45A2AA0DA49C47001148C5 /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = 8B45A2A50DA49C47001148C5 /* MainMenu.nib */; }; @@ -79,8 +75,11 @@ 8B45A5F80DA5EB9F001148C5 /* GTMUnitTestingWindow.gtmUTState in Resources */ = {isa = PBXBuildFile; fileRef = 8B45A5F50DA5EB9F001148C5 /* GTMUnitTestingWindow.gtmUTState */; }; 8B45A5F90DA5EB9F001148C5 /* GTMUnitTestingTestApp.gtmUTState in Resources */ = {isa = PBXBuildFile; fileRef = 8B45A5F60DA5EB9F001148C5 /* GTMUnitTestingTestApp.gtmUTState */; }; 8B45A6BB0DA67DD5001148C5 /* GTMUnitTestingImage.gtmUTState in Resources */ = {isa = PBXBuildFile; fileRef = 8B45A6B90DA67DD5001148C5 /* GTMUnitTestingImage.gtmUTState */; }; + 8B4D78080E40AFFA00EFEDD8 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B45A1990DA46AAA001148C5 /* QuartzCore.framework */; }; + 8B4D7A150E40D79200EFEDD8 /* GTMObjC2Runtime.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F32040DA34A1B0052CA40 /* GTMObjC2Runtime.m */; }; 8B55479C0DB3B7A50014CC1C /* GTMAppKit+UnitTesting.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B55479B0DB3B7A50014CC1C /* GTMAppKit+UnitTesting.m */; }; 8B5547B90DB3BB220014CC1C /* GTMAppKit+UnitTesting.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B55479B0DB3B7A50014CC1C /* GTMAppKit+UnitTesting.m */; }; + 8B61FDC00E4CDB8000FF9C21 /* GTMStackTrace.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B61FDBF0E4CDB8000FF9C21 /* GTMStackTrace.m */; }; 8B6F32080DA34A1B0052CA40 /* GTMObjC2RuntimeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F32050DA34A1B0052CA40 /* GTMObjC2RuntimeTest.m */; }; 8B6F32160DA34C830052CA40 /* GTMMethodCheckTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F31F10DA347720052CA40 /* GTMMethodCheckTest.m */; }; 8B7AD49A0DABBB5800B84F4A /* GTMNSBezierPath+ShadingTest.10.5.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 8B7AD4970DABBB5800B84F4A /* GTMNSBezierPath+ShadingTest.10.5.tiff */; }; @@ -135,6 +134,7 @@ F42597790E23FE3A003BEA3E /* GTMNSString+FindFolder.h in Headers */ = {isa = PBXBuildFile; fileRef = F42597760E23FE3A003BEA3E /* GTMNSString+FindFolder.h */; settings = {ATTRIBUTES = (Public, ); }; }; F425977A0E23FE3A003BEA3E /* GTMNSString+FindFolder.m in Sources */ = {isa = PBXBuildFile; fileRef = F42597770E23FE3A003BEA3E /* GTMNSString+FindFolder.m */; }; F425977F0E23FE43003BEA3E /* GTMNSString+FindFolderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F42597780E23FE3A003BEA3E /* GTMNSString+FindFolderTest.m */; }; + F427EFC40E4023DD00ADD2AA /* GTMProgressMonitorInputStreamTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F427EFC30E4023DD00ADD2AA /* GTMProgressMonitorInputStreamTest.m */; }; F428FF030D48E55E00382ED1 /* GTMNSBezierPath+CGPath.h in Headers */ = {isa = PBXBuildFile; fileRef = F428FEFF0D48E55E00382ED1 /* GTMNSBezierPath+CGPath.h */; settings = {ATTRIBUTES = (Public, ); }; }; F428FF040D48E55E00382ED1 /* GTMNSBezierPath+CGPath.m in Sources */ = {isa = PBXBuildFile; fileRef = F428FF000D48E55E00382ED1 /* GTMNSBezierPath+CGPath.m */; }; F428FF090D48E57300382ED1 /* GTMNSBezierPath+CGPathTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F428FF010D48E55E00382ED1 /* GTMNSBezierPath+CGPathTest.m */; }; @@ -162,7 +162,6 @@ F42E09550D199BBF00D5DDE0 /* GTMSystemVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE2930D198D24009257D2 /* GTMSystemVersion.m */; }; F42E095E0D199BD600D5DDE0 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; }; F42E09AE0D19A62F00D5DDE0 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F42E09AD0D19A62F00D5DDE0 /* Carbon.framework */; }; - F431221C0DD4E3B800F45252 /* GTMStackTrace.c in Sources */ = {isa = PBXBuildFile; fileRef = F43122190DD4E3B800F45252 /* GTMStackTrace.c */; }; F431221D0DD4E3B800F45252 /* GTMStackTrace.h in Headers */ = {isa = PBXBuildFile; fileRef = F431221A0DD4E3B800F45252 /* GTMStackTrace.h */; settings = {ATTRIBUTES = (Public, ); }; }; F431221F0DD4E3C900F45252 /* GTMStackTraceTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F431221B0DD4E3B800F45252 /* GTMStackTraceTest.m */; }; F435DE7C0DC0B6580069CDE8 /* GTMNSBezierPath+CGPathTest.ppc64.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F435DE7A0DC0B6580069CDE8 /* GTMNSBezierPath+CGPathTest.ppc64.tiff */; }; @@ -330,6 +329,7 @@ 8B45A6B90DA67DD5001148C5 /* GTMUnitTestingImage.gtmUTState */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = GTMUnitTestingImage.gtmUTState; sourceTree = "<group>"; }; 8B55479A0DB3B7A50014CC1C /* GTMAppKit+UnitTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMAppKit+UnitTesting.h"; sourceTree = "<group>"; }; 8B55479B0DB3B7A50014CC1C /* GTMAppKit+UnitTesting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMAppKit+UnitTesting.m"; sourceTree = "<group>"; }; + 8B61FDBF0E4CDB8000FF9C21 /* GTMStackTrace.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMStackTrace.m; sourceTree = "<group>"; }; 8B6F31EF0DA347720052CA40 /* GTMMethodCheck.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMMethodCheck.m; sourceTree = "<group>"; }; 8B6F31F10DA347720052CA40 /* GTMMethodCheckTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMMethodCheckTest.m; sourceTree = "<group>"; }; 8B6F31F40DA3489B0052CA40 /* GTMMethodCheck.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMMethodCheck.h; sourceTree = "<group>"; }; @@ -370,6 +370,7 @@ F42597760E23FE3A003BEA3E /* GTMNSString+FindFolder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSString+FindFolder.h"; sourceTree = "<group>"; }; F42597770E23FE3A003BEA3E /* GTMNSString+FindFolder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+FindFolder.m"; sourceTree = "<group>"; }; F42597780E23FE3A003BEA3E /* GTMNSString+FindFolderTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+FindFolderTest.m"; sourceTree = "<group>"; }; + F427EFC30E4023DD00ADD2AA /* GTMProgressMonitorInputStreamTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMProgressMonitorInputStreamTest.m; sourceTree = "<group>"; }; F428FEFF0D48E55E00382ED1 /* GTMNSBezierPath+CGPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSBezierPath+CGPath.h"; sourceTree = "<group>"; }; F428FF000D48E55E00382ED1 /* GTMNSBezierPath+CGPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSBezierPath+CGPath.m"; sourceTree = "<group>"; }; F428FF010D48E55E00382ED1 /* GTMNSBezierPath+CGPathTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSBezierPath+CGPathTest.m"; sourceTree = "<group>"; }; @@ -378,7 +379,6 @@ F42E086E0D199A5B00D5DDE0 /* GTM-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GTM-Info.plist"; sourceTree = "<group>"; }; F42E089B0D199B1800D5DDE0 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; }; F42E09AD0D19A62F00D5DDE0 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = "<absolute>"; }; - F43122190DD4E3B800F45252 /* GTMStackTrace.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = GTMStackTrace.c; sourceTree = "<group>"; }; F431221A0DD4E3B800F45252 /* GTMStackTrace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMStackTrace.h; sourceTree = "<group>"; }; F431221B0DD4E3B800F45252 /* GTMStackTraceTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMStackTraceTest.m; sourceTree = "<group>"; }; F435DE7A0DC0B6580069CDE8 /* GTMNSBezierPath+CGPathTest.ppc64.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMNSBezierPath+CGPathTest.ppc64.tiff"; sourceTree = "<group>"; }; @@ -516,6 +516,7 @@ F42E095E0D199BD600D5DDE0 /* Cocoa.framework in Frameworks */, F42E09AE0D19A62F00D5DDE0 /* Carbon.framework in Frameworks */, F43E4F6D0D4E60C50041161F /* libz.dylib in Frameworks */, + 8B4D78080E40AFFA00EFEDD8 /* QuartzCore.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -754,6 +755,7 @@ F9FD945D0E1D30F80005867E /* GTMPathTest.m */, F435E27D0DC7B0630069CDE8 /* GTMProgressMonitorInputStream.h */, F435E27E0DC7B0630069CDE8 /* GTMProgressMonitorInputStream.m */, + F427EFC30E4023DD00ADD2AA /* GTMProgressMonitorInputStreamTest.m */, F437F55A0D50BC0A00F5C3A4 /* GTMRegex.h */, F437F55B0D50BC0A00F5C3A4 /* GTMRegex.m */, F437F55C0D50BC0A00F5C3A4 /* GTMRegexTest.m */, @@ -763,7 +765,7 @@ F41A6F7F0E02EC3600788A6C /* GTMSignalHandler.h */, F41A6F800E02EC3600788A6C /* GTMSignalHandler.m */, F41A6F810E02EC3600788A6C /* GTMSignalHandlerTest.m */, - F43122190DD4E3B800F45252 /* GTMStackTrace.c */, + 8B61FDBF0E4CDB8000FF9C21 /* GTMStackTrace.m */, F431221A0DD4E3B800F45252 /* GTMStackTrace.h */, F431221B0DD4E3B800F45252 /* GTMStackTraceTest.m */, F48FE2920D198D24009257D2 /* GTMSystemVersion.h */, @@ -1162,7 +1164,6 @@ 8B45A0D50DA46A57001148C5 /* GTMNSObject+UnitTesting.m in Sources */, 8B45A0D60DA46A57001148C5 /* GTMNSObject+BindingUnitTesting.m in Sources */, 8B45A21A0DA46E1D001148C5 /* GTMGeometryUtils.m in Sources */, - 8B45A21D0DA46E2C001148C5 /* GTMObjC2Runtime.m in Sources */, 8B45A22C0DA46E51001148C5 /* GTMSystemVersion.m in Sources */, 8B45A2E20DA51ABC001148C5 /* GTMUnitTestingTest.m in Sources */, 8B7AD4AE0DABBFEE00B84F4A /* GTMUnitTestingBindingTest.m in Sources */, @@ -1181,7 +1182,7 @@ 8B45A2D00DA51A01001148C5 /* GTMUnitTestingUtilities.m in Sources */, 8B7DCBC40DFF0F800017E983 /* GTMMethodCheck.m in Sources */, 8B7DCEF10E002C210017E983 /* GTMDevLogUnitTestingBridge.m in Sources */, - 8B2C21B70E00885600B5ECB1 /* GTMObjC2Runtime.m in Sources */, + 8B4D7A150E40D79200EFEDD8 /* GTMObjC2Runtime.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1214,7 +1215,6 @@ 8B7DCBC20DFF0F7F0017E983 /* GTMMethodCheck.m in Sources */, 8B7DCBEE0DFF1A4F0017E983 /* GTMUnitTestDevLog.m in Sources */, 8B7DCE1A0DFF39850017E983 /* GTMSenTestCase.m in Sources */, - 8B2C21B50E00883F00B5ECB1 /* GTMObjC2Runtime.m in Sources */, F41A6F850E02EC4D00788A6C /* GTMSignalHandlerTest.m in Sources */, 8B3AA9F80E033E5F007E31B5 /* GTMValidatingContainersTest.m in Sources */, 8B7E35750E048E2D00EF70C8 /* GTMHTTPFetcherTest.m in Sources */, @@ -1224,6 +1224,7 @@ F98680C40E2C164300CEE8BF /* GTMLoggerTest.m in Sources */, F98681960E2C20C100CEE8BF /* GTMLogger+ASLTest.m in Sources */, F95803FB0E2FB0A80049A088 /* GTMLoggerRingBufferWriterTest.m in Sources */, + F427EFC40E4023DD00ADD2AA /* GTMProgressMonitorInputStreamTest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1251,7 +1252,6 @@ F41D258C0DBD21A300774EEB /* GTMBase64.m in Sources */, F435E08A0DC63F6D0069CDE8 /* GTMHTTPFetcher.m in Sources */, F435E2800DC7B0630069CDE8 /* GTMProgressMonitorInputStream.m in Sources */, - F431221C0DD4E3B800F45252 /* GTMStackTrace.c in Sources */, 33C372A70DD8A88500E97817 /* GTMNSString+URLArguments.m in Sources */, 33C374390DD8D44800E97817 /* GTMNSDictionary+URLArguments.m in Sources */, F4BC1C890DDDD45D00108B7D /* GTMHTTPServer.m in Sources */, @@ -1270,6 +1270,7 @@ F98680C30E2C163D00CEE8BF /* GTMLogger.m in Sources */, F98681970E2C20C800CEE8BF /* GTMLogger+ASL.m in Sources */, F95803F90E2FB0850049A088 /* GTMLoggerRingBufferWriter.m in Sources */, + 8B61FDC00E4CDB8000FF9C21 /* GTMStackTrace.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1287,7 +1288,6 @@ 8B7DCBC10DFF0F7F0017E983 /* GTMMethodCheck.m in Sources */, 8B7DCBED0DFF1A4F0017E983 /* GTMUnitTestDevLog.m in Sources */, 8B7DCE190DFF39850017E983 /* GTMSenTestCase.m in Sources */, - 8B2C21B60E00884000B5ECB1 /* GTMObjC2Runtime.m in Sources */, 8B1801A50E2533DB00280961 /* GTMLargeTypeWindowTest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1711,6 +1711,7 @@ "$(value)", "$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)", ); + GCC_ENABLE_OBJC_GC = supported; INFOPLIST_FILE = "UnitTest-Info.plist"; PRODUCT_NAME = "UnitTest - AppKit"; }; @@ -1724,6 +1725,7 @@ "$(value)", "$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)", ); + GCC_ENABLE_OBJC_GC = supported; GTM_NO_DEBUG_FRAMEWORKS = YES; INFOPLIST_FILE = "UnitTest-Info.plist"; PRODUCT_NAME = "UnitTest - Foundation"; @@ -1738,6 +1740,7 @@ "$(value)", "$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)", ); + GCC_ENABLE_OBJC_GC = supported; INFOPLIST_FILE = "UnitTest-Info.plist"; PRODUCT_NAME = "UnitTest - UnitTesting"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GTMUIUnitTestingHarness.app/Contents/MacOS/GTMUIUnitTestingHarness"; @@ -1747,6 +1750,7 @@ F4CA86550DB3ACE500B4AB10 /* LeopardOrLater-Debug */ = { isa = XCBuildConfiguration; buildSettings = { + GCC_ENABLE_OBJC_GC = supported; INFOPLIST_FILE = UnitTesting/GTMUIUnitTestingHarness/Info.plist; PRODUCT_NAME = GTMUIUnitTestingHarness; }; @@ -1792,6 +1796,7 @@ "$(value)", "$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)", ); + GCC_ENABLE_OBJC_GC = supported; INFOPLIST_FILE = "UnitTest-Info.plist"; PRODUCT_NAME = "UnitTest - AppKit"; }; @@ -1805,6 +1810,7 @@ "$(value)", "$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)", ); + GCC_ENABLE_OBJC_GC = supported; GTM_NO_DEBUG_FRAMEWORKS = YES; INFOPLIST_FILE = "UnitTest-Info.plist"; PRODUCT_NAME = "UnitTest - Foundation"; @@ -1819,6 +1825,7 @@ "$(value)", "$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)", ); + GCC_ENABLE_OBJC_GC = supported; INFOPLIST_FILE = "UnitTest-Info.plist"; PRODUCT_NAME = "UnitTest - UnitTesting"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GTMUIUnitTestingHarness.app/Contents/MacOS/GTMUIUnitTestingHarness"; @@ -1828,6 +1835,7 @@ F4CA86600DB3AD0300B4AB10 /* LeopardOrLater-Debug-gcov */ = { isa = XCBuildConfiguration; buildSettings = { + GCC_ENABLE_OBJC_GC = supported; INFOPLIST_FILE = UnitTesting/GTMUIUnitTestingHarness/Info.plist; PRODUCT_NAME = GTMUIUnitTestingHarness; }; @@ -1870,6 +1878,7 @@ "$(value)", "$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)", ); + GCC_ENABLE_OBJC_GC = supported; INFOPLIST_FILE = "UnitTest-Info.plist"; PRODUCT_NAME = "UnitTest - AppKit"; }; @@ -1883,6 +1892,7 @@ "$(value)", "$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)", ); + GCC_ENABLE_OBJC_GC = supported; GTM_NO_DEBUG_FRAMEWORKS = YES; INFOPLIST_FILE = "UnitTest-Info.plist"; PRODUCT_NAME = "UnitTest - Foundation"; @@ -1897,6 +1907,7 @@ "$(value)", "$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)", ); + GCC_ENABLE_OBJC_GC = supported; INFOPLIST_FILE = "UnitTest-Info.plist"; PRODUCT_NAME = "UnitTest - UnitTesting"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GTMUIUnitTestingHarness.app/Contents/MacOS/GTMUIUnitTestingHarness"; @@ -1906,6 +1917,7 @@ F4CA86670DB3AD0D00B4AB10 /* LeopardOrLater-Release */ = { isa = XCBuildConfiguration; buildSettings = { + GCC_ENABLE_OBJC_GC = supported; INFOPLIST_FILE = UnitTesting/GTMUIUnitTestingHarness/Info.plist; PRODUCT_NAME = GTMUIUnitTestingHarness; }; diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index 3b5193b..c3bab2b 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -53,6 +53,30 @@ Changes since 1.5.1 GTMLoggerRingBufferWriter.h for what the basic system and two optional additions can do. +- Added GTMNSMakeUncollectable for forcing objects to survive in a GC world. + +- Added GTMCFAutorelease to make the [GTMNSMakeCollectable(cfFoo) autorelease] + simpler and clearer, it's now just GTMCFAutorelease(cfFoo), and works in + both GC and non-GC world. + +- Added GTMIsGarbageCollectionEnabled to GTMGarbageCollection.h. See the note + there for it's usage. + +- Disabled the unittests for things on top of NSAppleScript in a GC world since + Apple has bugs and it can crash. See the unittest for a note about it. + +- GTMStackTrace now can figure out ObjC symbols. Downside it is now ObjC only. + +- GTMFourCharCode can now be used with NSAppleEventDescriptors easily. + typeType, typeKeyword, typeApplSignature, and typeEnumerated all get + turned into GTMFourCharCodes. + +- Fixed up crash in GTMLoggerRingBufferWriter when used with GC on. + +- Significant updates to GTMNSAppleScript+Handler allowing you to + list all handlers and properties (including inherited) and cleans up + several errors in how scripting was being handled. + Release 1.5.1 Changes since 1.5.0 diff --git a/UnitTesting/GTMDevLogUnitTestingBridge.m b/UnitTesting/GTMDevLogUnitTestingBridge.m index c04481c..3d2b84d 100644 --- a/UnitTesting/GTMDevLogUnitTestingBridge.m +++ b/UnitTesting/GTMDevLogUnitTestingBridge.m @@ -38,7 +38,7 @@ void _GTMUnitTestDevLog(NSString *format, ...) { if (devLogClass) { [devLogClass log:format args:argList]; } else { - NSLogv(format, argList); + NSLogv(format, argList); // COV_NF_LINE the class is in all our unittest setups } va_end(argList); } diff --git a/UnitTesting/GTMNSObject+UnitTesting.m b/UnitTesting/GTMNSObject+UnitTesting.m index 80450b3..8e0899d 100644 --- a/UnitTesting/GTMNSObject+UnitTesting.m +++ b/UnitTesting/GTMNSObject+UnitTesting.m @@ -654,7 +654,7 @@ static NSString *gGTMUnitTestSaveToDirectory = nil; kUTTagClassFilenameExtension); _GTMDevAssert(extension, @"No extension for uti: %@", uti); - return [GTMNSMakeCollectable(extension) autorelease]; + return GTMCFAutorelease(extension); #endif } |