aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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