aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar thomasvl <thomasvl@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2008-05-09 18:53:09 +0000
committerGravatar thomasvl <thomasvl@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2008-05-09 18:53:09 +0000
commitf90bcf3263b80b96754977ddbd5309704cf817fb (patch)
tree585db4c1126cea3bd0ff9b338628d33febe36e59
parente5c365c04b9f2f6d04b2c5bd828f39fab2882e7d (diff)
Flush out suppport for 64bit, GC support.
Added some more xcode configs related to the above. Removed some classes that the unittesting doesn't need (and aren't able to support 64bit). add base64, httpfetcher, and progress monitor stream.
-rw-r--r--AppKit/GTMDelegatingTableColumn.h19
-rw-r--r--AppKit/GTMDelegatingTableColumn.m27
-rw-r--r--AppKit/GTMLinearRGBShading.h6
-rw-r--r--AppKit/GTMLinearRGBShading.m76
-rw-r--r--AppKit/GTMLinearRGBShadingTest.m16
-rw-r--r--AppKit/GTMLoginItems.m31
-rw-r--r--AppKit/GTMNSBezierPath+CGPath.m6
-rw-r--r--AppKit/GTMNSBezierPath+CGPathTest.m14
-rw-r--r--AppKit/GTMNSBezierPath+RoundRect.h7
-rw-r--r--AppKit/GTMNSBezierPath+RoundRect.m22
-rw-r--r--AppKit/GTMNSBezierPath+RoundRectTest.m40
-rw-r--r--AppKit/GTMNSBezierPath+Shading.h9
-rw-r--r--AppKit/GTMNSBezierPath+Shading.m54
-rw-r--r--AppKit/GTMNSBezierPath+ShadingTest.m4
-rw-r--r--AppKit/GTMNSColor+Theme.m62
-rw-r--r--AppKit/GTMNSColor+ThemeTest.m199
-rw-r--r--AppKit/GTMNSWorkspace+ScreenSaver.m4
-rw-r--r--AppKit/GTMNSWorkspace+Theme.h39
-rw-r--r--AppKit/GTMNSWorkspace+Theme.m42
-rw-r--r--AppKit/GTMNSWorkspace+ThemeTest.m38
-rw-r--r--AppKit/TestData/GTMNSBezierPath+CGPathTest.ppc64.tiffbin0 -> 2556 bytes
-rw-r--r--AppKit/TestData/GTMNSBezierPath+CGPathTest.tiff (renamed from AppKit/GTMNSBezierPath+CGPathTest.tiff)bin2558 -> 2558 bytes
-rw-r--r--AppKit/TestData/GTMNSBezierPath+CGPathTest.x86_64.tiffbin0 -> 2556 bytes
-rw-r--r--AppKit/TestData/GTMNSBezierPath+RoundRectTest.ppc64.tiffbin0 -> 8042 bytes
-rw-r--r--AppKit/TestData/GTMNSBezierPath+RoundRectTest.tiff (renamed from AppKit/GTMNSBezierPath+RoundRectTest.tiff)bin8036 -> 8036 bytes
-rw-r--r--AppKit/TestData/GTMNSBezierPath+RoundRectTest.x86_64.tiffbin0 -> 8042 bytes
-rw-r--r--AppKit/TestData/GTMNSBezierPath+ShadingTest.10.5.tiff (renamed from AppKit/GTMNSBezierPath+ShadingTest.10.5.tiff)bin19450 -> 19450 bytes
-rw-r--r--DebugUtils/GTMDebugSelectorValidation.h56
-rw-r--r--DebugUtils/GTMMethodCheck.m10
-rw-r--r--Foundation/GTMBase64.h183
-rw-r--r--Foundation/GTMBase64.m697
-rw-r--r--Foundation/GTMBase64Test.m437
-rw-r--r--Foundation/GTMCalculatedRange.h13
-rw-r--r--Foundation/GTMCalculatedRange.m34
-rw-r--r--Foundation/GTMCalculatedRangeTest.m28
-rw-r--r--Foundation/GTMGeometryUtils.h27
-rw-r--r--Foundation/GTMGeometryUtils.m10
-rw-r--r--Foundation/GTMGeometryUtilsTest.m28
-rw-r--r--Foundation/GTMHTTPFetcher.h500
-rw-r--r--Foundation/GTMHTTPFetcher.m1889
-rw-r--r--Foundation/GTMHTTPFetcherTest.m543
-rw-r--r--Foundation/GTMNSData+zlib.h11
-rw-r--r--Foundation/GTMNSData+zlib.m30
-rw-r--r--Foundation/GTMNSData+zlibTest.m36
-rw-r--r--Foundation/GTMNSEnumerator+Filter.m2
-rw-r--r--Foundation/GTMNSString+HTML.m37
-rw-r--r--Foundation/GTMNSString+HTMLTest.m3
-rw-r--r--Foundation/GTMNSString+Utilities.h41
-rw-r--r--Foundation/GTMNSString+Utilities.m48
-rw-r--r--Foundation/GTMNSString+UtilitiesTest.m62
-rw-r--r--Foundation/GTMNSString+XML.m92
-rw-r--r--Foundation/GTMNSString+XMLTest.m6
-rw-r--r--Foundation/GTMObjC2Runtime.h16
-rw-r--r--Foundation/GTMObjC2Runtime.m2
-rw-r--r--Foundation/GTMObjectSingleton.h6
-rw-r--r--Foundation/GTMProgressMonitorInputStream.h73
-rw-r--r--Foundation/GTMProgressMonitorInputStream.m187
-rw-r--r--Foundation/GTMRegex.h12
-rw-r--r--Foundation/GTMRegex.m50
-rw-r--r--Foundation/GTMRegexTest.m50
-rw-r--r--Foundation/GTMScriptRunnerTest.m2
-rw-r--r--Foundation/GTMSystemVersion.h2
-rw-r--r--Foundation/GTMSystemVersion.m46
-rw-r--r--Foundation/TestData/GTMHTTPFetcherTestPage.html9
-rwxr-xr-xFoundation/TestData/GTMHTTPFetcherTestServer274
-rw-r--r--GTM.xcodeproj/project.pbxproj654
-rw-r--r--GTMDefines.h47
-rw-r--r--GTMiPhone.xcodeproj/project.pbxproj108
-rw-r--r--ReleaseNotes.txt20
-rw-r--r--UnitTesting/GTMAppKit+UnitTesting.m64
-rw-r--r--UnitTesting/GTMIPhoneUnitTestMain.m4
-rw-r--r--UnitTesting/GTMNSObject+BindingUnitTesting.m3
-rw-r--r--UnitTesting/GTMNSObject+UnitTesting.m118
-rw-r--r--UnitTesting/GTMUnitTestingBindingTest.m1
-rw-r--r--UnitTesting/GTMUnitTestingTest.m12
-rw-r--r--UnitTesting/GTMUnitTestingUtilities.m8
-rw-r--r--UnitTesting/TestData/GTMUIViewUnitTestingTest.gtmUTState (renamed from UnitTesting/GTMUIViewUnitTestingTest.gtmUTState)0
-rw-r--r--UnitTesting/TestData/GTMUIViewUnitTestingTest.png (renamed from UnitTesting/GTMUIViewUnitTestingTest.png)bin3214 -> 3214 bytes
-rw-r--r--UnitTesting/TestData/GTMUnitTestingImage.gtmUTState (renamed from UnitTesting/GTMUnitTestingImage.gtmUTState)0
-rw-r--r--UnitTesting/TestData/GTMUnitTestingImage.tiff (renamed from UnitTesting/GTMUnitTestingImage.tiff)bin67190 -> 67190 bytes
-rw-r--r--UnitTesting/TestData/GTMUnitTestingTest.nib/classes.nib (renamed from UnitTesting/GTMUnitTestingTest.nib/classes.nib)0
-rw-r--r--UnitTesting/TestData/GTMUnitTestingTest.nib/info.nib (renamed from UnitTesting/GTMUnitTestingTest.nib/info.nib)0
-rw-r--r--UnitTesting/TestData/GTMUnitTestingTest.nib/keyedobjects.nib (renamed from UnitTesting/GTMUnitTestingTest.nib/keyedobjects.nib)bin15667 -> 15667 bytes
-rw-r--r--UnitTesting/TestData/GTMUnitTestingTestApp.gtmUTState (renamed from UnitTesting/GTMUnitTestingTestApp.gtmUTState)288
-rw-r--r--UnitTesting/TestData/GTMUnitTestingView.tiff (renamed from UnitTesting/GTMUnitTestingView.tiff)bin161670 -> 161670 bytes
-rw-r--r--UnitTesting/TestData/GTMUnitTestingWindow.gtmUTState (renamed from UnitTesting/GTMUnitTestingWindow.gtmUTState)0
-rw-r--r--UnitTesting/TestData/GTMUnitTestingWindow.tiff (renamed from UnitTesting/GTMUnitTestingWindow.tiff)bin21226 -> 21226 bytes
-rw-r--r--XcodeConfig/Target/LoadableBundleGCSupported.xcconfig (renamed from AppKit/GTMNSColor+Theme.h)27
-rw-r--r--XcodeConfig/Target/SharedLibraryGCSupported.xcconfig30
-rw-r--r--XcodeConfig/Target/StaticLibraryGCSupported.xcconfig30
-rw-r--r--XcodeConfig/subconfig/64bit.xcconfig26
-rw-r--r--XcodeConfig/subconfig/GCSupported.xcconfig23
-rw-r--r--XcodeConfig/subconfig/LeopardOrLater.xcconfig4
-rw-r--r--XcodeConfig/subconfig/TigerOrLater.xcconfig2
94 files changed, 6279 insertions, 1457 deletions
diff --git a/AppKit/GTMDelegatingTableColumn.h b/AppKit/GTMDelegatingTableColumn.h
index a90b298..63abf20 100644
--- a/AppKit/GTMDelegatingTableColumn.h
+++ b/AppKit/GTMDelegatingTableColumn.h
@@ -17,16 +17,19 @@
//
#import <Cocoa/Cocoa.h>
+#import "GTMDefines.h"
-@interface GTMDelegatingTableColumn : NSTableColumn {
- @private
- IBOutlet id delegate_;
-}
-- (void)setDelegate:(id)delegate;
-- (id)delegate;
-- (id)dataCellForRow:(int)row;
+// NOTE: If you're using the 10.5 SDK, just use the new delegate method:
+// tableView:dataCellForTableColumn:row:
+
+@interface GTMDelegatingTableColumn : NSTableColumn
+// no instance state or new method, it will just invoke the tableview's delegate
+// w/ the method below.
@end
+// the method delegated to
@interface NSObject (GTMDelegatingTableColumnDelegate)
-- (id)tableColumn:(NSTableColumn*)column dataCellForRow:(int)row;
+- (id)gtm_tableView:(NSTableView *)tableView
+ dataCellForTableColumn:(NSTableColumn *)tableColumn
+ row:(NSInteger)row;
@end
diff --git a/AppKit/GTMDelegatingTableColumn.m b/AppKit/GTMDelegatingTableColumn.m
index d84e2e7..0bd2371 100644
--- a/AppKit/GTMDelegatingTableColumn.m
+++ b/AppKit/GTMDelegatingTableColumn.m
@@ -19,19 +19,22 @@
#import "GTMDelegatingTableColumn.h"
@implementation GTMDelegatingTableColumn
-- (void)setDelegate:(id)delegate {
- delegate_ = delegate;
-}
-
-- (id)delegate {
- return delegate_;
-}
-
-- (id)dataCellForRow:(int)row {
+- (id)dataCellForRow:(NSInteger)row {
id dataCell = nil;
- if (delegate_ && [delegate_ respondsToSelector:@selector(tableColumn:dataCellForRow:)]) {
- dataCell = [delegate_ tableColumn:self dataCellForRow:row];
- } else {
+ id delegate = [[self tableView] delegate];
+ BOOL sendSuper = YES;
+ if (delegate) {
+ if ([delegate respondsToSelector:@selector(gtm_tableView:dataCellForTableColumn:row:)]) {
+
+ dataCell = [delegate gtm_tableView:[self tableView]
+ dataCellForTableColumn:self
+ row:row];
+ sendSuper = NO;
+ } else {
+ _GTMDevLog(@"tableView delegate didn't implement gtm_tableView:dataCellForTableColumn:row:");
+ }
+ }
+ if (sendSuper) {
dataCell = [super dataCellForRow:row];
}
return dataCell;
diff --git a/AppKit/GTMLinearRGBShading.h b/AppKit/GTMLinearRGBShading.h
index f6b6405..d85a4eb 100644
--- a/AppKit/GTMLinearRGBShading.h
+++ b/AppKit/GTMLinearRGBShading.h
@@ -34,7 +34,7 @@
CGFunctionRef function_; // function used to calculated shading (STRONG)
CGColorSpaceRef colorSpace_; // colorspace used for shading (STRONG)
BOOL isCalibrated_; // are we using calibrated or device RGB.
- float colorValue[4]; // the RGBA color values
+ CGFloat colorValue_[4]; // the RGBA color values
}
/// Generate a shading with color |begin| at position 0.0 and color |end| at 1.0.
@@ -64,8 +64,8 @@
// a GTMLinearRGBShading
+ (id)shadingWithColors:(NSColor **)colors
fromSpaceNamed:(NSString*)colorSpaceName
- atPositions:(float *)positions
- count:(unsigned int)numberOfColors;
+ atPositions:(CGFloat *)positions
+ count:(NSUInteger)numberOfColors;
/// Designated initializer
// Args:
diff --git a/AppKit/GTMLinearRGBShading.m b/AppKit/GTMLinearRGBShading.m
index 31f22ca..18af0e0 100644
--- a/AppKit/GTMLinearRGBShading.m
+++ b/AppKit/GTMLinearRGBShading.m
@@ -19,24 +19,24 @@
#import "GTMLinearRGBShading.h"
// Carbon callback function required for CoreGraphics
-static void cShadeFunction(void *info, const float *inPos, float *outVals);
+static void cShadeFunction(void *info, const CGFloat *inPos, CGFloat *outVals);
@implementation GTMLinearRGBShading
+ (id)shadingFromColor:(NSColor *)begin toColor:(NSColor *)end
fromSpaceNamed:(NSString*)colorSpaceName {
NSColor *theColors[] = { begin, end };
- float thePositions[] = { 0.0f, 1.0f };
+ CGFloat thePositions[] = { 0.0, 1.0 };
return [[self class] shadingWithColors:theColors
fromSpaceNamed:colorSpaceName
atPositions:thePositions
- count:(sizeof(thePositions)/sizeof(float))];
+ count:(sizeof(thePositions)/sizeof(CGFloat))];
}
+ (id)shadingWithColors:(NSColor **)colors fromSpaceNamed:(NSString*)colorSpaceName
- atPositions:(float *)positions count:(unsigned)count {
+ atPositions:(CGFloat *)positions count:(NSUInteger)count {
GTMLinearRGBShading *theShading = [[[[self class] alloc] initWithColorSpaceName:colorSpaceName] autorelease];
- for (unsigned int i = 0; i < count; ++i) {
+ for (NSUInteger i = 0; i < count; ++i) {
[theShading insertStop:colors[i] atPosition:positions[i]];
}
return theShading;
@@ -50,14 +50,24 @@ static void cShadeFunction(void *info, const float *inPos, float *outVals);
isCalibrated_ = YES;
}
else {
- [self dealloc];
+ [self release];
self = nil;
}
}
return self;
}
-- (void) dealloc {
+- (void)finalize {
+ if (nil != function_) {
+ CGFunctionRelease(function_);
+ }
+ if (nil != colorSpace_) {
+ CGColorSpaceRelease(colorSpace_);
+ }
+ [super finalize];
+}
+
+- (void)dealloc {
if (nil != function_) {
CGFunctionRelease(function_);
}
@@ -68,7 +78,7 @@ static void cShadeFunction(void *info, const float *inPos, float *outVals);
}
-- (void)insertStop:(id)item atPosition:(float)position {
+- (void)insertStop:(id)item atPosition:(CGFloat)position {
NSString *colorSpaceName = isCalibrated_ ? NSCalibratedRGBColorSpace : NSDeviceRGBColorSpace;
NSColor *tempColor = [item colorUsingColorSpaceName: colorSpaceName];
if (nil != tempColor) {
@@ -77,13 +87,13 @@ static void cShadeFunction(void *info, const float *inPos, float *outVals);
}
// Calculate a linear value based on our stops
-- (id)valueAtPosition:(float)position {
- unsigned int index = 0;
- unsigned int colorCount = [self stopCount];
- float stop1Position = 0.0f;
+- (id)valueAtPosition:(CGFloat)position {
+ NSUInteger index = 0;
+ NSUInteger colorCount = [self stopCount];
+ CGFloat stop1Position = 0.0;
NSColor *stop1Color = [self stopAtIndex:index position:&stop1Position];
index += 1;
- float stop2Position = 0.0f;
+ CGFloat stop2Position = 0.0;
NSColor *stop2Color = nil;
NSColor *theColor = nil;
if (colorCount > 1) {
@@ -105,30 +115,30 @@ static void cShadeFunction(void *info, const float *inPos, float *outVals);
if (position <= stop1Position) {
// if we are less than our lowest position, return our first color
theColor = stop1Color;
- [stop1Color getRed:&colorValue[0] green:&colorValue[1]
- blue:&colorValue[2] alpha:&colorValue[3]];
+ [stop1Color getRed:&colorValue_[0] green:&colorValue_[1]
+ blue:&colorValue_[2] alpha:&colorValue_[3]];
} else if (position >= stop2Position) {
// likewise if we are greater than our highest position, return the last color
- [stop2Color getRed:&colorValue[0] green:&colorValue[1]
- blue:&colorValue[2] alpha:&colorValue[3]];
+ [stop2Color getRed:&colorValue_[0] green:&colorValue_[1]
+ blue:&colorValue_[2] alpha:&colorValue_[3]];
} else {
// otherwise interpolate between the two
position = (position - stop1Position) / (stop2Position - stop1Position);
- float red1, red2, green1, green2, blue1, blue2, alpha1, alpha2;
+ CGFloat red1, red2, green1, green2, blue1, blue2, alpha1, alpha2;
[stop1Color getRed:&red1 green:&green1 blue:&blue1 alpha:&alpha1];
[stop2Color getRed:&red2 green:&green2 blue:&blue2 alpha:&alpha2];
- colorValue[0] = (red2 - red1) * position + red1;
- colorValue[1] = (green2 - green1) * position + green1;
- colorValue[2] = (blue2 - blue1) * position + blue1;
- colorValue[3] = (alpha2 - alpha1) * position + alpha1;
+ colorValue_[0] = (red2 - red1) * position + red1;
+ colorValue_[1] = (green2 - green1) * position + green1;
+ colorValue_[2] = (blue2 - blue1) * position + blue1;
+ colorValue_[3] = (alpha2 - alpha1) * position + alpha1;
}
- // Yes, I am casting a float[] to an id to pass it by the compiler. This
+ // Yes, I am casting a CGFloat[] to an id to pass it by the compiler. This
// significantly improves performance though as I avoid creating an NSColor
// for every scanline which later has to be cleaned up in an autorelease pool
// somewhere. Causes guardmalloc to run significantly faster.
- return (id)colorValue;
+ return (id)colorValue_;
}
//
@@ -146,12 +156,12 @@ static void cShadeFunction(void *info, const float *inPos, float *outVals);
// a single float value
// outVals: where we store our return values. Since we are calculating
// an RGBA color, this is a pointer to an array of four float values
-// ranging from 0.0f to 1.0f
+// ranging from 0.0 to 1.0
//
//
-static void cShadeFunction(void *info, const float *inPos, float *outVals) {
+static void cShadeFunction(void *info, const CGFloat *inPos, CGFloat *outVals) {
id object = (id)info;
- float *colorValue = (float*)[object valueAtPosition:*inPos];
+ CGFloat *colorValue = (CGFloat*)[object valueAtPosition:*inPos];
outVals[0] = colorValue[0];
outVals[1] = colorValue[1];
outVals[2] = colorValue[2];
@@ -165,15 +175,15 @@ static void cShadeFunction(void *info, const float *inPos, float *outVals) {
// diposed if necessary in the dealloc call.
const CGFunctionCallbacks shadeFunctionCallbacks = { 0, &cShadeFunction, NULL };
- // TODO: this code assumes that we have a range from 0.0f to 1.0f
+ // TODO: this code assumes that we have a range from 0.0 to 1.0
// which may not be true according to the stops that the user has given us.
// In general you have stops at 0.0 and 1.0, so this will do for right now
// but may be an issue in the future.
- const float inRange[2] = { 0.0f, 1.0f };
- const float outRange[8] = { 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f };
- function_ = CGFunctionCreate(self,
- sizeof(inRange) / (sizeof(float) * 2), inRange,
- sizeof(outRange) / (sizeof(float) * 2), outRange,
+ const CGFloat inRange[2] = { 0.0, 1.0 };
+ const CGFloat outRange[8] = { 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0 };
+ function_ = CGFunctionCreate(self,
+ sizeof(inRange) / (sizeof(CGFloat) * 2), inRange,
+ sizeof(outRange) / (sizeof(CGFloat) * 2), outRange,
&shadeFunctionCallbacks);
}
return function_;
diff --git a/AppKit/GTMLinearRGBShadingTest.m b/AppKit/GTMLinearRGBShadingTest.m
index e90d84c..119fa79 100644
--- a/AppKit/GTMLinearRGBShadingTest.m
+++ b/AppKit/GTMLinearRGBShadingTest.m
@@ -34,8 +34,8 @@
toColor:blue
fromSpaceNamed:NSCalibratedRGBColorSpace];
STAssertNotNil(theShading,nil);
- STAssertEquals([theShading stopCount], 2U, nil);
- float *theColor = (float*)[theShading valueAtPosition: 0.5];
+ STAssertEquals([theShading stopCount], (NSUInteger)2, nil);
+ CGFloat *theColor = (CGFloat*)[theShading valueAtPosition: 0.5];
STAssertTrue(theColor[0] == [purple redComponent] &&
theColor[1] == [purple greenComponent] &&
theColor[2] == [purple blueComponent] &&
@@ -44,11 +44,11 @@
- (void)testShadingWith {
// Create a shading with kColorCount colors and make sure all the values are there.
- const unsigned int kColorCount = 100;
+ const NSUInteger kColorCount = 100;
NSColor *theColors[kColorCount];
- float thePositions[kColorCount];
- const float kColorIncrement = 1.0f / kColorCount;
- for (unsigned int i = 0; i < kColorCount; i++) {
+ CGFloat thePositions[kColorCount];
+ const CGFloat kColorIncrement = 1.0 / kColorCount;
+ for (NSUInteger i = 0; i < kColorCount; i++) {
thePositions[i] = kColorIncrement * i;
theColors[i] = [NSColor colorWithCalibratedRed:kColorIncrement * i
green:kColorIncrement * i
@@ -60,8 +60,8 @@
fromSpaceNamed:NSCalibratedRGBColorSpace
atPositions:thePositions
count:kColorCount];
- for (unsigned int i = 0; i < kColorCount; i++) {
- float *theColor = (float*)[theShading valueAtPosition: kColorIncrement * i];
+ for (NSUInteger i = 0; i < kColorCount; i++) {
+ CGFloat *theColor = (CGFloat*)[theShading valueAtPosition: kColorIncrement * i];
STAssertTrue(theColor[0] == kColorIncrement * i &&
theColor[1] == kColorIncrement * i &&
theColor[2] == kColorIncrement * i &&
diff --git a/AppKit/GTMLoginItems.m b/AppKit/GTMLoginItems.m
index d3197c0..cc34c11 100644
--- a/AppKit/GTMLoginItems.m
+++ b/AppKit/GTMLoginItems.m
@@ -18,6 +18,7 @@
//
#import "GTMLoginItems.h"
+#import "GTMDefines.h"
#include <Carbon/Carbon.h>
@@ -27,22 +28,22 @@ NSString * const kGTMLoginItemsPathKey = @"Path";
NSString * const kGTMLoginItemsHiddenKey = @"Hide";
@interface GTMLoginItems (PrivateMethods)
-+ (int)indexOfLoginItemWithValue:(id)value
- forKey:(NSString *)key
- loginItems:(NSArray *)items;
++ (NSInteger)indexOfLoginItemWithValue:(id)value
+ forKey:(NSString *)key
+ loginItems:(NSArray *)items;
+ (BOOL)compileAndRunScript:(NSString *)script
withError:(NSError **)errorInfo;
@end
@implementation GTMLoginItems (PrivateMethods)
-+ (int)indexOfLoginItemWithValue:(id)value
- forKey:(NSString *)key
- loginItems:(NSArray *)items {
++ (NSInteger)indexOfLoginItemWithValue:(id)value
+ forKey:(NSString *)key
+ loginItems:(NSArray *)items {
if (!value || !key || !items) return NSNotFound;
NSDictionary *item = nil;
NSEnumerator *itemsEnum = [items objectEnumerator];
- int found = -1;
+ NSInteger found = -1;
while ((item = [itemsEnum nextObject])) {
++found;
id itemValue = [item objectForKey:key];
@@ -105,8 +106,8 @@ NSString * const kGTMLoginItemsHiddenKey = @"Hide";
}
// build our results
NSMutableArray *result = [NSMutableArray array];
- int count = [scriptResult numberOfItems];
- for (int i = 0; i < count; ++i) {
+ NSInteger count = [scriptResult numberOfItems];
+ for (NSInteger i = 0; i < count; ++i) {
NSAppleEventDescriptor *aeItem = [scriptResult descriptorAtIndex:i+1];
NSAppleEventDescriptor *hidn = [aeItem descriptorForKeyword:kAEHidden];
NSAppleEventDescriptor *nam = [aeItem descriptorForKeyword:pName];
@@ -135,17 +136,17 @@ NSString * const kGTMLoginItemsHiddenKey = @"Hide";
+ (BOOL)pathInLoginItems:(NSString *)path {
NSArray *loginItems = [self loginItems:nil];
- int index = [self indexOfLoginItemWithValue:path
- forKey:kGTMLoginItemsPathKey
- loginItems:loginItems];
+ NSInteger index = [self indexOfLoginItemWithValue:path
+ forKey:kGTMLoginItemsPathKey
+ loginItems:loginItems];
return (index != NSNotFound) ? YES : NO;
}
+ (BOOL)itemWithNameInLoginItems:(NSString *)name {
NSArray *loginItems = [self loginItems:nil];
- int index = [self indexOfLoginItemWithValue:name
- forKey:kGTMLoginItemsNameKey
- loginItems:loginItems];
+ NSInteger index = [self indexOfLoginItemWithValue:name
+ forKey:kGTMLoginItemsNameKey
+ loginItems:loginItems];
return (index != NSNotFound) ? YES : NO;
}
diff --git a/AppKit/GTMNSBezierPath+CGPath.m b/AppKit/GTMNSBezierPath+CGPath.m
index c805f42..3bc6de9 100644
--- a/AppKit/GTMNSBezierPath+CGPath.m
+++ b/AppKit/GTMNSBezierPath+CGPath.m
@@ -1,5 +1,5 @@
//
-// GTMNSBezierPath+CGPath.h
+// GTMNSBezierPath+CGPath.m
//
// Category for extracting a CGPathRef from a NSBezierPath
//
@@ -33,13 +33,13 @@
CGMutablePathRef thePath = CGPathCreateMutable();
if (!thePath) return nil;
- unsigned int elementCount = [self elementCount];
+ NSInteger elementCount = [self elementCount];
// The maximum number of points is 3 for a NSCurveToBezierPathElement.
// (controlPoint1, controlPoint2, and endPoint)
NSPoint controlPoints[3];
- for (unsigned int i = 0; i < elementCount; i++) {
+ for (NSInteger i = 0; i < elementCount; i++) {
switch ([self elementAtIndex:i associatedPoints:controlPoints]) {
case NSMoveToBezierPathElement:
CGPathMoveToPoint(thePath, &CGAffineTransformIdentity,
diff --git a/AppKit/GTMNSBezierPath+CGPathTest.m b/AppKit/GTMNSBezierPath+CGPathTest.m
index 3ca11c3..e22ee07 100644
--- a/AppKit/GTMNSBezierPath+CGPathTest.m
+++ b/AppKit/GTMNSBezierPath+CGPathTest.m
@@ -36,11 +36,11 @@
// Draws all of our tests so that we can compare this to our stored image file.
- (void)gtm_unitTestViewDrawRect:(NSRect)rect contextInfo:(void*)contextInfo{
NSBezierPath *thePath = [NSBezierPath bezierPath];
- NSPoint theStart = NSMakePoint(20.0f, 20.0f);
+ NSPoint theStart = NSMakePoint(20.0, 20.0);
// Test moveto/lineto
[thePath moveToPoint: theStart];
- for (unsigned int i = 0; i < 10; ++i) {
+ for (NSUInteger i = 0; i < 10; ++i) {
NSPoint theNewPoint = NSMakePoint(i * 5, i * 10);
[thePath lineToPoint: theNewPoint];
theNewPoint = NSMakePoint(i * 2, i * 6);
@@ -48,11 +48,11 @@
}
// Test moveto/curveto
- for (unsigned int i = 0; i < 10; ++i) {
- NSPoint startPoint = NSMakePoint(5.0f, 50.0f);
- NSPoint endPoint = NSMakePoint(55.0f, 50.0f);
- NSPoint controlPoint1 = NSMakePoint(17.5f, 50.0f + 5.0f * i);
- NSPoint controlPoint2 = NSMakePoint(42.5f, 50.0f - 5.0f * i);
+ for (NSUInteger i = 0; i < 10; ++i) {
+ NSPoint startPoint = NSMakePoint(5.0, 50.0);
+ NSPoint endPoint = NSMakePoint(55.0, 50.0);
+ NSPoint controlPoint1 = NSMakePoint(17.5, 50.0 + 5.0 * i);
+ NSPoint controlPoint2 = NSMakePoint(42.5, 50.0 - 5.0 * i);
[thePath moveToPoint:startPoint];
[thePath curveToPoint:endPoint controlPoint1:controlPoint1 controlPoint2:controlPoint2];
}
diff --git a/AppKit/GTMNSBezierPath+RoundRect.h b/AppKit/GTMNSBezierPath+RoundRect.h
index 8e916fe..b0b48de 100644
--- a/AppKit/GTMNSBezierPath+RoundRect.h
+++ b/AppKit/GTMNSBezierPath+RoundRect.h
@@ -20,6 +20,7 @@
//
#import <Cocoa/Cocoa.h>
+#import "GTMDefines.h"
/// Category for adding utility functions for creating round rectangles.
@interface NSBezierPath (GMBezierPathRoundRectAdditions)
@@ -33,7 +34,8 @@
//
// Returns:
// Auto released NSBezierPath
-+ (NSBezierPath *)gtm_bezierPathWithRoundRect:(NSRect)rect cornerRadius:(float)radius;
++ (NSBezierPath *)gtm_bezierPathWithRoundRect:(NSRect)rect
+ cornerRadius:(CGFloat)radius;
/// Adds a path which is a round rectangle inscribed inside of rectangle |rect| with a corner radius of |radius|
//
@@ -41,5 +43,6 @@
// rect: outer rectangle to inscribe into
// radius: radius of the corners. |radius| is clamped internally
// to be no larger than the smaller of half |rect|'s width or height
-- (void)gtm_appendBezierPathWithRoundRect:(NSRect)rect cornerRadius:(float)radius;
+- (void)gtm_appendBezierPathWithRoundRect:(NSRect)rect
+ cornerRadius:(CGFloat)radius;
@end
diff --git a/AppKit/GTMNSBezierPath+RoundRect.m b/AppKit/GTMNSBezierPath+RoundRect.m
index 4b98dc9..d4e5050 100644
--- a/AppKit/GTMNSBezierPath+RoundRect.m
+++ b/AppKit/GTMNSBezierPath+RoundRect.m
@@ -24,14 +24,16 @@
@implementation NSBezierPath (GTMBezierPathRoundRectAdditions)
-+ (NSBezierPath *)gtm_bezierPathWithRoundRect:(NSRect)rect cornerRadius:(float)radius {
++ (NSBezierPath *)gtm_bezierPathWithRoundRect:(NSRect)rect
+ cornerRadius:(CGFloat)radius {
NSBezierPath *bezier = [NSBezierPath bezierPath];
[bezier gtm_appendBezierPathWithRoundRect:rect cornerRadius:radius];
return bezier;
}
-- (void)gtm_appendBezierPathWithRoundRect:(NSRect)rect cornerRadius:(float)radius {
+- (void)gtm_appendBezierPathWithRoundRect:(NSRect)rect
+ cornerRadius:(CGFloat)radius {
if (!NSIsEmptyRect(rect)) {
if (radius > 0.0) {
// Clamp radius to be no larger than half the rect's width or height.
@@ -42,10 +44,18 @@
NSPoint bottomRight = NSMakePoint(NSMaxX(rect), NSMinY(rect));
[self moveToPoint:NSMakePoint(NSMidX(rect), NSMaxY(rect))];
- [self appendBezierPathWithArcFromPoint:topLeft toPoint:rect.origin radius:radius];
- [self appendBezierPathWithArcFromPoint:rect.origin toPoint:bottomRight radius:radius];
- [self appendBezierPathWithArcFromPoint:bottomRight toPoint:topRight radius:radius];
- [self appendBezierPathWithArcFromPoint:topRight toPoint:topLeft radius:radius];
+ [self appendBezierPathWithArcFromPoint:topLeft
+ toPoint:rect.origin
+ radius:radius];
+ [self appendBezierPathWithArcFromPoint:rect.origin
+ toPoint:bottomRight
+ radius:radius];
+ [self appendBezierPathWithArcFromPoint:bottomRight
+ toPoint:topRight
+ radius:radius];
+ [self appendBezierPathWithArcFromPoint:topRight
+ toPoint:topLeft
+ radius:radius];
[self closePath];
} else {
// When radius <= 0.0, use plain rectangle.
diff --git a/AppKit/GTMNSBezierPath+RoundRectTest.m b/AppKit/GTMNSBezierPath+RoundRectTest.m
index 6ff4a18..c67c4b3 100644
--- a/AppKit/GTMNSBezierPath+RoundRectTest.m
+++ b/AppKit/GTMNSBezierPath+RoundRectTest.m
@@ -35,26 +35,26 @@
// Draws all of our tests so that we can compare this to our stored TIFF file.
- (void)gtm_unitTestViewDrawRect:(NSRect)rect contextInfo:(void*)contextInfo{
NSRect theRects[] = {
- NSMakeRect(0.0f, 10.0f, 0.0f, 0.0f), //Empty Rect test
- NSMakeRect(50.0f, 10.0f, 30.0f, 30.0f), //Square Test
- NSMakeRect(100.0f, 10.0f, 1.0f, 2.0f), //Small Test
- NSMakeRect(120.0f, 10.0f, 15.0f, 20.0f), //Medium Test
- NSMakeRect(140.0f, 10.0f, 150.0f, 30.0f) //Large Test
+ NSMakeRect(0.0, 10.0, 0.0, 0.0), //Empty Rect test
+ 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
};
- const unsigned int theRectCount = sizeof(theRects) / sizeof(NSRect);
+ const NSUInteger theRectCount = sizeof(theRects) / sizeof(NSRect);
// Line Width Tests
- float theLineWidths[] = { 0.5f, 50.0f, 2.0f };
- const unsigned int theLineWidthCount = sizeof(theLineWidths) / sizeof(float);
- unsigned int i,j;
+ CGFloat theLineWidths[] = { 0.5, 50.0, 2.0 };
+ const NSUInteger theLineWidthCount = sizeof(theLineWidths) / sizeof(CGFloat);
+ NSUInteger i,j;
for (i = 0; i < theLineWidthCount; ++i) {
for (j = 0; j < theRectCount; ++j) {
NSBezierPath *roundRect = [NSBezierPath gtm_bezierPathWithRoundRect:theRects[j]
- cornerRadius:20.0f];
+ cornerRadius:20.0];
[roundRect setLineWidth: theLineWidths[i]];
[roundRect stroke];
- float newWidth = 35.0f;
+ CGFloat newWidth = 35.0;
if (i < theLineWidthCount - 1) {
newWidth += theLineWidths[i + 1] + theLineWidths[i];
}
@@ -64,32 +64,32 @@
// Fill test
NSColor *theColors[] = {
- [NSColor colorWithCalibratedRed:1.0f green:0.0f blue:0.0f alpha:1.0f],
- [NSColor colorWithCalibratedRed:0.2f green:0.4f blue:0.6f alpha:0.4f]
+ [NSColor colorWithCalibratedRed:1.0 green:0.0 blue:0.0 alpha:1.0],
+ [NSColor colorWithCalibratedRed:0.2 green:0.4 blue:0.6 alpha:0.4]
};
- const unsigned int theColorCount = sizeof(theColors)/sizeof(NSColor);
+ const NSUInteger theColorCount = sizeof(theColors)/sizeof(NSColor);
for (i = 0; i < theColorCount; ++i) {
for (j = 0; j < theRectCount; ++j) {
NSBezierPath *roundRect = [NSBezierPath gtm_bezierPathWithRoundRect:theRects[j]
- cornerRadius:10.0f];
+ cornerRadius:10.0];
[theColors[i] setFill];
[roundRect fill];
- theRects[j].origin.y += 35.0f;
+ theRects[j].origin.y += 35.0;
}
}
// Flatness test
- float theFlatness[] = {0.0f, 0.1f, 1.0f, 10.0f};
- const unsigned int theFlatnessCount = sizeof(theFlatness)/sizeof(float);
+ CGFloat theFlatness[] = {0.0, 0.1, 1.0, 10.0};
+ const NSUInteger theFlatnessCount = sizeof(theFlatness)/sizeof(CGFloat);
for (i = 0; i < theFlatnessCount; i++) {
for (j = 0; j < theRectCount; ++j) {
NSBezierPath *roundRect = [NSBezierPath gtm_bezierPathWithRoundRect:theRects[j]
- cornerRadius:6.0f];
+ cornerRadius:6.0];
[roundRect setFlatness:theFlatness[i]];
[roundRect stroke];
- theRects[j].origin.y += 35.0f;
+ theRects[j].origin.y += 35.0;
}
}
}
diff --git a/AppKit/GTMNSBezierPath+Shading.h b/AppKit/GTMNSBezierPath+Shading.h
index b67043c..5d68728 100644
--- a/AppKit/GTMNSBezierPath+Shading.h
+++ b/AppKit/GTMNSBezierPath+Shading.h
@@ -19,6 +19,7 @@
//
#import <Cocoa/Cocoa.h>
+#import "GTMDefines.h"
@protocol GTMShading;
@@ -66,8 +67,8 @@
// last color in our shading?
// shading: the shading to use to take our colors from.
//
-- (void)gtm_strokeRadiallyFrom:(NSPoint)fromPoint fromRadius:(float)fromRadius
- to:(NSPoint)toPoint toRadius:(float)toRadius
+- (void)gtm_strokeRadiallyFrom:(NSPoint)fromPoint fromRadius:(CGFloat)fromRadius
+ to:(NSPoint)toPoint toRadius:(CGFloat)toRadius
extendingStart:(BOOL)extendingStart extendingEnd:(BOOL)extendingEnd
shading:(id<GTMShading>)shading;
@@ -113,8 +114,8 @@
// last color in our shading?
// shading: the shading to use to take our colors from.
//
-- (void)gtm_fillRadiallyFrom:(NSPoint)fromPoint fromRadius:(float)fromRadius
- to:(NSPoint)toPoint toRadius:(float)toRadius
+- (void)gtm_fillRadiallyFrom:(NSPoint)fromPoint fromRadius:(CGFloat)fromRadius
+ to:(NSPoint)toPoint toRadius:(CGFloat)toRadius
extendingStart:(BOOL)extendingStart extendingEnd:(BOOL)extendingEnd
shading:(id<GTMShading>)shading;
@end
diff --git a/AppKit/GTMNSBezierPath+Shading.m b/AppKit/GTMNSBezierPath+Shading.m
index f5935ab..4ab9ee3 100644
--- a/AppKit/GTMNSBezierPath+Shading.m
+++ b/AppKit/GTMNSBezierPath+Shading.m
@@ -22,6 +22,7 @@
#import "GTMNSBezierPath+CGPath.h"
#import "GTMShading.h"
#import "GTMGeometryUtils.h"
+#import "GTMMethodCheck.h"
@interface NSBezierPath (GTMBezierPathShadingAdditionsPrivate)
// Fills a CGPathRef either axially or radially with the given shading.
@@ -44,8 +45,8 @@
- (void)gtm_fillCGPath:(CGPathRef)path
axially:(BOOL)axially
asStroke:(BOOL)asStroke
- from:(NSPoint)fromPoint fromRadius:(float)fromRadius
- to:(NSPoint)toPoint toRadius:(float)toRadius
+ from:(NSPoint)fromPoint fromRadius:(CGFloat)fromRadius
+ to:(NSPoint)toPoint toRadius:(CGFloat)toRadius
extendingStart:(BOOL)extendingStart extendingEnd:(BOOL)extendingEnd
shading:(id<GTMShading>)shading;
@@ -62,7 +63,7 @@
// the projected point
- (NSPoint)gtm_projectLineFrom:(NSPoint)pointA
to:(NSPoint)pointB
- by:(float)length;
+ by:(CGFloat)length;
@end
@@ -70,8 +71,8 @@
- (void)gtm_fillCGPath:(CGPathRef)path
axially:(BOOL)axially asStroke:(BOOL)asStroke
- from:(NSPoint)fromPoint fromRadius:(float)fromRadius
- to:(NSPoint)toPoint toRadius:(float)toRadius
+ from:(NSPoint)fromPoint fromRadius:(CGFloat)fromRadius
+ to:(NSPoint)toPoint toRadius:(CGFloat)toRadius
extendingStart:(BOOL)extendingStart extendingEnd:(BOOL)extendingEnd
shading:(id<GTMShading>)shading {
CGFunctionRef shadingFunction = [shading shadeFunction];
@@ -79,15 +80,15 @@
CGContextRef currentContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
if (nil != currentContext) {
CGContextSaveGState(currentContext);
- float lineWidth = [self lineWidth];
- CGContextSetLineWidth(currentContext,lineWidth);
+ CGFloat lineWidth = [self lineWidth];
+ CGContextSetLineWidth(currentContext, lineWidth);
if (asStroke) {
// if we are using the stroke, we offset the from and to points
// by half the stroke width away from the center of the stroke.
// Otherwise we tend to end up with fills that only cover half of the
// because users set the start and end points based on the center
// of the stroke.
- float halfWidth = lineWidth * 0.5f;
+ CGFloat halfWidth = lineWidth * 0.5;
fromPoint = [self gtm_projectLineFrom:toPoint to:fromPoint by:halfWidth];
toPoint = [self gtm_projectLineFrom:fromPoint to:toPoint by:-halfWidth];
}
@@ -127,18 +128,24 @@
- (NSPoint)gtm_projectLineFrom:(NSPoint)pointA
to:(NSPoint)pointB
- by:(float)length {
+ by:(CGFloat)length {
NSPoint newPoint = pointB;
- float x = (pointB.x - pointA.x);
- float y = (pointB.y - pointA.y);
- if (x == 0.0f) {
+ CGFloat x = (pointB.x - pointA.x);
+ CGFloat y = (pointB.y - pointA.y);
+ if (x == 0.0) {
newPoint.y += length;
- } else if (y == 0.0f) {
+ } else if (y == 0.0) {
newPoint.x += length;
} else {
- float angle = atanf(y / x);
+#if CGFLOAT_IS_DOUBLE
+ CGFloat angle = atan(y / x);
+ newPoint.x += sin(angle) * length;
+ newPoint.y += cos(angle) * length;
+#else
+ CGFloat angle = atanf(y / x);
newPoint.x += sinf(angle) * length;
newPoint.y += cosf(angle) * length;
+#endif
}
return newPoint;
}
@@ -147,8 +154,7 @@
@implementation NSBezierPath (GTMBezierPathShadingAdditions)
-
-//METHOD_CHECK(NSBezierPath, gtm_createCGPath);
+GTM_METHOD_CHECK(NSBezierPath, gtm_createCGPath); // COV_NF_LINE
- (void)gtm_strokeAxiallyFrom:(NSPoint)fromPoint to:(NSPoint)toPoint
extendingStart:(BOOL)extendingStart extendingEnd:(BOOL)extendingEnd
@@ -156,8 +162,8 @@
CGPathRef thePath = [self gtm_createCGPath];
if (nil != thePath) {
[self gtm_fillCGPath:thePath axially:YES asStroke:YES
- from:fromPoint fromRadius:0.0f
- to:toPoint toRadius:0.0f
+ from:fromPoint fromRadius:(CGFloat)0.0
+ to:toPoint toRadius:(CGFloat)0.0
extendingStart:extendingStart extendingEnd:extendingEnd
shading:shading];
CGPathRelease(thePath);
@@ -165,8 +171,8 @@
}
-- (void)gtm_strokeRadiallyFrom:(NSPoint)fromPoint fromRadius:(float)fromRadius
- to:(NSPoint)toPoint toRadius:(float)toRadius
+- (void)gtm_strokeRadiallyFrom:(NSPoint)fromPoint fromRadius:(CGFloat)fromRadius
+ to:(NSPoint)toPoint toRadius:(CGFloat)toRadius
extendingStart:(BOOL)extendingStart extendingEnd:(BOOL)extendingEnd
shading:(id<GTMShading>)shading {
CGPathRef thePath = [self gtm_createCGPath];
@@ -187,8 +193,8 @@
CGPathRef thePath = [self gtm_createCGPath];
if (nil != thePath) {
[self gtm_fillCGPath:thePath axially:YES asStroke:NO
- from:fromPoint fromRadius:0.0f
- to:toPoint toRadius:0.0f
+ from:fromPoint fromRadius:(CGFloat)0.0
+ to:toPoint toRadius:(CGFloat)0.0
extendingStart:extendingStart extendingEnd:extendingEnd
shading:shading];
CGPathRelease(thePath);
@@ -196,8 +202,8 @@
}
-- (void)gtm_fillRadiallyFrom:(NSPoint)fromPoint fromRadius:(float)fromRadius
- to:(NSPoint)toPoint toRadius:(float)toRadius
+- (void)gtm_fillRadiallyFrom:(NSPoint)fromPoint fromRadius:(CGFloat)fromRadius
+ to:(NSPoint)toPoint toRadius:(CGFloat)toRadius
extendingStart:(BOOL)extendingStart extendingEnd:(BOOL)extendingEnd
shading:(id<GTMShading>)shading {
CGPathRef thePath = [self gtm_createCGPath];
diff --git a/AppKit/GTMNSBezierPath+ShadingTest.m b/AppKit/GTMNSBezierPath+ShadingTest.m
index 8b56cfb..9fa7a8f 100644
--- a/AppKit/GTMNSBezierPath+ShadingTest.m
+++ b/AppKit/GTMNSBezierPath+ShadingTest.m
@@ -40,13 +40,13 @@
[NSColor redColor], [NSColor yellowColor],
[NSColor blueColor], [NSColor greenColor],
[NSColor redColor] };
- float theFloatArray[] = { 0.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.00f };
+ CGFloat theFloatArray[] = { 0.0, 0.2, 0.4, 0.6, 0.8, 1.0 };
GTMLinearRGBShading *shading =
[GTMLinearRGBShading shadingWithColors:theColorArray
fromSpaceNamed:NSCalibratedRGBColorSpace
atPositions:theFloatArray
- count:sizeof(theFloatArray)/sizeof(float)];
+ count:sizeof(theFloatArray)/sizeof(CGFloat)];
NSBezierPath *shadedPath;
// axialStrokeRect test
diff --git a/AppKit/GTMNSColor+Theme.m b/AppKit/GTMNSColor+Theme.m
deleted file mode 100644
index 67e8459..0000000
--- a/AppKit/GTMNSColor+Theme.m
+++ /dev/null
@@ -1,62 +0,0 @@
-//
-// GTMNSColor+Theme.m
-//
-// Category for working with Themes and NSColor
-//
-// Copyright 2006-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 "GTMNSColor+Theme.h"
-#import "GTMDefines.h"
-
-@implementation NSColor (GTMColorThemeAdditions)
-
-/// Create up an NSColor based on a Theme Text Color
-/// Colors will be in the CalibratedRGB color space
-+ (id)gtm_colorWithThemeTextColor:(ThemeTextColor)textColor {
- NSColor *nsTextColor = nil;
- RGBColor rgbTextColor;
- OSStatus status = GetThemeTextColor(textColor, 32, true, &rgbTextColor);
- if (status == noErr) {
- float red = rgbTextColor.red / 65535.0f;
- float green = rgbTextColor.green / 65535.0f;
- float blue = rgbTextColor.blue / 65535.0f;
- nsTextColor = [NSColor colorWithCalibratedRed:red
- green:green
- blue:blue
- alpha:1.0f];
- } else {
- _GTMDevLog(@"Unable to create color for textcolor %d", textColor);
- }
- return nsTextColor;
-}
-
-/// Create up an NSColor based on a Theme Brush
-/// Colors will be in the CalibratedRGB color space
-+ (id)gtm_colorWithThemeBrush:(ThemeBrush)brush {
- NSColor *nsBrushColor = nil;
- RGBColor rgbBrushColor;
- OSStatus status = GetThemeBrushAsColor(brush, 32, true, &rgbBrushColor);
- if (status == noErr) {
- nsBrushColor = [NSColor colorWithCalibratedRed:rgbBrushColor.red / 65535.0f
- green:rgbBrushColor.green / 65535.0f
- blue:rgbBrushColor.blue / 65535.0f
- alpha:1.0f];
- } else {
- _GTMDevLog(@"Unable to create color for brushcolor %d", brush);
- }
- return nsBrushColor;
-}
-@end
diff --git a/AppKit/GTMNSColor+ThemeTest.m b/AppKit/GTMNSColor+ThemeTest.m
deleted file mode 100644
index e7d2a77..0000000
--- a/AppKit/GTMNSColor+ThemeTest.m
+++ /dev/null
@@ -1,199 +0,0 @@
-//
-// NSColor+ThemeTest.m
-//
-// Copyright 2006-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 <SenTestingKit/SenTestingKit.h>
-#import "GTMNSColor+Theme.h"
-#import "GTMNSWorkspace+Theme.h"
-#import "GTMSystemVersion.h"
-
-
-@interface GTMNSColor_ThemeTest : SenTestCase
-@end
-
-@implementation GTMNSColor_ThemeTest
-
-//METHOD_CHECK(NSWorkspace, themeAppearance);
-
-- (void)testColorWithThemeTextColor {
- float colorValues[][4] = {
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 0.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.499992, 0.499992, 0.499992, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.499992, 0.499992, 0.499992, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.499992, 0.499992, 0.499992, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.499992, 0.499992, 0.499992, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.499992, 0.499992, 0.499992, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.499992, 0.499992, 0.499992, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.499992, 0.499992, 0.499992, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.499992, 0.499992, 0.499992, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.449989, 0.449989, 0.449989, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.449989, 0.449989, 0.449989, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.499992, 0.499992, 0.499992, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.499992, 0.499992, 0.499992, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 0.599985, 0.599985, 0.599985, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 0.599985, 0.599985, 0.599985, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.499992, 0.499992, 0.499992, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.499992, 0.499992, 0.499992, 1.000000 },
- { 0.499992, 0.499992, 0.499992, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.499992, 0.499992, 0.499992, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 }
- };
-
- if ([GTMSystemVersion isLeopardOrGreater]) {
- // kThemeTextColorRootMenuDisabled changed to white in Leopard.
- colorValues[35][0] = 1.0;
- colorValues[35][1] = 1.0;
- colorValues[35][2] = 1.0;
- }
- for(int i = kThemeTextColorWhite; i < kThemeTextColorSystemDetail; i++) {
- if (i == 0) continue; // There is no brush 0
- NSColor *textColor = [NSColor gtm_colorWithThemeTextColor:i];
- float nsComponents[5];
- [textColor getComponents: nsComponents];
- for(int j = 0; j < 4; j++) {
- STAssertEqualsWithAccuracy(nsComponents[j], colorValues[i + 2][j], 0.000001,
- @"Theme Text Color %d is wrong", i);
- STAssertEqualObjects([textColor colorSpaceName], NSCalibratedRGBColorSpace,
- @"Color space must be CalibratedRGB");
- }
- }
-}
-
-- (void)testColorWithThemeBrushColor {
- float colorValues[][4] = {
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 0.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 0.980011, 0.990005, 0.990005, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.709789, 0.835294, 1.000000, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 0.492195, 0.675792, 0.847669, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.000000, 0.000000, 0.000000, 1.000000 },
- { 0.500008, 0.500008, 0.500008, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 0.600000, 0.600000, 0.600000, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 0.510002, 0.510002, 0.510002, 1.000000 },
- { 0.760006, 0.760006, 0.760006, 1.000000 },
- { 0.940002, 0.940002, 0.940002, 1.000000 },
- { 0.980011, 0.980011, 0.980011, 1.000000 },
- { 0.670008, 0.670008, 0.670008, 1.000000 },
- { 0.860014, 0.860014, 0.860014, 1.000000 },
- { 0.880003, 0.880003, 0.880003, 1.000000 },
- { 0.880003, 0.880003, 0.880003, 1.000000 },
- { 0.990005, 0.990005, 0.990005, 1.000000 },
- { 0.900008, 0.900008, 0.900008, 1.000000 },
- { 0.930007, 0.930007, 0.930007, 1.000000 },
- { 0.930007, 0.930007, 0.930007, 1.000000 },
- { 0.990005, 0.990005, 0.990005, 1.000000 },
- { 0.540002, 0.540002, 0.540002, 1.000000 },
- { 0.590005, 0.590005, 0.590005, 1.000000 },
- { 0.590005, 0.590005, 0.590005, 1.000000 },
- { 0.730007, 0.730007, 0.730007, 1.000000 },
- { 0.640009, 0.640009, 0.640009, 1.000000 },
- { 0.640009, 0.640009, 0.640009, 1.000000 },
- { 0.820005, 0.820005, 0.820005, 1.000000 },
- { 0.820005, 0.820005, 0.820005, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 1.000000, 1.000000, 1.000000, 1.000000 },
- { 0.925490, 0.952895, 0.996094, 1.000000 }
- };
-
- NSString *theme = [[NSWorkspace sharedWorkspace] gtm_themeAppearance];
- if ([theme isEqualToString:(NSString*)kThemeAppearanceAquaGraphite]) {
- // COV_NF_START
- // These are the only two brushes that change with an appearance change
- // In general we will be testing in blue, so this code won't get run
- colorValues[21][0] = 0.605478;
- colorValues[21][1] = 0.667979;
- colorValues[21][2] = 0.738293;
- colorValues[59][0] = 0.941192;
- colorValues[59][1] = 0.941192;
- colorValues[59][2] = 0.941192;
- // COV_NF_END
- }
- for(int i = kThemeBrushWhite; i < kThemeBrushListViewColumnDivider; i++) {
- // Brush "14" is the selection, so it will change depending on the system
- // There is no brush 0.
- if (i == 0 || i == 14) continue;
- NSColor *brushColor = [NSColor gtm_colorWithThemeBrush:i];
- float nsComponents[5];
- [brushColor getComponents: nsComponents];
- for(int j = 0; j < 4; j++) {
- STAssertEqualsWithAccuracy(nsComponents[j], colorValues[i + 2][j], 0.000001,
- @"Theme Text Brush %d is wrong", i + 2);
- STAssertEqualObjects([brushColor colorSpaceName], NSCalibratedRGBColorSpace,
- @"Color space must be CalibratedRB");
- }
- }
-}
-
-@end
diff --git a/AppKit/GTMNSWorkspace+ScreenSaver.m b/AppKit/GTMNSWorkspace+ScreenSaver.m
index c86a73c..9e1eb6a 100644
--- a/AppKit/GTMNSWorkspace+ScreenSaver.m
+++ b/AppKit/GTMNSWorkspace+ScreenSaver.m
@@ -77,8 +77,9 @@
BOOL answer = NO;
ScreenSaverController *controller = nil;
// We're calling into an "undocumented" framework here, so we are going to
- // step rather carefully.
+ // step rather carefully (and in 10.5.2 it's only 32bit).
+#if !__LP64__
Class screenSaverControllerClass = NSClassFromString(@"ScreenSaverController");
_GTMDevAssert(screenSaverControllerClass,
@"Are you linked with ScreenSaver.framework?"
@@ -96,6 +97,7 @@
}
}
}
+#endif // !__LP64__
if (!controller) {
// COV_NF_START
diff --git a/AppKit/GTMNSWorkspace+Theme.h b/AppKit/GTMNSWorkspace+Theme.h
deleted file mode 100644
index 364d623..0000000
--- a/AppKit/GTMNSWorkspace+Theme.h
+++ /dev/null
@@ -1,39 +0,0 @@
-//
-// GTMNSWorkspace+ScreenSaver.h
-//
-// Copyright 2007-2008 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy
-// of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-//
-
-#import <Cocoa/Cocoa.h>
-#import <Carbon/Carbon.h>
-
-enum {
- // This means that we had a failure getting the style
- kThemeScrollBarArrowsInvalid = -1,
- // This is the "missing" scroll bar arrow style that some people use
- kThemeScrollBarArrowsDouble = 2
-};
-
-/// Category for interacting with the screen saver
-@interface NSWorkspace (GTMWorkspaceThemeAddition)
-
-// returns one of the kThemeAppearance... constants as an autoreleased NSString
-// tells you whether we are running under blue or graphite
-- (NSString*)gtm_themeAppearance;
-
-// Returns how the user has their scroll bars configured.
-- (ThemeScrollBarArrowStyle)gtm_themeScrollBarArrowStyle;
-@end
-
diff --git a/AppKit/GTMNSWorkspace+Theme.m b/AppKit/GTMNSWorkspace+Theme.m
deleted file mode 100644
index 3afb801..0000000
--- a/AppKit/GTMNSWorkspace+Theme.m
+++ /dev/null
@@ -1,42 +0,0 @@
-//
-// GTMNSWorkspace+Theme.m
-//
-// Copyright 2007-2008 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy
-// of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-//
-
-#import "GTMNSWorkspace+Theme.h"
-#import "GTMGarbageCollection.h"
-
-@implementation NSWorkspace (GTMWorkspaceThemeAddition)
-
-- (NSString*)gtm_themeAppearance {
- CFStringRef cfTheme;
- NSString *nsTheme = nil;
- OSStatus theStatus = CopyThemeIdentifier(&cfTheme);
- if (theStatus == noErr) {
- nsTheme = [GTMNSMakeCollectable(cfTheme) autorelease];
- }
- return nsTheme;
-}
-
-- (ThemeScrollBarArrowStyle)gtm_themeScrollBarArrowStyle {
- ThemeScrollBarArrowStyle style = kThemeScrollBarArrowsInvalid;
- OSStatus theStatus = GetThemeScrollBarArrowStyle(&style);
- if (theStatus != noErr) {
- style = kThemeScrollBarArrowsInvalid; // COV_NF_LINE
- }
- return style;
-}
-@end
diff --git a/AppKit/GTMNSWorkspace+ThemeTest.m b/AppKit/GTMNSWorkspace+ThemeTest.m
deleted file mode 100644
index ae1cb66..0000000
--- a/AppKit/GTMNSWorkspace+ThemeTest.m
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-// NSWorkspace+ThemeTest.m
-//
-// Copyright 2007-2008 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy
-// of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-//
-
-#import <SenTestingKit/SenTestingKit.h>
-#import "GTMSenTestCase.h"
-#import "GTMNSWorkspace+Theme.h"
-
-@interface GTMNSWorkspace_ThemeTest : SenTestCase
-@end
-
-@implementation GTMNSWorkspace_ThemeTest
-
-- (void)testThemeAppearance {
- NSString *theme = [[NSWorkspace sharedWorkspace] gtm_themeAppearance];
- STAssertNotNil(theme, nil);
- STAssertTrue([theme hasPrefix:(NSString*)kThemeAppearanceAqua], nil);
-}
-
-- (void)testThemeScrollBarArrowStyle {
- ThemeScrollBarArrowStyle style = [[NSWorkspace sharedWorkspace] gtm_themeScrollBarArrowStyle];
- STAssertLessThanOrEqual(style, (ThemeScrollBarArrowStyle)kThemeScrollBarArrowsDouble, nil);
-}
-@end
diff --git a/AppKit/TestData/GTMNSBezierPath+CGPathTest.ppc64.tiff b/AppKit/TestData/GTMNSBezierPath+CGPathTest.ppc64.tiff
new file mode 100644
index 0000000..33c3e17
--- /dev/null
+++ b/AppKit/TestData/GTMNSBezierPath+CGPathTest.ppc64.tiff
Binary files differ
diff --git a/AppKit/GTMNSBezierPath+CGPathTest.tiff b/AppKit/TestData/GTMNSBezierPath+CGPathTest.tiff
index 98ec8f8..98ec8f8 100644
--- a/AppKit/GTMNSBezierPath+CGPathTest.tiff
+++ b/AppKit/TestData/GTMNSBezierPath+CGPathTest.tiff
Binary files differ
diff --git a/AppKit/TestData/GTMNSBezierPath+CGPathTest.x86_64.tiff b/AppKit/TestData/GTMNSBezierPath+CGPathTest.x86_64.tiff
new file mode 100644
index 0000000..33c3e17
--- /dev/null
+++ b/AppKit/TestData/GTMNSBezierPath+CGPathTest.x86_64.tiff
Binary files differ
diff --git a/AppKit/TestData/GTMNSBezierPath+RoundRectTest.ppc64.tiff b/AppKit/TestData/GTMNSBezierPath+RoundRectTest.ppc64.tiff
new file mode 100644
index 0000000..a4df8f8
--- /dev/null
+++ b/AppKit/TestData/GTMNSBezierPath+RoundRectTest.ppc64.tiff
Binary files differ
diff --git a/AppKit/GTMNSBezierPath+RoundRectTest.tiff b/AppKit/TestData/GTMNSBezierPath+RoundRectTest.tiff
index c41ab04..c41ab04 100644
--- a/AppKit/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
new file mode 100644
index 0000000..a4df8f8
--- /dev/null
+++ b/AppKit/TestData/GTMNSBezierPath+RoundRectTest.x86_64.tiff
Binary files differ
diff --git a/AppKit/GTMNSBezierPath+ShadingTest.10.5.tiff b/AppKit/TestData/GTMNSBezierPath+ShadingTest.10.5.tiff
index 040cb0d..040cb0d 100644
--- a/AppKit/GTMNSBezierPath+ShadingTest.10.5.tiff
+++ b/AppKit/TestData/GTMNSBezierPath+ShadingTest.10.5.tiff
Binary files differ
diff --git a/DebugUtils/GTMDebugSelectorValidation.h b/DebugUtils/GTMDebugSelectorValidation.h
index b3f1e73..82d00e8 100644
--- a/DebugUtils/GTMDebugSelectorValidation.h
+++ b/DebugUtils/GTMDebugSelectorValidation.h
@@ -40,39 +40,37 @@ static void GTMAssertSelectorNilOrImplementedWithArguments(id obj, SEL sel, ...)
if (obj && sel) {
// check that the selector is implemented
- if (![obj respondsToSelector:sel]) {
- _GTMDevAssert(NO,
- @"\"%@\" selector \"%@\" is unimplemented or misnamed",
- NSStringFromClass([obj class]),
- NSStringFromSelector(sel));
- } else {
- const char *expectedArgType;
- int argCount = 2; // skip self and _cmd
- NSMethodSignature *sig = [obj methodSignatureForSelector:sel];
-
- // check that each expected argument is present and of the correct type
- while ((expectedArgType = va_arg(argList, const char*)) != 0) {
+ _GTMDevAssert([obj respondsToSelector:sel],
+ @"\"%@\" selector \"%@\" is unimplemented or misnamed",
+ NSStringFromClass([obj class]),
+ NSStringFromSelector(sel));
+
+ const char *expectedArgType;
+ NSUInteger argCount = 2; // skip self and _cmd
+ NSMethodSignature *sig = [obj methodSignatureForSelector:sel];
+
+ // check that each expected argument is present and of the correct type
+ while ((expectedArgType = va_arg(argList, const char*)) != 0) {
- if ([sig numberOfArguments] > argCount) {
- const char *foundArgType = [sig getArgumentTypeAtIndex:argCount];
+ if ([sig numberOfArguments] > argCount) {
+ const char *foundArgType = [sig getArgumentTypeAtIndex:argCount];
- _GTMDevAssert(0 == strncmp(foundArgType, expectedArgType, strlen(expectedArgType)),
- @"\"%@\" selector \"%@\" argument %d should be type %s",
- NSStringFromClass([obj class]),
- NSStringFromSelector(sel),
- (argCount - 2),
- expectedArgType);
- }
- argCount++;
+ _GTMDevAssert(0 == strncmp(foundArgType, expectedArgType, strlen(expectedArgType)),
+ @"\"%@\" selector \"%@\" argument %d should be type %s",
+ NSStringFromClass([obj class]),
+ NSStringFromSelector(sel),
+ (argCount - 2),
+ expectedArgType);
}
-
- // check that the proper number of arguments are present in the selector
- _GTMDevAssert(argCount == [sig numberOfArguments],
- @"\"%@\" selector \"%@\" should have %d arguments",
- NSStringFromClass([obj class]),
- NSStringFromSelector(sel),
- (argCount - 2));
+ argCount++;
}
+
+ // check that the proper number of arguments are present in the selector
+ _GTMDevAssert(argCount == [sig numberOfArguments],
+ @"\"%@\" selector \"%@\" should have %d arguments",
+ NSStringFromClass([obj class]),
+ NSStringFromSelector(sel),
+ (argCount - 2));
}
va_end(argList);
diff --git a/DebugUtils/GTMMethodCheck.m b/DebugUtils/GTMMethodCheck.m
index 379f006..f91b1a9 100644
--- a/DebugUtils/GTMMethodCheck.m
+++ b/DebugUtils/GTMMethodCheck.m
@@ -47,11 +47,21 @@ static BOOL ConformsToNSObjectProtocol(Class cls) {
return YES;
}
+// iPhone SDK does not define the |Object| class, so we instead test for the
+// |NSObject| class.
+#if GTM_IPHONE_SDK
+ // Iterate through all the protocols |cls| supports looking for NSObject.
+ if (cls == [NSObject class]
+ || class_conformsToProtocol(cls, @protocol(NSObject))) {
+ return YES;
+ }
+#else
// Iterate through all the protocols |cls| supports looking for NSObject.
if (cls == [Object class]
|| class_conformsToProtocol(cls, @protocol(NSObject))) {
return YES;
}
+#endif
// Recursively check the superclasses.
return ConformsToNSObjectProtocol(class_getSuperclass(cls));
diff --git a/Foundation/GTMBase64.h b/Foundation/GTMBase64.h
new file mode 100644
index 0000000..169b1c3
--- /dev/null
+++ b/Foundation/GTMBase64.h
@@ -0,0 +1,183 @@
+//
+// GTMBase64.h
+//
+// Copyright 2006-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 <Foundation/Foundation.h>
+#import "GTMDefines.h"
+
+// GTMBase64
+//
+/// Helper for handling Base64 and WebSafeBase64 encodings
+//
+/// The webSafe methods use different character set and also the results aren't
+/// always padded to a multiple of 4 characters. This is done so the resulting
+/// data can be used in urls and url query arguments without needing any
+/// encoding. You must use the webSafe* methods together, the data does not
+/// interop with the RFC methods.
+//
+@interface GTMBase64 : NSObject
+
+//
+// Standard Base64 (RFC) handling
+//
+
+// encodeData:
+//
+/// Base64 encodes contents of the NSData object.
+//
+/// Returns:
+/// A new autoreleased NSData with the encoded payload. nil for any error.
+//
++(NSData *)encodeData:(NSData *)data;
+
+// decodeData:
+//
+/// Base64 decodes contents of the NSData object.
+//
+/// Returns:
+/// A new autoreleased NSData with the decoded payload. nil for any error.
+//
++(NSData *)decodeData:(NSData *)data;
+
+// encodeBytes:length:
+//
+/// Base64 encodes the data pointed at by |bytes|.
+//
+/// Returns:
+/// A new autoreleased NSData with the encoded payload. nil for any error.
+//
++(NSData *)encodeBytes:(const void *)bytes length:(NSUInteger)length;
+
+// decodeBytes:length:
+//
+/// Base64 decodes the data pointed at by |bytes|.
+//
+/// Returns:
+/// A new autoreleased NSData with the encoded payload. nil for any error.
+//
++(NSData *)decodeBytes:(const void *)bytes length:(NSUInteger)length;
+
+// stringByEncodingData:
+//
+/// Base64 encodes contents of the NSData object.
+//
+/// Returns:
+/// A new autoreleased NSString with the encoded payload. nil for any error.
+//
++(NSString *)stringByEncodingData:(NSData *)data;
+
+// stringByEncodingBytes:length:
+//
+/// Base64 encodes the data pointed at by |bytes|.
+//
+/// Returns:
+/// A new autoreleased NSString with the encoded payload. nil for any error.
+//
++(NSString *)stringByEncodingBytes:(const void *)bytes length:(NSUInteger)length;
+
+// decodeString:
+//
+/// Base64 decodes contents of the NSString.
+//
+/// Returns:
+/// A new autoreleased NSData with the decoded payload. nil for any error.
+//
++(NSData *)decodeString:(NSString *)string;
+
+//
+// Modified Base64 encoding so the results can go onto urls.
+//
+// The changes are in the characters generated and also allows the result to
+// not be padded to a multiple of 4.
+// Must use the matching call to encode/decode, won't interop with the
+// RFC versions.
+//
+
+// webSafeEncodeData:padded:
+//
+/// WebSafe Base64 encodes contents of the NSData object. If |padded| is YES
+/// then padding characters are added so the result length is a multiple of 4.
+//
+/// Returns:
+/// A new autoreleased NSData with the encoded payload. nil for any error.
+//
++(NSData *)webSafeEncodeData:(NSData *)data
+ padded:(BOOL)padded;
+
+// webSafeDecodeData:
+//
+/// WebSafe Base64 decodes contents of the NSData object.
+//
+/// Returns:
+/// A new autoreleased NSData with the decoded payload. nil for any error.
+//
++(NSData *)webSafeDecodeData:(NSData *)data;
+
+// webSafeEncodeBytes:length:padded:
+//
+/// WebSafe Base64 encodes the data pointed at by |bytes|. If |padded| is YES
+/// then padding characters are added so the result length is a multiple of 4.
+//
+/// Returns:
+/// A new autoreleased NSData with the encoded payload. nil for any error.
+//
++(NSData *)webSafeEncodeBytes:(const void *)bytes
+ length:(NSUInteger)length
+ padded:(BOOL)padded;
+
+// webSafeDecodeBytes:length:
+//
+/// WebSafe Base64 decodes the data pointed at by |bytes|.
+//
+/// Returns:
+/// A new autoreleased NSData with the encoded payload. nil for any error.
+//
++(NSData *)webSafeDecodeBytes:(const void *)bytes length:(NSUInteger)length;
+
+// stringByWebSafeEncodingData:padded:
+//
+/// WebSafe Base64 encodes contents of the NSData object. If |padded| is YES
+/// then padding characters are added so the result length is a multiple of 4.
+//
+/// Returns:
+/// A new autoreleased NSString with the encoded payload. nil for any error.
+//
++(NSString *)stringByWebSafeEncodingData:(NSData *)data
+ padded:(BOOL)padded;
+
+// stringByWebSafeEncodingBytes:length:padded:
+//
+/// WebSafe Base64 encodes the data pointed at by |bytes|. If |padded| is YES
+/// then padding characters are added so the result length is a multiple of 4.
+//
+/// Returns:
+/// A new autoreleased NSString with the encoded payload. nil for any error.
+//
++(NSString *)stringByWebSafeEncodingBytes:(const void *)bytes
+ length:(NSUInteger)length
+ padded:(BOOL)padded;
+
+// webSafeDecodeString:
+//
+/// WebSafe Base64 decodes contents of the NSString.
+//
+/// Returns:
+/// A new autoreleased NSData with the decoded payload. nil for any error.
+//
++(NSData *)webSafeDecodeString:(NSString *)string;
+
+@end
diff --git a/Foundation/GTMBase64.m b/Foundation/GTMBase64.m
new file mode 100644
index 0000000..06d0414
--- /dev/null
+++ b/Foundation/GTMBase64.m
@@ -0,0 +1,697 @@
+//
+// GTMBase64.m
+//
+// Copyright 2006-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 "GTMBase64.h"
+#import "GTMDefines.h"
+
+static const char *kBase64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char *kWebSafeBase64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
+static const char kBase64PaddingChar = '=';
+static const char kBase64InvalidChar = 99;
+
+static const char kBase64DecodeChars[] = {
+ // This array was generated by the following code:
+ // #include <sys/time.h>
+ // #include <stdlib.h>
+ // #include <string.h>
+ // main()
+ // {
+ // static const char Base64[] =
+ // "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ // char *pos;
+ // int idx, i, j;
+ // printf(" ");
+ // for (i = 0; i < 255; i += 8) {
+ // for (j = i; j < i + 8; j++) {
+ // pos = strchr(Base64, j);
+ // if ((pos == NULL) || (j == 0))
+ // idx = 99;
+ // else
+ // idx = pos - Base64;
+ // if (idx == 99)
+ // printf(" %2d, ", idx);
+ // else
+ // printf(" %2d/*%c*/,", idx, j);
+ // }
+ // printf("\n ");
+ // }
+ // }
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 62/*+*/, 99, 99, 99, 63/*/ */,
+ 52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/,
+ 60/*8*/, 61/*9*/, 99, 99, 99, 99, 99, 99,
+ 99, 0/*A*/, 1/*B*/, 2/*C*/, 3/*D*/, 4/*E*/, 5/*F*/, 6/*G*/,
+ 7/*H*/, 8/*I*/, 9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/,
+ 15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/,
+ 23/*X*/, 24/*Y*/, 25/*Z*/, 99, 99, 99, 99, 99,
+ 99, 26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/,
+ 33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/,
+ 41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/,
+ 49/*x*/, 50/*y*/, 51/*z*/, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99
+};
+
+static const char kWebSafeBase64DecodeChars[] = {
+ // This array was generated by the following code:
+ // #include <sys/time.h>
+ // #include <stdlib.h>
+ // #include <string.h>
+ // main()
+ // {
+ // static const char Base64[] =
+ // "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
+ // char *pos;
+ // int idx, i, j;
+ // printf(" ");
+ // for (i = 0; i < 255; i += 8) {
+ // for (j = i; j < i + 8; j++) {
+ // pos = strchr(Base64, j);
+ // if ((pos == NULL) || (j == 0))
+ // idx = 99;
+ // else
+ // idx = pos - Base64;
+ // if (idx == 99)
+ // printf(" %2d, ", idx);
+ // else
+ // printf(" %2d/*%c*/,", idx, j);
+ // }
+ // printf("\n ");
+ // }
+ // }
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 62/*-*/, 99, 99,
+ 52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/,
+ 60/*8*/, 61/*9*/, 99, 99, 99, 99, 99, 99,
+ 99, 0/*A*/, 1/*B*/, 2/*C*/, 3/*D*/, 4/*E*/, 5/*F*/, 6/*G*/,
+ 7/*H*/, 8/*I*/, 9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/,
+ 15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/,
+ 23/*X*/, 24/*Y*/, 25/*Z*/, 99, 99, 99, 99, 63/*_*/,
+ 99, 26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/,
+ 33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/,
+ 41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/,
+ 49/*x*/, 50/*y*/, 51/*z*/, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99
+};
+
+
+// Tests a charact to see if it's a whitespace character.
+//
+// Returns:
+// YES if the character is a whitespace character.
+// NO if the character is not a whitespace character.
+//
+FOUNDATION_STATIC_INLINE BOOL IsSpace(unsigned char c) {
+ // we use our own mapping here because we don't want anything w/ locale
+ // support.
+ static BOOL kSpaces[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, // 0-9
+ 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 10-19
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20-29
+ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 30-39
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40-49
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 50-59
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60-69
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 70-79
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80-89
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 90-99
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 100-109
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 110-119
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 120-129
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 130-139
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 140-149
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 150-159
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160-169
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 170-179
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 180-189
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 190-199
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 200-209
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 210-219
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 220-229
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 230-239
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 240-249
+ 0, 0, 0, 0, 0, 1, // 250-255
+ };
+ return kSpaces[c];
+}
+
+// Calculate how long the data will be once it's base64 encoded.
+//
+// Returns:
+// The guessed encoded length for a source length
+//
+FOUNDATION_STATIC_INLINE NSUInteger CalcEncodedLength(NSUInteger srcLen,
+ BOOL padded) {
+ NSUInteger intermediate_result = 8 * srcLen + 5;
+ NSUInteger len = intermediate_result / 6;
+ if (padded) {
+ len = ((len + 3) / 4) * 4;
+ }
+ return len;
+}
+
+// Tries to calculate how long the data will be once it's base64 decoded.
+// Unlinke the above, this is always an upperbound, since the source data
+// could have spaces and might end with the padding characters on them.
+//
+// Returns:
+// The guessed decoded length for a source length
+//
+FOUNDATION_STATIC_INLINE NSUInteger GuessDecodedLength(NSUInteger srcLen) {
+ return (srcLen + 3) / 4 * 3;
+}
+
+
+@interface GTMBase64 (PrivateMethods)
+
++(NSData *)baseEncode:(const void *)bytes
+ length:(NSUInteger)length
+ charset:(const char *)charset
+ padded:(BOOL)padded;
+
++(NSData *)baseDecode:(const void *)bytes
+ length:(NSUInteger)length
+ charset:(const char*)charset
+ requirePadding:(BOOL)requirePadding;
+
++(NSUInteger)baseEncode:(const char *)srcBytes
+ srcLen:(NSUInteger)srcLen
+ destBytes:(char *)destBytes
+ destLen:(NSUInteger)destLen
+ charset:(const char *)charset
+ padded:(BOOL)padded;
+
++(NSUInteger)baseDecode:(const char *)srcBytes
+ srcLen:(NSUInteger)srcLen
+ destBytes:(char *)destBytes
+ destLen:(NSUInteger)destLen
+ charset:(const char *)charset
+ requirePadding:(BOOL)requirePadding;
+
+@end
+
+
+@implementation GTMBase64
+
+//
+// Standard Base64 (RFC) handling
+//
+
++(NSData *)encodeData:(NSData *)data {
+ return [self baseEncode:[data bytes]
+ length:[data length]
+ charset:kBase64EncodeChars
+ padded:YES];
+}
+
++(NSData *)decodeData:(NSData *)data {
+ return [self baseDecode:[data bytes]
+ length:[data length]
+ charset:kBase64DecodeChars
+ requirePadding:YES];
+}
+
++(NSData *)encodeBytes:(const void *)bytes length:(NSUInteger)length {
+ return [self baseEncode:bytes
+ length:length
+ charset:kBase64EncodeChars
+ padded:YES];
+}
+
++(NSData *)decodeBytes:(const void *)bytes length:(NSUInteger)length {
+ return [self baseDecode:bytes
+ length:length
+ charset:kBase64DecodeChars
+ requirePadding:YES];
+}
+
++(NSString *)stringByEncodingData:(NSData *)data {
+ NSString *result = nil;
+ NSData *converted = [self baseEncode:[data bytes]
+ length:[data length]
+ charset:kBase64EncodeChars
+ padded:YES];
+ if (converted) {
+ result = [[[NSString alloc] initWithData:converted
+ encoding:NSASCIIStringEncoding] autorelease];
+ }
+ return result;
+}
+
++(NSString *)stringByEncodingBytes:(const void *)bytes length:(NSUInteger)length {
+ NSString *result = nil;
+ NSData *converted = [self baseEncode:bytes
+ length:length
+ charset:kBase64EncodeChars
+ padded:YES];
+ if (converted) {
+ result = [[[NSString alloc] initWithData:converted
+ encoding:NSASCIIStringEncoding] autorelease];
+ }
+ return result;
+}
+
++(NSData *)decodeString:(NSString *)string {
+ NSData *result = nil;
+ NSData *data = [string dataUsingEncoding:NSASCIIStringEncoding];
+ if (data) {
+ result = [self baseDecode:[data bytes]
+ length:[data length]
+ charset:kBase64DecodeChars
+ requirePadding:YES];
+ }
+ return result;
+}
+
+//
+// Modified Base64 encoding so the results can go onto urls.
+//
+// The changes are in the characters generated and also the result isn't
+// padded to a multiple of 4.
+// Must use the matching call to encode/decode, won't interop with the
+// RFC versions.
+//
+
++(NSData *)webSafeEncodeData:(NSData *)data
+ padded:(BOOL)padded {
+ return [self baseEncode:[data bytes]
+ length:[data length]
+ charset:kWebSafeBase64EncodeChars
+ padded:padded];
+}
+
++(NSData *)webSafeDecodeData:(NSData *)data {
+ return [self baseDecode:[data bytes]
+ length:[data length]
+ charset:kWebSafeBase64DecodeChars
+ requirePadding:NO];
+}
+
++(NSData *)webSafeEncodeBytes:(const void *)bytes
+ length:(NSUInteger)length
+ padded:(BOOL)padded {
+ return [self baseEncode:bytes
+ length:length
+ charset:kWebSafeBase64EncodeChars
+ padded:padded];
+}
+
++(NSData *)webSafeDecodeBytes:(const void *)bytes length:(NSUInteger)length {
+ return [self baseDecode:bytes
+ length:length
+ charset:kWebSafeBase64DecodeChars
+ requirePadding:NO];
+}
+
++(NSString *)stringByWebSafeEncodingData:(NSData *)data
+ padded:(BOOL)padded {
+ NSString *result = nil;
+ NSData *converted = [self baseEncode:[data bytes]
+ length:[data length]
+ charset:kWebSafeBase64EncodeChars
+ padded:padded];
+ if (converted) {
+ result = [[[NSString alloc] initWithData:converted
+ encoding:NSASCIIStringEncoding] autorelease];
+ }
+ return result;
+}
+
++(NSString *)stringByWebSafeEncodingBytes:(const void *)bytes
+ length:(NSUInteger)length
+ padded:(BOOL)padded {
+ NSString *result = nil;
+ NSData *converted = [self baseEncode:bytes
+ length:length
+ charset:kWebSafeBase64EncodeChars
+ padded:padded];
+ if (converted) {
+ result = [[[NSString alloc] initWithData:converted
+ encoding:NSASCIIStringEncoding] autorelease];
+ }
+ return result;
+}
+
++(NSData *)webSafeDecodeString:(NSString *)string {
+ NSData *result = nil;
+ NSData *data = [string dataUsingEncoding:NSASCIIStringEncoding];
+ if (data) {
+ result = [self baseDecode:[data bytes]
+ length:[data length]
+ charset:kWebSafeBase64DecodeChars
+ requirePadding:NO];
+ }
+ return result;
+}
+
+@end
+
+@implementation GTMBase64 (PrivateMethods)
+
+//
+// baseEncode:length:charset:padded:
+//
+// Does the common lifting of creating the dest NSData. it creates & sizes the
+// data for the results. |charset| is the characters to use for the encoding
+// of the data. |padding| controls if the encoded data should be padded to a
+// multiple of 4.
+//
+// Returns:
+// an autorelease NSData with the encoded data, nil if any error.
+//
++(NSData *)baseEncode:(const void *)bytes
+ length:(NSUInteger)length
+ charset:(const char *)charset
+ padded:(BOOL)padded {
+ // how big could it be?
+ NSUInteger maxLength = CalcEncodedLength(length, padded);
+ // make space
+ NSMutableData *result = [NSMutableData data];
+ [result setLength:maxLength];
+ // do it
+ NSUInteger finalLength = [self baseEncode:bytes
+ srcLen:length
+ destBytes:[result mutableBytes]
+ destLen:[result length]
+ charset:charset
+ padded:padded];
+ if (finalLength) {
+ _GTMDevAssert(finalLength == maxLength, @"how did we calc the length wrong?");
+ } else {
+ // shouldn't happen, this means we ran out of space
+ result = nil;
+ }
+ return result;
+}
+
+//
+// baseDecode:length:charset:requirePadding:
+//
+// Does the common lifting of creating the dest NSData. it creates & sizes the
+// data for the results. |charset| is the characters to use for the decoding
+// of the data.
+//
+// Returns:
+// an autorelease NSData with the decoded data, nil if any error.
+//
+//
++(NSData *)baseDecode:(const void *)bytes
+ length:(NSUInteger)length
+ charset:(const char *)charset
+ requirePadding:(BOOL)requirePadding {
+ // could try to calculate what it will end up as
+ NSUInteger maxLength = GuessDecodedLength(length);
+ // make space
+ NSMutableData *result = [NSMutableData data];
+ [result setLength:maxLength];
+ // do it
+ NSUInteger finalLength = [self baseDecode:bytes
+ srcLen:length
+ destBytes:[result mutableBytes]
+ destLen:[result length]
+ charset:charset
+ requirePadding:requirePadding];
+ if (finalLength) {
+ if (finalLength != maxLength) {
+ // resize down to how big it was
+ [result setLength:finalLength];
+ }
+ } else {
+ // either an error in the args, or we ran out of space
+ result = nil;
+ }
+ return result;
+}
+
+//
+// baseEncode:srcLen:destBytes:destLen:charset:padded:
+//
+// Encodes the buffer into the larger. returns the length of the encoded
+// data, or zero for an error.
+// |charset| is the characters to use for the encoding
+// |padded| tells if the result should be padded to a multiple of 4.
+//
+// Returns:
+// the length of the encoded data. zero if any error.
+//
++(NSUInteger)baseEncode:(const char *)srcBytes
+ srcLen:(NSUInteger)srcLen
+ destBytes:(char *)destBytes
+ destLen:(NSUInteger)destLen
+ charset:(const char *)charset
+ padded:(BOOL)padded {
+ if (!srcLen || !destLen || !srcBytes || !destBytes) {
+ return 0;
+ }
+
+ char *curDest = destBytes;
+ const unsigned char *curSrc = (const unsigned char *)(srcBytes);
+
+ // Three bytes of data encodes to four characters of cyphertext.
+ // So we can pump through three-byte chunks atomically.
+ while (srcLen > 2) {
+ // space?
+ _GTMDevAssert(destLen >= 4, @"our calc for encoded length was wrong");
+ curDest[0] = charset[curSrc[0] >> 2];
+ curDest[1] = charset[((curSrc[0] & 0x03) << 4) + (curSrc[1] >> 4)];
+ curDest[2] = charset[((curSrc[1] & 0x0f) << 2) + (curSrc[2] >> 6)];
+ curDest[3] = charset[curSrc[2] & 0x3f];
+
+ curDest += 4;
+ curSrc += 3;
+ srcLen -= 3;
+ destLen -= 4;
+ }
+
+ // now deal with the tail (<=2 bytes)
+ switch (srcLen) {
+ case 0:
+ // Nothing left; nothing more to do.
+ break;
+ case 1:
+ // One byte left: this encodes to two characters, and (optionally)
+ // two pad characters to round out the four-character cypherblock.
+ _GTMDevAssert(destLen >= 2, @"our calc for encoded length was wrong");
+ curDest[0] = charset[curSrc[0] >> 2];
+ curDest[1] = charset[(curSrc[0] & 0x03) << 4];
+ curDest += 2;
+ destLen -= 2;
+ if (padded) {
+ _GTMDevAssert(destLen >= 2, @"our calc for encoded length was wrong");
+ curDest[0] = kBase64PaddingChar;
+ curDest[1] = kBase64PaddingChar;
+ curDest += 2;
+ destLen -= 2;
+ }
+ break;
+ case 2:
+ // Two bytes left: this encodes to three characters, and (optionally)
+ // one pad character to round out the four-character cypherblock.
+ _GTMDevAssert(destLen >= 3, @"our calc for encoded length was wrong");
+ curDest[0] = charset[curSrc[0] >> 2];
+ curDest[1] = charset[((curSrc[0] & 0x03) << 4) + (curSrc[1] >> 4)];
+ curDest[2] = charset[(curSrc[1] & 0x0f) << 2];
+ curDest += 3;
+ destLen -= 3;
+ if (padded) {
+ _GTMDevAssert(destLen >= 1, @"our calc for encoded length was wrong");
+ curDest[0] = kBase64PaddingChar;
+ curDest += 1;
+ destLen -= 1;
+ }
+ break;
+ }
+ // return the length
+ return (curDest - destBytes);
+}
+
+//
+// baseDecode:srcLen:destBytes:destLen:charset:requirePadding:
+//
+// Decodes the buffer into the larger. returns the length of the decoded
+// data, or zero for an error.
+// |charset| is the character decoding buffer to use
+//
+// Returns:
+// the length of the encoded data. zero if any error.
+//
++(NSUInteger)baseDecode:(const char *)srcBytes
+ srcLen:(NSUInteger)srcLen
+ destBytes:(char *)destBytes
+ destLen:(NSUInteger)destLen
+ charset:(const char *)charset
+ requirePadding:(BOOL)requirePadding {
+ if (!srcLen || !destLen || !srcBytes || !destBytes) {
+ return 0;
+ }
+
+ int decode;
+ NSUInteger destIndex = 0;
+ int state = 0;
+ char ch = 0;
+ while (srcLen-- && (ch = *srcBytes++) != 0) {
+ if (IsSpace(ch)) // Skip whitespace
+ continue;
+
+ if (ch == kBase64PaddingChar)
+ break;
+
+ decode = charset[(unsigned int)ch];
+ if (decode == kBase64InvalidChar)
+ return 0;
+
+ // Four cyphertext characters decode to three bytes.
+ // Therefore we can be in one of four states.
+ switch (state) {
+ case 0:
+ // We're at the beginning of a four-character cyphertext block.
+ // This sets the high six bits of the first byte of the
+ // plaintext block.
+ _GTMDevAssert(destIndex < destLen, @"our calc for decoded length was wrong");
+ destBytes[destIndex] = decode << 2;
+ state = 1;
+ break;
+ case 1:
+ // We're one character into a four-character cyphertext block.
+ // This sets the low two bits of the first plaintext byte,
+ // and the high four bits of the second plaintext byte.
+ _GTMDevAssert((destIndex+1) < destLen, @"our calc for decoded length was wrong");
+ destBytes[destIndex] |= decode >> 4;
+ destBytes[destIndex+1] = (decode & 0x0f) << 4;
+ destIndex++;
+ state = 2;
+ break;
+ case 2:
+ // We're two characters into a four-character cyphertext block.
+ // This sets the low four bits of the second plaintext
+ // byte, and the high two bits of the third plaintext byte.
+ // However, if this is the end of data, and those two
+ // bits are zero, it could be that those two bits are
+ // leftovers from the encoding of data that had a length
+ // of two mod three.
+ _GTMDevAssert((destIndex+1) < destLen, @"our calc for decoded length was wrong");
+ destBytes[destIndex] |= decode >> 2;
+ destBytes[destIndex+1] = (decode & 0x03) << 6;
+ destIndex++;
+ state = 3;
+ break;
+ case 3:
+ // We're at the last character of a four-character cyphertext block.
+ // This sets the low six bits of the third plaintext byte.
+ _GTMDevAssert(destIndex < destLen, @"our calc for decoded length was wrong");
+ destBytes[destIndex] |= decode;
+ destIndex++;
+ state = 0;
+ break;
+ }
+ }
+
+ // We are done decoding Base-64 chars. Let's see if we ended
+ // on a byte boundary, and/or with erroneous trailing characters.
+ if (ch == kBase64PaddingChar) { // We got a pad char
+ if ((state == 0) || (state == 1)) {
+ return 0; // Invalid '=' in first or second position
+ }
+ if (srcLen == 0) {
+ if (state == 2) { // We run out of input but we still need another '='
+ return 0;
+ }
+ // Otherwise, we are in state 3 and only need this '='
+ } else {
+ if (state == 2) { // need another '='
+ while ((ch = *srcBytes++) && (srcLen-- > 0)) {
+ if (!IsSpace(ch))
+ break;
+ }
+ if (ch != kBase64PaddingChar) {
+ return 0;
+ }
+ }
+ // state = 1 or 2, check if all remain padding is space
+ while ((ch = *srcBytes++) && (srcLen-- > 0)) {
+ if (!IsSpace(ch)) {
+ return 0;
+ }
+ }
+ }
+ } else {
+ // We ended by seeing the end of the string.
+
+ if (requirePadding) {
+ // If we require padding, then anything but state 0 is an error.
+ if (state != 0) {
+ return 0;
+ }
+ } else {
+ // Make sure we have no partial bytes lying around. Note that we do not
+ // require trailing '=', so states 2 and 3 are okay too.
+ if (state == 1) {
+ return 0;
+ }
+ }
+ }
+
+ // If then next piece of output was valid and got written to it means we got a
+ // very carefully crafted input that appeared valid but contains some trailing
+ // bits past the real length, so just toss the thing.
+ if ((destIndex < destLen) &&
+ (destBytes[destIndex] != 0)) {
+ return 0;
+ }
+
+ return destIndex;
+}
+
+@end
diff --git a/Foundation/GTMBase64Test.m b/Foundation/GTMBase64Test.m
new file mode 100644
index 0000000..358c6ec
--- /dev/null
+++ b/Foundation/GTMBase64Test.m
@@ -0,0 +1,437 @@
+//
+// GTMBase64Test.m
+//
+// Copyright 2006-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 "GTMBase64.h"
+#include <stdlib.h> // for randiom/srandomdev
+
+static void FillWithRandom(char *data, NSUInteger len) {
+ char *max = data + len;
+ for ( ; data < max ; ++data) {
+ *data = random() & 0xFF;
+ }
+}
+
+static BOOL NoEqualChar(NSData *data) {
+ const char *scan = [data bytes];
+ const char *max = scan + [data length];
+ for ( ; scan < max ; ++scan) {
+ if (*scan == '=') {
+ return NO;
+ }
+ }
+ return YES;
+}
+
+@interface GTMBase64Test : SenTestCase
+@end
+
+@implementation GTMBase64Test
+
+- (void)setUp {
+ // seed random from /dev/random
+ srandomdev();
+}
+
+- (void)testBase64 {
+ // generate a range of sizes w/ random content
+ for (int x = 1 ; x < 1024 ; ++x) {
+ NSMutableData *data = [NSMutableData data];
+ STAssertNotNil(data, @"failed to alloc data block");
+
+ [data setLength:x];
+ FillWithRandom([data mutableBytes], [data length]);
+
+ // w/ *Bytes apis
+ NSData *encoded = [GTMBase64 encodeBytes:[data bytes] length:[data length]];
+ STAssertEquals(([encoded length] % 4), (NSUInteger)0,
+ @"encoded size via *Bytes apis should be a multiple of 4");
+ NSData *dataPrime = [GTMBase64 decodeBytes:[encoded bytes]
+ length:[encoded length]];
+ STAssertEqualObjects(data, dataPrime,
+ @"failed to round trip via *Bytes apis");
+
+ // w/ *Data apis
+ encoded = [GTMBase64 encodeData:data];
+ STAssertEquals(([encoded length] % 4), (NSUInteger)0,
+ @"encoded size via *Data apis should be a multiple of 4");
+ dataPrime = [GTMBase64 decodeData:encoded];
+ STAssertEqualObjects(data, dataPrime,
+ @"failed to round trip via *Data apis");
+
+ // Bytes to String and back
+ NSString *encodedString = [GTMBase64 stringByEncodingBytes:[data bytes]
+ length:[data length]];
+ STAssertEquals(([encodedString length] % 4), (NSUInteger)0,
+ @"encoded size for Bytes to Strings should be a multiple of 4");
+ dataPrime = [GTMBase64 decodeString:encodedString];
+ STAssertEqualObjects(data, dataPrime,
+ @"failed to round trip for Bytes to Strings");
+
+ // Data to String and back
+ encodedString = [GTMBase64 stringByEncodingData:data];
+ STAssertEquals(([encodedString length] % 4), (NSUInteger)0,
+ @"encoded size for Data to Strings should be a multiple of 4");
+ dataPrime = [GTMBase64 decodeString:encodedString];
+ STAssertEqualObjects(data, dataPrime,
+ @"failed to round trip for Bytes to Strings");
+ }
+
+ {
+ // now test all byte values
+ NSMutableData *data = [NSMutableData data];
+ STAssertNotNil(data, @"failed to alloc data block");
+
+ [data setLength:256];
+ unsigned char *scan = (unsigned char*)[data mutableBytes];
+ for (int x = 0 ; x <= 255 ; ++x) {
+ *scan++ = x;
+ }
+
+ // w/ *Bytes apis
+ NSData *encoded = [GTMBase64 encodeBytes:[data bytes] length:[data length]];
+ STAssertEquals(([encoded length] % 4), (NSUInteger)0,
+ @"encoded size via *Bytes apis should be a multiple of 4");
+ NSData *dataPrime = [GTMBase64 decodeBytes:[encoded bytes]
+ length:[encoded length]];
+ STAssertEqualObjects(data, dataPrime,
+ @"failed to round trip via *Bytes apis");
+
+ // w/ *Data apis
+ encoded = [GTMBase64 encodeData:data];
+ STAssertEquals(([encoded length] % 4), (NSUInteger)0,
+ @"encoded size via *Data apis should be a multiple of 4");
+ dataPrime = [GTMBase64 decodeData:encoded];
+ STAssertEqualObjects(data, dataPrime,
+ @"failed to round trip via *Data apis");
+
+ // Bytes to String and back
+ NSString *encodedString = [GTMBase64 stringByEncodingBytes:[data bytes]
+ length:[data length]];
+ STAssertEquals(([encodedString length] % 4), (NSUInteger)0,
+ @"encoded size for Bytes to Strings should be a multiple of 4");
+ dataPrime = [GTMBase64 decodeString:encodedString];
+ STAssertEqualObjects(data, dataPrime,
+ @"failed to round trip for Bytes to Strings");
+
+ // Data to String and back
+ encodedString = [GTMBase64 stringByEncodingData:data];
+ STAssertEquals(([encodedString length] % 4), (NSUInteger)0,
+ @"encoded size for Data to Strings should be a multiple of 4");
+ dataPrime = [GTMBase64 decodeString:encodedString];
+ STAssertEqualObjects(data, dataPrime,
+ @"failed to round trip for Data to Strings");
+ }
+
+ {
+ // test w/ a mix of spacing characters
+
+ // generate some data, encode it, and add spaces
+ NSMutableData *data = [NSMutableData data];
+ STAssertNotNil(data, @"failed to alloc data block");
+
+ [data setLength:253]; // should get some padding chars on the end
+ FillWithRandom([data mutableBytes], [data length]);
+
+ NSString *encodedString = [GTMBase64 stringByEncodingData:data];
+ NSMutableString *encodedAndSpaced =
+ [[encodedString mutableCopy] autorelease];
+
+ NSString *spaces[] = { @"\t", @"\n", @"\r", @" " };
+ const NSUInteger numSpaces = sizeof(spaces) / sizeof(NSString*);
+ for (int x = 0 ; x < 512 ; ++x) {
+ NSUInteger offset = random() % ([encodedAndSpaced length] + 1);
+ [encodedAndSpaced insertString:spaces[random() % numSpaces]
+ atIndex:offset];
+ }
+
+ // we'll need it as data for apis
+ NSData *encodedAsData =
+ [encodedAndSpaced dataUsingEncoding:NSASCIIStringEncoding];
+ STAssertNotNil(encodedAsData, @"failed to extract from string");
+ STAssertEquals([encodedAsData length], [encodedAndSpaced length],
+ @"lengths for encoded string and data didn't match?");
+
+ // all the decode modes
+ NSData *dataPrime = [GTMBase64 decodeData:encodedAsData];
+ STAssertEqualObjects(data, dataPrime,
+ @"failed Data decode w/ spaces");
+ dataPrime = [GTMBase64 decodeBytes:[encodedAsData bytes]
+ length:[encodedAsData length]];
+ STAssertEqualObjects(data, dataPrime,
+ @"failed Bytes decode w/ spaces");
+ dataPrime = [GTMBase64 decodeString:encodedAndSpaced];
+ STAssertEqualObjects(data, dataPrime,
+ @"failed String decode w/ spaces");
+ }
+}
+
+- (void)testWebSafeBase64 {
+ // loop to test w/ and w/o padding
+ for (int paddedLoop = 0; paddedLoop < 2 ; ++paddedLoop) {
+ BOOL padded = (paddedLoop == 1);
+
+ // generate a range of sizes w/ random content
+ for (int x = 1 ; x < 1024 ; ++x) {
+ NSMutableData *data = [NSMutableData data];
+ STAssertNotNil(data, @"failed to alloc data block");
+
+ [data setLength:x];
+ FillWithRandom([data mutableBytes], [data length]);
+
+ // w/ *Bytes apis
+ NSData *encoded = [GTMBase64 webSafeEncodeBytes:[data bytes]
+ length:[data length]
+ padded:padded];
+ if (padded) {
+ STAssertEquals(([encoded length] % 4), (NSUInteger)0,
+ @"encoded size via *Bytes apis should be a multiple of 4");
+ } else {
+ STAssertTrue(NoEqualChar(encoded),
+ @"encoded via *Bytes apis had a base64 padding char");
+ }
+ NSData *dataPrime = [GTMBase64 webSafeDecodeBytes:[encoded bytes]
+ length:[encoded length]];
+ STAssertEqualObjects(data, dataPrime,
+ @"failed to round trip via *Bytes apis");
+
+ // w/ *Data apis
+ encoded = [GTMBase64 webSafeEncodeData:data padded:padded];
+ if (padded) {
+ STAssertEquals(([encoded length] % 4), (NSUInteger)0,
+ @"encoded size via *Data apis should be a multiple of 4");
+ } else {
+ STAssertTrue(NoEqualChar(encoded),
+ @"encoded via *Data apis had a base64 padding char");
+ }
+ dataPrime = [GTMBase64 webSafeDecodeData:encoded];
+ STAssertEqualObjects(data, dataPrime,
+ @"failed to round trip via *Data apis");
+
+ // Bytes to String and back
+ NSString *encodedString =
+ [GTMBase64 stringByWebSafeEncodingBytes:[data bytes]
+ length:[data length]
+ padded:padded];
+ if (padded) {
+ STAssertEquals(([encoded length] % 4), (NSUInteger)0,
+ @"encoded size via *Bytes apis should be a multiple of 4");
+ } else {
+ STAssertTrue(NoEqualChar(encoded),
+ @"encoded via Bytes to Strings had a base64 padding char");
+ }
+ dataPrime = [GTMBase64 webSafeDecodeString:encodedString];
+ STAssertEqualObjects(data, dataPrime,
+ @"failed to round trip for Bytes to Strings");
+
+ // Data to String and back
+ encodedString =
+ [GTMBase64 stringByWebSafeEncodingData:data padded:padded];
+ if (padded) {
+ STAssertEquals(([encoded length] % 4), (NSUInteger)0,
+ @"encoded size via *Data apis should be a multiple of 4");
+ } else {
+ STAssertTrue(NoEqualChar(encoded),
+ @"encoded via Data to Strings had a base64 padding char");
+ }
+ dataPrime = [GTMBase64 webSafeDecodeString:encodedString];
+ STAssertEqualObjects(data, dataPrime,
+ @"failed to round trip for Data to Strings");
+ }
+
+ {
+ // now test all byte values
+ NSMutableData *data = [NSMutableData data];
+ STAssertNotNil(data, @"failed to alloc data block");
+
+ [data setLength:256];
+ unsigned char *scan = (unsigned char*)[data mutableBytes];
+ for (int x = 0 ; x <= 255 ; ++x) {
+ *scan++ = x;
+ }
+
+ // w/ *Bytes apis
+ NSData *encoded =
+ [GTMBase64 webSafeEncodeBytes:[data bytes]
+ length:[data length]
+ padded:padded];
+ if (padded) {
+ STAssertEquals(([encoded length] % 4), (NSUInteger)0,
+ @"encoded size via *Bytes apis should be a multiple of 4");
+ } else {
+ STAssertTrue(NoEqualChar(encoded),
+ @"encoded via *Bytes apis had a base64 padding char");
+ }
+ NSData *dataPrime = [GTMBase64 webSafeDecodeBytes:[encoded bytes]
+ length:[encoded length]];
+ STAssertEqualObjects(data, dataPrime,
+ @"failed to round trip via *Bytes apis");
+
+ // w/ *Data apis
+ encoded = [GTMBase64 webSafeEncodeData:data padded:padded];
+ if (padded) {
+ STAssertEquals(([encoded length] % 4), (NSUInteger)0,
+ @"encoded size via *Data apis should be a multiple of 4");
+ } else {
+ STAssertTrue(NoEqualChar(encoded),
+ @"encoded via *Data apis had a base64 padding char");
+ }
+ dataPrime = [GTMBase64 webSafeDecodeData:encoded];
+ STAssertEqualObjects(data, dataPrime,
+ @"failed to round trip via *Data apis");
+
+ // Bytes to String and back
+ NSString *encodedString =
+ [GTMBase64 stringByWebSafeEncodingBytes:[data bytes]
+ length:[data length]
+ padded:padded];
+ if (padded) {
+ STAssertEquals(([encoded length] % 4), (NSUInteger)0,
+ @"encoded size via *Bytes apis should be a multiple of 4");
+ } else {
+ STAssertTrue(NoEqualChar(encoded),
+ @"encoded via Bytes to Strings had a base64 padding char");
+ }
+ dataPrime = [GTMBase64 webSafeDecodeString:encodedString];
+ STAssertEqualObjects(data, dataPrime,
+ @"failed to round trip for Bytes to Strings");
+
+ // Data to String and back
+ encodedString =
+ [GTMBase64 stringByWebSafeEncodingData:data padded:padded];
+ if (padded) {
+ STAssertEquals(([encoded length] % 4), (NSUInteger)0,
+ @"encoded size via *Data apis should be a multiple of 4");
+ } else {
+ STAssertTrue(NoEqualChar(encoded),
+ @"encoded via Data to Strings had a base64 padding char");
+ }
+ dataPrime = [GTMBase64 webSafeDecodeString:encodedString];
+ STAssertEqualObjects(data, dataPrime,
+ @"failed to round trip for Data to Strings");
+ }
+
+ {
+ // test w/ a mix of spacing characters
+
+ // generate some data, encode it, and add spaces
+ NSMutableData *data = [NSMutableData data];
+ STAssertNotNil(data, @"failed to alloc data block");
+
+ [data setLength:253]; // should get some padding chars on the end
+ FillWithRandom([data mutableBytes], [data length]);
+
+ NSString *encodedString = [GTMBase64 stringByWebSafeEncodingData:data
+ padded:padded];
+ NSMutableString *encodedAndSpaced =
+ [[encodedString mutableCopy] autorelease];
+
+ NSString *spaces[] = { @"\t", @"\n", @"\r", @" " };
+ const NSUInteger numSpaces = sizeof(spaces) / sizeof(NSString*);
+ for (int x = 0 ; x < 512 ; ++x) {
+ NSUInteger offset = random() % ([encodedAndSpaced length] + 1);
+ [encodedAndSpaced insertString:spaces[random() % numSpaces]
+ atIndex:offset];
+ }
+
+ // we'll need it as data for apis
+ NSData *encodedAsData =
+ [encodedAndSpaced dataUsingEncoding:NSASCIIStringEncoding];
+ STAssertNotNil(encodedAsData, @"failed to extract from string");
+ STAssertEquals([encodedAsData length], [encodedAndSpaced length],
+ @"lengths for encoded string and data didn't match?");
+
+ // all the decode modes
+ NSData *dataPrime = [GTMBase64 webSafeDecodeData:encodedAsData];
+ STAssertEqualObjects(data, dataPrime,
+ @"failed Data decode w/ spaces");
+ dataPrime = [GTMBase64 webSafeDecodeBytes:[encodedAsData bytes]
+ length:[encodedAsData length]];
+ STAssertEqualObjects(data, dataPrime,
+ @"failed Bytes decode w/ spaces");
+ dataPrime = [GTMBase64 webSafeDecodeString:encodedAndSpaced];
+ STAssertEqualObjects(data, dataPrime,
+ @"failed String decode w/ spaces");
+ }
+ } // paddedLoop
+}
+
+- (void)testErrors {
+ const int something = 0;
+ NSString *nonAscString = [NSString stringWithUTF8String:"This test ©™®๒०᠐٧"];
+
+ STAssertNil([GTMBase64 encodeData:nil], @"it worked?");
+ STAssertNil([GTMBase64 decodeData:nil], @"it worked?");
+ STAssertNil([GTMBase64 encodeBytes:NULL length:10], @"it worked?");
+ STAssertNil([GTMBase64 encodeBytes:&something length:0], @"it worked?");
+ STAssertNil([GTMBase64 decodeBytes:NULL length:10], @"it worked?");
+ STAssertNil([GTMBase64 decodeBytes:&something length:0], @"it worked?");
+ STAssertNil([GTMBase64 stringByEncodingData:nil], @"it worked?");
+ STAssertNil([GTMBase64 stringByEncodingBytes:NULL length:10], @"it worked?");
+ STAssertNil([GTMBase64 stringByEncodingBytes:&something length:0], @"it worked?");
+ STAssertNil([GTMBase64 decodeString:nil], @"it worked?");
+ // test some pads at the end that aren't right
+ STAssertNil([GTMBase64 decodeString:@"=="], @"it worked?"); // just pads
+ STAssertNil([GTMBase64 decodeString:@"vw="], @"it worked?"); // missing pad (in state 2)
+ STAssertNil([GTMBase64 decodeString:@"vw"], @"it worked?"); // missing pad (in state 2)
+ STAssertNil([GTMBase64 decodeString:@"NNw"], @"it worked?"); // missing pad (in state 3)
+ STAssertNil([GTMBase64 decodeString:@"vw=v"], @"it worked?"); // missing pad, has something else
+ STAssertNil([GTMBase64 decodeString:@"v="], @"it worked?"); // missing a needed char, has pad instead
+ STAssertNil([GTMBase64 decodeString:@"v"], @"it worked?"); // missing a needed char
+ STAssertNil([GTMBase64 decodeString:@"vw== vw"], @"it worked?");
+ STAssertNil([GTMBase64 decodeString:nonAscString], @"it worked?");
+ STAssertNil([GTMBase64 decodeString:@"@@@not valid###"], @"it worked?");
+ // carefully crafted bad input to make sure we don't overwalk
+ STAssertNil([GTMBase64 decodeString:@"WD=="], @"it worked?");
+
+ STAssertNil([GTMBase64 webSafeEncodeData:nil padded:YES], @"it worked?");
+ STAssertNil([GTMBase64 webSafeDecodeData:nil], @"it worked?");
+ STAssertNil([GTMBase64 webSafeEncodeBytes:NULL length:10 padded:YES],
+ @"it worked?");
+ STAssertNil([GTMBase64 webSafeEncodeBytes:&something length:0 padded:YES],
+ @"it worked?");
+ STAssertNil([GTMBase64 webSafeDecodeBytes:NULL length:10], @"it worked?");
+ STAssertNil([GTMBase64 webSafeDecodeBytes:&something length:0], @"it worked?");
+ STAssertNil([GTMBase64 stringByWebSafeEncodingData:nil padded:YES],
+ @"it worked?");
+ STAssertNil([GTMBase64 stringByWebSafeEncodingBytes:NULL
+ length:10
+ padded:YES],
+ @"it worked?");
+ STAssertNil([GTMBase64 stringByWebSafeEncodingBytes:&something
+ length:0
+ padded:YES],
+ @"it worked?");
+ STAssertNil([GTMBase64 webSafeDecodeString:nil], @"it worked?");
+ // test some pads at the end that aren't right
+ STAssertNil([GTMBase64 webSafeDecodeString:@"=="], @"it worked?"); // just pad chars
+ STAssertNil([GTMBase64 webSafeDecodeString:@"aw="], @"it worked?"); // missing pad
+ STAssertNil([GTMBase64 webSafeDecodeString:@"aw=a"], @"it worked?"); // missing pad, has something else
+ STAssertNil([GTMBase64 webSafeDecodeString:@"a"], @"it worked?"); // missing a needed char
+ STAssertNil([GTMBase64 webSafeDecodeString:@"a="], @"it worked?"); // missing a needed char, has pad instead
+ STAssertNil([GTMBase64 webSafeDecodeString:@"aw== a"], @"it worked?"); // missing pad
+ STAssertNil([GTMBase64 webSafeDecodeString:nonAscString], @"it worked?");
+ STAssertNil([GTMBase64 webSafeDecodeString:@"@@@not valid###"], @"it worked?");
+ // carefully crafted bad input to make sure we don't overwalk
+ STAssertNil([GTMBase64 webSafeDecodeString:@"WD=="], @"it worked?");
+
+ // make sure our local helper is working right
+ STAssertFalse(NoEqualChar([NSData dataWithBytes:"aa=zz" length:5]), @"");
+}
+
+@end
diff --git a/Foundation/GTMCalculatedRange.h b/Foundation/GTMCalculatedRange.h
index 5f51b3e..5710da6 100644
--- a/Foundation/GTMCalculatedRange.h
+++ b/Foundation/GTMCalculatedRange.h
@@ -20,6 +20,7 @@
//
#import <Foundation/Foundation.h>
+#import "GTMDefines.h"
/// Allows you to calculate a value based on defined stops in a range.
//
@@ -46,7 +47,7 @@
// item: the object to place at |position|.
// position: the position in the range to put |item|.
//
-- (void)insertStop:(id)item atPosition:(float)position;
+- (void)insertStop:(id)item atPosition:(CGFloat)position;
// Removes a stop from the range at |position|.
//
@@ -56,7 +57,7 @@
// Returns:
// YES if there is a stop at |position| that has been removed
// NO if there is not a stop at the |position|
-- (BOOL)removeStopAtPosition:(float)position;
+- (BOOL)removeStopAtPosition:(CGFloat)position;
// Removes stop |index| from the range. Stops are ordered
// based on position where index of x < index of y if position
@@ -66,13 +67,13 @@
// item: the object to place at |position|.
// position: the position in the range to put |item|.
//
-- (void)removeStopAtIndex:(unsigned int)index;
+- (void)removeStopAtIndex:(NSUInteger)index;
// Returns the number of stops in the range.
//
// Returns:
// number of stops
-- (unsigned int)stopCount;
+- (NSUInteger)stopCount;
// Returns the value at position |position|.
// This function should be overridden by subclasses to calculate a
@@ -85,7 +86,7 @@
//
// Returns:
// value for position
-- (id)valueAtPosition:(float)position;
+- (id)valueAtPosition:(CGFloat)position;
// Returns the |index|'th stop and position in the set.
// Throws an exception if out of range.
@@ -97,5 +98,5 @@
//
// Returns:
// the stop at the index.
-- (id)stopAtIndex:(unsigned int)index position:(float*)outPosition;
+- (id)stopAtIndex:(NSUInteger)index position:(CGFloat*)outPosition;
@end
diff --git a/Foundation/GTMCalculatedRange.m b/Foundation/GTMCalculatedRange.m
index 8562d8a..435ad65 100644
--- a/Foundation/GTMCalculatedRange.m
+++ b/Foundation/GTMCalculatedRange.m
@@ -22,21 +22,21 @@
// position.
@interface GTMCalculatedRangeStopPrivate : NSObject {
id item_; // the item (STRONG)
- float position_; //
+ CGFloat position_; //
}
-+ (id)stopWithObject:(id)item position:(float)inPosition;
-- (id)initWithObject:(id)item position:(float)inPosition;
++ (id)stopWithObject:(id)item position:(CGFloat)inPosition;
+- (id)initWithObject:(id)item position:(CGFloat)inPosition;
- (id)item;
-- (float)position;
+- (CGFloat)position;
@end
@implementation GTMCalculatedRangeStopPrivate
-+ (id)stopWithObject:(id)item position:(float)inPosition {
++ (id)stopWithObject:(id)item position:(CGFloat)inPosition {
return [[[[self class] alloc] initWithObject:item position:inPosition] autorelease];
}
-- (id)initWithObject:(id)item position:(float)inPosition {
+- (id)initWithObject:(id)item position:(CGFloat)inPosition {
self = [super init];
if (self != nil) {
item_ = [item retain];
@@ -54,7 +54,7 @@
return item_;
}
-- (float)position {
+- (CGFloat)position {
return position_;
}
@@ -76,8 +76,8 @@
[super dealloc];
}
-- (void)insertStop:(id)item atPosition:(float)position {
- unsigned int index = 0;
+- (void)insertStop:(id)item atPosition:(CGFloat)position {
+ NSUInteger index = 0;
NSEnumerator *theEnumerator = [storage_ objectEnumerator];
GTMCalculatedRangeStopPrivate *theStop;
while (nil != (theStop = [theEnumerator nextObject])) {
@@ -85,15 +85,17 @@
index += 1;
}
else if ([theStop position] == position) {
+ // remove and stop the enum since we just modified the object
[storage_ removeObjectAtIndex:index];
+ break;
}
}
[storage_ insertObject:[GTMCalculatedRangeStopPrivate stopWithObject:item position:position]
- atIndex:index];
+ atIndex:index];
}
-- (BOOL)removeStopAtPosition:(float)position {
- unsigned int index = 0;
+- (BOOL)removeStopAtPosition:(CGFloat)position {
+ NSUInteger index = 0;
BOOL foundStop = NO;
NSEnumerator *theEnumerator = [storage_ objectEnumerator];
GTMCalculatedRangeStopPrivate *theStop;
@@ -111,15 +113,15 @@
return foundStop;
}
-- (void)removeStopAtIndex:(unsigned int)index {
+- (void)removeStopAtIndex:(NSUInteger)index {
[storage_ removeObjectAtIndex:index];
}
-- (unsigned int)stopCount {
+- (NSUInteger)stopCount {
return [storage_ count];
}
-- (id)stopAtIndex:(unsigned int)index position:(float*)outPosition {
+- (id)stopAtIndex:(NSUInteger)index position:(CGFloat*)outPosition {
GTMCalculatedRangeStopPrivate *theStop = [storage_ objectAtIndex:index];
if (nil != outPosition) {
*outPosition = [theStop position];
@@ -127,7 +129,7 @@
return [theStop item];
}
-- (id)valueAtPosition:(float)position {
+- (id)valueAtPosition:(CGFloat)position {
id theValue = nil;
GTMCalculatedRangeStopPrivate *theStop;
NSEnumerator *theEnumerator = [storage_ objectEnumerator];
diff --git a/Foundation/GTMCalculatedRangeTest.m b/Foundation/GTMCalculatedRangeTest.m
index 0c374c1..1d716c8 100644
--- a/Foundation/GTMCalculatedRangeTest.m
+++ b/Foundation/GTMCalculatedRangeTest.m
@@ -26,14 +26,14 @@
@implementation GTMCalculatedRangeTest
NSString *kStrings[] = { @"Fee", @"Fi", @"Fo", @"Fum" };
-const unsigned int kStringCount = sizeof(kStrings) / sizeof(NSString*);
-const float kOddPosition = 0.14159265f;
-const float kExistingPosition = 0.5f;
-const unsigned int kExisitingIndex = 2;
+const NSUInteger kStringCount = sizeof(kStrings) / sizeof(NSString*);
+const CGFloat kOddPosition = 0.14159265f;
+const CGFloat kExistingPosition = 0.5f;
+const NSUInteger kExisitingIndex = 2;
- (void)setUp {
range_ = [[GTMCalculatedRange alloc] init];
- for(unsigned int i = kStringCount; i > 0; --i) {
+ for(NSUInteger i = kStringCount; i > 0; --i) {
[range_ insertStop:kStrings[kStringCount - i] atPosition: 1.0f / i];
}
}
@@ -43,12 +43,22 @@ const unsigned int kExisitingIndex = 2;
}
- (void)testInsertStop {
+ // new position
NSString *theString = @"I smell the blood of an Englishman!";
- [range_ insertStop:theString atPosition: kOddPosition];
+ [range_ insertStop:theString atPosition:kOddPosition];
STAssertEquals([range_ stopCount], kStringCount + 1, @"Stop count was bad");
NSString *getString = [range_ valueAtPosition:kOddPosition];
STAssertNotNil(getString, @"String was bad");
STAssertEquals(theString, getString, @"Stops weren't equal");
+ // existing position
+ NSString *theStringTake2 = @"I smell the blood of an Englishman! Take 2";
+ [range_ insertStop:theStringTake2 atPosition:kOddPosition];
+ STAssertEquals([range_ stopCount], kStringCount + 1, @"Stop count was bad");
+ getString = [range_ valueAtPosition:kOddPosition];
+ STAssertNotNil(getString, @"String was bad");
+ STAssertEquals(theStringTake2, getString, @"Stops weren't equal");
+ STAssertNotEquals(theString, getString, @"Should be the new value");
+ STAssertNotEqualObjects(theString, getString, @"Should be the new value");
}
- (void)testRemoveStopAtPosition {
@@ -74,7 +84,7 @@ const unsigned int kExisitingIndex = 2;
}
- (void)testStopAtIndex {
- float thePosition;
+ CGFloat thePosition;
STAssertEqualObjects([range_ stopAtIndex:kStringCount - 1 position:nil], kStrings[kStringCount - 1], nil);
STAssertEqualObjects([range_ stopAtIndex:kExisitingIndex position:&thePosition], kStrings[kExisitingIndex], nil);
@@ -83,5 +93,9 @@ const unsigned int kExisitingIndex = 2;
STAssertThrows([range_ stopAtIndex:kStringCount position:nil], nil);
}
+- (void)testDescription {
+ // we expect a description of atleast a few chars
+ STAssertGreaterThan([[range_ description] length], (NSUInteger)10, nil);
+}
@end
diff --git a/Foundation/GTMGeometryUtils.h b/Foundation/GTMGeometryUtils.h
index 32c8745..a58ac13 100644
--- a/Foundation/GTMGeometryUtils.h
+++ b/Foundation/GTMGeometryUtils.h
@@ -19,15 +19,17 @@
// the License.
//
-#include <Foundation/Foundation.h>
+#import <Foundation/Foundation.h>
+#import "GTMDefines.h"
-typedef enum {
+enum {
GTMScaleProportionally = 0, // Fit proportionally
GTMScaleToFit, // Forced fit (distort if necessary)
GTMScaleNone // Don't scale (clip)
-} GTMScaling;
+};
+typedef NSUInteger GTMScaling;
-typedef enum {
+enum {
GTMRectAlignCenter = 0,
GTMRectAlignTop,
GTMRectAlignTopLeft,
@@ -37,7 +39,8 @@ typedef enum {
GTMRectAlignBottomLeft,
GTMRectAlignBottomRight,
GTMRectAlignRight
-} GTMRectAlignment;
+};
+typedef NSUInteger GTMRectAlignment;
#pragma mark Miscellaneous
@@ -49,10 +52,14 @@ typedef enum {
//
// Returns:
// Distance
-CG_INLINE float GTMDistanceBetweenPoints(NSPoint pt1, NSPoint pt2) {
- float dX = pt1.x - pt2.x;
- float dY = pt1.y - pt2.y;
+CG_INLINE CGFloat GTMDistanceBetweenPoints(NSPoint pt1, NSPoint pt2) {
+ CGFloat dX = pt1.x - pt2.x;
+ CGFloat dY = pt1.y - pt2.y;
+#if CGFLOAT_IS_DOUBLE
+ return sqrt(dX * dX + dY * dY);
+#else
return sqrtf(dX * dX + dY * dY);
+#endif
}
#pragma mark -
@@ -314,7 +321,7 @@ CG_INLINE CGRect GTMCGRectOfSize(CGSize size) {
//
// Returns:
// Converted Rect
-CG_INLINE NSRect GTMNSRectScale(NSRect inRect, float xScale, float yScale) {
+CG_INLINE NSRect GTMNSRectScale(NSRect inRect, CGFloat xScale, CGFloat yScale) {
return NSMakeRect(inRect.origin.x, inRect.origin.y,
inRect.size.width * xScale, inRect.size.height * yScale);
}
@@ -328,7 +335,7 @@ CG_INLINE NSRect GTMNSRectScale(NSRect inRect, float xScale, float yScale) {
//
// Returns:
// Converted Rect
-CG_INLINE CGRect GTMCGRectScale(CGRect inRect, float xScale, float yScale) {
+CG_INLINE CGRect GTMCGRectScale(CGRect inRect, CGFloat xScale, CGFloat yScale) {
return CGRectMake(inRect.origin.x, inRect.origin.y,
inRect.size.width * xScale, inRect.size.height * yScale);
}
diff --git a/Foundation/GTMGeometryUtils.m b/Foundation/GTMGeometryUtils.m
index f5b38dc..0e893ff 100644
--- a/Foundation/GTMGeometryUtils.m
+++ b/Foundation/GTMGeometryUtils.m
@@ -79,13 +79,13 @@ NSRect GTMAlignRectangles(NSRect alignee, NSRect aligner, GTMRectAlignment align
NSRect GTMScaleRectangleToSize(NSRect scalee, NSSize size, GTMScaling scaling) {
switch (scaling) {
case GTMScaleProportionally: {
- float height = NSHeight(scalee);
- float width = NSWidth(scalee);
+ CGFloat height = NSHeight(scalee);
+ CGFloat width = NSWidth(scalee);
if (isnormal(height) && isnormal(width) &&
(height > size.height || width > size.width)) {
- float horiz = size.width / width;
- float vert = size.height / height;
- float newScale = horiz < vert ? horiz : vert;
+ CGFloat horiz = size.width / width;
+ CGFloat vert = size.height / height;
+ CGFloat newScale = horiz < vert ? horiz : vert;
scalee = GTMNSRectScale(scalee, newScale, newScale);
}
break;
diff --git a/Foundation/GTMGeometryUtilsTest.m b/Foundation/GTMGeometryUtilsTest.m
index 2fb0c68..606ea6b 100644
--- a/Foundation/GTMGeometryUtilsTest.m
+++ b/Foundation/GTMGeometryUtilsTest.m
@@ -64,11 +64,11 @@
- (void)testGTMDistanceBetweenPoints {
NSPoint pt1 = NSMakePoint(0, 0);
NSPoint pt2 = NSMakePoint(3, 4);
- STAssertEquals(GTMDistanceBetweenPoints(pt1, pt2), 5.0f, nil);
- STAssertEquals(GTMDistanceBetweenPoints(pt2, pt1), 5.0f, nil);
+ STAssertEquals(GTMDistanceBetweenPoints(pt1, pt2), (CGFloat)5.0, nil);
+ STAssertEquals(GTMDistanceBetweenPoints(pt2, pt1), (CGFloat)5.0, nil);
pt1 = NSMakePoint(1, 1);
pt2 = NSMakePoint(1, 1);
- STAssertEquals(GTMDistanceBetweenPoints(pt1, pt2), 0.0f, nil);
+ STAssertEquals(GTMDistanceBetweenPoints(pt1, pt2), (CGFloat)0.0, nil);
}
- (void)testGTMAlignRectangles {
@@ -109,37 +109,37 @@
CGPoint cgPoint = GTMCGMidLeft(cgRect);
STAssertEquals(point.x, cgPoint.x, nil);
STAssertEquals(point.y, cgPoint.y, nil);
- STAssertEqualsWithAccuracy(point.y, 1.0f, 0.01f, nil);
- STAssertEqualsWithAccuracy(point.x, 0.0f, 0.01f, nil);
+ STAssertEqualsWithAccuracy(point.y, (CGFloat)1.0, (CGFloat)0.01, nil);
+ STAssertEqualsWithAccuracy(point.x, (CGFloat)0.0, (CGFloat)0.01, nil);
point = GTMNSMidRight(rect);
cgPoint = GTMCGMidRight(cgRect);
STAssertEquals(point.x, cgPoint.x, nil);
STAssertEquals(point.y, cgPoint.y, nil);
- STAssertEqualsWithAccuracy(point.y, 1.0f, 0.01f, nil);
- STAssertEqualsWithAccuracy(point.x, 2.0f, 0.01f, nil);
+ STAssertEqualsWithAccuracy(point.y, (CGFloat)1.0, (CGFloat)0.01, nil);
+ STAssertEqualsWithAccuracy(point.x, (CGFloat)2.0, (CGFloat)0.01, nil);
point = GTMNSMidTop(rect);
cgPoint = GTMCGMidTop(cgRect);
STAssertEquals(point.x, cgPoint.x, nil);
STAssertEquals(point.y, cgPoint.y, nil);
- STAssertEqualsWithAccuracy(point.y, 2.0f, 0.01f, nil);
- STAssertEqualsWithAccuracy(point.x, 1.0f, 0.01f, nil);
+ STAssertEqualsWithAccuracy(point.y, (CGFloat)2.0, (CGFloat)0.01, nil);
+ STAssertEqualsWithAccuracy(point.x, (CGFloat)1.0, (CGFloat)0.01, nil);
point = GTMNSMidBottom(rect);
cgPoint = GTMCGMidBottom(cgRect);
STAssertEquals(point.x, cgPoint.x, nil);
STAssertEquals(point.y, cgPoint.y, nil);
- STAssertEqualsWithAccuracy(point.y, 0.0f, 0.01f, nil);
- STAssertEqualsWithAccuracy(point.x, 1.0f, 0.01f, nil);
+ STAssertEqualsWithAccuracy(point.y, (CGFloat)0.0, (CGFloat)0.01, nil);
+ STAssertEqualsWithAccuracy(point.x, (CGFloat)1.0, (CGFloat)0.01, nil);
}
- (void)testGTMRectScaling {
NSRect rect = NSMakeRect(1.0f, 2.0f, 5.0f, 10.0f);
- NSRect rect2 = NSMakeRect(1.0f, 2.0f, 1.0f, 12.0f);
- STAssertEquals(GTMNSRectScale(rect, 0.2f, 1.2f),
+ NSRect rect2 = NSMakeRect((CGFloat)1.0, (CGFloat)2.0, (CGFloat)1.0, (CGFloat)12.0);
+ STAssertEquals(GTMNSRectScale(rect, (CGFloat)0.2, (CGFloat)1.2),
rect2, nil);
- STAssertEquals(GTMCGRectScale(GTMNSRectToCGRect(rect), 0.2f, 1.2f),
+ STAssertEquals(GTMCGRectScale(GTMNSRectToCGRect(rect), (CGFloat)0.2, (CGFloat)1.2),
GTMNSRectToCGRect(rect2), nil);
}
diff --git a/Foundation/GTMHTTPFetcher.h b/Foundation/GTMHTTPFetcher.h
new file mode 100644
index 0000000..181283a
--- /dev/null
+++ b/Foundation/GTMHTTPFetcher.h
@@ -0,0 +1,500 @@
+//
+// GTMHTTPFetcher.h
+//
+// Copyright 2007-2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+// This is essentially a wrapper around NSURLConnection for POSTs and GETs.
+// If setPostData: is called, then POST is assumed.
+//
+// When would you use this instead of NSURLConnection?
+//
+// - When you just want the result from a GET or POST
+// - When you want the "standard" behavior for connections (redirection handling
+// an so on)
+// - When you want to avoid cookie collisions with Safari and other applications
+// - When you want to provide if-modified-since headers
+// - When you need to set a credential for the http
+// - When you want to avoid changing WebKit's cookies
+//
+// This is assumed to be a one-shot fetch request; don't reuse the object
+// for a second fetch.
+//
+// The fetcher may be created auto-released, in which case it will release
+// itself after the fetch completion callback. The fetcher
+// is implicitly retained as long as a connection is pending.
+//
+// But if you may need to cancel the fetcher, allocate it with initWithRequest:
+// and have the delegate release the fetcher in the callbacks.
+//
+// Sample usage:
+//
+// NSURLRequest *request = [NSURLRequest requestWithURL:myURL];
+// GTMHTTPFetcher* myFetcher = [GTMHTTPFetcher httpFetcherWithRequest:request];
+//
+// [myFetcher setPostData:[postString dataUsingEncoding:NSUTF8StringEncoding]]; // for POSTs
+//
+// [myFetcher setCredential:[NSURLCredential authCredentialWithUsername:@"foo"
+// password:@"bar"]]; // optional http credential
+//
+// [myFetcher setFetchHistory:myMutableDictionary]; // optional, for persisting modified-dates
+//
+// [myFetcher beginFetchWithDelegate:self
+// didFinishSelector:@selector(myFetcher:finishedWithData:)
+// didFailSelector:@selector(myFetcher:failedWithError:)];
+//
+// Upon fetch completion, the callback selectors are invoked; they should have
+// these signatures (you can use any callback method names you want so long as
+// the signatures match these):
+//
+// - (void)myFetcher:(GTMHTTPFetcher *)fetcher finishedWithData:(NSData *)retrievedData;
+// - (void)myFetcher:(GTMHTTPFetcher *)fetcher failedWithError:(NSError *)error;
+//
+// NOTE: Fetches may retrieve data from the server even though the server
+// returned an error. The failWithError selector is called when the server
+// status is >= 300 (along with any server-supplied data, usually
+// some html explaining the error).
+// Status codes are at <http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html>
+//
+//
+// Proxies:
+//
+// Proxy handling is invisible so long as the system has a valid credential in
+// the keychain, which is normally true (else most NSURL-based apps would have
+// difficulty.) But when there is a proxy authetication error, the the fetcher
+// will call the failedWithError: method with the NSURLChallenge in the error's
+// userInfo. The error method can get the challenge info like this:
+//
+// NSURLAuthenticationChallenge *challenge
+// = [[error userInfo] objectForKey:kGTMHTTPFetcherErrorChallengeKey];
+// BOOL isProxyChallenge = [[challenge protectionSpace] isProxy];
+//
+// If a proxy error occurs, you can ask the user for the proxy username/password
+// and call fetcher's setProxyCredential: to provide those for the
+// next attempt to fetch.
+//
+//
+// Cookies:
+//
+// There are three supported mechanisms for remembering cookies between fetches.
+//
+// By default, GTMHTTPFetcher uses a mutable array held statically to track
+// cookies for all instantiated fetchers. This avoids server cookies being set
+// by servers for the application from interfering with Safari cookie settings,
+// and vice versa. The fetcher cookies are lost when the application quits.
+//
+// To rely instead on WebKit's global NSHTTPCookieStorage, call
+// setCookieStorageMethod: with kGTMHTTPFetcherCookieStorageMethodSystemDefault.
+//
+// If you provide a fetch history (such as for periodic checks, described
+// below) then the cookie storage mechanism is set to use the fetch
+// history rather than the static storage.
+//
+//
+// Fetching for periodic checks:
+//
+// The fetcher object can track "Last-modified" dates on returned data and
+// provide an "If-modified-since" header. This allows the server to save
+// bandwidth by providing a "Nothing changed" status message instead of response
+// data.
+//
+// To get this behavior, provide a persistent mutable dictionary to setFetchHistory:,
+// and look for the failedWithError: callback with code 304
+// (kGTMHTTPFetcherStatusNotModified) like this:
+//
+// - (void)myFetcher:(GTMHTTPFetcher *)fetcher failedWithError:(NSError *)error {
+// if ([[error domain] isEqual:kGTMHTTPFetcherStatusDomain] &&
+// ([error code] == kGTMHTTPFetcherStatusNotModified)) {
+// // [[error userInfo] objectForKey:kGTMHTTPFetcherStatusDataKey] is
+// // empty; use the data from the previous finishedWithData: for this URL
+// } else {
+// // handle other server status code
+// }
+// }
+//
+// The fetchHistory mutable dictionary should be maintained by the client between
+// fetches and given to each fetcher intended to have the If-modified-since header
+// or the same cookie storage.
+//
+//
+// Monitoring received data
+//
+// The optional received data selector should have the signature
+//
+// - (void)myFetcher:(GTMHTTPFetcher *)fetcher receivedData:(NSData *)dataReceivedSoFar;
+//
+// The bytes received so far are [dataReceivedSoFar length]. This number may go down
+// if a redirect causes the download to begin again from a new server.
+// If supplied by the server, the anticipated total download size is available as
+// [[myFetcher response] expectedContentLength] (may be -1 for unknown
+// download sizes.)
+//
+//
+// Automatic retrying of fetches
+//
+// The fetcher can optionally create a timer and reattempt certain kinds of
+// fetch failures (status codes 408, request timeout; 503, service unavailable;
+// 504, gateway timeout; networking errors NSURLErrorTimedOut and
+// NSURLErrorNetworkConnectionLost.) The user may set a retry selector to
+// customize the type of errors which will be retried.
+//
+// Retries are done in an exponential-backoff fashion (that is, after 1 second,
+// 2, 4, 8, and so on.)
+//
+// Enabling automatic retries looks like this:
+// [myFetcher setIsRetryEnabled:YES];
+//
+// With retries enabled, the success or failure callbacks are called only
+// when no more retries will be attempted. Calling the fetcher's stopFetching
+// method will terminate the retry timer, without the finished or failure
+// selectors being invoked.
+//
+// Optionally, the client may set the maximum retry interval:
+// [myFetcher setMaxRetryInterval:60.]; // in seconds; default is 600 seconds
+//
+// Also optionally, the client may provide a callback selector to determine
+// if a status code or other error should be retried.
+// [myFetcher setRetrySelector:@selector(myFetcher:willRetry:forError:)];
+//
+// If set, the retry selector should have the signature:
+// -(BOOL)fetcher:(GTMHTTPFetcher *)fetcher willRetry:(BOOL)suggestedWillRetry forError:(NSError *)error
+// and return YES to set the retry timer or NO to fail without additional
+// fetch attempts.
+//
+// The retry method may return the |suggestedWillRetry| argument to get the
+// default retry behavior. Server status codes are present in the error
+// argument, and have the domain kGTMHTTPFetcherStatusDomain. The user's method
+// may look something like this:
+//
+// -(BOOL)myFetcher:(GTMHTTPFetcher *)fetcher willRetry:(BOOL)suggestedWillRetry forError:(NSError *)error {
+//
+// // perhaps examine [error domain] and [error code], or [fetcher retryCount]
+// //
+// // return YES to start the retry timer, NO to proceed to the failure
+// // callback, or |suggestedWillRetry| to get default behavior for the
+// // current error domain and code values.
+// return suggestedWillRetry;
+// }
+
+
+
+#pragma once
+
+#import <Foundation/Foundation.h>
+
+#import "GTMDefines.h"
+
+#undef _EXTERN
+#undef _INITIALIZE_AS
+#ifdef GTMHTTPFETCHER_DEFINE_GLOBALS
+#define _EXTERN
+#define _INITIALIZE_AS(x) =x
+#else
+#define _EXTERN extern
+#define _INITIALIZE_AS(x)
+#endif
+
+// notifications & errors
+_EXTERN NSString* const kGTMHTTPFetcherErrorDomain _INITIALIZE_AS(@"com.google.GTMHTTPFetcher");
+_EXTERN NSString* const kGTMHTTPFetcherStatusDomain _INITIALIZE_AS(@"com.google.HTTPStatus");
+_EXTERN NSString* const kGTMHTTPFetcherErrorChallengeKey _INITIALIZE_AS(@"challenge");
+_EXTERN NSString* const kGTMHTTPFetcherStatusDataKey _INITIALIZE_AS(@"data"); // any data returns w/ a kGTMHTTPFetcherStatusDomain error
+
+
+// fetch history mutable dictionary keys
+_EXTERN NSString* const kGTMHTTPFetcherHistoryLastModifiedKey _INITIALIZE_AS(@"FetchHistoryLastModified");
+_EXTERN NSString* const kGTMHTTPFetcherHistoryDatedDataKey _INITIALIZE_AS(@"FetchHistoryDatedDataCache");
+_EXTERN NSString* const kGTMHTTPFetcherHistoryCookiesKey _INITIALIZE_AS(@"FetchHistoryCookies");
+
+enum {
+ kGTMHTTPFetcherErrorDownloadFailed = -1,
+ kGTMHTTPFetcherErrorAuthenticationChallengeFailed = -2,
+
+ kGTMHTTPFetcherStatusNotModified = 304
+};
+
+enum {
+ kGTMHTTPFetcherCookieStorageMethodStatic = 0,
+ kGTMHTTPFetcherCookieStorageMethodFetchHistory = 1,
+ kGTMHTTPFetcherCookieStorageMethodSystemDefault = 2
+};
+typedef NSUInteger GTMHTTPFetcherCookieStorageMethod;
+
+/// async retrieval of an http get or post
+@interface GTMHTTPFetcher : NSObject {
+ NSMutableURLRequest *request_;
+ NSURLConnection *connection_; // while connection_ is non-nil, delegate_ is retained
+ NSMutableData *downloadedData_;
+ NSURLCredential *credential_; // username & password
+ NSURLCredential *proxyCredential_; // credential supplied to proxy servers
+ NSData *postData_;
+ NSInputStream *postStream_;
+ NSMutableData *loggedStreamData_;
+ NSURLResponse *response_; // set in connection:didReceiveResponse:
+ id delegate_; // WEAK (though retained during an open connection)
+ SEL finishedSEL_; // should by implemented by delegate
+ SEL failedSEL_; // should be implemented by delegate
+ SEL receivedDataSEL_; // optional, set with setReceivedDataSelector
+ id userData_; // retained, if set by caller
+ NSArray *runLoopModes_; // optional, for 10.5 and later
+ NSMutableDictionary *fetchHistory_; // if supplied by the caller, used for Last-Modified-Since checks and cookies
+ BOOL shouldCacheDatedData_; // if true, remembers and returns data marked with a last-modified date
+ GTMHTTPFetcherCookieStorageMethod cookieStorageMethod_; // constant from above
+
+ BOOL isRetryEnabled_; // user wants auto-retry
+ SEL retrySEL_; // optional; set with setRetrySelector
+ NSTimer *retryTimer_;
+ unsigned int retryCount_;
+ NSTimeInterval maxRetryInterval_; // default 600 seconds
+ NSTimeInterval minRetryInterval_; // random between 1 and 2 seconds
+ NSTimeInterval retryFactor_; // default interval multiplier is 2
+ NSTimeInterval lastRetryInterval_;
+}
+
+/// create a fetcher
+//
+// httpFetcherWithRequest will return an autoreleased fetcher, but if
+// the connection is successfully created, the connection should retain the
+// fetcher for the life of the connection as well. So the caller doesn't have
+// to retain the fetcher explicitly unless they want to be able to cancel it.
++ (GTMHTTPFetcher *)httpFetcherWithRequest:(NSURLRequest *)request;
+
+// designated initializer
+- (id)initWithRequest:(NSURLRequest *)request;
+
+- (NSMutableURLRequest *)request;
+- (void)setRequest:(NSURLRequest *)theRequest;
+
+// setting the credential is optional; it is used if the connection receives
+// an authentication challenge
+- (NSURLCredential *)credential;
+- (void)setCredential:(NSURLCredential *)theCredential;
+
+// setting the proxy credential is optional; it is used if the connection
+// receives an authentication challenge from a proxy
+- (NSURLCredential *)proxyCredential;
+- (void)setProxyCredential:(NSURLCredential *)theCredential;
+
+
+// if post data or stream is not set, then a GET retrieval method is assumed
+- (NSData *)postData;
+- (void)setPostData:(NSData *)theData;
+
+// beware: In 10.4, NSInputStream fails to copy or retain
+// the data it was initialized with, contrary to docs.
+// NOTE: if logging is enabled and GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING is
+// 1, postStream will return a GTMProgressMonitorInputStream that wraps your
+// stream (so the upload can be logged).
+- (NSInputStream *)postStream;
+- (void)setPostStream:(NSInputStream *)theStream;
+
+- (GTMHTTPFetcherCookieStorageMethod)cookieStorageMethod;
+- (void)setCookieStorageMethod:(GTMHTTPFetcherCookieStorageMethod)method;
+
+// returns cookies from the currently appropriate cookie storage
+- (NSArray *)cookiesForURL:(NSURL *)theURL;
+
+// the delegate is not retained except during the connection
+- (id)delegate;
+- (void)setDelegate:(id)theDelegate;
+
+// the delegate's optional receivedData selector has a signature like:
+// - (void)myFetcher:(GTMHTTPFetcher *)fetcher receivedData:(NSData *)dataReceivedSoFar;
+- (SEL)receivedDataSelector;
+- (void)setReceivedDataSelector:(SEL)theSelector;
+
+
+// retrying; see comments at the top of the file. Calling
+// setIsRetryEnabled(YES) resets the min and max retry intervals.
+- (BOOL)isRetryEnabled;
+- (void)setIsRetryEnabled:(BOOL)flag;
+
+// retry selector is optional for retries.
+//
+// If present, it should have the signature:
+// -(BOOL)fetcher:(GTMHTTPFetcher *)fetcher willRetry:(BOOL)suggestedWillRetry forError:(NSError *)error
+// and return YES to cause a retry. See comments at the top of this file.
+- (SEL)retrySelector;
+- (void)setRetrySelector:(SEL)theSel;
+
+// retry intervals must be strictly less than maxRetryInterval, else
+// they will be limited to maxRetryInterval and no further retries will
+// be attempted. Setting maxRetryInterval to 0.0 will reset it to the
+// default value, 600 seconds.
+- (NSTimeInterval)maxRetryInterval;
+- (void)setMaxRetryInterval:(NSTimeInterval)secs;
+
+// Starting retry interval. Setting minRetryInterval to 0.0 will reset it
+// to a random value between 1.0 and 2.0 seconds. Clients should normally not
+// call this except for unit testing.
+- (NSTimeInterval)minRetryInterval;
+- (void)setMinRetryInterval:(NSTimeInterval)secs;
+
+// Multiplier used to increase the interval between retries, typically 2.0.
+// Clients should not need to call this.
+- (double)retryFactor;
+- (void)setRetryFactor:(double)multiplier;
+
+// number of retries attempted
+- (unsigned int)retryCount;
+
+// interval delay to precede next retry
+- (NSTimeInterval)nextRetryInterval;
+
+/// Begin fetching the request.
+//
+/// |delegate| can optionally implement the two selectors |finishedSEL| and
+/// |networkFailedSEL| or pass nil for them.
+/// Returns YES if the fetch is initiated. Delegate is retained between
+/// the beginFetch call until after the finish/fail callbacks.
+//
+// finishedSEL has a signature like:
+// - (void)fetcher:(GTMHTTPFetcher *)fetcher finishedWithData:(NSData *)data
+// failedSEL has a signature like:
+// - (void)fetcher:(GTMHTTPFetcher *)fetcher failedWithError:(NSError *)error
+//
+
+- (BOOL)beginFetchWithDelegate:(id)delegate
+ didFinishSelector:(SEL)finishedSEL
+ didFailSelector:(SEL)networkFailedSEL;
+
+// Returns YES if this is in the process of fetching a URL
+- (BOOL)isFetching;
+
+/// Cancel the fetch of the request that's currently in progress
+- (void)stopFetching;
+
+/// return the status code from the server response
+- (NSInteger)statusCode;
+
+/// the response, once it's been received
+- (NSURLResponse *)response;
+- (void)setResponse:(NSURLResponse *)response;
+
+// Fetch History is a useful, but a little complex at times...
+//
+// The caller should provide a mutable dictionary that can be used for storing
+// Last-Modified-Since checks and cookie storage (setFetchHistory implicity
+// calls setCookieStorageMethod w/
+// kGTMHTTPFetcherCookieStorageMethodFetchHistory if you passed a dictionary,
+// kGTMHTTPFetcherCookieStorageMethodStatic if you passed nil.
+//
+// The caller can hold onto the dictionary to reuse the modification dates and
+// cookies across multiple fetcher instances.
+//
+// With a fetch history dictionary setup, the http fetcher cache has the
+// modification dates returned by the servers cached, and future fetches will
+// return 304 to indicate the data hasn't changed since then (the data in the
+// NSError object will be of length zero to show nothing was fetched). This
+// reduces load on the server when the response data has not changed. See
+// shouldCacheDatedData below for additional 304 support.
+//
+// Side effect: setFetchHistory: implicitly calls setCookieStorageMethod:
+- (NSMutableDictionary *)fetchHistory;
+- (void)setFetchHistory:(NSMutableDictionary *)fetchHistory;
+
+// For fetched data with a last-modified date, the fetcher can optionally cache
+// the response data in the fetch history and return cached data instead of a
+// 304 error. Set this to NO if you want to manually handle last-modified and
+// status 304 (Not changed) rather than be delivered cached data from previous
+// fetches. Default is NO. When a cache result is returned, the didFinish
+// selector is called with the data, and [fetcher status] returns 200.
+//
+// If the caller has already provided a fetchHistory dictionary, they can also
+// enable fetcher handling of 304 (not changed) status responses. By setting
+// shouldCacheDatedData to YES, the fetcher will save any response that has a
+// last modifed reply header into the fetchHistory. Then any future fetches
+// using that same fetchHistory will automatically load the cached response and
+// return it to the caller (with a status of 200) in place of the 304 server
+// reply.
+- (BOOL)shouldCacheDatedData;
+- (void)setShouldCacheDatedData:(BOOL)flag;
+
+// Delete the last-modified dates and cached data from the fetch history.
+- (void)clearDatedDataHistory;
+
+/// userData is retained for the convenience of the caller
+- (id)userData;
+- (void)setUserData:(id)theObj;
+
+// using the fetcher while a modal dialog is displayed requires setting the
+// run-loop modes to include NSModalPanelRunLoopMode
+//
+// setting run loop modes does nothing if they are not supported,
+// such as on 10.4
+- (NSArray *)runLoopModes;
+- (void)setRunLoopModes:(NSArray *)modes;
+
++ (BOOL)doesSupportRunLoopModes;
++ (NSArray *)defaultRunLoopModes;
++ (void)setDefaultRunLoopModes:(NSArray *)modes;
+
+// users who wish to replace GTMHTTPFetcher's use of NSURLConnection
+// can do so globally here. The replacement should be a subclass of
+// NSURLConnection.
++ (Class)connectionClass;
++ (void)setConnectionClass:(Class)theClass;
+
+@end
+
+// GTM HTTP Logging
+//
+// All traffic using GTMHTTPFetcher can be easily logged. Call
+//
+// [GTMHTTPFetcher setIsLoggingEnabled:YES];
+//
+// to begin generating log files.
+//
+// Log files are put into a folder on the desktop called "GTMHTTPDebugLogs"
+// unless another directory is specified with +setLoggingDirectory.
+//
+// Each run of an application gets a separate set of log files. An html
+// file is generated to simplify browsing the run's http transactions.
+// The html file includes javascript links for inline viewing of uploaded
+// and downloaded data.
+//
+// A symlink is created in the logs folder to simplify finding the html file
+// for the latest run of the application; the symlink is called
+//
+// AppName_http_log_newest.html
+//
+// For better viewing of XML logs, use Camino or Firefox rather than Safari.
+//
+// Projects may define GTM_HTTPFETCHER_ENABLE_LOGGING to 0 to remove all of the
+// logging code (it defaults to 1). By default, any data uploaded via PUT/POST
+// w/ and NSInputStream will not be logged. You can enable this logging by
+// defining GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING to 1 (it defaults to 0).
+//
+
+@interface GTMHTTPFetcher (GTMHTTPFetcherLogging)
+
+// Note: the default logs directory is ~/Desktop/GTMHTTPDebugLogs; it will be
+// created as needed. If a custom directory is set, the directory should
+// already exist.
++ (void)setLoggingDirectory:(NSString *)path;
++ (NSString *)loggingDirectory;
+
+// client apps can turn logging on and off
++ (void)setIsLoggingEnabled:(BOOL)flag;
++ (BOOL)isLoggingEnabled;
+
+// client apps can optionally specify process name and date string used in
+// log file names
++ (void)setLoggingProcessName:(NSString *)str;
++ (NSString *)loggingProcessName;
+
++ (void)setLoggingDateStamp:(NSString *)str;
++ (NSString *)loggingDateStamp;
+@end
diff --git a/Foundation/GTMHTTPFetcher.m b/Foundation/GTMHTTPFetcher.m
new file mode 100644
index 0000000..9853d0d
--- /dev/null
+++ b/Foundation/GTMHTTPFetcher.m
@@ -0,0 +1,1889 @@
+//
+// GTMHTTPFetcher.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.
+//
+
+#define GTMHTTPFETCHER_DEFINE_GLOBALS 1
+
+#import "GTMHTTPFetcher.h"
+#import "GTMDebugSelectorValidation.h"
+
+@interface GTMHTTPFetcher (GTMHTTPFetcherLoggingInternal)
+- (void)logFetchWithError:(NSError *)error;
+- (void)logCapturePostStream;
+@end
+
+// Make sure that if logging is disabled, the InputStream logging is also
+// diabled.
+#if !GTM_HTTPFETCHER_ENABLE_LOGGING
+# undef GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING
+# define GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING 0
+#endif // GTM_HTTPFETCHER_ENABLE_LOGGING
+
+#if GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING
+#import "GTMProgressMonitorInputStream.h"
+@interface GTMInputStreamLogger : GTMProgressMonitorInputStream
+// GTMInputStreamLogger wraps any NSInputStream used for uploading so we can
+// capture a copy of the data for the log
+@end
+#endif // !GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
+@interface NSURLConnection (LeopardMethodsOnTigerBuilds)
+- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately;
+- (void)start;
+- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;
+@end
+#endif
+
+NSString* const kGTMLastModifiedHeader = @"Last-Modified";
+NSString* const kGTMIfModifiedSinceHeader = @"If-Modified-Since";
+
+
+NSMutableArray* gGTMFetcherStaticCookies = nil;
+Class gGTMFetcherConnectionClass = nil;
+NSArray *gGTMFetcherDefaultRunLoopModes = nil;
+
+const NSTimeInterval kDefaultMaxRetryInterval = 60. * 10.; // 10 minutes
+
+@interface GTMHTTPFetcher (PrivateMethods)
+- (void)setCookies:(NSArray *)newCookies
+ inArray:(NSMutableArray *)cookieStorageArray;
+- (NSArray *)cookiesForURL:(NSURL *)theURL inArray:(NSMutableArray *)cookieStorageArray;
+- (void)handleCookiesForResponse:(NSURLResponse *)response;
+- (BOOL)shouldRetryNowForStatus:(NSInteger)status error:(NSError *)error;
+- (void)destroyRetryTimer;
+- (void)beginRetryTimer;
+- (void)primeTimerWithNewTimeInterval:(NSTimeInterval)secs;
+- (void)retryFetch;
+@end
+
+@implementation GTMHTTPFetcher
+
++ (GTMHTTPFetcher *)httpFetcherWithRequest:(NSURLRequest *)request {
+ return [[[GTMHTTPFetcher alloc] initWithRequest:request] autorelease];
+}
+
++ (void)initialize {
+ if (!gGTMFetcherStaticCookies) {
+ gGTMFetcherStaticCookies = [[NSMutableArray alloc] init];
+ }
+}
+
+- (id)init {
+ return [self initWithRequest:nil];
+}
+
+- (id)initWithRequest:(NSURLRequest *)request {
+ if ((self = [super init]) != nil) {
+
+ request_ = [request mutableCopy];
+
+ [self setCookieStorageMethod:kGTMHTTPFetcherCookieStorageMethodStatic];
+ }
+ return self;
+}
+
+// TODO: do we need finalize to call stopFetching?
+
+- (void)dealloc {
+ [self stopFetching]; // releases connection_
+
+ [request_ release];
+ [downloadedData_ release];
+ [credential_ release];
+ [proxyCredential_ release];
+ [postData_ release];
+ [postStream_ release];
+ [loggedStreamData_ release];
+ [response_ release];
+ [userData_ release];
+ [runLoopModes_ release];
+ [fetchHistory_ release];
+ [self destroyRetryTimer];
+
+ [super dealloc];
+}
+
+#pragma mark -
+
+// Begin fetching the URL. |delegate| is not retained
+// The delegate must provide and implement the finished and failed selectors.
+//
+// finishedSEL has a signature like:
+// - (void)fetcher:(GTMHTTPFetcher *)fetcher finishedWithData:(NSData *)data
+// failedSEL has a signature like:
+// - (void)fetcher:(GTMHTTPFetcher *)fetcher failedWithError:(NSError *)error
+
+- (BOOL)beginFetchWithDelegate:(id)delegate
+ didFinishSelector:(SEL)finishedSEL
+ didFailSelector:(SEL)failedSEL {
+
+ GTMAssertSelectorNilOrImplementedWithArguments(delegate, finishedSEL, @encode(GTMHTTPFetcher *), @encode(NSData *), NULL);
+ GTMAssertSelectorNilOrImplementedWithArguments(delegate, failedSEL, @encode(GTMHTTPFetcher *), @encode(NSError *), NULL);
+ GTMAssertSelectorNilOrImplementedWithArguments(delegate, receivedDataSEL_, @encode(GTMHTTPFetcher *), @encode(NSData *), NULL);
+ GTMAssertSelectorNilOrImplementedWithArguments(delegate, retrySEL_, @encode(GTMHTTPFetcher *), @encode(BOOL), @encode(NSError *), NULL);
+
+ if (connection_ != nil) {
+ _GTMDevAssert(connection_ != nil,
+ @"fetch object %@ being reused; this should never happen",
+ self);
+ goto CannotBeginFetch;
+ }
+
+ if (request_ == nil) {
+ _GTMDevAssert(request_ != nil, @"beginFetchWithDelegate requires a request");
+ goto CannotBeginFetch;
+ }
+
+ [downloadedData_ release];
+ downloadedData_ = nil;
+
+ [self setDelegate:delegate];
+ finishedSEL_ = finishedSEL;
+ failedSEL_ = failedSEL;
+
+ if (postData_ || postStream_) {
+ if ([request_ HTTPMethod] == nil || [[request_ HTTPMethod] isEqual:@"GET"]) {
+ [request_ setHTTPMethod:@"POST"];
+ }
+
+ if (postData_) {
+ [request_ setHTTPBody:postData_];
+ } else {
+
+ // if logging is enabled, it needs a buffer to accumulate data from any
+ // NSInputStream used for uploading. Logging will wrap the input
+ // stream with a stream that lets us keep a copy the data being read.
+ if ([GTMHTTPFetcher isLoggingEnabled] && postStream_ != nil) {
+ loggedStreamData_ = [[NSMutableData alloc] init];
+ [self logCapturePostStream];
+ }
+
+ [request_ setHTTPBodyStream:postStream_];
+ }
+ }
+
+ if (fetchHistory_) {
+
+ // If this URL is in the history, set the Last-Modified header field
+
+ // if we have a history, we're tracking across fetches, so we don't
+ // want to pull results from a cache
+ [request_ setCachePolicy:NSURLRequestReloadIgnoringCacheData];
+
+ NSDictionary* lastModifiedDict = [fetchHistory_ objectForKey:kGTMHTTPFetcherHistoryLastModifiedKey];
+ NSString* urlString = [[request_ URL] absoluteString];
+ NSString* lastModifiedStr = [lastModifiedDict objectForKey:urlString];
+
+ // servers don't want last-modified-ifs on POSTs, so check for a body
+ if (lastModifiedStr
+ && [request_ HTTPBody] == nil
+ && [request_ HTTPBodyStream] == nil) {
+
+ [request_ addValue:lastModifiedStr forHTTPHeaderField:kGTMIfModifiedSinceHeader];
+ }
+ }
+
+ // get cookies for this URL from our storage array, if
+ // we have a storage array
+ if (cookieStorageMethod_ != kGTMHTTPFetcherCookieStorageMethodSystemDefault) {
+
+ NSArray *cookies = [self cookiesForURL:[request_ URL]];
+
+ if ([cookies count]) {
+
+ NSDictionary *headerFields = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
+ NSString *cookieHeader = [headerFields objectForKey:@"Cookie"]; // key used in header dictionary
+ if (cookieHeader) {
+ [request_ addValue:cookieHeader forHTTPHeaderField:@"Cookie"]; // header name
+ }
+ }
+ }
+
+ // finally, start the connection
+
+ Class connectionClass = [[self class] connectionClass];
+
+ NSArray *runLoopModes = nil;
+
+ if ([[self class] doesSupportRunLoopModes]) {
+
+ // use the connection-specific run loop modes, if they were provided,
+ // or else use the GTMHTTPFetcher default run loop modes, if any
+ if (runLoopModes_) {
+ runLoopModes = runLoopModes_;
+ } else {
+ runLoopModes = gGTMFetcherDefaultRunLoopModes;
+ }
+ }
+
+ if ([runLoopModes count] == 0) {
+
+ // if no run loop modes were specified, then we'll start the connection
+ // on the current run loop in the current mode
+ connection_ = [[connectionClass connectionWithRequest:request_
+ delegate:self] retain];
+ } else {
+
+ // schedule on current run loop in the specified modes
+ connection_ = [[connectionClass alloc] initWithRequest:request_
+ delegate:self
+ startImmediately:NO];
+
+ for (int idx = 0; idx < [runLoopModes count]; idx++) {
+ NSString *mode = [runLoopModes objectAtIndex:idx];
+ [connection_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:mode];
+ }
+ [connection_ start];
+ }
+
+ if (!connection_) {
+ _GTMDevAssert(connection_ != nil,
+ @"beginFetchWithDelegate could not create a connection");
+ goto CannotBeginFetch;
+ }
+
+ // we'll retain the delegate only during the outstanding connection (similar
+ // to what Cocoa does with performSelectorOnMainThread:) since we'd crash
+ // if the delegate was released in the interim. We don't retain the selector
+ // at other times, to avoid vicious retain loops. This retain is balanced in
+ // the -stopFetch method.
+ [delegate_ retain];
+
+ downloadedData_ = [[NSMutableData alloc] init];
+ return YES;
+
+CannotBeginFetch:
+
+ if (failedSEL) {
+
+ NSError *error = [NSError errorWithDomain:kGTMHTTPFetcherErrorDomain
+ code:kGTMHTTPFetcherErrorDownloadFailed
+ userInfo:nil];
+
+ [[self retain] autorelease]; // in case the callback releases us
+
+ [delegate performSelector:failedSEL_
+ withObject:self
+ withObject:error];
+ }
+
+ return NO;
+}
+
+// Returns YES if this is in the process of fetching a URL, or waiting to
+// retry
+- (BOOL)isFetching {
+ return (connection_ != nil || retryTimer_ != nil);
+}
+
+// Returns the status code set in connection:didReceiveResponse:
+- (NSInteger)statusCode {
+
+ NSInteger statusCode;
+
+ if (response_ != nil
+ && [response_ respondsToSelector:@selector(statusCode)]) {
+
+ statusCode = [(NSHTTPURLResponse *)response_ statusCode];
+ } else {
+ // Default to zero, in hopes of hinting "Unknown" (we can't be
+ // sure that things are OK enough to use 200).
+ statusCode = 0;
+ }
+ return statusCode;
+}
+
+// Cancel the fetch of the URL that's currently in progress.
+- (void)stopFetching {
+ [self destroyRetryTimer];
+
+ if (connection_) {
+ // in case cancelling the connection calls this recursively, we want
+ // to ensure that we'll only release the connection and delegate once,
+ // so first set connection_ to nil
+
+ NSURLConnection* oldConnection = connection_;
+ connection_ = nil;
+
+ // this may be called in a callback from the connection, so use autorelease
+ [oldConnection cancel];
+ [oldConnection autorelease];
+
+ // balance the retain done when the connection was opened
+ [delegate_ release];
+ }
+}
+
+- (void)retryFetch {
+
+ id holdDelegate = [[delegate_ retain] autorelease];
+
+ [self stopFetching];
+
+ [self beginFetchWithDelegate:holdDelegate
+ didFinishSelector:finishedSEL_
+ didFailSelector:failedSEL_];
+}
+
+#pragma mark NSURLConnection Delegate Methods
+
+//
+// NSURLConnection Delegate Methods
+//
+
+// This method just says "follow all redirects", which _should_ be the default behavior,
+// According to file:///Developer/ADC%20Reference%20Library/documentation/Cocoa/Conceptual/URLLoadingSystem
+// but the redirects were not being followed until I added this method. May be
+// a bug in the NSURLConnection code, or the documentation.
+//
+// In OS X 10.4.8 and earlier, the redirect request doesn't
+// get the original's headers and body. This causes POSTs to fail.
+// So we construct a new request, a copy of the original, with overrides from the
+// redirect.
+//
+// Docs say that if redirectResponse is nil, just return the redirectRequest.
+
+- (NSURLRequest *)connection:(NSURLConnection *)connection
+ willSendRequest:(NSURLRequest *)redirectRequest
+ redirectResponse:(NSURLResponse *)redirectResponse {
+
+ if (redirectRequest && redirectResponse) {
+ NSMutableURLRequest *newRequest = [[request_ mutableCopy] autorelease];
+ // copy the URL
+ NSURL *redirectURL = [redirectRequest URL];
+ NSURL *url = [newRequest URL];
+
+ // disallow scheme changes (say, from https to http)
+ NSString *redirectScheme = [url scheme];
+ NSString *newScheme = [redirectURL scheme];
+ NSString *newResourceSpecifier = [redirectURL resourceSpecifier];
+
+ if ([redirectScheme caseInsensitiveCompare:@"http"] == NSOrderedSame
+ && newScheme != nil
+ && [newScheme caseInsensitiveCompare:@"https"] == NSOrderedSame) {
+
+ // allow the change from http to https
+ redirectScheme = newScheme;
+ }
+
+ NSString *newUrlString = [NSString stringWithFormat:@"%@:%@",
+ redirectScheme, newResourceSpecifier];
+
+ NSURL *newURL = [NSURL URLWithString:newUrlString];
+ [newRequest setURL:newURL];
+
+ // any headers in the redirect override headers in the original.
+ NSDictionary *redirectHeaders = [redirectRequest allHTTPHeaderFields];
+ if (redirectHeaders) {
+ NSEnumerator *enumerator = [redirectHeaders keyEnumerator];
+ NSString *key;
+ while (nil != (key = [enumerator nextObject])) {
+ NSString *value = [redirectHeaders objectForKey:key];
+ [newRequest setValue:value forHTTPHeaderField:key];
+ }
+ }
+ redirectRequest = newRequest;
+
+ // save cookies from the response
+ [self handleCookiesForResponse:redirectResponse];
+
+ // log the response we just received
+ [self setResponse:redirectResponse];
+ [self logFetchWithError:nil];
+
+ // update the request for future logging
+ [self setRequest:redirectRequest];
+}
+ return redirectRequest;
+}
+
+- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
+
+ // this method is called when the server has determined that it
+ // has enough information to create the NSURLResponse
+ // it can be called multiple times, for example in the case of a
+ // redirect, so each time we reset the data.
+ [downloadedData_ setLength:0];
+
+ [self setResponse:response];
+
+ // save cookies from the response
+ [self handleCookiesForResponse:response];
+}
+
+
+// handleCookiesForResponse: handles storage of cookies for responses passed to
+// connection:willSendRequest:redirectResponse: and connection:didReceiveResponse:
+- (void)handleCookiesForResponse:(NSURLResponse *)response {
+
+ if (cookieStorageMethod_ == kGTMHTTPFetcherCookieStorageMethodSystemDefault) {
+
+ // do nothing special for NSURLConnection's default storage mechanism
+
+ } else if ([response respondsToSelector:@selector(allHeaderFields)]) {
+
+ // grab the cookies from the header as NSHTTPCookies and store them either
+ // into our static array or into the fetchHistory
+
+ NSDictionary *responseHeaderFields = [(NSHTTPURLResponse *)response allHeaderFields];
+ if (responseHeaderFields) {
+
+ NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:responseHeaderFields
+ forURL:[response URL]];
+ if ([cookies count] > 0) {
+
+ NSMutableArray *cookieArray = nil;
+
+ // static cookies are stored in gGTMFetcherStaticCookies; fetchHistory
+ // cookies are stored in fetchHistory_'s kGTMHTTPFetcherHistoryCookiesKey
+
+ if (cookieStorageMethod_ == kGTMHTTPFetcherCookieStorageMethodStatic) {
+
+ cookieArray = gGTMFetcherStaticCookies;
+
+ } else if (cookieStorageMethod_ == kGTMHTTPFetcherCookieStorageMethodFetchHistory
+ && fetchHistory_ != nil) {
+
+ cookieArray = [fetchHistory_ objectForKey:kGTMHTTPFetcherHistoryCookiesKey];
+ if (cookieArray == nil) {
+ cookieArray = [NSMutableArray array];
+ [fetchHistory_ setObject:cookieArray forKey:kGTMHTTPFetcherHistoryCookiesKey];
+ }
+ }
+
+ if (cookieArray) {
+ @synchronized(cookieArray) {
+ [self setCookies:cookies inArray:cookieArray];
+ }
+ }
+ }
+ }
+ }
+}
+
+-(void)connection:(NSURLConnection *)connection
+ didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
+
+ if ([challenge previousFailureCount] <= 2) {
+
+ NSURLCredential *credential = credential_;
+
+ if ([[challenge protectionSpace] isProxy] && proxyCredential_ != nil) {
+ credential = proxyCredential_;
+ }
+
+ // Here, if credential is still nil, then we *could* try to get it from
+ // NSURLCredentialStorage's defaultCredentialForProtectionSpace:.
+ // We don't, because we're assuming:
+ //
+ // - for server credentials, we only want ones supplied by the program
+ // calling http fetcher
+ // - for proxy credentials, if one were necessary and available in the
+ // keychain, it would've been found automatically by NSURLConnection
+ // and this challenge delegate method never would've been called
+ // anyway
+
+ if (credential) {
+ // try the credential
+ [[challenge sender] useCredential:credential
+ forAuthenticationChallenge:challenge];
+ return;
+ }
+ }
+
+ // If we don't have credentials, or we've already failed auth 3x...
+ [[challenge sender] cancelAuthenticationChallenge:challenge];
+
+ // report the error, putting the challenge as a value in the userInfo
+ // dictionary
+ NSDictionary *userInfo = [NSDictionary dictionaryWithObject:challenge
+ forKey:kGTMHTTPFetcherErrorChallengeKey];
+
+ NSError *error = [NSError errorWithDomain:kGTMHTTPFetcherErrorDomain
+ code:kGTMHTTPFetcherErrorAuthenticationChallengeFailed
+ userInfo:userInfo];
+
+ [self connection:connection didFailWithError:error];
+}
+
+
+
+- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
+ [downloadedData_ appendData:data];
+
+ if (receivedDataSEL_) {
+ [delegate_ performSelector:receivedDataSEL_
+ withObject:self
+ withObject:downloadedData_];
+ }
+}
+
+- (void)updateFetchHistory {
+
+ if (fetchHistory_) {
+
+ NSString* urlString = [[request_ URL] absoluteString];
+ if ([response_ respondsToSelector:@selector(allHeaderFields)]) {
+ NSDictionary *headers = [(NSHTTPURLResponse *)response_ allHeaderFields];
+ NSString* lastModifiedStr = [headers objectForKey:kGTMLastModifiedHeader];
+
+ // get the dictionary mapping URLs to last-modified dates
+ NSMutableDictionary* lastModifiedDict = [fetchHistory_ objectForKey:kGTMHTTPFetcherHistoryLastModifiedKey];
+ if (!lastModifiedDict) {
+ lastModifiedDict = [NSMutableDictionary dictionary];
+ [fetchHistory_ setObject:lastModifiedDict forKey:kGTMHTTPFetcherHistoryLastModifiedKey];
+ }
+
+ NSMutableDictionary* datedDataCache = nil;
+ if (shouldCacheDatedData_) {
+ // get the dictionary mapping URLs to cached, dated data
+ datedDataCache = [fetchHistory_ objectForKey:kGTMHTTPFetcherHistoryDatedDataKey];
+ if (!datedDataCache) {
+ datedDataCache = [NSMutableDictionary dictionary];
+ [fetchHistory_ setObject:datedDataCache forKey:kGTMHTTPFetcherHistoryDatedDataKey];
+ }
+ }
+
+ NSInteger statusCode = [self statusCode];
+ if (statusCode != kGTMHTTPFetcherStatusNotModified) {
+
+ // save this last modified date string for successful results (<300)
+ // If there's no last modified string, clear the dictionary
+ // entry for this URL. Also cache or delete the data, if appropriate
+ // (when datedDataCache is non-nil.)
+ if (lastModifiedStr && statusCode < 300) {
+ [lastModifiedDict setValue:lastModifiedStr forKey:urlString];
+ [datedDataCache setValue:downloadedData_ forKey:urlString];
+ } else {
+ [lastModifiedDict removeObjectForKey:urlString];
+ [datedDataCache removeObjectForKey:urlString];
+ }
+ }
+ }
+ }
+}
+
+// for error 304's ("Not Modified") where we've cached the data, return status
+// 200 ("OK") to the caller (but leave the fetcher status as 304)
+// and copy the cached data to downloadedData_.
+// For other errors or if there's no cached data, just return the actual status.
+- (NSInteger)statusAfterHandlingNotModifiedError {
+
+ NSInteger status = [self statusCode];
+ if (status == kGTMHTTPFetcherStatusNotModified && shouldCacheDatedData_) {
+
+ // get the dictionary of URLs and data
+ NSString* urlString = [[request_ URL] absoluteString];
+
+ NSDictionary* datedDataCache = [fetchHistory_ objectForKey:kGTMHTTPFetcherHistoryDatedDataKey];
+ NSData* cachedData = [datedDataCache objectForKey:urlString];
+
+ if (cachedData) {
+ // copy our stored data, and forge the status to pass on to the delegate
+ [downloadedData_ setData:cachedData];
+ status = 200;
+ }
+ }
+ return status;
+}
+
+- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
+
+ [self updateFetchHistory];
+
+ [[self retain] autorelease]; // in case the callback releases us
+
+ [self logFetchWithError:nil];
+
+ NSInteger status = [self statusAfterHandlingNotModifiedError];
+
+ if (status >= 300) {
+
+ if ([self shouldRetryNowForStatus:status error:nil]) {
+
+ [self beginRetryTimer];
+
+ } else {
+ // not retrying
+
+ // did they want failure notifications?
+ if (failedSEL_) {
+
+ NSDictionary *userInfo =
+ [NSDictionary dictionaryWithObject:downloadedData_
+ forKey:kGTMHTTPFetcherStatusDataKey];
+ NSError *error = [NSError errorWithDomain:kGTMHTTPFetcherStatusDomain
+ code:status
+ userInfo:userInfo];
+
+ [delegate_ performSelector:failedSEL_
+ withObject:self
+ withObject:error];
+ }
+ // we're done fetching
+ [self stopFetching];
+ }
+
+ } else if (finishedSEL_) {
+
+ // successful http status (under 300)
+ [delegate_ performSelector:finishedSEL_
+ withObject:self
+ withObject:downloadedData_];
+ [self stopFetching];
+ }
+
+}
+
+- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
+
+ [self logFetchWithError:error];
+
+ if ([self shouldRetryNowForStatus:0 error:error]) {
+
+ [self beginRetryTimer];
+
+ } else {
+
+ if (failedSEL_) {
+ [[self retain] autorelease]; // in case the callback releases us
+
+ [delegate_ performSelector:failedSEL_
+ withObject:self
+ withObject:error];
+ }
+
+ [self stopFetching];
+ }
+}
+
+#pragma mark Retries
+
+- (BOOL)isRetryError:(NSError *)error {
+
+ struct retryRecord {
+ NSString *const domain;
+ int code;
+ };
+
+ struct retryRecord retries[] = {
+ { kGTMHTTPFetcherStatusDomain, 408 }, // request timeout
+ { kGTMHTTPFetcherStatusDomain, 503 }, // service unavailable
+ { kGTMHTTPFetcherStatusDomain, 504 }, // request timeout
+ { NSURLErrorDomain, NSURLErrorTimedOut },
+ { NSURLErrorDomain, NSURLErrorNetworkConnectionLost },
+ { nil, 0 }
+ };
+
+ // NSError's isEqual always returns false for equal but distinct instances
+ // of NSError, so we have to compare the domain and code values explicitly
+
+ for (int idx = 0; retries[idx].domain != nil; idx++) {
+
+ if ([[error domain] isEqual:retries[idx].domain]
+ && [error code] == retries[idx].code) {
+
+ return YES;
+ }
+ }
+ return NO;
+}
+
+
+// shouldRetryNowForStatus:error: returns YES if the user has enabled retries
+// and the status or error is one that is suitable for retrying. "Suitable"
+// means either the isRetryError:'s list contains the status or error, or the
+// user's retrySelector: is present and returns YES when called.
+- (BOOL)shouldRetryNowForStatus:(NSInteger)status
+ error:(NSError *)error {
+
+ if ([self isRetryEnabled]) {
+
+ if ([self nextRetryInterval] < [self maxRetryInterval]) {
+
+ if (error == nil) {
+ // make an error for the status
+ error = [NSError errorWithDomain:kGTMHTTPFetcherStatusDomain
+ code:status
+ userInfo:nil];
+ }
+
+ BOOL willRetry = [self isRetryError:error];
+
+ if (retrySEL_) {
+ NSMethodSignature *signature = [delegate_ methodSignatureForSelector:retrySEL_];
+ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
+ [invocation setSelector:retrySEL_];
+ [invocation setTarget:delegate_];
+ [invocation setArgument:&self atIndex:2];
+ [invocation setArgument:&willRetry atIndex:3];
+ [invocation setArgument:&error atIndex:4];
+ [invocation invoke];
+
+ [invocation getReturnValue:&willRetry];
+ }
+
+ return willRetry;
+ }
+ }
+
+ return NO;
+}
+
+- (void)beginRetryTimer {
+
+ NSTimeInterval nextInterval = [self nextRetryInterval];
+ NSTimeInterval maxInterval = [self maxRetryInterval];
+
+ NSTimeInterval newInterval = MIN(nextInterval, maxInterval);
+
+ [self primeTimerWithNewTimeInterval:newInterval];
+}
+
+- (void)primeTimerWithNewTimeInterval:(NSTimeInterval)secs {
+
+ [self destroyRetryTimer];
+
+ lastRetryInterval_ = secs;
+
+ retryTimer_ = [NSTimer scheduledTimerWithTimeInterval:secs
+ target:self
+ selector:@selector(retryTimerFired:)
+ userInfo:nil
+ repeats:NO];
+ [retryTimer_ retain];
+}
+
+- (void)retryTimerFired:(NSTimer *)timer {
+
+ [self destroyRetryTimer];
+
+ retryCount_++;
+
+ [self retryFetch];
+}
+
+- (void)destroyRetryTimer {
+
+ [retryTimer_ invalidate];
+ [retryTimer_ autorelease];
+ retryTimer_ = nil;
+}
+
+- (unsigned int)retryCount {
+ return retryCount_;
+}
+
+- (NSTimeInterval)nextRetryInterval {
+ // the next wait interval is the factor (2.0) times the last interval,
+ // but never less than the minimum interval
+ NSTimeInterval secs = lastRetryInterval_ * retryFactor_;
+ secs = MIN(secs, maxRetryInterval_);
+ secs = MAX(secs, minRetryInterval_);
+
+ return secs;
+}
+
+- (BOOL)isRetryEnabled {
+ return isRetryEnabled_;
+}
+
+- (void)setIsRetryEnabled:(BOOL)flag {
+
+ if (flag && !isRetryEnabled_) {
+ // We defer initializing these until the user calls setIsRetryEnabled
+ // to avoid seeding the random number generator if it's not needed.
+ // However, it means min and max intervals for this fetcher are reset
+ // as a side effect of calling setIsRetryEnabled.
+ //
+ // seed the random value, and make an initial retry interval
+ // random between 1.0 and 2.0 seconds
+ srandomdev();
+ [self setMinRetryInterval:0.0];
+ [self setMaxRetryInterval:kDefaultMaxRetryInterval];
+ [self setRetryFactor:2.0];
+ lastRetryInterval_ = 0.0;
+ }
+ isRetryEnabled_ = flag;
+};
+
+- (SEL)retrySelector {
+ return retrySEL_;
+}
+
+- (void)setRetrySelector:(SEL)theSelector {
+ retrySEL_ = theSelector;
+}
+
+- (NSTimeInterval)maxRetryInterval {
+ return maxRetryInterval_;
+}
+
+- (void)setMaxRetryInterval:(NSTimeInterval)secs {
+ if (secs > 0) {
+ maxRetryInterval_ = secs;
+ } else {
+ maxRetryInterval_ = kDefaultMaxRetryInterval;
+ }
+}
+
+- (double)minRetryInterval {
+ return minRetryInterval_;
+}
+
+- (void)setMinRetryInterval:(NSTimeInterval)secs {
+ if (secs > 0) {
+ minRetryInterval_ = secs;
+ } else {
+ // set min interval to a random value between 1.0 and 2.0 seconds
+ // so that if multiple clients start retrying at the same time, they'll
+ // repeat at different times and avoid overloading the server
+ minRetryInterval_ = 1.0 + ((double)(random() & 0x0FFFF) / (double) 0x0FFFF);
+ }
+}
+
+- (double)retryFactor {
+ return retryFactor_;
+}
+
+- (void)setRetryFactor:(double)multiplier {
+ retryFactor_ = multiplier;
+}
+
+#pragma mark Getters and Setters
+
+- (NSMutableURLRequest *)request {
+ return request_;
+}
+
+- (void)setRequest:(NSURLRequest *)theRequest {
+ [request_ autorelease];
+ request_ = [theRequest mutableCopy];
+}
+
+- (NSURLCredential *)credential {
+ return credential_;
+}
+
+- (void)setCredential:(NSURLCredential *)theCredential {
+ [credential_ autorelease];
+ credential_ = [theCredential retain];
+}
+
+- (NSURLCredential *)proxyCredential {
+ return proxyCredential_;
+}
+
+- (void)setProxyCredential:(NSURLCredential *)theCredential {
+ [proxyCredential_ autorelease];
+ proxyCredential_ = [theCredential retain];
+}
+
+- (NSData *)postData {
+ return postData_;
+}
+
+- (void)setPostData:(NSData *)theData {
+ [postData_ autorelease];
+ postData_ = [theData retain];
+}
+
+- (NSInputStream *)postStream {
+ return postStream_;
+}
+
+- (void)setPostStream:(NSInputStream *)theStream {
+ [postStream_ autorelease];
+ postStream_ = [theStream retain];
+}
+
+- (GTMHTTPFetcherCookieStorageMethod)cookieStorageMethod {
+ return cookieStorageMethod_;
+}
+
+- (void)setCookieStorageMethod:(GTMHTTPFetcherCookieStorageMethod)method {
+
+ cookieStorageMethod_ = method;
+
+ if (method == kGTMHTTPFetcherCookieStorageMethodSystemDefault) {
+ [request_ setHTTPShouldHandleCookies:YES];
+ } else {
+ [request_ setHTTPShouldHandleCookies:NO];
+ }
+}
+
+- (id)delegate {
+ return delegate_;
+}
+
+- (void)setDelegate:(id)theDelegate {
+
+ // we retain delegate_ only during the life of the connection
+ if (connection_) {
+ [delegate_ autorelease];
+ delegate_ = [theDelegate retain];
+ } else {
+ delegate_ = theDelegate;
+ }
+}
+
+- (SEL)receivedDataSelector {
+ return receivedDataSEL_;
+}
+
+- (void)setReceivedDataSelector:(SEL)theSelector {
+ receivedDataSEL_ = theSelector;
+}
+
+- (NSURLResponse *)response {
+ return response_;
+}
+
+- (void)setResponse:(NSURLResponse *)response {
+ [response_ autorelease];
+ response_ = [response retain];
+}
+
+- (NSMutableDictionary *)fetchHistory {
+ return fetchHistory_;
+}
+
+- (void)setFetchHistory:(NSMutableDictionary *)fetchHistory {
+ [fetchHistory_ autorelease];
+ fetchHistory_ = [fetchHistory retain];
+
+ if (fetchHistory_ != nil) {
+ [self setCookieStorageMethod:kGTMHTTPFetcherCookieStorageMethodFetchHistory];
+ } else {
+ [self setCookieStorageMethod:kGTMHTTPFetcherCookieStorageMethodStatic];
+ }
+}
+
+- (void)setShouldCacheDatedData:(BOOL)flag {
+ shouldCacheDatedData_ = flag;
+ if (!flag) {
+ [self clearDatedDataHistory];
+ }
+}
+
+- (BOOL)shouldCacheDatedData {
+ return shouldCacheDatedData_;
+}
+
+// delete last-modified dates and cached data from the fetch history
+- (void)clearDatedDataHistory {
+ [fetchHistory_ removeObjectForKey:kGTMHTTPFetcherHistoryLastModifiedKey];
+ [fetchHistory_ removeObjectForKey:kGTMHTTPFetcherHistoryDatedDataKey];
+}
+
+- (id)userData {
+ return userData_;
+}
+
+- (void)setUserData:(id)theObj {
+ [userData_ autorelease];
+ userData_ = [theObj retain];
+}
+
+- (NSArray *)runLoopModes {
+ return runLoopModes_;
+}
+
+- (void)setRunLoopModes:(NSArray *)modes {
+ [runLoopModes_ autorelease];
+ runLoopModes_ = [modes retain];
+}
+
++ (BOOL)doesSupportRunLoopModes {
+ SEL sel = @selector(initWithRequest:delegate:startImmediately:);
+ return [NSURLConnection instancesRespondToSelector:sel];
+}
+
++ (NSArray *)defaultRunLoopModes {
+ return gGTMFetcherDefaultRunLoopModes;
+}
+
++ (void)setDefaultRunLoopModes:(NSArray *)modes {
+ [gGTMFetcherDefaultRunLoopModes autorelease];
+ gGTMFetcherDefaultRunLoopModes = [modes retain];
+}
+
++ (Class)connectionClass {
+ if (gGTMFetcherConnectionClass == nil) {
+ gGTMFetcherConnectionClass = [NSURLConnection class];
+ }
+ return gGTMFetcherConnectionClass;
+}
+
++ (void)setConnectionClass:(Class)theClass {
+ gGTMFetcherConnectionClass = theClass;
+}
+
+#pragma mark Cookies
+
+// return a cookie from the array with the same name, domain, and path as the
+// given cookie, or else return nil if none found
+//
+// Both the cookie being tested and all cookies in cookieStorageArray should
+// be valid (non-nil name, domains, paths)
+- (NSHTTPCookie *)cookieMatchingCookie:(NSHTTPCookie *)cookie
+ inArray:(NSArray *)cookieStorageArray {
+
+ NSUInteger numberOfCookies = [cookieStorageArray count];
+ NSString *name = [cookie name];
+ NSString *domain = [cookie domain];
+ NSString *path = [cookie path];
+
+ _GTMDevAssert(name && domain && path,
+ @"Invalid cookie (name:%@ domain:%@ path:%@)",
+ name, domain, path);
+
+ for (NSUInteger idx = 0; idx < numberOfCookies; idx++) {
+
+ NSHTTPCookie *storedCookie = [cookieStorageArray objectAtIndex:idx];
+
+ if ([[storedCookie name] isEqual:name]
+ && [[storedCookie domain] isEqual:domain]
+ && [[storedCookie path] isEqual:path]) {
+
+ return storedCookie;
+ }
+ }
+ return nil;
+}
+
+// remove any expired cookies from the array, excluding cookies with nil
+// expirations
+- (void)removeExpiredCookiesInArray:(NSMutableArray *)cookieStorageArray {
+
+ // count backwards since we're deleting items from the array
+ for (NSInteger idx = [cookieStorageArray count] - 1; idx >= 0; idx--) {
+
+ NSHTTPCookie *storedCookie = [cookieStorageArray objectAtIndex:idx];
+
+ NSDate *expiresDate = [storedCookie expiresDate];
+ if (expiresDate && [expiresDate timeIntervalSinceNow] < 0) {
+ [cookieStorageArray removeObjectAtIndex:idx];
+ }
+ }
+}
+
+
+// retrieve all cookies appropriate for the given URL, considering
+// domain, path, cookie name, expiration, security setting.
+// Side effect: removed expired cookies from the storage array
+- (NSArray *)cookiesForURL:(NSURL *)theURL inArray:(NSMutableArray *)cookieStorageArray {
+
+ [self removeExpiredCookiesInArray:cookieStorageArray];
+
+ NSMutableArray *foundCookies = [NSMutableArray array];
+
+ // we'll prepend "." to the desired domain, since we want the
+ // actual domain "nytimes.com" to still match the cookie domain ".nytimes.com"
+ // when we check it below with hasSuffix
+ NSString *host = [theURL host];
+ NSString *path = [theURL path];
+ NSString *scheme = [theURL scheme];
+
+ NSString *domain = nil;
+ if ([host isEqual:@"localhost"]) {
+ // the domain stored into NSHTTPCookies for localhost is "localhost.local"
+ domain = @"localhost.local";
+ } else {
+ if (host) {
+ domain = [@"." stringByAppendingString:host];
+ }
+ }
+
+ NSUInteger numberOfCookies = [cookieStorageArray count];
+ for (NSUInteger idx = 0; idx < numberOfCookies; idx++) {
+
+ NSHTTPCookie *storedCookie = [cookieStorageArray objectAtIndex:idx];
+
+ NSString *cookieDomain = [storedCookie domain];
+ NSString *cookiePath = [storedCookie path];
+ BOOL cookieIsSecure = [storedCookie isSecure];
+
+ BOOL domainIsOK = [domain hasSuffix:cookieDomain];
+ BOOL pathIsOK = [cookiePath isEqual:@"/"] || [path hasPrefix:cookiePath];
+ BOOL secureIsOK = (!cookieIsSecure) || [scheme isEqual:@"https"];
+
+ if (domainIsOK && pathIsOK && secureIsOK) {
+ [foundCookies addObject:storedCookie];
+ }
+ }
+ return foundCookies;
+}
+
+// return cookies for the given URL using the current cookie storage method
+- (NSArray *)cookiesForURL:(NSURL *)theURL {
+
+ NSArray *cookies = nil;
+ NSMutableArray *cookieStorageArray = nil;
+
+ if (cookieStorageMethod_ == kGTMHTTPFetcherCookieStorageMethodStatic) {
+ cookieStorageArray = gGTMFetcherStaticCookies;
+ } else if (cookieStorageMethod_ == kGTMHTTPFetcherCookieStorageMethodFetchHistory) {
+ cookieStorageArray = [fetchHistory_ objectForKey:kGTMHTTPFetcherHistoryCookiesKey];
+ } else {
+ // kGTMHTTPFetcherCookieStorageMethodSystemDefault
+ cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:theURL];
+ }
+
+ if (cookieStorageArray) {
+
+ @synchronized(cookieStorageArray) {
+
+ // cookiesForURL returns a new array of immutable NSCookie objects
+ // from cookieStorageArray
+ cookies = [self cookiesForURL:theURL
+ inArray:cookieStorageArray];
+ }
+ }
+ return cookies;
+}
+
+
+// add all cookies in the array |newCookies| to the storage array,
+// replacing cookies in the storage array as appropriate
+// Side effect: removes expired cookies from the storage array
+- (void)setCookies:(NSArray *)newCookies
+ inArray:(NSMutableArray *)cookieStorageArray {
+
+ [self removeExpiredCookiesInArray:cookieStorageArray];
+
+ NSEnumerator *newCookieEnum = [newCookies objectEnumerator];
+ NSHTTPCookie *newCookie;
+
+ while ((newCookie = [newCookieEnum nextObject]) != nil) {
+
+ if ([[newCookie name] length] > 0
+ && [[newCookie domain] length] > 0
+ && [[newCookie path] length] > 0) {
+
+ // remove the cookie if it's currently in the array
+ NSHTTPCookie *oldCookie = [self cookieMatchingCookie:newCookie
+ inArray:cookieStorageArray];
+ if (oldCookie) {
+ [cookieStorageArray removeObject:oldCookie];
+ }
+
+ // make sure the cookie hasn't already expired
+ NSDate *expiresDate = [newCookie expiresDate];
+ if ((!expiresDate) || [expiresDate timeIntervalSinceNow] > 0) {
+ [cookieStorageArray addObject:newCookie];
+ }
+
+ } else {
+ _GTMDevAssert(NO, @"Cookie incomplete: %@", newCookie);
+ }
+ }
+}
+@end
+
+#pragma mark Logging
+
+// NOTE: Threads and Logging
+//
+// All the NSURLConnection callbacks happen on one thread, so we don't have
+// to put any synchronization into the logging code. Yes, the state around
+// logging (it's directory, etc.) could use it, but for now, that's punted.
+
+
+// We don't invoke Leopard methods on 10.4, because we check if the methods are
+// implemented before invoking it, but we need to be able to compile without
+// warnings.
+// This declaration means if you target <=10.4, this method will compile
+// without complaint in this source, so you must test with
+// -respondsToSelector:, too.
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
+@interface NSFileManager (LeopardMethodsOnTigerBuilds)
+- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error;
+@end
+#endif
+// The iPhone Foundation removes the deprecated removeFileAtPath:handler:
+#if GTM_IPHONE_SDK
+@interface NSFileManager (TigerMethodsOniPhoneBuilds)
+- (BOOL)removeFileAtPath:(NSString *)path handler:(id)handler;
+@end
+#endif
+
+@implementation GTMHTTPFetcher (GTMHTTPFetcherLogging)
+
+// if GTM_HTTPFETCHER_ENABLE_LOGGING is defined by the user's project then
+// logging code will be compiled into the framework
+
+#if !GTM_HTTPFETCHER_ENABLE_LOGGING
+- (void)logFetchWithError:(NSError *)error {}
+
++ (void)setLoggingDirectory:(NSString *)path {}
++ (NSString *)loggingDirectory {return nil;}
+
++ (void)setIsLoggingEnabled:(BOOL)flag {}
++ (BOOL)isLoggingEnabled {return NO;}
+
++ (void)setLoggingProcessName:(NSString *)str {}
++ (NSString *)loggingProcessName {return nil;}
+
++ (void)setLoggingDateStamp:(NSString *)str {}
++ (NSString *)loggingDateStamp {return nil;}
+
+- (void)appendLoggedStreamData:(NSData *)newData {}
+- (void)logCapturePostStream {}
+#else // GTM_HTTPFETCHER_ENABLE_LOGGING
+
+// fetchers come and fetchers go, but statics are forever
+static BOOL gIsLoggingEnabled = NO;
+static NSString *gLoggingDirectoryPath = nil;
+static NSString *gLoggingDateStamp = nil;
+static NSString* gLoggingProcessName = nil;
+
++ (void)setLoggingDirectory:(NSString *)path {
+ [gLoggingDirectoryPath autorelease];
+ gLoggingDirectoryPath = [path copy];
+}
+
++ (NSString *)loggingDirectory {
+
+ if (!gLoggingDirectoryPath) {
+
+#if GTM_IPHONE_SDK
+ // default to a directory called GTMHTTPDebugLogs into a sandbox-safe
+ // directory that a devloper can find easily, the application home
+ NSArray *arr = [NSArray arrayWithObject:NSHomeDirectory()];
+#else
+ // default to a directory called GTMHTTPDebugLogs in the desktop folder
+ NSArray *arr = NSSearchPathForDirectoriesInDomains(NSDesktopDirectory,
+ NSUserDomainMask, YES);
+#endif
+
+ if ([arr count] > 0) {
+ NSString *const kGTMLogFolderName = @"GTMHTTPDebugLogs";
+
+ NSString *desktopPath = [arr objectAtIndex:0];
+ NSString *logsFolderPath = [desktopPath stringByAppendingPathComponent:kGTMLogFolderName];
+
+ BOOL doesFolderExist;
+ BOOL isDir = NO;
+ NSFileManager *fileManager = [NSFileManager defaultManager];
+ doesFolderExist = [fileManager fileExistsAtPath:logsFolderPath
+ isDirectory:&isDir];
+
+ if (!doesFolderExist) {
+ // make the directory
+ doesFolderExist = [fileManager createDirectoryAtPath:logsFolderPath
+ attributes:nil];
+ }
+
+ if (doesFolderExist) {
+ // it's there; store it in the global
+ gLoggingDirectoryPath = [logsFolderPath copy];
+ }
+ }
+ }
+ return gLoggingDirectoryPath;
+}
+
++ (void)setIsLoggingEnabled:(BOOL)flag {
+ gIsLoggingEnabled = flag;
+}
+
++ (BOOL)isLoggingEnabled {
+ return gIsLoggingEnabled;
+}
+
++ (void)setLoggingProcessName:(NSString *)str {
+ [gLoggingProcessName release];
+ gLoggingProcessName = [str copy];
+}
+
++ (NSString *)loggingProcessName {
+
+ // get the process name (once per run) replacing spaces with underscores
+ if (!gLoggingProcessName) {
+
+ NSString *procName = [[NSProcessInfo processInfo] processName];
+ NSMutableString *loggingProcessName;
+ loggingProcessName = [[NSMutableString alloc] initWithString:procName];
+
+ [loggingProcessName replaceOccurrencesOfString:@" "
+ withString:@"_"
+ options:0
+ range:NSMakeRange(0, [gLoggingProcessName length])];
+ gLoggingProcessName = loggingProcessName;
+ }
+ return gLoggingProcessName;
+}
+
++ (void)setLoggingDateStamp:(NSString *)str {
+ [gLoggingDateStamp release];
+ gLoggingDateStamp = [str copy];
+}
+
++ (NSString *)loggingDateStamp {
+ // we'll pick one date stamp per run, so a run that starts at a later second
+ // will get a unique results html file
+ if (!gLoggingDateStamp) {
+ // produce a string like 08-21_01-41-23PM
+
+ NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
+ [formatter setFormatterBehavior:NSDateFormatterBehavior10_4];
+ [formatter setDateFormat:@"M-dd_hh-mm-ssa"];
+
+ gLoggingDateStamp = [[formatter stringFromDate:[NSDate date]] retain] ;
+ }
+ return gLoggingDateStamp;
+}
+
+- (NSString *)cleanParameterFollowing:(NSString *)paramName
+ fromString:(NSString *)originalStr {
+ // We don't want the password written to disk
+ //
+ // find "&Passwd=" in the string, and replace it and the stuff that
+ // follows it with "Passwd=_snip_"
+
+ NSRange passwdRange = [originalStr rangeOfString:@"&Passwd="];
+ if (passwdRange.location != NSNotFound) {
+
+ // we found Passwd=; find the & that follows the parameter
+ NSUInteger origLength = [originalStr length];
+ NSRange restOfString = NSMakeRange(passwdRange.location+1,
+ origLength - passwdRange.location - 1);
+ NSRange rangeOfFollowingAmp = [originalStr rangeOfString:@"&"
+ options:0
+ range:restOfString];
+ NSRange replaceRange;
+ if (rangeOfFollowingAmp.location == NSNotFound) {
+ // found no other & so replace to end of string
+ replaceRange = NSMakeRange(passwdRange.location,
+ rangeOfFollowingAmp.location - passwdRange.location);
+ } else {
+ // another parameter after &Passwd=foo
+ replaceRange = NSMakeRange(passwdRange.location,
+ rangeOfFollowingAmp.location - passwdRange.location);
+ }
+
+ NSMutableString *result = [NSMutableString stringWithString:originalStr];
+ NSString *replacement = [NSString stringWithFormat:@"%@_snip_", paramName];
+
+ [result replaceCharactersInRange:replaceRange withString:replacement];
+ return result;
+ }
+ return originalStr;
+}
+
+// stringFromStreamData creates a string given the supplied data
+//
+// If NSString can create a UTF-8 string from the data, then that is returned.
+//
+// Otherwise, this routine tries to find a MIME boundary at the beginning of
+// the data block, and uses that to break up the data into parts. Each part
+// will be used to try to make a UTF-8 string. For parts that fail, a
+// replacement string showing the part header and <<n bytes>> is supplied
+// in place of the binary data.
+
+- (NSString *)stringFromStreamData:(NSData *)data {
+
+ if (data == nil) return nil;
+
+ // optimistically, see if the whole data block is UTF-8
+ NSString *streamDataStr = [[[NSString alloc] initWithData:data
+ encoding:NSUTF8StringEncoding] autorelease];
+ if (streamDataStr) return streamDataStr;
+
+ // Munge a buffer by replacing non-ASCII bytes with underscores,
+ // and turn that munged buffer an NSString. That gives us a string
+ // we can use with NSScanner.
+ NSMutableData *mutableData = [NSMutableData dataWithData:data];
+ unsigned char *bytes = [mutableData mutableBytes];
+
+ for (int idx = 0; idx < [mutableData length]; idx++) {
+ if (bytes[idx] > 0x7F || bytes[idx] == 0) {
+ bytes[idx] = '_';
+ }
+ }
+
+ NSString *mungedStr = [[[NSString alloc] initWithData:mutableData
+ encoding:NSUTF8StringEncoding] autorelease];
+ if (mungedStr != nil) {
+
+ // scan for the boundary string
+ NSString *boundary = nil;
+ NSScanner *scanner = [NSScanner scannerWithString:mungedStr];
+
+ if ([scanner scanUpToString:@"\r\n" intoString:&boundary]
+ && [boundary hasPrefix:@"--"]) {
+
+ // we found a boundary string; use it to divide the string into parts
+ NSArray *mungedParts = [mungedStr componentsSeparatedByString:boundary];
+
+ // look at each of the munged parts in the original string, and try to
+ // convert those into UTF-8
+ NSMutableArray *origParts = [NSMutableArray array];
+ NSUInteger offset = 0;
+ for (int partIdx = 0; partIdx < [mungedParts count]; partIdx++) {
+
+ NSString *mungedPart = [mungedParts objectAtIndex:partIdx];
+ NSUInteger partSize = [mungedPart length];
+
+ NSRange range = NSMakeRange(offset, partSize);
+ NSData *origPartData = [data subdataWithRange:range];
+
+ NSString *origPartStr = [[[NSString alloc] initWithData:origPartData
+ encoding:NSUTF8StringEncoding] autorelease];
+ if (origPartStr) {
+ // we could make this original part into UTF-8; use the string
+ [origParts addObject:origPartStr];
+ } else {
+ // this part can't be made into UTF-8; scan the header, if we can
+ NSString *header = nil;
+ NSScanner *headerScanner = [NSScanner scannerWithString:mungedPart];
+ if (![headerScanner scanUpToString:@"\r\n\r\n" intoString:&header]) {
+ // we couldn't find a header
+ header = @"";
+ }
+
+ // make a part string with the header and <<n bytes>>
+ NSString *binStr = [NSString stringWithFormat:@"\r%@\r<<%u bytes>>\r",
+ header, partSize - [header length]];
+ [origParts addObject:binStr];
+ }
+ offset += partSize + [boundary length];
+ }
+
+ // rejoin the original parts
+ streamDataStr = [origParts componentsJoinedByString:boundary];
+ }
+ }
+
+ if (!streamDataStr) {
+ // give up; just make a string showing the uploaded bytes
+ streamDataStr = [NSString stringWithFormat:@"<<%u bytes>>", [data length]];
+ }
+ return streamDataStr;
+}
+
+// logFetchWithError is called following a successful or failed fetch attempt
+//
+// This method does all the work for appending to and creating log files
+
+- (void)logFetchWithError:(NSError *)error {
+
+ if (![[self class] isLoggingEnabled]) return;
+
+ NSFileManager *fileManager = [NSFileManager defaultManager];
+
+ // TODO: add Javascript to display response data formatted in hex
+
+ NSString *logDirectory = [[self class] loggingDirectory];
+ NSString *processName = [[self class] loggingProcessName];
+ NSString *dateStamp = [[self class] loggingDateStamp];
+
+ // each response's NSData goes into its own xml or txt file, though all
+ // responses for this run of the app share a main html file. This
+ // counter tracks all fetch responses for this run of the app.
+ static int zResponseCounter = 0;
+ zResponseCounter++;
+
+ // file name for the html file containing plain text in a <textarea>
+ NSString *responseDataUnformattedFileName = nil;
+
+ // file name for the "formatted" (raw) data file
+ NSString *responseDataFormattedFileName = nil;
+ NSUInteger responseDataLength = [downloadedData_ length];
+
+ NSURLResponse *response = [self response];
+ NSString *responseBaseName = nil;
+
+ // if there's response data, decide what kind of file to put it in based
+ // on the first bytes of the file or on the mime type supplied by the server
+ if (responseDataLength) {
+ NSString *responseDataExtn = nil;
+
+ // generate a response file base name like
+ // SyncProto_http_response_10-16_01-56-58PM_3
+ responseBaseName = [NSString stringWithFormat:@"%@_http_response_%@_%d",
+ processName, dateStamp, zResponseCounter];
+
+ NSString *dataStr = [[[NSString alloc] initWithData:downloadedData_
+ encoding:NSUTF8StringEncoding] autorelease];
+ if (dataStr) {
+ // we were able to make a UTF-8 string from the response data
+
+ NSCharacterSet *whitespaceSet = [NSCharacterSet whitespaceCharacterSet];
+ dataStr = [dataStr stringByTrimmingCharactersInSet:whitespaceSet];
+
+ // save a plain-text version of the response data in an html cile
+ // containing a wrapped, scrollable <textarea>
+ //
+ // we'll use <textarea rows="33" cols="108" readonly=true wrap=soft>
+ // </textarea> to fit inside our iframe
+ responseDataUnformattedFileName = [responseBaseName stringByAppendingPathExtension:@"html"];
+ NSString *textFilePath = [logDirectory stringByAppendingPathComponent:responseDataUnformattedFileName];
+
+ NSString* wrapFmt = @"<textarea rows=\"33\" cols=\"108\" readonly=true"
+ " wrap=soft>\n%@\n</textarea>";
+ NSString* wrappedStr = [NSString stringWithFormat:wrapFmt, dataStr];
+ [wrappedStr writeToFile:textFilePath
+ atomically:NO
+ encoding:NSUTF8StringEncoding
+ error:nil];
+
+ // now determine the extension for the "formatted" file, which is really
+ // the raw data written with an appropriate extension
+
+ // for known file types, we'll write the data to a file with the
+ // appropriate extension
+ if ([dataStr hasPrefix:@"<?xml"]) {
+ responseDataExtn = @"xml";
+ } else if ([dataStr hasPrefix:@"<html"]) {
+ responseDataExtn = @"html";
+ } else {
+ // add more types of identifiable text here
+ }
+
+ } else if ([[response MIMEType] isEqual:@"image/jpeg"]) {
+ responseDataExtn = @"jpg";
+ } else if ([[response MIMEType] isEqual:@"image/gif"]) {
+ responseDataExtn = @"gif";
+ } else if ([[response MIMEType] isEqual:@"image/png"]) {
+ responseDataExtn = @"png";
+ } else {
+ // add more non-text types here
+ }
+
+ // if we have an extension, save the raw data in a file with that
+ // extension to be our "formatted" display file
+ if (responseDataExtn) {
+ responseDataFormattedFileName = [responseBaseName stringByAppendingPathExtension:responseDataExtn];
+ NSString *formattedFilePath = [logDirectory stringByAppendingPathComponent:responseDataFormattedFileName];
+
+ [downloadedData_ writeToFile:formattedFilePath atomically:NO];
+ }
+ }
+
+ // we'll have one main html file per run of the app
+ NSString *htmlName = [NSString stringWithFormat:@"%@_http_log_%@.html",
+ processName, dateStamp];
+ NSString *htmlPath =[logDirectory stringByAppendingPathComponent:htmlName];
+
+ // if the html file exists (from logging previous fetches) we don't need
+ // to re-write the header or the scripts
+ BOOL didFileExist = [fileManager fileExistsAtPath:htmlPath];
+
+ NSMutableString* outputHTML = [NSMutableString string];
+ NSURLRequest *request = [self request];
+
+ // we need file names for the various div's that we're going to show and hide,
+ // names unique to this response's bundle of data, so we format our div
+ // names with the counter that we incremented earlier
+ NSString *requestHeadersName = [NSString stringWithFormat:@"RequestHeaders%d", zResponseCounter];
+ NSString *postDataName = [NSString stringWithFormat:@"PostData%d", zResponseCounter];
+
+ NSString *responseHeadersName = [NSString stringWithFormat:@"ResponseHeaders%d", zResponseCounter];
+ NSString *responseDataDivName = [NSString stringWithFormat:@"ResponseData%d", zResponseCounter];
+ NSString *dataIFrameID = [NSString stringWithFormat:@"DataIFrame%d", zResponseCounter];
+
+ // we need a header to say we'll have UTF-8 text
+ if (!didFileExist) {
+ [outputHTML appendFormat:@"<html><head><meta http-equiv=\"content-type\" "
+ "content=\"text/html; charset=UTF-8\"><title>%@ HTTP fetch log %@</title>",
+ processName, dateStamp];
+ }
+
+ // write style sheets for each hideable element; each style sheet is
+ // customized with our current response number, since they'll share
+ // the html page with other responses
+ NSString *styleFormat = @"<style type=\"text/css\">div#%@ "
+ "{ margin: 0px 20px 0px 20px; display: none; }</style>\n";
+
+ [outputHTML appendFormat:styleFormat, requestHeadersName];
+ [outputHTML appendFormat:styleFormat, postDataName];
+ [outputHTML appendFormat:styleFormat, responseHeadersName];
+ [outputHTML appendFormat:styleFormat, responseDataDivName];
+
+ if (!didFileExist) {
+ // write javascript functions. The first one shows/hides the layer
+ // containing the iframe.
+ NSString *scriptFormat = @"<script type=\"text/javascript\"> "
+ "function toggleLayer(whichLayer){ var style2 = document.getElementById(whichLayer).style; "
+ "style2.display = style2.display ? \"\":\"block\";}</script>\n";
+ [outputHTML appendFormat:scriptFormat];
+
+ // the second function is passed the src file; if it's what's shown, it
+ // toggles the iframe's visibility. If some other src is shown, it shows
+ // the iframe and loads the new source. Note we want to load the source
+ // whenever we show the iframe too since Firefox seems to format it wrong
+ // when showing it if we don't reload it.
+ NSString *toggleIFScriptFormat = @"<script type=\"text/javascript\"> "
+ "function toggleIFrame(whichLayer,iFrameID,newsrc)"
+ "{ \n var iFrameElem=document.getElementById(iFrameID); "
+ "if (iFrameElem.src.indexOf(newsrc) != -1) { toggleLayer(whichLayer); } "
+ "else { document.getElementById(whichLayer).style.display=\"block\"; } "
+ "iFrameElem.src=newsrc; }</script>\n</head>\n<body>\n";
+ [outputHTML appendFormat:toggleIFScriptFormat];
+ }
+
+ // now write the visible html elements
+
+ // write the date & time
+ [outputHTML appendFormat:@"<b>%@</b><br>", [[NSDate date] description]];
+
+ // write the request URL
+ [outputHTML appendFormat:@"<b>request:</b> %@ <i>URL:</i> <code>%@</code><br>\n",
+ [request HTTPMethod], [request URL]];
+
+ // write the request headers, toggleable
+ NSDictionary *requestHeaders = [request allHTTPHeaderFields];
+ if ([requestHeaders count]) {
+ NSString *requestHeadersFormat = @"<a href=\"javascript:toggleLayer('%@');\">"
+ "request headers (%d)</a><div id=\"%@\"><pre>%@</pre></div><br>\n";
+ [outputHTML appendFormat:requestHeadersFormat,
+ requestHeadersName, // layer name
+ [requestHeaders count],
+ requestHeadersName,
+ [requestHeaders description]]; // description gives a human-readable dump
+ } else {
+ [outputHTML appendString:@"<i>Request headers: none</i><br>"];
+ }
+
+ // write the request post data, toggleable
+ NSData *postData = postData_;
+ if (loggedStreamData_) {
+ postData = loggedStreamData_;
+ }
+
+ if ([postData length]) {
+ NSString *postDataFormat = @"<a href=\"javascript:toggleLayer('%@');\">"
+ "posted data (%d bytes)</a><div id=\"%@\">%@</div><br>\n";
+ NSString *postDataStr = [self stringFromStreamData:postData];
+ if (postDataStr) {
+ NSString *postDataTextAreaFmt = @"<pre>%@</pre>";
+ if ([postDataStr rangeOfString:@"<"].location != NSNotFound) {
+ postDataTextAreaFmt = @"<textarea rows=\"15\" cols=\"100\""
+ " readonly=true wrap=soft>\n%@\n</textarea>";
+ }
+ NSString *cleanedPostData = [self cleanParameterFollowing:@"&Passwd="
+ fromString:postDataStr];
+ NSString *postDataTextArea = [NSString stringWithFormat:
+ postDataTextAreaFmt, cleanedPostData];
+
+ [outputHTML appendFormat:postDataFormat,
+ postDataName, // layer name
+ [postData length],
+ postDataName,
+ postDataTextArea];
+ }
+ } else {
+ // no post data
+ }
+
+ // write the response status, MIME type, URL
+ if (response) {
+ NSString *statusString = @"";
+ if ([response respondsToSelector:@selector(statusCode)]) {
+ NSInteger status = [(NSHTTPURLResponse *)response statusCode];
+ statusString = @"200";
+ if (status != 200) {
+ // purple for errors
+ statusString = [NSString stringWithFormat:@"<FONT COLOR=\"#FF00FF\">%d</FONT>",
+ status];
+ }
+ }
+
+ // show the response URL only if it's different from the request URL
+ NSString *responseURLStr = @"";
+ NSURL *responseURL = [response URL];
+
+ if (responseURL && ![responseURL isEqual:[request URL]]) {
+ NSString *responseURLFormat = @"<br><FONT COLOR=\"#FF00FF\">response URL:"
+ "</FONT> <code>%@</code>";
+ responseURLStr = [NSString stringWithFormat:responseURLFormat,
+ [responseURL absoluteString]];
+ }
+
+ NSDictionary *responseHeaders = nil;
+ if ([response respondsToSelector:@selector(allHeaderFields)]) {
+ responseHeaders = [(NSHTTPURLResponse *)response allHeaderFields];
+ }
+ [outputHTML appendFormat:@"<b>response:</b> <i>status:</i> %@ <i> "
+ "&nbsp;&nbsp;&nbsp;MIMEType:</i><code> %@</code>%@<br>\n",
+ statusString,
+ [response MIMEType],
+ responseURLStr,
+ responseHeaders ? [responseHeaders description] : @""];
+
+ // write the response headers, toggleable
+ if ([responseHeaders count]) {
+
+ NSString *cookiesSet = [responseHeaders objectForKey:@"Set-Cookie"];
+
+ NSString *responseHeadersFormat = @"<a href=\"javascript:toggleLayer("
+ "'%@');\">response headers (%d) %@</a><div id=\"%@\"><pre>%@</pre>"
+ "</div><br>\n";
+ [outputHTML appendFormat:responseHeadersFormat,
+ responseHeadersName,
+ [responseHeaders count],
+ (cookiesSet ? @"<i>sets cookies</i>" : @""),
+ responseHeadersName,
+ [responseHeaders description]];
+
+ } else {
+ [outputHTML appendString:@"<i>Response headers: none</i><br>\n"];
+ }
+ }
+
+ // error
+ if (error) {
+ [outputHTML appendFormat:@"<b>error:</b> %@ <br>\n", [error description]];
+ }
+
+ // write the response data. We have links to show formatted and text
+ // versions, but they both show it in the same iframe, and both
+ // links also toggle visible/hidden
+ if (responseDataFormattedFileName || responseDataUnformattedFileName) {
+
+ // response data, toggleable links -- formatted and text versions
+ if (responseDataFormattedFileName) {
+ [outputHTML appendFormat:@"response data (%d bytes) formatted <b>%@</b> ",
+ responseDataLength,
+ [responseDataFormattedFileName pathExtension]];
+
+ // inline (iframe) link
+ NSString *responseInlineFormattedDataNameFormat = @"&nbsp;&nbsp;<a "
+ "href=\"javascript:toggleIFrame('%@','%@','%@');\">inline</a>\n";
+ [outputHTML appendFormat:responseInlineFormattedDataNameFormat,
+ responseDataDivName, // div ID
+ dataIFrameID, // iframe ID (for reloading)
+ responseDataFormattedFileName]; // src to reload
+
+ // plain link (so the user can command-click it into another tab)
+ [outputHTML appendFormat:@"&nbsp;&nbsp;<a href=\"%@\">stand-alone</a><br>\n",
+ [responseDataFormattedFileName
+ stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
+ }
+ if (responseDataUnformattedFileName) {
+ [outputHTML appendFormat:@"response data (%d bytes) plain text ",
+ responseDataLength];
+
+ // inline (iframe) link
+ NSString *responseInlineDataNameFormat = @"&nbsp;&nbsp;<a href=\""
+ "javascript:toggleIFrame('%@','%@','%@');\">inline</a> \n";
+ [outputHTML appendFormat:responseInlineDataNameFormat,
+ responseDataDivName, // div ID
+ dataIFrameID, // iframe ID (for reloading)
+ responseDataUnformattedFileName]; // src to reload
+
+ // plain link (so the user can command-click it into another tab)
+ [outputHTML appendFormat:@"&nbsp;&nbsp;<a href=\"%@\">stand-alone</a><br>\n",
+ [responseDataUnformattedFileName
+ stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
+ }
+
+ // make the iframe
+ NSString *divHTMLFormat = @"<div id=\"%@\">%@</div><br>\n";
+ NSString *src = responseDataFormattedFileName ?
+ responseDataFormattedFileName : responseDataUnformattedFileName;
+ NSString *escapedSrc = [src stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+ NSString *iframeFmt = @" <iframe src=\"%@\" id=\"%@\" width=800 height=400>"
+ "\n<a href=\"%@\">%@</a>\n </iframe>\n";
+ NSString *dataIFrameHTML = [NSString stringWithFormat:iframeFmt,
+ escapedSrc, dataIFrameID, escapedSrc, src];
+ [outputHTML appendFormat:divHTMLFormat,
+ responseDataDivName, dataIFrameHTML];
+ } else {
+ // could not parse response data; just show the length of it
+ [outputHTML appendFormat:@"<i>Response data: %d bytes </i>\n",
+ responseDataLength];
+ }
+
+ [outputHTML appendString:@"<br><hr><p>"];
+
+ // append the HTML to the main output file
+ const char* htmlBytes = [outputHTML UTF8String];
+ NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:htmlPath
+ append:YES];
+ [stream open];
+ [stream write:(const uint8_t *) htmlBytes maxLength:strlen(htmlBytes)];
+ [stream close];
+
+ // make a symlink to the latest html
+ NSString *symlinkName = [NSString stringWithFormat:@"%@_http_log_newest.html",
+ processName];
+ NSString *symlinkPath = [logDirectory stringByAppendingPathComponent:symlinkName];
+
+ // removeFileAtPath might be going away, but removeItemAtPath does not exist
+ // in 10.4
+ if ([fileManager respondsToSelector:@selector(removeFileAtPath:handler:)]) {
+ [fileManager removeFileAtPath:symlinkPath handler:nil];
+ } else if ([fileManager respondsToSelector:@selector(removeItemAtPath:error:)]) {
+ // To make the next line compile when targeting 10.4, we declare
+ // removeItemAtPath:error: in an @interface above
+ [fileManager removeItemAtPath:symlinkPath error:NULL];
+ }
+
+ [fileManager createSymbolicLinkAtPath:symlinkPath pathContent:htmlPath];
+}
+
+- (void)logCapturePostStream {
+
+#if GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING
+ // This is called when beginning a fetch. The caller should have already
+ // verified that logging is enabled, and should have allocated
+ // loggedStreamData_ as a mutable object.
+
+ // If we're logging, we need to wrap the upload stream with our monitor
+ // stream subclass that will call us back with the bytes being read from the
+ // stream
+
+ // our wrapper will retain the old post stream
+ [postStream_ autorelease];
+
+ // length can be
+ postStream_ = [GTMInputStreamLogger inputStreamWithStream:postStream_
+ length:0];
+ [postStream_ retain];
+
+ // we don't really want monitoring callbacks; our subclass will be
+ // calling our appendLoggedStreamData: method at every read instead
+ [(GTMInputStreamLogger *)postStream_ setMonitorDelegate:self
+ selector:nil];
+#endif // GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING
+}
+
+- (void)appendLoggedStreamData:(NSData *)newData {
+ [loggedStreamData_ appendData:newData];
+}
+
+#endif // GTM_HTTPFETCHER_ENABLE_LOGGING
+@end
+
+#if GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING
+@implementation GTMInputStreamLogger
+- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len {
+
+ // capture the read stream data, and pass it to the delegate to append to
+ NSInteger result = [super read:buffer maxLength:len];
+ if (result >= 0) {
+ NSData *data = [NSData dataWithBytes:buffer length:result];
+ [monitorDelegate_ appendLoggedStreamData:data];
+ }
+ return result;
+}
+@end
+#endif // GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING
diff --git a/Foundation/GTMHTTPFetcherTest.m b/Foundation/GTMHTTPFetcherTest.m
new file mode 100644
index 0000000..16c8d0f
--- /dev/null
+++ b/Foundation/GTMHTTPFetcherTest.m
@@ -0,0 +1,543 @@
+//
+// GTMHTTPFetcherTest.m
+//
+// Copyright 2007-2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import <SenTestingKit/SenTestingKit.h>
+#import <unistd.h>
+#import "GTMHTTPFetcher.h"
+#import "GTMSenTestCase.h"
+
+@interface GTMHTTPFetcherTest : SenTestCase {
+ // these ivars are checked after fetches, and are reset by resetFetchResponse
+ NSData *fetchedData_;
+ NSError *fetcherError_;
+ NSInteger fetchedStatus_;
+ NSURLResponse *fetchedResponse_;
+ NSMutableURLRequest *fetchedRequest_;
+
+ // setup/teardown ivars
+ NSMutableDictionary *fetchHistory_;
+ NSTask *server_; // python http server
+ BOOL didServerLaunch_; // Tracks the state of our server
+ BOOL didServerDie_;
+ NSMutableData *launchBuffer_; // Storage for output from our server
+}
+@end
+
+@interface GTMHTTPFetcherTest (PrivateMethods)
+- (GTMHTTPFetcher *)doFetchWithURLString:(NSString *)urlString
+ cachingDatedData:(BOOL)doCaching;
+
+- (GTMHTTPFetcher *)doFetchWithURLString:(NSString *)urlString
+ cachingDatedData:(BOOL)doCaching
+ retrySelector:(SEL)retrySel
+ maxRetryInterval:(NSTimeInterval)maxRetryInterval
+ userData:(id)userData;
+
+- (NSString *)fileURLStringToTestFileName:(NSString *)name;
+@end
+
+@implementation GTMHTTPFetcherTest
+
+static const int kServerPortNumber = 54579;
+static const NSTimeInterval kRunLoopInterval = 0.01;
+// The bogus-fetch test can take >10s to pass. Pick something way higher
+// to avoid failing.
+static const NSTimeInterval kGiveUpInterval = 60.0; // bail on the test if 60 seconds elapse
+
+static NSString *const kValidFileName = @"GTMHTTPFetcherTestPage.html";
+
+- (void)gotData:(NSNotification*)notification {
+ // our server sends out a string to confirm that it launched
+ NSFileHandle *handle = [notification object];
+ NSData *launchMessageData = [handle availableData];
+ [launchBuffer_ appendData:launchMessageData];
+ NSString *launchStr =
+ [[[NSString alloc] initWithData:launchBuffer_
+ encoding:NSUTF8StringEncoding] autorelease];
+ didServerLaunch_ =
+ [launchStr rangeOfString:@"started GTMHTTPFetcherTestServer"].location != NSNotFound;
+ if (!didServerLaunch_) {
+ _GTMDevLog(@"gotData launching httpserver: %@", launchStr);
+ [handle readInBackgroundAndNotify];
+ }
+}
+
+- (void)didDie:(NSNotification*)notification {
+ _GTMDevLog(@"server died");
+ didServerDie_ = YES;
+}
+
+
+- (void)setUp {
+ fetchHistory_ = [[NSMutableDictionary alloc] init];
+
+ // run the python http server, located in the Tests directory
+ NSBundle *testBundle = [NSBundle bundleForClass:[self class]];
+ STAssertNotNil(testBundle, nil);
+
+ NSString *serverPath =
+ [testBundle pathForResource:@"GTMHTTPFetcherTestServer" ofType:@""];
+ STAssertNotNil(serverPath, nil);
+
+ NSArray *argArray = [NSArray arrayWithObjects:serverPath,
+ @"-p", [NSString stringWithFormat:@"%d", kServerPortNumber],
+ @"-r", [serverPath stringByDeletingLastPathComponent], nil];
+
+ server_ = [[NSTask alloc] init];
+ [server_ setArguments:argArray];
+ [server_ setLaunchPath:@"/usr/bin/python"];
+ [server_ setEnvironment:[NSDictionary dictionary]]; // don't inherit anything from us
+
+ // pipes will be cleaned up when server_ is torn down.
+ NSPipe *outputPipe = [NSPipe pipe];
+ NSPipe *errorPipe = [NSPipe pipe];
+
+ [server_ setStandardOutput:outputPipe];
+ [server_ setStandardError:errorPipe];
+
+ NSFileHandle *outputHandle = [outputPipe fileHandleForReading];
+ NSFileHandle *errorHandle = [errorPipe fileHandleForReading];
+
+ didServerLaunch_ = NO;
+ didServerDie_ = NO;
+
+ NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+ [center addObserver:self
+ selector:@selector(gotData:)
+ name:NSFileHandleDataAvailableNotification
+ object:outputHandle];
+ [center addObserver:self
+ selector:@selector(gotData:)
+ name:NSFileHandleDataAvailableNotification
+ object:errorHandle];
+ [center addObserver:self
+ selector:@selector(didDie:)
+ name:NSTaskDidTerminateNotification
+ object:server_];
+
+ [launchBuffer_ autorelease];
+ launchBuffer_ = [[NSMutableData data] retain];
+ [outputHandle waitForDataInBackgroundAndNotify];
+ [errorHandle waitForDataInBackgroundAndNotify];
+ [server_ launch];
+
+ NSDate* giveUpDate = [NSDate dateWithTimeIntervalSinceNow:kGiveUpInterval];
+ while ((!didServerDie_ && !didServerLaunch_) &&
+ [giveUpDate timeIntervalSinceNow] > 0) {
+ NSDate* loopIntervalDate =
+ [NSDate dateWithTimeIntervalSinceNow:kRunLoopInterval];
+ [[NSRunLoop currentRunLoop] runUntilDate:loopIntervalDate];
+ }
+
+ [center removeObserver:self];
+
+ STAssertTrue(didServerLaunch_ && [server_ isRunning] && !didServerDie_,
+ @"Python http server not launched.\n"
+ "Args:%@\n"
+ "Environment:%@\n",
+ [argArray componentsJoinedByString:@" "], [server_ environment]);
+}
+
+- (void)resetFetchResponse {
+ [fetchedData_ release];
+ fetchedData_ = nil;
+
+ [fetcherError_ release];
+ fetcherError_ = nil;
+
+ [fetchedRequest_ release];
+ fetchedRequest_ = nil;
+
+ [fetchedResponse_ release];
+ fetchedResponse_ = nil;
+
+ fetchedStatus_ = 0;
+}
+
+- (void)tearDown {
+ [server_ terminate];
+ [server_ waitUntilExit];
+ [server_ release];
+ server_ = nil;
+ [launchBuffer_ release];
+ launchBuffer_ = nil;
+ [self resetFetchResponse];
+
+ [fetchHistory_ release];
+ fetchHistory_ = nil;
+}
+
+- (void)testValidFetch {
+
+ NSString *urlString = [self fileURLStringToTestFileName:kValidFileName];
+
+ [self doFetchWithURLString:urlString cachingDatedData:YES];
+
+ STAssertNotNil(fetchedData_,
+ @"failed to fetch data, status:%ld error:%@, URL:%@",
+ (long)fetchedStatus_, fetcherError_, urlString);
+ STAssertNotNil(fetchedResponse_,
+ @"failed to get fetch response, status:%ld error:%@",
+ (long)fetchedStatus_, fetcherError_);
+ STAssertNotNil(fetchedRequest_, @"failed to get fetch request, URL %@",
+ urlString);
+ STAssertNil(fetcherError_, @"fetching data gave error: %@", fetcherError_);
+ STAssertEquals(fetchedStatus_, (NSInteger)200,
+ @"fetching data expected status 200, instead got %ld, for URL %@",
+ (long)fetchedStatus_, urlString);
+
+ // no cookies should be sent with our first request
+ NSDictionary *headers = [fetchedRequest_ allHTTPHeaderFields];
+ NSString *cookiesSent = [headers objectForKey:@"Cookie"];
+ STAssertNil(cookiesSent, @"Cookies sent unexpectedly: %@", cookiesSent);
+
+
+ // cookies should have been set by the response; specifically, TestCookie
+ // should be set to the name of the file requested
+ NSDictionary *responseHeaders;
+
+ responseHeaders = [(NSHTTPURLResponse *)fetchedResponse_ allHeaderFields];
+ NSString *cookiesSetString = [responseHeaders objectForKey:@"Set-Cookie"];
+ NSString *cookieExpected = [NSString stringWithFormat:@"TestCookie=%@",
+ kValidFileName];
+ STAssertEqualObjects(cookiesSetString, cookieExpected, @"Unexpected cookie");
+
+ // make a copy of the fetched data to compare with our next fetch from the
+ // cache
+ NSData *originalFetchedData = [[fetchedData_ copy] autorelease];
+
+ // Now fetch again so the "If modified since" header will be set (because
+ // we're calling setFetchHistory: below) and caching ON, and verify that we
+ // got a good data from the cache, along with a "Not modified" status
+
+ [self resetFetchResponse];
+
+ [self doFetchWithURLString:urlString cachingDatedData:YES];
+
+ STAssertEqualObjects(fetchedData_, originalFetchedData,
+ @"cache data mismatch");
+
+ STAssertNotNil(fetchedData_,
+ @"failed to fetch data, status:%ld error:%@, URL:%@",
+ (long)fetchedStatus_, fetcherError_, urlString);
+ STAssertNotNil(fetchedResponse_,
+ @"failed to get fetch response, status:%;d error:%@",
+ (long)fetchedStatus_, fetcherError_);
+ STAssertNotNil(fetchedRequest_, @"failed to get fetch request, URL %@",
+ urlString);
+ STAssertNil(fetcherError_, @"fetching data gave error: %@", fetcherError_);
+
+ STAssertEquals(fetchedStatus_, (NSInteger)kGTMHTTPFetcherStatusNotModified, // 304
+ @"fetching data expected status 304, instead got %ld, for URL %@",
+ (long)fetchedStatus_, urlString);
+
+ // the TestCookie set previously should be sent with this request
+ cookiesSent = [[fetchedRequest_ allHTTPHeaderFields] objectForKey:@"Cookie"];
+ STAssertEqualObjects(cookiesSent, cookieExpected, @"Cookie not sent");
+
+ // Now fetch twice without caching enabled, and verify that we got a
+ // "Not modified" status, along with a non-nil but empty NSData (which
+ // is normal for that status code)
+
+ [self resetFetchResponse];
+
+ [fetchHistory_ removeAllObjects];
+
+ [self doFetchWithURLString:urlString cachingDatedData:NO];
+
+ STAssertEqualObjects(fetchedData_, originalFetchedData,
+ @"cache data mismatch");
+
+ [self resetFetchResponse];
+ [self doFetchWithURLString:urlString cachingDatedData:NO];
+
+ STAssertNotNil(fetchedData_, @"");
+ STAssertEquals([fetchedData_ length], (NSUInteger)0, @"unexpected data");
+ STAssertEquals(fetchedStatus_, (NSInteger)kGTMHTTPFetcherStatusNotModified,
+ @"fetching data expected status 304, instead got %d", fetchedStatus_);
+ STAssertNil(fetcherError_, @"unexpected error: %@", fetcherError_);
+
+}
+
+- (void)testBogusFetch {
+ // fetch a live, invalid URL
+ NSString *badURLString = @"http://localhost:86/";
+ [self doFetchWithURLString:badURLString cachingDatedData:NO];
+
+ const int kServiceUnavailableStatus = 503;
+
+ if (fetchedStatus_ == kServiceUnavailableStatus) {
+ // some proxies give a "service unavailable" error for bogus fetches
+ } else {
+
+ if (fetchedData_) {
+ NSString *str = [[[NSString alloc] initWithData:fetchedData_
+ encoding:NSUTF8StringEncoding] autorelease];
+ STAssertNil(fetchedData_, @"fetched unexpected data: %@", str);
+ }
+
+ STAssertNotNil(fetcherError_, @"failed to receive fetching error");
+ STAssertEquals(fetchedStatus_, (NSInteger)0,
+ @"fetching data expected no status from no response, instead got %d",
+ fetchedStatus_);
+ }
+
+ // fetch with a specific status code from our http server
+ [self resetFetchResponse];
+
+ NSString *invalidWebPageFile =
+ [kValidFileName stringByAppendingString:@"?status=400"];
+ NSString *statusUrlString =
+ [self fileURLStringToTestFileName:invalidWebPageFile];
+
+ [self doFetchWithURLString:statusUrlString cachingDatedData:NO];
+
+ STAssertNotNil(fetchedData_, @"fetch lacked data with error info");
+ STAssertNil(fetcherError_, @"expected bad status but got an error");
+ STAssertEquals(fetchedStatus_, (NSInteger)400,
+ @"unexpected status, error=%@", fetcherError_);
+}
+
+- (void)testRetryFetches {
+
+ GTMHTTPFetcher *fetcher;
+
+ NSString *invalidFile = [kValidFileName stringByAppendingString:@"?status=503"];
+ NSString *urlString = [self fileURLStringToTestFileName:invalidFile];
+
+ SEL countRetriesSel = @selector(countRetriesFetcher:willRetry:forError:);
+ SEL fixRequestSel = @selector(fixRequestFetcher:willRetry:forError:);
+
+ //
+ // test: retry until timeout, then expect failure with status message
+ //
+
+ NSNumber *lotsOfRetriesNumber = [NSNumber numberWithInt:1000];
+
+ fetcher= [self doFetchWithURLString:urlString
+ cachingDatedData:NO
+ retrySelector:countRetriesSel
+ maxRetryInterval:5.0 // retry intervals of 1, 2, 4
+ userData:lotsOfRetriesNumber];
+
+ STAssertNotNil(fetchedData_, @"error data is expected");
+ STAssertEquals(fetchedStatus_, (NSInteger)503, nil);
+ STAssertEquals([fetcher retryCount], 3U, @"retry count unexpected");
+
+ //
+ // test: retry twice, then give up
+ //
+ [self resetFetchResponse];
+
+ NSNumber *twoRetriesNumber = [NSNumber numberWithInt:2];
+
+ fetcher= [self doFetchWithURLString:urlString
+ cachingDatedData:NO
+ retrySelector:countRetriesSel
+ maxRetryInterval:10.0 // retry intervals of 1, 2, 4, 8
+ userData:twoRetriesNumber];
+
+ STAssertNotNil(fetchedData_, @"error data is expected");
+ STAssertEquals(fetchedStatus_, (NSInteger)503, nil);
+ STAssertEquals([fetcher retryCount], 2U, @"retry count unexpected");
+
+
+ //
+ // test: retry, making the request succeed on the first retry
+ // by fixing the URL
+ //
+ [self resetFetchResponse];
+
+ fetcher= [self doFetchWithURLString:urlString
+ cachingDatedData:NO
+ retrySelector:fixRequestSel
+ maxRetryInterval:30.0 // should only retry once due to selector
+ userData:lotsOfRetriesNumber];
+
+ STAssertNotNil(fetchedData_, @"data is expected");
+ STAssertEquals(fetchedStatus_, (NSInteger)200, nil);
+ STAssertEquals([fetcher retryCount], 1U, @"retry count unexpected");
+}
+
+#pragma mark -
+
+- (GTMHTTPFetcher *)doFetchWithURLString:(NSString *)urlString
+ cachingDatedData:(BOOL)doCaching {
+
+ return [self doFetchWithURLString:(NSString *)urlString
+ cachingDatedData:(BOOL)doCaching
+ retrySelector:nil
+ maxRetryInterval:0
+ userData:nil];
+}
+
+- (GTMHTTPFetcher *)doFetchWithURLString:(NSString *)urlString
+ cachingDatedData:(BOOL)doCaching
+ retrySelector:(SEL)retrySel
+ maxRetryInterval:(NSTimeInterval)maxRetryInterval
+ userData:(id)userData {
+
+ NSURL *url = [NSURL URLWithString:urlString];
+ NSURLRequest *req = [NSURLRequest requestWithURL:url
+ cachePolicy:NSURLRequestReloadIgnoringCacheData
+ timeoutInterval:kGiveUpInterval];
+ GTMHTTPFetcher *fetcher = [GTMHTTPFetcher httpFetcherWithRequest:req];
+
+ STAssertNotNil(fetcher, @"Failed to allocate fetcher");
+
+ // setting the fetch history will add the "If-modified-since" header
+ // to repeat requests
+ [fetcher setFetchHistory:fetchHistory_];
+ if (doCaching != [fetcher shouldCacheDatedData]) {
+ // only set the value when it changes since setting it to nil clears out
+ // some of the state and our tests need the state between some non caching
+ // fetches.
+ [fetcher setShouldCacheDatedData:doCaching];
+ }
+
+ if (retrySel) {
+ [fetcher setIsRetryEnabled:YES];
+ [fetcher setRetrySelector:retrySel];
+ [fetcher setMaxRetryInterval:maxRetryInterval];
+ [fetcher setUserData:userData];
+
+ // we force a minimum retry interval for unit testing; otherwise,
+ // we'd have no idea how many retries will occur before the max
+ // retry interval occurs, since the minimum would be random
+ [fetcher setMinRetryInterval:1.0];
+ }
+
+ BOOL isFetching =
+ [fetcher beginFetchWithDelegate:self
+ didFinishSelector:@selector(testFetcher:finishedWithData:)
+ didFailSelector:@selector(testFetcher:failedWithError:)];
+ STAssertTrue(isFetching, @"Begin fetch failed");
+
+ if (isFetching) {
+
+ // Give time for the fetch to happen, but give up if 10 seconds elapse with
+ // no response
+ NSDate* giveUpDate = [NSDate dateWithTimeIntervalSinceNow:kGiveUpInterval];
+ while ((!fetchedData_ && !fetcherError_) &&
+ [giveUpDate timeIntervalSinceNow] > 0) {
+ NSDate* loopIntervalDate =
+ [NSDate dateWithTimeIntervalSinceNow:kRunLoopInterval];
+ [[NSRunLoop currentRunLoop] runUntilDate:loopIntervalDate];
+ }
+ }
+
+ return fetcher;
+}
+
+- (NSString *)fileURLStringToTestFileName:(NSString *)name {
+
+ // we need to create http URLs referring to the desired
+ // resource to be found by the python http server running locally
+
+ // return a localhost:port URL for the test file
+ NSString *urlString = [NSString stringWithFormat:@"http://localhost:%d/%@",
+ kServerPortNumber, name];
+
+
+ // we exclude the "?status=" that would indicate that the URL
+ // should cause a retryable error
+ NSRange range = [name rangeOfString:@"?status="];
+ if (range.length > 0) {
+ name = [name substringToIndex:range.location];
+ }
+
+ // we exclude the ".auth" extension that would indicate that the URL
+ // should be tested with authentication
+ if ([[name pathExtension] isEqual:@"auth"]) {
+ name = [name stringByDeletingPathExtension];
+ }
+
+ // just for sanity, let's make sure we see the file locally, so
+ // we can expect the Python http server to find it too
+ NSBundle *testBundle = [NSBundle bundleForClass:[self class]];
+ STAssertNotNil(testBundle, nil);
+
+ NSString *filePath =
+ [testBundle pathForResource:[name stringByDeletingPathExtension]
+ ofType:[name pathExtension]];
+ STAssertNotNil(filePath, nil);
+
+ return urlString;
+}
+
+
+
+- (void)testFetcher:(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 {
+ // 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];
+ fetchedStatus_ = [error code]; // this implicitly tests that the fetcher has kept the response
+ } else {
+ fetcherError_ = [error retain];
+ fetchedStatus_ = [fetcher statusCode];
+ }
+}
+
+
+// Selector for allowing up to N retries, where N is an NSNumber in the
+// fetcher's userData
+- (BOOL)countRetriesFetcher:(GTMHTTPFetcher *)fetcher
+ willRetry:(BOOL)suggestedWillRetry
+ forError:(NSError *)error {
+
+ int count = [fetcher retryCount];
+ int allowedRetryCount = [[fetcher userData] intValue];
+
+ BOOL shouldRetry = (count < allowedRetryCount);
+
+ STAssertEquals([fetcher nextRetryInterval], pow(2.0, [fetcher retryCount]),
+ @"unexpected next retry interval (expected %f, was %f)",
+ pow(2.0, [fetcher retryCount]),
+ [fetcher nextRetryInterval]);
+
+ return shouldRetry;
+}
+
+// Selector for retrying and changing the request to one that will succeed
+- (BOOL)fixRequestFetcher:(GTMHTTPFetcher *)fetcher
+ willRetry:(BOOL)suggestedWillRetry
+ forError:(NSError *)error {
+
+ STAssertEquals([fetcher nextRetryInterval], pow(2.0, [fetcher retryCount]),
+ @"unexpected next retry interval (expected %f, was %f)",
+ pow(2.0, [fetcher retryCount]),
+ [fetcher nextRetryInterval]);
+
+ // fix it - change the request to a URL which does not have a status value
+ NSString *urlString = [self fileURLStringToTestFileName:kValidFileName];
+
+ NSURL *url = [NSURL URLWithString:urlString];
+ [fetcher setRequest:[NSURLRequest requestWithURL:url]];
+
+ return YES; // do the retry fetch; it should succeed now
+}
+
+@end
+
diff --git a/Foundation/GTMNSData+zlib.h b/Foundation/GTMNSData+zlib.h
index 2c937c9..df31d24 100644
--- a/Foundation/GTMNSData+zlib.h
+++ b/Foundation/GTMNSData+zlib.h
@@ -17,6 +17,7 @@
//
#import <Foundation/Foundation.h>
+#import "GTMDefines.h"
/// Helpers for dealing w/ zlib inflate/deflate calls.
@interface NSData (GTMZLibAdditions)
@@ -25,7 +26,7 @@
//
// Uses the default compression level.
+ (NSData *)gtm_dataByGzippingBytes:(const void *)bytes
- length:(unsigned)length;
+ length:(NSUInteger)length;
/// Return an autoreleased NSData w/ the result of gzipping the payload of |data|.
//
@@ -36,7 +37,7 @@
//
// |level| can be 1-9, any other values will be clipped to that range.
+ (NSData *)gtm_dataByGzippingBytes:(const void *)bytes
- length:(unsigned)length
+ length:(NSUInteger)length
compressionLevel:(int)level;
/// Return an autoreleased NSData w/ the result of gzipping the payload of |data| using |level| compression level.
@@ -50,7 +51,7 @@
//
// Uses the default compression level.
+ (NSData *)gtm_dataByDeflatingBytes:(const void *)bytes
- length:(unsigned)length;
+ length:(NSUInteger)length;
/// Return an autoreleased NSData w/ the result of deflating the payload of |data|.
//
@@ -61,7 +62,7 @@
//
// |level| can be 1-9, any other values will be clipped to that range.
+ (NSData *)gtm_dataByDeflatingBytes:(const void *)bytes
- length:(unsigned)length
+ length:(NSUInteger)length
compressionLevel:(int)level;
/// Return an autoreleased NSData w/ the result of deflating the payload of |data| using |level| compression level.
@@ -73,7 +74,7 @@
//
// The bytes to decompress can be zlib or gzip payloads.
+ (NSData *)gtm_dataByInflatingBytes:(const void *)bytes
- length:(unsigned)length;
+ length:(NSUInteger)length;
/// Return an autoreleased NSData w/ the result of decompressing the payload of |data|.
//
diff --git a/Foundation/GTMNSData+zlib.m b/Foundation/GTMNSData+zlib.m
index 514477f..8ba1ddc 100644
--- a/Foundation/GTMNSData+zlib.m
+++ b/Foundation/GTMNSData+zlib.m
@@ -24,19 +24,25 @@
@interface NSData (GTMZlibAdditionsPrivate)
+ (NSData *)gtm_dataByCompressingBytes:(const void *)bytes
- length:(unsigned)length
+ length:(NSUInteger)length
compressionLevel:(int)level
useGzip:(BOOL)useGzip;
@end
@implementation NSData (GTMZlibAdditionsPrivate)
+ (NSData *)gtm_dataByCompressingBytes:(const void *)bytes
- length:(unsigned)length
+ length:(NSUInteger)length
compressionLevel:(int)level
useGzip:(BOOL)useGzip {
if (!bytes || !length) {
return nil;
}
+
+ // TODO: support 64bit inputs
+ // avail_in is a uInt, so if length > UINT_MAX we actually need to loop
+ // feeding the data until we've gotten it all in. not supporting this
+ // at the moment.
+ _GTMDevAssert(length <= UINT_MAX, @"Currently don't support >32bit lengths");
if (level == Z_DEFAULT_COMPRESSION) {
// the default value is actually outside the range, so we have to let it
@@ -70,7 +76,7 @@
unsigned char output[kChunkSize];
// setup the input
- strm.avail_in = length;
+ strm.avail_in = (unsigned int)length;
strm.next_in = (unsigned char*)bytes;
// loop to collect the data
@@ -119,7 +125,7 @@
@implementation NSData (GTMZLibAdditions)
+ (NSData *)gtm_dataByGzippingBytes:(const void *)bytes
- length:(unsigned)length {
+ length:(NSUInteger)length {
return [self gtm_dataByCompressingBytes:bytes
length:length
compressionLevel:Z_DEFAULT_COMPRESSION
@@ -134,7 +140,7 @@
} // gtm_dataByGzippingData:
+ (NSData *)gtm_dataByGzippingBytes:(const void *)bytes
- length:(unsigned)length
+ length:(NSUInteger)length
compressionLevel:(int)level {
return [self gtm_dataByCompressingBytes:bytes
length:length
@@ -151,7 +157,7 @@
} // gtm_dataByGzippingData:level:
+ (NSData *)gtm_dataByDeflatingBytes:(const void *)bytes
- length:(unsigned)length {
+ length:(NSUInteger)length {
return [self gtm_dataByCompressingBytes:bytes
length:length
compressionLevel:Z_DEFAULT_COMPRESSION
@@ -166,7 +172,7 @@
} // gtm_dataByDeflatingData:
+ (NSData *)gtm_dataByDeflatingBytes:(const void *)bytes
- length:(unsigned)length
+ length:(NSUInteger)length
compressionLevel:(int)level {
return [self gtm_dataByCompressingBytes:bytes
length:length
@@ -183,16 +189,22 @@
} // gtm_dataByDeflatingData:level:
+ (NSData *)gtm_dataByInflatingBytes:(const void *)bytes
- length:(unsigned)length {
+ length:(NSUInteger)length {
if (!bytes || !length) {
return nil;
}
+
+ // TODO: support 64bit inputs
+ // avail_in is a uInt, so if length > UINT_MAX we actually need to loop
+ // feeding the data until we've gotten it all in. not supporting this
+ // at the moment.
+ _GTMDevAssert(length <= UINT_MAX, @"Currently don't support >32bit lengths");
z_stream strm;
bzero(&strm, sizeof(z_stream));
// setup the input
- strm.avail_in = length;
+ strm.avail_in = (unsigned int)length;
strm.next_in = (unsigned char*)bytes;
int windowBits = 15; // 15 to enable any window size
diff --git a/Foundation/GTMNSData+zlibTest.m b/Foundation/GTMNSData+zlibTest.m
index 38dbce3..1d09e35 100644
--- a/Foundation/GTMNSData+zlibTest.m
+++ b/Foundation/GTMNSData+zlibTest.m
@@ -105,12 +105,12 @@ static BOOL HasGzipHeader(NSData *data) {
STAssertNil([NSData gtm_dataByInflatingData:data], nil);
// test deflated data runs that end before they are done
- for (int x = 1 ; x < [deflated length] ; ++x) {
+ for (NSUInteger x = 1 ; x < [deflated length] ; ++x) {
STAssertNil([NSData gtm_dataByInflatingBytes:[deflated bytes] length:x], nil);
}
// test gzipped data runs that end before they are done
- for (int x = 1 ; x < [gzipped length] ; ++x) {
+ for (NSUInteger x = 1 ; x < [gzipped length] ; ++x) {
STAssertNil([NSData gtm_dataByInflatingBytes:[gzipped bytes] length:x], nil);
}
@@ -175,21 +175,21 @@ static BOOL HasGzipHeader(NSData *data) {
// w/ *Bytes apis, default level
NSData *deflated = [NSData gtm_dataByDeflatingBytes:[data bytes] length:[data length]];
STAssertNotNil(deflated, @"failed to deflate data block");
- STAssertGreaterThan([deflated length], 0U, @"failed to deflate data block");
+ STAssertGreaterThan([deflated length], (NSUInteger)0, @"failed to deflate data block");
STAssertFalse(HasGzipHeader(deflated), @"has gzip header on zlib data");
NSData *dataPrime = [NSData gtm_dataByInflatingBytes:[deflated bytes] length:[deflated length]];
STAssertNotNil(dataPrime, @"failed to inflate data block");
- STAssertGreaterThan([dataPrime length], 0U, @"failed to inflate data block");
+ STAssertGreaterThan([dataPrime length], (NSUInteger)0, @"failed to inflate data block");
STAssertEqualObjects(data, dataPrime, @"failed to round trip via *Bytes apis");
// w/ *Data apis, default level
deflated = [NSData gtm_dataByDeflatingData:data];
STAssertNotNil(deflated, @"failed to deflate data block");
- STAssertGreaterThan([deflated length], 0U, @"failed to deflate data block");
+ STAssertGreaterThan([deflated length], (NSUInteger)0, @"failed to deflate data block");
STAssertFalse(HasGzipHeader(deflated), @"has gzip header on zlib data");
dataPrime = [NSData gtm_dataByInflatingData:deflated];
STAssertNotNil(dataPrime, @"failed to inflate data block");
- STAssertGreaterThan([dataPrime length], 0U, @"failed to inflate data block");
+ STAssertGreaterThan([dataPrime length], (NSUInteger)0, @"failed to inflate data block");
STAssertEqualObjects(data, dataPrime, @"failed to round trip via *Data apis");
// loop over the compression levels
@@ -199,21 +199,21 @@ static BOOL HasGzipHeader(NSData *data) {
length:[data length]
compressionLevel:level];
STAssertNotNil(deflated, @"failed to deflate data block");
- STAssertGreaterThan([deflated length], 0U, @"failed to deflate data block");
+ STAssertGreaterThan([deflated length], (NSUInteger)0, @"failed to deflate data block");
STAssertFalse(HasGzipHeader(deflated), @"has gzip header on zlib data");
dataPrime = [NSData gtm_dataByInflatingBytes:[deflated bytes] length:[deflated length]];
STAssertNotNil(dataPrime, @"failed to inflate data block");
- STAssertGreaterThan([dataPrime length], 0U, @"failed to inflate data block");
+ STAssertGreaterThan([dataPrime length], (NSUInteger)0, @"failed to inflate data block");
STAssertEqualObjects(data, dataPrime, @"failed to round trip via *Bytes apis");
// w/ *Data apis, using our level
deflated = [NSData gtm_dataByDeflatingData:data compressionLevel:level];
STAssertNotNil(deflated, @"failed to deflate data block");
- STAssertGreaterThan([deflated length], 0U, @"failed to deflate data block");
+ STAssertGreaterThan([deflated length], (NSUInteger)0, @"failed to deflate data block");
STAssertFalse(HasGzipHeader(deflated), @"has gzip header on zlib data");
dataPrime = [NSData gtm_dataByInflatingData:deflated];
STAssertNotNil(dataPrime, @"failed to inflate data block");
- STAssertGreaterThan([dataPrime length], 0U, @"failed to inflate data block");
+ STAssertGreaterThan([dataPrime length], (NSUInteger)0, @"failed to inflate data block");
STAssertEqualObjects(data, dataPrime, @"failed to round trip via *Data apis");
}
@@ -240,21 +240,21 @@ static BOOL HasGzipHeader(NSData *data) {
// w/ *Bytes apis, default level
NSData *gzipped = [NSData gtm_dataByGzippingBytes:[data bytes] length:[data length]];
STAssertNotNil(gzipped, @"failed to gzip data block");
- STAssertGreaterThan([gzipped length], 0U, @"failed to gzip data block");
+ STAssertGreaterThan([gzipped length], (NSUInteger)0, @"failed to gzip data block");
STAssertTrue(HasGzipHeader(gzipped), @"doesn't have gzip header on gzipped data");
NSData *dataPrime = [NSData gtm_dataByInflatingBytes:[gzipped bytes] length:[gzipped length]];
STAssertNotNil(dataPrime, @"failed to inflate data block");
- STAssertGreaterThan([dataPrime length], 0U, @"failed to inflate data block");
+ STAssertGreaterThan([dataPrime length], (NSUInteger)0, @"failed to inflate data block");
STAssertEqualObjects(data, dataPrime, @"failed to round trip via *Bytes apis");
// w/ *Data apis, default level
gzipped = [NSData gtm_dataByGzippingData:data];
STAssertNotNil(gzipped, @"failed to gzip data block");
- STAssertGreaterThan([gzipped length], 0U, @"failed to gzip data block");
+ STAssertGreaterThan([gzipped length], (NSUInteger)0, @"failed to gzip data block");
STAssertTrue(HasGzipHeader(gzipped), @"doesn't have gzip header on gzipped data");
dataPrime = [NSData gtm_dataByInflatingData:gzipped];
STAssertNotNil(dataPrime, @"failed to inflate data block");
- STAssertGreaterThan([dataPrime length], 0U, @"failed to inflate data block");
+ STAssertGreaterThan([dataPrime length], (NSUInteger)0, @"failed to inflate data block");
STAssertEqualObjects(data, dataPrime, @"failed to round trip via *Data apis");
// loop over the compression levels
@@ -264,21 +264,21 @@ static BOOL HasGzipHeader(NSData *data) {
length:[data length]
compressionLevel:level];
STAssertNotNil(gzipped, @"failed to gzip data block");
- STAssertGreaterThan([gzipped length], 0U, @"failed to gzip data block");
+ STAssertGreaterThan([gzipped length], (NSUInteger)0, @"failed to gzip data block");
STAssertTrue(HasGzipHeader(gzipped), @"doesn't have gzip header on gzipped data");
dataPrime = [NSData gtm_dataByInflatingBytes:[gzipped bytes] length:[gzipped length]];
STAssertNotNil(dataPrime, @"failed to inflate data block");
- STAssertGreaterThan([dataPrime length], 0U, @"failed to inflate data block");
+ STAssertGreaterThan([dataPrime length], (NSUInteger)0, @"failed to inflate data block");
STAssertEqualObjects(data, dataPrime, @"failed to round trip via *Bytes apis");
// w/ *Data apis, using our level
gzipped = [NSData gtm_dataByGzippingData:data compressionLevel:level];
STAssertNotNil(gzipped, @"failed to gzip data block");
- STAssertGreaterThan([gzipped length], 0U, @"failed to gzip data block");
+ STAssertGreaterThan([gzipped length], (NSUInteger)0, @"failed to gzip data block");
STAssertTrue(HasGzipHeader(gzipped), @"doesn't have gzip header on gzipped data");
dataPrime = [NSData gtm_dataByInflatingData:gzipped];
STAssertNotNil(dataPrime, @"failed to inflate data block");
- STAssertGreaterThan([dataPrime length], 0U, @"failed to inflate data block");
+ STAssertGreaterThan([dataPrime length], (NSUInteger)0, @"failed to inflate data block");
STAssertEqualObjects(data, dataPrime, @"failed to round trip via *Data apis");
}
diff --git a/Foundation/GTMNSEnumerator+Filter.m b/Foundation/GTMNSEnumerator+Filter.m
index 5ac3a7c..0fda2ca 100644
--- a/Foundation/GTMNSEnumerator+Filter.m
+++ b/Foundation/GTMNSEnumerator+Filter.m
@@ -45,7 +45,7 @@
// someone would have to subclass or directly create an object of this
// class, and this class is private to this impl.
- _GTMDevAssert(base, @"can't bas a nil base enumerator");
+ _GTMDevAssert(base, @"can't initWithBase: a nil base enumerator");
base_ = [base retain];
operation_ = filter;
other_ = [optionalOther retain];
diff --git a/Foundation/GTMNSString+HTML.m b/Foundation/GTMNSString+HTML.m
index a6abb0e..5178ba9 100644
--- a/Foundation/GTMNSString+HTML.m
+++ b/Foundation/GTMNSString+HTML.m
@@ -19,8 +19,6 @@
#import "GTMDefines.h"
#import "GTMNSString+HTML.h"
-#import "GTMNSString+Utilities.h"
-#import "GTMMethodCheck.h"
typedef struct {
NSString *escapeSequence;
@@ -373,23 +371,38 @@ static int EscapeMapCompare(const void *ucharVoid, const void *mapVoid) {
}
@implementation NSString (GTMNSStringHTMLAdditions)
-GTM_METHOD_CHECK(NSString, gtm_UTF16StringWithLength:);
- (NSString *)gtm_stringByEscapingHTMLUsingTable:(HTMLEscapeMap*)table
- ofSize:(int)size
+ ofSize:(NSUInteger)size
escapingUnicode:(BOOL)escapeUnicode {
- int length = [self length];
+ NSUInteger length = [self length];
if (!length) {
- return nil;
+ return self;
}
NSMutableString *finalString = [NSMutableString string];
-
NSMutableData *data2 = [NSMutableData dataWithCapacity:sizeof(unichar) * length];
- const unichar *buffer = (const unichar *)[self gtm_UTF16StringWithLength:nil];
+
+ // this block is common between GTMNSString+HTML and GTMNSString+XML but
+ // it's so short that it isn't really worth trying to share.
+ const unichar *buffer = CFStringGetCharactersPtr((CFStringRef)self);
+ if (!buffer) {
+ size_t memsize = length * sizeof(UniChar);
+
+ // nope, alloc buffer and fetch the chars ourselves
+ buffer = malloc(memsize);
+ if (!buffer) {
+ // COV_NF_START - Memory fail case
+ _GTMDevLog(@"couldn't alloc buffer");
+ return nil;
+ // COV_NF_END
+ }
+ [self getCharacters:(void*)buffer];
+ [NSData dataWithBytesNoCopy:(void*)buffer length:memsize];
+ }
if (!buffer || !data2) {
- // COV_NF_BEGIN
+ // COV_NF_START
_GTMDevLog(@"Unable to allocate buffer or data2");
return nil;
// COV_NF_END
@@ -397,9 +410,9 @@ GTM_METHOD_CHECK(NSString, gtm_UTF16StringWithLength:);
unichar *buffer2 = (unichar *)[data2 mutableBytes];
- int buffer2Length = 0;
+ NSUInteger buffer2Length = 0;
- for (int i = 0; i < length; ++i) {
+ for (NSUInteger i = 0; i < length; ++i) {
HTMLEscapeMap *val = bsearch(&buffer[i], table,
size / sizeof(HTMLEscapeMap),
sizeof(HTMLEscapeMap), EscapeMapCompare);
@@ -459,7 +472,7 @@ GTM_METHOD_CHECK(NSString, gtm_UTF16StringWithLength:);
}
NSRange escapeRange = NSMakeRange(subrange.location, semiColonRange.location - subrange.location + 1);
NSString *escapeString = [self substringWithRange:escapeRange];
- unsigned length = [escapeString length];
+ NSUInteger length = [escapeString length];
// a squence must be longer than 3 (&lt;) and less than 11 (&thetasym;)
if (length > 3 && length < 11) {
if ([escapeString characterAtIndex:1] == '#') {
diff --git a/Foundation/GTMNSString+HTMLTest.m b/Foundation/GTMNSString+HTMLTest.m
index c7b931a..a56c5a5 100644
--- a/Foundation/GTMNSString+HTMLTest.m
+++ b/Foundation/GTMNSString+HTMLTest.m
@@ -55,6 +55,9 @@
STAssertEqualObjects([string gtm_stringByEscapingForHTML],
[NSString stringWithUTF8String:"abcا1ب&lt;تdef&amp;"],
@"HTML escaping failed");
+
+ // test empty string
+ STAssertEqualObjects([@"" gtm_stringByEscapingForHTML], @"", nil);
} // testStringByEscapingHTML
- (void)testStringByEscapingAsciiHTML {
diff --git a/Foundation/GTMNSString+Utilities.h b/Foundation/GTMNSString+Utilities.h
deleted file mode 100644
index 3b4ee00..0000000
--- a/Foundation/GTMNSString+Utilities.h
+++ /dev/null
@@ -1,41 +0,0 @@
-//
-// GTMNSString+Utilities.h
-// Misc NSString Utilities
-//
-// 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 <Foundation/Foundation.h>
-
-@interface NSString (GTMNSStringUtilitiesAdditions)
-
-// Returns a a UTF16 buffer. Avoids copying the data if at all
-// possible for fastest possible/least memory access to the underlying
-// unicode characters (UTF16). This returned buffer is NOT null terminated.
-// *DANGER*
-// Since we avoid copying data you can only be guaranteed access to
-// the bytes of the data for the lifetime of the string that you have extracted
-// the data from. This exists to allow speedy access to the underlying buffer
-// and guaranteed memory cleanup if memory needs to be allocated.
-// Do not free the returned pointer.
-//
-// Args:
-// length - returns the number of unichars in the buffer. Send in nil if
-// you don't care.
-//
-// Returns:
-// pointer to the buffer. Nil on failure.
-- (const unichar*)gtm_UTF16StringWithLength:(size_t*)length;
-@end
diff --git a/Foundation/GTMNSString+Utilities.m b/Foundation/GTMNSString+Utilities.m
deleted file mode 100644
index 3419d43..0000000
--- a/Foundation/GTMNSString+Utilities.m
+++ /dev/null
@@ -1,48 +0,0 @@
-//
-// GTMNSString+Utilities.m
-// Misc NSString Utilities
-//
-// 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 "GTMDefines.h"
-#import "GTMNSString+Utilities.h"
-
-@implementation NSString (GTMNSStringUtilitiesAdditions)
-
-- (const unichar*)gtm_UTF16StringWithLength:(size_t*)length {
- size_t size = [self length];
- const UniChar *buffer = CFStringGetCharactersPtr((CFStringRef)self);
- if (!buffer) {
- size_t memsize = size * sizeof(UniChar);
-
- // nope, alloc buffer and fetch the chars ourselves
- buffer = malloc(memsize);
- if (!buffer) {
- // COV_NF_BEGIN - Memory fail case
- _GTMDevLog(@"couldn't alloc buffer");
- return nil;
- // COV_NF_END
- }
- [self getCharacters:(void*)buffer];
- [NSData dataWithBytesNoCopy:(void*)buffer length:size];
- }
- if (length) {
- *length = size;
- }
- return buffer;
-}
-
-@end
diff --git a/Foundation/GTMNSString+UtilitiesTest.m b/Foundation/GTMNSString+UtilitiesTest.m
deleted file mode 100644
index 8394aaf..0000000
--- a/Foundation/GTMNSString+UtilitiesTest.m
+++ /dev/null
@@ -1,62 +0,0 @@
-//
-// GTMNSString+UtilitiesTest.m
-// Misc NSString Utilities
-//
-// Copyright 2006-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 "GTMNSString+Utilities.h"
-
-@interface GTMNSString_UtilitiesTest : SenTestCase
-@end
-
-@implementation GTMNSString_UtilitiesTest
-
-- (void)testStringWithLength {
- NSString *string = @"";
- size_t length;
- const unichar *buffer = [string gtm_UTF16StringWithLength:&length];
- STAssertNotNULL(buffer, @"Buffer shouldn't be NULL");
- STAssertEquals(length, 0LU, @"Length should be 0");
-
- UniChar unicharBytes[] = { 0x50, 0x51, 0x52 };
- string = [[[NSString alloc] initWithCharactersNoCopy:unicharBytes
- length:3
- freeWhenDone:NO] autorelease];
- buffer = [string gtm_UTF16StringWithLength:&length];
- STAssertEquals(buffer,
- (const unichar*)unicharBytes,
- @"Pointers should be equal");
- STAssertEquals(length,
- 3UL,
- nil);
-
- char utf8Bytes[] = { 0x50, 0x51, 0x52, 0x0 };
- string = [NSString stringWithUTF8String:utf8Bytes];
- buffer = [string gtm_UTF16StringWithLength:&length];
- STAssertNotEquals(buffer,
- (const unichar*)utf8Bytes,
- @"Pointers should not be equal");
- STAssertEquals(length,
- 3UL,
- nil);
- buffer = [string gtm_UTF16StringWithLength:nil];
- STAssertNotEquals(buffer,
- (const unichar*)utf8Bytes,
- @"Pointers should not be equal");
-}
-@end
diff --git a/Foundation/GTMNSString+XML.m b/Foundation/GTMNSString+XML.m
index 7ea97b4..bc2f130 100644
--- a/Foundation/GTMNSString+XML.m
+++ b/Foundation/GTMNSString+XML.m
@@ -19,18 +19,17 @@
#import "GTMDefines.h"
#import "GTMNSString+XML.h"
#import "GTMGarbageCollection.h"
-#import "GTMNSString+Utilities.h"
-#import "GTMMethodCheck.h"
-
-typedef enum {
- kGMXMLCharModeEncodeQUOT = 0,
- kGMXMLCharModeEncodeAMP = 1,
- kGMXMLCharModeEncodeAPOS = 2,
- kGMXMLCharModeEncodeLT = 3,
- kGMXMLCharModeEncodeGT = 4,
- kGMXMLCharModeValid = 99,
- kGMXMLCharModeInvalid = 100,
-} GMXMLCharMode;
+
+enum {
+ kGTMXMLCharModeEncodeQUOT = 0,
+ kGTMXMLCharModeEncodeAMP = 1,
+ kGTMXMLCharModeEncodeAPOS = 2,
+ kGTMXMLCharModeEncodeLT = 3,
+ kGTMXMLCharModeEncodeGT = 4,
+ kGTMXMLCharModeValid = 99,
+ kGTMXMLCharModeInvalid = 100,
+};
+typedef NSUInteger GTMXMLCharMode;
static NSString *gXMLEntityList[] = {
// this must match the above order
@@ -41,7 +40,7 @@ static NSString *gXMLEntityList[] = {
@"&gt;",
};
-FOUNDATION_STATIC_INLINE GMXMLCharMode XMLModeForUnichar(UniChar c) {
+FOUNDATION_STATIC_INLINE GTMXMLCharMode XMLModeForUnichar(UniChar c) {
// Per XML spec Section 2.2 Characters
// ( http://www.w3.org/TR/REC-xml/#charsets )
@@ -53,42 +52,42 @@ FOUNDATION_STATIC_INLINE GMXMLCharMode XMLModeForUnichar(UniChar c) {
if (c >= 0x20) {
switch (c) {
case 34:
- return kGMXMLCharModeEncodeQUOT;
+ return kGTMXMLCharModeEncodeQUOT;
case 38:
- return kGMXMLCharModeEncodeAMP;
+ return kGTMXMLCharModeEncodeAMP;
case 39:
- return kGMXMLCharModeEncodeAPOS;
+ return kGTMXMLCharModeEncodeAPOS;
case 60:
- return kGMXMLCharModeEncodeLT;
+ return kGTMXMLCharModeEncodeLT;
case 62:
- return kGMXMLCharModeEncodeGT;
+ return kGTMXMLCharModeEncodeGT;
default:
- return kGMXMLCharModeValid;
+ return kGTMXMLCharModeValid;
}
} else {
if (c == '\n')
- return kGMXMLCharModeValid;
+ return kGTMXMLCharModeValid;
if (c == '\r')
- return kGMXMLCharModeValid;
+ return kGTMXMLCharModeValid;
if (c == '\t')
- return kGMXMLCharModeValid;
- return kGMXMLCharModeInvalid;
+ return kGTMXMLCharModeValid;
+ return kGTMXMLCharModeInvalid;
}
}
if (c < 0xE000)
- return kGMXMLCharModeInvalid;
+ return kGTMXMLCharModeInvalid;
if (c <= 0xFFFD)
- return kGMXMLCharModeValid;
+ return kGTMXMLCharModeValid;
// UniChar can't have the following values
// if (c < 0x10000)
- // return kGMXMLCharModeInvalid;
+ // return kGTMXMLCharModeInvalid;
// if (c <= 0x10FFFF)
- // return kGMXMLCharModeValid;
+ // return kGTMXMLCharModeValid;
- return kGMXMLCharModeInvalid;
+ return kGTMXMLCharModeInvalid;
} // XMLModeForUnichar
@@ -103,26 +102,42 @@ static NSString *AutoreleasedCloneForXML(NSString *src, BOOL escaping) {
// we can't use the CF call here because it leaves the invalid chars
// in the string.
- int length = [src length];
+ NSUInteger length = [src length];
if (!length) {
- return nil;
+ return src;
}
NSMutableString *finalString = [NSMutableString string];
- const UniChar *buffer = [src gtm_UTF16StringWithLength:nil];
- _GTMDevAssert(buffer, @"couldn't alloc buffer");
+
+ // this block is common between GTMNSString+HTML and GTMNSString+XML but
+ // it's so short that it isn't really worth trying to share.
+ const UniChar *buffer = CFStringGetCharactersPtr((CFStringRef)src);
+ if (!buffer) {
+ size_t memsize = length * sizeof(UniChar);
+
+ // nope, alloc buffer and fetch the chars ourselves
+ buffer = malloc(memsize);
+ if (!buffer) {
+ // COV_NF_START - Memory fail case
+ _GTMDevLog(@"couldn't alloc buffer");
+ return nil;
+ // COV_NF_END
+ }
+ [src getCharacters:(void*)buffer];
+ [NSData dataWithBytesNoCopy:(void*)buffer length:memsize];
+ }
const UniChar *goodRun = buffer;
- int goodRunLength = 0;
+ NSUInteger goodRunLength = 0;
- for (int i = 0; i < length; ++i) {
+ for (NSUInteger i = 0; i < length; ++i) {
- GMXMLCharMode cMode = XMLModeForUnichar(buffer[i]);
+ GTMXMLCharMode cMode = XMLModeForUnichar(buffer[i]);
// valid chars go as is, and if we aren't doing entities, then
// everything goes as is.
- if ((cMode == kGMXMLCharModeValid) ||
- (!escaping && (cMode != kGMXMLCharModeInvalid))) {
+ if ((cMode == kGTMXMLCharModeValid) ||
+ (!escaping && (cMode != kGTMXMLCharModeInvalid))) {
// goes as is
goodRunLength += 1;
} else {
@@ -137,7 +152,7 @@ static NSString *AutoreleasedCloneForXML(NSString *src, BOOL escaping) {
}
// if it wasn't invalid, add the encoded version
- if (cMode != kGMXMLCharModeInvalid) {
+ if (cMode != kGTMXMLCharModeInvalid) {
// add this encoded
[finalString appendString:gXMLEntityList[cMode]];
}
@@ -157,7 +172,6 @@ static NSString *AutoreleasedCloneForXML(NSString *src, BOOL escaping) {
} // AutoreleasedCloneForXML
@implementation NSString (GTMNSStringXMLAdditions)
-GTM_METHOD_CHECK(NSString, gtm_UTF16StringWithLength:);
- (NSString *)gtm_stringBySanitizingAndEscapingForXML {
return AutoreleasedCloneForXML(self, YES);
diff --git a/Foundation/GTMNSString+XMLTest.m b/Foundation/GTMNSString+XMLTest.m
index 926708f..f1e964d 100644
--- a/Foundation/GTMNSString+XMLTest.m
+++ b/Foundation/GTMNSString+XMLTest.m
@@ -53,6 +53,9 @@
STAssertEqualObjects([ascString gtm_stringBySanitizingAndEscapingForXML],
@"abcde\nf",
@"Sanitize and Escape for XML from asc buffer failed");
+
+ // test empty string
+ STAssertEqualObjects([@"" gtm_stringBySanitizingAndEscapingForXML], @"", nil);
}
- (void)testStringBySanitizingToXMLSpec {
@@ -82,6 +85,9 @@
STAssertEqualObjects([ascString gtm_stringBySanitizingToXMLSpec],
@"abcde\nf",
@"Sanitize and Escape for XML from asc buffer failed");
+
+ // test empty string
+ STAssertEqualObjects([@"" gtm_stringBySanitizingToXMLSpec], @"", nil);
}
@end
diff --git a/Foundation/GTMObjC2Runtime.h b/Foundation/GTMObjC2Runtime.h
index 325a752..ab34cfb 100644
--- a/Foundation/GTMObjC2Runtime.h
+++ b/Foundation/GTMObjC2Runtime.h
@@ -16,8 +16,8 @@
// the License.
//
-#import <objc/objc-runtime.h>
-#import <objc/Object.h>
+#import <objc/objc-api.h>
+#import "GTMDefines.h"
// These functions exist for code that we want to compile on both the < 10.5
// sdks and on the >= 10.5 sdks without warnings. It basically reimplements
@@ -35,6 +35,18 @@
#define AT_REQUIRED
#endif
+// The file objc-runtime.h was moved to runtime.h and in Leopard, objc-runtime.h
+// was just a wrapper around runtime.h. For the iPhone SDK, this objc-runtime.h
+// is removed in the iPhoneOS2.0 SDK.
+//
+// The |Object| class was removed in the iPhone2.0 SDK too.
+#if GTM_IPHONE_SDK
+#import <objc/runtime.h>
+#else
+#import <objc/objc-runtime.h>
+#import <objc/Object.h>
+#endif
+
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
#import "objc/Protocol.h"
diff --git a/Foundation/GTMObjC2Runtime.m b/Foundation/GTMObjC2Runtime.m
index 00a3c6e..df3c9ca 100644
--- a/Foundation/GTMObjC2Runtime.m
+++ b/Foundation/GTMObjC2Runtime.m
@@ -44,7 +44,7 @@ BOOL class_conformsToProtocol(Class cls, Protocol *protocol) {
struct objc_protocol_list *protos;
for (protos = cls->protocols; protos != NULL; protos = protos->next) {
- for (int i = 0; i < protos->count; i++) {
+ for (long i = 0; i < protos->count; i++) {
if ([protos->list[i] conformsTo:protocol]) {
return YES;
}
diff --git a/Foundation/GTMObjectSingleton.h b/Foundation/GTMObjectSingleton.h
index 116d232..4763b68 100644
--- a/Foundation/GTMObjectSingleton.h
+++ b/Foundation/GTMObjectSingleton.h
@@ -26,7 +26,7 @@
///
/// Sample usage:
///
-/// SINGLETON_BOILERPLATE(GMSomeUsefulManager, sharedSomeUsefulManager)
+/// GTMOBJECT_SINGLETON_BOILERPLATE(SomeUsefulManager, sharedSomeUsefulManager)
/// (with no trailing semicolon)
///
#define GTMOBJECT_SINGLETON_BOILERPLATE(_object_name_, _shared_obj_name_) \
@@ -57,8 +57,8 @@ static _object_name_ *z##_shared_obj_name_ = nil; \
- (id)retain { \
return self; \
} \
-- (unsigned int)retainCount { \
- return UINT_MAX; \
+- (NSUInteger)retainCount { \
+ return NSUIntegerMax; \
} \
- (void)release { \
} \
diff --git a/Foundation/GTMProgressMonitorInputStream.h b/Foundation/GTMProgressMonitorInputStream.h
new file mode 100644
index 0000000..d3da5cd
--- /dev/null
+++ b/Foundation/GTMProgressMonitorInputStream.h
@@ -0,0 +1,73 @@
+//
+// GTMProgressMonitorInputStream.m
+//
+// Copyright 2007-2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import <Foundation/Foundation.h>
+
+// The monitored input stream calls back into the monitor delegate
+// with the number of bytes and total size
+//
+// - (void)inputStream:(GTMProgressMonitorInputStream *)stream
+// hasDeliveredByteCount:(unsigned long long)numberOfBytesRead
+// ofTotalByteCount:(unsigned long long)dataLength;
+
+@interface GTMProgressMonitorInputStream : NSInputStream {
+
+ NSInputStream *inputStream_; // encapsulated stream that does the work
+
+ unsigned long long dataSize_; // size of data in the source
+ unsigned long long numBytesRead_; // bytes read from the input stream so far
+
+ __weak id monitorDelegate_; // WEAK, not retained
+ SEL monitorSelector_;
+
+ __weak id monitorSource_; // WEAK, not retained
+}
+
+// Length is passed to the progress callback; it may be zero if the progress
+// callback can handle that (mainly meant so the monitor delegate can update the
+// bounds/position for a progress indicator.
++ (id)inputStreamWithStream:(NSInputStream *)input
+ length:(unsigned long long)length;
+
+- (id)initWithStream:(NSInputStream *)input
+ length:(unsigned long long)length;
+
+// The monitor is called when bytes have been read
+//
+// monitorDelegate should respond to a selector with a signature matching:
+//
+// - (void)inputStream:(GTMProgressMonitorInputStream *)stream
+// hasDeliveredBytes:(unsigned long long)numReadSoFar
+// ofTotalBytes:(unsigned long long)total
+//
+// |total| will be the length passed when this GTMProgressMonitorInputStream was
+// created.
+
+- (void)setMonitorDelegate:(id)monitorDelegate // not retained
+ selector:(SEL)monitorSelector;
+- (id)monitorDelegate;
+- (SEL)monitorSelector;
+
+// The source argument lets the delegate know the source of this input stream.
+// this class does nothing w/ this, it's just here to provide context to your
+// monitorDelegate.
+- (void)setMonitorSource:(id)source; // not retained
+- (id)monitorSource;
+
+@end
+
diff --git a/Foundation/GTMProgressMonitorInputStream.m b/Foundation/GTMProgressMonitorInputStream.m
new file mode 100644
index 0000000..2336268
--- /dev/null
+++ b/Foundation/GTMProgressMonitorInputStream.m
@@ -0,0 +1,187 @@
+//
+// GTMProgressMonitorInputStream.m
+//
+// Copyright 2007-2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import "GTMProgressMonitorInputStream.h"
+#import "GTMDefines.h"
+#import "GTMDebugSelectorValidation.h"
+
+@implementation GTMProgressMonitorInputStream
+
+// we'll forward all unhandled messages to the NSInputStream class
+// or to the encapsulated input stream. This is needed
+// for all messages sent to NSInputStream which aren't
+// handled by our superclass; that includes various private run
+// loop calls.
++ (NSMethodSignature*)methodSignatureForSelector:(SEL)selector {
+ return [NSInputStream methodSignatureForSelector:selector];
+}
+
++ (void)forwardInvocation:(NSInvocation*)invocation {
+ [invocation invokeWithTarget:[NSInputStream class]];
+}
+
+- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector {
+ return [inputStream_ methodSignatureForSelector:selector];
+}
+
+- (void)forwardInvocation:(NSInvocation*)invocation {
+ [invocation invokeWithTarget:inputStream_];
+}
+
+#pragma mark -
+
++ (id)inputStreamWithStream:(NSInputStream *)input
+ length:(unsigned long long)length {
+
+ return [[[self alloc] initWithStream:input
+ length:length] autorelease];
+}
+
+- (id)initWithStream:(NSInputStream *)input
+ length:(unsigned long long)length {
+
+ if ((self = [super init]) != nil) {
+
+ inputStream_ = [input retain];
+ dataSize_ = length;
+ }
+ return self;
+}
+
+- (id)init {
+ _GTMDevAssert(NO, @"should call initWithStream:length:");
+ return nil;
+}
+
+- (void)dealloc {
+ [inputStream_ release];
+ [super dealloc];
+}
+
+#pragma mark -
+
+
+- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len {
+
+ NSInteger numRead = [inputStream_ read:buffer maxLength:len];
+
+ if (numRead > 0) {
+
+ numBytesRead_ += numRead;
+
+ if (monitorDelegate_ && monitorSelector_) {
+
+ // call the monitor delegate with the number of bytes read and the
+ // total bytes read
+
+ NSMethodSignature *signature = [monitorDelegate_ methodSignatureForSelector:monitorSelector_];
+ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
+ [invocation setSelector:monitorSelector_];
+ [invocation setTarget:monitorDelegate_];
+ [invocation setArgument:&self atIndex:2];
+ [invocation setArgument:&numBytesRead_ atIndex:3];
+ [invocation setArgument:&dataSize_ atIndex:4];
+ [invocation invoke];
+ }
+ }
+ return numRead;
+}
+
+- (BOOL)getBuffer:(uint8_t **)buffer length:(NSUInteger *)len {
+ return [inputStream_ getBuffer:buffer length:len];
+}
+
+- (BOOL)hasBytesAvailable {
+ return [inputStream_ hasBytesAvailable];
+}
+
+#pragma mark Standard messages
+
+// Pass expected messages to our encapsulated stream.
+//
+// We want our encapsulated NSInputStream to handle the standard messages;
+// we don't want the superclass to handle them.
+- (void)open {
+ [inputStream_ open];
+}
+
+- (void)close {
+ [inputStream_ close];
+}
+
+- (id)delegate {
+ return [inputStream_ delegate];
+}
+
+- (void)setDelegate:(id)delegate {
+ [inputStream_ setDelegate:delegate];
+}
+
+- (id)propertyForKey:(NSString *)key {
+ return [inputStream_ propertyForKey:key];
+}
+- (BOOL)setProperty:(id)property forKey:(NSString *)key {
+ return [inputStream_ setProperty:property forKey:key];
+}
+
+- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode {
+ [inputStream_ scheduleInRunLoop:aRunLoop forMode:mode];
+}
+- (void)removeFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode {
+ [inputStream_ removeFromRunLoop:aRunLoop forMode:mode];
+}
+
+- (NSStreamStatus)streamStatus {
+ return [inputStream_ streamStatus];
+}
+
+- (NSError *)streamError {
+ return [inputStream_ streamError];
+}
+
+#pragma mark Setters and getters
+
+- (void)setMonitorDelegate:(id)monitorDelegate
+ selector:(SEL)monitorSelector {
+ monitorDelegate_ = monitorDelegate; // non-retained
+ monitorSelector_ = monitorSelector;
+ GTMAssertSelectorNilOrImplementedWithArguments(monitorDelegate,
+ monitorSelector,
+ @encode(GTMProgressMonitorInputStream *),
+ @encode(unsigned long long),
+ @encode(unsigned long long),
+ NULL);
+}
+
+- (id)monitorDelegate {
+ return monitorDelegate_;
+}
+
+- (SEL)monitorSelector {
+ return monitorSelector_;
+}
+
+- (void)setMonitorSource:(id)source {
+ monitorSource_ = source; // non-retained
+}
+
+- (id)monitorSource {
+ return monitorSource_;
+}
+
+@end
diff --git a/Foundation/GTMRegex.h b/Foundation/GTMRegex.h
index ee56b98..75cffe2 100644
--- a/Foundation/GTMRegex.h
+++ b/Foundation/GTMRegex.h
@@ -18,9 +18,10 @@
#import <Foundation/Foundation.h>
#import <regex.h>
+#import "GTMDefines.h"
/// Options for controlling the behavior of the matches
-typedef enum {
+enum {
kGTMRegexOptionIgnoreCase = 0x01,
// Ignore case in matching, ie: 'a' matches 'a' or 'A'
@@ -48,7 +49,8 @@ typedef enum {
// and would also match
// fooAAA\nbar
-} GTMRegexOptions;
+};
+typedef NSUInteger GTMRegexOptions;
/// Global contants needed for errors from consuming patterns
@@ -148,7 +150,7 @@ _EXTERN NSString* kGTMRegexPatternErrorErrorString _INITIALIZE_AS(@"patternError
// Sub Patterns are basically the number of parenthesis blocks w/in the pattern.
// ie: The pattern "foo((bar)|(baz))" has 3 sub patterns.
//
-- (int)subPatternCount;
+- (NSUInteger)subPatternCount;
/// Returns YES if the whole string |str| matches the pattern.
- (BOOL)matchesString:(NSString *)str;
@@ -266,7 +268,7 @@ _EXTERN NSString* kGTMRegexPatternErrorErrorString _INITIALIZE_AS(@"patternError
@private
NSData *utf8StrBuf_;
regmatch_t *regMatches_; // STRONG: ie-we call free
- int numRegMatches_;
+ NSUInteger numRegMatches_;
BOOL isMatch_;
}
@@ -296,7 +298,7 @@ _EXTERN NSString* kGTMRegexPatternErrorErrorString _INITIALIZE_AS(@"patternError
// 4: nil
// 5: "baz"
//
-- (NSString *)subPatternString:(int)index;
+- (NSString *)subPatternString:(NSUInteger)index;
@end
diff --git a/Foundation/GTMRegex.m b/Foundation/GTMRegex.m
index c50ff2b..92eb576 100644
--- a/Foundation/GTMRegex.m
+++ b/Foundation/GTMRegex.m
@@ -47,7 +47,7 @@ static NSString *const kReplacementPattern =
BOOL allSegments_;
BOOL treatStartOfNewSegmentAsBeginningOfString_;
regoff_t curParseIndex_;
- regmatch_t *savedRegMatches_;
+ __strong regmatch_t *savedRegMatches_;
}
- (id)initWithRegex:(GTMRegex *)regex
processString:(NSString *)str
@@ -58,7 +58,7 @@ static NSString *const kReplacementPattern =
@interface GTMRegexStringSegment (PrivateMethods)
- (id)initWithUTF8StrBuf:(NSData *)utf8StrBuf
regMatches:(regmatch_t *)regMatches
- numRegMatches:(int)numRegMatches
+ numRegMatches:(NSUInteger)numRegMatches
isMatch:(BOOL)isMatch;
@end
@@ -89,10 +89,10 @@ static NSString *const kReplacementPattern =
// a unichar buffer and scanning that, along w/ pushing the data over in
// chunks (when possible).
- unsigned int len = [str length];
+ NSUInteger len = [str length];
NSMutableString *result = [NSMutableString stringWithCapacity:len];
- for (unsigned int x = 0; x < len; ++x) {
+ for (NSUInteger x = 0; x < len; ++x) {
unichar ch = [str characterAtIndex:x];
switch (ch) {
case '^':
@@ -190,6 +190,17 @@ static NSString *const kReplacementPattern =
return self;
}
+- (void)finalize {
+ // we used pattern_ as our flag that we initialized the regex_t
+ if (pattern_) {
+ regfree(&regexData_);
+ [pattern_ release];
+ // play it safe and clear it since we use it as a flag for regexData_
+ pattern_ = nil;
+ }
+ [super finalize];
+}
+
- (void)dealloc {
// we used pattern_ as our flag that we initialized the regex_t
if (pattern_) {
@@ -201,7 +212,7 @@ static NSString *const kReplacementPattern =
[super dealloc];
}
-- (int)subPatternCount {
+- (NSUInteger)subPatternCount {
return regexData_.re_nsub;
}
@@ -223,7 +234,7 @@ static NSString *const kReplacementPattern =
- (NSArray *)subPatternsOfString:(NSString *)str {
NSArray *result = nil;
- int count = regexData_.re_nsub + 1;
+ NSUInteger count = regexData_.re_nsub + 1;
regmatch_t *regMatches = malloc(sizeof(regmatch_t) * count);
if (!regMatches)
return nil; // COV_NF_LINE - no real way to force this in a unittest
@@ -243,22 +254,22 @@ static NSString *const kReplacementPattern =
if ((regMatches[0].rm_so != 0) ||
(regMatches[0].rm_eo != [str lengthOfBytesUsingEncoding:NSUTF8StringEncoding])) {
// only matched a sub part of the string
- return NO;
+ return nil;
}
NSMutableArray *buildResult = [NSMutableArray arrayWithCapacity:count];
- for (int x = 0 ; x < count ; ++x) {
+ for (NSUInteger x = 0 ; x < count ; ++x) {
if ((regMatches[x].rm_so == -1) && (regMatches[x].rm_eo == -1)) {
// add NSNull since it wasn't used
[buildResult addObject:[NSNull null]];
} else {
// fetch the string
const char *base = utf8Str + regMatches[x].rm_so;
- unsigned len = regMatches[x].rm_eo - regMatches[x].rm_so;
+ regoff_t len = regMatches[x].rm_eo - regMatches[x].rm_so;
NSString *sub =
[[[NSString alloc] initWithBytes:base
- length:len
+ length:(NSUInteger)len
encoding:NSUTF8StringEncoding] autorelease];
[buildResult addObject:sub];
}
@@ -284,10 +295,10 @@ static NSString *const kReplacementPattern =
flags:0]) {
// fetch the string
const char *base = utf8Str + regMatch.rm_so;
- unsigned len = regMatch.rm_eo - regMatch.rm_so;
+ regoff_t len = regMatch.rm_eo - regMatch.rm_so;
result =
[[[NSString alloc] initWithBytes:base
- length:len
+ length:(NSUInteger)len
encoding:NSUTF8StringEncoding] autorelease];
}
return result;
@@ -497,6 +508,7 @@ static NSString *const kReplacementPattern =
return self;
}
+// Don't need a finalize because savedRegMatches_ is marked __strong
- (void)dealloc {
if (savedRegMatches_) {
free(savedRegMatches_);
@@ -584,7 +596,7 @@ static NSString *const kReplacementPattern =
isMatch = NO;
// mark everything but the zero slot w/ not used
- for (int x = [regex_ subPatternCount]; x > 0; --x) {
+ for (NSUInteger x = [regex_ subPatternCount]; x > 0; --x) {
nextMatches[x].rm_so = nextMatches[x].rm_eo = -1;
}
nextMatches[0].rm_so = curParseIndex_;
@@ -611,7 +623,7 @@ static NSString *const kReplacementPattern =
if (allSegments_) {
isMatch = NO;
// mark everything but the zero slot w/ not used
- for (int x = [regex_ subPatternCount]; x > 0; --x) {
+ for (NSUInteger x = [regex_ subPatternCount]; x > 0; --x) {
nextMatches[x].rm_so = nextMatches[x].rm_eo = -1;
}
nextMatches[0].rm_so = curParseIndex_;
@@ -685,7 +697,7 @@ static NSString *const kReplacementPattern =
return [self subPatternString:0];
}
-- (NSString *)subPatternString:(int)index {
+- (NSString *)subPatternString:(NSUInteger)index {
if ((index < 0) || (index > numRegMatches_))
return nil;
@@ -695,9 +707,9 @@ static NSString *const kReplacementPattern =
// fetch the string
const char *base = (const char*)[utf8StrBuf_ bytes] + regMatches_[index].rm_so;
- unsigned len = regMatches_[index].rm_eo - regMatches_[index].rm_so;
+ regoff_t len = regMatches_[index].rm_eo - regMatches_[index].rm_so;
return [[[NSString alloc] initWithBytes:base
- length:len
+ length:(NSUInteger)len
encoding:NSUTF8StringEncoding] autorelease];
}
@@ -705,7 +717,7 @@ static NSString *const kReplacementPattern =
NSMutableString *result =
[NSMutableString stringWithFormat:@"%@<%p> { isMatch=\"%s\", subPatterns=(",
[self class], self, (isMatch_ ? "YES" : "NO")];
- for (int x = 0; x <= numRegMatches_; ++x) {
+ for (NSUInteger x = 0; x <= numRegMatches_; ++x) {
NSString *format = @", \"%.*s\"";
if (x == 0)
format = @" \"%.*s\"";
@@ -725,7 +737,7 @@ static NSString *const kReplacementPattern =
- (id)initWithUTF8StrBuf:(NSData *)utf8StrBuf
regMatches:(regmatch_t *)regMatches
- numRegMatches:(int)numRegMatches
+ numRegMatches:(NSUInteger)numRegMatches
isMatch:(BOOL)isMatch {
self = [super init];
if (!self) return nil;
diff --git a/Foundation/GTMRegexTest.m b/Foundation/GTMRegexTest.m
index 22c571e..033b560 100644
--- a/Foundation/GTMRegexTest.m
+++ b/Foundation/GTMRegexTest.m
@@ -71,7 +71,7 @@
withError:&error] autorelease], nil);
STAssertNotNil(error, nil);
STAssertEqualObjects([error domain], kGTMRegexErrorDomain, nil);
- STAssertEquals([error code], kGTMRegexPatternParseFailedError, nil);
+ STAssertEquals([error code], (NSInteger)kGTMRegexPatternParseFailedError, nil);
NSDictionary *userInfo = [error userInfo];
STAssertNotNil(userInfo, @"failed to get userInfo from error");
STAssertEqualObjects([userInfo objectForKey:kGTMRegexPatternErrorPattern], @"(.", nil);
@@ -106,7 +106,7 @@
withError:&error], nil);
STAssertNotNil(error, nil);
STAssertEqualObjects([error domain], kGTMRegexErrorDomain, nil);
- STAssertEquals([error code], kGTMRegexPatternParseFailedError, nil);
+ STAssertEquals([error code], (NSInteger)kGTMRegexPatternParseFailedError, nil);
userInfo = [error userInfo];
STAssertNotNil(userInfo, @"failed to get userInfo from error");
STAssertEqualObjects([userInfo objectForKey:kGTMRegexPatternErrorPattern], @"(.", nil);
@@ -385,11 +385,11 @@
}
- (void)testSubPatternCount {
- STAssertEquals(0, [[GTMRegex regexWithPattern:@".*"] subPatternCount], nil);
- STAssertEquals(1, [[GTMRegex regexWithPattern:@"(.*)"] subPatternCount], nil);
- STAssertEquals(1, [[GTMRegex regexWithPattern:@"[fo]*(.*)[bar]*"] subPatternCount], nil);
- STAssertEquals(3, [[GTMRegex regexWithPattern:@"([fo]*)(.*)([bar]*)"] subPatternCount], nil);
- STAssertEquals(7, [[GTMRegex regexWithPattern:@"(([bar]*)|([fo]*))(.*)(([bar]*)|([fo]*))"] subPatternCount], nil);
+ STAssertEquals((NSUInteger)0, [[GTMRegex regexWithPattern:@".*"] subPatternCount], nil);
+ STAssertEquals((NSUInteger)1, [[GTMRegex regexWithPattern:@"(.*)"] subPatternCount], nil);
+ STAssertEquals((NSUInteger)1, [[GTMRegex regexWithPattern:@"[fo]*(.*)[bar]*"] subPatternCount], nil);
+ STAssertEquals((NSUInteger)3, [[GTMRegex regexWithPattern:@"([fo]*)(.*)([bar]*)"] subPatternCount], nil);
+ STAssertEquals((NSUInteger)7, [[GTMRegex regexWithPattern:@"(([bar]*)|([fo]*))(.*)(([bar]*)|([fo]*))"] subPatternCount], nil);
}
- (void)testMatchesString {
@@ -418,15 +418,15 @@
- (void)testSubPatternsOfString {
GTMRegex *regex = [GTMRegex regexWithPattern:@"(fo(o+))((bar)|(baz))"];
STAssertNotNil(regex, nil);
- STAssertEquals(5, [regex subPatternCount], nil);
+ STAssertEquals((NSUInteger)5, [regex subPatternCount], nil);
NSArray *subPatterns = [regex subPatternsOfString:@"foooooobaz"];
STAssertNotNil(subPatterns, nil);
- STAssertEquals(6U, [subPatterns count], nil);
+ STAssertEquals((NSUInteger)6, [subPatterns count], nil);
STAssertEqualStrings(@"foooooobaz", [subPatterns objectAtIndex:0], nil);
STAssertEqualStrings(@"foooooo", [subPatterns objectAtIndex:1], nil);
STAssertEqualStrings(@"ooooo", [subPatterns objectAtIndex:2], nil);
STAssertEqualStrings(@"baz", [subPatterns objectAtIndex:3], nil);
- STAssertTrue(([NSNull null] == [subPatterns objectAtIndex:4]), nil);
+ STAssertEqualObjects([NSNull null], [subPatterns objectAtIndex:4], nil);
STAssertEqualStrings(@"baz", [subPatterns objectAtIndex:5], nil);
// not there
@@ -562,7 +562,7 @@
// now test the saved sub segments
regex = [GTMRegex regexWithPattern:@"(foo)((bar)|(baz))"];
STAssertNotNil(regex, nil);
- STAssertEquals(4, [regex subPatternCount], nil);
+ STAssertEquals((NSUInteger)4, [regex subPatternCount], nil);
enumerator = [regex segmentEnumeratorForString:@"foobarxxfoobaz"];
STAssertNotNil(enumerator, nil);
// "foobar"
@@ -605,7 +605,7 @@
STAssertNotNil(enumerator, nil);
NSArray *allSegments = [enumerator allObjects];
STAssertNotNil(allSegments, nil);
- STAssertEquals(6U, [allSegments count], nil);
+ STAssertEquals((NSUInteger)6, [allSegments count], nil);
// test we are getting the flags right for newline
regex = [GTMRegex regexWithPattern:@"^a"];
@@ -737,7 +737,7 @@
// now test the saved sub segments
regex = [GTMRegex regexWithPattern:@"(foo)((bar)|(baz))"];
STAssertNotNil(regex, nil);
- STAssertEquals(4, [regex subPatternCount], nil);
+ STAssertEquals((NSUInteger)4, [regex subPatternCount], nil);
enumerator = [regex matchSegmentEnumeratorForString:@"foobarxxfoobaz"];
STAssertNotNil(enumerator, nil);
// "foobar"
@@ -774,7 +774,7 @@
STAssertNotNil(enumerator, nil);
NSArray *allSegments = [enumerator allObjects];
STAssertNotNil(allSegments, nil);
- STAssertEquals(3U, [allSegments count], nil);
+ STAssertEquals((NSUInteger)3, [allSegments count], nil);
// test we are getting the flags right for newline
regex = [GTMRegex regexWithPattern:@"^a"];
@@ -880,23 +880,23 @@
// default options
GTMRegex *regex = [GTMRegex regexWithPattern:@"a+"];
STAssertNotNil(regex, nil);
- STAssertGreaterThan([[regex description] length], 10U,
+ STAssertGreaterThan([[regex description] length], (NSUInteger)10,
@"failed to get a reasonable description for regex");
// enumerator
NSEnumerator *enumerator = [regex segmentEnumeratorForString:@"aaabbbccc"];
STAssertNotNil(enumerator, nil);
- STAssertGreaterThan([[enumerator description] length], 10U,
+ STAssertGreaterThan([[enumerator description] length], (NSUInteger)10,
@"failed to get a reasonable description for regex enumerator");
// string segment
GTMRegexStringSegment *seg = [enumerator nextObject];
STAssertNotNil(seg, nil);
- STAssertGreaterThan([[seg description] length], 10U,
+ STAssertGreaterThan([[seg description] length], (NSUInteger)10,
@"failed to get a reasonable description for regex string segment");
// regex w/ other options
regex = [GTMRegex regexWithPattern:@"a+"
options:(kGTMRegexOptionIgnoreCase | kGTMRegexOptionSupressNewlineSupport)];
STAssertNotNil(regex, nil);
- STAssertGreaterThan([[regex description] length], 10U,
+ STAssertGreaterThan([[regex description] length], (NSUInteger)10,
@"failed to get a reasonable description for regex w/ options");
}
@@ -926,12 +926,12 @@
- (void)testSubPatternsOfPattern {
NSArray *subPatterns = [@"foooooobaz" gtm_subPatternsOfPattern:@"(fo(o+))((bar)|(baz))"];
STAssertNotNil(subPatterns, nil);
- STAssertEquals(6U, [subPatterns count], nil);
+ STAssertEquals((NSUInteger)6, [subPatterns count], nil);
STAssertEqualStrings(@"foooooobaz", [subPatterns objectAtIndex:0], nil);
STAssertEqualStrings(@"foooooo", [subPatterns objectAtIndex:1], nil);
STAssertEqualStrings(@"ooooo", [subPatterns objectAtIndex:2], nil);
STAssertEqualStrings(@"baz", [subPatterns objectAtIndex:3], nil);
- STAssertTrue(([NSNull null] == [subPatterns objectAtIndex:4]), nil);
+ STAssertEqualObjects([NSNull null], [subPatterns objectAtIndex:4], nil);
STAssertEqualStrings(@"baz", [subPatterns objectAtIndex:5], nil);
// not there
@@ -1089,7 +1089,7 @@
STAssertNotNil(enumerator, nil);
NSArray *allSegments = [enumerator allObjects];
STAssertNotNil(allSegments, nil);
- STAssertEquals(6U, [allSegments count], nil);
+ STAssertEquals((NSUInteger)6, [allSegments count], nil);
}
- (void)testMatchSegmentEnumeratorForPattern {
@@ -1170,14 +1170,14 @@
STAssertNotNil(enumerator, nil);
NSArray *allSegments = [enumerator allObjects];
STAssertNotNil(allSegments, nil);
- STAssertEquals(3U, [allSegments count], nil);
+ STAssertEquals((NSUInteger)3, [allSegments count], nil);
}
- (void)testAllSubstringsMatchedByPattern {
NSArray *segments =
[@"afoobarbfooobaarfoobarzz" gtm_allSubstringsMatchedByPattern:@"foo+ba+r"];
STAssertNotNil(segments, nil);
- STAssertEquals(3U, [segments count], nil);
+ STAssertEquals((NSUInteger)3, [segments count], nil);
STAssertEqualStrings([segments objectAtIndex:0], @"foobar", nil);
STAssertEqualStrings([segments objectAtIndex:1], @"fooobaar", nil);
STAssertEqualStrings([segments objectAtIndex:2], @"foobar", nil);
@@ -1185,12 +1185,12 @@
// test no match
segments = [@"aaa" gtm_allSubstringsMatchedByPattern:@"foo+ba+r"];
STAssertNotNil(segments, nil);
- STAssertEquals(0U, [segments count], nil);
+ STAssertEquals((NSUInteger)0, [segments count], nil);
// test only match
segments = [@"foobar" gtm_allSubstringsMatchedByPattern:@"foo+ba+r"];
STAssertNotNil(segments, nil);
- STAssertEquals(1U, [segments count], nil);
+ STAssertEquals((NSUInteger)1, [segments count], nil);
STAssertEqualStrings([segments objectAtIndex:0], @"foobar", nil);
}
diff --git a/Foundation/GTMScriptRunnerTest.m b/Foundation/GTMScriptRunnerTest.m
index a4497b6..5229800 100644
--- a/Foundation/GTMScriptRunnerTest.m
+++ b/Foundation/GTMScriptRunnerTest.m
@@ -215,7 +215,7 @@
// make sure description doesn't choke
GTMScriptRunner *sr = [GTMScriptRunner runner];
STAssertNotNil(sr, @"Script runner must not be nil");
- STAssertGreaterThan([[sr description] length], 10U,
+ STAssertGreaterThan([[sr description] length], (NSUInteger)10,
@"expected a description of at least 10 chars");
}
diff --git a/Foundation/GTMSystemVersion.h b/Foundation/GTMSystemVersion.h
index 13c3c19..0f19596 100644
--- a/Foundation/GTMSystemVersion.h
+++ b/Foundation/GTMSystemVersion.h
@@ -43,6 +43,6 @@
// Returns a YES/NO if the system is 10.5 or better
+ (BOOL)isLeopardOrGreater;
-#endif // GTM_IPHONE_SDK
+#endif // GTM_MACOS_SDK
@end
diff --git a/Foundation/GTMSystemVersion.m b/Foundation/GTMSystemVersion.m
index a2e4d7b..da767ae 100644
--- a/Foundation/GTMSystemVersion.m
+++ b/Foundation/GTMSystemVersion.m
@@ -17,20 +17,55 @@
//
#import "GTMSystemVersion.h"
+#if GTM_MACOS_SDK
+#import <Carbon/Carbon.h>
+#endif
-static int sGTMSystemVersionMajor = 0;
-static int sGTMSystemVersionMinor = 0;
-static int sGTMSystemVersionBugFix = 0;
+static SInt32 sGTMSystemVersionMajor = 0;
+static SInt32 sGTMSystemVersionMinor = 0;
+static SInt32 sGTMSystemVersionBugFix = 0;
@implementation GTMSystemVersion
+ (void)initialize {
if (self == [GTMSystemVersion class]) {
+ // Gestalt is the recommended way of getting the OS version (despite a
+ // comment to the contrary in the 10.4 headers and docs; see
+ // <http://lists.apple.com/archives/carbon-dev/2007/Aug/msg00089.html>).
+ // The iPhone doesn't have Gestalt though, so use the plist there.
+#if GTM_MACOS_SDK
+ require_noerr(Gestalt(gestaltSystemVersionMajor, &sGTMSystemVersionMajor), failedGestalt);
+ require_noerr(Gestalt(gestaltSystemVersionMinor, &sGTMSystemVersionMinor), failedGestalt);
+ require_noerr(Gestalt(gestaltSystemVersionBugFix, &sGTMSystemVersionBugFix), failedGestalt);
+
+ return;
+
+ failedGestalt:
+ ;
+#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_3
+ // gestaltSystemVersionMajor et al are only on 10.4 and above, so they
+ // could fail when running on 10.3.
+ SInt32 binaryCodedDec;
+ OSStatus err = err = Gestalt(gestaltSystemVersion, &binaryCodedDec);
+ _GTMDevAssert(!err, @"Unable to get version from Gestalt");
+
+ // Note that this code will return x.9.9 for any system rev parts that are
+ // greater than 9 (i.e., 10.10.10 will be 10.9.9). This shouldn't ever be a
+ // problem as the code above takes care of 10.4+.
+ int msb = (binaryCodedDec & 0x0000F000L) >> 12;
+ msb *= 10;
+ int lsb = (binaryCodedDec & 0x00000F00L) >> 8;
+ sGTMSystemVersionMajor = msb + lsb;
+ sGTMSystemVersionMinor = (binaryCodedDec & 0x000000F0L) >> 4;
+ sGTMSystemVersionBugFix = (binaryCodedDec & 0x0000000FL);
+#endif // MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_3
+
+#else // GTM_MACOS_SDK
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSDictionary *systemVersionPlist = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"];
NSString *version = [systemVersionPlist objectForKey:@"ProductVersion"];
_GTMDevAssert(version, @"Unable to get version");
NSArray *versionInfo = [version componentsSeparatedByString:@"."];
- int length = [versionInfo count];
+ NSUInteger length = [versionInfo count];
_GTMDevAssert(length > 1 && length < 4, @"Unparseable version %@", version);
sGTMSystemVersionMajor = [[versionInfo objectAtIndex:0] intValue];
_GTMDevAssert(sGTMSystemVersionMajor != 0, @"Unknown version for %@", version);
@@ -39,6 +74,7 @@ static int sGTMSystemVersionBugFix = 0;
sGTMSystemVersionBugFix = [[versionInfo objectAtIndex:2] intValue];
}
[pool release];
+#endif // GTM_MACOS_SDK
}
}
@@ -82,6 +118,6 @@ static int sGTMSystemVersionBugFix = 0;
(sGTMSystemVersionMajor == 10 && sGTMSystemVersionMinor >= 5);
}
-#endif // GTM_IPHONE_SDK
+#endif // GTM_MACOS_SDK
@end
diff --git a/Foundation/TestData/GTMHTTPFetcherTestPage.html b/Foundation/TestData/GTMHTTPFetcherTestPage.html
new file mode 100644
index 0000000..1f44469
--- /dev/null
+++ b/Foundation/TestData/GTMHTTPFetcherTestPage.html
@@ -0,0 +1,9 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Test Page</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+</head>
+<body>
+This is a simple test page
+</body>
+</html>
diff --git a/Foundation/TestData/GTMHTTPFetcherTestServer b/Foundation/TestData/GTMHTTPFetcherTestServer
new file mode 100755
index 0000000..838c110
--- /dev/null
+++ b/Foundation/TestData/GTMHTTPFetcherTestServer
@@ -0,0 +1,274 @@
+#!/usr/bin/python
+#
+# Copyright 2006-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.
+#
+# A simple server for testing the http calls
+
+"""A simple BaseHTTPRequestHandler for unit testing GTM Network code
+
+This http server is for use by GTMHTTPFetcherTest.m in testing
+both authentication and object retrieval.
+
+Requests to the path /accounts/ClientLogin are assumed to be
+for login; other requests are for object retrieval
+"""
+
+__author__ = 'Google Inc.'
+
+import string
+import cgi
+import time
+import os
+import sys
+import re
+import mimetypes
+import socket
+from BaseHTTPServer import BaseHTTPRequestHandler
+from BaseHTTPServer import HTTPServer
+from optparse import OptionParser
+
+class ServerTimeoutException(Exception):
+ pass
+
+
+class TimeoutServer(HTTPServer):
+
+ """HTTP server for testing GTM network requests.
+
+ This server will throw an exception if it receives no connections for
+ several minutes. We use this to ensure that the server will be cleaned
+ up if something goes wrong during the unit testing.
+ """
+
+ def get_request(self):
+ self.socket.settimeout(120.0)
+ result = None
+ while result is None:
+ try:
+ result = self.socket.accept()
+ except socket.timeout:
+ raise ServerTimeoutException
+ result[0].settimeout(None)
+ return result
+
+
+class SimpleServer(BaseHTTPRequestHandler):
+
+ """HTTP request handler for testing GTM network requests.
+
+ This is an implementation of a request handler for BaseHTTPServer,
+ specifically designed for GTM network code usage.
+
+ Normal requests for GET/POST/PUT simply retrieve the file from the
+ supplied path, starting in the current directory. A cookie called
+ TestCookie is set by the response header, with the value of the filename
+ requested.
+
+ DELETE requests always succeed.
+
+ Appending ?status=n results in a failure with status value n.
+
+ Paths ending in .auth have the .auth extension stripped, and must have
+ an authorization header of "GoogleLogin auth=GoodAuthToken" to succeed.
+
+ Successful results have a Last-Modified header set; if that header's value
+ ("thursday") is supplied in a request's "If-Modified-Since" header, the
+ result is 304 (Not Modified).
+
+ Requests to /accounts/ClientLogin will fail if supplied with a body
+ containing Passwd=bad. If they contain logintoken and logincaptcha values,
+ those must be logintoken=CapToken&logincaptch=good to succeed.
+ """
+
+ def do_GET(self):
+ self.doAllRequests()
+
+ def do_POST(self):
+ self.doAllRequests()
+
+ def do_PUT(self):
+ self.doAllRequests()
+
+ def do_DELETE(self):
+ self.doAllRequests()
+
+ def doAllRequests(self):
+ # This method handles all expected incoming requests
+ #
+ # Requests to path /accounts/ClientLogin are assumed to be for signing in
+ #
+ # Other paths are for retrieving a local file. An .auth appended
+ # to a file path will require authentication (meaning the Authorization
+ # header must be present with the value "GoogleLogin auth=GoodAuthToken".)
+ # If the token is present, the file (without uthe .auth at the end) will
+ # be returned.
+ #
+ # HTTP Delete commands succeed but return no data.
+ #
+ # GData override headers (putting the verb in X-HTTP-Method-Override)
+ # are supported.
+ #
+ # Any auth password is valid except "bad", which will fail, and "captcha",
+ # which will fail unless the authentication request's post string includes
+ # "logintoken=CapToken&logincaptcha=good"
+
+ # We will use a readable default result string since it should never show up
+ # in output
+ resultString = "default GTMHTTPFetcherTestServer result\n";
+ resultStatus = 0
+ headerType = "text/plain"
+ postString = ""
+ modifiedDate = "thursday" # clients should treat dates as opaque, generally
+
+ # auth queries and some others may include post data
+ postLength = int(self.headers.getheader("Content-Length", "0"));
+ if postLength > 0:
+ postString = self.rfile.read(postLength)
+
+ # auth queries and some GData queries include post data
+ ifModifiedSince = self.headers.getheader("If-Modified-Since", "");
+
+ # retrieve the auth header; require it if the file path ends
+ # with the string ".auth"
+ authorization = self.headers.getheader("Authorization", "")
+ if self.path.endswith(".auth"):
+ if authorization != "GoogleLogin auth=GoodAuthToken":
+ self.send_error(401,"Unauthorized: %s" % self.path)
+ return
+ self.path = self.path[:-5] # remove the .auth at the end
+
+ overrideHeader = self.headers.getheader("X-HTTP-Method-Override", "")
+ httpCommand = self.command
+ if httpCommand == "POST" and len(overrideHeader) > 0:
+ httpCommand = overrideHeader
+
+ try:
+ if self.path.endswith("/accounts/ClientLogin"):
+ #
+ # it's a sign-in attempt; it's good unless the password is "bad" or
+ # "captcha"
+ #
+
+ # use regular expression to find the password
+ password = ""
+ searchResult = re.search("(Passwd=)([^&\n]*)", postString)
+ if searchResult:
+ password = searchResult.group(2)
+
+ if password == "bad":
+ resultString = "Error=BadAuthentication\n"
+ resultStatus = 403
+
+ elif password == "captcha":
+ logintoken = ""
+ logincaptcha = ""
+
+ # use regular expressions to find the captcha token and answer
+ searchResult = re.search("(logintoken=)([^&\n]*)", postString);
+ if searchResult:
+ logintoken = searchResult.group(2)
+
+ searchResult = re.search("(logincaptcha=)([^&\n]*)", postString);
+ if searchResult:
+ logincaptcha = searchResult.group(2)
+
+ # if the captcha token is "CapToken" and the answer is "good"
+ # then it's a valid sign in
+ if (logintoken == "CapToken") and (logincaptcha == "good"):
+ resultString = "SID=GoodSID\nLSID=GoodLSID\nAuth=GoodAuthToken\n"
+ resultStatus = 200
+ else:
+ # incorrect captcha token or answer provided
+ resultString = ("Error=CaptchaRequired\nCaptchaToken=CapToken"
+ "\nCaptchaUrl=CapUrl\n")
+ resultStatus = 403
+
+ else:
+ # valid username/password
+ resultString = "SID=GoodSID\nLSID=GoodLSID\nAuth=GoodAuthToken\n"
+ resultStatus = 200
+
+ elif httpCommand == "DELETE":
+ #
+ # it's an object delete; read and return empty data
+ #
+ resultString = ""
+ resultStatus = 200
+ headerType = "text/plain"
+
+ else:
+ # queries that have something like "?status=456" should fail with the
+ # status code
+ searchResult = re.search("(status=)([0-9]+)", self.path)
+ if searchResult:
+ status = searchResult.group(2)
+ self.send_error(int(status),
+ "Test HTTP server status parameter: %s" % self.path)
+ return
+
+ # if the client gave us back our not modified date, then say there's no
+ # change in the response
+ if ifModifiedSince == modifiedDate:
+ self.send_response(304) # Not Modified
+ return
+
+ else:
+ #
+ # it's a file fetch; read and return the data file
+ #
+ f = open("." + self.path)
+ resultString = f.read()
+ f.close()
+ resultStatus = 200
+ fileTypeInfo = mimetypes.guess_type("." + self.path)
+ headerType = fileTypeInfo[0] # first part of the tuple is mime type
+
+ self.send_response(resultStatus)
+ self.send_header("Content-type", headerType)
+ self.send_header("Last-Modified", modifiedDate)
+
+ cookieValue = os.path.basename("." + self.path)
+ self.send_header('Set-Cookie', 'TestCookie=%s' % cookieValue)
+ self.end_headers()
+ self.wfile.write(resultString)
+
+ except IOError:
+ self.send_error(404,"File Not Found: %s" % self.path)
+
+
+def main():
+ try:
+ parser = OptionParser()
+ parser.add_option("-p", "--port", dest="port", help="Port to run server on",
+ type="int", default="80")
+ parser.add_option("-r", "--root", dest="root", help="Where to root server",
+ default=".")
+ (options, args) = parser.parse_args()
+ os.chdir(options.root)
+ server = TimeoutServer(("127.0.0.1", options.port), SimpleServer)
+ sys.stdout.write("started GTMHTTPFetcherTestServer,"
+ " serving files from root directory %s..." % os.getcwd());
+ sys.stdout.flush();
+ server.serve_forever()
+ except KeyboardInterrupt:
+ print "^C received, shutting down server"
+ server.socket.close()
+ except ServerTimeoutException:
+ print "Too long since the last request, shutting down server"
+ server.socket.close()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/GTM.xcodeproj/project.pbxproj b/GTM.xcodeproj/project.pbxproj
index 21685f0..d279b99 100644
--- a/GTM.xcodeproj/project.pbxproj
+++ b/GTM.xcodeproj/project.pbxproj
@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
- objectVersion = 42;
+ objectVersion = 44;
objects = {
/* Begin PBXAggregateTarget section */
@@ -24,18 +24,15 @@
/* Begin PBXBuildFile section */
8B2A9B200D8270DA00599386 /* GTMNSWorkspace+ScreenSaver.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B2A9B1D0D8270DA00599386 /* GTMNSWorkspace+ScreenSaver.m */; };
- 8B2A9B220D8270DA00599386 /* GTMNSWorkspace+ScreenSaver.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B2A9B1F0D8270DA00599386 /* GTMNSWorkspace+ScreenSaver.h */; };
- 8B2A9B230D8270DA00599386 /* GTMNSWorkspace+ScreenSaver.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B2A9B1D0D8270DA00599386 /* GTMNSWorkspace+ScreenSaver.m */; };
+ 8B2A9B220D8270DA00599386 /* GTMNSWorkspace+ScreenSaver.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B2A9B1F0D8270DA00599386 /* GTMNSWorkspace+ScreenSaver.h */; settings = {ATTRIBUTES = (Public, ); }; };
8B2A9B240D8270DA00599386 /* GTMNSWorkspace+ScreenSaverTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B2A9B1E0D8270DA00599386 /* GTMNSWorkspace+ScreenSaverTest.m */; };
8B2A9BEC0D82714A00599386 /* ScreenSaver.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B2A9BEB0D82714A00599386 /* ScreenSaver.framework */; };
- 8B2A9BED0D82714A00599386 /* ScreenSaver.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B2A9BEB0D82714A00599386 /* ScreenSaver.framework */; };
8B45A03A0DA46A2A001148C5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; };
8B45A0B80DA46A2F001148C5 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F42E089B0D199B1800D5DDE0 /* SenTestingKit.framework */; };
8B45A0D50DA46A57001148C5 /* GTMNSObject+UnitTesting.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE29C0D198D36009257D2 /* GTMNSObject+UnitTesting.m */; };
8B45A0D60DA46A57001148C5 /* GTMNSObject+BindingUnitTesting.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B1A14E90D900BC800CA1E8E /* GTMNSObject+BindingUnitTesting.m */; };
8B45A19A0DA46AAA001148C5 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B45A1990DA46AAA001148C5 /* QuartzCore.framework */; };
8B45A2040DA46DF6001148C5 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; };
- 8B45A2070DA46DFC001148C5 /* ScreenSaver.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B2A9BEB0D82714A00599386 /* ScreenSaver.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 */; };
@@ -59,23 +56,24 @@
8B7AD4AE0DABBFEE00B84F4A /* GTMUnitTestingBindingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B7AD4AD0DABBFEE00B84F4A /* GTMUnitTestingBindingTest.m */; };
8BC045C20DAE899100C2D1CA /* GTMGeometryUtilsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE2800D198D0E009257D2 /* GTMGeometryUtilsTest.m */; };
8BC046B90DAE8C4B00C2D1CA /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BC046B80DAE8C4B00C2D1CA /* ApplicationServices.framework */; };
- 8BC04CD30DB003BE00C2D1CA /* GTMNSString+Utilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 8BC04CD10DB003BE00C2D1CA /* GTMNSString+Utilities.h */; };
- 8BC04CD40DB003BE00C2D1CA /* GTMNSString+Utilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC04CD20DB003BE00C2D1CA /* GTMNSString+Utilities.m */; };
- 8BC04CD60DB003C300C2D1CA /* GTMNSString+UtilitiesTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC04CD50DB003C300C2D1CA /* GTMNSString+UtilitiesTest.m */; };
8BC04CD70DB003CD00C2D1CA /* GTMMethodCheck.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F31EF0DA347720052CA40 /* GTMMethodCheck.m */; };
- 8BC04CD80DB003D800C2D1CA /* GTMMethodCheck.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B6F31F40DA3489B0052CA40 /* GTMMethodCheck.h */; };
+ 8BC04CD80DB003D800C2D1CA /* GTMMethodCheck.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B6F31F40DA3489B0052CA40 /* GTMMethodCheck.h */; settings = {ATTRIBUTES = (Public, ); }; };
8BC04CDD0DB004A000C2D1CA /* GTMMethodCheck.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F31EF0DA347720052CA40 /* GTMMethodCheck.m */; };
8BC04CDE0DB004A000C2D1CA /* GTMMethodCheck.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F31EF0DA347720052CA40 /* GTMMethodCheck.m */; };
8BC04CDF0DB004A100C2D1CA /* GTMMethodCheck.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F31EF0DA347720052CA40 /* GTMMethodCheck.m */; };
+ 8BE869730DBE89C100749827 /* GTMNSBezierPath+RoundRectTest.x86_64.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 8BE869720DBE89C100749827 /* GTMNSBezierPath+RoundRectTest.x86_64.tiff */; };
8BEEA90D0DA7446300894774 /* GTMUnitTestingImage.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 8BEEA90A0DA7446300894774 /* GTMUnitTestingImage.tiff */; };
8BEEA90E0DA7446300894774 /* GTMUnitTestingWindow.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 8BEEA90B0DA7446300894774 /* GTMUnitTestingWindow.tiff */; };
8BEEA90F0DA7446300894774 /* GTMUnitTestingView.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 8BEEA90C0DA7446300894774 /* GTMUnitTestingView.tiff */; };
- F413908F0D75F63C00F72B31 /* GTMNSFileManager+Path.h in Headers */ = {isa = PBXBuildFile; fileRef = F413908C0D75F63C00F72B31 /* GTMNSFileManager+Path.h */; };
+ F413908F0D75F63C00F72B31 /* GTMNSFileManager+Path.h in Headers */ = {isa = PBXBuildFile; fileRef = F413908C0D75F63C00F72B31 /* GTMNSFileManager+Path.h */; settings = {ATTRIBUTES = (Public, ); }; };
F41390900D75F63C00F72B31 /* GTMNSFileManager+Path.m in Sources */ = {isa = PBXBuildFile; fileRef = F413908D0D75F63C00F72B31 /* GTMNSFileManager+Path.m */; };
F41390920D75F64D00F72B31 /* GTMNSFileManager+PathTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F413908E0D75F63C00F72B31 /* GTMNSFileManager+PathTest.m */; };
+ F41D258B0DBD21A300774EEB /* GTMBase64.h in Headers */ = {isa = PBXBuildFile; fileRef = F41D25880DBD21A300774EEB /* GTMBase64.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ F41D258C0DBD21A300774EEB /* GTMBase64.m in Sources */ = {isa = PBXBuildFile; fileRef = F41D25890DBD21A300774EEB /* GTMBase64.m */; };
+ F41D258F0DBD21B900774EEB /* GTMBase64Test.m in Sources */ = {isa = PBXBuildFile; fileRef = F41D258A0DBD21A300774EEB /* GTMBase64Test.m */; };
F424F7010D9AA02B000B87EF /* GTMNSData+zlibTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F43E4E600D4E5EC90041161F /* GTMNSData+zlibTest.m */; };
- F424F75F0D9AF019000B87EF /* GTMDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B1A16050D90344B00CA1E8E /* GTMDefines.h */; };
- F428FF030D48E55E00382ED1 /* GTMNSBezierPath+CGPath.h in Headers */ = {isa = PBXBuildFile; fileRef = F428FEFF0D48E55E00382ED1 /* GTMNSBezierPath+CGPath.h */; };
+ F424F75F0D9AF019000B87EF /* GTMDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B1A16050D90344B00CA1E8E /* GTMDefines.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 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 */; };
F42E082F0D19991400D5DDE0 /* GTMNSBezierPath+RoundRectTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE2830D198D0E009257D2 /* GTMNSBezierPath+RoundRectTest.m */; };
@@ -88,56 +86,60 @@
F42E089C0D199B1800D5DDE0 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F42E089B0D199B1800D5DDE0 /* SenTestingKit.framework */; };
F42E089D0D199B1800D5DDE0 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F42E089B0D199B1800D5DDE0 /* SenTestingKit.framework */; };
F42E09450D199BA400D5DDE0 /* GTMNSObject+UnitTesting.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE29C0D198D36009257D2 /* GTMNSObject+UnitTesting.m */; };
- F42E09490D199BBF00D5DDE0 /* GTMDelegatingTableColumn.h in Headers */ = {isa = PBXBuildFile; fileRef = F48FE27C0D198D0E009257D2 /* GTMDelegatingTableColumn.h */; };
+ F42E09490D199BBF00D5DDE0 /* GTMDelegatingTableColumn.h in Headers */ = {isa = PBXBuildFile; fileRef = F48FE27C0D198D0E009257D2 /* GTMDelegatingTableColumn.h */; settings = {ATTRIBUTES = (Public, ); }; };
F42E094A0D199BBF00D5DDE0 /* GTMDelegatingTableColumn.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE27D0D198D0E009257D2 /* GTMDelegatingTableColumn.m */; };
- F42E094B0D199BBF00D5DDE0 /* GTMGarbageCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = F48FE28D0D198D24009257D2 /* GTMGarbageCollection.h */; };
- F42E094C0D199BBF00D5DDE0 /* GTMGeometryUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = F48FE27E0D198D0E009257D2 /* GTMGeometryUtils.h */; };
+ F42E094B0D199BBF00D5DDE0 /* GTMGarbageCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = F48FE28D0D198D24009257D2 /* GTMGarbageCollection.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ F42E094C0D199BBF00D5DDE0 /* GTMGeometryUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = F48FE27E0D198D0E009257D2 /* GTMGeometryUtils.h */; settings = {ATTRIBUTES = (Public, ); }; };
F42E094D0D199BBF00D5DDE0 /* GTMGeometryUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE27F0D198D0E009257D2 /* GTMGeometryUtils.m */; };
- F42E094F0D199BBF00D5DDE0 /* GTMNSBezierPath+RoundRect.h in Headers */ = {isa = PBXBuildFile; fileRef = F48FE2810D198D0E009257D2 /* GTMNSBezierPath+RoundRect.h */; };
+ F42E094F0D199BBF00D5DDE0 /* GTMNSBezierPath+RoundRect.h in Headers */ = {isa = PBXBuildFile; fileRef = F48FE2810D198D0E009257D2 /* GTMNSBezierPath+RoundRect.h */; settings = {ATTRIBUTES = (Public, ); }; };
F42E09500D199BBF00D5DDE0 /* GTMNSBezierPath+RoundRect.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE2820D198D0E009257D2 /* GTMNSBezierPath+RoundRect.m */; };
- F42E09510D199BBF00D5DDE0 /* GTMNSString+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = F48FE28E0D198D24009257D2 /* GTMNSString+HTML.h */; };
+ F42E09510D199BBF00D5DDE0 /* GTMNSString+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = F48FE28E0D198D24009257D2 /* GTMNSString+HTML.h */; settings = {ATTRIBUTES = (Public, ); }; };
F42E09520D199BBF00D5DDE0 /* GTMNSString+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE28F0D198D24009257D2 /* GTMNSString+HTML.m */; };
- F42E09530D199BBF00D5DDE0 /* GTMObjectSingleton.h in Headers */ = {isa = PBXBuildFile; fileRef = F48FE2910D198D24009257D2 /* GTMObjectSingleton.h */; };
- F42E09540D199BBF00D5DDE0 /* GTMSystemVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = F48FE2920D198D24009257D2 /* GTMSystemVersion.h */; };
+ F42E09530D199BBF00D5DDE0 /* GTMObjectSingleton.h in Headers */ = {isa = PBXBuildFile; fileRef = F48FE2910D198D24009257D2 /* GTMObjectSingleton.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ F42E09540D199BBF00D5DDE0 /* GTMSystemVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = F48FE2920D198D24009257D2 /* GTMSystemVersion.h */; settings = {ATTRIBUTES = (Public, ); }; };
F42E09550D199BBF00D5DDE0 /* GTMSystemVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE2930D198D24009257D2 /* GTMSystemVersion.m */; };
F42E095E0D199BD600D5DDE0 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; };
- F42E09AA0D19A5E300D5DDE0 /* GTMNSWorkspace+Theme.h in Headers */ = {isa = PBXBuildFile; fileRef = F42E09A80D19A5E300D5DDE0 /* GTMNSWorkspace+Theme.h */; };
- F42E09AB0D19A5E300D5DDE0 /* GTMNSWorkspace+Theme.m in Sources */ = {isa = PBXBuildFile; fileRef = F42E09A90D19A5E300D5DDE0 /* GTMNSWorkspace+Theme.m */; };
F42E09AE0D19A62F00D5DDE0 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F42E09AD0D19A62F00D5DDE0 /* Carbon.framework */; };
- F42E0B0A0D19A6FB00D5DDE0 /* GTMNSWorkspace+ThemeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F42E0B090D19A6FB00D5DDE0 /* GTMNSWorkspace+ThemeTest.m */; };
- F437F55D0D50BC0A00F5C3A4 /* GTMRegex.h in Headers */ = {isa = PBXBuildFile; fileRef = F437F55A0D50BC0A00F5C3A4 /* GTMRegex.h */; };
+ F435DE7C0DC0B6580069CDE8 /* GTMNSBezierPath+CGPathTest.ppc64.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F435DE7A0DC0B6580069CDE8 /* GTMNSBezierPath+CGPathTest.ppc64.tiff */; };
+ F435DE7D0DC0B6580069CDE8 /* GTMNSBezierPath+CGPathTest.x86_64.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F435DE7B0DC0B6580069CDE8 /* GTMNSBezierPath+CGPathTest.x86_64.tiff */; };
+ F435DE8B0DC0B7620069CDE8 /* GTMNSBezierPath+RoundRectTest.ppc64.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F435DE8A0DC0B7620069CDE8 /* GTMNSBezierPath+RoundRectTest.ppc64.tiff */; };
+ F435E0890DC63F6D0069CDE8 /* GTMHTTPFetcher.h in Headers */ = {isa = PBXBuildFile; fileRef = F435E0870DC63F6D0069CDE8 /* GTMHTTPFetcher.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ F435E08A0DC63F6D0069CDE8 /* GTMHTTPFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = F435E0880DC63F6D0069CDE8 /* GTMHTTPFetcher.m */; };
+ F435E27F0DC7B0630069CDE8 /* GTMProgressMonitorInputStream.h in Headers */ = {isa = PBXBuildFile; fileRef = F435E27D0DC7B0630069CDE8 /* GTMProgressMonitorInputStream.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ F435E2800DC7B0630069CDE8 /* GTMProgressMonitorInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F435E27E0DC7B0630069CDE8 /* GTMProgressMonitorInputStream.m */; };
+ F435E3940DC8CAAF0069CDE8 /* GTMHTTPFetcherTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F435E3930DC8CAAF0069CDE8 /* GTMHTTPFetcherTest.m */; };
+ F435E3BB0DC8D1980069CDE8 /* GTMHTTPFetcherTestPage.html in Resources */ = {isa = PBXBuildFile; fileRef = F435E3B90DC8D1720069CDE8 /* GTMHTTPFetcherTestPage.html */; };
+ F435E3BC0DC8D1980069CDE8 /* GTMHTTPFetcherTestServer in Resources */ = {isa = PBXBuildFile; fileRef = F435E3BA0DC8D1720069CDE8 /* GTMHTTPFetcherTestServer */; };
+ F437F55D0D50BC0A00F5C3A4 /* GTMRegex.h in Headers */ = {isa = PBXBuildFile; fileRef = F437F55A0D50BC0A00F5C3A4 /* GTMRegex.h */; settings = {ATTRIBUTES = (Public, ); }; };
F437F55E0D50BC0A00F5C3A4 /* GTMRegex.m in Sources */ = {isa = PBXBuildFile; fileRef = F437F55B0D50BC0A00F5C3A4 /* GTMRegex.m */; };
F437F5620D50BC1D00F5C3A4 /* GTMRegexTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F437F55C0D50BC0A00F5C3A4 /* GTMRegexTest.m */; };
- F43DCDCD0D4796C600959A62 /* GTMLoginItems.h in Headers */ = {isa = PBXBuildFile; fileRef = F43DCDCB0D4796C600959A62 /* GTMLoginItems.h */; };
+ F43DCDCD0D4796C600959A62 /* GTMLoginItems.h in Headers */ = {isa = PBXBuildFile; fileRef = F43DCDCB0D4796C600959A62 /* GTMLoginItems.h */; settings = {ATTRIBUTES = (Public, ); }; };
F43DCDCE0D4796C600959A62 /* GTMLoginItems.m in Sources */ = {isa = PBXBuildFile; fileRef = F43DCDCC0D4796C600959A62 /* GTMLoginItems.m */; };
F43DCEC70D47BEA000959A62 /* GTMLoginItemsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F43DCEC60D47BEA000959A62 /* GTMLoginItemsTest.m */; };
- F43E447A0D4918B20041161F /* GTMLinearRGBShading.h in Headers */ = {isa = PBXBuildFile; fileRef = F43E44770D4918B20041161F /* GTMLinearRGBShading.h */; };
+ F43E447A0D4918B20041161F /* GTMLinearRGBShading.h in Headers */ = {isa = PBXBuildFile; fileRef = F43E44770D4918B20041161F /* GTMLinearRGBShading.h */; settings = {ATTRIBUTES = (Public, ); }; };
F43E447B0D4918B20041161F /* GTMLinearRGBShading.m in Sources */ = {isa = PBXBuildFile; fileRef = F43E44780D4918B20041161F /* GTMLinearRGBShading.m */; };
F43E447F0D4918BC0041161F /* GTMLinearRGBShadingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F43E44790D4918B20041161F /* GTMLinearRGBShadingTest.m */; };
- F43E4C280D4E361D0041161F /* GTMNSString+XML.h in Headers */ = {isa = PBXBuildFile; fileRef = F43E4C250D4E361D0041161F /* GTMNSString+XML.h */; };
+ F43E4C280D4E361D0041161F /* GTMNSString+XML.h in Headers */ = {isa = PBXBuildFile; fileRef = F43E4C250D4E361D0041161F /* GTMNSString+XML.h */; settings = {ATTRIBUTES = (Public, ); }; };
F43E4C290D4E361D0041161F /* GTMNSString+XML.m in Sources */ = {isa = PBXBuildFile; fileRef = F43E4C260D4E361D0041161F /* GTMNSString+XML.m */; };
F43E4C2D0D4E36230041161F /* GTMNSString+XMLTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F43E4C270D4E361D0041161F /* GTMNSString+XMLTest.m */; };
- F43E4DD90D4E56320041161F /* GTMNSEnumerator+Filter.h in Headers */ = {isa = PBXBuildFile; fileRef = F43E4DD60D4E56320041161F /* GTMNSEnumerator+Filter.h */; };
+ F43E4DD90D4E56320041161F /* GTMNSEnumerator+Filter.h in Headers */ = {isa = PBXBuildFile; fileRef = F43E4DD60D4E56320041161F /* GTMNSEnumerator+Filter.h */; settings = {ATTRIBUTES = (Public, ); }; };
F43E4DDA0D4E56320041161F /* GTMNSEnumerator+Filter.m in Sources */ = {isa = PBXBuildFile; fileRef = F43E4DD70D4E56320041161F /* GTMNSEnumerator+Filter.m */; };
F43E4DDE0D4E56380041161F /* GTMNSEnumerator+FilterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F43E4DD80D4E56320041161F /* GTMNSEnumerator+FilterTest.m */; };
- F43E4E610D4E5EC90041161F /* GTMNSData+zlib.h in Headers */ = {isa = PBXBuildFile; fileRef = F43E4E5E0D4E5EC90041161F /* GTMNSData+zlib.h */; };
+ F43E4E610D4E5EC90041161F /* GTMNSData+zlib.h in Headers */ = {isa = PBXBuildFile; fileRef = F43E4E5E0D4E5EC90041161F /* GTMNSData+zlib.h */; settings = {ATTRIBUTES = (Public, ); }; };
F43E4E620D4E5EC90041161F /* GTMNSData+zlib.m in Sources */ = {isa = PBXBuildFile; fileRef = F43E4E5F0D4E5EC90041161F /* GTMNSData+zlib.m */; };
F43E4F6D0D4E60C50041161F /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F43E4F6C0D4E60C50041161F /* libz.dylib */; };
- F47A79880D746EE9002302AB /* GTMScriptRunner.h in Headers */ = {isa = PBXBuildFile; fileRef = F47A79850D746EE9002302AB /* GTMScriptRunner.h */; };
+ F47A79880D746EE9002302AB /* GTMScriptRunner.h in Headers */ = {isa = PBXBuildFile; fileRef = F47A79850D746EE9002302AB /* GTMScriptRunner.h */; settings = {ATTRIBUTES = (Public, ); }; };
F47A79890D746EE9002302AB /* GTMScriptRunner.m in Sources */ = {isa = PBXBuildFile; fileRef = F47A79860D746EE9002302AB /* GTMScriptRunner.m */; };
F47A798B0D746EFC002302AB /* GTMScriptRunnerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F47A79870D746EE9002302AB /* GTMScriptRunnerTest.m */; };
- F47F1C120D490BC000925B8F /* GTMNSBezierPath+Shading.h in Headers */ = {isa = PBXBuildFile; fileRef = F47F1C0D0D490BC000925B8F /* GTMNSBezierPath+Shading.h */; };
+ F47F1C120D490BC000925B8F /* GTMNSBezierPath+Shading.h in Headers */ = {isa = PBXBuildFile; fileRef = F47F1C0D0D490BC000925B8F /* GTMNSBezierPath+Shading.h */; settings = {ATTRIBUTES = (Public, ); }; };
F47F1C130D490BC000925B8F /* GTMNSBezierPath+Shading.m in Sources */ = {isa = PBXBuildFile; fileRef = F47F1C0E0D490BC000925B8F /* GTMNSBezierPath+Shading.m */; };
F47F1C1B0D490BD200925B8F /* GTMNSBezierPath+ShadingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F47F1C110D490BC000925B8F /* GTMNSBezierPath+ShadingTest.m */; };
- F47F1C750D490E5C00925B8F /* GTMShading.h in Headers */ = {isa = PBXBuildFile; fileRef = F47F1C740D490E5C00925B8F /* GTMShading.h */; };
- F47F1CAF0D4910FD00925B8F /* GTMNSColor+Theme.h in Headers */ = {isa = PBXBuildFile; fileRef = F47F1CAC0D4910FD00925B8F /* GTMNSColor+Theme.h */; };
- F47F1CB00D4910FD00925B8F /* GTMNSColor+Theme.m in Sources */ = {isa = PBXBuildFile; fileRef = F47F1CAD0D4910FD00925B8F /* GTMNSColor+Theme.m */; };
- F47F1CB60D49110900925B8F /* GTMNSColor+ThemeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F47F1CAE0D4910FD00925B8F /* GTMNSColor+ThemeTest.m */; };
- F47F1D300D4914AD00925B8F /* GTMCalculatedRange.h in Headers */ = {isa = PBXBuildFile; fileRef = F47F1D2D0D4914AD00925B8F /* GTMCalculatedRange.h */; };
+ F47F1C750D490E5C00925B8F /* GTMShading.h in Headers */ = {isa = PBXBuildFile; fileRef = F47F1C740D490E5C00925B8F /* GTMShading.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ F47F1D300D4914AD00925B8F /* GTMCalculatedRange.h in Headers */ = {isa = PBXBuildFile; fileRef = F47F1D2D0D4914AD00925B8F /* GTMCalculatedRange.h */; settings = {ATTRIBUTES = (Public, ); }; };
F47F1D310D4914AD00925B8F /* GTMCalculatedRange.m in Sources */ = {isa = PBXBuildFile; fileRef = F47F1D2E0D4914AD00925B8F /* GTMCalculatedRange.m */; };
F47F1D350D4914B600925B8F /* GTMCalculatedRangeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F47F1D2F0D4914AD00925B8F /* GTMCalculatedRangeTest.m */; };
- F4CA854F0DAFAAF600B4AB10 /* GTMObjC2Runtime.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B6F32060DA34A1B0052CA40 /* GTMObjC2Runtime.h */; };
- F4FF22780D9D4835003880AC /* GTMDebugSelectorValidation.h in Headers */ = {isa = PBXBuildFile; fileRef = F4FF22770D9D4835003880AC /* GTMDebugSelectorValidation.h */; };
+ F4CA854F0DAFAAF600B4AB10 /* GTMObjC2Runtime.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B6F32060DA34A1B0052CA40 /* GTMObjC2Runtime.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ F4FF22780D9D4835003880AC /* GTMDebugSelectorValidation.h in Headers */ = {isa = PBXBuildFile; fileRef = F4FF22770D9D4835003880AC /* GTMDebugSelectorValidation.h */; settings = {ATTRIBUTES = (Public, ); }; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -155,6 +157,20 @@
remoteGlobalIDString = 8B45A2890DA49B99001148C5;
remoteInfo = UIUnitTestingHarness;
};
+ F41D254E0DB9067C00774EEB /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 8B45A0270DA4696C001148C5;
+ remoteInfo = "UnitTest - UnitTesting";
+ };
+ F41D25510DB9068700774EEB /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 8B45A0270DA4696C001148C5;
+ remoteInfo = "UnitTest - UnitTesting";
+ };
F42E08760D199A9B00D5DDE0 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
@@ -226,27 +242,35 @@
8B7AD4990DABBB5800B84F4A /* GTMNSBezierPath+RoundRectTest.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMNSBezierPath+RoundRectTest.tiff"; sourceTree = "<group>"; };
8B7AD4AD0DABBFEE00B84F4A /* GTMUnitTestingBindingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMUnitTestingBindingTest.m; sourceTree = "<group>"; };
8BC046B80DAE8C4B00C2D1CA /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = /System/Library/Frameworks/ApplicationServices.framework; sourceTree = "<absolute>"; };
- 8BC04CD10DB003BE00C2D1CA /* GTMNSString+Utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSString+Utilities.h"; sourceTree = "<group>"; };
- 8BC04CD20DB003BE00C2D1CA /* GTMNSString+Utilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+Utilities.m"; sourceTree = "<group>"; };
- 8BC04CD50DB003C300C2D1CA /* GTMNSString+UtilitiesTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+UtilitiesTest.m"; sourceTree = "<group>"; };
8BC04D140DB0061300C2D1CA /* RunMacOSUnitTests.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = RunMacOSUnitTests.sh; sourceTree = "<group>"; };
+ 8BE869720DBE89C100749827 /* GTMNSBezierPath+RoundRectTest.x86_64.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMNSBezierPath+RoundRectTest.x86_64.tiff"; sourceTree = "<group>"; };
8BEEA90A0DA7446300894774 /* GTMUnitTestingImage.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = GTMUnitTestingImage.tiff; sourceTree = "<group>"; };
8BEEA90B0DA7446300894774 /* GTMUnitTestingWindow.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = GTMUnitTestingWindow.tiff; sourceTree = "<group>"; };
8BEEA90C0DA7446300894774 /* GTMUnitTestingView.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = GTMUnitTestingView.tiff; sourceTree = "<group>"; };
F413908C0D75F63C00F72B31 /* GTMNSFileManager+Path.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSFileManager+Path.h"; sourceTree = "<group>"; };
F413908D0D75F63C00F72B31 /* GTMNSFileManager+Path.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSFileManager+Path.m"; sourceTree = "<group>"; };
F413908E0D75F63C00F72B31 /* GTMNSFileManager+PathTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSFileManager+PathTest.m"; sourceTree = "<group>"; };
+ F41D25880DBD21A300774EEB /* GTMBase64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMBase64.h; sourceTree = "<group>"; };
+ F41D25890DBD21A300774EEB /* GTMBase64.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMBase64.m; sourceTree = "<group>"; };
+ F41D258A0DBD21A300774EEB /* GTMBase64Test.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMBase64Test.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>"; };
F42E08210D19987200D5DDE0 /* UnitTest - Foundation.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "UnitTest - Foundation.octest"; sourceTree = BUILT_PRODUCTS_DIR; };
F42E086D0D199A5B00D5DDE0 /* GoogleToolboxForMac.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GoogleToolboxForMac.framework; sourceTree = BUILT_PRODUCTS_DIR; };
F42E086E0D199A5B00D5DDE0 /* GTM-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = "GTM-Info.plist"; sourceTree = "<group>"; };
- F42E089B0D199B1800D5DDE0 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = /System/Library/Frameworks/SenTestingKit.framework; sourceTree = "<absolute>"; };
- F42E09A80D19A5E300D5DDE0 /* GTMNSWorkspace+Theme.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSWorkspace+Theme.h"; sourceTree = "<group>"; };
- F42E09A90D19A5E300D5DDE0 /* GTMNSWorkspace+Theme.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSWorkspace+Theme.m"; 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>"; };
- F42E0B090D19A6FB00D5DDE0 /* GTMNSWorkspace+ThemeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSWorkspace+ThemeTest.m"; sourceTree = "<group>"; };
+ F435DE7A0DC0B6580069CDE8 /* GTMNSBezierPath+CGPathTest.ppc64.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMNSBezierPath+CGPathTest.ppc64.tiff"; sourceTree = "<group>"; };
+ F435DE7B0DC0B6580069CDE8 /* GTMNSBezierPath+CGPathTest.x86_64.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMNSBezierPath+CGPathTest.x86_64.tiff"; sourceTree = "<group>"; };
+ F435DE8A0DC0B7620069CDE8 /* GTMNSBezierPath+RoundRectTest.ppc64.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMNSBezierPath+RoundRectTest.ppc64.tiff"; sourceTree = "<group>"; };
+ F435E0870DC63F6D0069CDE8 /* GTMHTTPFetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMHTTPFetcher.h; sourceTree = "<group>"; };
+ F435E0880DC63F6D0069CDE8 /* GTMHTTPFetcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMHTTPFetcher.m; sourceTree = "<group>"; };
+ F435E27D0DC7B0630069CDE8 /* GTMProgressMonitorInputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMProgressMonitorInputStream.h; sourceTree = "<group>"; };
+ F435E27E0DC7B0630069CDE8 /* GTMProgressMonitorInputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMProgressMonitorInputStream.m; sourceTree = "<group>"; };
+ F435E3930DC8CAAF0069CDE8 /* GTMHTTPFetcherTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMHTTPFetcherTest.m; sourceTree = "<group>"; };
+ F435E3B90DC8D1720069CDE8 /* GTMHTTPFetcherTestPage.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = GTMHTTPFetcherTestPage.html; sourceTree = "<group>"; };
+ F435E3BA0DC8D1720069CDE8 /* GTMHTTPFetcherTestServer */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = GTMHTTPFetcherTestServer; sourceTree = "<group>"; };
F437F55A0D50BC0A00F5C3A4 /* GTMRegex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMRegex.h; sourceTree = "<group>"; };
F437F55B0D50BC0A00F5C3A4 /* GTMRegex.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMRegex.m; sourceTree = "<group>"; };
F437F55C0D50BC0A00F5C3A4 /* GTMRegexTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMRegexTest.m; sourceTree = "<group>"; };
@@ -273,9 +297,6 @@
F47F1C0E0D490BC000925B8F /* GTMNSBezierPath+Shading.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSBezierPath+Shading.m"; sourceTree = "<group>"; };
F47F1C110D490BC000925B8F /* GTMNSBezierPath+ShadingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSBezierPath+ShadingTest.m"; sourceTree = "<group>"; };
F47F1C740D490E5C00925B8F /* GTMShading.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMShading.h; sourceTree = "<group>"; };
- F47F1CAC0D4910FD00925B8F /* GTMNSColor+Theme.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSColor+Theme.h"; sourceTree = "<group>"; };
- F47F1CAD0D4910FD00925B8F /* GTMNSColor+Theme.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSColor+Theme.m"; sourceTree = "<group>"; };
- F47F1CAE0D4910FD00925B8F /* GTMNSColor+ThemeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSColor+ThemeTest.m"; sourceTree = "<group>"; };
F47F1D2D0D4914AD00925B8F /* GTMCalculatedRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMCalculatedRange.h; sourceTree = "<group>"; };
F47F1D2E0D4914AD00925B8F /* GTMCalculatedRange.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMCalculatedRange.m; sourceTree = "<group>"; };
F47F1D2F0D4914AD00925B8F /* GTMCalculatedRangeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMCalculatedRangeTest.m; sourceTree = "<group>"; };
@@ -310,6 +331,11 @@
F48FE2E10D198E4C009257D2 /* GTMSystemVersionTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMSystemVersionTest.m; sourceTree = "<group>"; };
F4C978090D5B79C7001C29A6 /* ReleaseNotes.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ReleaseNotes.txt; sourceTree = "<group>"; };
F4CA854E0DAFAAB600B4AB10 /* xcconfigs-readme.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "xcconfigs-readme.txt"; sourceTree = "<group>"; };
+ F4CA864A0DB3ACB200B4AB10 /* DebugLeopardOrLater.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = DebugLeopardOrLater.xcconfig; sourceTree = "<group>"; };
+ F4CA864B0DB3ACB200B4AB10 /* ReleaseLeopardOrLater.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ReleaseLeopardOrLater.xcconfig; sourceTree = "<group>"; };
+ F4CA864C0DB3ACD200B4AB10 /* LoadableBundleGCSupported.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = LoadableBundleGCSupported.xcconfig; sourceTree = "<group>"; };
+ F4CA864D0DB3ACD200B4AB10 /* SharedLibraryGCSupported.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = SharedLibraryGCSupported.xcconfig; sourceTree = "<group>"; };
+ F4CA864E0DB3ACD200B4AB10 /* StaticLibraryGCSupported.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = StaticLibraryGCSupported.xcconfig; sourceTree = "<group>"; };
F4FF22770D9D4835003880AC /* GTMDebugSelectorValidation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMDebugSelectorValidation.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -318,7 +344,6 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 8B45A2070DA46DFC001148C5 /* ScreenSaver.framework in Frameworks */,
8B45A2040DA46DF6001148C5 /* Cocoa.framework in Frameworks */,
8B45A0B80DA46A2F001148C5 /* SenTestingKit.framework in Frameworks */,
8B45A03A0DA46A2A001148C5 /* Foundation.framework in Frameworks */,
@@ -363,7 +388,6 @@
F42E08610D199A2B00D5DDE0 /* Cocoa.framework in Frameworks */,
F42E087F0D199AB400D5DDE0 /* GoogleToolboxForMac.framework in Frameworks */,
F42E089C0D199B1800D5DDE0 /* SenTestingKit.framework in Frameworks */,
- 8B2A9BED0D82714A00599386 /* ScreenSaver.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -428,6 +452,43 @@
path = GTMUIUnitTestingHarness;
sourceTree = "<group>";
};
+ F435E46C0DC8F23A0069CDE8 /* TestData */ = {
+ isa = PBXGroup;
+ children = (
+ 8B45A2DE0DA51A7E001148C5 /* GTMUnitTestingTest.nib */,
+ 8B45A6B90DA67DD5001148C5 /* GTMUnitTestingImage.gtmUTState */,
+ 8B45A5F50DA5EB9F001148C5 /* GTMUnitTestingWindow.gtmUTState */,
+ 8B45A5F60DA5EB9F001148C5 /* GTMUnitTestingTestApp.gtmUTState */,
+ 8BEEA90A0DA7446300894774 /* GTMUnitTestingImage.tiff */,
+ 8BEEA90B0DA7446300894774 /* GTMUnitTestingWindow.tiff */,
+ 8BEEA90C0DA7446300894774 /* GTMUnitTestingView.tiff */,
+ );
+ path = TestData;
+ sourceTree = "<group>";
+ };
+ F435E4840DC8F3DC0069CDE8 /* TestData */ = {
+ isa = PBXGroup;
+ children = (
+ 8B7AD4980DABBB5800B84F4A /* GTMNSBezierPath+CGPathTest.tiff */,
+ F435DE7A0DC0B6580069CDE8 /* GTMNSBezierPath+CGPathTest.ppc64.tiff */,
+ F435DE7B0DC0B6580069CDE8 /* GTMNSBezierPath+CGPathTest.x86_64.tiff */,
+ 8B7AD4990DABBB5800B84F4A /* GTMNSBezierPath+RoundRectTest.tiff */,
+ F435DE8A0DC0B7620069CDE8 /* GTMNSBezierPath+RoundRectTest.ppc64.tiff */,
+ 8BE869720DBE89C100749827 /* GTMNSBezierPath+RoundRectTest.x86_64.tiff */,
+ 8B7AD4970DABBB5800B84F4A /* GTMNSBezierPath+ShadingTest.10.5.tiff */,
+ );
+ path = TestData;
+ sourceTree = "<group>";
+ };
+ F435E4B50DC903E20069CDE8 /* TestData */ = {
+ isa = PBXGroup;
+ children = (
+ F435E3B90DC8D1720069CDE8 /* GTMHTTPFetcherTestPage.html */,
+ F435E3BA0DC8D1720069CDE8 /* GTMHTTPFetcherTestServer */,
+ );
+ path = TestData;
+ sourceTree = "<group>";
+ };
F48B91030D94485500D45044 /* TigerGcov */ = {
isa = PBXGroup;
children = (
@@ -460,25 +521,17 @@
F428FEFF0D48E55E00382ED1 /* GTMNSBezierPath+CGPath.h */,
F428FF000D48E55E00382ED1 /* GTMNSBezierPath+CGPath.m */,
F428FF010D48E55E00382ED1 /* GTMNSBezierPath+CGPathTest.m */,
- 8B7AD4980DABBB5800B84F4A /* GTMNSBezierPath+CGPathTest.tiff */,
F48FE2810D198D0E009257D2 /* GTMNSBezierPath+RoundRect.h */,
F48FE2820D198D0E009257D2 /* GTMNSBezierPath+RoundRect.m */,
F48FE2830D198D0E009257D2 /* GTMNSBezierPath+RoundRectTest.m */,
- 8B7AD4990DABBB5800B84F4A /* GTMNSBezierPath+RoundRectTest.tiff */,
F47F1C0D0D490BC000925B8F /* GTMNSBezierPath+Shading.h */,
F47F1C0E0D490BC000925B8F /* GTMNSBezierPath+Shading.m */,
- 8B7AD4970DABBB5800B84F4A /* GTMNSBezierPath+ShadingTest.10.5.tiff */,
F47F1C110D490BC000925B8F /* GTMNSBezierPath+ShadingTest.m */,
- F47F1CAC0D4910FD00925B8F /* GTMNSColor+Theme.h */,
- F47F1CAD0D4910FD00925B8F /* GTMNSColor+Theme.m */,
- F47F1CAE0D4910FD00925B8F /* GTMNSColor+ThemeTest.m */,
F47F1C740D490E5C00925B8F /* GTMShading.h */,
8B2A9B1D0D8270DA00599386 /* GTMNSWorkspace+ScreenSaver.m */,
8B2A9B1E0D8270DA00599386 /* GTMNSWorkspace+ScreenSaverTest.m */,
8B2A9B1F0D8270DA00599386 /* GTMNSWorkspace+ScreenSaver.h */,
- F42E09A80D19A5E300D5DDE0 /* GTMNSWorkspace+Theme.h */,
- F42E09A90D19A5E300D5DDE0 /* GTMNSWorkspace+Theme.m */,
- F42E0B090D19A6FB00D5DDE0 /* GTMNSWorkspace+ThemeTest.m */,
+ F435E4840DC8F3DC0069CDE8 /* TestData */,
);
path = AppKit;
sourceTree = "<group>";
@@ -486,6 +539,9 @@
F48FE2720D198CCE009257D2 /* Foundation */ = {
isa = PBXGroup;
children = (
+ F41D25880DBD21A300774EEB /* GTMBase64.h */,
+ F41D25890DBD21A300774EEB /* GTMBase64.m */,
+ F41D258A0DBD21A300774EEB /* GTMBase64Test.m */,
F48FE27E0D198D0E009257D2 /* GTMGeometryUtils.h */,
F48FE27F0D198D0E009257D2 /* GTMGeometryUtils.m */,
F48FE2800D198D0E009257D2 /* GTMGeometryUtilsTest.m */,
@@ -493,9 +549,9 @@
F47F1D2E0D4914AD00925B8F /* GTMCalculatedRange.m */,
F47F1D2F0D4914AD00925B8F /* GTMCalculatedRangeTest.m */,
F48FE28D0D198D24009257D2 /* GTMGarbageCollection.h */,
- 8B6F32040DA34A1B0052CA40 /* GTMObjC2Runtime.m */,
- 8B6F32050DA34A1B0052CA40 /* GTMObjC2RuntimeTest.m */,
- 8B6F32060DA34A1B0052CA40 /* GTMObjC2Runtime.h */,
+ F435E0870DC63F6D0069CDE8 /* GTMHTTPFetcher.h */,
+ F435E0880DC63F6D0069CDE8 /* GTMHTTPFetcher.m */,
+ F435E3930DC8CAAF0069CDE8 /* GTMHTTPFetcherTest.m */,
F43E4DD60D4E56320041161F /* GTMNSEnumerator+Filter.h */,
F43E4DD70D4E56320041161F /* GTMNSEnumerator+Filter.m */,
F43E4DD80D4E56320041161F /* GTMNSEnumerator+FilterTest.m */,
@@ -508,13 +564,15 @@
F43E4C250D4E361D0041161F /* GTMNSString+XML.h */,
F43E4C260D4E361D0041161F /* GTMNSString+XML.m */,
F43E4C270D4E361D0041161F /* GTMNSString+XMLTest.m */,
- 8BC04CD10DB003BE00C2D1CA /* GTMNSString+Utilities.h */,
- 8BC04CD20DB003BE00C2D1CA /* GTMNSString+Utilities.m */,
- 8BC04CD50DB003C300C2D1CA /* GTMNSString+UtilitiesTest.m */,
F43E4E5E0D4E5EC90041161F /* GTMNSData+zlib.h */,
F43E4E5F0D4E5EC90041161F /* GTMNSData+zlib.m */,
F43E4E600D4E5EC90041161F /* GTMNSData+zlibTest.m */,
+ 8B6F32040DA34A1B0052CA40 /* GTMObjC2Runtime.m */,
+ 8B6F32050DA34A1B0052CA40 /* GTMObjC2RuntimeTest.m */,
+ 8B6F32060DA34A1B0052CA40 /* GTMObjC2Runtime.h */,
F48FE2910D198D24009257D2 /* GTMObjectSingleton.h */,
+ F435E27D0DC7B0630069CDE8 /* GTMProgressMonitorInputStream.h */,
+ F435E27E0DC7B0630069CDE8 /* GTMProgressMonitorInputStream.m */,
F437F55A0D50BC0A00F5C3A4 /* GTMRegex.h */,
F437F55B0D50BC0A00F5C3A4 /* GTMRegex.m */,
F437F55C0D50BC0A00F5C3A4 /* GTMRegexTest.m */,
@@ -524,6 +582,7 @@
F48FE2920D198D24009257D2 /* GTMSystemVersion.h */,
F48FE2930D198D24009257D2 /* GTMSystemVersion.m */,
F48FE2E10D198E4C009257D2 /* GTMSystemVersionTest.m */,
+ F435E4B50DC903E20069CDE8 /* TestData */,
);
path = Foundation;
sourceTree = "<group>";
@@ -546,14 +605,8 @@
8B45A2E00DA51ABC001148C5 /* GTMUnitTestingTest.h */,
8B45A2E10DA51ABC001148C5 /* GTMUnitTestingTest.m */,
8B7AD4AD0DABBFEE00B84F4A /* GTMUnitTestingBindingTest.m */,
- 8B45A2DE0DA51A7E001148C5 /* GTMUnitTestingTest.nib */,
- 8B45A6B90DA67DD5001148C5 /* GTMUnitTestingImage.gtmUTState */,
- 8B45A5F50DA5EB9F001148C5 /* GTMUnitTestingWindow.gtmUTState */,
- 8B45A5F60DA5EB9F001148C5 /* GTMUnitTestingTestApp.gtmUTState */,
- 8BEEA90A0DA7446300894774 /* GTMUnitTestingImage.tiff */,
- 8BEEA90B0DA7446300894774 /* GTMUnitTestingWindow.tiff */,
- 8BEEA90C0DA7446300894774 /* GTMUnitTestingView.tiff */,
8B45A2A20DA49C47001148C5 /* GTMUIUnitTestingHarness */,
+ F435E46C0DC8F23A0069CDE8 /* TestData */,
);
path = UnitTesting;
sourceTree = "<group>";
@@ -561,7 +614,9 @@
F4CA852B0DAFA92A00B4AB10 /* Project */ = {
isa = PBXGroup;
children = (
+ F4CA864A0DB3ACB200B4AB10 /* DebugLeopardOrLater.xcconfig */,
F48FE2410D197F9A009257D2 /* DebugTigerOrLater.xcconfig */,
+ F4CA864B0DB3ACB200B4AB10 /* ReleaseLeopardOrLater.xcconfig */,
F48FE2440D197F9A009257D2 /* ReleaseTigerOrLater.xcconfig */,
);
path = Project;
@@ -571,10 +626,13 @@
isa = PBXGroup;
children = (
F48FE2420D197F9A009257D2 /* DebugUnittest.xcconfig */,
+ F4CA864C0DB3ACD200B4AB10 /* LoadableBundleGCSupported.xcconfig */,
F48FE2430D197F9A009257D2 /* LoadableBundle.xcconfig */,
F48FE2450D197F9A009257D2 /* ReleaseUnittest.xcconfig */,
F48FE2460D197F9A009257D2 /* SharedLibrary.xcconfig */,
+ F4CA864D0DB3ACD200B4AB10 /* SharedLibraryGCSupported.xcconfig */,
F48FE2470D197F9A009257D2 /* StaticLibrary.xcconfig */,
+ F4CA864E0DB3ACD200B4AB10 /* StaticLibraryGCSupported.xcconfig */,
);
path = Target;
sourceTree = "<group>";
@@ -604,12 +662,10 @@
F42E09510D199BBF00D5DDE0 /* GTMNSString+HTML.h in Headers */,
F42E09530D199BBF00D5DDE0 /* GTMObjectSingleton.h in Headers */,
F42E09540D199BBF00D5DDE0 /* GTMSystemVersion.h in Headers */,
- F42E09AA0D19A5E300D5DDE0 /* GTMNSWorkspace+Theme.h in Headers */,
F43DCDCD0D4796C600959A62 /* GTMLoginItems.h in Headers */,
F428FF030D48E55E00382ED1 /* GTMNSBezierPath+CGPath.h in Headers */,
F47F1C120D490BC000925B8F /* GTMNSBezierPath+Shading.h in Headers */,
F47F1C750D490E5C00925B8F /* GTMShading.h in Headers */,
- F47F1CAF0D4910FD00925B8F /* GTMNSColor+Theme.h in Headers */,
F47F1D300D4914AD00925B8F /* GTMCalculatedRange.h in Headers */,
F43E447A0D4918B20041161F /* GTMLinearRGBShading.h in Headers */,
F43E4C280D4E361D0041161F /* GTMNSString+XML.h in Headers */,
@@ -622,8 +678,10 @@
F424F75F0D9AF019000B87EF /* GTMDefines.h in Headers */,
F4FF22780D9D4835003880AC /* GTMDebugSelectorValidation.h in Headers */,
F4CA854F0DAFAAF600B4AB10 /* GTMObjC2Runtime.h in Headers */,
- 8BC04CD30DB003BE00C2D1CA /* GTMNSString+Utilities.h in Headers */,
8BC04CD80DB003D800C2D1CA /* GTMMethodCheck.h in Headers */,
+ F41D258B0DBD21A300774EEB /* GTMBase64.h in Headers */,
+ F435E0890DC63F6D0069CDE8 /* GTMHTTPFetcher.h in Headers */,
+ F435E27F0DC7B0630069CDE8 /* GTMProgressMonitorInputStream.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -679,6 +737,7 @@
);
dependencies = (
F42E08790D199AA600D5DDE0 /* PBXTargetDependency */,
+ F41D25520DB9068700774EEB /* PBXTargetDependency */,
);
name = "UnitTest - Foundation";
productName = "UnitTest - AppKit";
@@ -716,6 +775,7 @@
);
dependencies = (
F42E08770D199A9B00D5DDE0 /* PBXTargetDependency */,
+ F41D254F0DB9067C00774EEB /* PBXTargetDependency */,
);
name = "UnitTest - AppKit";
productName = "UnitTest - AppKit";
@@ -727,8 +787,11 @@
/* Begin PBXProject section */
0867D690FE84028FC02AAC07 /* Project object */ = {
isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = NO;
+ };
buildConfigurationList = 1DEB918108733D990010E9CD /* Build configuration list for PBXProject "GTM" */;
- compatibilityVersion = "Xcode 2.4";
+ compatibilityVersion = "Xcode 3.0";
hasScannedForEncodings = 1;
mainGroup = 0867D691FE84028FC02AAC07 /* GTM */;
productRefGroup = 034768DFFF38A50411DB9C8B /* Products */;
@@ -772,6 +835,8 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ F435E3BB0DC8D1980069CDE8 /* GTMHTTPFetcherTestPage.html in Resources */,
+ F435E3BC0DC8D1980069CDE8 /* GTMHTTPFetcherTestServer in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -789,6 +854,10 @@
8B7AD49A0DABBB5800B84F4A /* GTMNSBezierPath+ShadingTest.10.5.tiff in Resources */,
8B7AD49B0DABBB5800B84F4A /* GTMNSBezierPath+CGPathTest.tiff in Resources */,
8B7AD49C0DABBB5800B84F4A /* GTMNSBezierPath+RoundRectTest.tiff in Resources */,
+ 8BE869730DBE89C100749827 /* GTMNSBezierPath+RoundRectTest.x86_64.tiff in Resources */,
+ F435DE7C0DC0B6580069CDE8 /* GTMNSBezierPath+CGPathTest.ppc64.tiff in Resources */,
+ F435DE7D0DC0B6580069CDE8 /* GTMNSBezierPath+CGPathTest.x86_64.tiff in Resources */,
+ F435DE8B0DC0B7620069CDE8 /* GTMNSBezierPath+RoundRectTest.ppc64.tiff in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -878,8 +947,9 @@
8B6F32080DA34A1B0052CA40 /* GTMObjC2RuntimeTest.m in Sources */,
8B6F32160DA34C830052CA40 /* GTMMethodCheckTest.m in Sources */,
8BC045C20DAE899100C2D1CA /* GTMGeometryUtilsTest.m in Sources */,
- 8BC04CD60DB003C300C2D1CA /* GTMNSString+UtilitiesTest.m in Sources */,
8BC04CDE0DB004A000C2D1CA /* GTMMethodCheck.m in Sources */,
+ F41D258F0DBD21B900774EEB /* GTMBase64Test.m in Sources */,
+ F435E3940DC8CAAF0069CDE8 /* GTMHTTPFetcherTest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -892,11 +962,9 @@
F42E09500D199BBF00D5DDE0 /* GTMNSBezierPath+RoundRect.m in Sources */,
F42E09520D199BBF00D5DDE0 /* GTMNSString+HTML.m in Sources */,
F42E09550D199BBF00D5DDE0 /* GTMSystemVersion.m in Sources */,
- F42E09AB0D19A5E300D5DDE0 /* GTMNSWorkspace+Theme.m in Sources */,
F43DCDCE0D4796C600959A62 /* GTMLoginItems.m in Sources */,
F428FF040D48E55E00382ED1 /* GTMNSBezierPath+CGPath.m in Sources */,
F47F1C130D490BC000925B8F /* GTMNSBezierPath+Shading.m in Sources */,
- F47F1CB00D4910FD00925B8F /* GTMNSColor+Theme.m in Sources */,
F47F1D310D4914AD00925B8F /* GTMCalculatedRange.m in Sources */,
F43E447B0D4918B20041161F /* GTMLinearRGBShading.m in Sources */,
F43E4C290D4E361D0041161F /* GTMNSString+XML.m in Sources */,
@@ -907,8 +975,10 @@
F41390900D75F63C00F72B31 /* GTMNSFileManager+Path.m in Sources */,
8B2A9B200D8270DA00599386 /* GTMNSWorkspace+ScreenSaver.m in Sources */,
8B45A21E0DA46E34001148C5 /* GTMObjC2Runtime.m in Sources */,
- 8BC04CD40DB003BE00C2D1CA /* GTMNSString+Utilities.m in Sources */,
8BC04CD70DB003CD00C2D1CA /* GTMMethodCheck.m in Sources */,
+ F41D258C0DBD21A300774EEB /* GTMBase64.m in Sources */,
+ F435E08A0DC63F6D0069CDE8 /* GTMHTTPFetcher.m in Sources */,
+ F435E2800DC7B0630069CDE8 /* GTMProgressMonitorInputStream.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -918,13 +988,10 @@
files = (
F42E082F0D19991400D5DDE0 /* GTMNSBezierPath+RoundRectTest.m in Sources */,
F42E09450D199BA400D5DDE0 /* GTMNSObject+UnitTesting.m in Sources */,
- F42E0B0A0D19A6FB00D5DDE0 /* GTMNSWorkspace+ThemeTest.m in Sources */,
F43DCEC70D47BEA000959A62 /* GTMLoginItemsTest.m in Sources */,
F428FF090D48E57300382ED1 /* GTMNSBezierPath+CGPathTest.m in Sources */,
F47F1C1B0D490BD200925B8F /* GTMNSBezierPath+ShadingTest.m in Sources */,
- F47F1CB60D49110900925B8F /* GTMNSColor+ThemeTest.m in Sources */,
F43E447F0D4918BC0041161F /* GTMLinearRGBShadingTest.m in Sources */,
- 8B2A9B230D8270DA00599386 /* GTMNSWorkspace+ScreenSaver.m in Sources */,
8B2A9B240D8270DA00599386 /* GTMNSWorkspace+ScreenSaverTest.m in Sources */,
8BC04CDD0DB004A000C2D1CA /* GTMMethodCheck.m in Sources */,
8B5547B90DB3BB220014CC1C /* GTMAppKit+UnitTesting.m in Sources */,
@@ -944,6 +1011,16 @@
target = 8B45A2890DA49B99001148C5 /* UIUnitTestingHarness */;
targetProxy = 8B45A2D30DA51A0E001148C5 /* PBXContainerItemProxy */;
};
+ F41D254F0DB9067C00774EEB /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 8B45A0270DA4696C001148C5 /* UnitTest - UnitTesting */;
+ targetProxy = F41D254E0DB9067C00774EEB /* PBXContainerItemProxy */;
+ };
+ F41D25520DB9068700774EEB /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 8B45A0270DA4696C001148C5 /* UnitTest - UnitTesting */;
+ targetProxy = F41D25510DB9068700774EEB /* PBXContainerItemProxy */;
+ };
F42E08770D199A9B00D5DDE0 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = F42E086C0D199A5B00D5DDE0 /* GTM */;
@@ -978,25 +1055,25 @@
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
- 1DEB918208733D990010E9CD /* Debug */ = {
+ 1DEB918208733D990010E9CD /* TigerOrLater-Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = F48FE2410D197F9A009257D2 /* DebugTigerOrLater.xcconfig */;
buildSettings = {
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = GTM_Prefix.pch;
};
- name = Debug;
+ name = "TigerOrLater-Debug";
};
- 1DEB918308733D990010E9CD /* Release */ = {
+ 1DEB918308733D990010E9CD /* TigerOrLater-Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = F48FE2440D197F9A009257D2 /* ReleaseTigerOrLater.xcconfig */;
buildSettings = {
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = GTM_Prefix.pch;
};
- name = Release;
+ name = "TigerOrLater-Release";
};
- 8B45A02A0DA4696D001148C5 /* Debug */ = {
+ 8B45A02A0DA4696D001148C5 /* TigerOrLater-Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = F48FE2420D197F9A009257D2 /* DebugUnittest.xcconfig */;
buildSettings = {
@@ -1006,11 +1083,11 @@
);
INFOPLIST_FILE = "UnitTest-Info.plist";
PRODUCT_NAME = "UnitTest - UnitTesting";
- TEST_HOST = "$(ACTION)/$(CONFIGURATION)/GTMUIUnitTestingHarness.app/Contents/MacOS/GTMUIUnitTestingHarness";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GTMUIUnitTestingHarness.app/Contents/MacOS/GTMUIUnitTestingHarness";
};
- name = Debug;
+ name = "TigerOrLater-Debug";
};
- 8B45A02B0DA4696D001148C5 /* Debug-gcov */ = {
+ 8B45A02B0DA4696D001148C5 /* TigerOrLater-Debug-gcov */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = F48FE2420D197F9A009257D2 /* DebugUnittest.xcconfig */;
buildSettings = {
@@ -1020,11 +1097,11 @@
);
INFOPLIST_FILE = "UnitTest-Info.plist";
PRODUCT_NAME = "UnitTest - UnitTesting";
- TEST_HOST = "$(ACTION)/$(CONFIGURATION)/GTMUIUnitTestingHarness.app/Contents/MacOS/GTMUIUnitTestingHarness";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GTMUIUnitTestingHarness.app/Contents/MacOS/GTMUIUnitTestingHarness";
};
- name = "Debug-gcov";
+ name = "TigerOrLater-Debug-gcov";
};
- 8B45A02C0DA4696D001148C5 /* Release */ = {
+ 8B45A02C0DA4696D001148C5 /* TigerOrLater-Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = F48FE2450D197F9A009257D2 /* ReleaseUnittest.xcconfig */;
buildSettings = {
@@ -1034,35 +1111,35 @@
);
INFOPLIST_FILE = "UnitTest-Info.plist";
PRODUCT_NAME = "UnitTest - UnitTesting";
- TEST_HOST = "$(ACTION)/$(CONFIGURATION)/GTMUIUnitTestingHarness.app/Contents/MacOS/GTMUIUnitTestingHarness";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GTMUIUnitTestingHarness.app/Contents/MacOS/GTMUIUnitTestingHarness";
};
- name = Release;
+ name = "TigerOrLater-Release";
};
- 8B45A28E0DA49B99001148C5 /* Debug */ = {
+ 8B45A28E0DA49B99001148C5 /* TigerOrLater-Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
INFOPLIST_FILE = UnitTesting/GTMUIUnitTestingHarness/Info.plist;
PRODUCT_NAME = GTMUIUnitTestingHarness;
};
- name = Debug;
+ name = "TigerOrLater-Debug";
};
- 8B45A28F0DA49B99001148C5 /* Debug-gcov */ = {
+ 8B45A28F0DA49B99001148C5 /* TigerOrLater-Debug-gcov */ = {
isa = XCBuildConfiguration;
buildSettings = {
INFOPLIST_FILE = UnitTesting/GTMUIUnitTestingHarness/Info.plist;
PRODUCT_NAME = GTMUIUnitTestingHarness;
};
- name = "Debug-gcov";
+ name = "TigerOrLater-Debug-gcov";
};
- 8B45A2900DA49B99001148C5 /* Release */ = {
+ 8B45A2900DA49B99001148C5 /* TigerOrLater-Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
INFOPLIST_FILE = UnitTesting/GTMUIUnitTestingHarness/Info.plist;
PRODUCT_NAME = GTMUIUnitTestingHarness;
};
- name = Release;
+ name = "TigerOrLater-Release";
};
- F42E081F0D19987200D5DDE0 /* Debug */ = {
+ F42E081F0D19987200D5DDE0 /* TigerOrLater-Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = F48FE2420D197F9A009257D2 /* DebugUnittest.xcconfig */;
buildSettings = {
@@ -1073,9 +1150,9 @@
INFOPLIST_FILE = "UnitTest-Info.plist";
PRODUCT_NAME = "UnitTest - Foundation";
};
- name = Debug;
+ name = "TigerOrLater-Debug";
};
- F42E08200D19987200D5DDE0 /* Release */ = {
+ F42E08200D19987200D5DDE0 /* TigerOrLater-Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = F48FE2450D197F9A009257D2 /* ReleaseUnittest.xcconfig */;
buildSettings = {
@@ -1086,9 +1163,9 @@
INFOPLIST_FILE = "UnitTest-Info.plist";
PRODUCT_NAME = "UnitTest - Foundation";
};
- name = Release;
+ name = "TigerOrLater-Release";
};
- F42E08700D199A5C00D5DDE0 /* Debug */ = {
+ F42E08700D199A5C00D5DDE0 /* TigerOrLater-Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = F48FE2430D197F9A009257D2 /* LoadableBundle.xcconfig */;
buildSettings = {
@@ -1099,9 +1176,9 @@
INSTALL_PATH = "@loader_path/../Frameworks";
PRODUCT_NAME = GoogleToolboxForMac;
};
- name = Debug;
+ name = "TigerOrLater-Debug";
};
- F42E08710D199A5C00D5DDE0 /* Release */ = {
+ F42E08710D199A5C00D5DDE0 /* TigerOrLater-Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = F48FE2430D197F9A009257D2 /* LoadableBundle.xcconfig */;
buildSettings = {
@@ -1112,31 +1189,23 @@
INSTALL_PATH = "@loader_path/../Frameworks";
PRODUCT_NAME = GoogleToolboxForMac;
};
- name = Release;
+ name = "TigerOrLater-Release";
};
- F47204350D33BEDF00E9F378 /* Debug */ = {
+ F47204350D33BEDF00E9F378 /* TigerOrLater-Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
- COPY_PHASE_STRIP = NO;
- GCC_DYNAMIC_NO_PIC = NO;
- GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
- GCC_OPTIMIZATION_LEVEL = 0;
PRODUCT_NAME = "All UnitTests";
};
- name = Debug;
+ name = "TigerOrLater-Debug";
};
- F47204360D33BEDF00E9F378 /* Release */ = {
+ F47204360D33BEDF00E9F378 /* TigerOrLater-Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
- COPY_PHASE_STRIP = YES;
- GCC_ENABLE_FIX_AND_CONTINUE = NO;
- GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
PRODUCT_NAME = "All UnitTests";
- ZERO_LINK = NO;
};
- name = Release;
+ name = "TigerOrLater-Release";
};
- F48B91060D94489300D45044 /* Debug-gcov */ = {
+ F48B91060D94489300D45044 /* TigerOrLater-Debug-gcov */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = F48FE2410D197F9A009257D2 /* DebugTigerOrLater.xcconfig */;
buildSettings = {
@@ -1150,9 +1219,9 @@
);
OTHER_LDFLAGS = "-lgcov";
};
- name = "Debug-gcov";
+ name = "TigerOrLater-Debug-gcov";
};
- F48B91070D94489300D45044 /* Debug-gcov */ = {
+ F48B91070D94489300D45044 /* TigerOrLater-Debug-gcov */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = F48FE2430D197F9A009257D2 /* LoadableBundle.xcconfig */;
buildSettings = {
@@ -1163,20 +1232,16 @@
INSTALL_PATH = "@loader_path/../Frameworks";
PRODUCT_NAME = GoogleToolboxForMac;
};
- name = "Debug-gcov";
+ name = "TigerOrLater-Debug-gcov";
};
- F48B91080D94489300D45044 /* Debug-gcov */ = {
+ F48B91080D94489300D45044 /* TigerOrLater-Debug-gcov */ = {
isa = XCBuildConfiguration;
buildSettings = {
- COPY_PHASE_STRIP = NO;
- GCC_DYNAMIC_NO_PIC = NO;
- GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
- GCC_OPTIMIZATION_LEVEL = 0;
PRODUCT_NAME = "All UnitTests";
};
- name = "Debug-gcov";
+ name = "TigerOrLater-Debug-gcov";
};
- F48B91090D94489300D45044 /* Debug-gcov */ = {
+ F48B91090D94489300D45044 /* TigerOrLater-Debug-gcov */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = F48FE2420D197F9A009257D2 /* DebugUnittest.xcconfig */;
buildSettings = {
@@ -1187,9 +1252,9 @@
INFOPLIST_FILE = "UnitTest-Info.plist";
PRODUCT_NAME = "UnitTest - AppKit";
};
- name = "Debug-gcov";
+ name = "TigerOrLater-Debug-gcov";
};
- F48B910A0D94489300D45044 /* Debug-gcov */ = {
+ F48B910A0D94489300D45044 /* TigerOrLater-Debug-gcov */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = F48FE2420D197F9A009257D2 /* DebugUnittest.xcconfig */;
buildSettings = {
@@ -1200,9 +1265,9 @@
INFOPLIST_FILE = "UnitTest-Info.plist";
PRODUCT_NAME = "UnitTest - Foundation";
};
- name = "Debug-gcov";
+ name = "TigerOrLater-Debug-gcov";
};
- F48FE2670D198C1F009257D2 /* Debug */ = {
+ F48FE2670D198C1F009257D2 /* TigerOrLater-Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = F48FE2420D197F9A009257D2 /* DebugUnittest.xcconfig */;
buildSettings = {
@@ -1213,9 +1278,9 @@
INFOPLIST_FILE = "UnitTest-Info.plist";
PRODUCT_NAME = "UnitTest - AppKit";
};
- name = Debug;
+ name = "TigerOrLater-Debug";
};
- F48FE2680D198C1F009257D2 /* Release */ = {
+ F48FE2680D198C1F009257D2 /* TigerOrLater-Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = F48FE2450D197F9A009257D2 /* ReleaseUnittest.xcconfig */;
buildSettings = {
@@ -1226,7 +1291,241 @@
INFOPLIST_FILE = "UnitTest-Info.plist";
PRODUCT_NAME = "UnitTest - AppKit";
};
- name = Release;
+ name = "TigerOrLater-Release";
+ };
+ F4CA864F0DB3ACE500B4AB10 /* LeopardOrLater-Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F4CA864A0DB3ACB200B4AB10 /* DebugLeopardOrLater.xcconfig */;
+ buildSettings = {
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = GTM_Prefix.pch;
+ };
+ name = "LeopardOrLater-Debug";
+ };
+ F4CA86500DB3ACE500B4AB10 /* LeopardOrLater-Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F4CA864C0DB3ACD200B4AB10 /* LoadableBundleGCSupported.xcconfig */;
+ buildSettings = {
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ FRAMEWORK_VERSION = A;
+ INFOPLIST_FILE = "GTM-Info.plist";
+ INSTALL_PATH = "@loader_path/../Frameworks";
+ PRODUCT_NAME = GoogleToolboxForMac;
+ };
+ name = "LeopardOrLater-Debug";
+ };
+ F4CA86510DB3ACE500B4AB10 /* LeopardOrLater-Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "All UnitTests";
+ };
+ name = "LeopardOrLater-Debug";
+ };
+ F4CA86520DB3ACE500B4AB10 /* LeopardOrLater-Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F48FE2420D197F9A009257D2 /* DebugUnittest.xcconfig */;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(value)",
+ "$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)",
+ );
+ INFOPLIST_FILE = "UnitTest-Info.plist";
+ PRODUCT_NAME = "UnitTest - AppKit";
+ };
+ name = "LeopardOrLater-Debug";
+ };
+ F4CA86530DB3ACE500B4AB10 /* LeopardOrLater-Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F48FE2420D197F9A009257D2 /* DebugUnittest.xcconfig */;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(value)",
+ "$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)",
+ );
+ INFOPLIST_FILE = "UnitTest-Info.plist";
+ PRODUCT_NAME = "UnitTest - Foundation";
+ };
+ name = "LeopardOrLater-Debug";
+ };
+ F4CA86540DB3ACE500B4AB10 /* LeopardOrLater-Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F48FE2420D197F9A009257D2 /* DebugUnittest.xcconfig */;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(value)",
+ "$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)",
+ );
+ INFOPLIST_FILE = "UnitTest-Info.plist";
+ PRODUCT_NAME = "UnitTest - UnitTesting";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GTMUIUnitTestingHarness.app/Contents/MacOS/GTMUIUnitTestingHarness";
+ };
+ name = "LeopardOrLater-Debug";
+ };
+ F4CA86550DB3ACE500B4AB10 /* LeopardOrLater-Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INFOPLIST_FILE = UnitTesting/GTMUIUnitTestingHarness/Info.plist;
+ PRODUCT_NAME = GTMUIUnitTestingHarness;
+ };
+ name = "LeopardOrLater-Debug";
+ };
+ F4CA865A0DB3AD0300B4AB10 /* LeopardOrLater-Debug-gcov */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F4CA864A0DB3ACB200B4AB10 /* DebugLeopardOrLater.xcconfig */;
+ buildSettings = {
+ GCC_GENERATE_TEST_COVERAGE_FILES = YES;
+ GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = GTM_Prefix.pch;
+ OTHER_LDFLAGS = "-lgcov";
+ };
+ name = "LeopardOrLater-Debug-gcov";
+ };
+ F4CA865B0DB3AD0300B4AB10 /* LeopardOrLater-Debug-gcov */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F4CA864C0DB3ACD200B4AB10 /* LoadableBundleGCSupported.xcconfig */;
+ buildSettings = {
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ FRAMEWORK_VERSION = A;
+ INFOPLIST_FILE = "GTM-Info.plist";
+ INSTALL_PATH = "@loader_path/../Frameworks";
+ PRODUCT_NAME = GoogleToolboxForMac;
+ };
+ name = "LeopardOrLater-Debug-gcov";
+ };
+ F4CA865C0DB3AD0300B4AB10 /* LeopardOrLater-Debug-gcov */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "All UnitTests";
+ };
+ name = "LeopardOrLater-Debug-gcov";
+ };
+ F4CA865D0DB3AD0300B4AB10 /* LeopardOrLater-Debug-gcov */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F48FE2420D197F9A009257D2 /* DebugUnittest.xcconfig */;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(value)",
+ "$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)",
+ );
+ INFOPLIST_FILE = "UnitTest-Info.plist";
+ PRODUCT_NAME = "UnitTest - AppKit";
+ };
+ name = "LeopardOrLater-Debug-gcov";
+ };
+ F4CA865E0DB3AD0300B4AB10 /* LeopardOrLater-Debug-gcov */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F48FE2420D197F9A009257D2 /* DebugUnittest.xcconfig */;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(value)",
+ "$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)",
+ );
+ INFOPLIST_FILE = "UnitTest-Info.plist";
+ PRODUCT_NAME = "UnitTest - Foundation";
+ };
+ name = "LeopardOrLater-Debug-gcov";
+ };
+ F4CA865F0DB3AD0300B4AB10 /* LeopardOrLater-Debug-gcov */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F48FE2420D197F9A009257D2 /* DebugUnittest.xcconfig */;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(value)",
+ "$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)",
+ );
+ INFOPLIST_FILE = "UnitTest-Info.plist";
+ PRODUCT_NAME = "UnitTest - UnitTesting";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GTMUIUnitTestingHarness.app/Contents/MacOS/GTMUIUnitTestingHarness";
+ };
+ name = "LeopardOrLater-Debug-gcov";
+ };
+ F4CA86600DB3AD0300B4AB10 /* LeopardOrLater-Debug-gcov */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INFOPLIST_FILE = UnitTesting/GTMUIUnitTestingHarness/Info.plist;
+ PRODUCT_NAME = GTMUIUnitTestingHarness;
+ };
+ name = "LeopardOrLater-Debug-gcov";
+ };
+ F4CA86610DB3AD0D00B4AB10 /* LeopardOrLater-Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F4CA864B0DB3ACB200B4AB10 /* ReleaseLeopardOrLater.xcconfig */;
+ buildSettings = {
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = GTM_Prefix.pch;
+ };
+ name = "LeopardOrLater-Release";
+ };
+ F4CA86620DB3AD0D00B4AB10 /* LeopardOrLater-Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F4CA864C0DB3ACD200B4AB10 /* LoadableBundleGCSupported.xcconfig */;
+ buildSettings = {
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ FRAMEWORK_VERSION = A;
+ INFOPLIST_FILE = "GTM-Info.plist";
+ INSTALL_PATH = "@loader_path/../Frameworks";
+ PRODUCT_NAME = GoogleToolboxForMac;
+ };
+ name = "LeopardOrLater-Release";
+ };
+ F4CA86630DB3AD0D00B4AB10 /* LeopardOrLater-Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "All UnitTests";
+ };
+ name = "LeopardOrLater-Release";
+ };
+ F4CA86640DB3AD0D00B4AB10 /* LeopardOrLater-Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F48FE2450D197F9A009257D2 /* ReleaseUnittest.xcconfig */;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(value)",
+ "$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)",
+ );
+ INFOPLIST_FILE = "UnitTest-Info.plist";
+ PRODUCT_NAME = "UnitTest - AppKit";
+ };
+ name = "LeopardOrLater-Release";
+ };
+ F4CA86650DB3AD0D00B4AB10 /* LeopardOrLater-Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F48FE2450D197F9A009257D2 /* ReleaseUnittest.xcconfig */;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(value)",
+ "$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)",
+ );
+ INFOPLIST_FILE = "UnitTest-Info.plist";
+ PRODUCT_NAME = "UnitTest - Foundation";
+ };
+ name = "LeopardOrLater-Release";
+ };
+ F4CA86660DB3AD0D00B4AB10 /* LeopardOrLater-Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F48FE2450D197F9A009257D2 /* ReleaseUnittest.xcconfig */;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(value)",
+ "$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)",
+ );
+ INFOPLIST_FILE = "UnitTest-Info.plist";
+ PRODUCT_NAME = "UnitTest - UnitTesting";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GTMUIUnitTestingHarness.app/Contents/MacOS/GTMUIUnitTestingHarness";
+ };
+ name = "LeopardOrLater-Release";
+ };
+ F4CA86670DB3AD0D00B4AB10 /* LeopardOrLater-Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INFOPLIST_FILE = UnitTesting/GTMUIUnitTestingHarness/Info.plist;
+ PRODUCT_NAME = GTMUIUnitTestingHarness;
+ };
+ name = "LeopardOrLater-Release";
};
/* End XCBuildConfiguration section */
@@ -1234,72 +1533,93 @@
1DEB918108733D990010E9CD /* Build configuration list for PBXProject "GTM" */ = {
isa = XCConfigurationList;
buildConfigurations = (
- 1DEB918208733D990010E9CD /* Debug */,
- F48B91060D94489300D45044 /* Debug-gcov */,
- 1DEB918308733D990010E9CD /* Release */,
+ 1DEB918208733D990010E9CD /* TigerOrLater-Debug */,
+ F48B91060D94489300D45044 /* TigerOrLater-Debug-gcov */,
+ 1DEB918308733D990010E9CD /* TigerOrLater-Release */,
+ F4CA864F0DB3ACE500B4AB10 /* LeopardOrLater-Debug */,
+ F4CA865A0DB3AD0300B4AB10 /* LeopardOrLater-Debug-gcov */,
+ F4CA86610DB3AD0D00B4AB10 /* LeopardOrLater-Release */,
);
defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
+ defaultConfigurationName = "TigerOrLater-Release";
};
8B45A02D0DA4696D001148C5 /* Build configuration list for PBXNativeTarget "UnitTest - UnitTesting" */ = {
isa = XCConfigurationList;
buildConfigurations = (
- 8B45A02A0DA4696D001148C5 /* Debug */,
- 8B45A02B0DA4696D001148C5 /* Debug-gcov */,
- 8B45A02C0DA4696D001148C5 /* Release */,
+ 8B45A02A0DA4696D001148C5 /* TigerOrLater-Debug */,
+ 8B45A02B0DA4696D001148C5 /* TigerOrLater-Debug-gcov */,
+ 8B45A02C0DA4696D001148C5 /* TigerOrLater-Release */,
+ F4CA86540DB3ACE500B4AB10 /* LeopardOrLater-Debug */,
+ F4CA865F0DB3AD0300B4AB10 /* LeopardOrLater-Debug-gcov */,
+ F4CA86660DB3AD0D00B4AB10 /* LeopardOrLater-Release */,
);
defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
+ defaultConfigurationName = "TigerOrLater-Release";
};
8B45A2910DA49B9A001148C5 /* Build configuration list for PBXNativeTarget "UIUnitTestingHarness" */ = {
isa = XCConfigurationList;
buildConfigurations = (
- 8B45A28E0DA49B99001148C5 /* Debug */,
- 8B45A28F0DA49B99001148C5 /* Debug-gcov */,
- 8B45A2900DA49B99001148C5 /* Release */,
+ 8B45A28E0DA49B99001148C5 /* TigerOrLater-Debug */,
+ 8B45A28F0DA49B99001148C5 /* TigerOrLater-Debug-gcov */,
+ 8B45A2900DA49B99001148C5 /* TigerOrLater-Release */,
+ F4CA86550DB3ACE500B4AB10 /* LeopardOrLater-Debug */,
+ F4CA86600DB3AD0300B4AB10 /* LeopardOrLater-Debug-gcov */,
+ F4CA86670DB3AD0D00B4AB10 /* LeopardOrLater-Release */,
);
defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
+ defaultConfigurationName = "TigerOrLater-Release";
};
F42E081E0D19987200D5DDE0 /* Build configuration list for PBXNativeTarget "UnitTest - Foundation" */ = {
isa = XCConfigurationList;
buildConfigurations = (
- F42E081F0D19987200D5DDE0 /* Debug */,
- F48B910A0D94489300D45044 /* Debug-gcov */,
- F42E08200D19987200D5DDE0 /* Release */,
+ F42E081F0D19987200D5DDE0 /* TigerOrLater-Debug */,
+ F48B910A0D94489300D45044 /* TigerOrLater-Debug-gcov */,
+ F42E08200D19987200D5DDE0 /* TigerOrLater-Release */,
+ F4CA86530DB3ACE500B4AB10 /* LeopardOrLater-Debug */,
+ F4CA865E0DB3AD0300B4AB10 /* LeopardOrLater-Debug-gcov */,
+ F4CA86650DB3AD0D00B4AB10 /* LeopardOrLater-Release */,
);
defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
+ defaultConfigurationName = "TigerOrLater-Release";
};
F42E086F0D199A5C00D5DDE0 /* Build configuration list for PBXNativeTarget "GTM" */ = {
isa = XCConfigurationList;
buildConfigurations = (
- F42E08700D199A5C00D5DDE0 /* Debug */,
- F48B91070D94489300D45044 /* Debug-gcov */,
- F42E08710D199A5C00D5DDE0 /* Release */,
+ F42E08700D199A5C00D5DDE0 /* TigerOrLater-Debug */,
+ F48B91070D94489300D45044 /* TigerOrLater-Debug-gcov */,
+ F42E08710D199A5C00D5DDE0 /* TigerOrLater-Release */,
+ F4CA86500DB3ACE500B4AB10 /* LeopardOrLater-Debug */,
+ F4CA865B0DB3AD0300B4AB10 /* LeopardOrLater-Debug-gcov */,
+ F4CA86620DB3AD0D00B4AB10 /* LeopardOrLater-Release */,
);
defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
+ defaultConfigurationName = "TigerOrLater-Release";
};
F47204340D33BEDF00E9F378 /* Build configuration list for PBXAggregateTarget "All UnitTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
- F47204350D33BEDF00E9F378 /* Debug */,
- F48B91080D94489300D45044 /* Debug-gcov */,
- F47204360D33BEDF00E9F378 /* Release */,
+ F47204350D33BEDF00E9F378 /* TigerOrLater-Debug */,
+ F48B91080D94489300D45044 /* TigerOrLater-Debug-gcov */,
+ F47204360D33BEDF00E9F378 /* TigerOrLater-Release */,
+ F4CA86510DB3ACE500B4AB10 /* LeopardOrLater-Debug */,
+ F4CA865C0DB3AD0300B4AB10 /* LeopardOrLater-Debug-gcov */,
+ F4CA86630DB3AD0D00B4AB10 /* LeopardOrLater-Release */,
);
defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
+ defaultConfigurationName = "TigerOrLater-Release";
};
F48FE2660D198C1F009257D2 /* Build configuration list for PBXNativeTarget "UnitTest - AppKit" */ = {
isa = XCConfigurationList;
buildConfigurations = (
- F48FE2670D198C1F009257D2 /* Debug */,
- F48B91090D94489300D45044 /* Debug-gcov */,
- F48FE2680D198C1F009257D2 /* Release */,
+ F48FE2670D198C1F009257D2 /* TigerOrLater-Debug */,
+ F48B91090D94489300D45044 /* TigerOrLater-Debug-gcov */,
+ F48FE2680D198C1F009257D2 /* TigerOrLater-Release */,
+ F4CA86520DB3ACE500B4AB10 /* LeopardOrLater-Debug */,
+ F4CA865D0DB3AD0300B4AB10 /* LeopardOrLater-Debug-gcov */,
+ F4CA86640DB3AD0D00B4AB10 /* LeopardOrLater-Release */,
);
defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
+ defaultConfigurationName = "TigerOrLater-Release";
};
/* End XCConfigurationList section */
};
diff --git a/GTMDefines.h b/GTMDefines.h
index b8cd1ec..8bc1385 100644
--- a/GTMDefines.h
+++ b/GTMDefines.h
@@ -23,6 +23,19 @@
// is compiled.
// ----------------------------------------------------------------------------
+
+// GTMHTTPFetcher will support logging by default but only hook its input
+// stream support for logging when requested. You can control the inclusion of
+// the code by providing your own definitions for these w/in a prefix header.
+//
+#ifndef GTM_HTTPFETCHER_ENABLE_LOGGING
+# define GTM_HTTPFETCHER_ENABLE_LOGGING 1
+#endif // GTM_HTTPFETCHER_ENABLE_LOGGING
+#ifndef GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING
+# define GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING 0
+#endif // GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING
+
+
// _GTMDevLog & _GTMDevAssert
//
// _GTMDevLog & _GTMDevAssert are meant to be a very lightweight shell for
@@ -89,3 +102,37 @@
#define GTM_MACOS_SDK 1
#endif
+// To simplify support for 64bit (and Leopard in general), we provide the type
+// defines for non Leopard SDKs
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
+ // NSInteger/NSUInteger and Max/Mins
+ #ifndef NSINTEGER_DEFINED
+ #if __LP64__ || NS_BUILD_32_LIKE_64
+ typedef long NSInteger;
+ typedef unsigned long NSUInteger;
+ #else
+ typedef int NSInteger;
+ typedef unsigned int NSUInteger;
+ #endif
+ #define NSIntegerMax LONG_MAX
+ #define NSIntegerMin LONG_MIN
+ #define NSUIntegerMax ULONG_MAX
+ #define NSINTEGER_DEFINED 1
+ #endif // NSINTEGER_DEFINED
+ // CGFloat
+ #ifndef CGFLOAT_DEFINED
+ #if defined(__LP64__) && __LP64__
+ // This really is an untested path (64bit on Tiger?)
+ typedef double CGFloat;
+ #define CGFLOAT_MIN DBL_MIN
+ #define CGFLOAT_MAX DBL_MAX
+ #define CGFLOAT_IS_DOUBLE 1
+ #else /* !defined(__LP64__) || !__LP64__ */
+ typedef float CGFloat;
+ #define CGFLOAT_MIN FLT_MIN
+ #define CGFLOAT_MAX FLT_MAX
+ #define CGFLOAT_IS_DOUBLE 0
+ #endif /* !defined(__LP64__) || !__LP64__ */
+ #define CGFLOAT_DEFINED 1
+ #endif // CGFLOAT_DEFINED
+#endif // MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
diff --git a/GTMiPhone.xcodeproj/project.pbxproj b/GTMiPhone.xcodeproj/project.pbxproj
index aa2f713..0baf1b8 100644
--- a/GTMiPhone.xcodeproj/project.pbxproj
+++ b/GTMiPhone.xcodeproj/project.pbxproj
@@ -6,6 +6,20 @@
objectVersion = 45;
objects = {
+/* Begin PBXAggregateTarget section */
+ F4C7F9BF0DC62EC8009BEE5B /* All UnitTests */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = F4C7F9C50DC62F0C009BEE5B /* Build configuration list for PBXAggregateTarget "All UnitTests" */;
+ buildPhases = (
+ );
+ dependencies = (
+ F4C7F9C40DC62ECD009BEE5B /* PBXTargetDependency */,
+ );
+ name = "All UnitTests";
+ productName = "All UnitTests";
+ };
+/* End PBXAggregateTarget section */
+
/* Begin PBXBuildFile section */
1D3623EC0D0F72F000981E51 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D3623EB0D0F72F000981E51 /* CoreGraphics.framework */; };
1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; };
@@ -37,12 +51,24 @@
8BC0486C0DAE928A00C2D1CA /* GTMUIViewUnitTestingTest.png in Resources */ = {isa = PBXBuildFile; fileRef = 8BC048010DAE928A00C2D1CA /* GTMUIViewUnitTestingTest.png */; };
8BC04A720DAF144700C2D1CA /* GTMSystemVersionTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC04A710DAF144700C2D1CA /* GTMSystemVersionTest.m */; };
8BC04A750DAF145200C2D1CA /* GTMSystemVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC04A740DAF145200C2D1CA /* GTMSystemVersion.m */; };
- 8BC04C470DAFFCF300C2D1CA /* GTMNSString+Utilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC04C450DAFFCF300C2D1CA /* GTMNSString+Utilities.m */; };
- 8BC04C480DAFFCF300C2D1CA /* GTMNSString+UtilitiesTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC04C460DAFFCF300C2D1CA /* GTMNSString+UtilitiesTest.m */; };
8BC04D480DB0088500C2D1CA /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BC04D470DB0088500C2D1CA /* libz.dylib */; };
8BC04DE80DB023D400C2D1CA /* ReleaseNotes.txt in Resources */ = {isa = PBXBuildFile; fileRef = 8BC04DE70DB023D400C2D1CA /* ReleaseNotes.txt */; };
+ F439ADEB0DBD3C0000BE9B91 /* GTMBase64.m in Sources */ = {isa = PBXBuildFile; fileRef = F439ADE90DBD3C0000BE9B91 /* GTMBase64.m */; };
+ F439ADEC0DBD3C0000BE9B91 /* GTMBase64Test.m in Sources */ = {isa = PBXBuildFile; fileRef = F439ADEA0DBD3C0000BE9B91 /* GTMBase64Test.m */; };
+ F439ADF00DBD3C4000BE9B91 /* GTMGeometryUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = F439ADEE0DBD3C4000BE9B91 /* GTMGeometryUtils.m */; };
+ F439ADF10DBD3C4000BE9B91 /* GTMGeometryUtilsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F439ADEF0DBD3C4000BE9B91 /* GTMGeometryUtilsTest.m */; };
/* End PBXBuildFile section */
+/* Begin PBXContainerItemProxy section */
+ F4C7F9C30DC62ECD009BEE5B /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 1D6058900D05DD3D006BFB54 /* GTMiPhoneUnitTesting */;
+ remoteInfo = GTMiPhoneUnitTesting;
+ };
+/* End PBXContainerItemProxy section */
+
/* Begin PBXFileReference section */
1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
1D3623EB0D0F72F000981E51 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
@@ -99,11 +125,14 @@
8BC04A6F0DAF144200C2D1CA /* GTMSystemVersion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMSystemVersion.h; sourceTree = "<group>"; };
8BC04A710DAF144700C2D1CA /* GTMSystemVersionTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMSystemVersionTest.m; sourceTree = "<group>"; };
8BC04A740DAF145200C2D1CA /* GTMSystemVersion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMSystemVersion.m; sourceTree = "<group>"; };
- 8BC04C440DAFFCF300C2D1CA /* GTMNSString+Utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSString+Utilities.h"; sourceTree = "<group>"; };
- 8BC04C450DAFFCF300C2D1CA /* GTMNSString+Utilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+Utilities.m"; sourceTree = "<group>"; };
- 8BC04C460DAFFCF300C2D1CA /* GTMNSString+UtilitiesTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+UtilitiesTest.m"; sourceTree = "<group>"; };
8BC04D470DB0088500C2D1CA /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
8BC04DE70DB023D400C2D1CA /* ReleaseNotes.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ReleaseNotes.txt; sourceTree = "<group>"; };
+ F439ADE80DBD3C0000BE9B91 /* GTMBase64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMBase64.h; sourceTree = "<group>"; };
+ F439ADE90DBD3C0000BE9B91 /* GTMBase64.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMBase64.m; sourceTree = "<group>"; };
+ F439ADEA0DBD3C0000BE9B91 /* GTMBase64Test.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMBase64Test.m; sourceTree = "<group>"; };
+ F439ADED0DBD3C4000BE9B91 /* GTMGeometryUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMGeometryUtils.h; sourceTree = "<group>"; };
+ F439ADEE0DBD3C4000BE9B91 /* GTMGeometryUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMGeometryUtils.m; sourceTree = "<group>"; };
+ F439ADEF0DBD3C4000BE9B91 /* GTMGeometryUtilsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMGeometryUtilsTest.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -134,13 +163,13 @@
isa = PBXGroup;
children = (
8BC04DE70DB023D400C2D1CA /* ReleaseNotes.txt */,
- 8BC049840DAEC59100C2D1CA /* XcodeConfig */,
- 8B308AF40DAD070C00183556 /* RunIPhoneUnitTest.sh */,
8BC047750DAE926E00C2D1CA /* GTMDefines.h */,
8BC047760DAE928A00C2D1CA /* Foundation */,
8BC0479A0DAE928A00C2D1CA /* DebugUtils */,
8BC0479F0DAE928A00C2D1CA /* UnitTesting */,
+ 8BC049840DAEC59100C2D1CA /* XcodeConfig */,
32CA4F630368D1EE00C91783 /* GTMiPhone_Prefix.pch */,
+ 8B308AF40DAD070C00183556 /* RunIPhoneUnitTest.sh */,
29B97323FDCFA39411CA2CEA /* Frameworks */,
19C28FACFE9D520D11CA2CBB /* Products */,
);
@@ -162,16 +191,19 @@
8BC047760DAE928A00C2D1CA /* Foundation */ = {
isa = PBXGroup;
children = (
+ F439ADE80DBD3C0000BE9B91 /* GTMBase64.h */,
+ F439ADE90DBD3C0000BE9B91 /* GTMBase64.m */,
+ F439ADEA0DBD3C0000BE9B91 /* GTMBase64Test.m */,
8BC047770DAE928A00C2D1CA /* GTMCalculatedRange.h */,
8BC047780DAE928A00C2D1CA /* GTMCalculatedRange.m */,
8BC047790DAE928A00C2D1CA /* GTMCalculatedRangeTest.m */,
8BC0477A0DAE928A00C2D1CA /* GTMGarbageCollection.h */,
+ F439ADED0DBD3C4000BE9B91 /* GTMGeometryUtils.h */,
+ F439ADEE0DBD3C4000BE9B91 /* GTMGeometryUtils.m */,
+ F439ADEF0DBD3C4000BE9B91 /* GTMGeometryUtilsTest.m */,
8BC0477E0DAE928A00C2D1CA /* GTMNSData+zlib.h */,
8BC0477F0DAE928A00C2D1CA /* GTMNSData+zlib.m */,
8BC047800DAE928A00C2D1CA /* GTMNSData+zlibTest.m */,
- 8BC04C440DAFFCF300C2D1CA /* GTMNSString+Utilities.h */,
- 8BC04C450DAFFCF300C2D1CA /* GTMNSString+Utilities.m */,
- 8BC04C460DAFFCF300C2D1CA /* GTMNSString+UtilitiesTest.m */,
8BC047810DAE928A00C2D1CA /* GTMNSEnumerator+Filter.h */,
8BC047820DAE928A00C2D1CA /* GTMNSEnumerator+Filter.m */,
8BC047830DAE928A00C2D1CA /* GTMNSEnumerator+FilterTest.m */,
@@ -220,9 +252,9 @@
8BC047ED0DAE928A00C2D1CA /* GTMNSObject+UnitTesting.m */,
8BC047F60DAE928A00C2D1CA /* GTMSenTestCase.h */,
8BC047F70DAE928A00C2D1CA /* GTMSenTestCase.m */,
- 8BC048000DAE928A00C2D1CA /* GTMUIViewUnitTestingTest.gtmUTState */,
8BC048010DAE928A00C2D1CA /* GTMUIViewUnitTestingTest.png */,
8BC0480E0DAE928A00C2D1CA /* RunIPhoneUnitTest.sh */,
+ F435E49F0DC8F5290069CDE8 /* TestData */,
);
path = UnitTesting;
sourceTree = "<group>";
@@ -253,6 +285,14 @@
path = Project;
sourceTree = "<group>";
};
+ F435E49F0DC8F5290069CDE8 /* TestData */ = {
+ isa = PBXGroup;
+ children = (
+ 8BC048000DAE928A00C2D1CA /* GTMUIViewUnitTestingTest.gtmUTState */,
+ );
+ path = TestData;
+ sourceTree = "<group>";
+ };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -286,6 +326,7 @@
projectDirPath = "";
projectRoot = "";
targets = (
+ F4C7F9BF0DC62EC8009BEE5B /* All UnitTests */,
1D6058900D05DD3D006BFB54 /* GTMiPhoneUnitTesting */,
);
};
@@ -347,15 +388,25 @@
8BC048650DAE928A00C2D1CA /* GTMSenTestCase.m in Sources */,
8BC04A720DAF144700C2D1CA /* GTMSystemVersionTest.m in Sources */,
8BC04A750DAF145200C2D1CA /* GTMSystemVersion.m in Sources */,
- 8BC04C470DAFFCF300C2D1CA /* GTMNSString+Utilities.m in Sources */,
- 8BC04C480DAFFCF300C2D1CA /* GTMNSString+UtilitiesTest.m in Sources */,
8B5547CA0DB3BBF20014CC1C /* GTMUIKit+UnitTesting.m in Sources */,
8B5547CB0DB3BBF20014CC1C /* GTMUIKit+UnitTestingTest.m in Sources */,
+ F439ADEB0DBD3C0000BE9B91 /* GTMBase64.m in Sources */,
+ F439ADEC0DBD3C0000BE9B91 /* GTMBase64Test.m in Sources */,
+ F439ADF00DBD3C4000BE9B91 /* GTMGeometryUtils.m in Sources */,
+ F439ADF10DBD3C4000BE9B91 /* GTMGeometryUtilsTest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
+/* Begin PBXTargetDependency section */
+ F4C7F9C40DC62ECD009BEE5B /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 1D6058900D05DD3D006BFB54 /* GTMiPhoneUnitTesting */;
+ targetProxy = F4C7F9C30DC62ECD009BEE5B /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
/* Begin XCBuildConfiguration section */
1D6058940D05DD3E006BFB54 /* Debug */ = {
isa = XCBuildConfiguration;
@@ -407,6 +458,27 @@
};
name = Release;
};
+ F4C7F9C00DC62EC8009BEE5B /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "All UnitTests";
+ };
+ name = Debug;
+ };
+ F4C7F9C10DC62EC8009BEE5B /* Debug-gcov */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "All UnitTests";
+ };
+ name = "Debug-gcov";
+ };
+ F4C7F9C20DC62EC8009BEE5B /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "All UnitTests";
+ };
+ name = Release;
+ };
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@@ -430,6 +502,16 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ F4C7F9C50DC62F0C009BEE5B /* Build configuration list for PBXAggregateTarget "All UnitTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F4C7F9C00DC62EC8009BEE5B /* Debug */,
+ F4C7F9C10DC62EC8009BEE5B /* Debug-gcov */,
+ F4C7F9C20DC62EC8009BEE5B /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
/* End XCConfigurationList section */
};
rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt
index f86bfe6..29cf35a 100644
--- a/ReleaseNotes.txt
+++ b/ReleaseNotes.txt
@@ -7,7 +7,10 @@ Discussion group: http://groups.google.com/group/google-toolbox-for-mac
Release ?.?.?
Changes since 1.0.0
-- Updated the project so Xcode 3 is also happy.
+- Updated the project to Xcode 3. This is the only supported Xcode version
+ for the project. The code can build against the Tiger or Leopard SDKs, and
+ developers can pull individual files into a Xcode 2.x project and things
+ should work just fine.
- Fixed up the prefix header of the project and prefix handing in the Unittest
Xcode Config. (thanks schafdog)
@@ -84,6 +87,21 @@ Changes since 1.0.0
https://connect.apple.com/cgi-bin/WebObjects/MemberSite.woa/wa/getSoftware?bundleID=19915
+- Remove NSColor+Theme and NSWorkspace+Theme as they are no longer needed for
+ testing things for unittests, instead GTMUnitTestingUtilities.m(Lines 64-79)
+ force the user settable things to ensure tests are consistent.
+
+- Added GTMBase64.
+
+- Added GTMHTTPFetcher and GTMProgressMonitorInputStream.
+
+- Moved the data files for unittests into subdirectories call TestData to
+ help make it a little easier to find files w/in the main directories.
+
+- GTMDelegatingTableColumn get an overhaul to match the 10.5 sdk so it's closer
+ to a dropin for previous sdks.
+
+
Release 1.0.0
14-January-2008
diff --git a/UnitTesting/GTMAppKit+UnitTesting.m b/UnitTesting/GTMAppKit+UnitTesting.m
index 85f03a2..6cb5a8b 100644
--- a/UnitTesting/GTMAppKit+UnitTesting.m
+++ b/UnitTesting/GTMAppKit+UnitTesting.m
@@ -23,21 +23,34 @@
#import "GTMGeometryUtils.h"
#import "GTMMethodCheck.h"
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
+ #define ENCODE_NSINTEGER(coder, i, key) [(coder) encodeInt:(i) forKey:(key)]
+#else
+ #define ENCODE_NSINTEGER(coder, i, key) [(coder) encodeInteger:(i) forKey:(key)]
+#endif
+
@implementation NSApplication (GMUnitTestingAdditions)
GTM_METHOD_CHECK(NSObject, gtm_unitTestEncodeState:); // COV_NF_LINE
- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
[super gtm_unitTestEncodeState:inCoder];
- [inCoder encodeInt:[[self mainWindow] windowNumber]
- forKey:@"ApplicationMainWindow"];
-
+ ENCODE_NSINTEGER(inCoder, [[self mainWindow] windowNumber], @"ApplicationMainWindow");
+
// Descend down into the windows allowing them to store their state
NSEnumerator *windowEnum = [[self windows] objectEnumerator];
NSWindow *window = nil;
int i = 0;
while ((window = [windowEnum nextObject])) {
- [inCoder encodeObject:window forKey:[NSString stringWithFormat:@"Window %d", i]];
- i = i + 1;
+ if ([window isVisible]) {
+ // Only record visible windows because invisible windows may be closing on us
+ // This appears to happen differently in 64 bit vs 32 bit, and items
+ // in the window may hold an extra retain count for a while until the
+ // event loop is spun. To avoid all this, we just don't record non
+ // visible windows.
+ // See rdar://5851458 for details.
+ [inCoder encodeObject:window forKey:[NSString stringWithFormat:@"Window %d", i]];
+ i = i + 1;
+ }
}
// and encode the menu bar
@@ -81,7 +94,7 @@ GTM_METHOD_CHECK(NSObject, gtm_unitTestEncodeState:); // COV_NF_LINE
[inCoder encodeObject:[self class] forKey:@"ControlType"];
[inCoder encodeObject:[self objectValue] forKey:@"ControlValue"];
[inCoder encodeObject:[self selectedCell] forKey:@"ControlSelectedCell"];
- [inCoder encodeInt:[self tag] forKey:@"ControlTag"];
+ ENCODE_NSINTEGER(inCoder, [self tag], @"ControlTag");
[inCoder encodeBool:[self isEnabled] forKey:@"ControlIsEnabled"];
}
@@ -117,8 +130,8 @@ GTM_METHOD_CHECK(NSObject, gtm_unitTestEncodeState:); // COV_NF_LINE
// is going to be in the CellValue encoding.
[inCoder encodeObject:[self title] forKey:@"CellTitle"];
}
- [inCoder encodeInt:[self state] forKey:@"CellState"];
- [inCoder encodeInt:[self tag] forKey:@"CellTag"];
+ ENCODE_NSINTEGER(inCoder, [self state], @"CellState");
+ ENCODE_NSINTEGER(inCoder, [self tag], @"CellTag");
}
@end
@@ -163,8 +176,19 @@ GTM_METHOD_CHECK(NSObject, gtm_unitTestEncodeState:); // COV_NF_LINE
// inCoder - the coder to encode our state into
- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
[super gtm_unitTestEncodeState:inCoder];
- [inCoder encodeObject:[self title] forKey:@"MenuTitle"];
-
+ // Hack here to work around
+ // rdar://5881796 Application menu item title wrong when accessed programatically
+ // which causes us to have different results on x86_64 vs x386.
+ // Hack is braced intentionally. We don't record the title of the
+ // "application" menu or it's menu title because they are wrong on 32 bit.
+ // They appear to work right on 64bit.
+ {
+ NSMenu *mainMenu = [NSApp mainMenu];
+ NSMenu *appleMenu = [[mainMenu itemAtIndex:0] submenu];
+ if (![self isEqual:appleMenu]) {
+ [inCoder encodeObject:[self title] forKey:@"MenuTitle"];
+ }
+ }
// Descend down into the menuitems allowing them to store their state
NSEnumerator *menuItemEnum = [[self itemArray] objectEnumerator];
NSMenuItem *menuItem = nil;
@@ -179,15 +203,25 @@ GTM_METHOD_CHECK(NSObject, gtm_unitTestEncodeState:); // COV_NF_LINE
- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
[super gtm_unitTestEncodeState:inCoder];
- [inCoder encodeObject:[self title] forKey:@"MenuItemTitle"];
+ // Hack here to work around
+ // rdar://5881796 Application menu item title wrong when accessed programatically
+ // which causes us to have different results on x86_64 vs x386.
+ // See comment above.
+ {
+ NSMenu *mainMenu = [NSApp mainMenu];
+ NSMenuItem *appleMenuItem = [mainMenu itemAtIndex:0];
+ if (![self isEqual:appleMenuItem]) {
+ [inCoder encodeObject:[self title] forKey:@"MenuItemTitle"];
+ }
+ }
[inCoder encodeObject:[self keyEquivalent] forKey:@"MenuItemKeyEquivalent"];
[inCoder encodeBool:[self isSeparatorItem] forKey:@"MenuItemIsSeparator"];
- [inCoder encodeInt:[self state] forKey:@"MenuItemState"];
+ ENCODE_NSINTEGER(inCoder, [self state], @"MenuItemState");
[inCoder encodeBool:[self isEnabled] forKey:@"MenuItemIsEnabled"];
[inCoder encodeBool:[self isAlternate] forKey:@"MenuItemIsAlternate"];
[inCoder encodeObject:[self toolTip] forKey:@"MenuItemTooltip"];
- [inCoder encodeInt:[self tag] forKey:@"MenuItemTag"];
- [inCoder encodeInt:[self indentationLevel] forKey:@"MenuItemIndentationLevel"];
+ ENCODE_NSINTEGER(inCoder, [self tag], @"MenuItemTag");
+ ENCODE_NSINTEGER(inCoder, [self indentationLevel], @"MenuItemIndentationLevel");
// Do our submenu if neccessary
if ([self hasSubmenu]) {
@@ -221,7 +255,7 @@ GTM_METHOD_CHECK(NSObject, gtm_unitTestEncodeState:); // COV_NF_LINE
return self;
}
-- (void) dealloc {
+- (void)dealloc {
[drawer_ release];
[super dealloc];
}
diff --git a/UnitTesting/GTMIPhoneUnitTestMain.m b/UnitTesting/GTMIPhoneUnitTestMain.m
index 2af060b..16a3907 100644
--- a/UnitTesting/GTMIPhoneUnitTestMain.m
+++ b/UnitTesting/GTMIPhoneUnitTestMain.m
@@ -43,11 +43,11 @@ static int MethodSort(const void *a, const void *b) {
@implementation GTMIPhoneUnitTestDelegate
// Return YES if class is subclass (1 or more generations) of SenTestCase
-- (BOOL)isTestFixture:(Class)class {
+- (BOOL)isTestFixture:(Class)aClass {
BOOL iscase = NO;
Class testCaseClass = [SenTestCase class];
Class superclass;
- for (superclass = class;
+ for (superclass = aClass;
!iscase && superclass;
superclass = class_getSuperclass(superclass)) {
iscase = superclass == testCaseClass ? YES : NO;
diff --git a/UnitTesting/GTMNSObject+BindingUnitTesting.m b/UnitTesting/GTMNSObject+BindingUnitTesting.m
index 03d723d..7773542 100644
--- a/UnitTesting/GTMNSObject+BindingUnitTesting.m
+++ b/UnitTesting/GTMNSObject+BindingUnitTesting.m
@@ -306,11 +306,14 @@ BOOL GTMDoExposedBindingsFunctionCorrectly(NSObject *object,
- (NSMutableDictionary*)gtm_unitTestExposedBindingsTestValues:(NSString*)binding {
NSMutableDictionary *dict = [super gtm_unitTestExposedBindingsTestValues:binding];
+#if !__LP64__
if ([binding isEqualToString:NSAlignmentBinding]) {
// rdar://5851491 - Setting NSAlignmentBinding of search field to NSCenterTextAlignment broken
+ // This appears to not be a bug in 64 bit. Strange.
[dict setObject:[NSNumber numberWithInt:NSNaturalTextAlignment]
forKey:[NSNumber numberWithInt:NSCenterTextAlignment]];
}
+#endif
return dict;
}
diff --git a/UnitTesting/GTMNSObject+UnitTesting.m b/UnitTesting/GTMNSObject+UnitTesting.m
index 6aff70c..34aeb58 100644
--- a/UnitTesting/GTMNSObject+UnitTesting.m
+++ b/UnitTesting/GTMNSObject+UnitTesting.m
@@ -202,6 +202,8 @@ BOOL GTMIsObjectStateEqualToStateNamed(id object,
- (NSString *)gtm_saveToPathForFileNamed:(NSString*)name
extension:(NSString*)extension;
- (CGImageRef)gtm_createUnitTestImage;
+// Returns nil if there is no override
+- (NSString *)gtm_getOverrideDefaultUnitTestSaveToDirectory;
@end
// This is a keyed coder for storing unit test state data. It is used only by
@@ -388,12 +390,17 @@ static NSString *gGTMUnitTestSaveToDirectory = nil;
stringByDeletingLastPathComponent]
stringByDeletingLastPathComponent]
stringByAppendingPathComponent:@"Desktop"];
-#else
+#else
NSArray *desktopDirs = NSSearchPathForDirectoriesInDomains(NSDesktopDirectory,
NSUserDomainMask,
YES);
gGTMUnitTestSaveToDirectory = [desktopDirs objectAtIndex:0];
#endif
+ // Did we get overridden?
+ NSString *override = [self gtm_getOverrideDefaultUnitTestSaveToDirectory];
+ if (override) {
+ gGTMUnitTestSaveToDirectory = override;
+ }
[gGTMUnitTestSaveToDirectory retain];
}
result = gGTMUnitTestSaveToDirectory;
@@ -402,6 +409,29 @@ static NSString *gGTMUnitTestSaveToDirectory = nil;
return result;
}
+// Return nil if there is no override
+- (NSString *)gtm_getOverrideDefaultUnitTestSaveToDirectory {
+ NSString *result = nil;
+
+ // If we have an environment variable that ends in "BUILD_NUMBER" odds are
+ // we're on an automated build system, so use the build products dir as an
+ // override instead of writing on the desktop.
+ NSDictionary *env = [[NSProcessInfo processInfo] environment];
+ NSEnumerator *enumerator = [env keyEnumerator];
+ NSString *key = nil;
+ while (((key = [enumerator nextObject]) != nil) &&
+ ![key hasSuffix:@"BUILD_NUMBER"])
+ ;
+ if (key) {
+ result = [env objectForKey:@"BUILT_PRODUCTS_DIR"];
+ }
+
+ if (result && [result length] == 0) {
+ result = nil;
+ }
+ return result;
+}
+
/// Find the path for a file named name.extension in your bundle.
// Searches for the following:
// "name.extension",
@@ -443,15 +473,33 @@ static NSString *gGTMUnitTestSaveToDirectory = nil;
systemVersions[2] = [NSString stringWithFormat:@".%d", major];
systemVersions[3] = @"";
NSString *extensions[2];
-#if GTM_IPHONE_SDK
+#if GTM_IPHONE_SDK
extensions[0] = @".iPhone";
-#else
- const NXArchInfo *localInfo = NXGetLocalArchInfo();
- _GTMDevAssert(localInfo && localInfo->name, @"Couldn't get NXArchInfo");
- const NXArchInfo *genericInfo = NXGetArchInfoFromCpuType(localInfo->cputype, 0);
- _GTMDevAssert(genericInfo && genericInfo->name, @"Couldn't get generic NXArchInfo");
- extensions[0] = [NSString stringWithFormat:@".%s", genericInfo->name];
-#endif
+#else // !GTM_IPHONE_SDK
+ // In reading arch(3) you'd thing this would work:
+ //
+ // const NXArchInfo *localInfo = NXGetLocalArchInfo();
+ // _GTMDevAssert(localInfo && localInfo->name, @"Couldn't get NXArchInfo");
+ // const NXArchInfo *genericInfo = NXGetArchInfoFromCpuType(localInfo->cputype, 0);
+ // _GTMDevAssert(genericInfo && genericInfo->name, @"Couldn't get generic NXArchInfo");
+ // extensions[0] = [NSString stringWithFormat:@".%s", genericInfo->name];
+ //
+ // but on 64bit it returns the same things as on 32bit, so...
+#if __POWERPC__
+ #if __LP64__
+ extensions[0] = @".ppc64";
+ #else // !__LP64__
+ extensions[0] = @".ppc";
+ #endif // __LP64__
+#else // !__POWERPC__
+ #if __LP64__
+ extensions[0] = @".x86_64";
+ #else // !__LP64__
+ extensions[0] = @".i386";
+ #endif // __LP64__
+#endif // !__POWERPC__
+
+#endif // GTM_IPHONE_SDK
extensions[1] = @"";
int i,j;
@@ -481,11 +529,29 @@ static NSString *gGTMUnitTestSaveToDirectory = nil;
#if GTM_IPHONE_SDK
system = "iPhone";
#else
- const NXArchInfo *localInfo = NXGetLocalArchInfo();
- _GTMDevAssert(localInfo && localInfo->name, @"Couldn't get NXArchInfo");
- const NXArchInfo *genericInfo = NXGetArchInfoFromCpuType(localInfo->cputype, 0);
- _GTMDevAssert(genericInfo && genericInfo->name, @"Couldn't get generic NXArchInfo");
- system = genericInfo->name;
+ // In reading arch(3) you'd thing this would work:
+ //
+ // const NXArchInfo *localInfo = NXGetLocalArchInfo();
+ // _GTMDevAssert(localInfo && localInfo->name, @"Couldn't get NXArchInfo");
+ // const NXArchInfo *genericInfo = NXGetArchInfoFromCpuType(localInfo->cputype, 0);
+ // _GTMDevAssert(genericInfo && genericInfo->name, @"Couldn't get generic NXArchInfo");
+ // system = genericInfo->name;
+ //
+ // but on 64bit it returns the same things as on 32bit, so...
+#if __POWERPC__
+ #if __LP64__
+ system = "ppc64";
+ #else // !__LP64__
+ system = "ppc";
+ #endif // __LP64__
+#else // !__POWERPC__
+ #if __LP64__
+ system = "x86_64";
+ #else // !__LP64__
+ system = "i386";
+ #endif // __LP64__
+#endif // !__POWERPC__
+
#endif
long major, minor, bugFix;
[GTMSystemVersion getMajor:&major minor:&minor bugFix:&bugFix];
@@ -530,7 +596,7 @@ static NSString *gGTMUnitTestSaveToDirectory = nil;
_GTMDevAssert(cs, @"Couldn't create colorspace");
CGBitmapInfo info = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault;
if (data) {
- *data = calloc(bytesPerRow, height);
+ *data = (unsigned char*)calloc(bytesPerRow, height);
_GTMDevAssert(*data, @"Couldn't create bitmap");
}
context = CGBitmapContextCreate(data ? *data : NULL, width, height,
@@ -618,7 +684,7 @@ static NSString *gGTMUnitTestSaveToDirectory = nil;
NSDictionary *tiffDict = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:NSTIFFCompressionLZW]
forKey:(NSString*)kCGImagePropertyTIFFCompression];
NSDictionary *destProps = [NSDictionary dictionaryWithObjectsAndKeys:
- [NSNumber numberWithFloat:1.0],
+ [NSNumber numberWithFloat:1.0f],
(NSString*)kCGImageDestinationLossyCompressionQuality,
tiffDict,
(NSString*)kCGImagePropertyTIFFDictionary,
@@ -756,12 +822,12 @@ static NSString *gGTMUnitTestSaveToDirectory = nil;
_GTMDevAssert(diffContext, @"Can't make diff context");
size_t diffRowBytes = CGBitmapContextGetBytesPerRow(diffContext);
for (row = 0; row < imageHeight; row++) {
- unsigned long *imageRow = (unsigned long*)(imageData + imageBytesPerRow * row);
- unsigned long *fileRow = (unsigned long*)(fileData + fileBytesPerRow * row);
- unsigned long* diffRow = (unsigned long*)(diffData + diffRowBytes * row);
+ uint32_t *imageRow = (uint32_t*)(imageData + imageBytesPerRow * row);
+ uint32_t *fileRow = (uint32_t*)(fileData + fileBytesPerRow * row);
+ uint32_t* diffRow = (uint32_t*)(diffData + diffRowBytes * row);
for (col = 0; col < imageWidth; col++) {
- unsigned long imageColor = imageRow[col];
- unsigned long fileColor = fileRow[col];
+ uint32_t imageColor = imageRow[col];
+ uint32_t fileColor = fileRow[col];
unsigned char imageAlpha = imageColor & 0xF;
unsigned char imageBlue = imageColor >> 8 & 0xF;
@@ -783,12 +849,12 @@ static NSString *gGTMUnitTestSaveToDirectory = nil;
almostEqual(imageAlpha, fileAlpha);
answer &= equal;
if (diff) {
- unsigned long newColor;
+ uint32_t newColor;
if (equal) {
- newColor = (((unsigned long)imageRed) << 24) +
- (((unsigned long)imageGreen) << 16) +
- (((unsigned long)imageBlue) << 8) +
- (((unsigned long)imageAlpha) / 2);
+ newColor = (((uint32_t)imageRed) << 24) +
+ (((uint32_t)imageGreen) << 16) +
+ (((uint32_t)imageBlue) << 8) +
+ (((uint32_t)imageAlpha) / 2);
} else {
newColor = 0xFF0000FF;
}
diff --git a/UnitTesting/GTMUnitTestingBindingTest.m b/UnitTesting/GTMUnitTestingBindingTest.m
index c0ef93c..d27e8e7 100644
--- a/UnitTesting/GTMUnitTestingBindingTest.m
+++ b/UnitTesting/GTMUnitTestingBindingTest.m
@@ -48,6 +48,7 @@
NSWindow *window = [testWindowController window];
GTMTestExposedBindings(window, @"Window failed binding test");
[self doSubviewBindingTest:[window contentView]];
+ [window close];
[testWindowController release];
// Run a test against something with no bindings.
diff --git a/UnitTesting/GTMUnitTestingTest.m b/UnitTesting/GTMUnitTestingTest.m
index f152428..cc28a1a 100644
--- a/UnitTesting/GTMUnitTestingTest.m
+++ b/UnitTesting/GTMUnitTestingTest.m
@@ -204,18 +204,6 @@ NSString *const kGTMWindowSaveFileName = @"GTMUnitTestingWindow";
return field_;
}
-- (void)dealloc {
- NSWindow *window = [self window];
- int count = [window retainCount];
-
- // Spinning the run loop here to get rid of the window. Stupid issue
- // where there's a delayed selector holding a retain count on our window
- // rdar://5851458 - Closing a window with a NSTextView in it should get rid of it immediately
- while (count == [window retainCount]) {
- [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
- }
- [super dealloc];
-}
@end
@implementation GTMUnitTestingDelegate
diff --git a/UnitTesting/GTMUnitTestingUtilities.m b/UnitTesting/GTMUnitTestingUtilities.m
index 7c1b915..a37b7dd 100644
--- a/UnitTesting/GTMUnitTestingUtilities.m
+++ b/UnitTesting/GTMUnitTestingUtilities.m
@@ -54,8 +54,8 @@ static void RestoreColorProfile(void);
// Sets up the user interface so that we can run consistent UI unittests on it.
+ (void)setUpForUIUnitTests {
// Give some names to undocumented defaults values
- static const int MediumFontSmoothing = 2;
- static const int BlueTintedAppearance = 1;
+ const NSInteger MediumFontSmoothing = 2;
+ const NSInteger BlueTintedAppearance = 1;
// This sets up some basic values that we want as our defaults for doing pixel
// based user interface tests. These defaults only apply to the unit test app,
@@ -78,7 +78,7 @@ static void RestoreColorProfile(void);
// Use english plz
[defaults setObject:[NSArray arrayWithObject:@"en"] forKey:@"AppleLanguages"];
// How fast should we draw sheets. This speeds up the sheet tests considerably
- [defaults setFloat:.001 forKey:@"NSWindowResizeTime"];
+ [defaults setFloat:.001f forKey:@"NSWindowResizeTime"];
// Switch over the screen profile to "generic rgb". This installs an
// atexit handler to return our profile back when we are done.
SetColorProfileToGenericRGB();
@@ -111,7 +111,7 @@ BOOL AreCMProfilesEqual(CMProfileRef a, CMProfileRef b) {
static void RestoreColorProfile(void) {
if (gCurrentColorProfile) {
CGDirectDisplayID displayID = CGMainDisplayID();
- int error = CMSetProfileByAVID((UInt32)displayID, gCurrentColorProfile);
+ CMError error = CMSetProfileByAVID((UInt32)displayID, gCurrentColorProfile);
if (error) {
// COV_NF_START
// No way to force this case in a unittest.
diff --git a/UnitTesting/GTMUIViewUnitTestingTest.gtmUTState b/UnitTesting/TestData/GTMUIViewUnitTestingTest.gtmUTState
index 87ae9e5..87ae9e5 100644
--- a/UnitTesting/GTMUIViewUnitTestingTest.gtmUTState
+++ b/UnitTesting/TestData/GTMUIViewUnitTestingTest.gtmUTState
diff --git a/UnitTesting/GTMUIViewUnitTestingTest.png b/UnitTesting/TestData/GTMUIViewUnitTestingTest.png
index 03fd9f0..03fd9f0 100644
--- a/UnitTesting/GTMUIViewUnitTestingTest.png
+++ b/UnitTesting/TestData/GTMUIViewUnitTestingTest.png
Binary files differ
diff --git a/UnitTesting/GTMUnitTestingImage.gtmUTState b/UnitTesting/TestData/GTMUnitTestingImage.gtmUTState
index 969ddf6..969ddf6 100644
--- a/UnitTesting/GTMUnitTestingImage.gtmUTState
+++ b/UnitTesting/TestData/GTMUnitTestingImage.gtmUTState
diff --git a/UnitTesting/GTMUnitTestingImage.tiff b/UnitTesting/TestData/GTMUnitTestingImage.tiff
index 4d08297..4d08297 100644
--- a/UnitTesting/GTMUnitTestingImage.tiff
+++ b/UnitTesting/TestData/GTMUnitTestingImage.tiff
Binary files differ
diff --git a/UnitTesting/GTMUnitTestingTest.nib/classes.nib b/UnitTesting/TestData/GTMUnitTestingTest.nib/classes.nib
index cc51f67..cc51f67 100644
--- a/UnitTesting/GTMUnitTestingTest.nib/classes.nib
+++ b/UnitTesting/TestData/GTMUnitTestingTest.nib/classes.nib
diff --git a/UnitTesting/GTMUnitTestingTest.nib/info.nib b/UnitTesting/TestData/GTMUnitTestingTest.nib/info.nib
index 3fb1618..3fb1618 100644
--- a/UnitTesting/GTMUnitTestingTest.nib/info.nib
+++ b/UnitTesting/TestData/GTMUnitTestingTest.nib/info.nib
diff --git a/UnitTesting/GTMUnitTestingTest.nib/keyedobjects.nib b/UnitTesting/TestData/GTMUnitTestingTest.nib/keyedobjects.nib
index d3104e2..d3104e2 100644
--- a/UnitTesting/GTMUnitTestingTest.nib/keyedobjects.nib
+++ b/UnitTesting/TestData/GTMUnitTestingTest.nib/keyedobjects.nib
Binary files differ
diff --git a/UnitTesting/GTMUnitTestingTestApp.gtmUTState b/UnitTesting/TestData/GTMUnitTestingTestApp.gtmUTState
index 1b7c4e5..03e611c 100644
--- a/UnitTesting/GTMUnitTestingTestApp.gtmUTState
+++ b/UnitTesting/TestData/GTMUnitTestingTestApp.gtmUTState
@@ -256,13 +256,9 @@
<key>MenuItemTitle</key>
<string></string>
</dict>
- <key>MenuTitle</key>
- <string>Apple</string>
</dict>
<key>MenuItemTag</key>
<integer>0</integer>
- <key>MenuItemTitle</key>
- <string></string>
</dict>
<key>MenuItem 1</key>
<dict>
@@ -1482,290 +1478,6 @@
<key>CellTag</key>
<integer>0</integer>
<key>CellTitle</key>
- <string>Foo</string>
- <key>CellValue</key>
- <string>Foo</string>
- </dict>
- <key>ControlTag</key>
- <integer>0</integer>
- <key>ControlType</key>
- <string>NSTextField</string>
- <key>ControlValue</key>
- <string>Foo</string>
- <key>ViewIsHidden</key>
- <false/>
- </dict>
- <key>ViewSubView 5</key>
- <dict>
- <key>ControlIsEnabled</key>
- <true/>
- <key>ControlSelectedCell</key>
- <dict>
- <key>CellState</key>
- <integer>1</integer>
- <key>CellTag</key>
- <integer>0</integer>
- <key>CellTitle</key>
- <string>CheckMate!</string>
- <key>CellValue</key>
- <string>1</string>
- </dict>
- <key>ControlTag</key>
- <integer>0</integer>
- <key>ControlType</key>
- <string>NSButton</string>
- <key>ControlValue</key>
- <string>1</string>
- <key>ViewIsHidden</key>
- <false/>
- </dict>
- <key>ViewSubView 6</key>
- <dict>
- <key>ControlIsEnabled</key>
- <true/>
- <key>ControlSelectedCell</key>
- <dict>
- <key>CellState</key>
- <integer>1</integer>
- <key>CellTag</key>
- <integer>0</integer>
- <key>CellTitle</key>
- <string></string>
- <key>CellValue</key>
- <string>50</string>
- </dict>
- <key>ControlTag</key>
- <integer>0</integer>
- <key>ControlType</key>
- <string>NSSlider</string>
- <key>ControlValue</key>
- <string>50</string>
- <key>ViewIsHidden</key>
- <false/>
- </dict>
- <key>ViewSubView 7</key>
- <dict>
- <key>ControlIsEnabled</key>
- <true/>
- <key>ControlSelectedCell</key>
- <dict>
- <key>CellState</key>
- <integer>0</integer>
- <key>CellTag</key>
- <integer>0</integer>
- <key>CellTitle</key>
- <string>Cancel</string>
- <key>CellValue</key>
- <string>0</string>
- </dict>
- <key>ControlTag</key>
- <integer>0</integer>
- <key>ControlType</key>
- <string>NSButton</string>
- <key>ControlValue</key>
- <string>0</string>
- <key>ViewIsHidden</key>
- <false/>
- </dict>
- <key>ViewSubView 8</key>
- <dict>
- <key>ControlIsEnabled</key>
- <true/>
- <key>ControlTag</key>
- <integer>0</integer>
- <key>ControlType</key>
- <string>NSColorWell</string>
- <key>ViewIsHidden</key>
- <false/>
- </dict>
- <key>ViewSubView 9</key>
- <dict>
- <key>ViewIsHidden</key>
- <false/>
- </dict>
- </dict>
- <key>WindowIsMain</key>
- <false/>
- <key>WindowIsVisible</key>
- <false/>
- <key>WindowTitle</key>
- <string>Window</string>
- </dict>
- <key>Window 1</key>
- <dict>
- <key>WindowContent</key>
- <dict>
- <key>ViewIsHidden</key>
- <false/>
- <key>ViewSubView 0</key>
- <dict>
- <key>ViewIsHidden</key>
- <false/>
- <key>ViewSubView 0</key>
- <dict>
- <key>ViewIsHidden</key>
- <false/>
- <key>ViewSubView 0</key>
- <dict>
- <key>ControlIsEnabled</key>
- <true/>
- <key>ControlTag</key>
- <integer>0</integer>
- <key>ControlType</key>
- <string>NSTableView</string>
- <key>ViewIsHidden</key>
- <false/>
- </dict>
- </dict>
- <key>ViewSubView 1</key>
- <dict>
- <key>ControlIsEnabled</key>
- <false/>
- <key>ControlTag</key>
- <integer>0</integer>
- <key>ControlType</key>
- <string>NSScroller</string>
- <key>ViewIsHidden</key>
- <false/>
- </dict>
- <key>ViewSubView 2</key>
- <dict>
- <key>ControlIsEnabled</key>
- <false/>
- <key>ControlTag</key>
- <integer>0</integer>
- <key>ControlType</key>
- <string>NSScroller</string>
- <key>ViewIsHidden</key>
- <false/>
- </dict>
- <key>ViewSubView 3</key>
- <dict>
- <key>ViewIsHidden</key>
- <false/>
- <key>ViewSubView 0</key>
- <dict>
- <key>ViewIsHidden</key>
- <false/>
- </dict>
- </dict>
- <key>ViewSubView 4</key>
- <dict>
- <key>ViewIsHidden</key>
- <false/>
- </dict>
- </dict>
- <key>ViewSubView 1</key>
- <dict>
- <key>ViewIsHidden</key>
- <false/>
- </dict>
- <key>ViewSubView 10</key>
- <dict>
- <key>ViewIsHidden</key>
- <false/>
- <key>ViewSubView 0</key>
- <dict>
- <key>ViewIsHidden</key>
- <false/>
- <key>ViewSubView 0</key>
- <dict>
- <key>ViewIsHidden</key>
- <false/>
- </dict>
- </dict>
- <key>ViewSubView 1</key>
- <dict>
- <key>ControlIsEnabled</key>
- <false/>
- <key>ControlTag</key>
- <integer>0</integer>
- <key>ControlType</key>
- <string>NSScroller</string>
- <key>ViewIsHidden</key>
- <false/>
- </dict>
- <key>ViewSubView 2</key>
- <dict>
- <key>ControlIsEnabled</key>
- <false/>
- <key>ControlTag</key>
- <integer>0</integer>
- <key>ControlType</key>
- <string>NSScroller</string>
- <key>ViewIsHidden</key>
- <false/>
- </dict>
- </dict>
- <key>ViewSubView 11</key>
- <dict>
- <key>ViewIsHidden</key>
- <false/>
- <key>ViewSubView 0</key>
- <dict>
- <key>ViewIsHidden</key>
- <false/>
- </dict>
- </dict>
- <key>ViewSubView 2</key>
- <dict>
- <key>ControlIsEnabled</key>
- <true/>
- <key>ControlSelectedCell</key>
- <dict>
- <key>CellState</key>
- <integer>0</integer>
- <key>CellTag</key>
- <integer>0</integer>
- <key>CellTitle</key>
- <string>HaHa</string>
- <key>CellValue</key>
- <string>HaHa</string>
- </dict>
- <key>ControlTag</key>
- <integer>0</integer>
- <key>ControlType</key>
- <string>NSTextField</string>
- <key>ControlValue</key>
- <string>HaHa</string>
- <key>ViewIsHidden</key>
- <false/>
- </dict>
- <key>ViewSubView 3</key>
- <dict>
- <key>ControlIsEnabled</key>
- <true/>
- <key>ControlSelectedCell</key>
- <dict>
- <key>CellState</key>
- <integer>0</integer>
- <key>CellTag</key>
- <integer>0</integer>
- <key>CellTitle</key>
- <string>Still Haven't Found What I'm Searching For</string>
- <key>CellValue</key>
- <string>Still Haven't Found What I'm Searching For</string>
- </dict>
- <key>ControlTag</key>
- <integer>0</integer>
- <key>ControlType</key>
- <string>NSSearchField</string>
- <key>ControlValue</key>
- <string>Still Haven't Found What I'm Searching For</string>
- <key>ViewIsHidden</key>
- <false/>
- </dict>
- <key>ViewSubView 4</key>
- <dict>
- <key>ControlIsEnabled</key>
- <true/>
- <key>ControlSelectedCell</key>
- <dict>
- <key>CellState</key>
- <integer>1</integer>
- <key>CellTag</key>
- <integer>0</integer>
- <key>CellTitle</key>
<string>Once upon a time</string>
<key>CellValue</key>
<string>Once upon a time</string>
diff --git a/UnitTesting/GTMUnitTestingView.tiff b/UnitTesting/TestData/GTMUnitTestingView.tiff
index 228df73..228df73 100644
--- a/UnitTesting/GTMUnitTestingView.tiff
+++ b/UnitTesting/TestData/GTMUnitTestingView.tiff
Binary files differ
diff --git a/UnitTesting/GTMUnitTestingWindow.gtmUTState b/UnitTesting/TestData/GTMUnitTestingWindow.gtmUTState
index 27dd08e..27dd08e 100644
--- a/UnitTesting/GTMUnitTestingWindow.gtmUTState
+++ b/UnitTesting/TestData/GTMUnitTestingWindow.gtmUTState
diff --git a/UnitTesting/GTMUnitTestingWindow.tiff b/UnitTesting/TestData/GTMUnitTestingWindow.tiff
index 63f5649..63f5649 100644
--- a/UnitTesting/GTMUnitTestingWindow.tiff
+++ b/UnitTesting/TestData/GTMUnitTestingWindow.tiff
Binary files differ
diff --git a/AppKit/GTMNSColor+Theme.h b/XcodeConfig/Target/LoadableBundleGCSupported.xcconfig
index f63a143..861845a 100644
--- a/AppKit/GTMNSColor+Theme.h
+++ b/XcodeConfig/Target/LoadableBundleGCSupported.xcconfig
@@ -1,7 +1,12 @@
//
-// GTMNSColor+Theme.m
+// LoadableBundleGCSupported.xcconfig
//
-// Category for working with Themes and NSColor
+// Xcode configuration file for a loadable bundle that supports garbage
+// collection. Usually a Cocoa plugin or similar.
+//
+// This is a _Target_ config file, for use in the "Based on" popup of the
+// settings dialog for a target. Do not attempt to apply this as the base
+// of an Xcode configuration in the project settings dialog.
//
// Copyright 2006-2008 Google Inc.
//
@@ -18,18 +23,8 @@
// the License.
//
-#import <Cocoa/Cocoa.h>
-#import <Carbon/Carbon.h>
-
-/// Category for working with Themes and NSColor
-@interface NSColor (GTMColorThemeAdditions)
-
-/// Create up an NSColor based on a Theme Text Color.
-/// Colors will be in the CalibratedRGB color space
-+ (id)gtm_colorWithThemeTextColor:(ThemeTextColor)textColor;
-
-/// Create up an NSColor based on a Theme Brush
-/// Colors will be in the CalibratedRGB color space
-+ (id)gtm_colorWithThemeBrush:(ThemeBrush)brush;
+// Include the basic Loadable Bundle config
+#include "LoadableBundle.xcconfig"
-@end
+// Include the GC flag(s)
+#include "../subconfig/GCSupported.xcconfig"
diff --git a/XcodeConfig/Target/SharedLibraryGCSupported.xcconfig b/XcodeConfig/Target/SharedLibraryGCSupported.xcconfig
new file mode 100644
index 0000000..e6b56f7
--- /dev/null
+++ b/XcodeConfig/Target/SharedLibraryGCSupported.xcconfig
@@ -0,0 +1,30 @@
+//
+// SharedLibraryGCSupported.xcconfig
+//
+// Xcode configuration file for a shared library that support garbage
+// collection.
+//
+// This is a _Target_ config file, for use in the "Based on" popup of the
+// settings dialog for a target. Do not attempt to apply this as the base
+// of an Xcode configuration in the project settings dialog.
+//
+// Copyright 2006-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 the basic Shared Library config
+#include "SharedLibrary.xcconfig"
+
+// Include the GC flag(s)
+#include "../subconfig/GCSupported.xcconfig"
diff --git a/XcodeConfig/Target/StaticLibraryGCSupported.xcconfig b/XcodeConfig/Target/StaticLibraryGCSupported.xcconfig
new file mode 100644
index 0000000..6a57a0c
--- /dev/null
+++ b/XcodeConfig/Target/StaticLibraryGCSupported.xcconfig
@@ -0,0 +1,30 @@
+//
+// StaticLibraryGCSupported.xcconfig
+//
+// Xcode configuration file for a static library that supports garbage
+// collection.
+//
+// This is a _Target_ config file, for use in the "Based on" popup of the
+// settings dialog for a target. Do not attempt to apply this as the base
+// of an Xcode configuration in the project settings dialog.
+//
+// Copyright 2006-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 the basic Static Library config
+#include "StaticLibrary.xcconfig"
+
+// Include the GC flag(s)
+#include "../subconfig/GCSupported.xcconfig"
diff --git a/XcodeConfig/subconfig/64bit.xcconfig b/XcodeConfig/subconfig/64bit.xcconfig
new file mode 100644
index 0000000..bb9cae6
--- /dev/null
+++ b/XcodeConfig/subconfig/64bit.xcconfig
@@ -0,0 +1,26 @@
+//
+// 64bit.xcconfig
+//
+// Xcode configuration file to include for builds wanting 64bit support.
+//
+// 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.
+//
+
+// Compile all flavors: 32 & 64bit on both PPC and Intel
+ARCHS = i386 x86_64 ppc ppc64
+
+// Warn on implicit data conversions in 64bit builds
+GCC_WARN_64_TO_32_BIT_CONVERSION[arch=ppc64] = YES
+GCC_WARN_64_TO_32_BIT_CONVERSION[arch=x86_64] = YES
diff --git a/XcodeConfig/subconfig/GCSupported.xcconfig b/XcodeConfig/subconfig/GCSupported.xcconfig
new file mode 100644
index 0000000..74bd027
--- /dev/null
+++ b/XcodeConfig/subconfig/GCSupported.xcconfig
@@ -0,0 +1,23 @@
+//
+// GCSupported.xcconfig
+//
+// Xcode configuration file for making a build Garbage Collection enabled.
+// Use the *GCSupported specific configs in the Target folder.
+//
+// Copyright 2006-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.
+//
+
+// enable garbage collection (but don't require it)
+GCC_ENABLE_OBJC_GC = supported
diff --git a/XcodeConfig/subconfig/LeopardOrLater.xcconfig b/XcodeConfig/subconfig/LeopardOrLater.xcconfig
index b2828d7..6cc1a08 100644
--- a/XcodeConfig/subconfig/LeopardOrLater.xcconfig
+++ b/XcodeConfig/subconfig/LeopardOrLater.xcconfig
@@ -26,3 +26,7 @@ GCC_VERSION = 4.0
// On Leopard use Obj-C fast dispatch
GCC_FAST_OBJC_DISPATCH = YES
+
+// For Leopard, pull in 64bit support
+#include "64bit.xcconfig"
+
diff --git a/XcodeConfig/subconfig/TigerOrLater.xcconfig b/XcodeConfig/subconfig/TigerOrLater.xcconfig
index d8f7a3d..a693a58 100644
--- a/XcodeConfig/subconfig/TigerOrLater.xcconfig
+++ b/XcodeConfig/subconfig/TigerOrLater.xcconfig
@@ -20,7 +20,7 @@
//
// Default SDK and minimum OS version is 10.4
-SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk
+SDKROOT = $(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk
MACOSX_DEPLOYMENT_TARGET = 10.4
GCC_VERSION = 4.0