diff options
49 files changed, 2745 insertions, 283 deletions
diff --git a/AppKit/GTMNSImage+Scaling.h b/AppKit/GTMNSImage+Scaling.h new file mode 100644 index 0000000..297d18c --- /dev/null +++ b/AppKit/GTMNSImage+Scaling.h @@ -0,0 +1,46 @@ +// +// GTMNSImage+Scaling.h +// +// Scales NSImages to a variety of sizes for drawing +// +// 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> + +@interface NSImage (GTMNSImageScaling) + +// Return an existing representation of a size +- (NSImageRep *)gtm_representationOfSize:(NSSize)size; + +// Return the exact or next largest representation for a size +- (NSImageRep *)gtm_bestRepresentationForSize:(NSSize)size; + +// Create a new represetation for a given size +- (BOOL)gtm_createRepresentationOfSize:(NSSize)size; + +// Create 32 and 16px reps +- (BOOL)gtm_createIconRepresentations; + +// Remove reps larger than a given size and create a new rep if needed +- (void)gtm_shrinkToSize:(NSSize)size; + +// Remove reps larger than a given size +- (void)gtm_removeRepresentationsLargerThanSize:(NSSize)size; + +// Return a dup shrunk to a given size +- (NSImage *)gtm_duplicateOfSize:(NSSize)size; +@end diff --git a/AppKit/GTMNSImage+Scaling.m b/AppKit/GTMNSImage+Scaling.m new file mode 100644 index 0000000..57687fd --- /dev/null +++ b/AppKit/GTMNSImage+Scaling.m @@ -0,0 +1,168 @@ +// +// GTMNSImage+Scaling.m +// +// Scales NSImages to a variety of sizes for drawing +// +// 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 "GTMNSImage+Scaling.h" +#import "GTMGeometryUtils.h" + +@implementation NSImage (GTMNSImageScaling) + +- (NSImageRep *)gtm_bestRepresentationForSize:(NSSize)size { + NSImageRep *bestRep = [self gtm_representationOfSize:size]; + if (bestRep) { + return bestRep; + } + NSArray *reps = [self representations]; + + CGFloat repDistance = CGFLOAT_MAX; + + NSImageRep *thisRep; + NSEnumerator *repEnum = [reps objectEnumerator]; + while ((thisRep = [repEnum nextObject])) { + CGFloat thisDistance; + thisDistance = MIN(size.width - [thisRep size].width, + size.height-[thisRep size].height); + + if (repDistance < 0 && thisDistance > 0) continue; + if (ABS(thisDistance) < ABS(repDistance) + || (thisDistance < 0 && repDistance > 0)) { + repDistance = thisDistance; + bestRep = thisRep; + } + } + + if (!bestRep) { + bestRep = [self bestRepresentationForDevice:nil]; + } + + return bestRep; +} + +- (NSImageRep *)gtm_representationOfSize:(NSSize)size { + NSArray *reps = [self representations]; + + NSImageRep *thisRep; + NSEnumerator *repEnum = [reps objectEnumerator]; + while ((thisRep = [repEnum nextObject])) { + if (NSEqualSizes([thisRep size], size)) { + return thisRep; + } + } + return nil; +} + +- (BOOL)gtm_createIconRepresentations { + [self setFlipped:NO]; + [self gtm_createRepresentationOfSize:NSMakeSize(16, 16)]; + [self gtm_createRepresentationOfSize:NSMakeSize(32, 32)]; + [self setScalesWhenResized:NO]; + return YES; +} + +- (BOOL)gtm_createRepresentationOfSize:(NSSize)size { + if ([self gtm_representationOfSize:size]) { + return NO; + } + + NSBitmapImageRep *bestRep = + (NSBitmapImageRep *)[self gtm_bestRepresentationForSize:size]; + + NSRect drawRect = GTMNSScaleRectToRect(GTMNSRectOfSize([bestRep size]), + GTMNSRectOfSize(size), + GTMScaleProportionally, + GTMRectAlignCenter); + + if ([bestRep respondsToSelector:@selector(CGImage)]) { + CGImageRef imageRef = (CGImageRef)[bestRep performSelector:@selector(CGImage)]; + + CGColorSpaceRef cspace = CGColorSpaceCreateDeviceRGB(); + if (!cspace) return NO; + + CGContextRef smallContext = + CGBitmapContextCreate(NULL, + size.width, + size.height, + 8, // bits per component + size.width * 4, // bytes per pixel + cspace, + kCGBitmapByteOrder32Host + | kCGImageAlphaPremultipliedLast); + CFRelease(cspace); + + if (!smallContext) return NO; + + + CGContextDrawImage(smallContext, GTMNSRectToCGRect(drawRect), imageRef); + + CGImageRef smallImage = CGBitmapContextCreateImage(smallContext); + + if (smallImage) { + NSBitmapImageRep *cgRep = + [[[NSBitmapImageRep alloc] initWithCGImage:smallImage] autorelease]; + [self addRepresentation:cgRep]; + CGImageRelease(smallImage); + } else { + CGContextRelease(smallContext); + return NO; + } + CGContextRelease(smallContext); + return YES; + } else { + // This functionality is here to allow it to work under Tiger + // It can probably only be called safely from the main thread + NSImage* scaledImage = [[NSImage alloc] initWithSize:size]; + [scaledImage lockFocus]; + NSGraphicsContext *graphicsContext = [NSGraphicsContext currentContext]; + [graphicsContext setImageInterpolation:NSImageInterpolationHigh]; + [graphicsContext setShouldAntialias:YES]; + [bestRep drawInRect:drawRect]; + NSBitmapImageRep* iconRep = + [[[NSBitmapImageRep alloc] initWithFocusedViewRect: + NSMakeRect(0, 0, size.width, size.height)] autorelease]; + [scaledImage unlockFocus]; + [scaledImage release]; + [self addRepresentation:iconRep]; + return YES; + } + return NO; +} + +- (void)gtm_removeRepresentationsLargerThanSize:(NSSize)size { + NSEnumerator *e = [[self representations] reverseObjectEnumerator]; + NSImageRep *thisRep; + while((thisRep = [e nextObject]) ) { + if ([thisRep size].width > size.width + && [thisRep size].height > size.height) + [self removeRepresentation:thisRep]; + } +} + +- (NSImage *)gtm_duplicateOfSize:(NSSize)size { + NSImage *duplicate = [[self copy] autorelease]; + [duplicate gtm_shrinkToSize:size]; + [duplicate setFlipped:NO]; + return duplicate; +} + +- (void)gtm_shrinkToSize:(NSSize)size { + [self gtm_createRepresentationOfSize:size]; + [self setSize:size]; + [self gtm_removeRepresentationsLargerThanSize:size]; +} +@end diff --git a/AppKit/GTMNSImage+ScalingTest.m b/AppKit/GTMNSImage+ScalingTest.m new file mode 100644 index 0000000..ec17cc5 --- /dev/null +++ b/AppKit/GTMNSImage+ScalingTest.m @@ -0,0 +1,47 @@ +// +// GTMNSImage+ScalingTest.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 <Cocoa/Cocoa.h> + +#import "GTMSenTestCase.h" + +#import "GTMNSImage+Scaling.h" + +@interface GTMNSImage_ScalingTest : GTMTestCase +@end + +@implementation GTMNSImage_ScalingTest + +- (void)testScaling { + NSImage *testImage = [NSImage imageNamed:@"NSApplicationIcon"]; + + NSImageRep *rep = nil; + rep = [testImage gtm_bestRepresentationForSize:NSMakeSize(99, 99)]; + STAssertTrue(NSEqualSizes([rep size], NSMakeSize(128, 128)), nil); + + [testImage gtm_createIconRepresentations]; + STAssertNotNil([testImage gtm_representationOfSize:NSMakeSize(16, 16)], nil); + STAssertNotNil([testImage gtm_representationOfSize:NSMakeSize(32, 32)], nil); + + NSImage *duplicate = [testImage gtm_duplicateOfSize: NSMakeSize(48, 48)]; + rep = [duplicate gtm_bestRepresentationForSize:NSMakeSize(50, 50)]; + STAssertTrue(NSEqualSizes([rep size], NSMakeSize(48, 48)), nil); + +} + +@end diff --git a/DebugUtils/GTMMethodCheckTest.m b/DebugUtils/GTMMethodCheckTest.m index 91b7300..7b9fa0c 100644 --- a/DebugUtils/GTMMethodCheckTest.m +++ b/DebugUtils/GTMMethodCheckTest.m @@ -45,5 +45,9 @@ GTM_METHOD_CHECK(GTMMethodCheckTest, GTMMethodCheckTestClassMethod); // COV_NF_ // GTMMethodCheck only runs in debug STAssertTrue(gTestCheckVar, @"Should be true"); #endif + + // Next two calls just verify our code coverage + [self GTMMethodCheckTestMethod]; + [[self class] GTMMethodCheckTestClassMethod]; } @end diff --git a/Foundation/GTMExceptionalInlines.h b/Foundation/GTMExceptionalInlines.h new file mode 100644 index 0000000..25635c1 --- /dev/null +++ b/Foundation/GTMExceptionalInlines.h @@ -0,0 +1,52 @@ +// +// GTMExceptionalInlines.h +// +// 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> +#import "GTMDefines.h" + +// This file exists because when you have full warnings on you can run into +// troubles with functions that Apple has inlined that have structures or +// local variables defined in them. +// You only see this warning if you have -Wuninitialized turned on, +// and you will only see them in release mode. -Wno-unitialized turns them +// off, but you also lose all the good warnings that come with -Wuninitialized. +// If you have the inline versions of any of the functions below in a +// @syncronized, or @try block, you will get +// warning: variable 'r' might be clobbered by 'longjmp' or 'vfork' +// By moving this local vars "out of line" you fix the problem. +// These functions do nothing more than act as "out of line" calls to the +// functions they are masking to avoid the warning. +// If you run into others, feel free to add them. + +// Please only use these to avoid the warning above. Use the Apple defined +// functions where possible. + +FOUNDATION_EXPORT NSRange GTMNSMakeRange(NSUInteger loc, NSUInteger len); + +FOUNDATION_EXPORT CGPoint GTMCGPointMake(CGFloat x, CGFloat y); +FOUNDATION_EXPORT CGSize GTMCGSizeMake(CGFloat width, CGFloat height); +FOUNDATION_EXPORT CGRect GTMCGRectMake(CGFloat x, CGFloat y, + CGFloat width, CGFloat height); + +#if !GTM_IPHONE_SDK +// iPhone does not have NSTypes defined, only CGTypes. So no NSRect, NSPoint etc. +FOUNDATION_EXPORT NSPoint GTMNSMakePoint(CGFloat x, CGFloat y); +FOUNDATION_EXPORT NSSize GTMNSMakeSize(CGFloat w, CGFloat h); +FOUNDATION_EXPORT NSRect GTMNSMakeRect(CGFloat x, CGFloat y, + CGFloat w, CGFloat h); +#endif diff --git a/Foundation/GTMExceptionalInlines.m b/Foundation/GTMExceptionalInlines.m new file mode 100644 index 0000000..120e235 --- /dev/null +++ b/Foundation/GTMExceptionalInlines.m @@ -0,0 +1,52 @@ +// +// GTMExceptionalInlines.m +// +// Copyright 2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMExceptionalInlines.h" + +NSRange GTMNSMakeRange(NSUInteger loc, NSUInteger len) { + return NSMakeRange(loc, len); +} + +CGPoint GTMCGPointMake(CGFloat x, CGFloat y) { + return CGPointMake(x, y); +} + +CGSize GTMCGSizeMake(CGFloat width, CGFloat height) { + return CGSizeMake(width, height); +} + +CGRect GTMCGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height) { + return CGRectMake(x, y, width, height); +} + +#if !GTM_IPHONE_SDK +// iPhone does not have NSTypes defined, only CGTypes. So no NSRect, NSPoint etc. + +NSPoint GTMNSMakePoint(CGFloat x, CGFloat y) { + return NSMakePoint(x, y); +} + +NSSize GTMNSMakeSize(CGFloat w, CGFloat h) { + return NSMakeSize(w, h); +} + +NSRect GTMNSMakeRect(CGFloat x, CGFloat y, CGFloat w, CGFloat h) { + return NSMakeRect(x, y, w, h); +} + +#endif diff --git a/Foundation/GTMExceptionalInlinesTest.m b/Foundation/GTMExceptionalInlinesTest.m new file mode 100644 index 0000000..f785301 --- /dev/null +++ b/Foundation/GTMExceptionalInlinesTest.m @@ -0,0 +1,65 @@ +// +// GTMExceptionalInlinesTest.m +// +// Copyright 2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMSenTestCase.h" +#import "GTMExceptionalInlines.h" + +@interface GTMExceptionalInlinesTest : GTMTestCase +@end + +@implementation GTMExceptionalInlinesTest +- (void)testExceptionalInlines { + // Numbers chosen basically at random. + NSUInteger loc = 5; + NSUInteger len = 10; + CGFloat x = 22.5; + CGFloat y = 40.2; + CGFloat h = 21.6; + CGFloat w = 54.2; + + NSRange range1 = GTMNSMakeRange(loc, len); + NSRange range2 = NSMakeRange(loc, len); + STAssertTrue(NSEqualRanges(range1, range2), nil); + + CGPoint cgpoint1 = GTMCGPointMake(x, y); + CGPoint cgpoint2 = CGPointMake(x, y); + STAssertTrue(CGPointEqualToPoint(cgpoint1, cgpoint2), nil); + + CGSize cgsize1 = GTMCGSizeMake(x, y); + CGSize cgsize2 = CGSizeMake(x, y); + STAssertTrue(CGSizeEqualToSize(cgsize1, cgsize2), nil); + + CGRect cgrect1 = GTMCGRectMake(x, y, w, h); + CGRect cgrect2 = CGRectMake(x, y, w, h); + STAssertTrue(CGRectEqualToRect(cgrect1, cgrect2), nil); + +#if !GTM_IPHONE_SDK + NSPoint point1 = GTMNSMakePoint(x, y); + NSPoint point2 = NSMakePoint(x, y); + STAssertTrue(NSEqualPoints(point1, point2), nil); + + NSSize size1 = GTMNSMakeSize(w, h); + NSSize size2 = NSMakeSize(w, h); + STAssertTrue(NSEqualSizes(size1, size2), nil); + + NSRect rect1 = GTMNSMakeRect(x, y, w, h); + NSRect rect2 = NSMakeRect(x, y, w, h); + STAssertTrue(NSEqualRects(rect1, rect2), nil); +#endif +} +@end diff --git a/Foundation/GTMGeometryUtils.h b/Foundation/GTMGeometryUtils.h index 9691ffc..855b97f 100644 --- a/Foundation/GTMGeometryUtils.h +++ b/Foundation/GTMGeometryUtils.h @@ -25,7 +25,8 @@ enum { GTMScaleProportionally = 0, // Fit proportionally GTMScaleToFit, // Forced fit (distort if necessary) - GTMScaleNone // Don't scale (clip) + GTMScaleNone, // Don't scale (clip) + GTMScaleToFillProportionally = 101 // Scale proportionally to fill area }; typedef NSUInteger GTMScaling; @@ -398,6 +399,27 @@ CG_INLINE NSRect GTMNSAlignRectangles(NSRect alignee, NSRect aligner, alignment)); } +/// Align a rectangle to another +// +// Args: +// scalee - rect to be scaled +// scaler - rect to scale to +// scaling - way to scale the rectangle +// alignment - way to align the scaled rectangle +CG_INLINE NSRect GTMNSScaleRectToRect(NSRect scalee, + NSRect scaler, + GTMScaling scaling, + GTMRectAlignment alignment) { + + return GTMCGRectToNSRect( + GTMCGAlignRectangles( + GTMCGScaleRectangleToSize(GTMNSRectToCGRect(scalee), + GTMNSSizeToCGSize(scaler.size), + scaling), + GTMNSRectToCGRect(scaler), + alignment)); +} + /// Scale rectangle // // Args: diff --git a/Foundation/GTMGeometryUtils.m b/Foundation/GTMGeometryUtils.m index 9ac2933..07de80c 100644 --- a/Foundation/GTMGeometryUtils.m +++ b/Foundation/GTMGeometryUtils.m @@ -78,6 +78,8 @@ CGRect GTMCGAlignRectangles(CGRect alignee, CGRect aligner, GTMRectAlignment ali CGRect GTMCGScaleRectangleToSize(CGRect scalee, CGSize size, GTMScaling scaling) { switch (scaling) { + + case GTMScaleToFillProportionally: case GTMScaleProportionally: { CGFloat height = CGRectGetHeight(scalee); CGFloat width = CGRectGetWidth(scalee); @@ -85,12 +87,14 @@ CGRect GTMCGScaleRectangleToSize(CGRect scalee, CGSize size, GTMScaling scaling) (height > size.height || width > size.width)) { CGFloat horiz = size.width / width; CGFloat vert = size.height / height; - CGFloat newScale = horiz < vert ? horiz : vert; + BOOL expand = (scaling == GTMScaleToFillProportionally); + // We use the smaller scale unless expand is true. In that case, larger. + CGFloat newScale = ((horiz < vert) ^ expand) ? horiz : vert; scalee = GTMCGRectScale(scalee, newScale, newScale); } break; } - + case GTMScaleToFit: scalee.size = size; break; diff --git a/Foundation/GTMGeometryUtilsTest.m b/Foundation/GTMGeometryUtilsTest.m index 5a59c8b..38b86fb 100644 --- a/Foundation/GTMGeometryUtilsTest.m +++ b/Foundation/GTMGeometryUtilsTest.m @@ -164,6 +164,31 @@ STAssertEquals(result, rect, nil); } + +- (void)testGTMNSScaleRectToRect { + typedef struct { + NSRect expectedRect; + GTMScaling scaling; + GTMRectAlignment alignment; + } TestData; + + NSRect rect1 = NSMakeRect(0, 0, 4, 4); + NSRect rect2 = NSMakeRect(0, 0, 2, 1); + + TestData data[] = { + { NSMakeRect(2, 3, 2, 1), GTMScaleToFillProportionally, GTMRectAlignTopRight }, + { NSMakeRect(0, 0, 4, 4), GTMScaleToFit, GTMRectAlignCenter }, + { NSMakeRect(1, 1.5, 2, 1), GTMScaleNone, GTMRectAlignCenter }, + { NSMakeRect(1, 0, 2, 1), GTMScaleProportionally, GTMRectAlignBottom }, + }; + + for (size_t i = 0; i < sizeof(data) / sizeof(TestData); i++) { + NSRect outRect = GTMNSScaleRectToRect(rect2, rect1, data[i].scaling, data[i].alignment); + STAssertEquals(outRect, data[i].expectedRect, nil); + } +} + + - (void)testGTMNSDistanceBetweenPoints { NSPoint pt1 = NSMakePoint(0, 0); NSPoint pt2 = NSMakePoint(3, 4); diff --git a/Foundation/GTMHTTPFetcher.m b/Foundation/GTMHTTPFetcher.m index a73d752..614f3ad 100644 --- a/Foundation/GTMHTTPFetcher.m +++ b/Foundation/GTMHTTPFetcher.m @@ -697,6 +697,7 @@ CannotBeginFetch: { nil, 0 } }; + BOOL isGood = NO; // NSError's isEqual always returns false for equal but distinct instances // of NSError, so we have to compare the domain and code values explicitly @@ -705,10 +706,11 @@ CannotBeginFetch: if ([[error domain] isEqual:retries[idx].domain] && [error code] == retries[idx].code) { - return YES; + isGood = YES; + break; } } - return NO; + return isGood; } @@ -927,11 +929,9 @@ CannotBeginFetch: cookieStorageMethod_ = method; - if (method == kGTMHTTPFetcherCookieStorageMethodSystemDefault) { - [request_ setHTTPShouldHandleCookies:YES]; - } else { - [request_ setHTTPShouldHandleCookies:NO]; - } + BOOL handleCookies + = method == kGTMHTTPFetcherCookieStorageMethodSystemDefault ? YES : NO; + [request_ setHTTPShouldHandleCookies:handleCookies]; } - (id)delegate { @@ -973,12 +973,10 @@ CannotBeginFetch: - (void)setFetchHistory:(NSMutableDictionary *)fetchHistory { [fetchHistory_ autorelease]; fetchHistory_ = [fetchHistory retain]; - - if (fetchHistory_ != nil) { - [self setCookieStorageMethod:kGTMHTTPFetcherCookieStorageMethodFetchHistory]; - } else { - [self setCookieStorageMethod:kGTMHTTPFetcherCookieStorageMethodStatic]; - } + GTMHTTPFetcherCookieStorageMethod method + = fetchHistory_ ? kGTMHTTPFetcherCookieStorageMethodFetchHistory + : kGTMHTTPFetcherCookieStorageMethodStatic; + [self setCookieStorageMethod:method]; } - (void)setShouldCacheDatedData:(BOOL)flag { diff --git a/Foundation/GTMLoggerTest.m b/Foundation/GTMLoggerTest.m index 9093c7a..043c73d 100644 --- a/Foundation/GTMLoggerTest.m +++ b/Foundation/GTMLoggerTest.m @@ -94,12 +94,20 @@ @"GTMLoggerUnitTest.log"] retain]; STAssertNotNil(path_, nil); // Make sure we're cleaned up from the last run +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050 [[NSFileManager defaultManager] removeFileAtPath:path_ handler:nil]; +#else + [[NSFileManager defaultManager] removeItemAtPath:path_ error:NULL]; +#endif } - (void)tearDown { STAssertNotNil(path_, nil); +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050 [[NSFileManager defaultManager] removeFileAtPath:path_ handler:nil]; +#else + [[NSFileManager defaultManager] removeItemAtPath:path_ error:NULL]; +#endif [path_ release]; path_ = nil; } @@ -372,8 +380,8 @@ STAssertNotNil(fmtr, nil); // E.g. 2008-01-04 09:16:26.906 otest[5567/0xa07d0f60] [lvl=1] (no func) test - static NSString *const kFormatBasePattern = - @"[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3} otest\\[[0-9]+/0x[0-9a-f]+\\] \\[lvl=[0-3]\\] \\(no func\\) "; + NSString * kFormatBasePattern = + @"[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3} ((otest)|(GTMiPhoneTest))\\[[0-9]+/0x[0-9a-f]+\\] \\[lvl=[0-3]\\] \\(no func\\) "; NSString *msg = nil; msg = [self stringFromFormatter:fmtr diff --git a/Foundation/GTMNSData+zlibTest.m b/Foundation/GTMNSData+zlibTest.m index 1b6264c..0d86886 100644 --- a/Foundation/GTMNSData+zlibTest.m +++ b/Foundation/GTMNSData+zlibTest.m @@ -25,13 +25,64 @@ @interface GTMNSData_zlibTest : GTMTestCase @end - -static void FillWithRandom(char *data, unsigned long len) { - char *max = data + len; - for ( ; data < max ; ++data) { - *data = random() & 0xFF; - } -} +// NOTE: we don't need to test the actually compressor/inflation (we're using +// zlib, it works), we just need to test our wrapper. So we can use canned +// data, etc. (and yes, when using random data, things once failed because +// we generated a random block of data that was valid compressed data?!) + +static unsigned char randomDataLarge[] = { + // openssl rand -rand /dev/random 512 | xxd -i + 0xe1, 0xa6, 0xe2, 0xa2, 0x0b, 0xf7, 0x8d, 0x6b, 0x31, 0xfe, 0xaa, 0x64, + 0x50, 0xbe, 0x52, 0x7e, 0x83, 0x74, 0x00, 0x8f, 0x62, 0x96, 0xc7, 0xe9, + 0x20, 0x59, 0x78, 0xc6, 0xea, 0x10, 0xd5, 0xdb, 0x3f, 0x6b, 0x13, 0xd9, + 0x44, 0x18, 0x24, 0x17, 0x63, 0xc8, 0x74, 0xa5, 0x37, 0x6c, 0x9c, 0x00, + 0xe5, 0xcf, 0x0a, 0xdf, 0xb9, 0x66, 0xb1, 0xbd, 0x04, 0x8f, 0x55, 0x9e, + 0xb0, 0x24, 0x4e, 0xf0, 0xc4, 0x69, 0x2c, 0x1f, 0x63, 0x9f, 0x41, 0xa8, + 0x89, 0x9b, 0x98, 0x00, 0xb6, 0x78, 0xf7, 0xe4, 0x4c, 0x72, 0x14, 0x84, + 0xaa, 0x3d, 0xc1, 0x42, 0x9f, 0x12, 0x85, 0xdd, 0x16, 0x8b, 0x8f, 0x67, + 0xe0, 0x26, 0x5b, 0x5e, 0xaa, 0xe7, 0xd3, 0x67, 0xfe, 0x21, 0x77, 0xa6, + 0x52, 0xde, 0x33, 0x8b, 0x96, 0x49, 0x6a, 0xd6, 0x58, 0x58, 0x36, 0x00, + 0x23, 0xd2, 0x45, 0x13, 0x9f, 0xd9, 0xc7, 0x2d, 0x55, 0x12, 0xb4, 0xfe, + 0x53, 0x27, 0x1f, 0x14, 0x71, 0x9b, 0x7e, 0xcc, 0x5e, 0x8c, 0x59, 0xef, + 0x80, 0xac, 0x89, 0xf4, 0x45, 0x8d, 0x98, 0x6d, 0x97, 0xfd, 0x53, 0x5f, + 0x19, 0xd6, 0x11, 0xf7, 0xcb, 0x5d, 0xca, 0xab, 0xe1, 0x01, 0xf1, 0xe9, + 0x1f, 0x1f, 0xf3, 0x53, 0x76, 0xa2, 0x59, 0x8e, 0xb3, 0x91, 0xff, 0xe8, + 0x1b, 0xc0, 0xc0, 0xda, 0xdd, 0x93, 0xb5, 0x9d, 0x62, 0x13, 0xb8, 0x07, + 0xf2, 0xf5, 0xb9, 0x4b, 0xe1, 0x09, 0xed, 0xdb, 0xe6, 0xd9, 0x2d, 0xc4, + 0x0d, 0xb6, 0xbd, 0xfc, 0xdb, 0x5c, 0xcc, 0xf6, 0x53, 0x4e, 0x01, 0xa4, + 0x03, 0x95, 0x4a, 0xa4, 0xaa, 0x4f, 0x45, 0xaf, 0xbf, 0xf1, 0x7e, 0x60, + 0x1d, 0x86, 0x93, 0x65, 0x7b, 0x24, 0x0c, 0x09, 0xe0, 0xd1, 0xd8, 0x60, + 0xd9, 0xd9, 0x55, 0x2a, 0xec, 0xd5, 0xdc, 0xd0, 0xc6, 0x5e, 0x2c, 0x22, + 0xf5, 0x19, 0x0b, 0xc3, 0xa1, 0x38, 0x11, 0x67, 0x6f, 0x6c, 0x0e, 0x34, + 0x44, 0x83, 0xee, 0xd3, 0xf2, 0x4b, 0x7b, 0x03, 0x68, 0xfe, 0xc5, 0x76, + 0xb2, 0x2e, 0x26, 0xeb, 0x1f, 0x66, 0x02, 0xa4, 0xd9, 0xda, 0x28, 0x3a, + 0xc3, 0x94, 0x03, 0xe8, 0x29, 0x7e, 0xfe, 0x3d, 0xc8, 0xc1, 0x0a, 0x74, + 0xc7, 0xaf, 0xa6, 0x84, 0x86, 0x85, 0xc3, 0x8c, 0x00, 0x38, 0xd4, 0xb5, + 0xb2, 0xe0, 0xf0, 0xc4, 0x8d, 0x10, 0x0d, 0xf1, 0xcd, 0x05, 0xdb, 0xd0, + 0xcf, 0x17, 0x4f, 0xa8, 0xe5, 0xf0, 0x53, 0x55, 0x62, 0xc7, 0x55, 0xe5, + 0xbe, 0x18, 0x2f, 0xda, 0x48, 0xf1, 0xaa, 0x85, 0x46, 0x80, 0x15, 0x70, + 0x82, 0xd2, 0xa6, 0xb0, 0x3d, 0x31, 0xb5, 0xcc, 0x23, 0x95, 0x5e, 0x15, + 0x35, 0x32, 0xd0, 0x86, 0xd1, 0x6e, 0x2d, 0xc0, 0xfe, 0x45, 0xae, 0x28, + 0x24, 0xa7, 0x14, 0xf4, 0xe9, 0xb5, 0x6f, 0xac, 0x25, 0xf9, 0x88, 0xf6, + 0x60, 0x5d, 0x6b, 0x5c, 0xf2, 0x38, 0xe8, 0xdc, 0xbd, 0xa6, 0x13, 0xc0, + 0xa4, 0xc8, 0xe9, 0x7a, 0xc6, 0xb6, 0x88, 0x26, 0x98, 0x9f, 0xe3, 0x9a, + 0xd9, 0x5b, 0xd4, 0xd0, 0x02, 0x1f, 0x55, 0x30, 0xbe, 0xde, 0x9c, 0xd1, + 0x53, 0x93, 0x72, 0xe6, 0x19, 0x79, 0xe9, 0xf1, 0x70, 0x78, 0x92, 0x31, + 0xf6, 0x17, 0xc0, 0xdd, 0x99, 0xc8, 0x97, 0x67, 0xdc, 0xf6, 0x67, 0x6b, + 0x9b, 0x1c, 0x90, 0xea, 0x1a, 0x9e, 0x26, 0x68, 0xc2, 0x13, 0x94, 0x3a, + 0x3e, 0x73, 0x61, 0x4e, 0x37, 0xa8, 0xa1, 0xfa, 0xf8, 0x22, 0xdd, 0x20, + 0x40, 0xc6, 0x52, 0x27, 0x47, 0x1a, 0x79, 0xfa, 0x40, 0xa6, 0x62, 0x6b, + 0xe6, 0xc7, 0x67, 0xb7, 0xa8, 0x2d, 0xd1, 0x9f, 0x17, 0xb8, 0x77, 0x5e, + 0x97, 0x1e, 0x92, 0xd7, 0xd2, 0x25, 0x04, 0x92, 0xf9, 0x41, 0x70, 0x93, + 0xe1, 0x13, 0x07, 0x94, 0x8e, 0x0b, 0x82, 0x98 +}; + +static unsigned char randomDataSmall[] = { + // openssl rand -rand /dev/random 24 | xxd -i + 0xd1, 0xec, 0x35, 0xc3, 0xa0, 0x4c, 0x73, 0x37, 0x2f, 0x5a, 0x12, 0x44, + 0xee, 0xe4, 0x22, 0x07, 0x29, 0xa8, 0x4a, 0xde, 0xc8, 0xbb, 0xe7, 0xdb +}; + static BOOL HasGzipHeader(NSData *data) { // very simple check @@ -43,20 +94,11 @@ static BOOL HasGzipHeader(NSData *data) { @implementation GTMNSData_zlibTest -- (void)setUp { - // seed random from /dev/random - srandomdev(); -} - - (void)testBoundryValues { - NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init]; - STAssertNotNil(localPool, @"failed to alloc local pool"); - // build some test data - NSMutableData *data = [NSMutableData data]; + NSData *data = [NSData dataWithBytes:randomDataLarge + length:sizeof(randomDataLarge)]; STAssertNotNil(data, @"failed to alloc data block"); - [data setLength:512]; - FillWithRandom([data mutableBytes], [data length]); // bogus args to start STAssertNil([NSData gtm_dataByDeflatingData:nil], nil); @@ -107,29 +149,28 @@ static BOOL HasGzipHeader(NSData *data) { STAssertNil([NSData gtm_dataByInflatingData:data], nil); // test deflated data runs that end before they are done - [GTMUnitTestDevLog expect:[deflated length] - 1 + [GTMUnitTestDevLog expect:([deflated length] / 11) + 1 casesOfString:@"Error trying to inflate some of the payload, " "error -5"]; - for (NSUInteger x = 1 ; x < [deflated length] ; ++x) { + for (NSUInteger x = 1 ; x < [deflated length] ; x += 11) { STAssertNil([NSData gtm_dataByInflatingBytes:[deflated bytes] length:x], nil); } // test gzipped data runs that end before they are done - [GTMUnitTestDevLog expect:[gzipped length] - 1 + [GTMUnitTestDevLog expect:([gzipped length] / 11) + 1 casesOfString:@"Error trying to inflate some of the payload, " "error -5"]; - for (NSUInteger x = 1 ; x < [gzipped length] ; ++x) { + for (NSUInteger x = 1 ; x < [gzipped length] ; x += 11) { STAssertNil([NSData gtm_dataByInflatingBytes:[gzipped bytes] length:x], nil); } // test extra data before the deflated/gzipped data (just to make sure we // don't seek to the "real" data) - NSMutableData *prefixedDeflated = [NSMutableData data]; + NSMutableData *prefixedDeflated = + [NSMutableData dataWithBytes:randomDataSmall length:sizeof(randomDataSmall)]; STAssertNotNil(prefixedDeflated, @"failed to alloc data block"); - [prefixedDeflated setLength:20]; - FillWithRandom([prefixedDeflated mutableBytes], [prefixedDeflated length]); [prefixedDeflated appendData:deflated]; [GTMUnitTestDevLog expectString:@"Error trying to inflate some of the " "payload, error -3"]; @@ -139,10 +180,9 @@ static BOOL HasGzipHeader(NSData *data) { STAssertNil([NSData gtm_dataByInflatingBytes:[prefixedDeflated bytes] length:[prefixedDeflated length]], nil); - NSMutableData *prefixedGzipped = [NSMutableData data]; + NSMutableData *prefixedGzipped = + [NSMutableData dataWithBytes:randomDataSmall length:sizeof(randomDataSmall)]; STAssertNotNil(prefixedDeflated, @"failed to alloc data block"); - [prefixedGzipped setLength:20]; - FillWithRandom([prefixedGzipped mutableBytes], [prefixedGzipped length]); [prefixedGzipped appendData:gzipped]; [GTMUnitTestDevLog expectString:@"Error trying to inflate some of the " "payload, error -3"]; @@ -179,171 +219,141 @@ static BOOL HasGzipHeader(NSData *data) { STAssertNil([NSData gtm_dataByInflatingBytes:[suffixedGZipped bytes] length:[suffixedGZipped length]], nil); - - [localPool release]; } - (void)testInflateDeflate { - // generate a range of sizes w/ random content - for (int n = 0 ; n < 2 ; ++n) { - for (int x = 1 ; x < 128 ; ++x) { - NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init]; - STAssertNotNil(localPool, @"failed to alloc local pool"); - - NSMutableData *data = [NSMutableData data]; - STAssertNotNil(data, @"failed to alloc data block"); - - // first pass small blocks, second pass, larger ones, but second pass - // avoid making them multimples of 128. - [data setLength:((n*x*128) + x)]; - FillWithRandom([data mutableBytes], [data length]); - - // 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], - (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], - (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], - (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], - (NSUInteger)0, @"failed to inflate data block"); - STAssertEqualObjects(data, - dataPrime, @"failed to round trip via *Data apis"); - - // loop over the compression levels - for (int level = 1 ; level < 9 ; ++level) { - // w/ *Bytes apis, using our level - deflated = [NSData gtm_dataByDeflatingBytes:[data bytes] - length:[data length] - compressionLevel:level]; - STAssertNotNil(deflated, @"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], - (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], - (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], - (NSUInteger)0, @"failed to inflate data block"); - STAssertEqualObjects(data, - dataPrime, @"failed to round trip via *Data apis"); - } - - [localPool release]; - } + NSData *data = [NSData dataWithBytes:randomDataLarge + length:sizeof(randomDataLarge)]; + STAssertNotNil(data, @"failed to alloc data block"); + + // 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], + (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], + (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], + (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], + (NSUInteger)0, @"failed to inflate data block"); + STAssertEqualObjects(data, + dataPrime, @"failed to round trip via *Data apis"); + + // loop over the compression levels + for (int level = 1 ; level < 9 ; ++level) { + // w/ *Bytes apis, using our level + deflated = [NSData gtm_dataByDeflatingBytes:[data bytes] + length:[data length] + compressionLevel:level]; + STAssertNotNil(deflated, @"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], + (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], + (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], + (NSUInteger)0, @"failed to inflate data block"); + STAssertEqualObjects(data, + dataPrime, @"failed to round trip via *Data apis"); } } - (void)testInflateGzip { - // generate a range of sizes w/ random content - for (int n = 0 ; n < 2 ; ++n) { - for (int x = 1 ; x < 128 ; ++x) { - NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init]; - STAssertNotNil(localPool, @"failed to alloc local pool"); - - NSMutableData *data = [NSMutableData data]; - STAssertNotNil(data, @"failed to alloc data block"); - - // first pass small blocks, second pass, larger ones, but second pass - // avoid making them multimples of 128. - [data setLength:((n*x*128) + x)]; - FillWithRandom([data mutableBytes], [data length]); - - // 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], - (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], - (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], - (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], - (NSUInteger)0, @"failed to inflate data block"); - STAssertEqualObjects(data, dataPrime, - @"failed to round trip via *Data apis"); - - // loop over the compression levels - for (int level = 1 ; level < 9 ; ++level) { - // w/ *Bytes apis, using our level - gzipped = [NSData gtm_dataByGzippingBytes:[data bytes] - length:[data length] - compressionLevel:level]; - STAssertNotNil(gzipped, @"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], - (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], - (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], - (NSUInteger)0, @"failed to inflate data block"); - STAssertEqualObjects(data, - dataPrime, @"failed to round trip via *Data apis"); - } - - [localPool release]; - } + NSData *data = [NSData dataWithBytes:randomDataLarge + length:sizeof(randomDataLarge)]; + STAssertNotNil(data, @"failed to alloc data block"); + + // 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], + (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], + (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], + (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], + (NSUInteger)0, @"failed to inflate data block"); + STAssertEqualObjects(data, dataPrime, + @"failed to round trip via *Data apis"); + + // loop over the compression levels + for (int level = 1 ; level < 9 ; ++level) { + // w/ *Bytes apis, using our level + gzipped = [NSData gtm_dataByGzippingBytes:[data bytes] + length:[data length] + compressionLevel:level]; + STAssertNotNil(gzipped, @"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], + (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], + (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], + (NSUInteger)0, @"failed to inflate data block"); + STAssertEqualObjects(data, + dataPrime, @"failed to round trip via *Data apis"); } } diff --git a/Foundation/GTMNSString+Replace.h b/Foundation/GTMNSString+Replace.h index 3a88c80..edc8478 100644 --- a/Foundation/GTMNSString+Replace.h +++ b/Foundation/GTMNSString+Replace.h @@ -21,6 +21,9 @@ /// Give easy search-n-replace functionality to NSString. @interface NSString (GTMStringReplaceAdditions) +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050 +// 10.5 has stringByReplacingOccurrencesOfString:withString:, use that directly. + /// Returns a new autoreleased string by replacing all occurrences of // |oldString| with |newString| (case sensitive). If |oldString| is nil or // @"" nothing is done and |self| is returned. If |newString| is nil, it's @@ -37,4 +40,6 @@ - (NSString *)gtm_stringByReplacingString:(NSString *)target withString:(NSString *)replacement; +#endif // MAC_OS_X_VERSION_MIN_REQUIRED < 1050 + @end diff --git a/Foundation/GTMNSString+Replace.m b/Foundation/GTMNSString+Replace.m index 4e8195c..f617945 100644 --- a/Foundation/GTMNSString+Replace.m +++ b/Foundation/GTMNSString+Replace.m @@ -21,6 +21,9 @@ @implementation NSString (GTMStringReplaceAdditions) +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050 +// 10.5 has stringByReplacingOccurrencesOfString:withString:, use that directly. + - (NSString *)gtm_stringByReplacingString:(NSString *)target withString:(NSString *)replacement { // If |target| was nil, then do nothing and return |self| @@ -45,4 +48,6 @@ return result; } +#endif // MAC_OS_X_VERSION_MIN_REQUIRED < 1050 + @end diff --git a/Foundation/GTMNSString+ReplaceTest.m b/Foundation/GTMNSString+ReplaceTest.m index e814040..805136c 100644 --- a/Foundation/GTMNSString+ReplaceTest.m +++ b/Foundation/GTMNSString+ReplaceTest.m @@ -24,6 +24,8 @@ @implementation GTMNSString_ReplaceTest +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050 + - (void)testStringByReplacingStringWithString { NSString *testString = @"a bc debc gh"; NSString *result; @@ -52,4 +54,6 @@ @"replacing '' with anything should yield the original string"); } +#endif // MAC_OS_X_VERSION_MIN_REQUIRED < 1050 + @end diff --git a/Foundation/GTMPathTest.m b/Foundation/GTMPathTest.m index 3130c4f..0570bb3 100644 --- a/Foundation/GTMPathTest.m +++ b/Foundation/GTMPathTest.m @@ -46,7 +46,11 @@ // Make sure it's safe to remove this directory before nuking it. STAssertNotNil(testDirectory_, nil); STAssertNotEqualObjects(testDirectory_, @"/", nil); +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050 [[NSFileManager defaultManager] removeFileAtPath:testDirectory_ handler:nil]; +#else + [[NSFileManager defaultManager] removeItemAtPath:testDirectory_ error:NULL]; +#endif } - (void)testBasicCreation { diff --git a/Foundation/GTMValidatingContainersTest.m b/Foundation/GTMValidatingContainersTest.m index ed17327..f9cc377 100644 --- a/Foundation/GTMValidatingContainersTest.m +++ b/Foundation/GTMValidatingContainersTest.m @@ -292,6 +292,9 @@ [dictionary removeObjectForKey:@"Key2"]; [dictionary removeObjectForKey:@"Key1"]; STAssertEquals([dictionary count], (NSUInteger)0, @"should have no objects left"); + + // So we get full code coverage + [testSubClass_ foo]; #if !(GTM_CONTAINERS_VALIDATE && GTM_CONTAINERS_VALIDATION_FAILED_LOG && !GTM_CONTAINERS_VALIDATION_FAILED_ASSERT) // If we're not validating, we don't expect any logs [GTMUnitTestDevLog resetExpectedLogs]; diff --git a/GTM.xcodeproj/project.pbxproj b/GTM.xcodeproj/project.pbxproj index c51faa8..9fb14d1 100644 --- a/GTM.xcodeproj/project.pbxproj +++ b/GTM.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 44; + objectVersion = 45; objects = { /* Begin PBXAggregateTarget section */ @@ -42,13 +42,20 @@ 33C374380DD8D44800E97817 /* GTMNSDictionary+URLArguments.h in Headers */ = {isa = PBXBuildFile; fileRef = 33C374360DD8D44800E97817 /* GTMNSDictionary+URLArguments.h */; settings = {ATTRIBUTES = (Public, ); }; }; 33C374390DD8D44800E97817 /* GTMNSDictionary+URLArguments.m in Sources */ = {isa = PBXBuildFile; fileRef = 33C374370DD8D44800E97817 /* GTMNSDictionary+URLArguments.m */; }; 33C3745F0DD8D85B00E97817 /* GTMNSDictionary+URLArgumentsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 33C3745E0DD8D85B00E97817 /* GTMNSDictionary+URLArgumentsTest.m */; }; + 7F3EB38E0E5E09C700A7A75E /* GTMNSImage+Scaling.h in Headers */ = {isa = PBXBuildFile; fileRef = 7F3EB38C0E5E09C700A7A75E /* GTMNSImage+Scaling.h */; }; + 7F3EB38F0E5E09C700A7A75E /* GTMNSImage+Scaling.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F3EB38D0E5E09C700A7A75E /* GTMNSImage+Scaling.m */; }; + 7F3EB3940E5E0A2100A7A75E /* GTMNSImage+ScalingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F3EB3930E5E0A2100A7A75E /* GTMNSImage+ScalingTest.m */; }; + 7F3EB5540E5F0B0400A7A75E /* GTMUnitTestingImage.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 8BEEA90A0DA7446300894774 /* GTMUnitTestingImage.tiff */; }; + 7F3EB5870E5F0CBB00A7A75E /* GTMLargeTypeWindowTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B1801A40E2533DB00280961 /* GTMLargeTypeWindowTest.m */; }; 8B1801A20E2533D500280961 /* GTMLargeTypeWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B1801A00E2533D500280961 /* GTMLargeTypeWindow.m */; }; 8B1801A30E2533D500280961 /* GTMLargeTypeWindow.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B1801A10E2533D500280961 /* GTMLargeTypeWindow.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 8B1801A50E2533DB00280961 /* GTMLargeTypeWindowTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B1801A40E2533DB00280961 /* GTMLargeTypeWindowTest.m */; }; 8B1801AE0E25341B00280961 /* GTMLargeTypeWindowImageTest.gtmUTState in Resources */ = {isa = PBXBuildFile; fileRef = 8B1801A80E25341B00280961 /* GTMLargeTypeWindowImageTest.gtmUTState */; }; 8B1801B20E25341B00280961 /* GTMLargeTypeWindowLongTextTest.gtmUTState in Resources */ = {isa = PBXBuildFile; fileRef = 8B1801AC0E25341B00280961 /* GTMLargeTypeWindowLongTextTest.gtmUTState */; }; 8B1801B30E25341B00280961 /* GTMLargeTypeWindowShortTextTest.gtmUTState in Resources */ = {isa = PBXBuildFile; fileRef = 8B1801AD0E25341B00280961 /* GTMLargeTypeWindowShortTextTest.gtmUTState */; }; 8B1802420E25592200280961 /* GTMLargeTypeWindowMediumTextTest.gtmUTState in Resources */ = {isa = PBXBuildFile; fileRef = 8B1802410E25592200280961 /* GTMLargeTypeWindowMediumTextTest.gtmUTState */; }; + 8B1B49180E5F8E2100A08972 /* GTMExceptionalInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B1B49160E5F8E2100A08972 /* GTMExceptionalInlines.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8B1B49190E5F8E2100A08972 /* GTMExceptionalInlines.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B1B49170E5F8E2100A08972 /* GTMExceptionalInlines.m */; }; + 8B1B49260E5F97C800A08972 /* GTMExceptionalInlinesTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B1B491B0E5F904C00A08972 /* GTMExceptionalInlinesTest.m */; }; 8B3344210DBF7A36009FD32C /* GTMNSAppleScript+HandlerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3344170DBF7A36009FD32C /* GTMNSAppleScript+HandlerTest.m */; }; 8B3344230DBF7A36009FD32C /* GTMNSAppleEventDescriptor+HandlerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B33441A0DBF7A36009FD32C /* GTMNSAppleEventDescriptor+HandlerTest.m */; }; 8B3344250DBF7A36009FD32C /* GTMNSAppleEventDescriptor+FoundationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B33441D0DBF7A36009FD32C /* GTMNSAppleEventDescriptor+FoundationTest.m */; }; @@ -291,6 +298,9 @@ 33C374360DD8D44800E97817 /* GTMNSDictionary+URLArguments.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSDictionary+URLArguments.h"; sourceTree = "<group>"; }; 33C374370DD8D44800E97817 /* GTMNSDictionary+URLArguments.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSDictionary+URLArguments.m"; sourceTree = "<group>"; }; 33C3745E0DD8D85B00E97817 /* GTMNSDictionary+URLArgumentsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSDictionary+URLArgumentsTest.m"; sourceTree = "<group>"; }; + 7F3EB38C0E5E09C700A7A75E /* GTMNSImage+Scaling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSImage+Scaling.h"; sourceTree = "<group>"; }; + 7F3EB38D0E5E09C700A7A75E /* GTMNSImage+Scaling.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSImage+Scaling.m"; sourceTree = "<group>"; }; + 7F3EB3930E5E0A2100A7A75E /* GTMNSImage+ScalingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSImage+ScalingTest.m"; sourceTree = "<group>"; }; 8B1801A00E2533D500280961 /* GTMLargeTypeWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMLargeTypeWindow.m; sourceTree = "<group>"; }; 8B1801A10E2533D500280961 /* GTMLargeTypeWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMLargeTypeWindow.h; sourceTree = "<group>"; }; 8B1801A40E2533DB00280961 /* GTMLargeTypeWindowTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMLargeTypeWindowTest.m; sourceTree = "<group>"; }; @@ -301,6 +311,9 @@ 8B1A14E90D900BC800CA1E8E /* GTMNSObject+BindingUnitTesting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSObject+BindingUnitTesting.m"; sourceTree = "<group>"; }; 8B1A14EA0D900BC800CA1E8E /* GTMNSObject+BindingUnitTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSObject+BindingUnitTesting.h"; sourceTree = "<group>"; }; 8B1A16050D90344B00CA1E8E /* GTMDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMDefines.h; sourceTree = "<group>"; }; + 8B1B49160E5F8E2100A08972 /* GTMExceptionalInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMExceptionalInlines.h; sourceTree = "<group>"; }; + 8B1B49170E5F8E2100A08972 /* GTMExceptionalInlines.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMExceptionalInlines.m; sourceTree = "<group>"; }; + 8B1B491B0E5F904C00A08972 /* GTMExceptionalInlinesTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMExceptionalInlinesTest.m; sourceTree = "<group>"; }; 8B3344170DBF7A36009FD32C /* GTMNSAppleScript+HandlerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSAppleScript+HandlerTest.m"; sourceTree = "<group>"; }; 8B3344180DBF7A36009FD32C /* GTMNSAppleScript+Handler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSAppleScript+Handler.m"; sourceTree = "<group>"; }; 8B3344190DBF7A36009FD32C /* GTMNSAppleScript+Handler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSAppleScript+Handler.h"; sourceTree = "<group>"; }; @@ -674,6 +687,9 @@ F47F1C0D0D490BC000925B8F /* GTMNSBezierPath+Shading.h */, F47F1C0E0D490BC000925B8F /* GTMNSBezierPath+Shading.m */, F47F1C110D490BC000925B8F /* GTMNSBezierPath+ShadingTest.m */, + 7F3EB38C0E5E09C700A7A75E /* GTMNSImage+Scaling.h */, + 7F3EB38D0E5E09C700A7A75E /* GTMNSImage+Scaling.m */, + 7F3EB3930E5E0A2100A7A75E /* GTMNSImage+ScalingTest.m */, F47F1C740D490E5C00925B8F /* GTMShading.h */, F435E4840DC8F3DC0069CDE8 /* TestData */, ); @@ -696,6 +712,9 @@ F41D25880DBD21A300774EEB /* GTMBase64.h */, F41D25890DBD21A300774EEB /* GTMBase64.m */, F41D258A0DBD21A300774EEB /* GTMBase64Test.m */, + 8B1B49160E5F8E2100A08972 /* GTMExceptionalInlines.h */, + 8B1B49170E5F8E2100A08972 /* GTMExceptionalInlines.m */, + 8B1B491B0E5F904C00A08972 /* GTMExceptionalInlinesTest.m */, 8BE2836B0DED0F130035B3F8 /* GTMFourCharCode.m */, 8BE2836C0DED0F130035B3F8 /* GTMFourCharCodeTest.m */, 8BE2836D0DED0F130035B3F8 /* GTMFourCharCode.h */, @@ -899,6 +918,8 @@ F92B9FA80E2E64B900A2FE61 /* GTMLogger.h in Headers */, F92B9FA90E2E64BC00A2FE61 /* GTMLogger+ASL.h in Headers */, F95803FA0E2FB08F0049A088 /* GTMLoggerRingBufferWriter.h in Headers */, + 8B1B49180E5F8E2100A08972 /* GTMExceptionalInlines.h in Headers */, + 7F3EB38E0E5E09C700A7A75E /* GTMNSImage+Scaling.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1040,7 +1061,7 @@ BuildIndependentTargetsInParallel = YES; }; buildConfigurationList = 1DEB918108733D990010E9CD /* Build configuration list for PBXProject "GTM" */; - compatibilityVersion = "Xcode 3.0"; + compatibilityVersion = "Xcode 3.1"; hasScannedForEncodings = 1; mainGroup = 0867D691FE84028FC02AAC07 /* GTM */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; @@ -1112,6 +1133,7 @@ 8B1801B20E25341B00280961 /* GTMLargeTypeWindowLongTextTest.gtmUTState in Resources */, 8B1801B30E25341B00280961 /* GTMLargeTypeWindowShortTextTest.gtmUTState in Resources */, 8B1802420E25592200280961 /* GTMLargeTypeWindowMediumTextTest.gtmUTState in Resources */, + 7F3EB5540E5F0B0400A7A75E /* GTMUnitTestingImage.tiff in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1228,6 +1250,7 @@ F98681960E2C20C100CEE8BF /* GTMLogger+ASLTest.m in Sources */, F95803FB0E2FB0A80049A088 /* GTMLoggerRingBufferWriterTest.m in Sources */, F427EFC40E4023DD00ADD2AA /* GTMProgressMonitorInputStreamTest.m in Sources */, + 8B1B49260E5F97C800A08972 /* GTMExceptionalInlinesTest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1275,6 +1298,8 @@ F95803F90E2FB0850049A088 /* GTMLoggerRingBufferWriter.m in Sources */, 8B61FDC00E4CDB8000FF9C21 /* GTMStackTrace.m in Sources */, 8B58E9950E547EB000A0E02E /* GTMGetURLHandler.m in Sources */, + 8B1B49190E5F8E2100A08972 /* GTMExceptionalInlines.m in Sources */, + 7F3EB38F0E5E09C700A7A75E /* GTMNSImage+Scaling.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1292,7 +1317,8 @@ 8B7DCBC10DFF0F7F0017E983 /* GTMMethodCheck.m in Sources */, 8B7DCBED0DFF1A4F0017E983 /* GTMUnitTestDevLog.m in Sources */, 8B7DCE190DFF39850017E983 /* GTMSenTestCase.m in Sources */, - 8B1801A50E2533DB00280961 /* GTMLargeTypeWindowTest.m in Sources */, + 7F3EB3940E5E0A2100A7A75E /* GTMNSImage+ScalingTest.m in Sources */, + 7F3EB5870E5F0CBB00A7A75E /* GTMLargeTypeWindowTest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/GTMiPhone.xcodeproj/project.pbxproj b/GTMiPhone.xcodeproj/project.pbxproj index 5cc5cf0..bf1133b 100644 --- a/GTMiPhone.xcodeproj/project.pbxproj +++ b/GTMiPhone.xcodeproj/project.pbxproj @@ -29,9 +29,7 @@ 8B3AA8F30E032FC7007E31B5 /* GTMNSString+URLArguments.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3AA8F10E032FC7007E31B5 /* GTMNSString+URLArguments.m */; }; 8B3AA8F40E032FC7007E31B5 /* GTMNSString+URLArgumentsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3AA8F20E032FC7007E31B5 /* GTMNSString+URLArgumentsTest.m */; }; 8B3AA9220E033624007E31B5 /* GTMHTTPFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3AA91D0E033624007E31B5 /* GTMHTTPFetcher.m */; }; - 8B3AA9230E033624007E31B5 /* GTMHTTPFetcherTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3AA91E0E033624007E31B5 /* GTMHTTPFetcherTest.m */; }; 8B3AA9240E033624007E31B5 /* GTMHTTPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3AA9200E033624007E31B5 /* GTMHTTPServer.m */; }; - 8B3AA9250E033624007E31B5 /* GTMHTTPServerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3AA9210E033624007E31B5 /* GTMHTTPServerTest.m */; }; 8B3AA9290E033647007E31B5 /* GTMTestHTTPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3AA9280E033647007E31B5 /* GTMTestHTTPServer.m */; }; 8B3AA9340E0336AC007E31B5 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B3AA9330E0336AC007E31B5 /* CFNetwork.framework */; }; 8B3AA96A0E0337E4007E31B5 /* GTMHTTPFetcherTestPage.html in Resources */ = {isa = PBXBuildFile; fileRef = 8B3AA9690E0337E4007E31B5 /* GTMHTTPFetcherTestPage.html */; }; @@ -39,12 +37,15 @@ 8B41EC100E0711D40040CF9F /* GTMValidatingContainers.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B41EC0D0E0711D40040CF9F /* GTMValidatingContainers.m */; }; 8B5547CA0DB3BBF20014CC1C /* GTMUIKit+UnitTesting.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B5547C70DB3BBF20014CC1C /* GTMUIKit+UnitTesting.m */; }; 8B5547CB0DB3BBF20014CC1C /* GTMUIKit+UnitTestingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B5547C90DB3BBF20014CC1C /* GTMUIKit+UnitTestingTest.m */; }; + 8B5A9E200E71CB6C005DA441 /* AddressBook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B5A9E1F0E71CB6C005DA441 /* AddressBook.framework */; }; 8B7DCEAA0DFF4C760017E983 /* GTMDevLogUnitTestingBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B7DCEA90DFF4C760017E983 /* GTMDevLogUnitTestingBridge.m */; }; 8B7DCEAD0DFF4CA60017E983 /* GTMUnitTestDevLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B7DCEAC0DFF4CA60017E983 /* GTMUnitTestDevLog.m */; }; + 8BA5F40B0E75669000798036 /* GTMABAddressBook.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BA5F4080E75669000798036 /* GTMABAddressBook.m */; }; + 8BA5F40C0E75669000798036 /* phone.png in Resources */ = {isa = PBXBuildFile; fileRef = 8BA5F40A0E75669000798036 /* phone.png */; }; + 8BA5F52C0E7567AB00798036 /* GTMABAddressBookTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BA5F52B0E7567AB00798036 /* GTMABAddressBookTest.m */; }; 8BC0480F0DAE928A00C2D1CA /* GTMCalculatedRange.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC047780DAE928A00C2D1CA /* GTMCalculatedRange.m */; }; 8BC048100DAE928A00C2D1CA /* GTMCalculatedRangeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC047790DAE928A00C2D1CA /* GTMCalculatedRangeTest.m */; }; 8BC048130DAE928A00C2D1CA /* GTMNSData+zlib.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC0477F0DAE928A00C2D1CA /* GTMNSData+zlib.m */; }; - 8BC048140DAE928A00C2D1CA /* GTMNSData+zlibTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC047800DAE928A00C2D1CA /* GTMNSData+zlibTest.m */; }; 8BC048150DAE928A00C2D1CA /* GTMNSEnumerator+Filter.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC047820DAE928A00C2D1CA /* GTMNSEnumerator+Filter.m */; }; 8BC048160DAE928A00C2D1CA /* GTMNSEnumerator+FilterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC047830DAE928A00C2D1CA /* GTMNSEnumerator+FilterTest.m */; }; 8BC048170DAE928A00C2D1CA /* GTMNSFileManager+Path.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC047850DAE928A00C2D1CA /* GTMNSFileManager+Path.m */; }; @@ -67,6 +68,21 @@ 8BC04A750DAF145200C2D1CA /* GTMSystemVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC04A740DAF145200C2D1CA /* GTMSystemVersion.m */; }; 8BC04D480DB0088500C2D1CA /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BC04D470DB0088500C2D1CA /* libz.dylib */; }; 8BC04DE80DB023D400C2D1CA /* ReleaseNotes.txt in Resources */ = {isa = PBXBuildFile; fileRef = 8BC04DE70DB023D400C2D1CA /* ReleaseNotes.txt */; }; + 8BDA25120E759A6300C9769D /* GTMHTTPFetcherTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3AA91E0E033624007E31B5 /* GTMHTTPFetcherTest.m */; }; + 8BDA25130E759A6400C9769D /* GTMHTTPServerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3AA9210E033624007E31B5 /* GTMHTTPServerTest.m */; }; + 8BDA25140E759A6500C9769D /* GTMNSData+zlibTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC047800DAE928A00C2D1CA /* GTMNSData+zlibTest.m */; }; + F418AF990E7558EC004FB565 /* GTMExceptionalInlines.m in Sources */ = {isa = PBXBuildFile; fileRef = F418AF940E7558DC004FB565 /* GTMExceptionalInlines.m */; }; + F418AF9A0E7558EC004FB565 /* GTMExceptionalInlinesTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F418AF950E7558DC004FB565 /* GTMExceptionalInlinesTest.m */; }; + F418AFA50E7559C7004FB565 /* GTMLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = F418AFA30E7559C7004FB565 /* GTMLogger.m */; }; + F418AFA60E7559C7004FB565 /* GTMLoggerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F418AFA40E7559C7004FB565 /* GTMLoggerTest.m */; }; + F418AFB40E755B4D004FB565 /* GTMLoggerRingBufferWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = F418AFB20E755B4D004FB565 /* GTMLoggerRingBufferWriter.m */; }; + F418AFB50E755B4D004FB565 /* GTMLoggerRingBufferWriterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F418AFB30E755B4D004FB565 /* GTMLoggerRingBufferWriterTest.m */; }; + F418AFCD0E755C94004FB565 /* GTMNSDictionary+URLArguments.m in Sources */ = {isa = PBXBuildFile; fileRef = F418AFCB0E755C94004FB565 /* GTMNSDictionary+URLArguments.m */; }; + F418AFCE0E755C94004FB565 /* GTMNSDictionary+URLArgumentsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F418AFCC0E755C94004FB565 /* GTMNSDictionary+URLArgumentsTest.m */; }; + F418AFD70E755D44004FB565 /* GTMPath.m in Sources */ = {isa = PBXBuildFile; fileRef = F418AFD50E755D44004FB565 /* GTMPath.m */; }; + F418AFD80E755D44004FB565 /* GTMPathTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F418AFD60E755D44004FB565 /* GTMPathTest.m */; }; + F418AFEB0E755F21004FB565 /* GTMProgressMonitorInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F418AFE90E755F21004FB565 /* GTMProgressMonitorInputStream.m */; }; + F418AFEC0E755F21004FB565 /* GTMProgressMonitorInputStreamTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F418AFEA0E755F21004FB565 /* GTMProgressMonitorInputStreamTest.m */; }; 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 */; }; @@ -91,7 +107,6 @@ 32CA4F630368D1EE00C91783 /* GTM_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTM_Prefix.pch; sourceTree = "<group>"; }; 67A7820A0E00927400EBF506 /* GTMIPhoneUnitTestDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMIPhoneUnitTestDelegate.h; sourceTree = "<group>"; }; 67A7820B0E00927400EBF506 /* GTMIPhoneUnitTestDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMIPhoneUnitTestDelegate.m; sourceTree = "<group>"; }; - 8B308AF40DAD070C00183556 /* RunIPhoneUnitTest.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = RunIPhoneUnitTest.sh; path = UnitTesting/RunIPhoneUnitTest.sh; sourceTree = "<group>"; }; 8B308BCD0DAD0B8400183556 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 8B3AA8F00E032FC7007E31B5 /* GTMNSString+URLArguments.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSString+URLArguments.h"; sourceTree = "<group>"; }; 8B3AA8F10E032FC7007E31B5 /* GTMNSString+URLArguments.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+URLArguments.m"; sourceTree = "<group>"; }; @@ -112,9 +127,14 @@ 8B5547C70DB3BBF20014CC1C /* GTMUIKit+UnitTesting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMUIKit+UnitTesting.m"; sourceTree = "<group>"; }; 8B5547C80DB3BBF20014CC1C /* GTMUIKit+UnitTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMUIKit+UnitTesting.h"; sourceTree = "<group>"; }; 8B5547C90DB3BBF20014CC1C /* GTMUIKit+UnitTestingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMUIKit+UnitTestingTest.m"; sourceTree = "<group>"; }; + 8B5A9E1F0E71CB6C005DA441 /* AddressBook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBook.framework; path = System/Library/Frameworks/AddressBook.framework; sourceTree = SDKROOT; }; 8B7DCEA90DFF4C760017E983 /* GTMDevLogUnitTestingBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMDevLogUnitTestingBridge.m; sourceTree = "<group>"; }; 8B7DCEAB0DFF4CA60017E983 /* GTMUnitTestDevLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMUnitTestDevLog.h; sourceTree = "<group>"; }; 8B7DCEAC0DFF4CA60017E983 /* GTMUnitTestDevLog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMUnitTestDevLog.m; sourceTree = "<group>"; }; + 8BA5F4070E75669000798036 /* GTMABAddressBook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMABAddressBook.h; sourceTree = "<group>"; }; + 8BA5F4080E75669000798036 /* GTMABAddressBook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMABAddressBook.m; sourceTree = "<group>"; }; + 8BA5F40A0E75669000798036 /* phone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = phone.png; sourceTree = "<group>"; }; + 8BA5F52B0E7567AB00798036 /* GTMABAddressBookTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMABAddressBookTest.m; sourceTree = "<group>"; }; 8BC047750DAE926E00C2D1CA /* GTMDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMDefines.h; sourceTree = "<group>"; }; 8BC047770DAE928A00C2D1CA /* GTMCalculatedRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMCalculatedRange.h; sourceTree = "<group>"; }; 8BC047780DAE928A00C2D1CA /* GTMCalculatedRange.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMCalculatedRange.m; sourceTree = "<group>"; }; @@ -162,6 +182,26 @@ 8BC04A740DAF145200C2D1CA /* GTMSystemVersion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMSystemVersion.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>"; }; + F418AF6D0E755732004FB565 /* GTMiPhone-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GTMiPhone-Info.plist"; sourceTree = "<group>"; }; + F418AF910E755893004FB565 /* xcconfigs-readme.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "xcconfigs-readme.txt"; sourceTree = "<group>"; }; + F418AF930E7558DC004FB565 /* GTMExceptionalInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMExceptionalInlines.h; sourceTree = "<group>"; }; + F418AF940E7558DC004FB565 /* GTMExceptionalInlines.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMExceptionalInlines.m; sourceTree = "<group>"; }; + F418AF950E7558DC004FB565 /* GTMExceptionalInlinesTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMExceptionalInlinesTest.m; sourceTree = "<group>"; }; + F418AFA20E7559C7004FB565 /* GTMLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMLogger.h; sourceTree = "<group>"; }; + F418AFA30E7559C7004FB565 /* GTMLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMLogger.m; sourceTree = "<group>"; }; + F418AFA40E7559C7004FB565 /* GTMLoggerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMLoggerTest.m; sourceTree = "<group>"; }; + F418AFB10E755B4D004FB565 /* GTMLoggerRingBufferWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMLoggerRingBufferWriter.h; sourceTree = "<group>"; }; + F418AFB20E755B4D004FB565 /* GTMLoggerRingBufferWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMLoggerRingBufferWriter.m; sourceTree = "<group>"; }; + F418AFB30E755B4D004FB565 /* GTMLoggerRingBufferWriterTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMLoggerRingBufferWriterTest.m; sourceTree = "<group>"; }; + F418AFCA0E755C94004FB565 /* GTMNSDictionary+URLArguments.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSDictionary+URLArguments.h"; sourceTree = "<group>"; }; + F418AFCB0E755C94004FB565 /* GTMNSDictionary+URLArguments.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSDictionary+URLArguments.m"; sourceTree = "<group>"; }; + F418AFCC0E755C94004FB565 /* GTMNSDictionary+URLArgumentsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSDictionary+URLArgumentsTest.m"; sourceTree = "<group>"; }; + F418AFD40E755D44004FB565 /* GTMPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMPath.h; sourceTree = "<group>"; }; + F418AFD50E755D44004FB565 /* GTMPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMPath.m; sourceTree = "<group>"; }; + F418AFD60E755D44004FB565 /* GTMPathTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMPathTest.m; sourceTree = "<group>"; }; + F418AFE80E755F21004FB565 /* GTMProgressMonitorInputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMProgressMonitorInputStream.h; sourceTree = "<group>"; }; + F418AFE90E755F21004FB565 /* GTMProgressMonitorInputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMProgressMonitorInputStream.m; sourceTree = "<group>"; }; + F418AFEA0E755F21004FB565 /* GTMProgressMonitorInputStreamTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMProgressMonitorInputStreamTest.m; 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>"; }; @@ -181,6 +221,7 @@ 8B308BCE0DAD0B8400183556 /* QuartzCore.framework in Frameworks */, 8BC04D480DB0088500C2D1CA /* libz.dylib in Frameworks */, 8B3AA9340E0336AC007E31B5 /* CFNetwork.framework in Frameworks */, + 8B5A9E200E71CB6C005DA441 /* AddressBook.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -200,14 +241,15 @@ children = ( 8BC04DE70DB023D400C2D1CA /* ReleaseNotes.txt */, 8BC047750DAE926E00C2D1CA /* GTMDefines.h */, + 8BA5F4060E75669000798036 /* iPhone */, 8BC047760DAE928A00C2D1CA /* Foundation */, 8BC0479A0DAE928A00C2D1CA /* DebugUtils */, 8BC0479F0DAE928A00C2D1CA /* UnitTesting */, 8BC049840DAEC59100C2D1CA /* XcodeConfig */, - 32CA4F630368D1EE00C91783 /* GTM_Prefix.pch */, - 8B308AF40DAD070C00183556 /* RunIPhoneUnitTest.sh */, 29B97323FDCFA39411CA2CEA /* Frameworks */, 19C28FACFE9D520D11CA2CBB /* Products */, + 32CA4F630368D1EE00C91783 /* GTM_Prefix.pch */, + F418AF6D0E755732004FB565 /* GTMiPhone-Info.plist */, ); name = CustomTemplate; sourceTree = "<group>"; @@ -216,6 +258,7 @@ isa = PBXGroup; children = ( 8B3AA9330E0336AC007E31B5 /* CFNetwork.framework */, + 8B5A9E1F0E71CB6C005DA441 /* AddressBook.framework */, 8BC04D470DB0088500C2D1CA /* libz.dylib */, 8B308BCD0DAD0B8400183556 /* QuartzCore.framework */, 1D3623EB0D0F72F000981E51 /* CoreGraphics.framework */, @@ -233,15 +276,40 @@ path = TestData; sourceTree = "<group>"; }; + 8BA5F4060E75669000798036 /* iPhone */ = { + isa = PBXGroup; + children = ( + 8BA5F4070E75669000798036 /* GTMABAddressBook.h */, + 8BA5F4080E75669000798036 /* GTMABAddressBook.m */, + 8BA5F52B0E7567AB00798036 /* GTMABAddressBookTest.m */, + 8BA5F4090E75669000798036 /* TestData */, + ); + path = iPhone; + sourceTree = "<group>"; + }; + 8BA5F4090E75669000798036 /* TestData */ = { + isa = PBXGroup; + children = ( + 8BA5F40A0E75669000798036 /* phone.png */, + ); + path = TestData; + sourceTree = "<group>"; + }; 8BC047760DAE928A00C2D1CA /* Foundation */ = { isa = PBXGroup; children = ( + F418AFD40E755D44004FB565 /* GTMPath.h */, + F418AFD50E755D44004FB565 /* GTMPath.m */, + F418AFD60E755D44004FB565 /* GTMPathTest.m */, F439ADE80DBD3C0000BE9B91 /* GTMBase64.h */, F439ADE90DBD3C0000BE9B91 /* GTMBase64.m */, F439ADEA0DBD3C0000BE9B91 /* GTMBase64Test.m */, 8BC047770DAE928A00C2D1CA /* GTMCalculatedRange.h */, 8BC047780DAE928A00C2D1CA /* GTMCalculatedRange.m */, 8BC047790DAE928A00C2D1CA /* GTMCalculatedRangeTest.m */, + F418AF930E7558DC004FB565 /* GTMExceptionalInlines.h */, + F418AF940E7558DC004FB565 /* GTMExceptionalInlines.m */, + F418AF950E7558DC004FB565 /* GTMExceptionalInlinesTest.m */, 8BC0477A0DAE928A00C2D1CA /* GTMGarbageCollection.h */, F439ADED0DBD3C4000BE9B91 /* GTMGeometryUtils.h */, F439ADEE0DBD3C4000BE9B91 /* GTMGeometryUtils.m */, @@ -252,9 +320,18 @@ 8B3AA91F0E033624007E31B5 /* GTMHTTPServer.h */, 8B3AA9200E033624007E31B5 /* GTMHTTPServer.m */, 8B3AA9210E033624007E31B5 /* GTMHTTPServerTest.m */, + F418AFA20E7559C7004FB565 /* GTMLogger.h */, + F418AFA30E7559C7004FB565 /* GTMLogger.m */, + F418AFA40E7559C7004FB565 /* GTMLoggerTest.m */, + F418AFB10E755B4D004FB565 /* GTMLoggerRingBufferWriter.h */, + F418AFB20E755B4D004FB565 /* GTMLoggerRingBufferWriter.m */, + F418AFB30E755B4D004FB565 /* GTMLoggerRingBufferWriterTest.m */, 8BC0477E0DAE928A00C2D1CA /* GTMNSData+zlib.h */, 8BC0477F0DAE928A00C2D1CA /* GTMNSData+zlib.m */, 8BC047800DAE928A00C2D1CA /* GTMNSData+zlibTest.m */, + F418AFCA0E755C94004FB565 /* GTMNSDictionary+URLArguments.h */, + F418AFCB0E755C94004FB565 /* GTMNSDictionary+URLArguments.m */, + F418AFCC0E755C94004FB565 /* GTMNSDictionary+URLArgumentsTest.m */, 8BC047810DAE928A00C2D1CA /* GTMNSEnumerator+Filter.h */, 8BC047820DAE928A00C2D1CA /* GTMNSEnumerator+Filter.m */, 8BC047830DAE928A00C2D1CA /* GTMNSEnumerator+FilterTest.m */, @@ -272,6 +349,9 @@ 8BC0478C0DAE928A00C2D1CA /* GTMNSString+XMLTest.m */, 8BC0478D0DAE928A00C2D1CA /* GTMObjC2Runtime.h */, 8BC047900DAE928A00C2D1CA /* GTMObjectSingleton.h */, + F418AFE80E755F21004FB565 /* GTMProgressMonitorInputStream.h */, + F418AFE90E755F21004FB565 /* GTMProgressMonitorInputStream.m */, + F418AFEA0E755F21004FB565 /* GTMProgressMonitorInputStreamTest.m */, 8BC047910DAE928A00C2D1CA /* GTMRegex.h */, 8BC047920DAE928A00C2D1CA /* GTMRegex.m */, 8BC047930DAE928A00C2D1CA /* GTMRegexTest.m */, @@ -327,6 +407,7 @@ 8BC049840DAEC59100C2D1CA /* XcodeConfig */ = { isa = PBXGroup; children = ( + F418AF910E755893004FB565 /* xcconfigs-readme.txt */, 8BC04F020DB15A5300C2D1CA /* Project */, 8BC0498E0DAEC59100C2D1CA /* subconfig */, ); @@ -406,6 +487,7 @@ 8BC0486C0DAE928A00C2D1CA /* GTMUIViewUnitTestingTest.png in Resources */, 8BC04DE80DB023D400C2D1CA /* ReleaseNotes.txt in Resources */, 8B3AA96A0E0337E4007E31B5 /* GTMHTTPFetcherTestPage.html in Resources */, + 8BA5F40C0E75669000798036 /* phone.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -435,7 +517,6 @@ 8BC0480F0DAE928A00C2D1CA /* GTMCalculatedRange.m in Sources */, 8BC048100DAE928A00C2D1CA /* GTMCalculatedRangeTest.m in Sources */, 8BC048130DAE928A00C2D1CA /* GTMNSData+zlib.m in Sources */, - 8BC048140DAE928A00C2D1CA /* GTMNSData+zlibTest.m in Sources */, 8BC048150DAE928A00C2D1CA /* GTMNSEnumerator+Filter.m in Sources */, 8BC048160DAE928A00C2D1CA /* GTMNSEnumerator+FilterTest.m in Sources */, 8BC048170DAE928A00C2D1CA /* GTMNSFileManager+Path.m in Sources */, @@ -466,12 +547,27 @@ 8B3AA8F30E032FC7007E31B5 /* GTMNSString+URLArguments.m in Sources */, 8B3AA8F40E032FC7007E31B5 /* GTMNSString+URLArgumentsTest.m in Sources */, 8B3AA9220E033624007E31B5 /* GTMHTTPFetcher.m in Sources */, - 8B3AA9230E033624007E31B5 /* GTMHTTPFetcherTest.m in Sources */, 8B3AA9240E033624007E31B5 /* GTMHTTPServer.m in Sources */, - 8B3AA9250E033624007E31B5 /* GTMHTTPServerTest.m in Sources */, 8B3AA9290E033647007E31B5 /* GTMTestHTTPServer.m in Sources */, 8B41EC0F0E0711D40040CF9F /* GTMValidatingContainersTest.m in Sources */, 8B41EC100E0711D40040CF9F /* GTMValidatingContainers.m in Sources */, + F418AF990E7558EC004FB565 /* GTMExceptionalInlines.m in Sources */, + F418AF9A0E7558EC004FB565 /* GTMExceptionalInlinesTest.m in Sources */, + F418AFA50E7559C7004FB565 /* GTMLogger.m in Sources */, + F418AFA60E7559C7004FB565 /* GTMLoggerTest.m in Sources */, + F418AFB40E755B4D004FB565 /* GTMLoggerRingBufferWriter.m in Sources */, + F418AFB50E755B4D004FB565 /* GTMLoggerRingBufferWriterTest.m in Sources */, + F418AFCD0E755C94004FB565 /* GTMNSDictionary+URLArguments.m in Sources */, + F418AFCE0E755C94004FB565 /* GTMNSDictionary+URLArgumentsTest.m in Sources */, + F418AFD70E755D44004FB565 /* GTMPath.m in Sources */, + F418AFD80E755D44004FB565 /* GTMPathTest.m in Sources */, + F418AFEB0E755F21004FB565 /* GTMProgressMonitorInputStream.m in Sources */, + F418AFEC0E755F21004FB565 /* GTMProgressMonitorInputStreamTest.m in Sources */, + 8BA5F40B0E75669000798036 /* GTMABAddressBook.m in Sources */, + 8BA5F52C0E7567AB00798036 /* GTMABAddressBookTest.m in Sources */, + 8BDA25120E759A6300C9769D /* GTMHTTPFetcherTest.m in Sources */, + 8BDA25130E759A6400C9769D /* GTMHTTPServerTest.m in Sources */, + 8BDA25140E759A6500C9769D /* GTMNSData+zlibTest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -506,8 +602,8 @@ isa = XCBuildConfiguration; baseConfigurationReference = 8BC049850DAEC59100C2D1CA /* DebugiPhone.xcconfig */; buildSettings = { - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; GCC_PREFIX_HEADER = GTM_Prefix.pch; + SDKROOT = iphonesimulator2.0; }; name = "Debug-gcov"; }; @@ -525,6 +621,7 @@ baseConfigurationReference = 8BC049850DAEC59100C2D1CA /* DebugiPhone.xcconfig */; buildSettings = { GCC_PREFIX_HEADER = GTM_Prefix.pch; + SDKROOT = iphonesimulator2.0; }; name = Debug; }; @@ -533,6 +630,7 @@ baseConfigurationReference = 8BC049890DAEC59100C2D1CA /* ReleaseiPhone.xcconfig */; buildSettings = { GCC_PREFIX_HEADER = GTM_Prefix.pch; + SDKROOT = iphonesimulator2.0; }; name = Release; }; diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index 047935d..5dbfe96 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -85,6 +85,21 @@ Changes since 1.5.1 Allows you to index .xcodeproj, .scpt, .scptd, .xib, .nib, and .aib files. See Readmes beside individual projects in SpotlightPlugins. +- Added GTMExceptionalInlines for dealing with cases where you get + warning: variable 'r' might be clobbered by 'longjmp' or 'vfork' + when using certain Apple inlined functions in @synchronized/@try blocks. + +- Updated to Xcode 3.1 so the GTM and iPhone project have the same baseline. + The code should work in other version of xcode, but the projects and + xcconfig files now use 3.1 features. + +- Added GTMABAddressBook which is a cocoa wrapper for the 'C' AddressBook + APIs on the iPhone. + +- Added several set environment variable statements to RunIPhoneUnitTest.sh + to encourage bugs to come out of the woodwork. + + Release 1.5.1 Changes since 1.5.0 16-June-2008 diff --git a/UnitTesting/GTMIPhoneUnitTestDelegate.m b/UnitTesting/GTMIPhoneUnitTestDelegate.m index f3df548..f93c472 100644 --- a/UnitTesting/GTMIPhoneUnitTestDelegate.m +++ b/UnitTesting/GTMIPhoneUnitTestDelegate.m @@ -161,7 +161,7 @@ static int MethodSort(const void *a, const void *b) { NSDate *fixtureEndDate = [NSDate date]; NSTimeInterval fixtureEndTime = [fixtureEndDate timeIntervalSinceDate:fixtureStartDate]; NSString *fixtureEndString = [NSString stringWithFormat:@"Test Suite '%@' finished at %@.\n" - "Executed %d tests, with %d failures (%d unexpected) in %0.3f (%0.3f) seconds\n", + "Executed %d tests, with %d failures (%d unexpected) in %0.3f (%0.3f) seconds\n\n", fixtureName, fixtureEndDate, fixtureTotal, fixtureFailures, fixtureFailures, fixtureEndTime, fixtureEndTime]; @@ -176,7 +176,7 @@ static int MethodSort(const void *a, const void *b) { NSDate *suiteEndDate = [NSDate date]; NSTimeInterval suiteEndTime = [suiteEndDate timeIntervalSinceDate:suiteStartDate]; NSString *suiteEndString = [NSString stringWithFormat:@"Test Suite '%@' finished at %@.\n" - "Executed %d tests, with %d failures (%d unexpected) in %0.3f (%0.3f) seconds\n", + "Executed %d tests, with %d failures (%d unexpected) in %0.3f (%0.3f) seconds\n\n", suiteName, suiteEndDate, suiteTotal, suiteFailures, suiteFailures, suiteEndTime, suiteEndTime]; diff --git a/UnitTesting/GTMUIKit+UnitTesting.h b/UnitTesting/GTMUIKit+UnitTesting.h index 578ee4d..bb78a7a 100644 --- a/UnitTesting/GTMUIKit+UnitTesting.h +++ b/UnitTesting/GTMUIKit+UnitTesting.h @@ -85,6 +85,18 @@ - (BOOL)gtm_shouldEncodeStateForSubviews; @end +// Category to help UIImage testing. UIImage can be tested using +// GTMAssertObjectImageEqualToImageNamed macro, which automatically creates +// result images and diff images in case test fails. +@interface UIImage (GTMUnitTestingAdditions) <GTMUnitTestingImaging> +// Returns an image containing a representation suitable for use in comparing +// against a master image. +// +// Returns: +// an image of the object +- (CGImageRef)gtm_createUnitTestImage; +@end + // A view that allows you to delegate out drawing using the formal // GTMUnitTestViewDelegate protocol // This is useful when writing up unit tests for visual elements. diff --git a/UnitTesting/GTMUIKit+UnitTesting.m b/UnitTesting/GTMUIKit+UnitTesting.m index 7b98886..d1cf2b2 100644 --- a/UnitTesting/GTMUIKit+UnitTesting.m +++ b/UnitTesting/GTMUIKit+UnitTesting.m @@ -116,3 +116,11 @@ return NO; } @end + +@implementation UIImage (GTMUnitTestingAdditions) +- (CGImageRef)gtm_createUnitTestImage { + CGImageRef imageRef = [self CGImage]; + CGImageRetain(imageRef); + return imageRef; +} +@end diff --git a/UnitTesting/GTMUIKit+UnitTestingTest.m b/UnitTesting/GTMUIKit+UnitTestingTest.m index d39ba9e..8b3effa 100644 --- a/UnitTesting/GTMUIKit+UnitTestingTest.m +++ b/UnitTesting/GTMUIKit+UnitTestingTest.m @@ -40,6 +40,13 @@ GTMAssertObjectStateEqualToStateNamed(view, @"GTMUIViewUnitTestingTest", nil); } +- (void)testUIImage { + NSString* name = @"GTMUIViewUnitTestingTest"; + UIImage* image = + [UIImage imageNamed:[name stringByAppendingPathExtension:@"png"]]; + GTMAssertObjectImageEqualToImageNamed(image, name, nil); +} + - (void)gtm_unitTestViewDrawRect:(CGRect)rect contextInfo:(void*)contextInfo { UIApplication *app = [UIApplication sharedApplication]; STAssertEqualObjects(app, diff --git a/UnitTesting/RunIPhoneUnitTest.sh b/UnitTesting/RunIPhoneUnitTest.sh index 9c09622..0089032 100755 --- a/UnitTesting/RunIPhoneUnitTest.sh +++ b/UnitTesting/RunIPhoneUnitTest.sh @@ -17,11 +17,27 @@ # Runs all unittests through the iPhone simulator. We don't handle running them # on the device. To run on the device just choose "run". + if [ "$IPHONEOS_DEPLOYMENT_TARGET" == "" ]; then + # We kill the iPhone simulator because otherwise we run into issues where + # the unittests fail becuase the simulator is currently running, and + # at this time the iPhone SDK won't allow two simulators running at the same + # time. + /usr/bin/killall "iPhone Simulator" export DYLD_ROOT_PATH="$SDKROOT" export DYLD_FRAMEWORK_PATH="$CONFIGURATION_BUILD_DIR" export IPHONE_SIMULATOR_ROOT="$SDKROOT" export CFFIXED_USER_HOME="$USER_LIBRARY_DIR/Application Support/iPhone Simulator/User" + + # Encourage errors + export MallocScribble=YES + export MallocPreScribble=YES + export MallocGuardEdges=YES + export CFZombieLevel=3 + export NSAutoreleaseFreedObjectCheckEnabled=YES + export NSZombieEnabled=YES + export OBJC_DEBUG_FRAGILE_SUPERCLASSES=YES + "$TARGET_BUILD_DIR/$EXECUTABLE_PATH" -RegisterForSystemEvents else echo "note: Skipping running of unittests for device build." diff --git a/UnitTesting/RunMacOSUnitTests.sh b/UnitTesting/RunMacOSUnitTests.sh index 2c8440a..45d0afc 100755 --- a/UnitTesting/RunMacOSUnitTests.sh +++ b/UnitTesting/RunMacOSUnitTests.sh @@ -1,5 +1,5 @@ # -# RunUnitTests.sh +# RunMacOSUnitTests.sh # Copyright 2008 Google Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may not diff --git a/XcodeConfig/Project/DebugLeopardOrLater.xcconfig b/XcodeConfig/Project/DebugLeopardOrLater.xcconfig index 6aebf12..5bbd4a2 100644 --- a/XcodeConfig/Project/DebugLeopardOrLater.xcconfig +++ b/XcodeConfig/Project/DebugLeopardOrLater.xcconfig @@ -1,7 +1,8 @@ // // DebugLeopardOrLater.xcconfig // -// Xcode configuration file for building a Debug target on Leopard or later. +// Xcode configuration file for building a Debug configuration of a project +// on Leopard or later. // // This is a _Configuration_ Xcode config file for use in the "Based on" popup // of the project configuration editor. Do _not_ use this as the config base diff --git a/XcodeConfig/Project/DebugTigerOrLater.xcconfig b/XcodeConfig/Project/DebugTigerOrLater.xcconfig index 725ea49..b8893ce 100644 --- a/XcodeConfig/Project/DebugTigerOrLater.xcconfig +++ b/XcodeConfig/Project/DebugTigerOrLater.xcconfig @@ -1,7 +1,8 @@ // // DebugTigerOrLater.xcconfig // -// Xcode configuration file for building a Debug target on Tiger or later. +// Xcode configuration file for building a Debug configuration of a project +// on Tiger or later. // // This is a _Configuration_ Xcode config file for use in the "Based on" popup // of the project configuration editor. Do _not_ use this as the config base diff --git a/XcodeConfig/Project/DebugiPhone.xcconfig b/XcodeConfig/Project/DebugiPhone.xcconfig index 1ba99b4..4af17bd 100644 --- a/XcodeConfig/Project/DebugiPhone.xcconfig +++ b/XcodeConfig/Project/DebugiPhone.xcconfig @@ -1,7 +1,8 @@ // // DebugiPhoneSimulator.xcconfig // -// Xcode configuration file for building a Debug target on iPhone +// Xcode configuration file for building a Debug configuration of a project +// for iPhone. // // Copyright 2006-2008 Google Inc. // diff --git a/XcodeConfig/Project/ReleaseLeopardOrLater.xcconfig b/XcodeConfig/Project/ReleaseLeopardOrLater.xcconfig index 0188d10..f62b287 100644 --- a/XcodeConfig/Project/ReleaseLeopardOrLater.xcconfig +++ b/XcodeConfig/Project/ReleaseLeopardOrLater.xcconfig @@ -1,7 +1,8 @@ // // ReleaseLeopardOrLater.xcconfig // -// Xcode configuration file for building a Release target on Leopard or later. +// Xcode configuration file for building a Release configuration of a project +// on Leopard or later. // // This is a _Configuration_ Xcode config file for use in the "Based on" popup // of the project configuration editor. Do _not_ use this as the config base diff --git a/XcodeConfig/Project/ReleaseTigerOrLater.xcconfig b/XcodeConfig/Project/ReleaseTigerOrLater.xcconfig index a024c4b..d604d88 100644 --- a/XcodeConfig/Project/ReleaseTigerOrLater.xcconfig +++ b/XcodeConfig/Project/ReleaseTigerOrLater.xcconfig @@ -1,7 +1,8 @@ // // ReleaseTigerOrLater.xcconfig // -// Xcode configuration file for building a Release target on Tiger or later. +// Xcode configuration file for building a Release configuration of a project +// on Tiger or later. // // This is a _Configuration_ Xcode config file for use in the "Based on" popup // of the project configuration editor. Do _not_ use this as the config base diff --git a/XcodeConfig/Project/ReleaseiPhone.xcconfig b/XcodeConfig/Project/ReleaseiPhone.xcconfig index d6c0127..1473a5a 100644 --- a/XcodeConfig/Project/ReleaseiPhone.xcconfig +++ b/XcodeConfig/Project/ReleaseiPhone.xcconfig @@ -1,7 +1,8 @@ // // ReleaseAspenSimulator.xcconfig // -// Xcode configuration file for building a Release target on iPhone +// Xcode configuration file for building a Release configuration of a project +// for iPhone. // // Copyright 2006-2008 Google Inc. // diff --git a/XcodeConfig/Target/LoadableBundle.xcconfig b/XcodeConfig/Target/LoadableBundle.xcconfig index 78ed39a..8b824d7 100644 --- a/XcodeConfig/Target/LoadableBundle.xcconfig +++ b/XcodeConfig/Target/LoadableBundle.xcconfig @@ -1,8 +1,8 @@ // // LoadableBundle.xcconfig // -// Xcode configuration file for a loadable bundle. Usually a Cocoa plugin or -// similar. +// Xcode configuration file for a loadable bundle target. 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 diff --git a/XcodeConfig/Target/LoadableBundleGCSupported.xcconfig b/XcodeConfig/Target/LoadableBundleGCSupported.xcconfig index 861845a..aa8d5a7 100644 --- a/XcodeConfig/Target/LoadableBundleGCSupported.xcconfig +++ b/XcodeConfig/Target/LoadableBundleGCSupported.xcconfig @@ -1,7 +1,7 @@ // // LoadableBundleGCSupported.xcconfig // -// Xcode configuration file for a loadable bundle that supports garbage +// Xcode configuration file for a loadable bundle target 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 diff --git a/XcodeConfig/Target/SharedLibrary.xcconfig b/XcodeConfig/Target/SharedLibrary.xcconfig index 7593392..7c37d50 100644 --- a/XcodeConfig/Target/SharedLibrary.xcconfig +++ b/XcodeConfig/Target/SharedLibrary.xcconfig @@ -1,7 +1,7 @@ // // SharedLibrary.xcconfig // -// Xcode configuration file for a shared library. +// Xcode configuration file for a shared library target. // // 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 diff --git a/XcodeConfig/Target/SharedLibraryGCSupported.xcconfig b/XcodeConfig/Target/SharedLibraryGCSupported.xcconfig index e6b56f7..74ab907 100644 --- a/XcodeConfig/Target/SharedLibraryGCSupported.xcconfig +++ b/XcodeConfig/Target/SharedLibraryGCSupported.xcconfig @@ -1,7 +1,7 @@ // // SharedLibraryGCSupported.xcconfig // -// Xcode configuration file for a shared library that support garbage +// Xcode configuration file for a shared library target that support garbage // collection. // // This is a _Target_ config file, for use in the "Based on" popup of the diff --git a/XcodeConfig/Target/StaticLibrary.xcconfig b/XcodeConfig/Target/StaticLibrary.xcconfig index f459e83..df1016b 100644 --- a/XcodeConfig/Target/StaticLibrary.xcconfig +++ b/XcodeConfig/Target/StaticLibrary.xcconfig @@ -1,7 +1,7 @@ // // StaticLibrary.xcconfig // -// Xcode configuration file for a static library. +// Xcode configuration file for a static library target. // // 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 diff --git a/XcodeConfig/Target/StaticLibraryGCSupported.xcconfig b/XcodeConfig/Target/StaticLibraryGCSupported.xcconfig index 6a57a0c..07ea1d5 100644 --- a/XcodeConfig/Target/StaticLibraryGCSupported.xcconfig +++ b/XcodeConfig/Target/StaticLibraryGCSupported.xcconfig @@ -1,7 +1,7 @@ // // StaticLibraryGCSupported.xcconfig // -// Xcode configuration file for a static library that supports garbage +// Xcode configuration file for a static library target that supports garbage // collection. // // This is a _Target_ config file, for use in the "Based on" popup of the diff --git a/XcodeConfig/subconfig/64bit.xcconfig b/XcodeConfig/subconfig/64bit.xcconfig deleted file mode 100644 index bb9cae6..0000000 --- a/XcodeConfig/subconfig/64bit.xcconfig +++ /dev/null @@ -1,26 +0,0 @@ -// -// 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/General.xcconfig b/XcodeConfig/subconfig/General.xcconfig index b6a9a02..6031f46 100644 --- a/XcodeConfig/subconfig/General.xcconfig +++ b/XcodeConfig/subconfig/General.xcconfig @@ -19,8 +19,22 @@ // the License. // -// Build for PPC and Intel -ARCHS = i386 ppc +// NOTE: as of Xcode 3.1, for iPhone development, the two SDKs you can match are: +// SDK_NAME iphoneos2.0 +// SDK_NAME iphonesimulator2.0 +// for Mac OS developement, the values are: +// SDK_NAME macosx10.4 +// SDK_NAME macosx10.5 + +// Build for PPC and Intel (Leopard gets 64bit also) +ARCHS[sdk=macosx10.4*] = i386 ppc +ARCHS[sdk=macosx10.5*] = i386 x86_64 ppc ppc64 +// Build for arm for iPhone or Intel for the iPhone Simulator +ARCHS[sdk=iphoneos*] = armv6 +ARCHS[sdk=iphonesimulator*] = i386 + +// Build only the active architecture on iphone device targets +ONLY_ACTIVE_ARCH[sdk=iphoneos*] = YES // Zerolink prevents link warnings so turn it off ZERO_LINK = NO @@ -46,3 +60,9 @@ ALWAYS_SEARCH_USER_PATHS = NO // Turn on position dependent code for most cases (overridden where appropriate) GCC_DYNAMIC_NO_PIC = YES + +// Warn on implicit data conversions in 64bit builds +GCC_WARN_64_TO_32_BIT_CONVERSION[arch=*64*] = YES + +// Use Obj-C fast dispatch (configs don't support 10.2 where you don't want it) +GCC_FAST_OBJC_DISPATCH = YES diff --git a/XcodeConfig/subconfig/LeopardOrLater.xcconfig b/XcodeConfig/subconfig/LeopardOrLater.xcconfig index 6cc1a08..fa4dfce 100644 --- a/XcodeConfig/subconfig/LeopardOrLater.xcconfig +++ b/XcodeConfig/subconfig/LeopardOrLater.xcconfig @@ -23,10 +23,3 @@ SDKROOT = ${DEVELOPER_SDK_DIR}/MacOSX10.5.sdk MACOSX_DEPLOYMENT_TARGET = 10.5 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 a693a58..bc0f8f4 100644 --- a/XcodeConfig/subconfig/TigerOrLater.xcconfig +++ b/XcodeConfig/subconfig/TigerOrLater.xcconfig @@ -23,6 +23,3 @@ SDKROOT = $(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk MACOSX_DEPLOYMENT_TARGET = 10.4 GCC_VERSION = 4.0 - -// On Tiger use Obj-C fast dispatch -GCC_FAST_OBJC_DISPATCH = YES diff --git a/XcodeConfig/subconfig/iPhone.xcconfig b/XcodeConfig/subconfig/iPhone.xcconfig index 42445f1..0948db8 100644 --- a/XcodeConfig/subconfig/iPhone.xcconfig +++ b/XcodeConfig/subconfig/iPhone.xcconfig @@ -17,21 +17,10 @@ // License for the specific language governing permissions and limitations under // the License. -// Build for ARM -ARCHS = armv6 - -// Build only the active architecture -ONLY_ACTIVE_ARCH = YES - // Code signing. Should be overridden if releasing CODE_SIGN_IDENTITY[sdk=iphoneos*] = iPhone Developer // Default SDK and minimum OS version is the iphone SDK. SDKROOT = iphoneos2.0 - MACOSX_DEPLOYMENT_TARGET = 10.5 GCC_VERSION = 4.0 - -// On iPhone use Obj-C fast dispatch -GCC_FAST_OBJC_DISPATCH = YES - diff --git a/iPhone/GTMABAddressBook.h b/iPhone/GTMABAddressBook.h new file mode 100644 index 0000000..48e7f88 --- /dev/null +++ b/iPhone/GTMABAddressBook.h @@ -0,0 +1,319 @@ +// +// GTMAddressBook.h +// +// 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. +// + +// These classes wrap up the iPhone AddressBook 'C' API in a manner very +// similar to that found on Mac OS X. They differ only in that none of these +// routines throws, and some of the types are different as necessitated by +// the APIs that they wrap. These wrappers also protect you from a number +// of issues in the AddressBook API (as of iPhone SDK 2.0/2.1) +// +// Note that there is a strings file that you may want to localize +// (GTMABAddressBook.strings). +// +// If things seem strange, it may be due to one of the following radars: +// 6208390 Integer and real values don't work in ABMultiValueRefs +// (and this isn't part of the title, but dictionaries don't work +// either) +// 6207605 RecordIDs for people and groups are not unique in AddressBook +// 6204021 kABGroupNameProperty and kABPersonFirstNameProperty have the same +// value +// 6203982 ABPersonCopyLocalizedPropertyName returns name for +// kABGroupNameProperty +// 6203961 ABPersonGetTypeOfProperty returns a type for kABGroupNameProperty +// 6203854 ABMultiValues hash to their address +// 6203836 ABRecords hash to their address +// 6203606 Need CFTypeIDs for AddressBook CFTypes +// 6202868 ABPersonSetImageData should validate image data +// 6202860 Passing nil person into ABGroupAddMember crashes +// 6202827 Passing nil info ABMultiValueAddValueAndLabel causes crash +// 6202807 ABMultiValueInsertValueAndLabelAtIndex allows you to insert values +// past end +// 6201276 Removing a NULL record using ABAddressBookRemoveRecord crashes +// 6201258 Adding a NULL record using ABAddressBookAddRecord crashes +// 6201046 ABRecordSetValue returns true even if you pass in a bad type for a +// value +// 6201032 ABRecordSetValue return "true" even if you pass nil as a value +// 6201005 ABRecordRemoveValue returns true for value that aren't in the record +// 6200703 ABAddressBookAddRecord doesn't add an item to the people array until +// it's saved +// 6200638 ABAddressBookHasUnsavedChanges doesn't work + +#import <UIKit/UIKit.h> +#import <AddressBook/AddressBook.h> +#import "GTMDefines.h" + +#if !GTM_IPHONE_SDK +#error This file is for iPhone use only use ABAddressBook on Mac OS X +#endif + +@class GTMABPerson; +@class GTMABGroup; +@class GTMABRecord; + +extern NSString *const kGTMABUnknownPropertyName; + +// Wrapper for an AddressBook on iPhone +@interface GTMABAddressBook : NSObject { + @private + ABAddressBookRef addressBook_; +} + +// Returns a new instance of an address book. ++ (GTMABAddressBook *)addressBook; + +// Return the address book reference +- (ABAddressBookRef)addressBookRef; + +// Saves changes made since the last save +// Return YES if successful (or there was no change) +- (BOOL)save; + +// Saves changes made since the last save +// Return YES if successful (or there was no change) +- (BOOL)saveAndReturnError:(NSError **)error; + +// Returns YES if there are unsaved changes +// The unsaved changes flag is automatically set when changes are made +// As of iPhone 2.1, this does not work, and will always return NO. +// Radar 6200638: ABAddressBookHasUnsavedChanges doesn't work +- (BOOL)hasUnsavedChanges; + +// Reverts any changes that have been made and resets the unsaved flag +// Be sure to read notes for -hasUnsavedChanges and -people and -groups. +- (void)revert; + +// Returns a GTMABPerson matching an ID +// Returns nil if the record could not be found +- (GTMABPerson *)personForId:(ABRecordID)uniqueId; + +// Returns a GTMABGroup matching an ID +// Returns nil if the record could not be found +- (GTMABGroup *)groupForId:(ABRecordID)uniqueId; + +// Adds a record (ABPerson or ABGroup) to the AddressBook database +// Be sure to read notes for -people and -group. +- (BOOL)addRecord:(GTMABRecord *)record; + +// Removes a record (ABPerson or ABGroup) from the AddressBook database +- (BOOL)removeRecord:(GTMABRecord *)record; + +// Returns an array (GTMABPerson) of all the people in the AddressBook database +// As of iPhone 2.1, this array will not contain new entries until you save +// the address book. +// Radar 6200703: ABAddressBookAddRecord doesn't add an item to the people array +// until it's saved +- (NSArray *)people; + +// Returns an array of all the groups (GTMABGroup) in the AddressBook database +// As of iPhone 2.1, this array will not contain new entries until you save +// the address book. +// Radar 6200703: ABAddressBookAddRecord doesn't add an item to the people array +// until it's saved +- (NSArray *)groups; + +// Returns a localized name for a given label ++ (NSString *)localizedLabel:(CFStringRef)label; + +@end + +// Wrapper for a ABRecord on iPhone. +// A abstract class. Instantiate one of the concrete subclasses, GTMABPerson or +// GTMABGroup. +@interface GTMABRecord : NSObject { + @private + ABRecordRef record_; +} + +// Create a record with a recordRef. +// Since GTMABRecord is an abstract base class, attempting to create one +// of these directly will throw an exception. Use with one of the concrete +// subclasses. ++ (id)recordWithRecord:(ABRecordRef)record; + +// Designated initializer +// Since GTMABRecord is an abstract base class, attempting to create one +// of these directly will throw an exception. Use with one of the concrete +// subclasses. +- (id)initWithRecord:(ABRecordRef)record; + +// Return our recordRef +- (ABRecordRef)recordRef; + +// Return the recordID for the record +- (ABRecordID)recordID; + +// Returns the value of a given property. +// The type of the value depends on the property type. +- (id)valueForProperty:(ABPropertyID)property; + +// Set the value of a given property. +// The type of the value must match the property type. +// Returns YES if value set properly +- (BOOL)setValue:(id)value forProperty:(ABPropertyID)property; + +// Removes the value for the property +// Returns yes if value removed +- (BOOL)removeValueForProperty:(ABPropertyID)property; + +// returns a human friendly name for the record +- (NSString *)compositeName; + +// returns the type of a property ++ (ABPropertyType)typeOfProperty:(ABPropertyID)property; + +// returns a human friendly localized name for a property ++ (NSString *)localizedPropertyName:(ABPropertyID)property; +@end + +// Wrapper for an ABPerson on iPhone +@interface GTMABPerson : GTMABRecord + +// Creates a person with a first name and a last name. ++ (GTMABPerson *)personWithFirstName:(NSString *)first + lastName:(NSString *)last; + +// Sets image data for a person. Data must be to a block of data that +// will create a valid UIImage. +- (BOOL)setImageData:(NSData *)data; + +// Returns the image data. +- (NSData *)imageData; + +// Returns the image for a person +- (UIImage *)image; + +// Sets a the image for a person +- (BOOL)setImage:(UIImage *)image; + +// Returns the format in with names are composited ++ (ABPersonCompositeNameFormat)compositeNameFormat; +@end + +// Wrapper for a ABGroup on iPhone +@interface GTMABGroup : GTMABRecord +// Create a new group named |name| ++ (GTMABGroup *)groupNamed:(NSString *)name; + +// Return an array of members (GTMABPerson) +- (NSArray *)members; + +// Add a member to a group +- (BOOL)addMember:(GTMABPerson *)person; + +// Remove a member from a group +- (BOOL)removeMember:(GTMABPerson *)person; +@end + +// GTMABMultiValue does not supprt NSFastEnumeration because in +// the Apple frameworks it returns identifiers which are already NSStrings. +// In our case identifiers aren't NS types, and it doesn't make sense +// to convert them to NSNumbers just to convert them back so you can +// actually get at the values and labels. +// Instead we supply valueEnumerator and labelEnumerator which you can +// fast enumerate on to get values and labels directly. +@interface GTMABMultiValue : NSObject <NSCopying, NSMutableCopying> { + @protected + ABMultiValueRef multiValue_; +} + +// Create a multi value +- (id)initWithMultiValue:(ABMultiValueRef)multiValue; + +// return it's ref +- (ABMultiValueRef)multiValueRef; + +// Returns the number of value/label pairs +- (NSUInteger)count; + +// Returns a value at a given index +// Returns nil if index is out of bounds +- (id)valueAtIndex:(NSUInteger)idx; + +// Returns a label at a given index +// Returns nil if index is out of bounds +- (NSString *)labelAtIndex:(NSUInteger)idx; + +// Returns an identifier at a given index +// Returns kABMultiValueInvalidIdentifier if index is out of bounds +- (ABMultiValueIdentifier)identifierAtIndex:(NSUInteger)idx; + +// Returns the index of a given identifier +// Returns NSNotFound if not found +- (NSUInteger)indexForIdentifier:(ABMultiValueIdentifier)identifier; + +// Type of the contents of this multivalue +- (ABPropertyType)propertyType; + +// Returns the value for a given identifier +// Returns nil if the identifier is not found +- (id)valueForIdentifier:(ABMultiValueIdentifier)identifier; + +// Returns the value for a given identifier +// Returns nil if the identifier is not found +- (NSString *)labelForIdentifier:(ABMultiValueIdentifier)identifier; + +// Returns an enumerator for enumerating through values +- (NSEnumerator *)valueEnumerator; + +// Returns an enumerator for enumerating through labels +- (NSEnumerator *)labelEnumerator; + +@end + +@interface GTMABMutableMultiValue : GTMABMultiValue { + @private + // Use unsigned long here instead of NSUInteger because that's what + // NSFastEnumeration Protocol wants currently (iPhone 2.1) + unsigned long mutations_; +} + +// Create a new mutable multivalue with a given type ++ (id)valueWithPropertyType:(ABPropertyType)type; + +// Create a new mutable multivalue with a given type +- (id)initWithPropertyType:(ABPropertyType)type; + +// Create a new mutable multivalue based on |multiValue| +- (id)initWithMutableMultiValue:(ABMutableMultiValueRef)multiValue; + +// Adds a value with its label +// Returns the identifier if successful, kABMultiValueInvalidIdentifier +// otherwise. +- (ABMultiValueIdentifier)addValue:(id)value withLabel:(CFStringRef)label; + +// Insert a value/label pair at a given index +// Returns the identifier if successful. kABMultiValueInvalidIdentifier +// otherwise +// If index is out of bounds, returns kABMultiValueInvalidIdentifier. +- (ABMultiValueIdentifier)insertValue:(id)value + withLabel:(CFStringRef)label + atIndex:(NSUInteger)index; + +// Removes a value/label pair at a given index +// Returns NO if index out of bounds +- (BOOL)removeValueAndLabelAtIndex:(NSUInteger)index; + +// Replaces a value at a given index +// Returns NO if index out of bounds +- (BOOL)replaceValueAtIndex:(NSUInteger)index withValue:(id)value; + +// Replaces a label at a given index +// Returns NO if index out of bounds +- (BOOL)replaceLabelAtIndex:(NSUInteger)index withLabel:(CFStringRef)label; + +@end diff --git a/iPhone/GTMABAddressBook.m b/iPhone/GTMABAddressBook.m new file mode 100644 index 0000000..0c54364 --- /dev/null +++ b/iPhone/GTMABAddressBook.m @@ -0,0 +1,902 @@ +// +// GTMAddressBook.m +// +// Copyright 2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMABAddressBook.h" +#import "GTMGarbageCollection.h" + +NSString *const kGTMABUnknownPropertyName = @"UNKNOWN_PROPERTY"; + +typedef struct { + ABPropertyType pType; + Class class; +} TypeClassNameMap; + +@interface GTMABMultiValue () +- (unsigned long*)mutations; +@end + +@interface GTMABMutableMultiValue () +// Checks to see if a value is a valid type to be stored in this multivalue +- (BOOL)checkValueType:(id)value; +@end + +@interface GTMABMultiValueEnumerator : NSEnumerator { + @private + __weak ABMultiValueRef ref_; // ref_ cached from enumeree_ + GTMABMultiValue *enumeree_; + unsigned long mutations_; + NSUInteger count_; + NSUInteger index_; + BOOL useLabels_; +} ++ (id)valueEnumeratorFor:(GTMABMultiValue*)enumeree; ++ (id)labelEnumeratorFor:(GTMABMultiValue*)enumeree; +- (id)initWithEnumeree:(GTMABMultiValue*)enumeree useLabels:(BOOL)useLabels; +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state + objects:(id *)stackbuf + count:(NSUInteger)len; +@end + +@implementation GTMABAddressBook ++ (GTMABAddressBook *)addressBook { + return [[[self alloc] init] autorelease]; +} + +- (id)init { + if ((self = [super init])) { + addressBook_ = ABAddressBookCreate(); + if (!addressBook_) { + // COV_NF_START + [self release]; + self = nil; + // COV_NF_END + } + } + return self; +} + +- (void)dealloc { + if (addressBook_) { + CFRelease(addressBook_); + } + [super dealloc]; +} + +- (BOOL)save { + return [self saveAndReturnError:NULL]; +} + +- (BOOL)saveAndReturnError:(NSError **)error { + CFErrorRef cfError = NULL; + bool wasGood = ABAddressBookSave(addressBook_, &cfError); + GTMCFAutorelease(cfError); + if (error) { + *error = (NSError *)cfError; // COV_NF_LINE + } + return wasGood ? YES : NO; +} + +- (BOOL)hasUnsavedChanges { + return ABAddressBookHasUnsavedChanges(addressBook_); +} + +- (void)revert { + ABAddressBookRevert(addressBook_); +} + +- (BOOL)addRecord:(GTMABRecord *)record { + // Note: we check for bad data here because of radar + // 6201258 Adding a NULL record using ABAddressBookAddRecord crashes + if (!record) return NO; + CFErrorRef cfError = NULL; + bool wasGood = ABAddressBookAddRecord(addressBook_, + [record recordRef], &cfError); + if (cfError) { + // COV_NF_START + _GTMDevLog(@"Error in [%@ %@]: %@", + [self class], NSStringFromSelector(_cmd), cfError); + CFRelease(cfError); + // COV_NF_END + } + return wasGood ? YES : NO; +} + +- (BOOL)removeRecord:(GTMABRecord *)record { + // Note: we check for bad data here because of radar + // 6201276 Removing a NULL record using ABAddressBookRemoveRecord crashes + if (!record) return NO; + CFErrorRef cfError = NULL; + bool wasGood = ABAddressBookRemoveRecord(addressBook_, + [record recordRef], &cfError); + if (cfError) { + // COV_NF_START + _GTMDevLog(@"Error in [%@ %@]: %@", + [self class], NSStringFromSelector(_cmd), cfError); + CFRelease(cfError); + // COV_NF_END + } + return wasGood ? YES : NO; +} + +- (NSArray *)people { + NSArray *people + = GTMCFAutorelease(ABAddressBookCopyArrayOfAllPeople(addressBook_)); + NSMutableArray *result = [NSMutableArray arrayWithCapacity:[people count]]; + for (id person in people) { + [result addObject:[GTMABPerson recordWithRecord:person]]; + } + return result; +} + +- (NSArray *)groups { + NSArray *groups + = GTMCFAutorelease(ABAddressBookCopyArrayOfAllGroups(addressBook_)); + NSMutableArray *result = [NSMutableArray arrayWithCapacity:[groups count]]; + for (id group in groups) { + [result addObject:[GTMABGroup recordWithRecord:group]]; + } + return result; +} + +- (ABAddressBookRef)addressBookRef { + return addressBook_; +} + +- (GTMABPerson *)personForId:(ABRecordID)uniqueId { + GTMABPerson *person = nil; + ABRecordRef ref = ABAddressBookGetPersonWithRecordID(addressBook_, uniqueId); + if (ref) { + person = [GTMABPerson recordWithRecord:ref]; + } + return person; +} + +- (GTMABGroup *)groupForId:(ABRecordID)uniqueId { + GTMABGroup *group = nil; + ABRecordRef ref = ABAddressBookGetGroupWithRecordID(addressBook_, uniqueId); + if (ref) { + group = [GTMABGroup recordWithRecord:ref]; + } + return group; +} + ++ (NSString *)localizedLabel:(CFStringRef)label { + return GTMCFAutorelease(ABAddressBookCopyLocalizedLabel(label)); +} + +@end + +@implementation GTMABRecord ++ (id)recordWithRecord:(ABRecordRef)record { + return [[[self alloc] initWithRecord:record] autorelease]; +} + +- (id)initWithRecord:(ABRecordRef)record { + if ((self = [super init])) { + if ([self class] == [GTMABRecord class]) { + [self doesNotRecognizeSelector:_cmd]; + } + if (!record) { + [self release]; + self = nil; + } else { + record_ = CFRetain(record); + } + } + return self; +} + +- (NSUInteger)hash { + // This really isn't completely valid due to + // 6203836 ABRecords hash to their address + // but it's the best we can do without knowing what properties + // are in a record, and we don't have an API for that. + return CFHash(record_); +} + +- (BOOL)isEqual:(id)object { + // This really isn't completely valid due to + // 6203836 ABRecords hash to their address + // but it's the best we can do without knowing what properties + // are in a record, and we don't have an API for that. + return [object respondsToSelector:@selector(recordRef)] + && CFEqual(record_, [object recordRef]); +} + +- (void)dealloc { + if (record_) { + CFRelease(record_); + } + [super dealloc]; +} + +- (ABRecordRef)recordRef { + return record_; +} + +- (ABRecordID)recordID { + return ABRecordGetRecordID(record_); +} + +- (id)valueForProperty:(ABPropertyID)property { + id value = GTMCFAutorelease(ABRecordCopyValue(record_, property)); + if (value) { + if ([[self class] typeOfProperty:property] & kABMultiValueMask) { + value = [[[GTMABMultiValue alloc] initWithMultiValue:value] autorelease]; + } + } + return value; +} + +- (BOOL)setValue:(id)value forProperty:(ABPropertyID)property { + if (!value) return NO; + // We check the type here because of + // Radar 6201046 ABRecordSetValue returns true even if you pass in a bad type + // for a value + TypeClassNameMap fullTypeMap[] = { + { kABStringPropertyType, [NSString class] }, + { kABIntegerPropertyType, [NSNumber class] }, + { kABRealPropertyType, [NSNumber class] }, + { kABDateTimePropertyType, [NSDate class] }, + { kABDictionaryPropertyType, [NSDictionary class] }, + { kABMultiStringPropertyType, [GTMABMultiValue class] }, + { kABMultiRealPropertyType, [GTMABMultiValue class] }, + { kABMultiDateTimePropertyType, [GTMABMultiValue class] }, + { kABMultiDictionaryPropertyType, [GTMABMultiValue class] } + }; + ABPropertyType type = [[self class] typeOfProperty:property]; + BOOL wasFound = NO; + for (size_t i = 0; i < sizeof(fullTypeMap) / sizeof(TypeClassNameMap); ++i) { + if (fullTypeMap[i].pType == type) { + wasFound = YES; + if (![[value class] isSubclassOfClass:fullTypeMap[i].class]) { + return NO; + } + } + } + if (!wasFound) { + return NO; + } + if (type & kABMultiValueMask) { + value = (id)[value multiValueRef]; + } + CFErrorRef cfError = nil; + bool wasGood = ABRecordSetValue(record_, property, (CFTypeRef)value, &cfError); + if (cfError) { + // COV_NF_START + _GTMDevLog(@"Error in [%@ %@]: %@", + [self class], NSStringFromSelector(_cmd), cfError); + CFRelease(cfError); + // COV_NF_END + } + return wasGood ? YES : NO; +} + +- (BOOL)removeValueForProperty:(ABPropertyID)property { + CFErrorRef cfError = nil; + // We check to see if the value is in the property because of: + // Radar 6201005 ABRecordRemoveValue returns true for value that aren't + // in the record + id value = [self valueForProperty:property]; + bool wasGood = value && ABRecordRemoveValue(record_, property, &cfError); + if (cfError) { + // COV_NF_START + _GTMDevLog(@"Error in [%@ %@]: %@", + [self class], NSStringFromSelector(_cmd), cfError); + CFRelease(cfError); + // COV_NF_END + } + return wasGood ? YES : NO; +} + +- (NSString *)compositeName { + return GTMCFAutorelease(ABRecordCopyCompositeName(record_)); +} + +// COV_NF_START +// Both of these methods are to be overridden by their subclasses ++ (ABPropertyType)typeOfProperty:(ABPropertyID)property { + [self doesNotRecognizeSelector:_cmd]; + return kABInvalidPropertyType; +} + ++ (NSString *)localizedPropertyName:(ABPropertyID)property { + [self doesNotRecognizeSelector:_cmd]; + return nil; +} +// COV_NF_END +@end + +@implementation GTMABPerson + ++ (GTMABPerson *)personWithFirstName:(NSString *)first + lastName:(NSString *)last { + GTMABPerson *person = [[[self alloc] init] autorelease]; + if (person) { + BOOL isGood = YES; + if (first) { + isGood = [person setValue:first forProperty:kABPersonFirstNameProperty]; + } + if (isGood && last) { + isGood = [person setValue:last forProperty:kABPersonLastNameProperty]; + } + if (!isGood) { + // COV_NF_START + // Marked as NF because I don't know how to force an error + [person release]; + person = nil; + // COV_NF_END + } + } + return person; +} + +- (id)init { + ABRecordRef person = ABPersonCreate(); + self = [super initWithRecord:person]; + if (person) { + CFRelease(person); + } + return self; +} + +- (BOOL)setImageData:(NSData *)data { + CFErrorRef cfError = NULL; + bool wasGood = NO; + if (!data) { + wasGood = ABPersonRemoveImageData([self recordRef], &cfError); + } else { + // We verify that the data is good because of: + // Radar 6202868 ABPersonSetImageData should validate image data + UIImage *image = [UIImage imageWithData:data]; + wasGood = image && ABPersonSetImageData([self recordRef], + (CFDataRef)data, &cfError); + } + if (cfError) { + // COV_NF_START + _GTMDevLog(@"Error in [%@ %@]: %@", + [self class], NSStringFromSelector(_cmd), cfError); + CFRelease(cfError); + // COV_NF_END + } + return wasGood ? YES : NO; +} + +- (UIImage *)image { + return [UIImage imageWithData:[self imageData]]; +} + +- (BOOL)setImage:(UIImage *)image { + NSData *data = UIImagePNGRepresentation(image); + return [self setImageData:data]; +} + +- (NSData *)imageData { + return GTMCFAutorelease(ABPersonCopyImageData([self recordRef])); +} + +- (NSString *)description { + return [NSString stringWithFormat:@"%@ %@ %@ %d", + [self class], + [self valueForProperty:kABPersonFirstNameProperty], + [self valueForProperty:kABPersonLastNameProperty], + [self recordID]]; +} + ++ (NSString *)localizedPropertyName:(ABPropertyID)property { + return GTMCFAutorelease(ABPersonCopyLocalizedPropertyName(property)); +} + ++ (ABPersonCompositeNameFormat)compositeNameFormat { + return ABPersonGetCompositeNameFormat(); +} + ++ (ABPropertyType)typeOfProperty:(ABPropertyID)property { + return ABPersonGetTypeOfProperty(property); +} +@end + +@implementation GTMABGroup + ++ (GTMABGroup *)groupNamed:(NSString *)name { + GTMABGroup *group = [[[self alloc] init] autorelease]; + if (group) { + if (![group setValue:name forProperty:kABGroupNameProperty]) { + // COV_NF_START + // Can't get setValue to fail for me + [group release]; + group = nil; + // COV_NF_END + } + } + return group; +} + +- (id)init { + ABRecordRef group = ABGroupCreate(); + self = [super initWithRecord:group]; + if (group) { + CFRelease(group); + } + return self; +} + +- (NSArray *)members { + NSArray *people + = GTMCFAutorelease(ABGroupCopyArrayOfAllMembers([self recordRef])); + NSMutableArray *gtmPeople = [NSMutableArray arrayWithCapacity:[people count]]; + NSEnumerator *enumerator = [people objectEnumerator]; + ABRecordRef person; + while ((person = [enumerator nextObject])) { + [gtmPeople addObject:[GTMABPerson recordWithRecord:person]]; + } + return gtmPeople; +} + +- (BOOL)addMember:(GTMABPerson *)person { + CFErrorRef cfError = nil; + // We check for person because of + // Radar 6202860 Passing nil person into ABGroupAddMember crashes + bool wasGood = person && ABGroupAddMember([self recordRef], + [person recordRef], &cfError); + if (cfError) { + // COV_NF_START + _GTMDevLog(@"Error in [%@ %@]: %@", + [self class], NSStringFromSelector(_cmd), cfError); + CFRelease(cfError); + // COV_NF_END + } + return wasGood ? YES : NO; +} + +- (BOOL)removeMember:(GTMABPerson *)person { + CFErrorRef cfError = nil; + // We check for person because of + // Radar 6202860 Passing nil person into ABGroupAddMember crashes + // (I know this is remove, but it crashes there too) + bool wasGood = person && ABGroupRemoveMember([self recordRef], + [person recordRef], &cfError); + if (cfError) { + // COV_NF_START + _GTMDevLog(@"Error in [%@ %@]: %@", + [self class], NSStringFromSelector(_cmd), cfError); + CFRelease(cfError); + // COV_NF_END + } + return wasGood ? YES : NO; +} + ++ (ABPropertyType)typeOfProperty:(ABPropertyID)property { + ABPropertyType type = kABInvalidPropertyType; + if (property == kABGroupNameProperty) { + type = kABStringPropertyType; + } + return type; +} + ++ (NSString *)localizedPropertyName:(ABPropertyID)property { + NSString *name = kGTMABUnknownPropertyName; + if (property == kABGroupNameProperty) { + name = NSLocalizedStringFromTable(@"Name", + @"GTMABAddressBook", + @"name property"); + } + return name; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"%@ %@ %d", + [self class], + [self valueForProperty:kABGroupNameProperty], + [self recordID]]; +} +@end + +@implementation GTMABMultiValue +- (id)init { + [self doesNotRecognizeSelector:_cmd]; + return nil; // COV_NF_LINE +} + +- (id)initWithMultiValue:(ABMultiValueRef)multiValue { + if ((self = [super init])) { + if (!multiValue) { + [self release]; + self = nil; + } else { + multiValue_ = CFRetain(multiValue); + } + } + return self; +} + +- (id)copyWithZone:(NSZone *)zone { + return [[GTMABMultiValue alloc] initWithMultiValue:multiValue_]; +} + +- (id)mutableCopyWithZone:(NSZone *)zone { + return [[GTMABMutableMultiValue alloc] initWithMultiValue:multiValue_]; +} + +- (NSUInteger)hash { + // I'm implementing hash instead of using CFHash(multiValue_) because + // 6203854 ABMultiValues hash to their address + NSUInteger count = [self count]; + NSUInteger hash = 0; + for (NSUInteger i = 0; i < count; ++i) { + NSString *label = [self labelAtIndex:i]; + id value = [self valueAtIndex:i]; + hash += [label hash]; + hash += [value hash]; + } + return hash; +} + +- (BOOL)isEqual:(id)object { + // I'm implementing isEqual instea of using CFEquals(multiValue,...) because + // 6203854 ABMultiValues hash to their address + // and it appears CFEquals just calls through to hash to compare them. + BOOL isEqual = NO; + if ([object respondsToSelector:@selector(multiValueRef)]) { + isEqual = multiValue_ == [object multiValueRef]; + if (!isEqual) { + NSUInteger count = [self count]; + NSUInteger objCount = [object count]; + isEqual = count == objCount; + for (NSUInteger i = 0; isEqual && i < count; ++i) { + NSString *label = [self labelAtIndex:i]; + NSString *objLabel = [object labelAtIndex:i]; + isEqual = [label isEqual:objLabel]; + if (isEqual) { + id value = [self valueAtIndex:i]; + id objValue = [object valueAtIndex:i]; + isEqual = [value isEqual:objValue]; + } + } + } + } + return isEqual; +} + +- (void)dealloc { + if (multiValue_) { + CFRelease(multiValue_); + } + [super dealloc]; +} + +- (ABMultiValueRef)multiValueRef { + return multiValue_; +} + +- (NSUInteger)count { + return ABMultiValueGetCount(multiValue_); +} + +- (id)valueAtIndex:(NSUInteger)idx { + id value = nil; + if (idx < [self count]) { + value = GTMCFAutorelease(ABMultiValueCopyValueAtIndex(multiValue_, idx)); + ABPropertyType type = [self propertyType]; + if (type == kABIntegerPropertyType + || type == kABRealPropertyType + || type == kABDictionaryPropertyType) { + // This is because of + // 6208390 Integer and real values don't work in ABMultiValueRefs + // Apparently they forget to add a ref count on int, real and + // dictionary values in ABMultiValueCopyValueAtIndex, although they do + // remember them for all other types. + // Once they fix this, this will lead to a leak, but I figure the leak + // is better than the crash. Our unittests will test to make sure that + // this is the case, and once we find a system that has this fixed, we + // can conditionalize this code. Look for testRadar6208390 in + // GTMABAddressBookTest.m + // Also, search for 6208390 below and fix the fast enumerator to actually + // be somewhat performant when this is fixed. + [value retain]; + } + } + return value; +} + +- (NSString *)labelAtIndex:(NSUInteger)idx { + NSString *label = nil; + if (idx < [self count]) { + label = GTMCFAutorelease(ABMultiValueCopyLabelAtIndex(multiValue_, idx)); + } + return label; +} + +- (ABMultiValueIdentifier)identifierAtIndex:(NSUInteger)idx { + ABMultiValueIdentifier identifier = kABMultiValueInvalidIdentifier; + if (idx < [self count]) { + identifier = ABMultiValueGetIdentifierAtIndex(multiValue_, idx); + } + return identifier; +} + +- (NSUInteger)indexForIdentifier:(ABMultiValueIdentifier)identifier { + NSUInteger idx = ABMultiValueGetIndexForIdentifier(multiValue_, identifier); + return idx == (NSUInteger)kCFNotFound ? (NSUInteger)NSNotFound : idx; +} + +- (ABPropertyType)propertyType { + return ABMultiValueGetPropertyType(multiValue_); +} + +- (id)valueForIdentifier:(ABMultiValueIdentifier)identifier { + return [self valueAtIndex:[self indexForIdentifier:identifier]]; +} + +- (NSString *)labelForIdentifier:(ABMultiValueIdentifier)identifier { + return [self labelAtIndex:[self indexForIdentifier:identifier]]; +} + +- (unsigned long*)mutations { + // We just need some constant non-zero value here so fast enumeration works. + // Dereferencing self should give us the isa which will stay constant + // over the enumeration. + return (unsigned long*)self; +} + +- (NSEnumerator *)valueEnumerator { + return [GTMABMultiValueEnumerator valueEnumeratorFor:self]; +} + +- (NSEnumerator *)labelEnumerator { + return [GTMABMultiValueEnumerator labelEnumeratorFor:self]; +} + +@end + +@implementation GTMABMutableMultiValue ++ (id)valueWithPropertyType:(ABPropertyType)type { + return [[[self alloc] initWithPropertyType:type] autorelease]; +} + +- (id)initWithPropertyType:(ABPropertyType)type { + ABMutableMultiValueRef ref = nil; + if (!(type & kABMultiValueMask) && (type != kABInvalidPropertyType)) { + ref = ABMultiValueCreateMutable(type); + } + self = [super initWithMultiValue:ref]; + if (ref) { + CFRelease(ref); + } + return self; +} + +- (id)initWithMultiValue:(ABMultiValueRef)multiValue { + ABMutableMultiValueRef ref = nil; + if (multiValue) { + ref = ABMultiValueCreateMutableCopy(multiValue); + } + self = [super initWithMultiValue:ref]; + if (ref) { + CFRelease(ref); + } + return self; +} + +- (id)initWithMutableMultiValue:(ABMutableMultiValueRef)multiValue { + return [super initWithMultiValue:multiValue]; +} + +- (BOOL)checkValueType:(id)value { + BOOL isGood = NO; + if (value) { + TypeClassNameMap singleValueTypeMap[] = { + { kABStringPropertyType, [NSString class] }, + { kABIntegerPropertyType, [NSNumber class] }, + { kABRealPropertyType, [NSNumber class] }, + { kABDateTimePropertyType, [NSDate class] }, + { kABDictionaryPropertyType, [NSDictionary class] }, + }; + ABPropertyType type = [self propertyType]; + for (size_t i = 0; + i < sizeof(singleValueTypeMap) / sizeof(TypeClassNameMap); ++i) { + if (singleValueTypeMap[i].pType == type) { + if ([[value class] isSubclassOfClass:singleValueTypeMap[i].class]) { + isGood = YES; + break; + } + } + } + } + return isGood; +} + +- (ABMultiValueIdentifier)addValue:(id)value withLabel:(CFStringRef)label { + ABMultiValueIdentifier identifier = kABMultiValueInvalidIdentifier; + // We check label and value here because of + // radar 6202827 Passing nil info ABMultiValueAddValueAndLabel causes crash + if (!label + || ![self checkValueType:value] + || !ABMultiValueAddValueAndLabel(multiValue_, + value, + label, + &identifier)) { + identifier = kABMultiValueInvalidIdentifier; + } else { + mutations_++; + } + return identifier; +} + +- (ABMultiValueIdentifier)insertValue:(id)value + withLabel:(CFStringRef)label + atIndex:(NSUInteger)idx { + ABMultiValueIdentifier identifier = kABMultiValueInvalidIdentifier; + // We perform a check here to ensure that we don't get bitten by + // Radar 6202807 ABMultiValueInsertValueAndLabelAtIndex allows you to insert + // values past end + NSUInteger count = [self count]; + // We check label and value here because of + // radar 6202827 Passing nil info ABMultiValueAddValueAndLabel causes crash + if (idx > count + || !label + || ![self checkValueType:value] + || !ABMultiValueInsertValueAndLabelAtIndex(multiValue_, + value, + label, + idx, + &identifier)) { + identifier = kABMultiValueInvalidIdentifier; + } else { + mutations_++; + } + return identifier; +} + +- (BOOL)removeValueAndLabelAtIndex:(NSUInteger)idx { + BOOL isGood = NO; + NSUInteger count = [self count]; + if (idx < count) { + if (ABMultiValueRemoveValueAndLabelAtIndex(multiValue_, + idx)) { + mutations_++; + isGood = YES; + } + } + return isGood; +} + +- (BOOL)replaceValueAtIndex:(NSUInteger)idx withValue:(id)value { + BOOL isGood = NO; + NSUInteger count = [self count]; + if (idx < count && [self checkValueType:value]) { + if (ABMultiValueReplaceValueAtIndex(multiValue_, + value, idx)) { + mutations_++; + isGood = YES; + } + } + return isGood; +} + +- (BOOL)replaceLabelAtIndex:(NSUInteger)idx withLabel:(CFStringRef)label{ + BOOL isGood = NO; + NSUInteger count = [self count]; + if (idx < count) { + if (ABMultiValueReplaceLabelAtIndex(multiValue_, + label, + idx)) { + mutations_++; + isGood = YES; + } + } + return isGood; +} + +- (unsigned long*)mutations { + return &mutations_; +} +@end + + +@implementation GTMABMultiValueEnumerator + ++ (id)valueEnumeratorFor:(GTMABMultiValue*)enumeree { + return [[[self alloc] initWithEnumeree:enumeree useLabels:NO] autorelease]; +} + ++ (id)labelEnumeratorFor:(GTMABMultiValue*)enumeree { + return [[[self alloc] initWithEnumeree:enumeree useLabels:YES] autorelease]; +} + +- (id)initWithEnumeree:(GTMABMultiValue*)enumeree useLabels:(BOOL)useLabels { + if ((self = [super init])) { + if (enumeree) { + enumeree_ = [enumeree retain]; + useLabels_ = useLabels; + } else { + // COV_NF_START + // Since this is a private class where the enumeree creates us + // there is no way we should ever get here. + [self release]; + self = nil; + // COV_NF_END + } + } + return self; +} + +- (void)dealloc { + [enumeree_ release]; + [super dealloc]; +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state + objects:(id *)stackbuf + count:(NSUInteger)len { + NSUInteger i; + if (!ref_) { + count_ = [enumeree_ count]; + ref_ = [enumeree_ multiValueRef]; + } + + for (i = 0; state->state < count_ && i < len; ++i, ++state->state) { + if (useLabels_) { + stackbuf[i] = GTMCFAutorelease(ABMultiValueCopyLabelAtIndex(ref_, + state->state)); + } else { + // Yes this is slow, but necessary in light of radar 6208390 + // Once this is fixed we can go to something similar to the label + // case which should speed stuff up again. Hopefully anybody who wants + // real performance is willing to move down to the C API anyways. + stackbuf[i] = [enumeree_ valueAtIndex:state->state]; + } + } + + state->itemsPtr = stackbuf; + state->mutationsPtr = [enumeree_ mutations]; + return i; +} + +- (id)nextObject { + id value = nil; + if (!ref_) { + count_ = [enumeree_ count]; + mutations_ = *[enumeree_ mutations]; + ref_ = [enumeree_ multiValueRef]; + + } + if (mutations_ != *[enumeree_ mutations]) { + NSString *reason = [NSString stringWithFormat:@"*** Collection <%@> was " + "mutated while being enumerated", enumeree_]; + [[NSException exceptionWithName:NSGenericException + reason:reason + userInfo:nil] raise]; + } + if (index_ < count_) { + if (useLabels_) { + value = GTMCFAutorelease(ABMultiValueCopyLabelAtIndex(ref_, + index_)); + } else { + // Yes this is slow, but necessary in light of radar 6208390 + // Once this is fixed we can go to something similar to the label + // case which should speed stuff up again. Hopefully anybody who wants + // real performance is willing to move down to the C API anyways. + value = [enumeree_ valueAtIndex:index_]; + } + index_ += 1; + } + return value; +} +@end + diff --git a/iPhone/GTMABAddressBook.strings b/iPhone/GTMABAddressBook.strings Binary files differnew file mode 100644 index 0000000..b5e010e --- /dev/null +++ b/iPhone/GTMABAddressBook.strings diff --git a/iPhone/GTMABAddressBookTest.m b/iPhone/GTMABAddressBookTest.m new file mode 100644 index 0000000..3ecb796 --- /dev/null +++ b/iPhone/GTMABAddressBookTest.m @@ -0,0 +1,558 @@ +// +// GTMAddressBookTest.m +// +// Copyright 2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMSenTestCase.h" +#import "GTMABAddressBook.h" + +@interface GTMABAddressBookTest : GTMTestCase { + @private + GTMABAddressBook *book_; +} +@end + + +@implementation GTMABAddressBookTest +- (void)setUp { + // Create a book forcing it out of it's autorelease pool. + // I force it out of the release pool, so that we will see any errors + // for it immediately at teardown, and it will be clear which release + // caused us problems. + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + book_ = [[GTMABAddressBook addressBook] retain]; + [pool release]; + STAssertNotNil(book_, nil); + STAssertFalse([book_ hasUnsavedChanges], nil); +} + +- (void)tearDown { + [book_ release]; +} + +- (void)testGenericAddressBook { + STAssertEqualObjects([GTMABAddressBook localizedLabel:kABHomeLabel], + @"home", + nil); + STAssertThrows([GTMABRecord recordWithRecord:nil], nil); +} + +- (void)testAddingAndRemovingPerson { + // Create a person + GTMABPerson *person = [GTMABPerson personWithFirstName:@"Bart" + lastName:@"Simpson"]; + STAssertNotNil(person, nil); + + // Add person + NSArray *people = [book_ people]; + STAssertFalse([people containsObject:person], nil); + STAssertTrue([book_ addRecord:person], nil); + + // Normally this next line would be STAssertTrue, however due to + // Radar 6200638: ABAddressBookHasUnsavedChanges doesn't work + // We will check to make sure it stays broken ;-) + STAssertFalse([book_ hasUnsavedChanges], nil); + + people = [book_ people]; + STAssertNotNil(people, nil); + // Normally this next line would be STAssertTrue, however due to + // Radar 6200703: ABAddressBookAddRecord doesn't add an item to the people + // array until it's saved + // We will check to make sure it stays broken ;-) + STAssertFalse([people containsObject:person], nil); + + // Save book_ + STAssertTrue([book_ save], nil); + people = [book_ people]; + STAssertNotNil(people, nil); + STAssertTrue([people containsObject:person], nil); + + ABRecordID recordID = [person recordID]; + STAssertNotEquals(recordID, kABRecordInvalidID, nil); + + GTMABRecord *record = [book_ personForId:recordID]; + STAssertEqualObjects(record, person, nil); + + // Remove person + STAssertTrue([book_ removeRecord:person], nil); + // Normally this next line would be STAssertTrue, however due to + // Radar 6200638: ABAddressBookHasUnsavedChanges doesn't work + // We will check to make sure it stays broken ;-) + STAssertFalse([book_ hasUnsavedChanges], nil); + + // Normally this next line would be STAssertFalse, however due to + // Radar 6200703: ABAddressBookAddRecord doesn't add an item to the people + // array until it's saved + // We will check to make sure it stays broken ;-) + STAssertTrue([people containsObject:person], nil); + + // Save Book + STAssertTrue([book_ save], nil); + people = [book_ people]; + STAssertFalse([book_ hasUnsavedChanges], nil); + STAssertFalse([people containsObject:person], nil); + record = [book_ personForId:recordID]; + STAssertNil(record, nil); + + // Revert book_ + STAssertTrue([book_ addRecord:person], nil); + // Normally this next line would be STAssertTrue, however due to + // Radar 6200638: ABAddressBookHasUnsavedChanges doesn't work + // We will check to make sure it stays broken ;-) + STAssertFalse([book_ hasUnsavedChanges], nil); + + [book_ revert]; + STAssertFalse([book_ hasUnsavedChanges], nil); + + // Bogus data + STAssertFalse([book_ addRecord:nil], nil); + STAssertFalse([book_ removeRecord:nil], nil); + + STAssertNotNULL([book_ addressBookRef], nil); + +} + +- (void)testAddingAndRemovingGroup { + // Create a group + GTMABGroup *group = [GTMABGroup groupNamed:@"Test"]; + STAssertNotNil(group, nil); + + // Add group + NSArray *groups = [book_ groups]; + STAssertFalse([groups containsObject:group], nil); + STAssertTrue([book_ addRecord:group], nil); + + // Normally this next line would be STAssertTrue, however due to + // Radar 6200638: ABAddressBookHasUnsavedChanges doesn't work + // We will check to make sure it stays broken ;-) + STAssertFalse([book_ hasUnsavedChanges], nil); + + groups = [book_ groups]; + STAssertNotNil(groups, nil); + // Normally this next line would be STAssertTrue, however due to + // Radar 6200703: ABAddressBookAddRecord doesn't add an item to the groups + // array until it's saved + // We will check to make sure it stays broken ;-) + STAssertFalse([groups containsObject:group], nil); + + // Save book_ + STAssertTrue([book_ save], nil); + groups = [book_ groups]; + STAssertNotNil(groups, nil); + STAssertTrue([groups containsObject:group], nil); + + ABRecordID recordID = [group recordID]; + STAssertNotEquals(recordID, kABRecordInvalidID, nil); + + GTMABRecord *record = [book_ groupForId:recordID]; + STAssertEqualObjects(record, group, nil); + + // Remove group + STAssertTrue([book_ removeRecord:group], nil); + // Normally this next line would be STAssertTrue, however due to + // Radar 6200638: ABAddressBookHasUnsavedChanges doesn't work + // We will check to make sure it stays broken ;-) + STAssertFalse([book_ hasUnsavedChanges], nil); + + // Normally this next line would be STAssertFalse, however due to + // Radar 6200703: ABAddressBookAddRecord doesn't add an item to the groups + // array until it's saved + // We will check to make sure it stays broken ;-) + STAssertTrue([groups containsObject:group], nil); + + // Save Book + STAssertTrue([book_ save], nil); + groups = [book_ groups]; + STAssertFalse([book_ hasUnsavedChanges], nil); + STAssertFalse([groups containsObject:group], nil); + record = [book_ groupForId:recordID]; + STAssertNil(record, nil); + + // Revert book_ + STAssertTrue([book_ addRecord:group], nil); + // Normally this next line would be STAssertTrue, however due to + // Radar 6200638: ABAddressBookHasUnsavedChanges doesn't work + // We will check to make sure it stays broken ;-) + STAssertFalse([book_ hasUnsavedChanges], nil); + + [book_ revert]; + STAssertFalse([book_ hasUnsavedChanges], nil); +} + +- (void)testPerson { + GTMABPerson *person = [[[GTMABPerson alloc] initWithRecord:nil] autorelease]; + STAssertNil(person, nil); + person = [GTMABPerson personWithFirstName:@"Bart" + lastName:nil]; + STAssertNotNil(person, nil); + STAssertEqualObjects([person compositeName], @"Bart", nil); + NSString *firstName = [person valueForProperty:kABPersonFirstNameProperty]; + STAssertEqualObjects(firstName, @"Bart", nil); + NSString *lastName = [person valueForProperty:kABPersonLastNameProperty]; + STAssertNil(lastName, nil); + STAssertTrue([person removeValueForProperty:kABPersonFirstNameProperty], nil); + STAssertFalse([person removeValueForProperty:kABPersonFirstNameProperty], nil); + STAssertFalse([person removeValueForProperty:kABPersonLastNameProperty], nil); + STAssertFalse([person setValue:nil forProperty:kABPersonFirstNameProperty], nil); + STAssertFalse([person setValue:[NSNumber numberWithInt:1] + forProperty:kABPersonFirstNameProperty], nil); + STAssertFalse([person setValue:@"Bart" + forProperty:kABPersonBirthdayProperty], nil); + + ABPropertyType property + = [GTMABPerson typeOfProperty:kABPersonLastNameProperty]; + STAssertEquals(property, (ABPropertyType)kABStringPropertyType, nil); + + NSString *string + = [GTMABPerson localizedPropertyName:kABPersonLastNameProperty]; + STAssertEqualObjects(string, @"Last", nil); + + string = [GTMABPerson localizedPropertyName:kABRecordInvalidID]; + STAssertEqualObjects(string, kGTMABUnknownPropertyName, nil); + + string = [person description]; + STAssertNotNil(string, nil); + + ABPersonCompositeNameFormat format = [GTMABPerson compositeNameFormat]; + STAssertTrue(format == kABPersonCompositeNameFormatFirstNameFirst || + format == kABPersonCompositeNameFormatLastNameFirst, nil); + + NSData *data = [person imageData]; + STAssertNil(data, nil); + STAssertTrue([person setImageData:nil], nil); + data = [person imageData]; + STAssertNil(data, nil); + UIImage *image = [UIImage imageNamed:@"phone.png"]; + STAssertNotNil(image, nil); + data = UIImagePNGRepresentation(image); + STAssertTrue([person setImageData:data], nil); + NSData *data2 = [person imageData]; + STAssertEqualObjects(data, data2, nil); + STAssertTrue([person setImageData:nil], nil); + data = [person imageData]; + STAssertNil(data, nil); + + STAssertTrue([person setImage:image], nil); + UIImage *image2 = [person image]; + STAssertNotNil(image2, nil); + STAssertEqualObjects(UIImagePNGRepresentation(image), + UIImagePNGRepresentation(image2), nil); + + person = [GTMABPerson personWithFirstName:@"Bart" + lastName:@"Simpson"]; + + data = [NSData dataWithBytes:"a" length:1]; + STAssertFalse([person setImageData:data], nil); + + GTMABMutableMultiValue *value + = [GTMABMutableMultiValue valueWithPropertyType:kABStringPropertyType]; + STAssertNotNil(value, nil); + STAssertNotEquals([value addValue:@"222-222-2222" + withLabel:kABHomeLabel], + kABMultiValueInvalidIdentifier, nil); + STAssertNotEquals([value addValue:@"333-333-3333" + withLabel:kABWorkLabel], + kABMultiValueInvalidIdentifier, nil); + STAssertTrue([person setValue:value forProperty:kABPersonPhoneProperty], nil); + id value2 = [person valueForProperty:kABPersonPhoneProperty]; + STAssertNotNil(value2, nil); + STAssertEqualObjects(value, value2, nil); + STAssertEquals([value hash], [value2 hash], nil); + STAssertNotEquals([person hash], (NSUInteger)0, nil); +} + +- (void)testGroup { + GTMABGroup *group = [[[GTMABGroup alloc] initWithRecord:nil] autorelease]; + STAssertNil(group, nil); + group = [GTMABGroup groupNamed:@"TestGroup"]; + STAssertNotNil(group, nil); + STAssertEqualObjects([group compositeName], @"TestGroup", nil); + NSString *name = [group valueForProperty:kABGroupNameProperty]; + STAssertEqualObjects(name, @"TestGroup", nil); + NSString *lastName = [group valueForProperty:kABPersonLastNameProperty]; + STAssertNil(lastName, nil); + STAssertTrue([group removeValueForProperty:kABGroupNameProperty], nil); + STAssertFalse([group removeValueForProperty:kABGroupNameProperty], nil); + STAssertFalse([group removeValueForProperty:kABPersonLastNameProperty], nil); + STAssertFalse([group setValue:nil forProperty:kABGroupNameProperty], nil); + STAssertFalse([group setValue:[NSNumber numberWithInt:1] + forProperty:kABGroupNameProperty], nil); + STAssertFalse([group setValue:@"Bart" + forProperty:kABPersonBirthdayProperty], nil); + + ABPropertyType property = [GTMABGroup typeOfProperty:kABGroupNameProperty]; + STAssertEquals(property, (ABPropertyType)kABStringPropertyType, nil); + + property = [GTMABGroup typeOfProperty:kABPersonLastNameProperty]; + STAssertEquals(property, (ABPropertyType)kABInvalidPropertyType, nil); + + NSString *string = [GTMABGroup localizedPropertyName:kABGroupNameProperty]; + STAssertEqualObjects(string, @"Name", nil); + + string = [GTMABGroup localizedPropertyName:kABPersonLastNameProperty]; + STAssertEqualObjects(string, kGTMABUnknownPropertyName, nil); + + string = [GTMABGroup localizedPropertyName:kABRecordInvalidID]; + STAssertEqualObjects(string, kGTMABUnknownPropertyName, nil); + + string = [group description]; + STAssertNotNil(string, nil); + + // Adding and removing members + group = [GTMABGroup groupNamed:@"TestGroup2"]; + NSArray *members = [group members]; + STAssertEquals([members count], (NSUInteger)0, @"Members: %@", members); + + STAssertFalse([group addMember:nil], nil); + + members = [group members]; + STAssertEquals([members count], (NSUInteger)0, @"Members: %@", members); + + GTMABPerson *person = [GTMABPerson personWithFirstName:@"Bart" + lastName:@"Simpson"]; + STAssertNotNil(person, nil); + STAssertTrue([book_ addRecord:person], nil); + STAssertTrue([book_ save], nil); + STAssertTrue([group addMember:person], nil); + STAssertTrue([book_ addRecord:group], nil); + STAssertTrue([book_ save], nil); + members = [group members]; + STAssertEquals([members count], (NSUInteger)1, @"Members: %@", members); + STAssertTrue([group removeMember:person], nil); + STAssertFalse([group removeMember:person], nil); + STAssertFalse([group removeMember:nil], nil); + STAssertTrue([book_ removeRecord:group], nil); + STAssertTrue([book_ removeRecord:person], nil); + STAssertTrue([book_ save], nil); +} + + +- (void)testMultiValues { + STAssertThrows([[GTMABMultiValue alloc] init], nil); + STAssertThrows([[GTMABMutableMultiValue alloc] init], nil); + GTMABMultiValue *value = [[GTMABMultiValue alloc] initWithMultiValue:nil]; + STAssertNil(value, nil); + GTMABMutableMultiValue *mutValue + = [GTMABMutableMultiValue valueWithPropertyType:kABInvalidPropertyType]; + STAssertNil(mutValue, nil); + mutValue + = [[[GTMABMutableMultiValue alloc] + initWithMutableMultiValue:nil] autorelease]; + STAssertNil(mutValue, nil); + mutValue + = [[[GTMABMutableMultiValue alloc] + initWithMultiValue:nil] autorelease]; + STAssertNil(mutValue, nil); + mutValue = [GTMABMutableMultiValue valueWithPropertyType:kABStringPropertyType]; + STAssertNotNil(mutValue, nil); + value = [[mutValue copy] autorelease]; + STAssertEqualObjects([value class], [GTMABMultiValue class], nil); + mutValue = [[value mutableCopy] autorelease]; + STAssertEqualObjects([mutValue class], [GTMABMutableMultiValue class], nil); + STAssertEquals([mutValue count], (NSUInteger)0, nil); + STAssertNil([mutValue valueAtIndex:0], nil); + STAssertNil([mutValue labelAtIndex:0], nil); + STAssertEquals([mutValue identifierAtIndex:0], + kABMultiValueInvalidIdentifier, nil); + STAssertEquals([mutValue propertyType], + (ABPropertyType)kABStringPropertyType, nil); + ABMultiValueIdentifier ident = [mutValue addValue:nil + withLabel:kABHomeLabel]; + STAssertEquals(ident, kABMultiValueInvalidIdentifier, nil); + ident = [mutValue addValue:@"val1" + withLabel:nil]; + STAssertEquals(ident, kABMultiValueInvalidIdentifier, nil); + ident = [mutValue insertValue:@"val1" + withLabel:nil + atIndex:0]; + STAssertEquals(ident, kABMultiValueInvalidIdentifier, nil); + ident = [mutValue insertValue:nil + withLabel:kABHomeLabel + atIndex:0]; + STAssertEquals(ident, kABMultiValueInvalidIdentifier, nil); + ident = [mutValue addValue:@"val1" + withLabel:kABHomeLabel]; + STAssertNotEquals(ident, kABMultiValueInvalidIdentifier, nil); + ABMultiValueIdentifier identCheck = [mutValue identifierAtIndex:0]; + STAssertEquals(ident, identCheck, nil); + NSUInteger idx = [mutValue indexForIdentifier:ident]; + STAssertEquals(idx, (NSUInteger)0, nil); + STAssertTrue([mutValue replaceLabelAtIndex:0 + withLabel:kABWorkLabel], nil); + STAssertFalse([mutValue replaceLabelAtIndex:10 + withLabel:kABWorkLabel], nil); + STAssertTrue([mutValue replaceValueAtIndex:0 + withValue:@"newVal1"], nil); + STAssertFalse([mutValue replaceValueAtIndex:10 + withValue:@"newVal1"], nil); + + STAssertEqualObjects([mutValue valueForIdentifier:ident], @"newVal1", nil); + STAssertEqualObjects([mutValue labelForIdentifier:ident], + (NSString *)kABWorkLabel, nil); + + ABMultiValueIdentifier ident2 = [mutValue insertValue:@"val2" + withLabel:kABOtherLabel + atIndex:0]; + STAssertNotEquals(ident2, kABMultiValueInvalidIdentifier, nil); + STAssertNotEquals(ident2, ident, nil); + ABMultiValueIdentifier ident3 = [mutValue insertValue:@"val3" + withLabel:kABPersonPhoneMainLabel + atIndex:10]; + STAssertEquals(ident3, kABMultiValueInvalidIdentifier, nil); + NSUInteger idx3 = [mutValue indexForIdentifier:ident3]; + STAssertEquals(idx3, (NSUInteger)NSNotFound, nil); + STAssertTrue([mutValue removeValueAndLabelAtIndex:1], nil); + STAssertFalse([mutValue removeValueAndLabelAtIndex:1], nil); + + NSUInteger idx4 + = [mutValue indexForIdentifier:kABMultiValueInvalidIdentifier]; + STAssertEquals(idx4, (NSUInteger)NSNotFound, nil); + + STAssertNotNULL([mutValue multiValueRef], nil); + + // Enumerator test + mutValue = [GTMABMutableMultiValue valueWithPropertyType:kABIntegerPropertyType]; + STAssertNotNil(mutValue, nil); + for (int i = 0; i < 100; i++) { + NSString *label = [NSString stringWithFormat:@"label %d", i]; + NSNumber *val = [NSNumber numberWithInt:i]; + STAssertNotEquals([mutValue addValue:val + withLabel:(CFStringRef)label], + kABMultiValueInvalidIdentifier, nil); + } + int count = 0; + for (NSString *label in [mutValue labelEnumerator]) { + NSString *testLabel = [NSString stringWithFormat:@"label %d", count++]; + STAssertEqualObjects(label, testLabel, nil); + } + count = 0; + value = [[mutValue copy] autorelease]; + for (NSNumber *val in [value valueEnumerator]) { + STAssertEqualObjects(val, [NSNumber numberWithInt:count++], nil); + } + + // Test messing with the values while we're enumerating them + NSEnumerator *labelEnum = [mutValue labelEnumerator]; + NSEnumerator *valueEnum = [mutValue valueEnumerator]; + STAssertNotNil(labelEnum, nil); + STAssertNotNil(valueEnum, nil); + STAssertNotNil([labelEnum nextObject], nil); + STAssertNotNil([valueEnum nextObject], nil); + STAssertTrue([mutValue removeValueAndLabelAtIndex:0], nil); + STAssertThrows([labelEnum nextObject], nil); + STAssertThrows([valueEnum nextObject], nil); + + // Test messing with the values while we're fast enumerating them + // Should throw an exception on the second access. + BOOL exceptionThrown = NO; + // Start at one because we removed index 0 above. + count = 1; + @try { + for (NSString *label in [mutValue labelEnumerator]) { + NSString *testLabel = [NSString stringWithFormat:@"label %d", count++]; + STAssertEqualObjects(label, testLabel, nil); + STAssertTrue([mutValue removeValueAndLabelAtIndex:50], nil); + } + } @catch(NSException *e) { + STAssertEqualObjects([e name], NSGenericException, @"Got %@ instead", e); + STAssertEquals(count, 2, + @"Should have caught it on the second access"); + exceptionThrown = YES; + } // COV_NF_LINE - because we always catch, this brace doesn't get exec'd + STAssertTrue(exceptionThrown, @"We should have thrown an exception" + @" because the values under the enumerator were modified"); + +} + +- (void)testRadar6208390 { + ABPropertyType types[] = { + kABStringPropertyType, + kABIntegerPropertyType, + kABRealPropertyType, + kABDateTimePropertyType, + kABDictionaryPropertyType + }; + for (size_t j = 0; j < sizeof(types) / sizeof(ABPropertyType); ++j) { + ABPropertyType type = types[j]; + ABMultiValueRef ref = ABMultiValueCreateMutable(type); + STAssertNotNULL(ref, nil); + NSString *label = [[NSString alloc] initWithString:@"label"]; + STAssertNotNil(label, nil); + id val; + switch (type) { + case kABDictionaryPropertyType: + val = [[NSDictionary alloc] initWithObjectsAndKeys:@"1", @"1", nil]; + break; + + case kABStringPropertyType: + val = [[NSString alloc] initWithFormat:@"value %d"]; + break; + + case kABIntegerPropertyType: + case kABRealPropertyType: + val = [[NSNumber alloc] initWithInt:143]; + break; + + case kABDateTimePropertyType: + val = [[NSDate alloc] init]; + break; + } + STAssertNotNil(val, + @"Testing type %d, %@", type, val); + NSUInteger firstRetainCount = [val retainCount]; + STAssertNotEquals(firstRetainCount, + (NSUInteger)0, + @"Testing type %d, %@", type, val); + + ABMultiValueIdentifier identifier; + STAssertTrue(ABMultiValueAddValueAndLabel(ref, + val, + (CFStringRef)label, + &identifier), + @"Testing type %d, %@", type, val); + NSUInteger secondRetainCount = [val retainCount]; + STAssertEquals(firstRetainCount + 1, + secondRetainCount, + @"Testing type %d, %@", type, val); + [label release]; + [val release]; + NSUInteger thirdRetainCount = [val retainCount]; + STAssertEquals(firstRetainCount, + thirdRetainCount, + @"Testing type %d, %@", type, val); + + val = (id)ABMultiValueCopyValueAtIndex(ref, 0); + NSUInteger fourthRetainCount = [val retainCount]; + if (type == kABIntegerPropertyType + || type == kABRealPropertyType + || type == kABDictionaryPropertyType) { + // We are verifying that yes indeed 6208390 is still broken + STAssertEquals(fourthRetainCount, + thirdRetainCount, + @"Testing type %d, %@. If you see this error it may " + @"be time to update the code to change retain behaviors" + @"with this os version", type, val); + } else { + STAssertEquals(fourthRetainCount, + thirdRetainCount + 1, + @"Testing type %d, %@", type, val); + [val release]; + } + + CFRelease(ref); + } +} +@end diff --git a/iPhone/TestData/phone.png b/iPhone/TestData/phone.png Binary files differnew file mode 100644 index 0000000..7e4051f --- /dev/null +++ b/iPhone/TestData/phone.png |