aboutsummaryrefslogtreecommitdiff
path: root/Foundation
diff options
context:
space:
mode:
Diffstat (limited to 'Foundation')
-rw-r--r--Foundation/GTMCalculatedRange.h2
-rw-r--r--Foundation/GTMCalculatedRangeTest.m1
-rw-r--r--Foundation/GTMGeometryUtils.h377
-rw-r--r--Foundation/GTMGeometryUtils.m104
-rw-r--r--Foundation/GTMGeometryUtilsTest.m181
-rw-r--r--Foundation/GTMNSData+zlib.m82
-rw-r--r--Foundation/GTMNSData+zlibTest.m118
-rw-r--r--Foundation/GTMNSEnumerator+Filter.m54
-rw-r--r--Foundation/GTMNSEnumerator+FilterTest.m101
-rw-r--r--Foundation/GTMNSFileManager+Path.h3
-rw-r--r--Foundation/GTMNSFileManager+PathTest.m211
-rw-r--r--Foundation/GTMNSString+HTML.m65
-rw-r--r--Foundation/GTMNSString+HTMLTest.m1
-rw-r--r--Foundation/GTMNSString+Utilities.h41
-rw-r--r--Foundation/GTMNSString+Utilities.m48
-rw-r--r--Foundation/GTMNSString+UtilitiesTest.m62
-rw-r--r--Foundation/GTMNSString+XML.m51
-rw-r--r--Foundation/GTMNSString+XMLTest.m42
-rw-r--r--Foundation/GTMObjC2Runtime.h54
-rw-r--r--Foundation/GTMObjC2Runtime.m148
-rw-r--r--Foundation/GTMObjC2RuntimeTest.m385
-rw-r--r--Foundation/GTMObjectSingleton.h6
-rw-r--r--Foundation/GTMRegex.h41
-rw-r--r--Foundation/GTMRegex.m132
-rw-r--r--Foundation/GTMRegexTest.m185
-rw-r--r--Foundation/GTMScriptRunner.m20
-rw-r--r--Foundation/GTMScriptRunnerTest.m163
-rw-r--r--Foundation/GTMSystemVersion.h23
-rw-r--r--Foundation/GTMSystemVersion.m107
-rw-r--r--Foundation/GTMSystemVersionTest.m32
30 files changed, 2537 insertions, 303 deletions
diff --git a/Foundation/GTMCalculatedRange.h b/Foundation/GTMCalculatedRange.h
index 5823ba6..5f51b3e 100644
--- a/Foundation/GTMCalculatedRange.h
+++ b/Foundation/GTMCalculatedRange.h
@@ -19,7 +19,7 @@
// the License.
//
-#import <Cocoa/Cocoa.h>
+#import <Foundation/Foundation.h>
/// Allows you to calculate a value based on defined stops in a range.
//
diff --git a/Foundation/GTMCalculatedRangeTest.m b/Foundation/GTMCalculatedRangeTest.m
index cd336f0..0c374c1 100644
--- a/Foundation/GTMCalculatedRangeTest.m
+++ b/Foundation/GTMCalculatedRangeTest.m
@@ -16,7 +16,6 @@
// the License.
//
-#import <SenTestingKit/SenTestingKit.h>
#import "GTMCalculatedRange.h"
#import "GTMSenTestCase.h"
diff --git a/Foundation/GTMGeometryUtils.h b/Foundation/GTMGeometryUtils.h
new file mode 100644
index 0000000..32c8745
--- /dev/null
+++ b/Foundation/GTMGeometryUtils.h
@@ -0,0 +1,377 @@
+//
+// GTMGeometryUtils.h
+//
+// Utilities for geometrical utilities such as conversions
+// between different types.
+//
+// Copyright 2006-2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#include <Foundation/Foundation.h>
+
+typedef enum {
+ GTMScaleProportionally = 0, // Fit proportionally
+ GTMScaleToFit, // Forced fit (distort if necessary)
+ GTMScaleNone // Don't scale (clip)
+} GTMScaling;
+
+typedef enum {
+ GTMRectAlignCenter = 0,
+ GTMRectAlignTop,
+ GTMRectAlignTopLeft,
+ GTMRectAlignTopRight,
+ GTMRectAlignLeft,
+ GTMRectAlignBottom,
+ GTMRectAlignBottomLeft,
+ GTMRectAlignBottomRight,
+ GTMRectAlignRight
+} GTMRectAlignment;
+
+#pragma mark Miscellaneous
+
+/// Calculate the distance between two points.
+//
+// Args:
+// pt1 first point
+// pt2 second point
+//
+// Returns:
+// Distance
+CG_INLINE float GTMDistanceBetweenPoints(NSPoint pt1, NSPoint pt2) {
+ float dX = pt1.x - pt2.x;
+ float dY = pt1.y - pt2.y;
+ return sqrtf(dX * dX + dY * dY);
+}
+
+#pragma mark -
+#pragma mark Point Conversion
+
+/// Quickly convert from a CGPoint to a NSPoint.
+//
+/// CGPoints are relative to 0,0 in lower left;
+/// NSPoints are relative to 0,0 in lower left
+//
+// Args:
+// inPoint: CGPoint to convert
+//
+// Returns:
+// Converted NSPoint
+CG_INLINE NSPoint GTMCGPointToNSPoint(CGPoint inPoint) {
+ return NSMakePoint(inPoint.x, inPoint.y);
+}
+
+/// Quickly convert from a NSPoint to a CGPoint.
+//
+/// CGPoints are relative to 0,0 in lower left;
+/// NSPoints are relative to 0,0 in lower left
+//
+// Args:
+// inPoint: NSPoint to convert
+//
+// Returns:
+// Converted CGPoint
+CG_INLINE CGPoint GTMNSPointToCGPoint(NSPoint inPoint) {
+ return CGPointMake(inPoint.x, inPoint.y);
+}
+
+#pragma mark -
+#pragma mark Rect Conversion
+
+/// Convert from a CGRect to a NSRect.
+//
+/// NSRect are relative to 0,0 in lower left;
+/// CGRect are relative to 0,0 in lower left
+//
+// Args:
+// inRect: CGRect to convert
+//
+// Returns:
+// Converted NSRect
+CG_INLINE NSRect GTMCGRectToNSRect(CGRect inRect) {
+ return NSMakeRect(inRect.origin.x,inRect.origin.y,inRect.size.width,inRect.size.height);
+}
+
+/// Convert from a NSRect to a CGRect.
+//
+/// NSRect are relative to 0,0 in lower left;
+/// CGRect are relative to 0,0 in lower left
+//
+// Args:
+// inRect: NSRect to convert
+//
+// Returns:
+// Converted CGRect
+CG_INLINE CGRect GTMNSRectToCGRect(NSRect inRect) {
+ return CGRectMake(inRect.origin.x,inRect.origin.y,inRect.size.width,inRect.size.height);
+}
+
+#pragma mark -
+#pragma mark Size Conversion
+
+/// Convert from a CGSize to an NSSize.
+//
+// Args:
+// inSize: CGSize to convert
+//
+// Returns:
+// Converted NSSize
+CG_INLINE NSSize GTMCGSizeToNSSize(CGSize inSize) {
+ return NSMakeSize(inSize.width, inSize.height);
+}
+
+/// Convert from a NSSize to a CGSize.
+//
+// Args:
+// inSize: NSSize to convert
+//
+// Returns:
+// Converted CGSize
+CG_INLINE CGSize GTMNSSizeToCGSize(NSSize inSize) {
+ return CGSizeMake(inSize.width, inSize.height);
+}
+
+#pragma mark -
+#pragma mark Point On Rect
+
+/// Return middle of left side of rectangle
+//
+// Args:
+// rect - rectangle
+//
+// Returns:
+// point located in the middle of left side of rect
+CG_INLINE NSPoint GTMNSMidLeft(NSRect rect) {
+ return NSMakePoint(NSMinX(rect), NSMidY(rect));
+}
+
+/// Return middle of right side of rectangle
+//
+// Args:
+// rect - rectangle
+//
+// Returns:
+// point located in the middle of right side of rect
+CG_INLINE NSPoint GTMNSMidRight(NSRect rect) {
+ return NSMakePoint(NSMaxX(rect), NSMidY(rect));
+}
+
+/// Return middle of top side of rectangle
+//
+// Args:
+// rect - rectangle
+//
+// Returns:
+// point located in the middle of top side of rect
+CG_INLINE NSPoint GTMNSMidTop(NSRect rect) {
+ return NSMakePoint(NSMidX(rect), NSMaxY(rect));
+}
+
+/// Return middle of bottom side of rectangle
+//
+// Args:
+// rect - rectangle
+//
+// Returns:
+// point located in the middle of bottom side of rect
+CG_INLINE NSPoint GTMNSMidBottom(NSRect rect) {
+ return NSMakePoint(NSMidX(rect), NSMinY(rect));
+}
+
+/// Return center of rectangle
+//
+// Args:
+// rect - rectangle
+//
+// Returns:
+// point located in the center of rect
+CG_INLINE NSPoint GTMNSCenter(NSRect rect) {
+ return NSMakePoint(NSMidX(rect), NSMidY(rect));
+}
+
+/// Return middle of left side of rectangle
+//
+// Args:
+// rect - rectangle
+//
+// Returns:
+// point located in the middle of left side of rect
+CG_INLINE CGPoint GTMCGMidLeft(CGRect rect) {
+ return CGPointMake(CGRectGetMinX(rect), CGRectGetMidY(rect));
+}
+
+/// Return middle of right side of rectangle
+//
+// Args:
+// rect - rectangle
+//
+// Returns:
+// point located in the middle of right side of rect
+CG_INLINE CGPoint GTMCGMidRight(CGRect rect) {
+ return CGPointMake(CGRectGetMaxX(rect), CGRectGetMidY(rect));
+}
+
+/// Return middle of top side of rectangle
+//
+// Args:
+// rect - rectangle
+//
+// Returns:
+// point located in the middle of top side of rect
+CG_INLINE CGPoint GTMCGMidTop(CGRect rect) {
+ return CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
+}
+
+/// Return middle of bottom side of rectangle
+//
+// Args:
+// rect - rectangle
+//
+// Returns:
+// point located in the middle of bottom side of rect
+CG_INLINE CGPoint GTMCGMidBottom(CGRect rect) {
+ return CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
+}
+
+/// Return center of rectangle
+//
+// Args:
+// rect - rectangle
+//
+// Returns:
+// point located in the center of rect
+CG_INLINE CGPoint GTMCGCenter(CGRect rect) {
+ return CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
+}
+
+#pragma mark -
+#pragma mark Rect-Size Conversion
+
+/// Return size of rectangle
+//
+// Args:
+// rect - rectangle
+//
+// Returns:
+// size of rectangle
+CG_INLINE NSSize GTMNSRectSize(NSRect rect) {
+ return NSMakeSize(NSWidth(rect), NSHeight(rect));
+}
+
+/// Return size of rectangle
+//
+// Args:
+// rect - rectangle
+//
+// Returns:
+// size of rectangle
+CG_INLINE CGSize GTMCGRectSize(CGRect rect) {
+ return CGSizeMake(CGRectGetWidth(rect), CGRectGetHeight(rect));
+}
+
+/// Return rectangle of size
+//
+// Args:
+// size - size
+//
+// Returns:
+// rectangle of size (origin 0,0)
+CG_INLINE NSRect GTMNSRectOfSize(NSSize size) {
+ return NSMakeRect(0.0f, 0.0f, size.width, size.height);
+}
+
+/// Return rectangle of size
+//
+// Args:
+// size - size
+//
+// Returns:
+// rectangle of size (origin 0,0)
+CG_INLINE CGRect GTMCGRectOfSize(CGSize size) {
+ return CGRectMake(0.0f, 0.0f, size.width, size.height);
+}
+
+#pragma mark -
+#pragma mark Rect Scaling and Alignment
+
+/// Scales an NSRect
+//
+// Args:
+// inRect: Rect to scale
+// xScale: fraction to scale (1.0 is 100%)
+// yScale: fraction to scale (1.0 is 100%)
+//
+// Returns:
+// Converted Rect
+CG_INLINE NSRect GTMNSRectScale(NSRect inRect, float xScale, float yScale) {
+ return NSMakeRect(inRect.origin.x, inRect.origin.y,
+ inRect.size.width * xScale, inRect.size.height * yScale);
+}
+
+/// Scales an CGRect
+//
+// Args:
+// inRect: Rect to scale
+// xScale: fraction to scale (1.0 is 100%)
+// yScale: fraction to scale (1.0 is 100%)
+//
+// Returns:
+// Converted Rect
+CG_INLINE CGRect GTMCGRectScale(CGRect inRect, float xScale, float yScale) {
+ return CGRectMake(inRect.origin.x, inRect.origin.y,
+ inRect.size.width * xScale, inRect.size.height * yScale);
+}
+
+/// Align rectangles
+//
+// Args:
+// alignee - rect to be aligned
+// aligner - rect to be aligned from
+NSRect GTMAlignRectangles(NSRect alignee, NSRect aligner,
+ GTMRectAlignment alignment);
+
+/// Align rectangles
+//
+// Args:
+// alignee - rect to be aligned
+// aligner - rect to be aligned from
+// alignment - way to align the rectangles
+CG_INLINE CGRect GTMCGAlignRectangles(CGRect alignee, CGRect aligner,
+ GTMRectAlignment alignment) {
+ return GTMNSRectToCGRect(GTMAlignRectangles(GTMCGRectToNSRect(alignee),
+ GTMCGRectToNSRect(aligner),
+ alignment));
+}
+
+/// Scale rectangle
+//
+// Args:
+// scalee - rect to be scaled
+// size - size to scale to
+// scaling - way to scale the rectangle
+NSRect GTMScaleRectangleToSize(NSRect scalee, NSSize size,
+ GTMScaling scaling);
+
+/// Scale rectangle
+//
+// Args:
+// scalee - rect to be scaled
+// size - size to scale to
+// scaling - way to scale the rectangle
+CG_INLINE CGRect GTMCGScaleRectangleToSize(CGRect scalee, CGSize size,
+ GTMScaling scaling) {
+ return GTMNSRectToCGRect(GTMScaleRectangleToSize(GTMCGRectToNSRect(scalee),
+ GTMCGSizeToNSSize(size),
+ scaling));
+}
diff --git a/Foundation/GTMGeometryUtils.m b/Foundation/GTMGeometryUtils.m
new file mode 100644
index 0000000..f5b38dc
--- /dev/null
+++ b/Foundation/GTMGeometryUtils.m
@@ -0,0 +1,104 @@
+//
+// GTMGeometryUtils.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 "GTMGeometryUtils.h"
+
+/// Align rectangles
+//
+// Args:
+// alignee - rect to be aligned
+// aligner - rect to be aligned to
+// alignment - alignment to be applied to alignee based on aligner
+
+NSRect GTMAlignRectangles(NSRect alignee, NSRect aligner, GTMRectAlignment alignment) {
+ switch (alignment) {
+ case GTMRectAlignTop:
+ alignee.origin.x = aligner.origin.x + (NSWidth(aligner) * .5f - NSWidth(alignee) * .5f);
+ alignee.origin.y = aligner.origin.y + NSHeight(aligner) - NSHeight(alignee);
+ break;
+
+ case GTMRectAlignTopLeft:
+ alignee.origin.x = aligner.origin.x;
+ alignee.origin.y = aligner.origin.y + NSHeight(aligner) - NSHeight(alignee);
+ break;
+
+ case GTMRectAlignTopRight:
+ alignee.origin.x = aligner.origin.x + NSWidth(aligner) - NSWidth(alignee);
+ alignee.origin.y = aligner.origin.y + NSHeight(aligner) - NSHeight(alignee);
+ break;
+
+ case GTMRectAlignLeft:
+ alignee.origin.x = aligner.origin.x;
+ alignee.origin.y = aligner.origin.y + (NSHeight(aligner) * .5f - NSHeight(alignee) * .5f);
+ break;
+
+ case GTMRectAlignBottomLeft:
+ alignee.origin.x = aligner.origin.x;
+ alignee.origin.y = aligner.origin.y;
+ break;
+
+ case GTMRectAlignBottom:
+ alignee.origin.x = aligner.origin.x + (NSWidth(aligner) * .5f - NSWidth(alignee) * .5f);
+ alignee.origin.y = aligner.origin.y;
+ break;
+
+ case GTMRectAlignBottomRight:
+ alignee.origin.x = aligner.origin.x + NSWidth(aligner) - NSWidth(alignee);
+ alignee.origin.y = aligner.origin.y;
+ break;
+
+ case GTMRectAlignRight:
+ alignee.origin.x = aligner.origin.x + NSWidth(aligner) - NSWidth(alignee);
+ alignee.origin.y = aligner.origin.y + (NSHeight(aligner) * .5f - NSHeight(alignee) * .5f);
+ break;
+
+ default:
+ case GTMRectAlignCenter:
+ alignee.origin.x = aligner.origin.x + (NSWidth(aligner) * .5f - NSWidth(alignee) * .5f);
+ alignee.origin.y = aligner.origin.y + (NSHeight(aligner) * .5f - NSHeight(alignee) * .5f);
+ break;
+ }
+ return alignee;
+}
+
+NSRect GTMScaleRectangleToSize(NSRect scalee, NSSize size, GTMScaling scaling) {
+ switch (scaling) {
+ case GTMScaleProportionally: {
+ float height = NSHeight(scalee);
+ float width = NSWidth(scalee);
+ if (isnormal(height) && isnormal(width) &&
+ (height > size.height || width > size.width)) {
+ float horiz = size.width / width;
+ float vert = size.height / height;
+ float newScale = horiz < vert ? horiz : vert;
+ scalee = GTMNSRectScale(scalee, newScale, newScale);
+ }
+ break;
+ }
+
+ case GTMScaleToFit:
+ scalee.size = size;
+ break;
+
+ case GTMScaleNone:
+ default:
+ // Do nothing
+ break;
+ }
+ return scalee;
+}
diff --git a/Foundation/GTMGeometryUtilsTest.m b/Foundation/GTMGeometryUtilsTest.m
new file mode 100644
index 0000000..2fb0c68
--- /dev/null
+++ b/Foundation/GTMGeometryUtilsTest.m
@@ -0,0 +1,181 @@
+//
+// GTMGeometryUtilsTest.m
+//
+// Copyright 2006-2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import "GTMSenTestCase.h"
+#import "GTMGeometryUtils.h"
+
+@interface GTMGeometryUtilsTest : SenTestCase
+@end
+
+@implementation GTMGeometryUtilsTest
+
+- (void)testGTMCGPointToNSPoint {
+ CGPoint cgPoint = CGPointMake(15.1,6.2);
+ NSPoint nsPoint = GTMCGPointToNSPoint(cgPoint);
+ STAssertTrue(CGPointEqualToPoint(*(CGPoint*)&nsPoint, cgPoint), nil);
+}
+
+- (void)testGTMNSPointToCGPoint {
+ NSPoint nsPoint = NSMakePoint(10.2,1.5);
+ CGPoint cgPoint = GTMNSPointToCGPoint(nsPoint);
+ STAssertTrue(CGPointEqualToPoint(cgPoint, *(CGPoint*)&nsPoint), nil);
+}
+
+- (void)testGTMCGRectToNSRect {
+ CGRect cgRect = CGRectMake(1.5,2.4,10.6,11.7);
+ NSRect nsRect = GTMCGRectToNSRect(cgRect);
+ STAssertTrue(CGRectEqualToRect(cgRect, *(CGRect*)&nsRect), nil);
+}
+
+
+- (void)testGTMNSRectToCGRect {
+ NSRect nsRect = NSMakeRect(4.6,3.2,22.1,45.0);
+ CGRect cgRect = GTMNSRectToCGRect(nsRect);
+ STAssertTrue(CGRectEqualToRect(cgRect, *(CGRect*)&nsRect), nil);
+}
+
+- (void)testGTMCGSizeToNSSize {
+ CGSize cgSize = {5,6};
+ NSSize nsSize = GTMCGSizeToNSSize(cgSize);
+ STAssertTrue(CGSizeEqualToSize(cgSize, *(CGSize*)&nsSize), nil);
+}
+
+- (void)testGTMNSSizeToCGSize {
+ NSSize nsSize = {22,15};
+ CGSize cgSize = GTMNSSizeToCGSize(nsSize);
+ STAssertTrue(CGSizeEqualToSize(cgSize, *(CGSize*)&nsSize), nil);
+}
+
+- (void)testGTMDistanceBetweenPoints {
+ NSPoint pt1 = NSMakePoint(0, 0);
+ NSPoint pt2 = NSMakePoint(3, 4);
+ STAssertEquals(GTMDistanceBetweenPoints(pt1, pt2), 5.0f, nil);
+ STAssertEquals(GTMDistanceBetweenPoints(pt2, pt1), 5.0f, nil);
+ pt1 = NSMakePoint(1, 1);
+ pt2 = NSMakePoint(1, 1);
+ STAssertEquals(GTMDistanceBetweenPoints(pt1, pt2), 0.0f, nil);
+}
+
+- (void)testGTMAlignRectangles {
+ typedef struct {
+ NSPoint expectedOrigin;
+ GTMRectAlignment alignment;
+ } TestData;
+
+ TestData data[] = {
+ { {1,2}, GTMRectAlignTop },
+ { {0,2}, GTMRectAlignTopLeft },
+ { {2,2}, GTMRectAlignTopRight },
+ { {0,1}, GTMRectAlignLeft },
+ { {1,0}, GTMRectAlignBottom },
+ { {0,0}, GTMRectAlignBottomLeft },
+ { {2,0}, GTMRectAlignBottomRight },
+ { {2,1}, GTMRectAlignRight },
+ { {1,1}, GTMRectAlignCenter },
+ };
+
+ NSRect rect1 = NSMakeRect(0, 0, 4, 4);
+ NSRect rect2 = NSMakeRect(0, 0, 2, 2);
+
+ for (int i = 0; i < sizeof(data) / sizeof(TestData); i++) {
+ NSRect expectedRect;
+ expectedRect.origin = data[i].expectedOrigin;
+ expectedRect.size = NSMakeSize(2, 2);
+ NSRect outRect = GTMAlignRectangles(rect2, rect1, data[i].alignment);
+ STAssertEquals(outRect, expectedRect, nil);
+ }
+}
+
+- (void)testGTMPointsOnRect {
+ NSRect rect = NSMakeRect(0, 0, 2, 2);
+ CGRect cgRect = GTMNSRectToCGRect(rect);
+
+ NSPoint point = GTMNSMidLeft(rect);
+ CGPoint cgPoint = GTMCGMidLeft(cgRect);
+ STAssertEquals(point.x, cgPoint.x, nil);
+ STAssertEquals(point.y, cgPoint.y, nil);
+ STAssertEqualsWithAccuracy(point.y, 1.0f, 0.01f, nil);
+ STAssertEqualsWithAccuracy(point.x, 0.0f, 0.01f, nil);
+
+ point = GTMNSMidRight(rect);
+ cgPoint = GTMCGMidRight(cgRect);
+ STAssertEquals(point.x, cgPoint.x, nil);
+ STAssertEquals(point.y, cgPoint.y, nil);
+ STAssertEqualsWithAccuracy(point.y, 1.0f, 0.01f, nil);
+ STAssertEqualsWithAccuracy(point.x, 2.0f, 0.01f, nil);
+
+ point = GTMNSMidTop(rect);
+ cgPoint = GTMCGMidTop(cgRect);
+ STAssertEquals(point.x, cgPoint.x, nil);
+ STAssertEquals(point.y, cgPoint.y, nil);
+ STAssertEqualsWithAccuracy(point.y, 2.0f, 0.01f, nil);
+ STAssertEqualsWithAccuracy(point.x, 1.0f, 0.01f, nil);
+
+ point = GTMNSMidBottom(rect);
+ cgPoint = GTMCGMidBottom(cgRect);
+ STAssertEquals(point.x, cgPoint.x, nil);
+ STAssertEquals(point.y, cgPoint.y, nil);
+ STAssertEqualsWithAccuracy(point.y, 0.0f, 0.01f, nil);
+ STAssertEqualsWithAccuracy(point.x, 1.0f, 0.01f, nil);
+}
+
+- (void)testGTMRectScaling {
+ NSRect rect = NSMakeRect(1.0f, 2.0f, 5.0f, 10.0f);
+ NSRect rect2 = NSMakeRect(1.0f, 2.0f, 1.0f, 12.0f);
+ STAssertEquals(GTMNSRectScale(rect, 0.2f, 1.2f),
+ rect2, nil);
+ STAssertEquals(GTMCGRectScale(GTMNSRectToCGRect(rect), 0.2f, 1.2f),
+ GTMNSRectToCGRect(rect2), nil);
+}
+
+- (void)testGTMScaleRectangleToSize {
+ NSRect rect = NSMakeRect(0.0f, 0.0f, 10.0f, 10.0f);
+ typedef struct {
+ NSSize size_;
+ NSSize newSize_;
+ } Test;
+ Test tests[] = {
+ { { 5.0, 10.0 }, { 5.0, 5.0 } },
+ { { 10.0, 5.0 }, { 5.0, 5.0 } },
+ { { 10.0, 10.0 }, { 10.0, 10.0 } },
+ { { 11.0, 11.0, }, { 10.0, 10.0 } },
+ { { 5.0, 2.0 }, { 2.0, 2.0 } },
+ { { 2.0, 5.0 }, { 2.0, 2.0 } },
+ { { 2.0, 2.0 }, { 2.0, 2.0 } },
+ { { 0.0, 10.0 }, { 0.0, 0.0 } }
+ };
+
+ for (size_t i = 0; i < sizeof(tests) / sizeof(Test); ++i) {
+ NSRect result = GTMScaleRectangleToSize(rect, tests[i].size_,
+ GTMScaleProportionally);
+ STAssertEquals(result, GTMNSRectOfSize(tests[i].newSize_), @"failed on test %z", i);
+ }
+
+ NSRect result = GTMScaleRectangleToSize(NSZeroRect, tests[0].size_,
+ GTMScaleProportionally);
+ STAssertEquals(result, NSZeroRect, nil);
+
+ result = GTMScaleRectangleToSize(rect, tests[0].size_,
+ GTMScaleToFit);
+ STAssertEquals(result, GTMNSRectOfSize(tests[0].size_), nil);
+
+ result = GTMScaleRectangleToSize(rect, tests[0].size_,
+ GTMScaleNone);
+ STAssertEquals(result, rect, nil);
+}
+@end
diff --git a/Foundation/GTMNSData+zlib.m b/Foundation/GTMNSData+zlib.m
index be84114..514477f 100644
--- a/Foundation/GTMNSData+zlib.m
+++ b/Foundation/GTMNSData+zlib.m
@@ -18,6 +18,7 @@
#import "GTMNSData+zlib.h"
#import <zlib.h>
+#import "GTMDefines.h"
#define kChunkSize 1024
@@ -33,31 +34,35 @@
length:(unsigned)length
compressionLevel:(int)level
useGzip:(BOOL)useGzip {
- if (!bytes || !length) return nil;
+ if (!bytes || !length) {
+ return nil;
+ }
if (level == Z_DEFAULT_COMPRESSION) {
// the default value is actually outside the range, so we have to let it
// through specifically.
- } else if (level < Z_BEST_SPEED)
+ } else if (level < Z_BEST_SPEED) {
level = Z_BEST_SPEED;
- else if (level > Z_BEST_COMPRESSION)
+ } else if (level > Z_BEST_COMPRESSION) {
level = Z_BEST_COMPRESSION;
+ }
z_stream strm;
bzero(&strm, sizeof(z_stream));
int windowBits = 15; // the default
int memLevel = 8; // the default
- if (useGzip)
+ if (useGzip) {
windowBits += 16; // enable gzip header instead of zlib header
+ }
int retCode;
if ((retCode = deflateInit2(&strm, level, Z_DEFLATED, windowBits,
memLevel, Z_DEFAULT_STRATEGY)) != Z_OK) {
-#ifdef DEBUG
- NSLog(@"Failed to init for deflate w/ level %d, error %d",
- level, retCode);
-#endif
+ // COV_NF_START - no real way to force this in a unittest (we guard all args)
+ _GTMDevLog(@"Failed to init for deflate w/ level %d, error %d",
+ level, retCode);
return nil;
+ // COV_NF_END
}
// hint the size at 1/4 the input size
@@ -75,12 +80,15 @@
strm.next_out = output;
retCode = deflate(&strm, Z_FINISH);
if ((retCode != Z_OK) && (retCode != Z_STREAM_END)) {
-#ifdef DEBUG
- NSLog(@"Error trying to deflate some of the payload, error %d",
- retCode);
-#endif
+ // COV_NF_START - no real way to force this in a unittest
+ // (in inflate, we can feed bogus/truncated data to test, but an error
+ // here would be some internal issue w/in zlib, and there isn't any real
+ // way to test it)
+ _GTMDevLog(@"Error trying to deflate some of the payload, error %d",
+ retCode);
deflateEnd(&strm);
return nil;
+ // COV_NF_END
}
// collect what we got
unsigned gotBack = kChunkSize - strm.avail_out;
@@ -90,16 +98,13 @@
} while (retCode == Z_OK);
-#ifdef DEBUG
- if (strm.avail_in != 0) {
- NSLog(@"thought we finished deflate w/o using all input, %u bytes left",
- strm.avail_in);
- }
- if (retCode != Z_STREAM_END) {
- NSLog(@"thought we finished deflate w/o getting a result of stream end, code %d",
- retCode);
- }
-#endif
+ // if the loop exits, we used all input and the stream ended
+ _GTMDevAssert(strm.avail_in == 0,
+ @"thought we finished deflate w/o using all input, %u bytes left",
+ strm.avail_in);
+ _GTMDevAssert(retCode == Z_STREAM_END,
+ @"thought we finished deflate w/o getting a result of stream end, code %d",
+ retCode);
// clean up
deflateEnd(&strm);
@@ -179,7 +184,9 @@
+ (NSData *)gtm_dataByInflatingBytes:(const void *)bytes
length:(unsigned)length {
- if (!bytes || !length) return nil;
+ if (!bytes || !length) {
+ return nil;
+ }
z_stream strm;
bzero(&strm, sizeof(z_stream));
@@ -192,10 +199,10 @@
windowBits += 32; // and +32 to enable zlib or gzip header detection.
int retCode;
if ((retCode = inflateInit2(&strm, windowBits)) != Z_OK) {
-#ifdef DEBUG
- NSLog(@"Failed to init for inflate, error %d", retCode);
-#endif
+ // COV_NF_START - no real way to force this in a unittest (we guard all args)
+ _GTMDevLog(@"Failed to init for inflate, error %d", retCode);
return nil;
+ // COV_NF_END
}
// hint the size at 4x the input size
@@ -209,10 +216,8 @@
strm.next_out = output;
retCode = inflate(&strm, Z_NO_FLUSH);
if ((retCode != Z_OK) && (retCode != Z_STREAM_END)) {
-#ifdef DEBUG
- NSLog(@"Error trying to inflate some of the payload, error %d",
- retCode);
-#endif
+ _GTMDevLog(@"Error trying to inflate some of the payload, error %d",
+ retCode);
inflateEnd(&strm);
return nil;
}
@@ -224,16 +229,17 @@
} while (retCode == Z_OK);
-#ifdef DEBUG
+ // make sure there wasn't more data tacked onto the end of a valid compressed
+ // stream.
if (strm.avail_in != 0) {
- NSLog(@"thought we finished inflate w/o using all input, %u bytes left",
- strm.avail_in);
- }
- if (retCode != Z_STREAM_END) {
- NSLog(@"thought we finished inflate w/o getting a result of stream end, code %d",
- retCode);
+ _GTMDevLog(@"thought we finished inflate w/o using all input, %u bytes left",
+ strm.avail_in);
+ result = nil;
}
-#endif
+ // the only way out of the loop was by hitting the end of the stream
+ _GTMDevAssert(retCode == Z_STREAM_END,
+ @"thought we finished inflate w/o getting a result of stream end, code %d",
+ retCode);
// clean up
inflateEnd(&strm);
diff --git a/Foundation/GTMNSData+zlibTest.m b/Foundation/GTMNSData+zlibTest.m
index 0d863d5..38dbce3 100644
--- a/Foundation/GTMNSData+zlibTest.m
+++ b/Foundation/GTMNSData+zlibTest.m
@@ -16,7 +16,6 @@
// the License.
//
-#import <SenTestingKit/SenTestingKit.h>
#import "GTMSenTestCase.h"
#import "GTMNSData+zlib.h"
@@ -36,11 +35,9 @@ static void FillWithRandom(char *data, unsigned long len) {
static BOOL HasGzipHeader(NSData *data) {
// very simple check
- if ([data length] > 2) {
- const unsigned char *bytes = [data bytes];
- return (bytes[0] == 0x1f) && (bytes[1] == 0x8b);
- }
- return NO;
+ const unsigned char *bytes = [data bytes];
+ return ([data length] > 2) &&
+ ((bytes[0] == 0x1f) && (bytes[1] == 0x8b));
}
@@ -51,6 +48,115 @@ static BOOL HasGzipHeader(NSData *data) {
srandomdev();
}
+- (void)testBoundryValues {
+ NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init];
+ STAssertNotNil(localPool, @"failed to alloc local pool");
+
+ // build some test data
+ NSMutableData *data = [NSMutableData data];
+ 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);
+ STAssertNil([NSData gtm_dataByDeflatingBytes:nil length:666], nil);
+ STAssertNil([NSData gtm_dataByDeflatingBytes:[data bytes] length:0], nil);
+ STAssertNil([NSData gtm_dataByGzippingData:nil], nil);
+ STAssertNil([NSData gtm_dataByGzippingBytes:nil length:666], nil);
+ STAssertNil([NSData gtm_dataByGzippingBytes:[data bytes] length:0], nil);
+ STAssertNil([NSData gtm_dataByInflatingData:nil], nil);
+ STAssertNil([NSData gtm_dataByInflatingBytes:nil length:666], nil);
+ STAssertNil([NSData gtm_dataByInflatingBytes:[data bytes] length:0], nil);
+
+ // test deflate w/ compression levels out of range
+ NSData *deflated = [NSData gtm_dataByDeflatingData:data
+ compressionLevel:-4];
+ STAssertNotNil(deflated, nil);
+ STAssertFalse(HasGzipHeader(deflated), nil);
+ NSData *dataPrime = [NSData gtm_dataByInflatingData:deflated];
+ STAssertNotNil(dataPrime, nil);
+ STAssertEqualObjects(data, dataPrime, nil);
+ deflated = [NSData gtm_dataByDeflatingData:data
+ compressionLevel:20];
+ STAssertNotNil(deflated, nil);
+ STAssertFalse(HasGzipHeader(deflated), nil);
+ dataPrime = [NSData gtm_dataByInflatingData:deflated];
+ STAssertNotNil(dataPrime, nil);
+ STAssertEqualObjects(data, dataPrime, nil);
+
+ // test gzip w/ compression levels out of range
+ NSData *gzipped = [NSData gtm_dataByGzippingData:data
+ compressionLevel:-4];
+ STAssertNotNil(gzipped, nil);
+ STAssertTrue(HasGzipHeader(gzipped), nil);
+ dataPrime = [NSData gtm_dataByInflatingData:gzipped];
+ STAssertNotNil(dataPrime, nil);
+ STAssertEqualObjects(data, dataPrime, nil);
+ gzipped = [NSData gtm_dataByGzippingData:data
+ compressionLevel:20];
+ STAssertNotNil(gzipped, nil);
+ STAssertTrue(HasGzipHeader(gzipped), nil);
+ dataPrime = [NSData gtm_dataByInflatingData:gzipped];
+ STAssertNotNil(dataPrime, nil);
+ STAssertEqualObjects(data, dataPrime, nil);
+
+ // test non-compressed data data itself
+ STAssertNil([NSData gtm_dataByInflatingData:data], nil);
+
+ // test deflated data runs that end before they are done
+ for (int x = 1 ; x < [deflated length] ; ++x) {
+ STAssertNil([NSData gtm_dataByInflatingBytes:[deflated bytes] length:x], nil);
+ }
+
+ // test gzipped data runs that end before they are done
+ for (int x = 1 ; x < [gzipped length] ; ++x) {
+ 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];
+ STAssertNotNil(prefixedDeflated, @"failed to alloc data block");
+ [prefixedDeflated setLength:20];
+ FillWithRandom([prefixedDeflated mutableBytes], [prefixedDeflated length]);
+ [prefixedDeflated appendData:deflated];
+ STAssertNil([NSData gtm_dataByInflatingData:prefixedDeflated], nil);
+ STAssertNil([NSData gtm_dataByInflatingBytes:[prefixedDeflated bytes]
+ length:[prefixedDeflated length]],
+ nil);
+ NSMutableData *prefixedGzipped = [NSMutableData data];
+ STAssertNotNil(prefixedDeflated, @"failed to alloc data block");
+ [prefixedGzipped setLength:20];
+ FillWithRandom([prefixedGzipped mutableBytes], [prefixedGzipped length]);
+ [prefixedGzipped appendData:gzipped];
+ STAssertNil([NSData gtm_dataByInflatingData:prefixedGzipped], nil);
+ STAssertNil([NSData gtm_dataByInflatingBytes:[prefixedGzipped bytes]
+ length:[prefixedGzipped length]],
+ nil);
+
+ // test extra data after the deflated/gzipped data (just to make sure we
+ // don't ignore some of the data)
+ NSMutableData *suffixedDeflated = [NSMutableData data];
+ STAssertNotNil(suffixedDeflated, @"failed to alloc data block");
+ [suffixedDeflated appendData:deflated];
+ [suffixedDeflated appendBytes:[data bytes] length:20];
+ STAssertNil([NSData gtm_dataByInflatingData:suffixedDeflated], nil);
+ STAssertNil([NSData gtm_dataByInflatingBytes:[suffixedDeflated bytes]
+ length:[suffixedDeflated length]],
+ nil);
+ NSMutableData *suffixedGZipped = [NSMutableData data];
+ STAssertNotNil(suffixedGZipped, @"failed to alloc data block");
+ [suffixedGZipped appendData:gzipped];
+ [suffixedGZipped appendBytes:[data bytes] length:20];
+ STAssertNil([NSData gtm_dataByInflatingData:suffixedGZipped], nil);
+ 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) {
diff --git a/Foundation/GTMNSEnumerator+Filter.m b/Foundation/GTMNSEnumerator+Filter.m
index 60c85f3..5ac3a7c 100644
--- a/Foundation/GTMNSEnumerator+Filter.m
+++ b/Foundation/GTMNSEnumerator+Filter.m
@@ -17,33 +17,35 @@
//
#import "GTMNSEnumerator+Filter.h"
+#import "GTMDebugSelectorValidation.h"
+#import "GTMDefines.h"
// a private subclass of NSEnumerator that does all the work.
// public interface just returns one of these.
// This top level class contains all the additional boilerplate. Specific
// behavior is in the subclasses.
-@interface GTMEnumerator : NSEnumerator {
+@interface GTMEnumeratorPrivateBase : NSEnumerator {
@protected
NSEnumerator *base_; // STRONG
SEL operation_; // either a predicate or a transform depending on context.
id other_; // STRONG, may be nil
}
-- (id)nextObject;
+@end
+
+@interface GTMEnumeratorPrivateBase (SubclassesMustProvide)
- (BOOL)filterObject:(id)obj returning:(id *)resultp;
@end
-@implementation GTMEnumerator
+
+@implementation GTMEnumeratorPrivateBase
- (id)initWithBase:(NSEnumerator *)base
sel:(SEL)filter
object:(id)optionalOther {
self = [super init];
if (self) {
- // specializing a nil enumerator returns nil.
- if (nil == base) {
- [self release];
- return nil;
- }
-
+ // someone would have to subclass or directly create an object of this
+ // class, and this class is private to this impl.
+ _GTMDevAssert(base, @"can't bas a nil base enumerator");
base_ = [base retain];
operation_ = filter;
other_ = [optionalOther retain];
@@ -51,11 +53,9 @@
return self;
}
-// it is an error to call this initializer.
-- (id)init {
- [self doesNotRecognizeSelector:_cmd];
- return nil;
-}
+// we don't provide an init because this base class is private to this
+// impl, and no one would be able to create it (if they do, they get whatever
+// they happens...).
- (void)dealloc {
[base_ release];
@@ -72,17 +72,10 @@
}
return nil;
}
-
-// subclasses must override
-- (BOOL)filterObject:(id)obj returning:(id *)resultp {
- [self doesNotRecognizeSelector:_cmd];
- return NO;
-}
@end
// a transformer, for each item in the enumerator, returns a f(item).
-@interface GTMEnumeratorTransformer : GTMEnumerator
-- (BOOL)filterObject:(id)obj returning:(id *)resultp;
+@interface GTMEnumeratorTransformer : GTMEnumeratorPrivateBase
@end
@implementation GTMEnumeratorTransformer
- (BOOL)filterObject:(id)obj returning:(id *)resultp {
@@ -93,8 +86,7 @@
// a transformer, for each item in the enumerator, returns a f(item).
// a target transformer swaps the target and the argument.
-@interface GTMEnumeratorTargetTransformer : GTMEnumerator
-- (BOOL)filterObject:(id)obj returning:(id *)resultp;
+@interface GTMEnumeratorTargetTransformer : GTMEnumeratorPrivateBase
@end
@implementation GTMEnumeratorTargetTransformer
- (BOOL)filterObject:(id)obj returning:(id *)resultp {
@@ -104,8 +96,7 @@
@end
// a filter, for each item in the enumerator, if(f(item)) { returns item. }
-@interface GTMEnumeratorFilter : GTMEnumerator
-- (BOOL)filterObject:(id)obj returning:(id *)resultp;
+@interface GTMEnumeratorFilter : GTMEnumeratorPrivateBase
@end
@implementation GTMEnumeratorFilter
// We must take care here, since Intel leaves junk in high bytes of return register
@@ -119,8 +110,7 @@
// a target filter, for each item in the enumerator, if(f(item)) { returns item. }
// a target transformer swaps the target and the argument.
-@interface GTMEnumeratorTargetFilter : GTMEnumerator
-- (BOOL)filterObject:(id)obj returning:(id *)resultp;
+@interface GTMEnumeratorTargetFilter : GTMEnumeratorPrivateBase
@end
@implementation GTMEnumeratorTargetFilter
// We must take care here, since Intel leaves junk in high bytes of return register
@@ -151,6 +141,10 @@
- (NSEnumerator *)gtm_filteredEnumeratorByTarget:(id)target
performOnEachSelector:(SEL)selector {
+ // make sure the object impls this selector taking an object as an arg.
+ GTMAssertSelectorNilOrImplementedWithArguments(target, selector,
+ @encode(id),
+ NULL);
return [[[GTMEnumeratorTargetFilter alloc] initWithBase:self
sel:selector
object:target] autorelease];
@@ -158,6 +152,10 @@
- (NSEnumerator *)gtm_enumeratorByTarget:(id)target
performOnEachSelector:(SEL)selector {
+ // make sure the object impls this selector taking an object as an arg.
+ GTMAssertSelectorNilOrImplementedWithArguments(target, selector,
+ @encode(id),
+ NULL);
return [[[GTMEnumeratorTargetTransformer alloc] initWithBase:self
sel:selector
object:target] autorelease];
diff --git a/Foundation/GTMNSEnumerator+FilterTest.m b/Foundation/GTMNSEnumerator+FilterTest.m
index 99bde01..224b8b0 100644
--- a/Foundation/GTMNSEnumerator+FilterTest.m
+++ b/Foundation/GTMNSEnumerator+FilterTest.m
@@ -16,7 +16,7 @@
// the License.
//
-#import <SenTestingKit/SenTestingKit.h>
+#import "GTMSenTestCase.h"
#import "GTMNSEnumerator+Filter.h"
@interface GTMNSEnumerator_FilterTest : SenTestCase
@@ -24,8 +24,8 @@
@implementation GTMNSEnumerator_FilterTest
-// test using an NSSet enumerator.
- (void)testEnumeratorByMakingEachObjectPerformSelector {
+ // test w/ a set of strings
NSSet *numbers = [NSSet setWithObjects: @"1", @"2", @"3", nil];
NSEnumerator *e = [[numbers objectEnumerator]
gtm_enumeratorByMakingEachObjectPerformSelector:@selector(stringByAppendingString:)
@@ -37,32 +37,97 @@
}
NSSet *trailingSpacesGood = [NSSet setWithObjects: @"1 ", @"2 ", @"3 ", nil];
STAssertEqualObjects(trailingSpaces, trailingSpacesGood, @"");
+
+ // test an empty set
NSSet *empty = [NSSet set];
- NSEnumerator *ee = [[empty objectEnumerator]
+ e = [[empty objectEnumerator]
gtm_enumeratorByMakingEachObjectPerformSelector:@selector(stringByAppendingString:)
withObject:@" "];
-
- NSMutableSet *emptySpaces = [NSMutableSet set];
- while (nil != (obj = [ee nextObject])) {
- [emptySpaces addObject:obj];
- }
- STAssertEqualObjects(empty, emptySpaces, @"");
+ STAssertNil([e nextObject],
+ @"shouldn't have gotten anything from first advance of enumerator");
}
-// test using an NSDictionary enumerator.
- (void)testFilteredEnumeratorByMakingEachObjectPerformSelector {
- NSDictionary *numbers = [NSDictionary dictionaryWithObjectsAndKeys: @"1", @"1", @"", @"", @"3", @"3", nil];
+ // test with a dict of strings
+ NSDictionary *testDict = [NSDictionary dictionaryWithObjectsAndKeys:
+ @"foo", @"1",
+ @"bar", @"2",
+ @"foobar", @"3",
+ nil];
+ // test those that have prefixes
+ NSEnumerator *e = [[testDict objectEnumerator]
+ gtm_filteredEnumeratorByMakingEachObjectPerformSelector:@selector(hasPrefix:)
+ withObject:@"foo"];
+ // since the dictionary iterates in any order, compare as sets
+ NSSet *filteredValues = [NSSet setWithArray:[e allObjects]];
+ NSSet *expectedValues = [NSSet setWithObjects:@"foo", @"foobar", nil];
+ STAssertEqualObjects(filteredValues, expectedValues, @"");
- // |length| filters out length 0 objects
- NSEnumerator *e = [[numbers objectEnumerator]
- gtm_filteredEnumeratorByMakingEachObjectPerformSelector:@selector(length)
- withObject:nil];
+ // test an empty set
+ NSSet *empty = [NSSet set];
+ e = [[empty objectEnumerator]
+ gtm_filteredEnumeratorByMakingEachObjectPerformSelector:@selector(hasPrefix:)
+ withObject:@"foo"];
+ STAssertNil([e nextObject],
+ @"shouldn't have gotten anything from first advance of enumerator");
+
+ // test an set that will filter out
+ NSSet *filterAway = [NSSet setWithObjects:@"bar", @"baz", nil];
+ e = [[filterAway objectEnumerator]
+ gtm_filteredEnumeratorByMakingEachObjectPerformSelector:@selector(hasPrefix:)
+ withObject:@"foo"];
+ STAssertNil([e nextObject],
+ @"shouldn't have gotten anything from first advance of enumerator");
+}
- NSArray *lengths = [e allObjects];
- NSArray *lengthsGood = [NSArray arrayWithObjects: @"1", @"3", nil];
- STAssertEqualObjects(lengths, lengthsGood, @"");
+- (void)testEnumeratorByTargetPerformOnEachSelector {
+ // test w/ a set of strings
+ NSSet *numbers = [NSSet setWithObjects: @"1", @"2", @"3", nil];
+ NSString *target = @"foo";
+ NSEnumerator *e = [[numbers objectEnumerator]
+ gtm_enumeratorByTarget:target
+ performOnEachSelector:@selector(stringByAppendingString:)];
+ // since the set iterates in any order, compare as sets
+ NSSet *collectedValues = [NSSet setWithArray:[e allObjects]];
+ NSSet *expectedValues = [NSSet setWithObjects:@"foo1", @"foo2", @"foo3", nil];
+ STAssertEqualObjects(collectedValues, expectedValues, @"");
+
+ // test an empty set
+ NSSet *empty = [NSSet set];
+ e = [[empty objectEnumerator]
+ gtm_enumeratorByTarget:target
+ performOnEachSelector:@selector(stringByAppendingString:)];
+ STAssertNil([e nextObject],
+ @"shouldn't have gotten anything from first advance of enumerator");
}
+- (void)testFilteredEnumeratorByTargetPerformOnEachSelector {
+ // test w/ a set of strings
+ NSSet *numbers = [NSSet setWithObjects:@"1", @"2", @"3", @"4", nil];
+ NSSet *target = [NSSet setWithObjects:@"2", @"4", @"6", nil];
+ NSEnumerator *e = [[numbers objectEnumerator]
+ gtm_filteredEnumeratorByTarget:target
+ performOnEachSelector:@selector(containsObject:)];
+ // since the set iterates in any order, compare as sets
+ NSSet *filteredValues = [NSSet setWithArray:[e allObjects]];
+ NSSet *expectedValues = [NSSet setWithObjects:@"2", @"4", nil];
+ STAssertEqualObjects(filteredValues, expectedValues, @"");
+
+ // test an empty set
+ NSSet *empty = [NSSet set];
+ e = [[empty objectEnumerator]
+ gtm_filteredEnumeratorByTarget:target
+ performOnEachSelector:@selector(containsObject:)];
+ STAssertNil([e nextObject],
+ @"shouldn't have gotten anything from first advance of enumerator");
+ // test an set that will filter out
+ NSSet *filterAway = [NSSet setWithObjects:@"bar", @"baz", nil];
+ e = [[filterAway objectEnumerator]
+ gtm_filteredEnumeratorByTarget:target
+ performOnEachSelector:@selector(containsObject:)];
+ STAssertNil([e nextObject],
+ @"shouldn't have gotten anything from first advance of enumerator");
+}
@end
diff --git a/Foundation/GTMNSFileManager+Path.h b/Foundation/GTMNSFileManager+Path.h
index 330c45e..95ba41e 100644
--- a/Foundation/GTMNSFileManager+Path.h
+++ b/Foundation/GTMNSFileManager+Path.h
@@ -52,7 +52,8 @@
/// Args:
/// extension - the file extension (excluding the leading ".") to match.
/// If nil, all files are matched.
-/// directoryPath - the directory to look in. Subdirectories are not traversed.
+/// directoryPath - the directory to look in. NOTE: Subdirectories are NOT
+/// traversed.
///
/// Returns:
/// An NSArray of absolute file paths that have |extension|. nil is returned
diff --git a/Foundation/GTMNSFileManager+PathTest.m b/Foundation/GTMNSFileManager+PathTest.m
index 10b4cbd..0f2eb35 100644
--- a/Foundation/GTMNSFileManager+PathTest.m
+++ b/Foundation/GTMNSFileManager+PathTest.m
@@ -16,30 +16,72 @@
// the License.
//
-#import <SenTestingKit/SenTestingKit.h>
+#import "GTMSenTestCase.h"
#import "GTMNSFileManager+Path.h"
-@interface GTMNSFileManager_PathTest : SenTestCase
+@interface GTMNSFileManager_PathTest : SenTestCase {
+ NSString *baseDir_;
+}
@end
@implementation GTMNSFileManager_PathTest
+- (void)setUp {
+ // make a directory to scribble in
+ baseDir_ =
+ [[NSTemporaryDirectory()
+ stringByAppendingPathComponent:@"GTMNSFileManager_PathTest"] retain];
+ if (baseDir_) {
+ NSFileManager *fm = [NSFileManager defaultManager];
+ if (![fm fileExistsAtPath:baseDir_] &&
+ ![fm createDirectoryAtPath:baseDir_ attributes:nil]) {
+ // COV_NF_START
+ // if the dir exists or we failed to create it, drop the baseDir_
+ [baseDir_ release];
+ baseDir_ = nil;
+ // COV_NF_END
+ }
+ }
+}
+
+- (void)tearDown {
+ if (baseDir_) {
+ // clean up our directory
+ NSFileManager *fm = [NSFileManager defaultManager];
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
+ NSError *error = nil;
+ [fm removeItemAtPath:baseDir_ error:&error];
+ STAssertNil(error,
+ @"Unable to delete %@: %@", baseDir_, [error description]);
+#else
+ [fm removeFileAtPath:baseDir_ handler:nil];
+#endif
+
+ [baseDir_ release];
+ baseDir_ = nil;
+ }
+}
+
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
- (void)testCreateFullPathToDirectoryAttributes {
- NSString *baseDir =
- [NSTemporaryDirectory() stringByAppendingPathComponent:@"testCreateFullPathToDirectoryAttributes"];
- NSString *testPath = [baseDir stringByAppendingPathComponent:@"/foo/bar/baz"];
+ STAssertNotNil(baseDir_, @"setUp failed");
+
+ NSString *testPath =
+ [baseDir_ stringByAppendingPathComponent:@"/foo/bar/baz"];
+ STAssertNotNil(testPath, nil);
NSFileManager *fm = [NSFileManager defaultManager];
STAssertFalse([fm fileExistsAtPath:testPath],
- @"You must delete '%@' before running this test", baseDir);
+ @"You must delete '%@' before running this test", testPath);
+ STAssertFalse([fm gtm_createFullPathToDirectory:nil attributes:nil],
+ @"didn't fail on nil input");
+
STAssertTrue([fm gtm_createFullPathToDirectory:testPath attributes:nil],
@"Failed to create nested testPath");
-
- STAssertTrue([fm removeFileAtPath:baseDir handler:nil],
- @"Failed to delete \'%@\'", baseDir);
+ STAssertTrue([fm gtm_createFullPathToDirectory:testPath attributes:nil],
+ @"Failed to succeed on second create of testPath");
NSString *pathToFail = [@"/etc" stringByAppendingPathComponent:testPath];
STAssertFalse([fm gtm_createFullPathToDirectory:pathToFail attributes:nil],
@@ -52,7 +94,156 @@
#endif // MAC_OS_X_VERSION_MIN_REQUIRED < 1050
- (void)testfilePathsWithExtensionsInDirectory {
- // TODO: need a test for filePathsWithExtensions:inDirectory:
+ STAssertNotNil(baseDir_, @"setUp failed");
+
+ NSFileManager *fm = [NSFileManager defaultManager];
+ NSString *bogusPath = @"/some/place/that/does/not/exist";
+
+ // --------------------------------------------------------------------------
+ // test fail cases first
+
+ // single
+ STAssertNil([fm gtm_filePathsWithExtension:nil inDirectory:nil],
+ @"shouldn't have gotten anything for nil dir");
+ STAssertNil([fm gtm_filePathsWithExtension:@"txt" inDirectory:nil],
+ @"shouldn't have gotten anything for nil dir");
+ STAssertNil([fm gtm_filePathsWithExtension:@"txt" inDirectory:bogusPath],
+ @"shouldn't have gotten anything for a bogus dir");
+ // array
+ STAssertNil([fm gtm_filePathsWithExtensions:nil inDirectory:nil],
+ @"shouldn't have gotten anything for nil dir");
+ STAssertNil([fm gtm_filePathsWithExtensions:[NSArray array] inDirectory:nil],
+ @"shouldn't have gotten anything for nil dir");
+ STAssertNil([fm gtm_filePathsWithExtensions:[NSArray arrayWithObject:@"txt"]
+ inDirectory:nil],
+ @"shouldn't have gotten anything for nil dir");
+ STAssertNil([fm gtm_filePathsWithExtensions:[NSArray arrayWithObject:@"txt"]
+ inDirectory:bogusPath],
+ @"shouldn't have gotten anything for a bogus dir");
+
+ // --------------------------------------------------------------------------
+ // create some test data
+
+ NSString *testDirs[] = {
+ @"", @"/foo", // mave a subdir to make sure we don't match w/in it
+ };
+ NSString *testFiles[] = {
+ @"a.txt", @"b.txt", @"c.rtf", @"d.m",
+ };
+
+ for (int i = 0; i < sizeof(testDirs) / sizeof(NSString*); i++) {
+ NSString *testDir = nil;
+ if ([testDirs[i] length]) {
+ testDir = [baseDir_ stringByAppendingPathComponent:testDirs[i]];
+ STAssertTrue([fm createDirectoryAtPath:testDir attributes:nil], nil);
+ } else {
+ testDir = baseDir_;
+ }
+ for (int j = 0; j < sizeof(testFiles) / sizeof(NSString*); j++) {
+ NSString *testFile = [testDir stringByAppendingPathComponent:testFiles[j]];
+ STAssertTrue([@"test" writeToFile:testFile atomically:YES], nil);
+ }
+ }
+
+ // build set of the top level items
+ NSMutableArray *allFiles = [NSMutableArray array];
+ for (int i = 0; i < sizeof(testDirs) / sizeof(NSString*); i++) {
+ if ([testDirs[i] length]) {
+ NSString *testDir = [baseDir_ stringByAppendingPathComponent:testDirs[i]];
+ [allFiles addObject:testDir];
+ }
+ }
+ for (int j = 0; j < sizeof(testFiles) / sizeof(NSString*); j++) {
+ NSString *testFile = [baseDir_ stringByAppendingPathComponent:testFiles[j]];
+ [allFiles addObject:testFile];
+ }
+
+ NSArray *matches = nil;
+ NSArray *expectedMatches = nil;
+ NSArray *extensions = nil;
+
+ // NOTE: we do all compares w/ sets so order doesn't matter
+
+ // --------------------------------------------------------------------------
+ // test match all
+
+ // single
+ matches = [fm gtm_filePathsWithExtension:nil inDirectory:baseDir_];
+ STAssertEqualObjects([NSSet setWithArray:matches],
+ [NSSet setWithArray:allFiles],
+ @"didn't get all files for nil extension");
+ matches = [fm gtm_filePathsWithExtension:@"" inDirectory:baseDir_];
+ STAssertEqualObjects([NSSet setWithArray:matches],
+ [NSSet setWithArray:allFiles],
+ @"didn't get all files for nil extension");
+ // array
+ matches = [fm gtm_filePathsWithExtensions:nil inDirectory:baseDir_];
+ STAssertEqualObjects([NSSet setWithArray:matches],
+ [NSSet setWithArray:allFiles],
+ @"didn't get all files for nil extension");
+ matches = [fm gtm_filePathsWithExtensions:[NSArray array]
+ inDirectory:baseDir_];
+ STAssertEqualObjects([NSSet setWithArray:matches],
+ [NSSet setWithArray:allFiles],
+ @"didn't get all files for nil extension");
+
+ // --------------------------------------------------------------------------
+ // test match something
+
+ // single
+ extensions = [NSArray arrayWithObject:@"txt"];
+ matches = [fm gtm_filePathsWithExtension:@"txt" inDirectory:baseDir_];
+ expectedMatches = [allFiles pathsMatchingExtensions:extensions];
+ STAssertEqualObjects([NSSet setWithArray:matches],
+ [NSSet setWithArray:expectedMatches],
+ @"didn't get expected files");
+ // array
+ matches = [fm gtm_filePathsWithExtensions:extensions inDirectory:baseDir_];
+ expectedMatches = [allFiles pathsMatchingExtensions:extensions];
+ STAssertEqualObjects([NSSet setWithArray:matches],
+ [NSSet setWithArray:expectedMatches],
+ @"didn't get expected files");
+ extensions = [NSArray arrayWithObjects:@"txt", @"rtf", @"xyz", nil];
+ matches = [fm gtm_filePathsWithExtensions:extensions inDirectory:baseDir_];
+ expectedMatches = [allFiles pathsMatchingExtensions:extensions];
+ STAssertEqualObjects([NSSet setWithArray:matches],
+ [NSSet setWithArray:expectedMatches],
+ @"didn't get expected files");
+
+ // --------------------------------------------------------------------------
+ // test match nothing
+
+ // single
+ extensions = [NSArray arrayWithObject:@"xyz"];
+ matches = [fm gtm_filePathsWithExtension:@"xyz" inDirectory:baseDir_];
+ expectedMatches = [allFiles pathsMatchingExtensions:extensions];
+ STAssertEqualObjects([NSSet setWithArray:matches],
+ [NSSet setWithArray:expectedMatches],
+ @"didn't get expected files");
+ // array
+ matches = [fm gtm_filePathsWithExtensions:extensions inDirectory:baseDir_];
+ expectedMatches = [allFiles pathsMatchingExtensions:extensions];
+ STAssertEqualObjects([NSSet setWithArray:matches],
+ [NSSet setWithArray:expectedMatches],
+ @"didn't get expected files");
+
+ // --------------------------------------------------------------------------
+ // test match an empty dir
+
+ // create the empty dir
+ NSString *emptyDir = [baseDir_ stringByAppendingPathComponent:@"emptyDir"];
+ STAssertTrue([fm createDirectoryAtPath:emptyDir attributes:nil], nil);
+
+ // single
+ matches = [fm gtm_filePathsWithExtension:@"txt" inDirectory:emptyDir];
+ STAssertEqualObjects([NSSet setWithArray:matches], [NSSet set],
+ @"expected empty dir");
+ // array
+ matches = [fm gtm_filePathsWithExtensions:[NSArray arrayWithObject:@"txt"]
+ inDirectory:emptyDir];
+ STAssertEqualObjects([NSSet setWithArray:matches], [NSSet set],
+ @"expected empty dir");
+
}
@end
diff --git a/Foundation/GTMNSString+HTML.m b/Foundation/GTMNSString+HTML.m
index f9e99dc..a6abb0e 100644
--- a/Foundation/GTMNSString+HTML.m
+++ b/Foundation/GTMNSString+HTML.m
@@ -17,7 +17,10 @@
// the License.
//
+#import "GTMDefines.h"
#import "GTMNSString+HTML.h"
+#import "GTMNSString+Utilities.h"
+#import "GTMMethodCheck.h"
typedef struct {
NSString *escapeSequence;
@@ -355,7 +358,7 @@ static HTMLEscapeMap gUnicodeHTMLEscapeMap[] = {
// Utility function for Bsearching table above
-static int escapeMapCompare(const void *ucharVoid, const void *mapVoid) {
+static int EscapeMapCompare(const void *ucharVoid, const void *mapVoid) {
unichar *uchar = (unichar*)ucharVoid;
HTMLEscapeMap *map = (HTMLEscapeMap*)mapVoid;
int val;
@@ -370,45 +373,49 @@ static int escapeMapCompare(const void *ucharVoid, const void *mapVoid) {
}
@implementation NSString (GTMNSStringHTMLAdditions)
+GTM_METHOD_CHECK(NSString, gtm_UTF16StringWithLength:);
- (NSString *)gtm_stringByEscapingHTMLUsingTable:(HTMLEscapeMap*)table
ofSize:(int)size
- escapingUnicode:(BOOL)escapeUnicode {
- NSMutableString *finalString = [NSMutableString string];
+ escapingUnicode:(BOOL)escapeUnicode {
int length = [self length];
- require_quiet(length != 0, cantConvertAnything);
+ if (!length) {
+ return nil;
+ }
+
+ NSMutableString *finalString = [NSMutableString string];
- unichar *buffer = malloc(sizeof(unichar) * length);
- require_action(buffer, cantAllocBuffer, finalString = nil);
- unichar *buffer2 = malloc(sizeof(unichar) * length);
- require_action(buffer2, cantAllocBuffer2, finalString = nil);
+ NSMutableData *data2 = [NSMutableData dataWithCapacity:sizeof(unichar) * length];
+ const unichar *buffer = (const unichar *)[self gtm_UTF16StringWithLength:nil];
+
+ if (!buffer || !data2) {
+ // COV_NF_BEGIN
+ _GTMDevLog(@"Unable to allocate buffer or data2");
+ return nil;
+ // COV_NF_END
+ }
+
+ unichar *buffer2 = (unichar *)[data2 mutableBytes];
- [self getCharacters:buffer];
int buffer2Length = 0;
for (int i = 0; i < length; ++i) {
HTMLEscapeMap *val = bsearch(&buffer[i], table,
size / sizeof(HTMLEscapeMap),
- sizeof(HTMLEscapeMap), escapeMapCompare);
+ sizeof(HTMLEscapeMap), EscapeMapCompare);
if (val || (escapeUnicode && buffer[i] > 127)) {
if (buffer2Length) {
- CFStringRef buffer2String =
- CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
- buffer2, buffer2Length,
- kCFAllocatorNull);
- require_action(buffer2String, cantCreateString, finalString = nil);
- [finalString appendString:(NSString*)buffer2String];
- CFRelease(buffer2String);
+ CFStringAppendCharacters((CFMutableStringRef)finalString,
+ buffer2,
+ buffer2Length);
buffer2Length = 0;
}
if (val) {
[finalString appendString:val->escapeSequence];
}
- else if (escapeUnicode && buffer[i] > 127) {
+ else {
+ _GTMDevAssert(escapeUnicode && buffer[i] > 127, @"Illegal Character");
[finalString appendFormat:@"&#%d;", buffer[i]];
- } else {
- // Should never get here. Assert in debug.
- require_action(NO, cantCreateString, finalString = nil);
}
} else {
buffer2[buffer2Length] = buffer[i];
@@ -416,20 +423,10 @@ static int escapeMapCompare(const void *ucharVoid, const void *mapVoid) {
}
}
if (buffer2Length) {
- CFStringRef buffer2String =
- CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
- buffer2, buffer2Length,
- kCFAllocatorNull);
- require_action(buffer2String, cantCreateString, finalString = nil);
- [finalString appendString:(NSString*)buffer2String];
- CFRelease(buffer2String);
+ CFStringAppendCharacters((CFMutableStringRef)finalString,
+ buffer2,
+ buffer2Length);
}
-cantCreateString:
- free(buffer2);
-cantAllocBuffer2:
- free(buffer);
-cantAllocBuffer:
-cantConvertAnything:
return finalString;
}
diff --git a/Foundation/GTMNSString+HTMLTest.m b/Foundation/GTMNSString+HTMLTest.m
index b60ed9d..c7b931a 100644
--- a/Foundation/GTMNSString+HTMLTest.m
+++ b/Foundation/GTMNSString+HTMLTest.m
@@ -16,7 +16,6 @@
// the License.
//
-#import <SenTestingKit/SenTestingKit.h>
#import "GTMSenTestCase.h"
#import "GTMNSString+HTML.h"
diff --git a/Foundation/GTMNSString+Utilities.h b/Foundation/GTMNSString+Utilities.h
new file mode 100644
index 0000000..3b4ee00
--- /dev/null
+++ b/Foundation/GTMNSString+Utilities.h
@@ -0,0 +1,41 @@
+//
+// GTMNSString+Utilities.h
+// Misc NSString Utilities
+//
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface NSString (GTMNSStringUtilitiesAdditions)
+
+// Returns a a UTF16 buffer. Avoids copying the data if at all
+// possible for fastest possible/least memory access to the underlying
+// unicode characters (UTF16). This returned buffer is NOT null terminated.
+// *DANGER*
+// Since we avoid copying data you can only be guaranteed access to
+// the bytes of the data for the lifetime of the string that you have extracted
+// the data from. This exists to allow speedy access to the underlying buffer
+// and guaranteed memory cleanup if memory needs to be allocated.
+// Do not free the returned pointer.
+//
+// Args:
+// length - returns the number of unichars in the buffer. Send in nil if
+// you don't care.
+//
+// Returns:
+// pointer to the buffer. Nil on failure.
+- (const unichar*)gtm_UTF16StringWithLength:(size_t*)length;
+@end
diff --git a/Foundation/GTMNSString+Utilities.m b/Foundation/GTMNSString+Utilities.m
new file mode 100644
index 0000000..3419d43
--- /dev/null
+++ b/Foundation/GTMNSString+Utilities.m
@@ -0,0 +1,48 @@
+//
+// GTMNSString+Utilities.m
+// Misc NSString Utilities
+//
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import "GTMDefines.h"
+#import "GTMNSString+Utilities.h"
+
+@implementation NSString (GTMNSStringUtilitiesAdditions)
+
+- (const unichar*)gtm_UTF16StringWithLength:(size_t*)length {
+ size_t size = [self length];
+ const UniChar *buffer = CFStringGetCharactersPtr((CFStringRef)self);
+ if (!buffer) {
+ size_t memsize = size * sizeof(UniChar);
+
+ // nope, alloc buffer and fetch the chars ourselves
+ buffer = malloc(memsize);
+ if (!buffer) {
+ // COV_NF_BEGIN - Memory fail case
+ _GTMDevLog(@"couldn't alloc buffer");
+ return nil;
+ // COV_NF_END
+ }
+ [self getCharacters:(void*)buffer];
+ [NSData dataWithBytesNoCopy:(void*)buffer length:size];
+ }
+ if (length) {
+ *length = size;
+ }
+ return buffer;
+}
+
+@end
diff --git a/Foundation/GTMNSString+UtilitiesTest.m b/Foundation/GTMNSString+UtilitiesTest.m
new file mode 100644
index 0000000..8394aaf
--- /dev/null
+++ b/Foundation/GTMNSString+UtilitiesTest.m
@@ -0,0 +1,62 @@
+//
+// GTMNSString+UtilitiesTest.m
+// Misc NSString Utilities
+//
+// Copyright 2006-2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+
+#import "GTMSenTestCase.h"
+#import "GTMNSString+Utilities.h"
+
+@interface GTMNSString_UtilitiesTest : SenTestCase
+@end
+
+@implementation GTMNSString_UtilitiesTest
+
+- (void)testStringWithLength {
+ NSString *string = @"";
+ size_t length;
+ const unichar *buffer = [string gtm_UTF16StringWithLength:&length];
+ STAssertNotNULL(buffer, @"Buffer shouldn't be NULL");
+ STAssertEquals(length, 0LU, @"Length should be 0");
+
+ UniChar unicharBytes[] = { 0x50, 0x51, 0x52 };
+ string = [[[NSString alloc] initWithCharactersNoCopy:unicharBytes
+ length:3
+ freeWhenDone:NO] autorelease];
+ buffer = [string gtm_UTF16StringWithLength:&length];
+ STAssertEquals(buffer,
+ (const unichar*)unicharBytes,
+ @"Pointers should be equal");
+ STAssertEquals(length,
+ 3UL,
+ nil);
+
+ char utf8Bytes[] = { 0x50, 0x51, 0x52, 0x0 };
+ string = [NSString stringWithUTF8String:utf8Bytes];
+ buffer = [string gtm_UTF16StringWithLength:&length];
+ STAssertNotEquals(buffer,
+ (const unichar*)utf8Bytes,
+ @"Pointers should not be equal");
+ STAssertEquals(length,
+ 3UL,
+ nil);
+ buffer = [string gtm_UTF16StringWithLength:nil];
+ STAssertNotEquals(buffer,
+ (const unichar*)utf8Bytes,
+ @"Pointers should not be equal");
+}
+@end
diff --git a/Foundation/GTMNSString+XML.m b/Foundation/GTMNSString+XML.m
index c08f7b7..7ea97b4 100644
--- a/Foundation/GTMNSString+XML.m
+++ b/Foundation/GTMNSString+XML.m
@@ -16,8 +16,11 @@
// the License.
//
+#import "GTMDefines.h"
#import "GTMNSString+XML.h"
#import "GTMGarbageCollection.h"
+#import "GTMNSString+Utilities.h"
+#import "GTMMethodCheck.h"
typedef enum {
kGMXMLCharModeEncodeQUOT = 0,
@@ -88,6 +91,7 @@ FOUNDATION_STATIC_INLINE GMXMLCharMode XMLModeForUnichar(UniChar c) {
return kGMXMLCharModeInvalid;
} // XMLModeForUnichar
+
static NSString *AutoreleasedCloneForXML(NSString *src, BOOL escaping) {
//
// NOTE:
@@ -99,23 +103,16 @@ static NSString *AutoreleasedCloneForXML(NSString *src, BOOL escaping) {
// we can't use the CF call here because it leaves the invalid chars
// in the string.
-
- NSMutableString *finalString = [NSMutableString string];
int length = [src length];
- require_quiet(length != 0, cantConvertAnything);
-
- // see if we can just use the interal version
- BOOL freeBuffer = NO;
- UniChar *buffer = (UniChar*)CFStringGetCharactersPtr((CFStringRef)src);
- if (!buffer) {
- // nope, alloc buffer and fetch the chars ourselves
- buffer = malloc(sizeof(UniChar) * length);
- require_action(buffer, cantCreateString, finalString = nil);
- freeBuffer = YES;
- [src getCharacters:buffer];
+ if (!length) {
+ return nil;
}
- UniChar *goodRun = buffer;
+ NSMutableString *finalString = [NSMutableString string];
+ const UniChar *buffer = [src gtm_UTF16StringWithLength:nil];
+ _GTMDevAssert(buffer, @"couldn't alloc buffer");
+
+ const UniChar *goodRun = buffer;
int goodRunLength = 0;
for (int i = 0; i < length; ++i) {
@@ -133,13 +130,9 @@ static NSString *AutoreleasedCloneForXML(NSString *src, BOOL escaping) {
// start by adding what we already collected (if anything)
if (goodRunLength) {
- CFStringRef goodRunString =
- CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
- goodRun, goodRunLength,
- kCFAllocatorNull);
- require_action(goodRunString != NULL, cantCreateString, finalString = nil);
- [finalString appendString:(NSString*)goodRunString];
- CFRelease(goodRunString);
+ CFStringAppendCharacters((CFMutableStringRef)finalString,
+ goodRun,
+ goodRunLength);
goodRunLength = 0;
}
@@ -156,23 +149,15 @@ static NSString *AutoreleasedCloneForXML(NSString *src, BOOL escaping) {
// anything left to add?
if (goodRunLength) {
- CFStringRef goodRunString =
- CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
- goodRun, goodRunLength,
- kCFAllocatorNull);
- require_action(goodRunString != NULL, cantCreateString2, finalString = nil);
- [finalString appendString:(NSString*)goodRunString];
- CFRelease(goodRunString);
+ CFStringAppendCharacters((CFMutableStringRef)finalString,
+ goodRun,
+ goodRunLength);
}
-cantCreateString:
-cantCreateString2:
- if (freeBuffer)
- free(buffer);
-cantConvertAnything:
return finalString;
} // AutoreleasedCloneForXML
@implementation NSString (GTMNSStringXMLAdditions)
+GTM_METHOD_CHECK(NSString, gtm_UTF16StringWithLength:);
- (NSString *)gtm_stringBySanitizingAndEscapingForXML {
return AutoreleasedCloneForXML(self, YES);
diff --git a/Foundation/GTMNSString+XMLTest.m b/Foundation/GTMNSString+XMLTest.m
index 52834f5..926708f 100644
--- a/Foundation/GTMNSString+XMLTest.m
+++ b/Foundation/GTMNSString+XMLTest.m
@@ -17,7 +17,7 @@
//
-#import <SenTestingKit/SenTestingKit.h>
+#import "GTMSenTestCase.h"
#import "GTMNSString+XML.h"
@@ -27,33 +27,61 @@
@implementation GTMNSString_XMLTest
- (void)testStringBySanitizingAndEscapingForXML {
+ // test the substitutions cases
UniChar chars[] = {
'z', 0, 'z', 1, 'z', 4, 'z', 5, 'z', 34, 'z', 38, 'z', 39, 'z',
- 60, 'z', 62, 'z', ' ', 'z', 0xd800, 'z', 0xDFFF, 'z', 0xFFFE,
- 'z', 0xFFFF, 'z' };
+ 60, 'z', 62, 'z', ' ', 'z', 0xd800, 'z', 0xDFFF, 'z', 0xE000,
+ 'z', 0xFFFE, 'z', 0xFFFF, 'z', '\n', 'z', '\r', 'z', '\t', 'z' };
NSString *string1 = [NSString stringWithCharacters:chars
length:sizeof(chars) / sizeof(UniChar)];
- NSString *string2 = @"zzzzz&quot;z&amp;z&apos;z&lt;z&gt;z zzzzz";
+ NSString *string2 =
+ [NSString stringWithFormat:@"zzzzz&quot;z&amp;z&apos;z&lt;z&gt;z zzz%Czzz\nz\rz\tz",
+ 0xE000];
STAssertEqualObjects([string1 gtm_stringBySanitizingAndEscapingForXML],
string2,
@"Sanitize and Escape for XML failed");
+
+ // force the backing store of the NSString to test extraction paths
+ char ascBuffer[] = "a\01bcde\nf";
+ NSString *ascString =
+ [[[NSString alloc] initWithBytesNoCopy:ascBuffer
+ length:sizeof(ascBuffer) / sizeof(char)
+ encoding:NSASCIIStringEncoding
+ freeWhenDone:NO] autorelease];
+ STAssertEqualObjects([ascString gtm_stringBySanitizingAndEscapingForXML],
+ @"abcde\nf",
+ @"Sanitize and Escape for XML from asc buffer failed");
}
- (void)testStringBySanitizingToXMLSpec {
+ // test the substitutions cases
UniChar chars[] = {
'z', 0, 'z', 1, 'z', 4, 'z', 5, 'z', 34, 'z', 38, 'z', 39, 'z',
- 60, 'z', 62, 'z', ' ', 'z', 0xd800, 'z', 0xDFFF, 'z', 0xFFFE,
- 'z', 0xFFFF, 'z' };
+ 60, 'z', 62, 'z', ' ', 'z', 0xd800, 'z', 0xDFFF, 'z', 0xE000,
+ 'z', 0xFFFE, 'z', 0xFFFF, 'z', '\n', 'z', '\r', 'z', '\t', 'z' };
NSString *string1 = [NSString stringWithCharacters:chars
length:sizeof(chars) / sizeof(UniChar)];
- NSString *string2 = @"zzzzz\"z&z'z<z>z zzzzz";
+ NSString *string2 =
+ [NSString stringWithFormat:@"zzzzz\"z&z'z<z>z zzz%Czzz\nz\rz\tz",
+ 0xE000];
STAssertEqualObjects([string1 gtm_stringBySanitizingToXMLSpec],
string2,
@"Sanitize for XML failed");
+
+ // force the backing store of the NSString to test extraction paths
+ char ascBuffer[] = "a\01bcde\nf";
+ NSString *ascString =
+ [[[NSString alloc] initWithBytesNoCopy:ascBuffer
+ length:sizeof(ascBuffer) / sizeof(char)
+ encoding:NSASCIIStringEncoding
+ freeWhenDone:NO] autorelease];
+ STAssertEqualObjects([ascString gtm_stringBySanitizingToXMLSpec],
+ @"abcde\nf",
+ @"Sanitize and Escape for XML from asc buffer failed");
}
@end
diff --git a/Foundation/GTMObjC2Runtime.h b/Foundation/GTMObjC2Runtime.h
new file mode 100644
index 0000000..325a752
--- /dev/null
+++ b/Foundation/GTMObjC2Runtime.h
@@ -0,0 +1,54 @@
+//
+// GTMObjC2Runtime.h
+//
+// Copyright 2007-2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import <objc/objc-runtime.h>
+#import <objc/Object.h>
+
+// These functions exist for code that we want to compile on both the < 10.5
+// sdks and on the >= 10.5 sdks without warnings. It basically reimplements
+// certain parts of the objc2 runtime in terms of the objc1 runtime. It is not
+// a complete implementation as I've only implemented the routines I know we
+// use. Feel free to add more as necessary.
+// These functions are not documented because they conform to the documentation
+// for the ObjC2 Runtime.
+
+#if OBJC_API_VERSION >= 2 // Only have optional and req'd keywords in ObjC2.
+#define AT_OPTIONAL @optional
+#define AT_REQUIRED @required
+#else
+#define AT_OPTIONAL
+#define AT_REQUIRED
+#endif
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+#import "objc/Protocol.h"
+
+Class object_getClass(id obj);
+const char *class_getName(Class cls);
+BOOL class_conformsToProtocol(Class cls, Protocol *protocol);
+Class class_getSuperclass(Class cls);
+Method *class_copyMethodList(Class cls, unsigned int *outCount);
+SEL method_getName(Method m);
+void method_exchangeImplementations(Method m1, Method m2);
+IMP method_getImplementation(Method method);
+IMP method_setImplementation(Method method, IMP imp);
+struct objc_method_description protocol_getMethodDescription(Protocol *p,
+ SEL aSel,
+ BOOL isRequiredMethod,
+ BOOL isInstanceMethod);
+#endif // OBJC2_UNAVAILABLE
diff --git a/Foundation/GTMObjC2Runtime.m b/Foundation/GTMObjC2Runtime.m
new file mode 100644
index 0000000..00a3c6e
--- /dev/null
+++ b/Foundation/GTMObjC2Runtime.m
@@ -0,0 +1,148 @@
+//
+// GTMObjC2Runtime.m
+//
+// Copyright 2007-2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import "GTMObjC2Runtime.h"
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+#import <stdlib.h>
+#import <string.h>
+
+Class object_getClass(id obj) {
+ if (!obj) return NULL;
+ return obj->isa;
+}
+
+const char *class_getName(Class cls) {
+ if (!cls) return "nil";
+ return cls->name;
+}
+
+BOOL class_conformsToProtocol(Class cls, Protocol *protocol) {
+ // We intentionally don't check cls as it crashes on Leopard so we want
+ // to crash on Tiger as well.
+ // I logged
+ // Radar 5572978 class_conformsToProtocol crashes when arg1 is passed as nil
+ // because it seems odd that this API won't accept nil for cls considering
+ // all the other apis will accept nil args.
+ // If this does get fixed, remember to enable the unit tests.
+ if (!protocol) return NO;
+
+ struct objc_protocol_list *protos;
+ for (protos = cls->protocols; protos != NULL; protos = protos->next) {
+ for (int i = 0; i < protos->count; i++) {
+ if ([protos->list[i] conformsTo:protocol]) {
+ return YES;
+ }
+ }
+ }
+ return NO;
+}
+
+Class class_getSuperclass(Class cls) {
+ if (!cls) return NULL;
+ return cls->super_class;
+}
+
+Method *class_copyMethodList(Class cls, unsigned int *outCount) {
+ if (!cls) return NULL;
+
+ unsigned int count = 0;
+ void *iterator = NULL;
+ struct objc_method_list *mlist;
+ Method *methods = NULL;
+ if (outCount) *outCount = 0;
+
+ while ( (mlist = class_nextMethodList(cls, &iterator)) ) {
+ if (mlist->method_count == 0) continue;
+ methods = (Method *)realloc(methods,
+ sizeof(Method) * (count + mlist->method_count + 1));
+ if (!methods) {
+ //Memory alloc failed, so what can we do?
+ return NULL; // COV_NF_LINE
+ }
+ for (int i = 0; i < mlist->method_count; i++) {
+ methods[i + count] = &mlist->method_list[i];
+ }
+ count += mlist->method_count;
+ }
+
+ // List must be NULL terminated
+ if (methods) {
+ methods[count] = NULL;
+ }
+ if (outCount) *outCount = count;
+ return methods;
+}
+
+SEL method_getName(Method method) {
+ if (!method) return NULL;
+ return method->method_name;
+}
+
+IMP method_getImplementation(Method method) {
+ if (!method) return NULL;
+ return method->method_imp;
+}
+
+IMP method_setImplementation(Method method, IMP imp) {
+ // We intentionally don't test method for nil.
+ // Leopard fails here, so should we.
+ // I logged this as Radar:
+ // 5572981 method_setImplementation crashes if you pass nil for the
+ // method arg (arg 1)
+ // because it seems odd that this API won't accept nil for method considering
+ // all the other apis will accept nil args.
+ // If this does get fixed, remember to enable the unit tests.
+ IMP oldImp = method->method_imp;
+ method->method_imp = imp;
+ return oldImp;
+}
+
+void method_exchangeImplementations(Method m1, Method m2) {
+ if (m1 == m2) return;
+ if (!m1 || !m2) return;
+ IMP imp2 = method_getImplementation(m2);
+ IMP imp1 = method_setImplementation(m1, imp2);
+ method_setImplementation(m2, imp1);
+}
+
+struct objc_method_description protocol_getMethodDescription(Protocol *p,
+ SEL aSel,
+ BOOL isRequiredMethod,
+ BOOL isInstanceMethod) {
+ struct objc_method_description *descPtr = NULL;
+ // No such thing as required in ObjC1.
+ if (isInstanceMethod) {
+ descPtr = [p descriptionForInstanceMethod:aSel];
+ } else {
+ descPtr = [p descriptionForClassMethod:aSel];
+ }
+
+ struct objc_method_description desc;
+ if (descPtr) {
+ desc = *descPtr;
+ } else {
+ bzero(&desc, sizeof(desc));
+ }
+ return desc;
+}
+
+
+#endif
+
+
diff --git a/Foundation/GTMObjC2RuntimeTest.m b/Foundation/GTMObjC2RuntimeTest.m
new file mode 100644
index 0000000..2a7f354
--- /dev/null
+++ b/Foundation/GTMObjC2RuntimeTest.m
@@ -0,0 +1,385 @@
+//
+// GTMObjC2RuntimeTest.m
+//
+// Copyright 2007-2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import "GTMObjC2Runtime.h"
+#import "GTMSenTestCase.h"
+#import <string.h>
+
+@protocol GTMObjC2Runtime_TestProtocol
+@end
+
+@protocol GTMObjC2Runtime_Test2Protocol
+AT_OPTIONAL
+- (NSString*)optional;
+AT_REQUIRED
+- (NSString*)required;
+AT_OPTIONAL
++ (NSString*)class_optional;
+AT_REQUIRED
++ (NSString*)class_required;
+@end
+
+@interface GTMObjC2RuntimeTest : SenTestCase {
+ Class cls_;
+}
+@end
+
+@interface GTMObjC2Runtime_TestClass : NSObject <GTMObjC2Runtime_TestProtocol>
+- (NSString*)kwyjibo;
+
+@end
+
+@interface GTMObjC2Runtime_TestClass (GMObjC2Runtime_TestClassCategory)
+- (NSString*)eatMyShorts;
+@end
+
+@implementation GTMObjC2Runtime_TestClass
+
++ (NSString*)dontHaveACow {
+ return @"dontHaveACow";
+}
+
+- (NSString*)kwyjibo {
+ return @"kwyjibo";
+}
+@end
+
+@implementation GTMObjC2Runtime_TestClass (GMObjC2Runtime_TestClassCategory)
+- (NSString*)eatMyShorts {
+ return @"eatMyShorts";
+}
+
++ (NSString*)brokeHisBrain {
+ return @"brokeHisBrain";
+}
+
+@end
+
+@interface GTMObjC2NotificationWatcher : NSObject
+@end
+
+@implementation GTMObjC2NotificationWatcher
+- (void)startedTest:(NSNotification *)notification {
+ // Logs if we are testing on Tiger or Leopard runtime.
+ NSString *testName = [(SenTest*)[[notification object] test] name];
+ NSString *className = NSStringFromClass([GTMObjC2RuntimeTest class]);
+ if ([testName isEqualToString:className]) {
+ NSString *runtimeString;
+#ifndef OBJC2_UNAVAILABLE
+ runtimeString = @"ObjC1";
+#else
+ runtimeString = @"ObjC2";
+#endif
+ NSLog(@"Running GTMObjC2RuntimeTests using %@ runtime.", runtimeString);
+ NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
+ [nc removeObserver:self];
+ [self autorelease];
+ }
+}
+@end
+
+@implementation GTMObjC2RuntimeTest
+
++ (void)initialize {
+ // This allows us to track which runtime we are actually testing.
+ NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
+
+ // Watcher is released when it is notified.
+ GTMObjC2NotificationWatcher *watcher = [[GTMObjC2NotificationWatcher alloc] init];
+ [nc addObserver:watcher
+ selector:@selector(startedTest:)
+ name:SenTestSuiteDidStartNotification
+ object:nil];
+}
+
+- (void)setUp {
+ cls_ = [[GTMObjC2Runtime_TestClass class] retain];
+}
+
+- (void)tearDown {
+ [cls_ release];
+}
+
+- (void)test_object_getClass {
+ // Nil Checks
+ STAssertNil(object_getClass(nil), nil);
+
+ // Standard use check
+ GTMObjC2Runtime_TestClass *test = [[[cls_ alloc] init] autorelease];
+ Class cls = object_getClass(test);
+ STAssertEqualObjects(cls, cls_, nil);
+}
+
+- (void)test_class_getName {
+ // Nil Checks
+ const char *name = class_getName(nil);
+ STAssertEqualCStrings(name, "nil", nil);
+
+ // Standard use check
+ STAssertEqualCStrings(class_getName(cls_), "GTMObjC2Runtime_TestClass", nil);
+}
+
+- (void)test_class_conformsToProtocol {
+ // Nil Checks
+ STAssertFalse(class_conformsToProtocol(cls_, @protocol(NSObject)), nil);
+ STAssertFalse(class_conformsToProtocol(cls_, nil), nil);
+ // The following two tests intentionally commented out as they fail on
+ // Leopard with a crash, so we fail on Tiger intentionally as well.
+ // STAssertFalse(class_conformsToProtocol(nil, @protocol(NSObject)), nil);
+ // STAssertFalse(class_conformsToProtocol(nil, nil), nil);
+
+ // Standard use check
+ STAssertTrue(class_conformsToProtocol(cls_,
+ @protocol(GTMObjC2Runtime_TestProtocol)),
+ nil);
+}
+
+- (void)test_class_getSuperclass {
+ // Nil Checks
+ STAssertNil(class_getSuperclass(nil), nil);
+
+ // Standard use check
+ STAssertEqualObjects(class_getSuperclass(cls_), [NSObject class], nil);
+}
+
+- (void)test_class_copyMethodList {
+ // Nil Checks
+ Method *list = class_copyMethodList(nil, nil);
+ STAssertNULL(list, nil);
+
+ // Standard use check
+ list = class_copyMethodList(cls_, nil);
+ STAssertNotNULL(list, nil);
+ free(list);
+ unsigned int count = 0;
+ list = class_copyMethodList(cls_, &count);
+ STAssertNotNULL(list, nil);
+ STAssertEquals(count, 2U, nil);
+ STAssertNULL(list[count], nil);
+ free(list);
+
+ // Now test meta class
+ count = 0;
+ list = class_copyMethodList((Class)objc_getMetaClass(class_getName(cls_)),
+ &count);
+ STAssertNotNULL(list, nil);
+ STAssertEquals(count, 2U, nil);
+ STAssertNULL(list[count], nil);
+ free(list);
+}
+
+- (void)test_method_getName {
+ // Nil Checks
+ STAssertNULL(method_getName(nil), nil);
+
+ // Standard use check
+ Method *list = class_copyMethodList(cls_, nil);
+ STAssertNotNULL(list, nil);
+ const char* selName1 = sel_getName(method_getName(list[0]));
+ const char* selName2 = sel_getName(@selector(kwyjibo));
+ const char* selName3 = sel_getName(@selector(eatMyShorts));
+ BOOL isGood = ((strcmp(selName1, selName2)) == 0 || (strcmp(selName1, selName3) == 0));
+ STAssertTrue(isGood, nil);
+ free(list);
+}
+
+- (void)test_method_exchangeImplementations {
+ // nil checks
+ method_exchangeImplementations(nil, nil);
+
+ // Standard use check
+ GTMObjC2Runtime_TestClass *test = [[GTMObjC2Runtime_TestClass alloc] init];
+ STAssertNotNil(test, nil);
+
+ // Get initial values
+ NSString *val1 = [test kwyjibo];
+ STAssertNotNil(val1, nil);
+ NSString *val2 = [test eatMyShorts];
+ STAssertNotNil(val2, nil);
+ NSString *val3 = [GTMObjC2Runtime_TestClass dontHaveACow];
+ STAssertNotNil(val3, nil);
+ NSString *val4 = [GTMObjC2Runtime_TestClass brokeHisBrain];
+ STAssertNotNil(val4, nil);
+
+ // exchange the imps
+ Method *list = class_copyMethodList(cls_, nil);
+ STAssertNotNULL(list, nil);
+ method_exchangeImplementations(list[0], list[1]);
+
+ // test against initial values
+ NSString *val5 = [test kwyjibo];
+ STAssertNotNil(val5, nil);
+ NSString *val6 = [test eatMyShorts];
+ STAssertNotNil(val6, nil);
+ STAssertEqualStrings(val1, val6, nil);
+ STAssertEqualStrings(val2, val5, nil);
+
+ // Check that other methods not affected
+ STAssertEqualStrings([GTMObjC2Runtime_TestClass dontHaveACow], val3, nil);
+ STAssertEqualStrings([GTMObjC2Runtime_TestClass brokeHisBrain], val4, nil);
+
+ // exchange the imps back
+ method_exchangeImplementations(list[0], list[1]);
+
+ // and test against initial values again
+ NSString *val7 = [test kwyjibo];
+ STAssertNotNil(val7, nil);
+ NSString *val8 = [test eatMyShorts];
+ STAssertNotNil(val8, nil);
+ STAssertEqualStrings(val1, val7, nil);
+ STAssertEqualStrings(val2, val8, nil);
+
+ method_exchangeImplementations(list[0], nil);
+ method_exchangeImplementations(nil, list[0]);
+
+ val7 = [test kwyjibo];
+ STAssertNotNil(val7, nil);
+ val8 = [test eatMyShorts];
+ STAssertNotNil(val8, nil);
+ STAssertEqualStrings(val1, val7, nil);
+ STAssertEqualStrings(val2, val8, nil);
+
+ free(list);
+ [test release];
+}
+
+- (void)test_method_getImplementation {
+ // Nil Checks
+ STAssertNULL(method_getImplementation(nil), nil);
+
+ // Standard use check
+ Method *list = class_copyMethodList(cls_, nil);
+ STAssertNotNULL(list, nil);
+ STAssertNotNULL(method_getImplementation(list[0]), nil);
+ free(list);
+}
+
+- (void)test_method_setImplementation {
+ // Nil Checks
+ // This case intentionally not tested. Passing nil to method_setImplementation
+ // on Leopard crashes. It does on Tiger as well.
+ // STAssertNULL(method_setImplementation(nil, nil), nil);
+
+ // Standard use check
+ GTMObjC2Runtime_TestClass *test = [[GTMObjC2Runtime_TestClass alloc] init];
+ Method *list = class_copyMethodList(cls_, nil);
+
+ // Get initial value
+ NSString *str1 = objc_msgSend(test, method_getName(list[0]));
+ STAssertNotNil(str1, nil);
+
+ // set the imp to something else
+ IMP oldImp = method_setImplementation(list[0], method_getImplementation(list[1]));
+ STAssertNotNULL(oldImp, nil);
+
+ // make sure they are different
+ NSString *str2 = objc_msgSend(test,method_getName(list[0]));
+ STAssertNotNil(str2, nil);
+ STAssertNotEqualStrings(str1, str2, nil);
+
+ // reset the imp
+ IMP newImp = method_setImplementation(list[0], oldImp);
+ STAssertNotEquals(oldImp, newImp, nil);
+
+ // test nils
+ oldImp = method_setImplementation(list[0], nil);
+ STAssertNotNULL(oldImp, nil);
+
+ newImp = method_setImplementation(list[0], oldImp);
+ STAssertNULL(newImp, nil);
+
+ [test release];
+ free(list);
+}
+
+- (void)test_protocol_getMethodDescription {
+ // Check nil cases
+ struct objc_method_description desc = protocol_getMethodDescription(nil, nil,
+ YES, YES);
+ STAssertNULL(desc.name, nil);
+ desc = protocol_getMethodDescription(nil, @selector(optional), YES, YES);
+ STAssertNULL(desc.name, nil);
+ desc = protocol_getMethodDescription(@protocol(GTMObjC2Runtime_Test2Protocol),
+ nil, YES, YES);
+ STAssertNULL(desc.name, nil);
+
+ // Instance Methods
+ // Check Required case. Only OBJC2 supports required.
+ desc = protocol_getMethodDescription(@protocol(GTMObjC2Runtime_Test2Protocol),
+ @selector(optional), YES, YES);
+#if OBJC_API_VERSION >= 2
+ STAssertNULL(desc.name, nil);
+#else
+ STAssertNotNULL(desc.name, nil);
+#endif
+
+ // Check Required case. Only OBJC2 supports required.
+ desc = protocol_getMethodDescription(@protocol(GTMObjC2Runtime_Test2Protocol),
+ @selector(required), YES, YES);
+
+ STAssertNotNULL(desc.name, nil);
+
+ // Check Optional case. Only OBJC2 supports optional.
+ desc = protocol_getMethodDescription(@protocol(GTMObjC2Runtime_Test2Protocol),
+ @selector(optional), NO, YES);
+
+ STAssertNotNULL(desc.name, nil);
+
+ // Check Optional case. Only OBJC2 supports optional.
+ desc = protocol_getMethodDescription(@protocol(GTMObjC2Runtime_Test2Protocol),
+ @selector(required), NO, YES);
+#if OBJC_API_VERSION >= 2
+ STAssertNULL(desc.name, nil);
+#else
+ STAssertNotNULL(desc.name, nil);
+#endif
+
+ // Class Methods
+ // Check Required case. Only OBJC2 supports required.
+ desc = protocol_getMethodDescription(@protocol(GTMObjC2Runtime_Test2Protocol),
+ @selector(class_optional), YES, NO);
+#if OBJC_API_VERSION >= 2
+ STAssertNULL(desc.name, nil);
+#else
+ STAssertNotNULL(desc.name, nil);
+#endif
+
+ // Check Required case. Only OBJC2 supports required.
+ desc = protocol_getMethodDescription(@protocol(GTMObjC2Runtime_Test2Protocol),
+ @selector(class_required), YES, NO);
+
+ STAssertNotNULL(desc.name, nil);
+
+ // Check Optional case. Only OBJC2 supports optional.
+ desc = protocol_getMethodDescription(@protocol(GTMObjC2Runtime_Test2Protocol),
+ @selector(class_optional), NO, NO);
+
+ STAssertNotNULL(desc.name, nil);
+
+ // Check Optional case. Only OBJC2 supports optional.
+ desc = protocol_getMethodDescription(@protocol(GTMObjC2Runtime_Test2Protocol),
+ @selector(class_required), NO, NO);
+#if OBJC_API_VERSION >= 2
+ STAssertNULL(desc.name, nil);
+#else
+ STAssertNotNULL(desc.name, nil);
+#endif
+
+}
+
+@end
diff --git a/Foundation/GTMObjectSingleton.h b/Foundation/GTMObjectSingleton.h
index 9bdeb0a..116d232 100644
--- a/Foundation/GTMObjectSingleton.h
+++ b/Foundation/GTMObjectSingleton.h
@@ -17,6 +17,8 @@
// the License.
//
+#import "GTMDefines.h"
+
/// This macro implements the various methods needed to make a safe singleton.
//
/// This Singleton pattern was taken from:
@@ -35,7 +37,7 @@ static _object_name_ *z##_shared_obj_name_ = nil; \
/* Note that 'self' may not be the same as _object_name_ */ \
/* first assignment done in allocWithZone but we must reassign in case init fails */ \
z##_shared_obj_name_ = [[self alloc] init]; \
- NSAssert((z##_shared_obj_name_ != nil), @"didn't catch singleton allocation"); \
+ _GTMDevAssert((z##_shared_obj_name_ != nil), @"didn't catch singleton allocation"); \
} \
} \
return z##_shared_obj_name_; \
@@ -49,7 +51,7 @@ static _object_name_ *z##_shared_obj_name_ = nil; \
} \
\
/* We can't return the shared instance, because it's been init'd */ \
- NSAssert(NO, @"use the singleton API, not alloc+init"); \
+ _GTMDevAssert(NO, @"use the singleton API, not alloc+init"); \
return nil; \
} \
- (id)retain { \
diff --git a/Foundation/GTMRegex.h b/Foundation/GTMRegex.h
index 8e0f492..ee56b98 100644
--- a/Foundation/GTMRegex.h
+++ b/Foundation/GTMRegex.h
@@ -50,6 +50,28 @@ typedef enum {
} GTMRegexOptions;
+/// Global contants needed for errors from consuming patterns
+
+#undef _EXTERN
+#undef _INITIALIZE_AS
+#ifdef GTMREGEX_DEFINE_GLOBALS
+#define _EXTERN
+#define _INITIALIZE_AS(x) =x
+#else
+#define _EXTERN extern
+#define _INITIALIZE_AS(x)
+#endif
+
+_EXTERN NSString* kGTMRegexErrorDomain _INITIALIZE_AS(@"com.google_toolbox_for_mac.GTMRegexDomain");
+
+enum {
+ kGTMRegexPatternParseFailedError = -100
+};
+
+// Keys for the userInfo from a kGTMRegexErrorDomain/kGTMRegexPatternParseFailedError error
+_EXTERN NSString* kGTMRegexPatternErrorPattern _INITIALIZE_AS(@"pattern");
+_EXTERN NSString* kGTMRegexPatternErrorErrorString _INITIALIZE_AS(@"patternError");
+
/// Class for doing Extended Regex operations w/ libregex (see re_format(7)).
//
// NOTE: the docs for recomp/regexec make *no* claims about i18n. All work
@@ -102,6 +124,11 @@ typedef enum {
/// Create a new, autoreleased object w/ the given regex pattern and specify the matching options
+ (id)regexWithPattern:(NSString *)pattern options:(GTMRegexOptions)options;
+/// Create a new, autoreleased object w/ the given regex pattern, specify the matching options and receive any error consuming the pattern.
++ (id)regexWithPattern:(NSString *)pattern
+ options:(GTMRegexOptions)options
+ withError:(NSError **)outErrorOrNULL;
+
/// Returns a new, autoreleased copy of |str| w/ any pattern chars in it escaped so they have no meaning when used w/in a pattern.
+ (NSString *)escapedPatternForString:(NSString *)str;
@@ -111,6 +138,11 @@ typedef enum {
/// Initialize a new object w/ the given regex pattern and specify the matching options
- (id)initWithPattern:(NSString *)pattern options:(GTMRegexOptions)options;
+/// Initialize a new object w/ the given regex pattern, specify the matching options, and receive any error consuming the pattern.
+- (id)initWithPattern:(NSString *)pattern
+ options:(GTMRegexOptions)options
+ withError:(NSError **)outErrorOrNULL;
+
/// Returns the number of sub patterns in the pattern
//
// Sub Patterns are basically the number of parenthesis blocks w/in the pattern.
@@ -140,6 +172,12 @@ typedef enum {
//
- (NSArray *)subPatternsOfString:(NSString *)str;
+/// Returns the first match for this pattern in |str|.
+- (NSString *)firstSubStringMatchedInString:(NSString *)str;
+
+/// Returns YES if this pattern some substring of |str|.
+- (BOOL)matchesSubStringInString:(NSString *)str;
+
/// Returns a new, autoreleased enumerator that will walk segments (GTMRegexStringSegment) of |str| based on the pattern.
//
// This will split the string into "segments" using the given pattern. You get
@@ -307,6 +345,9 @@ typedef enum {
/// Returns a new, autoreleased string w/ the first substring that matched the regex |pattern| using the default match options
- (NSString *)gtm_firstSubStringMatchedByPattern:(NSString *)pattern;
+/// Returns YES if a substring string matches regex |pattern| using the default match options
+- (BOOL)gtm_subStringMatchesPattern:(NSString *)pattern;
+
/// Returns a new, autoreleased array of substrings in the string that match the regex |pattern| using the default match options
//
// Note: if the string has no matches, you get an empty array.
diff --git a/Foundation/GTMRegex.m b/Foundation/GTMRegex.m
index d7900fa..c50ff2b 100644
--- a/Foundation/GTMRegex.m
+++ b/Foundation/GTMRegex.m
@@ -16,7 +16,9 @@
// the License.
//
+#define GTMREGEX_DEFINE_GLOBALS 1
#import "GTMRegex.h"
+#import "GTMDefines.h"
// This is the pattern to use for walking replacement text when doing
// substitutions.
@@ -71,6 +73,14 @@ static NSString *const kReplacementPattern =
options:options] autorelease];
}
++ (id)regexWithPattern:(NSString *)pattern
+ options:(GTMRegexOptions)options
+ withError:(NSError **)outErrorOrNULL {
+ return [[[self alloc] initWithPattern:pattern
+ options:options
+ withError:outErrorOrNULL] autorelease];
+}
+
+ (NSString *)escapedPatternForString:(NSString *)str {
if (str == nil)
return nil;
@@ -117,9 +127,18 @@ static NSString *const kReplacementPattern =
}
- (id)initWithPattern:(NSString *)pattern options:(GTMRegexOptions)options {
+ return [self initWithPattern:pattern options:options withError:nil];
+}
+
+- (id)initWithPattern:(NSString *)pattern
+ options:(GTMRegexOptions)options
+ withError:(NSError **)outErrorOrNULL {
+
self = [super init];
if (!self) return nil;
+ if (outErrorOrNULL) *outErrorOrNULL = nil;
+
if ([pattern length] == 0) {
[self release];
return nil;
@@ -138,17 +157,31 @@ static NSString *const kReplacementPattern =
// error info). we use pattern_ as this flag.
pattern_ = [pattern copy];
if (!pattern_) {
+ // COV_NF_START - no real way to force this in a unittest
[self release];
return nil;
+ // COV_NF_END
}
// compile it
int compResult = regcomp(&regexData_, [pattern_ UTF8String], flags);
if (compResult != 0) {
- // we don't want to throw if we failed, so we'll return nil, but still
- // log the error just so it's out there.
NSString *errorStr = [self errorMessage:compResult];
- NSLog(@"Invalid pattern \"%@\", error: \"%@\"", pattern_, errorStr);
+ if (outErrorOrNULL) {
+ // include the pattern and patternError message in the userInfo.
+ NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
+ pattern_, kGTMRegexPatternErrorPattern,
+ errorStr, kGTMRegexPatternErrorErrorString,
+ nil];
+ *outErrorOrNULL = [NSError errorWithDomain:kGTMRegexErrorDomain
+ code:kGTMRegexPatternParseFailedError
+ userInfo:userInfo];
+ } else {
+ // if caller didn't get us an NSError to fill in, we log the error to help
+ // debugging.
+ _GTMDevLog(@"Invalid pattern \"%@\", error: \"%@\"",
+ pattern_, errorStr);
+ }
[self release];
return nil;
@@ -193,7 +226,7 @@ static NSString *const kReplacementPattern =
int count = regexData_.re_nsub + 1;
regmatch_t *regMatches = malloc(sizeof(regmatch_t) * count);
if (!regMatches)
- return nil;
+ return nil; // COV_NF_LINE - no real way to force this in a unittest
// wrap it all in a try so we don't leak the malloc
@try {
@@ -232,7 +265,7 @@ static NSString *const kReplacementPattern =
}
result = buildResult;
- }
+ } // COV_NF_LINE - radar 5851992 not all brackets reachable w/ obj-c exceptions and coverage
@finally {
free(regMatches);
}
@@ -240,6 +273,38 @@ static NSString *const kReplacementPattern =
return result;
}
+- (NSString *)firstSubStringMatchedInString:(NSString *)str {
+ NSString *result = nil;
+
+ regmatch_t regMatch;
+ const char *utf8Str = [str UTF8String];
+ if ([self runRegexOnUTF8:utf8Str
+ nmatch:1
+ pmatch:&regMatch
+ flags:0]) {
+ // fetch the string
+ const char *base = utf8Str + regMatch.rm_so;
+ unsigned len = regMatch.rm_eo - regMatch.rm_so;
+ result =
+ [[[NSString alloc] initWithBytes:base
+ length:len
+ encoding:NSUTF8StringEncoding] autorelease];
+ }
+ return result;
+}
+
+- (BOOL)matchesSubStringInString:(NSString *)str {
+ regmatch_t regMatch;
+ if ([self runRegexOnUTF8:[str UTF8String]
+ nmatch:1
+ pmatch:&regMatch
+ flags:0]) {
+ // don't really care what matched, just report the match
+ return YES;
+ }
+ return NO;
+}
+
- (NSEnumerator *)segmentEnumeratorForString:(NSString *)str {
return [[[GTMRegexEnumerator alloc] initWithRegex:self
processString:str
@@ -266,8 +331,9 @@ static NSString *const kReplacementPattern =
[GTMRegex regexWithPattern:kReplacementPattern
options:kGTMRegexOptionSupressNewlineSupport];
#ifdef DEBUG
- if (!replacementRegex)
- NSLog(@"failed to parse out replacement regex!!!");
+ if (!replacementRegex) {
+ _GTMDevLog(@"failed to parse out replacement regex!!!"); // COV_NF_LINE
+ }
#endif
GTMRegexEnumerator *relacementEnumerator =
[[[GTMRegexEnumerator alloc] initWithRegex:replacementRegex
@@ -292,8 +358,10 @@ static NSString *const kReplacementPattern =
// pull them all into an array so we can walk this as many times as needed.
replacements = [relacementEnumerator allObjects];
if (!replacements) {
- NSLog(@"failed to create the replacements for subtituations");
+ // COV_NF_START - no real way to force this in a unittest
+ _GTMDevLog(@"failed to create the replacements for substitutions");
return nil;
+ // COV_NF_END
}
}
@@ -388,9 +456,11 @@ static NSString *const kReplacementPattern =
if (execResult != 0) {
#ifdef DEBUG
if (execResult != REG_NOMATCH) {
+ // COV_NF_START - no real way to force this in a unittest
NSString *errorStr = [self errorMessage:execResult];
- NSLog(@"%@: matching string \"%.20s...\", had error: \"%@\"",
- self, utf8Str, errorStr);
+ _GTMDevLog(@"%@: matching string \"%.20s...\", had error: \"%@\"",
+ self, utf8Str, errorStr);
+ // COV_NF_END
}
#endif
return NO;
@@ -402,9 +472,8 @@ static NSString *const kReplacementPattern =
@implementation GTMRegexEnumerator
-- (id)init {
- return [self initWithRegex:nil processString:nil allSegments:NO];
-}
+// we don't block init because the class isn't exported, so no one can
+// create one, or if they do, they get whatever happens...
- (id)initWithRegex:(GTMRegex *)regex
processString:(NSString *)str
@@ -480,7 +549,7 @@ static NSString *const kReplacementPattern =
size_t matchBufSize = ([regex_ subPatternCount] + 1) * sizeof(regmatch_t);
nextMatches = malloc(matchBufSize);
if (!nextMatches)
- return nil;
+ return nil; // COV_NF_LINE - no real way to force this in a unittest
// setup our range to work on
nextMatches[0].rm_so = curParseIndex_;
@@ -511,7 +580,7 @@ static NSString *const kReplacementPattern =
savedRegMatches_ = nextMatches;
nextMatches = malloc(matchBufSize);
if (!nextMatches)
- return nil;
+ return nil; // COV_NF_LINE - no real way to force this in a unittest
isMatch = NO;
// mark everything but the zero slot w/ not used
@@ -568,14 +637,13 @@ static NSString *const kReplacementPattern =
isMatch:isMatch] autorelease];
nextMatches = nil;
}
- }
+ } // COV_NF_START - no real way to force this in a test
@catch (id e) {
- NSLog(@"Exceptions while trying to advance enumeration (%@)", e);
- }
-
- // if we still have something in our temp, free it
- if (nextMatches)
- free(nextMatches);
+ _GTMDevLog(@"Exceptions while trying to advance enumeration (%@)", e);
+ // if we still have something in our temp, free it
+ if (nextMatches)
+ free(nextMatches);
+ } // COV_NF_END
return result;
}
@@ -593,10 +661,10 @@ static NSString *const kReplacementPattern =
@implementation GTMRegexStringSegment
- (id)init {
- return [self initWithUTF8StrBuf:nil
- regMatches:nil
- numRegMatches:0
- isMatch:NO];
+ // make sure init is never called, the class in in the header so someone
+ // could try to create it by mistake.
+ [self doesNotRecognizeSelector:_cmd];
+ return nil; // COV_NF_LINE - return is just here to keep gcc happy
}
- (void)dealloc {
@@ -669,8 +737,11 @@ static NSString *const kReplacementPattern =
// check the args
if (!utf8StrBuf_ || !regMatches_ || (numRegMatches_ < 0)) {
+ // COV_NF_START
+ // this could only happen something messed w/ our internal state.
[self release];
return nil;
+ // COV_NF_END
}
return self;
@@ -692,9 +763,12 @@ static NSString *const kReplacementPattern =
- (NSString *)gtm_firstSubStringMatchedByPattern:(NSString *)pattern {
GTMRegex *regex = [GTMRegex regexWithPattern:pattern];
- NSEnumerator *enumerator = [regex matchSegmentEnumeratorForString:self];
- GTMRegexStringSegment *firstMatch = [enumerator nextObject];
- return [firstMatch string];
+ return [regex firstSubStringMatchedInString:self];
+}
+
+- (BOOL)gtm_subStringMatchesPattern:(NSString *)pattern {
+ GTMRegex *regex = [GTMRegex regexWithPattern:pattern];
+ return [regex matchesSubStringInString:self];
}
- (NSArray *)gtm_allSubstringsMatchedByPattern:(NSString *)pattern {
diff --git a/Foundation/GTMRegexTest.m b/Foundation/GTMRegexTest.m
index 71c8405..22c571e 100644
--- a/Foundation/GTMRegexTest.m
+++ b/Foundation/GTMRegexTest.m
@@ -16,8 +16,6 @@
// the License.
//
-#import <SenTestingKit/SenTestingKit.h>
-
#import "GTMSenTestCase.h"
#import "GTMRegex.h"
@@ -62,6 +60,22 @@
STAssertNil([[[GTMRegex alloc] initWithPattern:@"(."] autorelease], nil);
STAssertNil([[[GTMRegex alloc] initWithPattern:@"(."
options:kGTMRegexOptionIgnoreCase] autorelease], nil);
+ // fail cases w/ error param
+ NSError *error = nil;
+ STAssertNil([[[GTMRegex alloc] initWithPattern:nil
+ options:kGTMRegexOptionIgnoreCase
+ withError:&error] autorelease], nil);
+ STAssertNil(error, @"no pattern, shouldn't get error object");
+ STAssertNil([[[GTMRegex alloc] initWithPattern:@"(."
+ options:kGTMRegexOptionIgnoreCase
+ withError:&error] autorelease], nil);
+ STAssertNotNil(error, nil);
+ STAssertEqualObjects([error domain], kGTMRegexErrorDomain, nil);
+ STAssertEquals([error code], kGTMRegexPatternParseFailedError, nil);
+ NSDictionary *userInfo = [error userInfo];
+ STAssertNotNil(userInfo, @"failed to get userInfo from error");
+ STAssertEqualObjects([userInfo objectForKey:kGTMRegexPatternErrorPattern], @"(.", nil);
+ STAssertNotNil([userInfo objectForKey:kGTMRegexPatternErrorErrorString], nil);
// basic pattern w/ options
STAssertNotNil([[[GTMRegex alloc] initWithPattern:@"(.*)"] autorelease], nil);
@@ -69,6 +83,11 @@
options:0] autorelease], nil);
STAssertNotNil([[[GTMRegex alloc] initWithPattern:@"(.*)"
options:kGTMRegexOptionIgnoreCase] autorelease], nil);
+ error = nil;
+ STAssertNotNil([[[GTMRegex alloc] initWithPattern:@"(.*)"
+ options:kGTMRegexOptionIgnoreCase
+ withError:&error] autorelease], nil);
+ STAssertNil(error, @"shouldn't have been any error");
// fail cases (helper)
STAssertNil([GTMRegex regexWithPattern:nil], nil);
@@ -77,13 +96,39 @@
STAssertNil([GTMRegex regexWithPattern:@"(."], nil);
STAssertNil([GTMRegex regexWithPattern:@"(."
options:0], nil);
-
+ // fail cases (helper) w/ error param
+ STAssertNil([GTMRegex regexWithPattern:nil
+ options:kGTMRegexOptionIgnoreCase
+ withError:&error], nil);
+ STAssertNil(error, @"no pattern, shouldn't get error object");
+ STAssertNil([GTMRegex regexWithPattern:@"(."
+ options:kGTMRegexOptionIgnoreCase
+ withError:&error], nil);
+ STAssertNotNil(error, nil);
+ STAssertEqualObjects([error domain], kGTMRegexErrorDomain, nil);
+ STAssertEquals([error code], kGTMRegexPatternParseFailedError, nil);
+ userInfo = [error userInfo];
+ STAssertNotNil(userInfo, @"failed to get userInfo from error");
+ STAssertEqualObjects([userInfo objectForKey:kGTMRegexPatternErrorPattern], @"(.", nil);
+ STAssertNotNil([userInfo objectForKey:kGTMRegexPatternErrorErrorString], nil);
+
// basic pattern w/ options (helper)
STAssertNotNil([GTMRegex regexWithPattern:@"(.*)"], nil);
STAssertNotNil([GTMRegex regexWithPattern:@"(.*)"
options:0], nil);
STAssertNotNil([GTMRegex regexWithPattern:@"(.*)"
options:kGTMRegexOptionIgnoreCase], nil);
+ error = nil;
+ STAssertNotNil([GTMRegex regexWithPattern:@"(.*)"
+ options:kGTMRegexOptionIgnoreCase
+ withError:&error], nil);
+ STAssertNil(error, @"shouldn't have been any error");
+
+ // not really a test on GTMRegex, but make sure we block attempts to directly
+ // alloc/init a GTMRegexStringSegment.
+ STAssertThrowsSpecificNamed([[[GTMRegexStringSegment alloc] init] autorelease],
+ NSException, NSInvalidArgumentException,
+ @"shouldn't have been able to alloc/init a GTMRegexStringSegment");
}
- (void)testOptions {
@@ -397,10 +442,68 @@
STAssertNil(subPatterns, nil);
}
+- (void)testFirstSubStringMatchedInString {
+ // simple pattern
+ GTMRegex *regex = [GTMRegex regexWithPattern:@"foo.*bar"];
+ STAssertNotNil(regex, nil);
+ STAssertEqualStrings([regex firstSubStringMatchedInString:@"foobar"],
+ @"foobar", nil);
+ STAssertEqualStrings([regex firstSubStringMatchedInString:@"foobydoo spambar"],
+ @"foobydoo spambar", nil);
+ STAssertEqualStrings([regex firstSubStringMatchedInString:@"zzfoobarzz"],
+ @"foobar", nil);
+ STAssertEqualStrings([regex firstSubStringMatchedInString:@"zzfoobydoo spambarzz"],
+ @"foobydoo spambar", nil);
+ STAssertNil([regex firstSubStringMatchedInString:@"abcdef"], nil);
+ STAssertNil([regex firstSubStringMatchedInString:@""], nil);
+ // pattern w/ sub patterns
+ regex = [GTMRegex regexWithPattern:@"(foo)(.*)(bar)"];
+ STAssertNotNil(regex, nil);
+ STAssertEqualStrings([regex firstSubStringMatchedInString:@"foobar"],
+ @"foobar", nil);
+ STAssertEqualStrings([regex firstSubStringMatchedInString:@"foobydoo spambar"],
+ @"foobydoo spambar", nil);
+ STAssertEqualStrings([regex firstSubStringMatchedInString:@"zzfoobarzz"],
+ @"foobar", nil);
+ STAssertEqualStrings([regex firstSubStringMatchedInString:@"zzfoobydoo spambarzz"],
+ @"foobydoo spambar", nil);
+ STAssertNil([regex firstSubStringMatchedInString:@"abcdef"], nil);
+ STAssertNil([regex firstSubStringMatchedInString:@""], nil);
+}
+
+- (void)testMatchesSubStringInString {
+ // simple pattern
+ GTMRegex *regex = [GTMRegex regexWithPattern:@"foo.*bar"];
+ STAssertNotNil(regex, nil);
+ STAssertTrue([regex matchesSubStringInString:@"foobar"], nil);
+ STAssertTrue([regex matchesSubStringInString:@"foobydoo spambar"], nil);
+ STAssertTrue([regex matchesSubStringInString:@"zzfoobarzz"], nil);
+ STAssertTrue([regex matchesSubStringInString:@"zzfoobydoo spambarzz"], nil);
+ STAssertFalse([regex matchesSubStringInString:@"abcdef"], nil);
+ STAssertFalse([regex matchesSubStringInString:@""], nil);
+ // pattern w/ sub patterns
+ regex = [GTMRegex regexWithPattern:@"(foo)(.*)(bar)"];
+ STAssertNotNil(regex, nil);
+ STAssertTrue([regex matchesSubStringInString:@"foobar"], nil);
+ STAssertTrue([regex matchesSubStringInString:@"foobydoo spambar"], nil);
+ STAssertTrue([regex matchesSubStringInString:@"zzfoobarzz"], nil);
+ STAssertTrue([regex matchesSubStringInString:@"zzfoobydoo spambarzz"], nil);
+ STAssertFalse([regex matchesSubStringInString:@"abcdef"], nil);
+ STAssertFalse([regex matchesSubStringInString:@""], nil);
+}
+
- (void)testSegmentEnumeratorForString {
GTMRegex *regex = [GTMRegex regexWithPattern:@"foo+ba+r"];
STAssertNotNil(regex, nil);
- NSEnumerator *enumerator = [regex segmentEnumeratorForString:@"afoobarbfooobaarfoobarzz"];
+
+ // test odd input
+ NSEnumerator *enumerator = [regex segmentEnumeratorForString:@""];
+ STAssertNotNil(enumerator, nil);
+ enumerator = [regex segmentEnumeratorForString:nil];
+ STAssertNil(enumerator, nil);
+
+ // on w/ the normal tests
+ enumerator = [regex segmentEnumeratorForString:@"afoobarbfooobaarfoobarzz"];
STAssertNotNil(enumerator, nil);
// "a"
GTMRegexStringSegment *seg = [enumerator nextObject];
@@ -566,12 +669,32 @@
// (end)
seg = [enumerator nextObject];
STAssertNil(seg, nil);
+
+ // make sure the enum cleans up if not walked to the end
+ regex = [GTMRegex regexWithPattern:@"b+"];
+ STAssertNotNil(regex, nil);
+ enumerator = [regex segmentEnumeratorForString:@"aabbcc"];
+ STAssertNotNil(enumerator, nil);
+ // "aa"
+ seg = [enumerator nextObject];
+ STAssertNotNil(seg, nil);
+ STAssertFalse([seg isMatch], nil);
+ STAssertEqualStrings([seg string], @"aa", nil);
+ // and done w/o walking the rest
}
- (void)testMatchSegmentEnumeratorForString {
GTMRegex *regex = [GTMRegex regexWithPattern:@"foo+ba+r"];
STAssertNotNil(regex, nil);
- NSEnumerator *enumerator = [regex matchSegmentEnumeratorForString:@"afoobarbfooobaarfoobarzz"];
+
+ // test odd input
+ NSEnumerator *enumerator = [regex matchSegmentEnumeratorForString:@""];
+ STAssertNotNil(enumerator, nil);
+ enumerator = [regex matchSegmentEnumeratorForString:nil];
+ STAssertNil(enumerator, nil);
+
+ // on w/ the normal tests
+ enumerator = [regex matchSegmentEnumeratorForString:@"afoobarbfooobaarfoobarzz"];
STAssertNotNil(enumerator, nil);
// "a" - skipped
// "foobar"
@@ -718,6 +841,13 @@
[regex stringByReplacingMatchesInString:@"weefoobydoo spambardoggies"
withReplacement:@""],
nil);
+ STAssertEqualStrings(@"",
+ [regex stringByReplacingMatchesInString:@""
+ withReplacement:@"abc"],
+ nil);
+ STAssertNil([regex stringByReplacingMatchesInString:nil
+ withReplacement:@"abc"],
+ nil);
// use optional and invale subexpression parts to confirm that works
regex = [GTMRegex regexWithPattern:@"(fo(o+))((bar)|(baz))"];
STAssertNotNil(regex, nil);
@@ -746,6 +876,30 @@
nil);
}
+- (void)testDescriptions {
+ // default options
+ GTMRegex *regex = [GTMRegex regexWithPattern:@"a+"];
+ STAssertNotNil(regex, nil);
+ STAssertGreaterThan([[regex description] length], 10U,
+ @"failed to get a reasonable description for regex");
+ // enumerator
+ NSEnumerator *enumerator = [regex segmentEnumeratorForString:@"aaabbbccc"];
+ STAssertNotNil(enumerator, nil);
+ STAssertGreaterThan([[enumerator description] length], 10U,
+ @"failed to get a reasonable description for regex enumerator");
+ // string segment
+ GTMRegexStringSegment *seg = [enumerator nextObject];
+ STAssertNotNil(seg, nil);
+ STAssertGreaterThan([[seg description] length], 10U,
+ @"failed to get a reasonable description for regex string segment");
+ // regex w/ other options
+ regex = [GTMRegex regexWithPattern:@"a+"
+ options:(kGTMRegexOptionIgnoreCase | kGTMRegexOptionSupressNewlineSupport)];
+ STAssertNotNil(regex, nil);
+ STAssertGreaterThan([[regex description] length], 10U,
+ @"failed to get a reasonable description for regex w/ options");
+}
+
@end
@implementation NSString_GTMRegexAdditions
@@ -818,6 +972,23 @@
STAssertNil([@"" gtm_firstSubStringMatchedByPattern:@"(foo)(.*)(bar)"], nil);
}
+- (void)testSubStringMatchesPattern {
+ // simple pattern
+ STAssertTrue([@"foobar" gtm_subStringMatchesPattern:@"foo.*bar"], nil);
+ STAssertTrue([@"foobydoo spambar" gtm_subStringMatchesPattern:@"foo.*bar"], nil);
+ STAssertTrue([@"zzfoobarzz" gtm_subStringMatchesPattern:@"foo.*bar"], nil);
+ STAssertTrue([@"zzfoobydoo spambarzz" gtm_subStringMatchesPattern:@"foo.*bar"], nil);
+ STAssertFalse([@"abcdef" gtm_subStringMatchesPattern:@"foo.*bar"], nil);
+ STAssertFalse([@"" gtm_subStringMatchesPattern:@"foo.*bar"], nil);
+ // pattern w/ sub patterns
+ STAssertTrue([@"foobar" gtm_subStringMatchesPattern:@"(foo)(.*)(bar)"], nil);
+ STAssertTrue([@"foobydoo spambar" gtm_subStringMatchesPattern:@"(foo)(.*)(bar)"], nil);
+ STAssertTrue([@"zzfoobarzz" gtm_subStringMatchesPattern:@"(foo)(.*)(bar)"], nil);
+ STAssertTrue([@"zzfoobydoo spambarzz" gtm_subStringMatchesPattern:@"(foo)(.*)(bar)"], nil);
+ STAssertFalse([@"abcdef" gtm_subStringMatchesPattern:@"(foo)(.*)(bar)"], nil);
+ STAssertFalse([@"" gtm_subStringMatchesPattern:@"(foo)(.*)(bar)"], nil);
+}
+
- (void)testSegmentEnumeratorForPattern {
NSEnumerator *enumerator =
[@"afoobarbfooobaarfoobarzz" gtm_segmentEnumeratorForPattern:@"foo+ba+r"];
@@ -1038,6 +1209,10 @@
[@"weefoobydoo spambardoggies" gtm_stringByReplacingMatchesOfPattern:@"(foo)(.*)(bar)"
withReplacement:@""],
nil);
+ STAssertEqualStrings(@"",
+ [@"" gtm_stringByReplacingMatchesOfPattern:@"(foo)(.*)(bar)"
+ withReplacement:@"abc"],
+ nil);
// use optional and invale subexpression parts to confirm that works
STAssertEqualStrings(@"aaa baz bar bar foo baz aaa",
[@"aaa foooooobaz fooobar bar foo baz aaa" gtm_stringByReplacingMatchesOfPattern:@"(fo(o+))((bar)|(baz))"
diff --git a/Foundation/GTMScriptRunner.m b/Foundation/GTMScriptRunner.m
index e2d0995..8a76c4a 100644
--- a/Foundation/GTMScriptRunner.m
+++ b/Foundation/GTMScriptRunner.m
@@ -17,6 +17,7 @@
//
#import "GTMScriptRunner.h"
+#import "GTMDefines.h"
static BOOL LaunchNSTaskCatchingExceptions(NSTask *task);
@@ -77,8 +78,8 @@ static BOOL LaunchNSTaskCatchingExceptions(NSTask *task);
}
- (NSString *)description {
- return [NSString stringWithFormat:@"%@<%p>{ interpreter = '%@', args = %@ }",
- [self class], self, interpreter_, interpreterArgs_];
+ return [NSString stringWithFormat:@"%@<%p>{ interpreter = '%@', args = %@, environment = %@ }",
+ [self class], self, interpreter_, interpreterArgs_, environment_];
}
- (NSString *)run:(NSString *)cmds {
@@ -112,6 +113,11 @@ static BOOL LaunchNSTaskCatchingExceptions(NSTask *task);
if (trimsWhitespace_) {
*err = [*err stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
+
+ // let folks test for nil instead of @""
+ if ([*err length] < 1) {
+ *err = nil;
+ }
}
[task terminate];
@@ -120,6 +126,7 @@ static BOOL LaunchNSTaskCatchingExceptions(NSTask *task);
output = [output stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
+ // let folks test for nil instead of @""
if ([output length] < 1) {
output = nil;
}
@@ -159,6 +166,11 @@ static BOOL LaunchNSTaskCatchingExceptions(NSTask *task);
if (trimsWhitespace_) {
*err = [*err stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
+
+ // let folks test for nil instead of @""
+ if ([*err length] < 1) {
+ *err = nil;
+ }
}
[task terminate];
@@ -167,6 +179,7 @@ static BOOL LaunchNSTaskCatchingExceptions(NSTask *task);
output = [output stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
+ // let folks test for nil instead of @""
if ([output length] < 1) {
output = nil;
}
@@ -233,7 +246,8 @@ static BOOL LaunchNSTaskCatchingExceptions(NSTask *task) {
[task launch];
} @catch (id ex) {
isOK = NO;
- NSLog(@"Failed to launch interpreter '%@' due to: %@", [task launchPath], ex);
+ _GTMDevLog(@"Failed to launch interpreter '%@' due to: %@",
+ [task launchPath], ex);
}
return isOK;
}
diff --git a/Foundation/GTMScriptRunnerTest.m b/Foundation/GTMScriptRunnerTest.m
index dc92ac7..a4497b6 100644
--- a/Foundation/GTMScriptRunnerTest.m
+++ b/Foundation/GTMScriptRunnerTest.m
@@ -16,15 +16,16 @@
// the License.
//
-#import <SenTestingKit/SenTestingKit.h>
#import <sys/types.h>
#import <unistd.h>
+#import "GTMSenTestCase.h"
#import "GTMScriptRunner.h"
@interface GTMScriptRunnerTest : SenTestCase {
@private
NSString *shScript_;
NSString *perlScript_;
+ NSString *shOutputScript_;
}
@end
@@ -53,6 +54,15 @@
@"}\n"
@"print \"$i\n\"\n"
writeToFile:perlScript_ atomically:YES encoding:NSUTF8StringEncoding error:nil];
+
+ shOutputScript_ = [NSString stringWithFormat:@"/tmp/script_runner_unittest_err_%d_%d_sh", geteuid(), getpid()];
+ [@"#!/bin/sh\n"
+ @"if [ \"err\" = \"$1\" ]; then\n"
+ @" echo \" on err \" > /dev/stderr\n"
+ @"else\n"
+ @" echo \" on out \"\n"
+ @"fi\n"
+ writeToFile:shOutputScript_ atomically:YES encoding:NSUTF8StringEncoding error:nil];
}
- (void)tearDown {
@@ -62,6 +72,9 @@
path = [perlScript_ fileSystemRepresentation];
if (path)
unlink(path);
+ path = [shOutputScript_ fileSystemRepresentation];
+ if (path)
+ unlink(path);
}
- (void)testShCommands {
@@ -167,6 +180,8 @@
STAssertNotNil(sr, @"Script runner must not be nil");
NSString *output = nil;
+ STAssertNil([sr environment], @"should start w/ empty env");
+
output = [sr run:@"/usr/bin/env | wc -l"];
int numVars = [output intValue];
STAssertTrue(numVars > 0, @"numVars should be positive");
@@ -196,6 +211,152 @@
@"should be back down to %d vars", numVars);
}
+- (void)testDescription {
+ // make sure description doesn't choke
+ GTMScriptRunner *sr = [GTMScriptRunner runner];
+ STAssertNotNil(sr, @"Script runner must not be nil");
+ STAssertGreaterThan([[sr description] length], 10U,
+ @"expected a description of at least 10 chars");
+}
+
+- (void)testRunCommandOutputHandling {
+ // Test whitespace trimming & stdout vs. stderr w/ run command api
+
+ GTMScriptRunner *sr = [GTMScriptRunner runnerWithBash];
+ STAssertNotNil(sr, @"Script runner must not be nil");
+ NSString *output = nil;
+ NSString *err = nil;
+
+ // w/o whitespace trimming
+ {
+ [sr setTrimsWhitespace:NO];
+ STAssertFalse([sr trimsWhitespace], @"setTrimsWhitespace to NO failed");
+
+ // test stdout
+ output = [sr run:@"echo \" on out \"" standardError:&err];
+ STAssertEqualObjects(output, @" on out \n", @"failed to get stdout output");
+ STAssertNil(err, @"stderr should have been empty");
+
+ // test stderr
+ output = [sr run:@"echo \" on err \" > /dev/stderr" standardError:&err];
+ STAssertNil(output, @"stdout should have been empty");
+ STAssertEqualObjects(err, @" on err \n", nil);
+ }
+
+ // w/ whitespace trimming
+ {
+ [sr setTrimsWhitespace:YES];
+ STAssertTrue([sr trimsWhitespace], @"setTrimsWhitespace to YES failed");
+
+ // test stdout
+ output = [sr run:@"echo \" on out \"" standardError:&err];
+ STAssertEqualObjects(output, @"on out", @"failed to get stdout output");
+ STAssertNil(err, @"stderr should have been empty");
+
+ // test stderr
+ output = [sr run:@"echo \" on err \" > /dev/stderr" standardError:&err];
+ STAssertNil(output, @"stdout should have been empty");
+ STAssertEqualObjects(err, @"on err", nil);
+ }
+}
+
+- (void)testScriptOutputHandling {
+ // Test whitespace trimming & stdout vs. stderr w/ script api
+
+ GTMScriptRunner *sr = [GTMScriptRunner runner];
+ STAssertNotNil(sr, @"Script runner must not be nil");
+ NSString *output = nil;
+ NSString *err = nil;
+
+ // w/o whitespace trimming
+ {
+ [sr setTrimsWhitespace:NO];
+ STAssertFalse([sr trimsWhitespace], @"setTrimsWhitespace to NO failed");
+
+ // test stdout
+ output = [sr runScript:shOutputScript_
+ withArgs:[NSArray arrayWithObject:@"out"]
+ standardError:&err];
+ STAssertEqualObjects(output, @" on out \n", nil);
+ STAssertNil(err, @"stderr should have been empty");
+
+ // test stderr
+ output = [sr runScript:shOutputScript_
+ withArgs:[NSArray arrayWithObject:@"err"]
+ standardError:&err];
+ STAssertNil(output, @"stdout should have been empty");
+ STAssertEqualObjects(err, @" on err \n", nil);
+ }
+
+ // w/ whitespace trimming
+ {
+ [sr setTrimsWhitespace:YES];
+ STAssertTrue([sr trimsWhitespace], @"setTrimsWhitespace to YES failed");
+
+ // test stdout
+ output = [sr runScript:shOutputScript_
+ withArgs:[NSArray arrayWithObject:@"out"]
+ standardError:&err];
+ STAssertEqualObjects(output, @"on out", nil);
+ STAssertNil(err, @"stderr should have been empty");
+
+ // test stderr
+ output = [sr runScript:shOutputScript_
+ withArgs:[NSArray arrayWithObject:@"err"]
+ standardError:&err];
+ STAssertNil(output, @"stdout should have been empty");
+ STAssertEqualObjects(err, @"on err", nil);
+ }
+}
+
+- (void)testBadRunCommandInput {
+ GTMScriptRunner *sr = [GTMScriptRunner runner];
+ STAssertNotNil(sr, @"Script runner must not be nil");
+ NSString *err = nil;
+
+ STAssertNil([sr run:nil standardError:&err], nil);
+ STAssertNil(err, nil);
+}
+
+- (void)testBadScriptInput {
+ GTMScriptRunner *sr = [GTMScriptRunner runner];
+ STAssertNotNil(sr, @"Script runner must not be nil");
+ NSString *err = nil;
+
+ STAssertNil([sr runScript:nil withArgs:nil standardError:&err], nil);
+ STAssertNil(err, nil);
+ STAssertNil([sr runScript:@"/path/that/does/not/exists/foo/bar/baz"
+ withArgs:nil standardError:&err], nil);
+ STAssertNotNil(err,
+ @"should have gotten something about the path not existing");
+}
+
+- (void)testBadCmdInterpreter {
+ GTMScriptRunner *sr =
+ [GTMScriptRunner runnerWithInterpreter:@"/path/that/does/not/exists/interpreter"];
+ STAssertNotNil(sr, @"Script runner must not be nil");
+ NSString *err = nil;
+
+ STAssertNil([sr run:nil standardError:&err], nil);
+ STAssertNil(err, nil);
+ STAssertNil([sr run:@"ls /" standardError:&err], nil);
+ STAssertNil(err, nil);
+}
+
+- (void)testBadScriptInterpreter {
+ GTMScriptRunner *sr =
+ [GTMScriptRunner runnerWithInterpreter:@"/path/that/does/not/exists/interpreter"];
+ STAssertNotNil(sr, @"Script runner must not be nil");
+ NSString *err = nil;
+
+ STAssertNil([sr runScript:shScript_ withArgs:nil standardError:&err], nil);
+ STAssertNil(err, nil);
+ STAssertNil([sr runScript:@"/path/that/does/not/exists/foo/bar/baz"
+ withArgs:nil standardError:&err], nil);
+ STAssertNil(err, nil);
+}
+
+
@end
@implementation GTMScriptRunnerTest (PrivateMethods)
diff --git a/Foundation/GTMSystemVersion.h b/Foundation/GTMSystemVersion.h
index c9f9d75..13c3c19 100644
--- a/Foundation/GTMSystemVersion.h
+++ b/Foundation/GTMSystemVersion.h
@@ -17,29 +17,32 @@
//
#import <Foundation/Foundation.h>
+#import "GTMDefines.h"
-/// A class for getting information about what system we are running on
+// A class for getting information about what system we are running on
@interface GTMSystemVersion : NSObject
-/// Returns YES if running on 10.3, NO otherwise.
+// Returns the current system version major.minor.bugFix
++ (void)getMajor:(long*)major minor:(long*)minor bugFix:(long*)bugFix;
+
+#if GTM_MACOS_SDK
+// Returns YES if running on 10.3, NO otherwise.
+ (BOOL)isPanther;
-/// Returns YES if running on 10.4, NO otherwise.
+// Returns YES if running on 10.4, NO otherwise.
+ (BOOL)isTiger;
-/// Returns YES if running on 10.5, NO otherwise.
+// Returns YES if running on 10.5, NO otherwise.
+ (BOOL)isLeopard;
-/// Returns a YES/NO if the system is 10.3 or better
+// Returns a YES/NO if the system is 10.3 or better
+ (BOOL)isPantherOrGreater;
-/// Returns a YES/NO if the system is 10.4 or better
+// Returns a YES/NO if the system is 10.4 or better
+ (BOOL)isTigerOrGreater;
-/// Returns a YES/NO if the system is 10.5 or better
+// Returns a YES/NO if the system is 10.5 or better
+ (BOOL)isLeopardOrGreater;
-
-/// Returns the current system version major.minor.bugFix
-+ (void)getMajor:(long*)major minor:(long*)minor bugFix:(long*)bugFix;
+#endif // GTM_IPHONE_SDK
@end
diff --git a/Foundation/GTMSystemVersion.m b/Foundation/GTMSystemVersion.m
index d9b1923..a2e4d7b 100644
--- a/Foundation/GTMSystemVersion.m
+++ b/Foundation/GTMSystemVersion.m
@@ -17,88 +17,71 @@
//
#import "GTMSystemVersion.h"
-#import <Carbon/Carbon.h>
-#import <stdlib.h>
+
+static int sGTMSystemVersionMajor = 0;
+static int sGTMSystemVersionMinor = 0;
+static int sGTMSystemVersionBugFix = 0;
@implementation GTMSystemVersion
++ (void)initialize {
+ if (self == [GTMSystemVersion class]) {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ NSDictionary *systemVersionPlist = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"];
+ NSString *version = [systemVersionPlist objectForKey:@"ProductVersion"];
+ _GTMDevAssert(version, @"Unable to get version");
+ NSArray *versionInfo = [version componentsSeparatedByString:@"."];
+ int length = [versionInfo count];
+ _GTMDevAssert(length > 1 && length < 4, @"Unparseable version %@", version);
+ sGTMSystemVersionMajor = [[versionInfo objectAtIndex:0] intValue];
+ _GTMDevAssert(sGTMSystemVersionMajor != 0, @"Unknown version for %@", version);
+ sGTMSystemVersionMinor = [[versionInfo objectAtIndex:1] intValue];
+ if (length == 3) {
+ sGTMSystemVersionBugFix = [[versionInfo objectAtIndex:2] intValue];
+ }
+ [pool release];
+ }
+}
++ (void)getMajor:(long*)major minor:(long*)minor bugFix:(long*)bugFix {
+ if (major) {
+ *major = sGTMSystemVersionMajor;
+ }
+ if (minor) {
+ *minor = sGTMSystemVersionMinor;
+ }
+ if (major) {
+ *bugFix = sGTMSystemVersionBugFix;
+ }
+}
+
+#if GTM_MACOS_SDK
+ (BOOL)isPanther {
- long major, minor;
- [self getMajor:&major minor:&minor bugFix:nil];
- return major == 10 && minor == 3;
+ return sGTMSystemVersionMajor == 10 && sGTMSystemVersionMinor == 3;
}
+ (BOOL)isTiger {
- long major, minor;
- [self getMajor:&major minor:&minor bugFix:nil];
- return major == 10 && minor == 4;
+ return sGTMSystemVersionMajor == 10 && sGTMSystemVersionMinor == 4;
}
+ (BOOL)isLeopard {
- long major, minor;
- [self getMajor:&major minor:&minor bugFix:nil];
- return major == 10 && minor == 5;
+ return sGTMSystemVersionMajor == 10 && sGTMSystemVersionMinor == 5;
}
+ (BOOL)isPantherOrGreater {
- long major, minor;
- [self getMajor:&major minor:&minor bugFix:nil];
- return (major > 10) || (major == 10 && minor >= 3);
+ return (sGTMSystemVersionMajor > 10) ||
+ (sGTMSystemVersionMajor == 10 && sGTMSystemVersionMinor >= 3);
}
+ (BOOL)isTigerOrGreater {
- long major, minor;
- [self getMajor:&major minor:&minor bugFix:nil];
- return (major > 10) || (major == 10 && minor >= 4);
+ return (sGTMSystemVersionMajor > 10) ||
+ (sGTMSystemVersionMajor == 10 && sGTMSystemVersionMinor >= 4);
}
+ (BOOL)isLeopardOrGreater {
- long major, minor;
- [self getMajor:&major minor:&minor bugFix:nil];
- return (major > 10) || (major == 10 && minor >= 5);
+ return (sGTMSystemVersionMajor > 10) ||
+ (sGTMSystemVersionMajor == 10 && sGTMSystemVersionMinor >= 5);
}
-+ (void)getMajor:(long*)major minor:(long*)minor bugFix:(long*)bugFix {
- long binaryCodedDec;
-
- if (major) {
- require_noerr(Gestalt(gestaltSystemVersionMajor, major), failedGestalt);
- }
- if (minor) {
- require_noerr(Gestalt(gestaltSystemVersionMinor, minor), failedGestalt);
- }
- if (bugFix) {
- require_noerr(Gestalt(gestaltSystemVersionBugFix, bugFix), failedGestalt);
- }
- return;
-
-failedGestalt:
- // gestaltSystemVersionMajor et al are only on 10.4 and above, so they
- // could fail if we have this code on 10.3.
- if (Gestalt(gestaltSystemVersion, &binaryCodedDec)) {
- // not much else we can do...
- if (major) *major = 0;
- if (minor) *minor = 0;
- if (bugFix) *bugFix = 0;
- return;
- }
-
- // Note that this code will return x.9.9 for any system rev parts that are
- // greater than 9 (ie 10.10.10 will be 10.9.9. This shouldn't ever be a
- // problem as the code above takes care of this for any system above 10.4.
- if (major) {
- int msb = (binaryCodedDec & 0x0000F000L) >> 12;
- msb *= 10;
- int lsb = (binaryCodedDec & 0x00000F00L) >> 8;
- *major = msb + lsb;
- }
- if (minor) {
- *minor = (binaryCodedDec & 0x000000F0L) >> 4;
- }
- if (bugFix) {
- *bugFix = (binaryCodedDec & 0x0000000FL);
- }
-
-}
+#endif // GTM_IPHONE_SDK
@end
diff --git a/Foundation/GTMSystemVersionTest.m b/Foundation/GTMSystemVersionTest.m
index 57b22aa..38c37eb 100644
--- a/Foundation/GTMSystemVersionTest.m
+++ b/Foundation/GTMSystemVersionTest.m
@@ -16,7 +16,7 @@
// the License.
//
-#import <SenTestingKit/SenTestingKit.h>
+#import "GTMSenTestCase.h"
#import "GTMSystemVersion.h"
@interface GTMSystemVersionTest : SenTestCase
@@ -28,20 +28,26 @@
long minor;
long bugFix;
+ [GTMSystemVersion getMajor:nil minor:nil bugFix:nil];
[GTMSystemVersion getMajor:&major minor:&minor bugFix:&bugFix];
+#if GTM_IPHONE_SDK
+ STAssertTrue(major >= 2 && minor >= 0 && bugFix >= 0, nil);
+#else
STAssertTrue(major >= 10 && minor >= 3 && bugFix >= 0, nil);
- STAssertTrue([GTMSystemVersion isPantherOrGreater], nil);
- if (minor > 3) {
- STAssertTrue([GTMSystemVersion isTigerOrGreater], nil);
- } else {
- STAssertFalse([GTMSystemVersion isTigerOrGreater], nil);
- }
- if (minor > 4) {
- STAssertTrue([GTMSystemVersion isLeopardOrGreater], nil);
- } else {
- STAssertFalse([GTMSystemVersion isLeopardOrGreater], nil);
- }
- [GTMSystemVersion getMajor:nil minor:nil bugFix:nil];
+ BOOL isPanther = (major == 10) && (minor == 3);
+ BOOL isTiger = (major == 10) && (minor == 4);
+ BOOL isLeopard = (major == 10) && (minor == 5);
+ BOOL isLater = (major > 10) || ((major == 10) && (minor > 5));
+ STAssertEquals([GTMSystemVersion isPanther], isPanther, nil);
+ STAssertEquals([GTMSystemVersion isPantherOrGreater],
+ (BOOL)(isPanther || isTiger || isLeopard || isLater), nil);
+ STAssertEquals([GTMSystemVersion isTiger], isTiger, nil);
+ STAssertEquals([GTMSystemVersion isTigerOrGreater],
+ (BOOL)(isTiger || isLeopard || isLater), nil);
+ STAssertEquals([GTMSystemVersion isLeopard], isLeopard, nil);
+ STAssertEquals([GTMSystemVersion isLeopardOrGreater],
+ (BOOL)(isLeopard || isLater), nil);
+#endif
}
@end