aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar thomasvl@gmail.com <thomasvl@gmail.com@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2008-08-12 17:21:32 +0000
committerGravatar thomasvl@gmail.com <thomasvl@gmail.com@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2008-08-12 17:21:32 +0000
commit7063d76a007fbf636250d7199d6f24ec487163b1 (patch)
tree5a1f2f0a4b597f62df3e2fe858d76d37b22bbe89
parent43982f07ba6a0a9839e32e774855c9d2068e9d5e (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.
-rw-r--r--AppKit/GTMLargeTypeWindow.h9
-rw-r--r--AppKit/GTMLargeTypeWindow.m210
-rw-r--r--AppKit/GTMLargeTypeWindowTest.m100
-rw-r--r--AppKit/GTMLinearRGBShadingTest.m37
-rw-r--r--AppKit/GTMLoginItems.m10
-rw-r--r--AppKit/GTMLoginItemsTest.m46
-rw-r--r--AppKit/GTMNSBezierPath+CGPath.m4
-rw-r--r--AppKit/GTMNSBezierPath+RoundRectTest.m16
-rw-r--r--AppKit/GTMNSBezierPath+ShadingTest.m96
-rw-r--r--AppKit/TestData/GTMNSBezierPath+RoundRectTest.ppc64.tiffbin8042 -> 10728 bytes
-rw-r--r--AppKit/TestData/GTMNSBezierPath+RoundRectTest.tiffbin8036 -> 10724 bytes
-rw-r--r--AppKit/TestData/GTMNSBezierPath+RoundRectTest.x86_64.tiffbin8042 -> 10728 bytes
-rw-r--r--AppKit/TestData/GTMNSBezierPath+ShadingTest.10.5.tiffbin19450 -> 45080 bytes
-rw-r--r--Foundation/GTMFourCharCode.m4
-rw-r--r--Foundation/GTMGarbageCollection.h44
-rw-r--r--Foundation/GTMGeometryUtils.h2
-rw-r--r--Foundation/GTMGeometryUtilsTest.m123
-rw-r--r--Foundation/GTMHTTPFetcher.m22
-rw-r--r--Foundation/GTMHTTPFetcherTest.m14
-rw-r--r--Foundation/GTMHTTPServer.h2
-rw-r--r--Foundation/GTMHTTPServer.m34
-rw-r--r--Foundation/GTMHTTPServerTest.m9
-rw-r--r--Foundation/GTMLogger+ASLTest.m3
-rw-r--r--Foundation/GTMLogger.m2
-rw-r--r--Foundation/GTMLoggerRingBufferWriter.h27
-rw-r--r--Foundation/GTMLoggerRingBufferWriter.m96
-rw-r--r--Foundation/GTMLoggerRingBufferWriterTest.m115
-rw-r--r--Foundation/GTMNSAppleEvent+HandlerTest.applescript19
-rw-r--r--Foundation/GTMNSAppleEventDescriptor+Foundation.h15
-rw-r--r--Foundation/GTMNSAppleEventDescriptor+Foundation.m43
-rw-r--r--Foundation/GTMNSAppleEventDescriptor+FoundationTest.m29
-rw-r--r--Foundation/GTMNSAppleEventDescriptor+Handler.m2
-rw-r--r--Foundation/GTMNSAppleEventDescriptor+HandlerTest.m2
-rw-r--r--Foundation/GTMNSAppleScript+Handler.h83
-rw-r--r--Foundation/GTMNSAppleScript+Handler.m399
-rw-r--r--Foundation/GTMNSAppleScript+HandlerTest.m222
-rw-r--r--Foundation/GTMNSString+FindFolder.m9
-rw-r--r--Foundation/GTMNSString+URLArguments.m2
-rw-r--r--Foundation/GTMNSString+XMLTest.m2
-rw-r--r--Foundation/GTMObjC2Runtime.h34
-rw-r--r--Foundation/GTMProgressMonitorInputStream.m36
-rw-r--r--Foundation/GTMProgressMonitorInputStreamTest.m257
-rw-r--r--Foundation/GTMRegex.m5
-rw-r--r--Foundation/GTMScriptRunnerTest.m8
-rw-r--r--Foundation/GTMStackTrace.c92
-rw-r--r--Foundation/GTMStackTrace.h67
-rw-r--r--Foundation/GTMStackTrace.m228
-rw-r--r--Foundation/GTMStackTraceTest.m8
-rw-r--r--GTM.xcodeproj/project.pbxproj36
-rw-r--r--ReleaseNotes.txt24
-rw-r--r--UnitTesting/GTMDevLogUnitTestingBridge.m2
-rw-r--r--UnitTesting/GTMNSObject+UnitTesting.m2
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
index a4df8f8..ef22b6b 100644
--- a/AppKit/TestData/GTMNSBezierPath+RoundRectTest.ppc64.tiff
+++ b/AppKit/TestData/GTMNSBezierPath+RoundRectTest.ppc64.tiff
Binary files differ
diff --git a/AppKit/TestData/GTMNSBezierPath+RoundRectTest.tiff b/AppKit/TestData/GTMNSBezierPath+RoundRectTest.tiff
index c41ab04..b165acf 100644
--- a/AppKit/TestData/GTMNSBezierPath+RoundRectTest.tiff
+++ b/AppKit/TestData/GTMNSBezierPath+RoundRectTest.tiff
Binary files differ
diff --git a/AppKit/TestData/GTMNSBezierPath+RoundRectTest.x86_64.tiff b/AppKit/TestData/GTMNSBezierPath+RoundRectTest.x86_64.tiff
index a4df8f8..ef22b6b 100644
--- a/AppKit/TestData/GTMNSBezierPath+RoundRectTest.x86_64.tiff
+++ b/AppKit/TestData/GTMNSBezierPath+RoundRectTest.x86_64.tiff
Binary files differ
diff --git a/AppKit/TestData/GTMNSBezierPath+ShadingTest.10.5.tiff b/AppKit/TestData/GTMNSBezierPath+ShadingTest.10.5.tiff
index 040cb0d..b44b5bf 100644
--- a/AppKit/TestData/GTMNSBezierPath+ShadingTest.10.5.tiff
+++ b/AppKit/TestData/GTMNSBezierPath+ShadingTest.10.5.tiff
Binary files differ
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
}