aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar thomasvl <thomasvl@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2008-04-14 17:21:02 +0000
committerGravatar thomasvl <thomasvl@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2008-04-14 17:21:02 +0000
commitcdf070c8d76ffc4eaa24e8671756cbbe9ceb2890 (patch)
treefaa9ae3a72a6591d6a6add7ceed7f91e92ade11f
parent0aaecac6ff2bc89e58a0c8c6d6ad62e02fb2b011 (diff)
See the ReleaseNotes for the full details, highlights:
- bug fixes - code coverage support - more complete unittests - full support for unittesting UIs - support for the iphone sdk (include ui unittesting)
-rw-r--r--AppKit/GTMGeometryUtilsTest.m323
-rw-r--r--AppKit/GTMLinearRGBShading.m2
-rw-r--r--AppKit/GTMNSBezierPath+CGPath.m3
-rw-r--r--AppKit/GTMNSBezierPath+CGPathTest.m21
-rw-r--r--AppKit/GTMNSBezierPath+CGPathTest.tifbin1090 -> 0 bytes
-rw-r--r--AppKit/GTMNSBezierPath+CGPathTest.tiffbin0 -> 2558 bytes
-rw-r--r--AppKit/GTMNSBezierPath+RoundRectTest.m25
-rw-r--r--AppKit/GTMNSBezierPath+RoundRectTest.tifbin6982 -> 0 bytes
-rw-r--r--AppKit/GTMNSBezierPath+RoundRectTest.tiffbin0 -> 8036 bytes
-rw-r--r--AppKit/GTMNSBezierPath+ShadingTest.10.4.tifbin12964 -> 0 bytes
-rw-r--r--AppKit/GTMNSBezierPath+ShadingTest.10.5.tiff (renamed from AppKit/GTMNSBezierPath+ShadingTest.10.5.tif)bin17982 -> 19450 bytes
-rw-r--r--AppKit/GTMNSBezierPath+ShadingTest.m4
-rw-r--r--AppKit/GTMNSColor+Theme.m9
-rw-r--r--AppKit/GTMNSColor+ThemeTest.m21
-rw-r--r--AppKit/GTMNSWorkspace+ScreenSaver.m17
-rw-r--r--AppKit/GTMNSWorkspace+Theme.m2
-rw-r--r--DebugUtils/GTMDebugSelectorValidation.h86
-rw-r--r--DebugUtils/GTMMethodCheck.h88
-rw-r--r--DebugUtils/GTMMethodCheck.m144
-rw-r--r--DebugUtils/GTMMethodCheckTest.m69
-rw-r--r--Foundation/GTMCalculatedRange.h2
-rw-r--r--Foundation/GTMCalculatedRangeTest.m1
-rw-r--r--Foundation/GTMGeometryUtils.h (renamed from AppKit/GTMGeometryUtils.h)229
-rw-r--r--Foundation/GTMGeometryUtils.m (renamed from AppKit/GTMGeometryUtils.m)76
-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
-rw-r--r--GTM.xcodeproj/project.pbxproj588
-rw-r--r--GTMDefines.h91
-rw-r--r--GTMiPhone-Info.plist26
-rwxr-xr-xGTMiPhone.xcodeproj/project.pbxproj436
-rw-r--r--GTMiPhone_Prefix.pch20
-rw-r--r--ReleaseNotes.txt62
-rw-r--r--TigerGcov/libgcov.abin0 -> 35048 bytes
-rw-r--r--TigerGcov/libgcov_readme.html11
-rwxr-xr-x[-rw-r--r--]UnitTesting/GTMAppKit+UnitTesting.h (renamed from UnitTesting/GTMNSView+UnitTesting.h)83
-rwxr-xr-xUnitTesting/GTMAppKit+UnitTesting.m304
-rw-r--r--UnitTesting/GTMCALayer+UnitTesting.h46
-rw-r--r--UnitTesting/GTMCALayer+UnitTesting.m89
-rwxr-xr-xUnitTesting/GTMIPhoneUnitTestMain.m181
-rw-r--r--UnitTesting/GTMNSObject+BindingUnitTesting.h103
-rw-r--r--UnitTesting/GTMNSObject+BindingUnitTesting.m440
-rw-r--r--UnitTesting/GTMNSObject+UnitTesting.h513
-rw-r--r--UnitTesting/GTMNSObject+UnitTesting.m886
-rw-r--r--UnitTesting/GTMNSView+UnitTesting.m135
-rw-r--r--UnitTesting/GTMSenTestCase.h803
-rw-r--r--UnitTesting/GTMSenTestCase.m140
-rw-r--r--UnitTesting/GTMUIKit+UnitTesting.h128
-rw-r--r--UnitTesting/GTMUIKit+UnitTesting.m118
-rw-r--r--UnitTesting/GTMUIKit+UnitTestingTest.m58
-rw-r--r--UnitTesting/GTMUIUnitTestingHarness/English.lproj/MainMenu.nib/designable.nib1973
-rw-r--r--UnitTesting/GTMUIUnitTestingHarness/English.lproj/MainMenu.nib/keyedobjects.nibbin0 -> 16183 bytes
-rw-r--r--UnitTesting/GTMUIUnitTestingHarness/Info.plist28
-rw-r--r--UnitTesting/GTMUIUnitTestingHarness/main.m27
-rw-r--r--UnitTesting/GTMUIViewUnitTestingTest.gtmUTState33
-rw-r--r--UnitTesting/GTMUIViewUnitTestingTest.pngbin0 -> 3214 bytes
-rw-r--r--UnitTesting/GTMUnitTestingBindingTest.m117
-rw-r--r--UnitTesting/GTMUnitTestingImage.gtmUTState30
-rw-r--r--UnitTesting/GTMUnitTestingImage.tiffbin0 -> 67190 bytes
-rw-r--r--UnitTesting/GTMUnitTestingTest.h29
-rw-r--r--UnitTesting/GTMUnitTestingTest.m265
-rw-r--r--UnitTesting/GTMUnitTestingTest.nib/classes.nib86
-rw-r--r--UnitTesting/GTMUnitTestingTest.nib/info.nib20
-rw-r--r--UnitTesting/GTMUnitTestingTest.nib/keyedobjects.nibbin0 -> 15667 bytes
-rw-r--r--UnitTesting/GTMUnitTestingTestApp.gtmUTState1879
-rw-r--r--UnitTesting/GTMUnitTestingUtilities.h42
-rw-r--r--UnitTesting/GTMUnitTestingUtilities.m172
-rw-r--r--UnitTesting/GTMUnitTestingView.tiffbin0 -> 161670 bytes
-rw-r--r--UnitTesting/GTMUnitTestingWindow.gtmUTState291
-rw-r--r--UnitTesting/GTMUnitTestingWindow.tiffbin0 -> 21226 bytes
-rwxr-xr-xUnitTesting/RunIPhoneUnitTest.sh24
-rwxr-xr-xUnitTesting/RunMacOSUnitTests.sh41
-rw-r--r--XcodeConfig/Project/DebugLeopardOrLater.xcconfig34
-rw-r--r--XcodeConfig/Project/DebugTigerOrLater.xcconfig (renamed from XcodeConfig/DebugTigerOrLater.xcconfig)6
-rw-r--r--XcodeConfig/Project/DebugiPhone.xcconfig33
-rw-r--r--XcodeConfig/Project/ReleaseLeopardOrLater.xcconfig33
-rw-r--r--XcodeConfig/Project/ReleaseTigerOrLater.xcconfig (renamed from XcodeConfig/ReleaseTigerOrLater.xcconfig)6
-rw-r--r--XcodeConfig/Project/ReleaseiPhone.xcconfig33
-rw-r--r--XcodeConfig/Target/DebugUnittest.xcconfig (renamed from XcodeConfig/DebugUnittest.xcconfig)2
-rw-r--r--XcodeConfig/Target/LoadableBundle.xcconfig (renamed from XcodeConfig/LoadableBundle.xcconfig)0
-rw-r--r--XcodeConfig/Target/ReleaseUnittest.xcconfig (renamed from XcodeConfig/ReleaseUnittest.xcconfig)2
-rw-r--r--XcodeConfig/Target/SharedLibrary.xcconfig (renamed from XcodeConfig/SharedLibrary.xcconfig)0
-rw-r--r--XcodeConfig/Target/StaticLibrary.xcconfig (renamed from XcodeConfig/StaticLibrary.xcconfig)0
-rw-r--r--XcodeConfig/subconfig/General.xcconfig4
-rw-r--r--XcodeConfig/subconfig/LeopardOrLater.xcconfig28
-rw-r--r--XcodeConfig/subconfig/TigerOrLater.xcconfig5
-rw-r--r--XcodeConfig/subconfig/Unittest.xcconfig12
-rw-r--r--XcodeConfig/subconfig/iPhone.xcconfig37
-rw-r--r--XcodeConfig/xcconfigs-readme.txt12
112 files changed, 12082 insertions, 1961 deletions
diff --git a/AppKit/GTMGeometryUtilsTest.m b/AppKit/GTMGeometryUtilsTest.m
deleted file mode 100644
index 0fad449..0000000
--- a/AppKit/GTMGeometryUtilsTest.m
+++ /dev/null
@@ -1,323 +0,0 @@
-//
-// 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 <SenTestingKit/SenTestingKit.h>
-#import "GTMGeometryUtils.h"
-
-@interface GTMGeometryUtilsTest : SenTestCase
-@end
-
-@implementation GTMGeometryUtilsTest
-
-- (void)testGTMGetMainDisplayHeight {
- STAssertTrue(CGRectGetHeight(CGDisplayBounds(CGMainDisplayID())) == GTMGetMainDisplayHeight(), nil);
-}
-
-
-- (void)testGTMGlobalHIPointToNSPoint {
- HIPoint hiPoint = CGPointMake(12.5,14.5);
- NSPoint nsPoint = GTMGlobalHIPointToNSPoint(hiPoint);
- STAssertTrue(nsPoint.x == hiPoint.x &&
- nsPoint.y == GTMGetMainDisplayHeight() - hiPoint.y, nil);
-}
-
-
-- (void)testGTMGlobalNSPointToHIPoint {
- NSPoint nsPoint = NSMakePoint(12.5,14.5);
- HIPoint hiPoint = GTMGlobalNSPointToHIPoint(nsPoint);
- STAssertTrue(nsPoint.x == hiPoint.x &&
- nsPoint.y == GTMGetMainDisplayHeight() - hiPoint.y, nil);
-}
-
-
-- (void)testGTMGlobalCGPointToNSPoint {
- CGPoint cgPoint = CGPointMake(15.1,6.2);
- NSPoint nsPoint = GTMCGPointToNSPoint(cgPoint);
- STAssertTrue(CGPointEqualToPoint(*(CGPoint*)&nsPoint, cgPoint), nil);
-
-}
-
-
-- (void)testGTMGlobalNSPointToCGPoint {
- NSPoint nsPoint = NSMakePoint(10.2,1.5);
- CGPoint cgPoint = GTMNSPointToCGPoint(nsPoint);
- STAssertTrue(CGPointEqualToPoint(cgPoint, *(CGPoint*)&nsPoint), nil);
-}
-
-
-- (void)testGTMGlobalCGPointToHIPoint {
- CGPoint cgPoint = CGPointMake(12.5,14.5);
- HIPoint hiPoint = GTMGlobalCGPointToHIPoint(cgPoint);
- STAssertTrue(cgPoint.x == hiPoint.x &&
- cgPoint.y == GTMGetMainDisplayHeight() - hiPoint.y, nil);
-}
-
-
-- (void)testGTMGlobalHIPointToCGPoint {
- HIPoint hiPoint = CGPointMake(12.5,14.5);
- CGPoint cgPoint = GTMGlobalHIPointToCGPoint(hiPoint);
- STAssertTrue(cgPoint.x == hiPoint.x &&
- cgPoint.y == GTMGetMainDisplayHeight() - hiPoint.y, nil);
-}
-
-
-- (void)testGTMGlobalNSRectToHIRect {
- NSRect nsRect = NSMakeRect(40,16,17,18);
- HIRect hiRect = GTMGlobalNSRectToHIRect(nsRect);
- STAssertTrue(nsRect.origin.x == hiRect.origin.x &&
- nsRect.origin.y == GTMGetMainDisplayHeight() - hiRect.origin.y - hiRect.size.height &&
- nsRect.size.height == hiRect.size.height &&
- nsRect.size.width == hiRect.size.width, nil);
-}
-
-
-- (void)testGTMGlobalCGRectToHIRect {
- CGRect cgRect = CGRectMake(40,16,17,19.0);
- HIRect hiRect = GTMGlobalCGRectToHIRect(cgRect);
- STAssertTrue(cgRect.origin.x == hiRect.origin.x &&
- cgRect.origin.y == GTMGetMainDisplayHeight() - hiRect.origin.y - hiRect.size.height &&
- cgRect.size.height == hiRect.size.height &&
- cgRect.size.width == hiRect.size.width, nil);
-}
-
-
-- (void)testGTMGlobalHIRectToNSRect {
- HIRect hiRect = CGRectMake(40.2,16.3,17.2,18.9);
- NSRect nsRect = GTMGlobalHIRectToNSRect(hiRect);
- STAssertTrue(nsRect.origin.x == hiRect.origin.x &&
- nsRect.origin.y == GTMGetMainDisplayHeight() - hiRect.origin.y - hiRect.size.height &&
- nsRect.size.height == hiRect.size.height &&
- nsRect.size.width == hiRect.size.width, nil);
-}
-
-
-- (void)testGTMGlobalCGRectToNSRect {
- CGRect cgRect = CGRectMake(1.5,2.4,10.6,11.7);
- NSRect nsRect = GTMCGRectToNSRect(cgRect);
- STAssertTrue(CGRectEqualToRect(cgRect, *(CGRect*)&nsRect), nil);
-}
-
-
-- (void)testGTMGlobalNSRectToCGRect {
- NSRect nsRect = NSMakeRect(4.6,3.2,22.1,45.0);
- CGRect cgRect = GTMNSRectToCGRect(nsRect);
- STAssertTrue(CGRectEqualToRect(cgRect, *(CGRect*)&nsRect), nil);
-}
-
-
-- (void)testGTMGlobalHIRectToCGRect {
- HIRect hiRect = CGRectMake(40.2,16.3,17.2,18.9);
- CGRect cgRect = GTMGlobalHIRectToCGRect(hiRect);
- STAssertTrue(cgRect.origin.x == hiRect.origin.x &&
- cgRect.origin.y == GTMGetMainDisplayHeight() - hiRect.origin.y - hiRect.size.height &&
- cgRect.size.height == hiRect.size.height &&
- cgRect.size.width == hiRect.size.width, nil);
-}
-
-
-- (void)testGTMGlobalRectToNSRect {
- Rect rect = { 10,50,40,60 };
- NSRect nsRect = GTMGlobalRectToNSRect(rect);
- HIRect hiRect1 = GTMRectToHIRect(rect);
- HIRect hiRect2 = GTMGlobalNSRectToHIRect(nsRect);
- STAssertTrue(CGRectEqualToRect(hiRect1,hiRect2), nil);
-}
-
-
-- (void)testGTMGlobalNSRectToRect {
- NSRect nsRect = NSMakeRect(1.5,2.4,10.6,11.7);
- HIRect hiRect = GTMGlobalNSRectToHIRect(nsRect);
- Rect rect1 = GTMGlobalNSRectToRect(nsRect);
- Rect rect2 = GTMHIRectToRect(hiRect);
- STAssertTrue(rect1.left == rect2.left &&
- rect1.right == rect2.right &&
- rect1.top == rect2.top &&
- rect1.bottom == rect2.bottom, nil);
-}
-
-
-- (void)testGTMGlobalRectToHIRect {
- Rect rect = { 10,20,30,40 };
- HIRect hiRect = GTMRectToHIRect(rect);
- STAssertTrue(CGRectEqualToRect(hiRect, CGRectMake(20,10,20,20)), nil);
-}
-
-
-- (void)testGTMGlobalHIRectToRect {
- HIRect hiRect = CGRectMake(1.5,2.4,10.6,11.7);
- Rect rect = GTMHIRectToRect(hiRect);
- STAssertTrue(rect.left == 1 &&
- rect.right == 13 &&
- rect.top == 2 &&
- rect.bottom == 15, nil);
-}
-
-
-- (void)testGTMGlobalCGRectToRect {
- CGRect cgRect = CGRectMake(1.5,2.4,10.6,11.7);
- HIRect hiRect = GTMGlobalCGRectToHIRect(cgRect);
- Rect rect1 = GTMGlobalCGRectToRect(cgRect);
- Rect rect2 = GTMHIRectToRect(hiRect);
- STAssertTrue(rect1.left == rect2.left &&
- rect1.right == rect2.right &&
- rect1.top == rect2.top &&
- rect1.bottom == rect2.bottom, nil);
-}
-
-
-- (void)testGTMGlobalRectToCGRect {
- Rect rect = { 10,50,40,60 };
- CGRect nsRect = GTMGlobalRectToCGRect(rect);
- HIRect hiRect1 = GTMRectToHIRect(rect);
- HIRect hiRect2 = GTMGlobalCGRectToHIRect(nsRect);
- STAssertTrue(CGRectEqualToRect(hiRect1,hiRect2), 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;
- NSImageAlignment alignment;
- } TestData;
-
- TestData data[] = {
- { {1,2}, NSImageAlignTop },
- { {0,2}, NSImageAlignTopLeft },
- { {2,2}, NSImageAlignTopRight },
- { {0,1}, NSImageAlignLeft },
- { {1,0}, NSImageAlignBottom },
- { {0,0}, NSImageAlignBottomLeft },
- { {2,0}, NSImageAlignBottomRight },
- { {2,1}, NSImageAlignRight },
- { {1,1}, NSImageAlignCenter },
- };
-
- 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_,
- NSScaleProportionally);
- STAssertEquals(result, GTMNSRectOfSize(tests[i].newSize_), @"failed on test %z", i);
- }
-
- NSRect result = GTMScaleRectangleToSize(NSZeroRect, tests[0].size_,
- NSScaleProportionally);
- STAssertEquals(result, NSZeroRect, nil);
-
- result = GTMScaleRectangleToSize(rect, tests[0].size_,
- NSScaleToFit);
- STAssertEquals(result, GTMNSRectOfSize(tests[0].size_), nil);
-
- result = GTMScaleRectangleToSize(rect, tests[0].size_,
- NSScaleNone);
- STAssertEquals(result, rect, nil);
-
-}
-@end
diff --git a/AppKit/GTMLinearRGBShading.m b/AppKit/GTMLinearRGBShading.m
index ab5bda6..31f22ca 100644
--- a/AppKit/GTMLinearRGBShading.m
+++ b/AppKit/GTMLinearRGBShading.m
@@ -165,7 +165,7 @@ static void cShadeFunction(void *info, const float *inPos, float *outVals) {
// diposed if necessary in the dealloc call.
const CGFunctionCallbacks shadeFunctionCallbacks = { 0, &cShadeFunction, NULL };
- // TODO: (dmaclach): this code assumes that we have a range from 0.0f to 1.0f
+ // TODO: this code assumes that we have a range from 0.0f to 1.0f
// which may not be true according to the stops that the user has given us.
// In general you have stops at 0.0 and 1.0, so this will do for right now
// but may be an issue in the future.
diff --git a/AppKit/GTMNSBezierPath+CGPath.m b/AppKit/GTMNSBezierPath+CGPath.m
index 97464fc..c805f42 100644
--- a/AppKit/GTMNSBezierPath+CGPath.m
+++ b/AppKit/GTMNSBezierPath+CGPath.m
@@ -18,6 +18,7 @@
// the License.
//
#import "GTMNSBezierPath+CGPath.h"
+#import "GTMDefines.h"
@implementation NSBezierPath (GTMBezierPathCGPathAdditions)
@@ -58,7 +59,7 @@
CGPathCloseSubpath(thePath);
break;
default:
- NSLog(@"Unknown element at [NSBezierPath (GTMBezierPathCGPathAdditions) cgPath]");
+ _GTMDevLog(@"Unknown element at [NSBezierPath (GTMBezierPathCGPathAdditions) cgPath]");
break;
};
}
diff --git a/AppKit/GTMNSBezierPath+CGPathTest.m b/AppKit/GTMNSBezierPath+CGPathTest.m
index c8506d7..3ca11c3 100644
--- a/AppKit/GTMNSBezierPath+CGPathTest.m
+++ b/AppKit/GTMNSBezierPath+CGPathTest.m
@@ -20,7 +20,8 @@
#import <SenTestingKit/SenTestingKit.h>
#import "GTMNSBezierPath+CGPath.h"
-#import "GTMNSView+UnitTesting.h"
+#import "GTMAppKit+UnitTesting.h"
+#import "GTMSenTestCase.h"
@interface GTMNSBezierPath_CGPathTest : SenTestCase<GTMUnitTestViewDrawer>
@end
@@ -32,8 +33,8 @@
}
-// Draws all of our tests so that we can compare this to our stored TIFF file.
-- (void)unitTestViewDrawRect:(NSRect)rect contextInfo:(void*)contextInfo{
+// Draws all of our tests so that we can compare this to our stored image file.
+- (void)gtm_unitTestViewDrawRect:(NSRect)rect contextInfo:(void*)contextInfo{
NSBezierPath *thePath = [NSBezierPath bezierPath];
NSPoint theStart = NSMakePoint(20.0f, 20.0f);
@@ -59,17 +60,11 @@
[thePath closePath];
CGPathRef cgPath = [thePath gtm_createCGPath];
- if (nil == cgPath) {
- @throw [NSException failureInFile:[NSString stringWithCString:__FILE__]
- atLine:__LINE__
- withDescription:@"Nil CGPath"];
- }
+ STAssertNotNULL(cgPath, @"Nil CGPath");
+
CGContextRef cgContext = [[NSGraphicsContext currentContext] graphicsPort];
- if (nil == cgContext) {
- @throw [NSException failureInFile:[NSString stringWithCString:__FILE__]
- atLine:__LINE__
- withDescription:@"Nil CGContext"];
- }
+ STAssertNotNULL(cgContext, @"Nil cgContext");
+
CGContextAddPath(cgContext, cgPath);
CGContextStrokePath(cgContext);
CGPathRelease(cgPath);
diff --git a/AppKit/GTMNSBezierPath+CGPathTest.tif b/AppKit/GTMNSBezierPath+CGPathTest.tif
deleted file mode 100644
index a7fee9b..0000000
--- a/AppKit/GTMNSBezierPath+CGPathTest.tif
+++ /dev/null
Binary files differ
diff --git a/AppKit/GTMNSBezierPath+CGPathTest.tiff b/AppKit/GTMNSBezierPath+CGPathTest.tiff
new file mode 100644
index 0000000..98ec8f8
--- /dev/null
+++ b/AppKit/GTMNSBezierPath+CGPathTest.tiff
Binary files differ
diff --git a/AppKit/GTMNSBezierPath+RoundRectTest.m b/AppKit/GTMNSBezierPath+RoundRectTest.m
index 61bd2dd..6ff4a18 100644
--- a/AppKit/GTMNSBezierPath+RoundRectTest.m
+++ b/AppKit/GTMNSBezierPath+RoundRectTest.m
@@ -20,7 +20,7 @@
#import <SenTestingKit/SenTestingKit.h>
#import "GTMNSBezierPath+RoundRect.h"
-#import "GTMNSView+UnitTesting.h"
+#import "GTMAppKit+UnitTesting.h"
@interface GTMNSBezierPath_RoundRectTest : SenTestCase<GTMUnitTestViewDrawer>
@end
@@ -28,12 +28,12 @@
@implementation GTMNSBezierPath_RoundRectTest
- (void)testRoundRects {
- GTMAssertDrawingEqualToFile(self, NSMakeSize(330, 430), @"GTMNSBezierPath+RoundRectTest", nil, nil);
+ GTMAssertDrawingEqualToFile(self, NSMakeSize(330, 430),
+ @"GTMNSBezierPath+RoundRectTest", nil, nil);
}
-
// Draws all of our tests so that we can compare this to our stored TIFF file.
-- (void)unitTestViewDrawRect:(NSRect)rect contextInfo:(void*)contextInfo{
+- (void)gtm_unitTestViewDrawRect:(NSRect)rect contextInfo:(void*)contextInfo{
NSRect theRects[] = {
NSMakeRect(0.0f, 10.0f, 0.0f, 0.0f), //Empty Rect test
NSMakeRect(50.0f, 10.0f, 30.0f, 30.0f), //Square Test
@@ -50,25 +50,29 @@
for (i = 0; i < theLineWidthCount; ++i) {
for (j = 0; j < theRectCount; ++j) {
- NSBezierPath *roundRect = [NSBezierPath gtm_bezierPathWithRoundRect:theRects[j] cornerRadius:20.0f];
+ NSBezierPath *roundRect = [NSBezierPath gtm_bezierPathWithRoundRect:theRects[j]
+ cornerRadius:20.0f];
[roundRect setLineWidth: theLineWidths[i]];
[roundRect stroke];
float newWidth = 35.0f;
- if (i < theLineWidthCount - 1) newWidth += theLineWidths[i + 1] + theLineWidths[i];
+ if (i < theLineWidthCount - 1) {
+ newWidth += theLineWidths[i + 1] + theLineWidths[i];
+ }
theRects[j].origin.y += newWidth;
}
}
// Fill test
NSColor *theColors[] = {
- [NSColor colorWithDeviceRed:1.0f green:0.0f blue:0.0f alpha:1.0f],
- [NSColor colorWithDeviceRed:0.2f green:0.4f blue:0.6f alpha:0.4f]
+ [NSColor colorWithCalibratedRed:1.0f green:0.0f blue:0.0f alpha:1.0f],
+ [NSColor colorWithCalibratedRed:0.2f green:0.4f blue:0.6f alpha:0.4f]
};
const unsigned int theColorCount = sizeof(theColors)/sizeof(NSColor);
for (i = 0; i < theColorCount; ++i) {
for (j = 0; j < theRectCount; ++j) {
- NSBezierPath *roundRect = [NSBezierPath gtm_bezierPathWithRoundRect:theRects[j] cornerRadius:10.0f];
+ NSBezierPath *roundRect = [NSBezierPath gtm_bezierPathWithRoundRect:theRects[j]
+ cornerRadius:10.0f];
[theColors[i] setFill];
[roundRect fill];
theRects[j].origin.y += 35.0f;
@@ -81,7 +85,8 @@
for (i = 0; i < theFlatnessCount; i++) {
for (j = 0; j < theRectCount; ++j) {
- NSBezierPath *roundRect = [NSBezierPath gtm_bezierPathWithRoundRect:theRects[j] cornerRadius:6.0f];
+ NSBezierPath *roundRect = [NSBezierPath gtm_bezierPathWithRoundRect:theRects[j]
+ cornerRadius:6.0f];
[roundRect setFlatness:theFlatness[i]];
[roundRect stroke];
theRects[j].origin.y += 35.0f;
diff --git a/AppKit/GTMNSBezierPath+RoundRectTest.tif b/AppKit/GTMNSBezierPath+RoundRectTest.tif
deleted file mode 100644
index 7ce0d45..0000000
--- a/AppKit/GTMNSBezierPath+RoundRectTest.tif
+++ /dev/null
Binary files differ
diff --git a/AppKit/GTMNSBezierPath+RoundRectTest.tiff b/AppKit/GTMNSBezierPath+RoundRectTest.tiff
new file mode 100644
index 0000000..c41ab04
--- /dev/null
+++ b/AppKit/GTMNSBezierPath+RoundRectTest.tiff
Binary files differ
diff --git a/AppKit/GTMNSBezierPath+ShadingTest.10.4.tif b/AppKit/GTMNSBezierPath+ShadingTest.10.4.tif
deleted file mode 100644
index 44a09b5..0000000
--- a/AppKit/GTMNSBezierPath+ShadingTest.10.4.tif
+++ /dev/null
Binary files differ
diff --git a/AppKit/GTMNSBezierPath+ShadingTest.10.5.tif b/AppKit/GTMNSBezierPath+ShadingTest.10.5.tiff
index 6d4570a..040cb0d 100644
--- a/AppKit/GTMNSBezierPath+ShadingTest.10.5.tif
+++ b/AppKit/GTMNSBezierPath+ShadingTest.10.5.tiff
Binary files differ
diff --git a/AppKit/GTMNSBezierPath+ShadingTest.m b/AppKit/GTMNSBezierPath+ShadingTest.m
index bed6a4e..8b56cfb 100644
--- a/AppKit/GTMNSBezierPath+ShadingTest.m
+++ b/AppKit/GTMNSBezierPath+ShadingTest.m
@@ -21,7 +21,7 @@
#import <SenTestingKit/SenTestingKit.h>
#import "GTMLinearRGBShading.h"
-#import "GTMNSView+UnitTesting.h"
+#import "GTMAppKit+UnitTesting.h"
#import "GTMNSBezierPath+Shading.h"
@interface GTMNSBezierPath_ShadingTest : SenTestCase<GTMUnitTestViewDrawer>
@@ -34,7 +34,7 @@
}
-- (void)unitTestViewDrawRect:(NSRect)rect contextInfo:(void*)contextInfo{
+- (void)gtm_unitTestViewDrawRect:(NSRect)rect contextInfo:(void*)contextInfo {
NSColor *theColorArray[] = { [NSColor blueColor],
[NSColor redColor], [NSColor yellowColor],
diff --git a/AppKit/GTMNSColor+Theme.m b/AppKit/GTMNSColor+Theme.m
index 40326a7..67e8459 100644
--- a/AppKit/GTMNSColor+Theme.m
+++ b/AppKit/GTMNSColor+Theme.m
@@ -19,6 +19,7 @@
//
#import "GTMNSColor+Theme.h"
+#import "GTMDefines.h"
@implementation NSColor (GTMColorThemeAdditions)
@@ -37,9 +38,7 @@
blue:blue
alpha:1.0f];
} else {
-#ifdef DEBUG
- NSLog(@"Unable to create color for textcolor %d", textColor);
-#endif
+ _GTMDevLog(@"Unable to create color for textcolor %d", textColor);
}
return nsTextColor;
}
@@ -56,9 +55,7 @@
blue:rgbBrushColor.blue / 65535.0f
alpha:1.0f];
} else {
-#ifdef DEBUG
- NSLog(@"Unable to create color for brushcolor %d", brush);
-#endif
+ _GTMDevLog(@"Unable to create color for brushcolor %d", brush);
}
return nsBrushColor;
}
diff --git a/AppKit/GTMNSColor+ThemeTest.m b/AppKit/GTMNSColor+ThemeTest.m
index 4115dda..e7d2a77 100644
--- a/AppKit/GTMNSColor+ThemeTest.m
+++ b/AppKit/GTMNSColor+ThemeTest.m
@@ -82,21 +82,21 @@
{ 0.499992, 0.499992, 0.499992, 1.000000 },
{ 0.000000, 0.000000, 0.000000, 1.000000 }
};
-
- if ([GTMSystemVersion isLeopardOrGreater]) {
- // kThemeTextColorRootMenuDisabled changed to white in Leopard.
- colorValues[35][0] = 1.0;
- colorValues[35][1] = 1.0;
- colorValues[35][2] = 1.0;
- }
+
+ if ([GTMSystemVersion isLeopardOrGreater]) {
+ // kThemeTextColorRootMenuDisabled changed to white in Leopard.
+ colorValues[35][0] = 1.0;
+ colorValues[35][1] = 1.0;
+ colorValues[35][2] = 1.0;
+ }
for(int i = kThemeTextColorWhite; i < kThemeTextColorSystemDetail; i++) {
- if (i == 0) continue;
+ if (i == 0) continue; // There is no brush 0
NSColor *textColor = [NSColor gtm_colorWithThemeTextColor:i];
float nsComponents[5];
[textColor getComponents: nsComponents];
for(int j = 0; j < 4; j++) {
STAssertEqualsWithAccuracy(nsComponents[j], colorValues[i + 2][j], 0.000001,
- @"Theme Text Color %d is wrong", i);
+ @"Theme Text Color %d is wrong", i);
STAssertEqualObjects([textColor colorSpaceName], NSCalibratedRGBColorSpace,
@"Color space must be CalibratedRGB");
}
@@ -169,13 +169,16 @@
NSString *theme = [[NSWorkspace sharedWorkspace] gtm_themeAppearance];
if ([theme isEqualToString:(NSString*)kThemeAppearanceAquaGraphite]) {
+ // COV_NF_START
// These are the only two brushes that change with an appearance change
+ // In general we will be testing in blue, so this code won't get run
colorValues[21][0] = 0.605478;
colorValues[21][1] = 0.667979;
colorValues[21][2] = 0.738293;
colorValues[59][0] = 0.941192;
colorValues[59][1] = 0.941192;
colorValues[59][2] = 0.941192;
+ // COV_NF_END
}
for(int i = kThemeBrushWhite; i < kThemeBrushListViewColumnDivider; i++) {
// Brush "14" is the selection, so it will change depending on the system
diff --git a/AppKit/GTMNSWorkspace+ScreenSaver.m b/AppKit/GTMNSWorkspace+ScreenSaver.m
index 270f9bc..c86a73c 100644
--- a/AppKit/GTMNSWorkspace+ScreenSaver.m
+++ b/AppKit/GTMNSWorkspace+ScreenSaver.m
@@ -19,6 +19,7 @@
#import <Carbon/Carbon.h>
#import <ScreenSaver/ScreenSaver.h>
#import "GTMNSWorkspace+ScreenSaver.h"
+#import "GTMDefines.h"
// Interesting class descriptions extracted from ScreenSaver.framework using
// class-dump. Note that these are "not documented".
@@ -79,21 +80,25 @@
// step rather carefully.
Class screenSaverControllerClass = NSClassFromString(@"ScreenSaverController");
- NSAssert(screenSaverControllerClass,
- @"Are you linked with ScreenSaver.framework?"
- " Can't find ScreenSaverController class.");
+ _GTMDevAssert(screenSaverControllerClass,
+ @"Are you linked with ScreenSaver.framework?"
+ " Can't find ScreenSaverController class.");
if ([screenSaverControllerClass respondsToSelector:@selector(controller)]) {
controller = [ScreenSaverController controller];
if (controller) {
if ([controller respondsToSelector:@selector(screenSaverIsRunning)]) {
answer = [controller screenSaverIsRunning];
} else {
- NSLog(@"ScreenSaverController no longer supports -screenSaverIsRunning?");
+ // COV_NF_START
+ _GTMDevLog(@"ScreenSaverController no longer supports -screenSaverIsRunning?");
+ controller = nil;
+ // COV_NF_END
}
}
}
if (!controller) {
+ // COV_NF_START
// If we can't get the controller, chances are we are being run from the
// command line and don't have access to the window server. As such we are
// going to fallback to the older method of figuring out if a screen saver
@@ -106,7 +111,7 @@
= ProcessInformationCopyDictionary(&psn,
kProcessDictionaryIncludeAllInformationMask);
- require(cfProcessInfo, CantGetFrontProcessInfo);
+ require(cfProcessInfo, CantGetFrontProcess);
NSString *bundlePath = [(NSDictionary*)cfProcessInfo objectForKey:@"BundlePath"];
@@ -116,8 +121,8 @@
answer = [bundlePath hasSuffix:@"ScreenSaverEngine.app"] ||
[bundlePath hasSuffix:@"SecurityAgent.app"];
CFRelease(cfProcessInfo);
+ // COV_NF_END
}
-CantGetFrontProcessInfo:
CantGetFrontProcess:
return answer;
}
diff --git a/AppKit/GTMNSWorkspace+Theme.m b/AppKit/GTMNSWorkspace+Theme.m
index 86b9935..3afb801 100644
--- a/AppKit/GTMNSWorkspace+Theme.m
+++ b/AppKit/GTMNSWorkspace+Theme.m
@@ -35,7 +35,7 @@
ThemeScrollBarArrowStyle style = kThemeScrollBarArrowsInvalid;
OSStatus theStatus = GetThemeScrollBarArrowStyle(&style);
if (theStatus != noErr) {
- style = kThemeScrollBarArrowsInvalid;
+ style = kThemeScrollBarArrowsInvalid; // COV_NF_LINE
}
return style;
}
diff --git a/DebugUtils/GTMDebugSelectorValidation.h b/DebugUtils/GTMDebugSelectorValidation.h
new file mode 100644
index 0000000..b3f1e73
--- /dev/null
+++ b/DebugUtils/GTMDebugSelectorValidation.h
@@ -0,0 +1,86 @@
+//
+// GTMDebugSelectorValidation.h
+//
+// This file should only be included within an implimation file. In any
+// function that takes an object and selector to invoke, you should call:
+//
+// GTMAssertSelectorNilOrImplementedWithArguments(obj, sel, @encode(arg1type), ..., NULL)
+//
+// This will then validate that the selector is defined and using the right
+// type(s), this can help catch errors much earlier then waiting for the
+// selector to actually fire (and in the case of error selectors, might never
+// really be tested until in the field).
+//
+// 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.
+//
+
+#if DEBUG
+
+#import <stdarg.h>
+#import "GTMDefines.h"
+
+static void GTMAssertSelectorNilOrImplementedWithArguments(id obj, SEL sel, ...) {
+
+ // verify that the object's selector is implemented with the proper
+ // number and type of arguments
+ va_list argList;
+ va_start(argList, sel);
+
+ if (obj && sel) {
+ // check that the selector is implemented
+ if (![obj respondsToSelector:sel]) {
+ _GTMDevAssert(NO,
+ @"\"%@\" selector \"%@\" is unimplemented or misnamed",
+ NSStringFromClass([obj class]),
+ NSStringFromSelector(sel));
+ } else {
+ const char *expectedArgType;
+ int argCount = 2; // skip self and _cmd
+ NSMethodSignature *sig = [obj methodSignatureForSelector:sel];
+
+ // check that each expected argument is present and of the correct type
+ while ((expectedArgType = va_arg(argList, const char*)) != 0) {
+
+ if ([sig numberOfArguments] > argCount) {
+ const char *foundArgType = [sig getArgumentTypeAtIndex:argCount];
+
+ _GTMDevAssert(0 == strncmp(foundArgType, expectedArgType, strlen(expectedArgType)),
+ @"\"%@\" selector \"%@\" argument %d should be type %s",
+ NSStringFromClass([obj class]),
+ NSStringFromSelector(sel),
+ (argCount - 2),
+ expectedArgType);
+ }
+ argCount++;
+ }
+
+ // check that the proper number of arguments are present in the selector
+ _GTMDevAssert(argCount == [sig numberOfArguments],
+ @"\"%@\" selector \"%@\" should have %d arguments",
+ NSStringFromClass([obj class]),
+ NSStringFromSelector(sel),
+ (argCount - 2));
+ }
+ }
+
+ va_end(argList);
+}
+
+#else // DEBUG
+
+// make it go away if not debug
+#define GTMAssertSelectorNilOrImplementedWithArguments(obj, sel, ...) do { } while (0)
+
+#endif // DEBUG
diff --git a/DebugUtils/GTMMethodCheck.h b/DebugUtils/GTMMethodCheck.h
new file mode 100644
index 0000000..0915c0b
--- /dev/null
+++ b/DebugUtils/GTMMethodCheck.h
@@ -0,0 +1,88 @@
+//
+// GTMMethodCheck.h
+//
+// Copyright 2006-2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import <Foundation/Foundation.h>
+#import <stdio.h>
+#import <sysexits.h>
+
+/// A macro for enforcing debug time checks to make sure all required methods are linked in
+//
+// When using categories, it can be very easy to forget to include the
+// implementation of a category.
+// Let's say you had a class foo that depended on method bar of class baz, and
+// method bar was implemented as a member of a category.
+// You could add the following code:
+// @implementation foo
+// GTM_METHOD_CHECK(baz, bar)
+// @end
+// and the code would check to make sure baz was implemented just before main
+// was called. This works for both dynamic libraries, and executables.
+//
+// Classes (or one of their superclasses) being checked must conform to the
+// NSObject protocol. We will check this, and spit out a warning if a class does
+// not conform to NSObject.
+//
+// This is not compiled into release builds.
+
+#ifdef DEBUG
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// If you get an error for GTMMethodCheckMethodChecker not being defined,
+// you need to link in GTMMethodCheck.m. We keep it hidden so that we can have
+// it living in several separate images without conflict.
+// Functions with the ((constructor)) attribute are called after all +loads
+// have been called. See "Initializing Objective-C Classes" in
+// http://developer.apple.com/documentation/DeveloperTools/Conceptual/DynamicLibraries/Articles/DynamicLibraryDesignGuidelines.html#//apple_ref/doc/uid/TP40002013-DontLinkElementID_20
+
+__attribute__ ((constructor, visibility("hidden"))) void GTMMethodCheckMethodChecker(void);
+
+#ifdef __cplusplus
+};
+#endif
+
+// This is the "magic".
+// A) we need a multi layer define here so that the stupid preprocessor
+// expands __LINE__ out the way we want it. We need LINE so that each of
+// out GTM_METHOD_CHECKs generates a unique class method for the class.
+#define GTM_METHOD_CHECK(class, method) GTM_METHOD_CHECK_INNER(class, method, __LINE__)
+#define GTM_METHOD_CHECK_INNER(class, method, line) GTM_METHOD_CHECK_INNER_INNER(class, method, line)
+
+// B) Create up a class method called xxGMethodCheckMethod+class+line that the
+// GTMMethodCheckMethodChecker function can look for and call. We
+// look for GTMMethodCheckMethodChecker to enforce linkage of
+// GTMMethodCheck.m.
+#define GTM_METHOD_CHECK_INNER_INNER(class, method, line) \
++ (void)xxGMMethodCheckMethod ## class ## line { \
+ void (*addr)() = GTMMethodCheckMethodChecker; \
+ if (addr && ![class instancesRespondToSelector:@selector(method)] \
+ && ![class respondsToSelector:@selector(method)]) { \
+ fprintf(stderr, "%s:%d: error: We need method '%s' to be linked in for class '%s'\n", \
+ __FILE__, line, #method, #class); \
+ exit(EX_SOFTWARE); \
+ } \
+}
+
+#else // !DEBUG
+
+// Do nothing in debug
+#define GTM_METHOD_CHECK(class, method)
+
+#endif // DEBUG
diff --git a/DebugUtils/GTMMethodCheck.m b/DebugUtils/GTMMethodCheck.m
new file mode 100644
index 0000000..379f006
--- /dev/null
+++ b/DebugUtils/GTMMethodCheck.m
@@ -0,0 +1,144 @@
+//
+// GTMMethodCheck.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.
+//
+
+// Don't want any of this in release builds
+#ifdef DEBUG
+#import "GTMDefines.h"
+#import "GTMMethodCheck.h"
+#import "GTMObjC2Runtime.h"
+#import <dlfcn.h>
+
+// Checks to see if the cls passed in (or one of it's superclasses) conforms
+// to NSObject protocol. Inheriting from NSObject is the easiest way to do this
+// but not all classes (i.e. NSProxy) inherit from NSObject. Also, some classes
+// inherit from Object instead of NSObject which is fine, and we'll count as
+// conforming to NSObject for our needs.
+static BOOL ConformsToNSObjectProtocol(Class cls) {
+ // If we get nil, obviously doesn't conform.
+ if (!cls) return NO;
+ const char *className = class_getName(cls);
+ if (!className) return NO;
+
+ // We're going to assume that all Apple classes will work
+ // (and aren't being checked)
+ // Note to apple: why doesn't obj-c have real namespaces instead of two
+ // letter hacks? If you name your own classes starting with NS this won't
+ // work for you.
+ // Some classes (like _NSZombie) start with _NS.
+ // On Leopard we have to look for CFObject as well.
+ if ((strncmp(className, "NS", 2) == 0) ||
+ (strncmp(className, "_NS", 3) == 0) ||
+ (strcmp(className, "CFObject") == 0)) {
+ return YES;
+ }
+
+ // Iterate through all the protocols |cls| supports looking for NSObject.
+ if (cls == [Object class]
+ || class_conformsToProtocol(cls, @protocol(NSObject))) {
+ return YES;
+ }
+
+ // Recursively check the superclasses.
+ return ConformsToNSObjectProtocol(class_getSuperclass(cls));
+}
+
+void GTMMethodCheckMethodChecker(void) {
+ // Run through all the classes looking for class methods that are
+ // prefixed with xxGMMethodCheckMethod. If it finds one, it calls it.
+ // See GTMMethodCheck.h to see what it does.
+ int numClasses = 0;
+ int newNumClasses = objc_getClassList(NULL, 0);
+ int i;
+ Class *classes = NULL;
+ while (numClasses < newNumClasses) {
+ numClasses = newNumClasses;
+ classes = realloc(classes, sizeof(Class) * numClasses);
+ _GTMDevAssert(classes, @"Unable to allocate memory for classes");
+ newNumClasses = objc_getClassList(classes, numClasses);
+ }
+ for (i = 0; i < numClasses; ++i) {
+ Class cls = classes[i];
+
+ // Since we are directly calling objc_msgSend, we need to conform to
+ // @protocol(NSObject), or else we will tumble into a _objc_msgForward
+ // recursive loop when we try and call a function by name.
+ if (!ConformsToNSObjectProtocol(cls)) {
+ _GTMDevLog(@"GTMMethodCheckMethodChecker: Class %s does not conform to "
+ "@protocol(NSObject), so won't be checked",
+ class_getName(cls));
+ continue;
+ }
+ // Since we are looking for a class method (+xxGMMethodCheckMethod...)
+ // we need to query the isa pointer to see what methods it support, but
+ // send the method (if it's supported) to the class itself.
+ unsigned int count;
+ Class metaClass = objc_getMetaClass(class_getName(cls));
+ Method *methods = class_copyMethodList(metaClass, &count);
+ unsigned int j;
+ for (j = 0; j < count; ++j) {
+ SEL selector = method_getName(methods[j]);
+ const char *name = sel_getName(selector);
+ if (strstr(name, "xxGTMMethodCheckMethod") == name) {
+ // Check to make sure that the method we are checking comes
+ // from the same image that we are in. Since GTMMethodCheckMethodChecker
+ // is not exported, we should always find the copy in our local
+ // image. We compare the address of it's image with the address of
+ // the image which implements the method we want to check. If
+ // they match we continue. This does two things:
+ // a) minimizes the amount of calls we make to the xxxGTMMethodCheck
+ // methods. They should only be called once.
+ // b) prevents initializers for various classes being called too early
+ Dl_info methodCheckerInfo;
+ if (!dladdr(GTMMethodCheckMethodChecker,
+ &methodCheckerInfo)) {
+ // COV_NF_START
+ // Don't know how to force this case in a unittest.
+ // Certainly hope we never see it.
+ _GTMDevLog(@"GTMMethodCheckMethodChecker: Unable to get dladdr info "
+ "for GTMMethodCheckMethodChecker while introspecting +[%@ %@]]",
+ class_getName(cls), name);
+ continue;
+ // COV_NF_END
+ }
+ Dl_info methodInfo;
+ if (!dladdr(method_getImplementation(methods[j]),
+ &methodInfo)) {
+ // COV_NF_START
+ // Don't know how to force this case in a unittest
+ // Certainly hope we never see it.
+ _GTMDevLog(@"GTMMethodCheckMethodChecker: Unable to get dladdr info "
+ "for %@ while introspecting +[%@ %@]]", name,
+ class_getName(cls), name);
+ continue;
+ // COV_NF_END
+ }
+ if (methodCheckerInfo.dli_fbase == methodInfo.dli_fbase) {
+ objc_msgSend(cls, selector);
+ }
+ }
+ }
+ if (methods) {
+ free(methods);
+ }
+ }
+ if (classes) {
+ free(classes);
+ }
+}
+
+#endif // DEBUG
diff --git a/DebugUtils/GTMMethodCheckTest.m b/DebugUtils/GTMMethodCheckTest.m
new file mode 100644
index 0000000..b5e7e6b
--- /dev/null
+++ b/DebugUtils/GTMMethodCheckTest.m
@@ -0,0 +1,69 @@
+//
+// GTMMethodCheckTest.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 "GTMMethodCheck.h"
+
+static BOOL gTestCheckVar = NO;
+
+// This is a contrived class that doesn't inherit from NSObject, but does
+// implement some of it's functionality to force test a case in
+// GTMMethodCheck.
+@interface GTMClassThatDoesntInheritFromNSObject
++ (BOOL)instancesRespondToSelector:(SEL)selector;
++ (BOOL)respondsToSelector:(SEL)selector;
+@end
+
+@implementation GTMClassThatDoesntInheritFromNSObject
+GTM_METHOD_CHECK(GTMClassThatDoesntInheritFromNSObject, GTMMethodCheckTestMethod);
+- (void)GTMMethodCheckTestMethod {
+}
++ (BOOL)instancesRespondToSelector:(SEL)selector {
+ return YES;
+}
+
++ (BOOL)respondsToSelector:(SEL)selector {
+ return YES;
+}
+@end
+
+@interface GTMMethodCheckTest : SenTestCase
+@end
+
+@implementation GTMMethodCheckTest
+GTM_METHOD_CHECK(GTMMethodCheckTest, GTMMethodCheckTestMethod);
+GTM_METHOD_CHECK(GTMMethodCheckTest, GTMMethodCheckTestClassMethod);
+
+- (void)GTMMethodCheckTestMethod {
+}
+
++ (void)GTMMethodCheckTestClassMethod {
+}
+
++ (void)xxGTMMethodCheckMethodTestCheck {
+ // This gets called because of its special name by GMMethodCheck
+ // Look at the Macros in GMMethodCheck.h for details.
+ gTestCheckVar = YES;
+}
+
+- (void)testGTMMethodCheck {
+#ifdef DEBUG
+ // GTMMethodCheck only runs in debug
+ STAssertTrue(gTestCheckVar, @"Should be true");
+#endif
+}
+@end
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/AppKit/GTMGeometryUtils.h b/Foundation/GTMGeometryUtils.h
index 1dd274f..32c8745 100644
--- a/AppKit/GTMGeometryUtils.h
+++ b/Foundation/GTMGeometryUtils.h
@@ -19,9 +19,25 @@
// the License.
//
-#include <Carbon/Carbon.h>
-#include <Cocoa/Cocoa.h>
-
+#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
@@ -39,46 +55,9 @@ CG_INLINE float GTMDistanceBetweenPoints(NSPoint pt1, NSPoint pt2) {
return sqrtf(dX * dX + dY * dY);
}
-/// Returns the height of the main display (the one with the menu bar).
-//
-/// The value updates itself automatically whenever the user
-/// repositions their monitors, or changes resolutions etc.
-//
-// Returns:
-// height of the main display area.
-float GTMGetMainDisplayHeight(void);
-
#pragma mark -
#pragma mark Point Conversion
-/// Quickly convert from a global HIPoint to a global NSPoint.
-//
-/// HIPoints are relative to 0,0 in upper left;
-/// NSPoints are relative to 0,0 in lower left
-//
-// Args:
-// inPoint: HIPoint to convert
-//
-// Returns:
-// Converted NSPoint
-CG_INLINE NSPoint GTMGlobalHIPointToNSPoint(HIPoint inPoint) {
- return NSMakePoint(inPoint.x, GTMGetMainDisplayHeight() - inPoint.y);
-}
-
-/// Quickly convert from a global NSPoint to a global HIPoint.
-//
-/// HIPoints are relative to 0,0 in upper left;
-/// NSPoints are relative to 0,0 in lower left
-//
-// Args:
-// inPoint: NSPoint to convert
-//
-// Returns:
-// Converted HIPoint
-CG_INLINE HIPoint GTMGlobalNSPointToHIPoint(NSPoint inPoint) {
- return CGPointMake(inPoint.x, GTMGetMainDisplayHeight() - inPoint.y);
-}
-
/// Quickly convert from a CGPoint to a NSPoint.
//
/// CGPoints are relative to 0,0 in lower left;
@@ -107,100 +86,9 @@ CG_INLINE CGPoint GTMNSPointToCGPoint(NSPoint inPoint) {
return CGPointMake(inPoint.x, inPoint.y);
}
-/// Quickly convert from a global HIPoint to a global CGPoint.
-//
-/// HIPoints are relative to 0,0 in upper left;
-/// CGPoints are relative to 0,0 in lower left
-//
-// Args:
-// inPoint: NSPoint to convert
-//
-// Returns:
-// Converted CGPoint
-CG_INLINE HIPoint GTMGlobalCGPointToHIPoint(CGPoint inPoint) {
- return GTMGlobalNSPointToHIPoint(GTMCGPointToNSPoint(inPoint));
-}
-
-/// Quickly convert from a global CGPoint to a global HIPoint.
-//
-/// HIPoints are relative to 0,0 in upper left;
-/// CGPoints are relative to 0,0 in lower left
-//
-// Args:
-// inPoint: CGPoint to convert
-//
-// Returns:
-// Converted NSPoint
-CG_INLINE CGPoint GTMGlobalHIPointToCGPoint(HIPoint inPoint) {
- return GTMNSPointToCGPoint(GTMGlobalHIPointToNSPoint(inPoint));
-}
-
#pragma mark -
#pragma mark Rect Conversion
-/// Convert from a global NSRect to a global HIRect.
-//
-/// HIRect are relative to 0,0 in upper left;
-/// NSRect are relative to 0,0 in lower left
-//
-// Args:
-// inRect: NSRect to convert
-//
-// Returns:
-// Converted HIRect
-HIRect GTMGlobalNSRectToHIRect(NSRect inRect);
-
-/// Convert from a rect to a HIRect.
-//
-/// HIRect are relative to 0,0 in upper left;
-/// Rect are relative to 0,0 in upper left
-//
-// Args:
-// inRect: Rect to convert
-//
-// Returns:
-// Converted HIRect
-HIRect GTMRectToHIRect(Rect inRect);
-
-/// Convert from a global HIRect to a global NSRect.
-//
-/// NSRect are relative to 0,0 in lower left;
-/// HIRect are relative to 0,0 in upper left
-//
-// Args:
-// inRect: HIRect to convert
-//
-// Returns:
-// Converted NSRect
-NSRect GTMGlobalHIRectToNSRect(HIRect inRect);
-
-
-/// Convert from a HIRect to a Rect.
-//
-/// Rect are relative to 0,0 in upper left;
-/// HIRect are relative to 0,0 in upper left
-//
-// Args:
-// inRect: HIRect to convert
-//
-// Returns:
-// Converted Rect
-Rect GTMHIRectToRect(HIRect inRect);
-
-/// Convert from a global Rect to a global NSRect.
-//
-/// NSRect are relative to 0,0 in lower left;
-/// Rect are relative to 0,0 in upper left
-//
-// Args:
-// inRect: Rect to convert
-//
-// Returns:
-// Converted NSRect
-CG_INLINE NSRect GTMGlobalRectToNSRect(Rect inRect) {
- return GTMGlobalHIRectToNSRect(GTMRectToHIRect(inRect));
-}
-
/// Convert from a CGRect to a NSRect.
//
/// NSRect are relative to 0,0 in lower left;
@@ -229,76 +117,6 @@ CG_INLINE CGRect GTMNSRectToCGRect(NSRect inRect) {
return CGRectMake(inRect.origin.x,inRect.origin.y,inRect.size.width,inRect.size.height);
}
-/// Convert from a global HIRect to a global CGRect.
-//
-/// HIRect are relative to 0,0 in upper left;
-/// CGRect are relative to 0,0 in lower left
-//
-// Args:
-// inRect: HIRect to convert
-//
-// Returns:
-// Converted CGRect
-CG_INLINE CGRect GTMGlobalHIRectToCGRect(HIRect inRect) {
- return GTMNSRectToCGRect(GTMGlobalHIRectToNSRect(inRect));
-}
-
-/// Convert from a global Rect to a global CGRect.
-//
-/// Rect are relative to 0,0 in upper left;
-/// CGRect are relative to 0,0 in lower left
-//
-// Args:
-// inRect: Rect to convert
-//
-// Returns:
-// Converted CGRect
-CG_INLINE CGRect GTMGlobalRectToCGRect(Rect inRect) {
- return GTMNSRectToCGRect(GTMGlobalRectToNSRect(inRect));
-}
-
-/// Convert from a global NSRect to a global Rect.
-//
-/// Rect are relative to 0,0 in upper left;
-/// NSRect are relative to 0,0 in lower left
-//
-// Args:
-// inRect: NSRect to convert
-//
-// Returns:
-// Converted Rect
-CG_INLINE Rect GTMGlobalNSRectToRect(NSRect inRect) {
- return GTMHIRectToRect(GTMGlobalNSRectToHIRect(inRect));
-}
-
-/// Convert from a global CGRect to a global HIRect.
-//
-/// HIRect are relative to 0,0 in upper left;
-/// CGRect are relative to 0,0 in lower left
-//
-// Args:
-// inRect: CGRect to convert
-//
-// Returns:
-// Converted HIRect
-CG_INLINE HIRect GTMGlobalCGRectToHIRect(CGRect inRect) {
- return GTMGlobalNSRectToHIRect(GTMCGRectToNSRect(inRect));
-}
-
-/// Convert from a global CGRect to a global Rect.
-//
-/// Rect are relative to 0,0 in upper left;
-/// CGRect are relative to 0,0 in lower left
-//
-// Args:
-// inRect: CGRect to convert
-//
-// Returns:
-// Converted Rect
-CG_INLINE Rect GTMGlobalCGRectToRect(CGRect inRect) {
- return GTMHIRectToRect(GTMGlobalCGRectToHIRect(inRect));
-}
-
#pragma mark -
#pragma mark Size Conversion
@@ -521,7 +339,7 @@ CG_INLINE CGRect GTMCGRectScale(CGRect inRect, float xScale, float yScale) {
// alignee - rect to be aligned
// aligner - rect to be aligned from
NSRect GTMAlignRectangles(NSRect alignee, NSRect aligner,
- NSImageAlignment alignment);
+ GTMRectAlignment alignment);
/// Align rectangles
//
@@ -530,7 +348,7 @@ NSRect GTMAlignRectangles(NSRect alignee, NSRect aligner,
// aligner - rect to be aligned from
// alignment - way to align the rectangles
CG_INLINE CGRect GTMCGAlignRectangles(CGRect alignee, CGRect aligner,
- NSImageAlignment alignment) {
+ GTMRectAlignment alignment) {
return GTMNSRectToCGRect(GTMAlignRectangles(GTMCGRectToNSRect(alignee),
GTMCGRectToNSRect(aligner),
alignment));
@@ -543,7 +361,7 @@ CG_INLINE CGRect GTMCGAlignRectangles(CGRect alignee, CGRect aligner,
// size - size to scale to
// scaling - way to scale the rectangle
NSRect GTMScaleRectangleToSize(NSRect scalee, NSSize size,
- NSImageScaling scaling);
+ GTMScaling scaling);
/// Scale rectangle
//
@@ -552,9 +370,8 @@ NSRect GTMScaleRectangleToSize(NSRect scalee, NSSize size,
// size - size to scale to
// scaling - way to scale the rectangle
CG_INLINE CGRect GTMCGScaleRectangleToSize(CGRect scalee, CGSize size,
- NSImageScaling scaling) {
+ GTMScaling scaling) {
return GTMNSRectToCGRect(GTMScaleRectangleToSize(GTMCGRectToNSRect(scalee),
GTMCGSizeToNSSize(size),
scaling));
}
-
diff --git a/AppKit/GTMGeometryUtils.m b/Foundation/GTMGeometryUtils.m
index efc543e..f5b38dc 100644
--- a/AppKit/GTMGeometryUtils.m
+++ b/Foundation/GTMGeometryUtils.m
@@ -18,54 +18,6 @@
#import "GTMGeometryUtils.h"
-float GTMGetMainDisplayHeight(void) {
- float height = 0;
- NSArray *screens = [NSScreen screens];
- // We may have a headless machine without any screens. In this case we
- // return 0.
- if ([screens count] > 0) {
- height = NSHeight([(NSScreen*)[screens objectAtIndex: 0] frame]);
- }
- return height;
-}
-
-
-// Rect conversion routines.
-HIRect GTMGlobalNSRectToHIRect(NSRect inRect) {
- HIRect theRect;
- theRect.origin = GTMGlobalNSPointToHIPoint(inRect.origin);
- theRect.origin.y -= inRect.size.height;
- theRect.size = CGSizeMake(inRect.size.width, inRect.size.height);
- return theRect;
-}
-
-
-HIRect GTMRectToHIRect(Rect inRect) {
- HIRect theRect;
- theRect.origin = CGPointMake(inRect.left,inRect.top);
- theRect.size = CGSizeMake(inRect.right - inRect.left, inRect.bottom - inRect.top);
- return theRect;
-}
-
-
-NSRect GTMGlobalHIRectToNSRect(HIRect inRect) {
- NSRect theRect;
- theRect.origin = GTMGlobalHIPointToNSPoint(inRect.origin);
- theRect.origin.y -= inRect.size.height;
- theRect.size = NSMakeSize(inRect.size.width, inRect.size.height);
- return theRect;
-}
-
-
-Rect GTMHIRectToRect(HIRect inRect) {
- Rect theRect;
- theRect.left = inRect.origin.x;
- theRect.right = ceilf(inRect.origin.x + inRect.size.width);
- theRect.top = inRect.origin.y;
- theRect.bottom = ceilf(inRect.origin.y + inRect.size.height);
- return theRect;
-}
-
/// Align rectangles
//
// Args:
@@ -73,50 +25,50 @@ Rect GTMHIRectToRect(HIRect inRect) {
// aligner - rect to be aligned to
// alignment - alignment to be applied to alignee based on aligner
-NSRect GTMAlignRectangles(NSRect alignee, NSRect aligner, NSImageAlignment alignment) {
+NSRect GTMAlignRectangles(NSRect alignee, NSRect aligner, GTMRectAlignment alignment) {
switch (alignment) {
- case NSImageAlignTop:
+ 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 NSImageAlignTopLeft:
+ case GTMRectAlignTopLeft:
alignee.origin.x = aligner.origin.x;
alignee.origin.y = aligner.origin.y + NSHeight(aligner) - NSHeight(alignee);
break;
- case NSImageAlignTopRight:
+ case GTMRectAlignTopRight:
alignee.origin.x = aligner.origin.x + NSWidth(aligner) - NSWidth(alignee);
alignee.origin.y = aligner.origin.y + NSHeight(aligner) - NSHeight(alignee);
break;
- case NSImageAlignLeft:
+ case GTMRectAlignLeft:
alignee.origin.x = aligner.origin.x;
alignee.origin.y = aligner.origin.y + (NSHeight(aligner) * .5f - NSHeight(alignee) * .5f);
break;
- case NSImageAlignBottomLeft:
+ case GTMRectAlignBottomLeft:
alignee.origin.x = aligner.origin.x;
alignee.origin.y = aligner.origin.y;
break;
- case NSImageAlignBottom:
+ case GTMRectAlignBottom:
alignee.origin.x = aligner.origin.x + (NSWidth(aligner) * .5f - NSWidth(alignee) * .5f);
alignee.origin.y = aligner.origin.y;
break;
- case NSImageAlignBottomRight:
+ case GTMRectAlignBottomRight:
alignee.origin.x = aligner.origin.x + NSWidth(aligner) - NSWidth(alignee);
alignee.origin.y = aligner.origin.y;
break;
- case NSImageAlignRight:
+ 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 NSImageAlignCenter:
+ 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;
@@ -124,9 +76,9 @@ NSRect GTMAlignRectangles(NSRect alignee, NSRect aligner, NSImageAlignment align
return alignee;
}
-NSRect GTMScaleRectangleToSize(NSRect scalee, NSSize size, NSImageScaling scaling) {
+NSRect GTMScaleRectangleToSize(NSRect scalee, NSSize size, GTMScaling scaling) {
switch (scaling) {
- case NSScaleProportionally: {
+ case GTMScaleProportionally: {
float height = NSHeight(scalee);
float width = NSWidth(scalee);
if (isnormal(height) && isnormal(width) &&
@@ -139,11 +91,11 @@ NSRect GTMScaleRectangleToSize(NSRect scalee, NSSize size, NSImageScaling scalin
break;
}
- case NSScaleToFit:
+ case GTMScaleToFit:
scalee.size = size;
break;
- case NSScaleNone:
+ case GTMScaleNone:
default:
// Do nothing
break;
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
diff --git a/GTM.xcodeproj/project.pbxproj b/GTM.xcodeproj/project.pbxproj
index f941194..21685f0 100644
--- a/GTM.xcodeproj/project.pbxproj
+++ b/GTM.xcodeproj/project.pbxproj
@@ -13,6 +13,7 @@
buildPhases = (
);
dependencies = (
+ 8B45A0390DA46A20001148C5 /* PBXTargetDependency */,
F472042F0D33BEB500E9F378 /* PBXTargetDependency */,
F472042D0D33BEB500E9F378 /* PBXTargetDependency */,
);
@@ -28,26 +29,65 @@
8B2A9B240D8270DA00599386 /* GTMNSWorkspace+ScreenSaverTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B2A9B1E0D8270DA00599386 /* GTMNSWorkspace+ScreenSaverTest.m */; };
8B2A9BEC0D82714A00599386 /* ScreenSaver.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B2A9BEB0D82714A00599386 /* ScreenSaver.framework */; };
8B2A9BED0D82714A00599386 /* ScreenSaver.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B2A9BEB0D82714A00599386 /* ScreenSaver.framework */; };
+ 8B45A03A0DA46A2A001148C5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; };
+ 8B45A0B80DA46A2F001148C5 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F42E089B0D199B1800D5DDE0 /* SenTestingKit.framework */; };
+ 8B45A0D50DA46A57001148C5 /* GTMNSObject+UnitTesting.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE29C0D198D36009257D2 /* GTMNSObject+UnitTesting.m */; };
+ 8B45A0D60DA46A57001148C5 /* GTMNSObject+BindingUnitTesting.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B1A14E90D900BC800CA1E8E /* GTMNSObject+BindingUnitTesting.m */; };
+ 8B45A19A0DA46AAA001148C5 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B45A1990DA46AAA001148C5 /* QuartzCore.framework */; };
+ 8B45A2040DA46DF6001148C5 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; };
+ 8B45A2070DA46DFC001148C5 /* ScreenSaver.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B2A9BEB0D82714A00599386 /* ScreenSaver.framework */; };
+ 8B45A21A0DA46E1D001148C5 /* GTMGeometryUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE27F0D198D0E009257D2 /* GTMGeometryUtils.m */; };
+ 8B45A21D0DA46E2C001148C5 /* GTMObjC2Runtime.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F32040DA34A1B0052CA40 /* GTMObjC2Runtime.m */; };
+ 8B45A21E0DA46E34001148C5 /* GTMObjC2Runtime.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F32040DA34A1B0052CA40 /* GTMObjC2Runtime.m */; };
+ 8B45A22C0DA46E51001148C5 /* GTMSystemVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE2930D198D24009257D2 /* GTMSystemVersion.m */; };
+ 8B45A2AA0DA49C47001148C5 /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = 8B45A2A50DA49C47001148C5 /* MainMenu.nib */; };
+ 8B45A2AC0DA49C47001148C5 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B45A2A80DA49C47001148C5 /* main.m */; };
+ 8B45A2B30DA49CA9001148C5 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; };
+ 8B45A2D00DA51A01001148C5 /* GTMUnitTestingUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B45A2680DA498A0001148C5 /* GTMUnitTestingUtilities.m */; };
+ 8B45A2DF0DA51A7E001148C5 /* GTMUnitTestingTest.nib in Resources */ = {isa = PBXBuildFile; fileRef = 8B45A2DE0DA51A7E001148C5 /* GTMUnitTestingTest.nib */; };
+ 8B45A2E20DA51ABC001148C5 /* GTMUnitTestingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B45A2E10DA51ABC001148C5 /* GTMUnitTestingTest.m */; };
+ 8B45A5F80DA5EB9F001148C5 /* GTMUnitTestingWindow.gtmUTState in Resources */ = {isa = PBXBuildFile; fileRef = 8B45A5F50DA5EB9F001148C5 /* GTMUnitTestingWindow.gtmUTState */; };
+ 8B45A5F90DA5EB9F001148C5 /* GTMUnitTestingTestApp.gtmUTState in Resources */ = {isa = PBXBuildFile; fileRef = 8B45A5F60DA5EB9F001148C5 /* GTMUnitTestingTestApp.gtmUTState */; };
+ 8B45A6BB0DA67DD5001148C5 /* GTMUnitTestingImage.gtmUTState in Resources */ = {isa = PBXBuildFile; fileRef = 8B45A6B90DA67DD5001148C5 /* GTMUnitTestingImage.gtmUTState */; };
+ 8B55479C0DB3B7A50014CC1C /* GTMAppKit+UnitTesting.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B55479B0DB3B7A50014CC1C /* GTMAppKit+UnitTesting.m */; };
+ 8B5547B90DB3BB220014CC1C /* GTMAppKit+UnitTesting.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B55479B0DB3B7A50014CC1C /* GTMAppKit+UnitTesting.m */; };
+ 8B6F32080DA34A1B0052CA40 /* GTMObjC2RuntimeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F32050DA34A1B0052CA40 /* GTMObjC2RuntimeTest.m */; };
+ 8B6F32160DA34C830052CA40 /* GTMMethodCheckTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F31F10DA347720052CA40 /* GTMMethodCheckTest.m */; };
+ 8B7AD49A0DABBB5800B84F4A /* GTMNSBezierPath+ShadingTest.10.5.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 8B7AD4970DABBB5800B84F4A /* GTMNSBezierPath+ShadingTest.10.5.tiff */; };
+ 8B7AD49B0DABBB5800B84F4A /* GTMNSBezierPath+CGPathTest.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 8B7AD4980DABBB5800B84F4A /* GTMNSBezierPath+CGPathTest.tiff */; };
+ 8B7AD49C0DABBB5800B84F4A /* GTMNSBezierPath+RoundRectTest.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 8B7AD4990DABBB5800B84F4A /* GTMNSBezierPath+RoundRectTest.tiff */; };
+ 8B7AD4AE0DABBFEE00B84F4A /* GTMUnitTestingBindingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B7AD4AD0DABBFEE00B84F4A /* GTMUnitTestingBindingTest.m */; };
+ 8BC045C20DAE899100C2D1CA /* GTMGeometryUtilsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE2800D198D0E009257D2 /* GTMGeometryUtilsTest.m */; };
+ 8BC046B90DAE8C4B00C2D1CA /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BC046B80DAE8C4B00C2D1CA /* ApplicationServices.framework */; };
+ 8BC04CD30DB003BE00C2D1CA /* GTMNSString+Utilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 8BC04CD10DB003BE00C2D1CA /* GTMNSString+Utilities.h */; };
+ 8BC04CD40DB003BE00C2D1CA /* GTMNSString+Utilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC04CD20DB003BE00C2D1CA /* GTMNSString+Utilities.m */; };
+ 8BC04CD60DB003C300C2D1CA /* GTMNSString+UtilitiesTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC04CD50DB003C300C2D1CA /* GTMNSString+UtilitiesTest.m */; };
+ 8BC04CD70DB003CD00C2D1CA /* GTMMethodCheck.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F31EF0DA347720052CA40 /* GTMMethodCheck.m */; };
+ 8BC04CD80DB003D800C2D1CA /* GTMMethodCheck.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B6F31F40DA3489B0052CA40 /* GTMMethodCheck.h */; };
+ 8BC04CDD0DB004A000C2D1CA /* GTMMethodCheck.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F31EF0DA347720052CA40 /* GTMMethodCheck.m */; };
+ 8BC04CDE0DB004A000C2D1CA /* GTMMethodCheck.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F31EF0DA347720052CA40 /* GTMMethodCheck.m */; };
+ 8BC04CDF0DB004A100C2D1CA /* GTMMethodCheck.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F31EF0DA347720052CA40 /* GTMMethodCheck.m */; };
+ 8BEEA90D0DA7446300894774 /* GTMUnitTestingImage.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 8BEEA90A0DA7446300894774 /* GTMUnitTestingImage.tiff */; };
+ 8BEEA90E0DA7446300894774 /* GTMUnitTestingWindow.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 8BEEA90B0DA7446300894774 /* GTMUnitTestingWindow.tiff */; };
+ 8BEEA90F0DA7446300894774 /* GTMUnitTestingView.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 8BEEA90C0DA7446300894774 /* GTMUnitTestingView.tiff */; };
F413908F0D75F63C00F72B31 /* GTMNSFileManager+Path.h in Headers */ = {isa = PBXBuildFile; fileRef = F413908C0D75F63C00F72B31 /* GTMNSFileManager+Path.h */; };
F41390900D75F63C00F72B31 /* GTMNSFileManager+Path.m in Sources */ = {isa = PBXBuildFile; fileRef = F413908D0D75F63C00F72B31 /* GTMNSFileManager+Path.m */; };
F41390920D75F64D00F72B31 /* GTMNSFileManager+PathTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F413908E0D75F63C00F72B31 /* GTMNSFileManager+PathTest.m */; };
+ F424F7010D9AA02B000B87EF /* GTMNSData+zlibTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F43E4E600D4E5EC90041161F /* GTMNSData+zlibTest.m */; };
+ F424F75F0D9AF019000B87EF /* GTMDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B1A16050D90344B00CA1E8E /* GTMDefines.h */; };
F428FF030D48E55E00382ED1 /* GTMNSBezierPath+CGPath.h in Headers */ = {isa = PBXBuildFile; fileRef = F428FEFF0D48E55E00382ED1 /* GTMNSBezierPath+CGPath.h */; };
F428FF040D48E55E00382ED1 /* GTMNSBezierPath+CGPath.m in Sources */ = {isa = PBXBuildFile; fileRef = F428FF000D48E55E00382ED1 /* GTMNSBezierPath+CGPath.m */; };
F428FF090D48E57300382ED1 /* GTMNSBezierPath+CGPathTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F428FF010D48E55E00382ED1 /* GTMNSBezierPath+CGPathTest.m */; };
- F428FF0A0D48E57300382ED1 /* GTMNSBezierPath+CGPathTest.tif in Resources */ = {isa = PBXBuildFile; fileRef = F428FF020D48E55E00382ED1 /* GTMNSBezierPath+CGPathTest.tif */; };
- F42E082E0D19991400D5DDE0 /* GTMGeometryUtilsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE2800D198D0E009257D2 /* GTMGeometryUtilsTest.m */; };
F42E082F0D19991400D5DDE0 /* GTMNSBezierPath+RoundRectTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE2830D198D0E009257D2 /* GTMNSBezierPath+RoundRectTest.m */; };
- F42E08300D19991400D5DDE0 /* GTMNSBezierPath+RoundRectTest.tif in Resources */ = {isa = PBXBuildFile; fileRef = F48FE2840D198D0E009257D2 /* GTMNSBezierPath+RoundRectTest.tif */; };
F42E08330D19992100D5DDE0 /* GTMNSString+HTMLTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE2900D198D24009257D2 /* GTMNSString+HTMLTest.m */; };
F42E08340D19992100D5DDE0 /* GTMSystemVersionTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE2E10D198E4C009257D2 /* GTMSystemVersionTest.m */; };
F42E08610D199A2B00D5DDE0 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; };
F42E08640D199A2F00D5DDE0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; };
- F42E087F0D199AB400D5DDE0 /* GTM.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F42E086D0D199A5B00D5DDE0 /* GTM.framework */; };
- F42E08800D199AB500D5DDE0 /* GTM.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F42E086D0D199A5B00D5DDE0 /* GTM.framework */; };
+ F42E087F0D199AB400D5DDE0 /* GoogleToolboxForMac.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F42E086D0D199A5B00D5DDE0 /* GoogleToolboxForMac.framework */; };
+ F42E08800D199AB500D5DDE0 /* GoogleToolboxForMac.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F42E086D0D199A5B00D5DDE0 /* GoogleToolboxForMac.framework */; };
F42E089C0D199B1800D5DDE0 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F42E089B0D199B1800D5DDE0 /* SenTestingKit.framework */; };
F42E089D0D199B1800D5DDE0 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F42E089B0D199B1800D5DDE0 /* SenTestingKit.framework */; };
F42E09450D199BA400D5DDE0 /* GTMNSObject+UnitTesting.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE29C0D198D36009257D2 /* GTMNSObject+UnitTesting.m */; };
- F42E09460D199BA400D5DDE0 /* GTMNSView+UnitTesting.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE29E0D198D36009257D2 /* GTMNSView+UnitTesting.m */; };
F42E09490D199BBF00D5DDE0 /* GTMDelegatingTableColumn.h in Headers */ = {isa = PBXBuildFile; fileRef = F48FE27C0D198D0E009257D2 /* GTMDelegatingTableColumn.h */; };
F42E094A0D199BBF00D5DDE0 /* GTMDelegatingTableColumn.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE27D0D198D0E009257D2 /* GTMDelegatingTableColumn.m */; };
F42E094B0D199BBF00D5DDE0 /* GTMGarbageCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = F48FE28D0D198D24009257D2 /* GTMGarbageCollection.h */; };
@@ -82,15 +122,12 @@
F43E4DDE0D4E56380041161F /* GTMNSEnumerator+FilterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F43E4DD80D4E56320041161F /* GTMNSEnumerator+FilterTest.m */; };
F43E4E610D4E5EC90041161F /* GTMNSData+zlib.h in Headers */ = {isa = PBXBuildFile; fileRef = F43E4E5E0D4E5EC90041161F /* GTMNSData+zlib.h */; };
F43E4E620D4E5EC90041161F /* GTMNSData+zlib.m in Sources */ = {isa = PBXBuildFile; fileRef = F43E4E5F0D4E5EC90041161F /* GTMNSData+zlib.m */; };
- F43E4E660D4E5ED40041161F /* GTMNSData+zlibTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F43E4E600D4E5EC90041161F /* GTMNSData+zlibTest.m */; };
F43E4F6D0D4E60C50041161F /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F43E4F6C0D4E60C50041161F /* libz.dylib */; };
F47A79880D746EE9002302AB /* GTMScriptRunner.h in Headers */ = {isa = PBXBuildFile; fileRef = F47A79850D746EE9002302AB /* GTMScriptRunner.h */; };
F47A79890D746EE9002302AB /* GTMScriptRunner.m in Sources */ = {isa = PBXBuildFile; fileRef = F47A79860D746EE9002302AB /* GTMScriptRunner.m */; };
F47A798B0D746EFC002302AB /* GTMScriptRunnerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F47A79870D746EE9002302AB /* GTMScriptRunnerTest.m */; };
F47F1C120D490BC000925B8F /* GTMNSBezierPath+Shading.h in Headers */ = {isa = PBXBuildFile; fileRef = F47F1C0D0D490BC000925B8F /* GTMNSBezierPath+Shading.h */; };
F47F1C130D490BC000925B8F /* GTMNSBezierPath+Shading.m in Sources */ = {isa = PBXBuildFile; fileRef = F47F1C0E0D490BC000925B8F /* GTMNSBezierPath+Shading.m */; };
- F47F1C190D490BD200925B8F /* GTMNSBezierPath+ShadingTest.10.4.tif in Resources */ = {isa = PBXBuildFile; fileRef = F47F1C0F0D490BC000925B8F /* GTMNSBezierPath+ShadingTest.10.4.tif */; };
- F47F1C1A0D490BD200925B8F /* GTMNSBezierPath+ShadingTest.10.5.tif in Resources */ = {isa = PBXBuildFile; fileRef = F47F1C100D490BC000925B8F /* GTMNSBezierPath+ShadingTest.10.5.tif */; };
F47F1C1B0D490BD200925B8F /* GTMNSBezierPath+ShadingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F47F1C110D490BC000925B8F /* GTMNSBezierPath+ShadingTest.m */; };
F47F1C750D490E5C00925B8F /* GTMShading.h in Headers */ = {isa = PBXBuildFile; fileRef = F47F1C740D490E5C00925B8F /* GTMShading.h */; };
F47F1CAF0D4910FD00925B8F /* GTMNSColor+Theme.h in Headers */ = {isa = PBXBuildFile; fileRef = F47F1CAC0D4910FD00925B8F /* GTMNSColor+Theme.h */; };
@@ -99,9 +136,25 @@
F47F1D300D4914AD00925B8F /* GTMCalculatedRange.h in Headers */ = {isa = PBXBuildFile; fileRef = F47F1D2D0D4914AD00925B8F /* GTMCalculatedRange.h */; };
F47F1D310D4914AD00925B8F /* GTMCalculatedRange.m in Sources */ = {isa = PBXBuildFile; fileRef = F47F1D2E0D4914AD00925B8F /* GTMCalculatedRange.m */; };
F47F1D350D4914B600925B8F /* GTMCalculatedRangeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F47F1D2F0D4914AD00925B8F /* GTMCalculatedRangeTest.m */; };
+ F4CA854F0DAFAAF600B4AB10 /* GTMObjC2Runtime.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B6F32060DA34A1B0052CA40 /* GTMObjC2Runtime.h */; };
+ F4FF22780D9D4835003880AC /* GTMDebugSelectorValidation.h in Headers */ = {isa = PBXBuildFile; fileRef = F4FF22770D9D4835003880AC /* GTMDebugSelectorValidation.h */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
+ 8B45A0380DA46A20001148C5 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 8B45A0270DA4696C001148C5;
+ remoteInfo = "UnitTest - UnitTesting";
+ };
+ 8B45A2D30DA51A0E001148C5 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 8B45A2890DA49B99001148C5;
+ remoteInfo = UIUnitTestingHarness;
+ };
F42E08760D199A9B00D5DDE0 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
@@ -137,19 +190,57 @@
0867D6A5FE840307C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
32DBCF5E0370ADEE00C91783 /* GTM_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTM_Prefix.pch; sourceTree = "<group>"; };
+ 8B1A14E90D900BC800CA1E8E /* GTMNSObject+BindingUnitTesting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSObject+BindingUnitTesting.m"; sourceTree = "<group>"; };
+ 8B1A14EA0D900BC800CA1E8E /* GTMNSObject+BindingUnitTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSObject+BindingUnitTesting.h"; sourceTree = "<group>"; };
+ 8B1A16050D90344B00CA1E8E /* GTMDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMDefines.h; sourceTree = "<group>"; };
8B2A9B1D0D8270DA00599386 /* GTMNSWorkspace+ScreenSaver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSWorkspace+ScreenSaver.m"; sourceTree = "<group>"; };
8B2A9B1E0D8270DA00599386 /* GTMNSWorkspace+ScreenSaverTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSWorkspace+ScreenSaverTest.m"; sourceTree = "<group>"; };
8B2A9B1F0D8270DA00599386 /* GTMNSWorkspace+ScreenSaver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSWorkspace+ScreenSaver.h"; sourceTree = "<group>"; };
8B2A9BEB0D82714A00599386 /* ScreenSaver.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ScreenSaver.framework; path = /System/Library/Frameworks/ScreenSaver.framework; sourceTree = "<absolute>"; };
+ 8B45A0280DA4696C001148C5 /* UnitTest - UnitTesting.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "UnitTest - UnitTesting.octest"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 8B45A1990DA46AAA001148C5 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = /System/Library/Frameworks/QuartzCore.framework; sourceTree = "<absolute>"; };
+ 8B45A2670DA498A0001148C5 /* GTMUnitTestingUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMUnitTestingUtilities.h; sourceTree = "<group>"; };
+ 8B45A2680DA498A0001148C5 /* GTMUnitTestingUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMUnitTestingUtilities.m; sourceTree = "<group>"; };
+ 8B45A28A0DA49B99001148C5 /* GTMUIUnitTestingHarness.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GTMUIUnitTestingHarness.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 8B45A2A60DA49C47001148C5 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/MainMenu.nib; sourceTree = "<group>"; };
+ 8B45A2A70DA49C47001148C5 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ 8B45A2A80DA49C47001148C5 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ 8B45A2DE0DA51A7E001148C5 /* GTMUnitTestingTest.nib */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; path = GTMUnitTestingTest.nib; sourceTree = "<group>"; };
+ 8B45A2E00DA51ABC001148C5 /* GTMUnitTestingTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMUnitTestingTest.h; sourceTree = "<group>"; };
+ 8B45A2E10DA51ABC001148C5 /* GTMUnitTestingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMUnitTestingTest.m; sourceTree = "<group>"; };
+ 8B45A5F50DA5EB9F001148C5 /* GTMUnitTestingWindow.gtmUTState */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = GTMUnitTestingWindow.gtmUTState; sourceTree = "<group>"; };
+ 8B45A5F60DA5EB9F001148C5 /* GTMUnitTestingTestApp.gtmUTState */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = GTMUnitTestingTestApp.gtmUTState; sourceTree = "<group>"; };
+ 8B45A6B90DA67DD5001148C5 /* GTMUnitTestingImage.gtmUTState */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = GTMUnitTestingImage.gtmUTState; sourceTree = "<group>"; };
+ 8B55479A0DB3B7A50014CC1C /* GTMAppKit+UnitTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMAppKit+UnitTesting.h"; sourceTree = "<group>"; };
+ 8B55479B0DB3B7A50014CC1C /* GTMAppKit+UnitTesting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMAppKit+UnitTesting.m"; sourceTree = "<group>"; };
+ 8B6F31EF0DA347720052CA40 /* GTMMethodCheck.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMMethodCheck.m; sourceTree = "<group>"; };
+ 8B6F31F10DA347720052CA40 /* GTMMethodCheckTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMMethodCheckTest.m; sourceTree = "<group>"; };
+ 8B6F31F40DA3489B0052CA40 /* GTMMethodCheck.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMMethodCheck.h; sourceTree = "<group>"; };
+ 8B6F32040DA34A1B0052CA40 /* GTMObjC2Runtime.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMObjC2Runtime.m; sourceTree = "<group>"; };
+ 8B6F32050DA34A1B0052CA40 /* GTMObjC2RuntimeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMObjC2RuntimeTest.m; sourceTree = "<group>"; };
+ 8B6F32060DA34A1B0052CA40 /* GTMObjC2Runtime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMObjC2Runtime.h; sourceTree = "<group>"; };
+ 8B726BD00D91C0860090C251 /* GTMCALayer+UnitTesting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMCALayer+UnitTesting.m"; sourceTree = "<group>"; };
+ 8B726BD10D91C0860090C251 /* GTMCALayer+UnitTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMCALayer+UnitTesting.h"; sourceTree = "<group>"; };
+ 8B7AD4970DABBB5800B84F4A /* GTMNSBezierPath+ShadingTest.10.5.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMNSBezierPath+ShadingTest.10.5.tiff"; sourceTree = "<group>"; };
+ 8B7AD4980DABBB5800B84F4A /* GTMNSBezierPath+CGPathTest.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMNSBezierPath+CGPathTest.tiff"; sourceTree = "<group>"; };
+ 8B7AD4990DABBB5800B84F4A /* GTMNSBezierPath+RoundRectTest.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMNSBezierPath+RoundRectTest.tiff"; sourceTree = "<group>"; };
+ 8B7AD4AD0DABBFEE00B84F4A /* GTMUnitTestingBindingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMUnitTestingBindingTest.m; sourceTree = "<group>"; };
+ 8BC046B80DAE8C4B00C2D1CA /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = /System/Library/Frameworks/ApplicationServices.framework; sourceTree = "<absolute>"; };
+ 8BC04CD10DB003BE00C2D1CA /* GTMNSString+Utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSString+Utilities.h"; sourceTree = "<group>"; };
+ 8BC04CD20DB003BE00C2D1CA /* GTMNSString+Utilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+Utilities.m"; sourceTree = "<group>"; };
+ 8BC04CD50DB003C300C2D1CA /* GTMNSString+UtilitiesTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+UtilitiesTest.m"; sourceTree = "<group>"; };
+ 8BC04D140DB0061300C2D1CA /* RunMacOSUnitTests.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = RunMacOSUnitTests.sh; sourceTree = "<group>"; };
+ 8BEEA90A0DA7446300894774 /* GTMUnitTestingImage.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = GTMUnitTestingImage.tiff; sourceTree = "<group>"; };
+ 8BEEA90B0DA7446300894774 /* GTMUnitTestingWindow.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = GTMUnitTestingWindow.tiff; sourceTree = "<group>"; };
+ 8BEEA90C0DA7446300894774 /* GTMUnitTestingView.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = GTMUnitTestingView.tiff; sourceTree = "<group>"; };
F413908C0D75F63C00F72B31 /* GTMNSFileManager+Path.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSFileManager+Path.h"; sourceTree = "<group>"; };
F413908D0D75F63C00F72B31 /* GTMNSFileManager+Path.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSFileManager+Path.m"; sourceTree = "<group>"; };
F413908E0D75F63C00F72B31 /* GTMNSFileManager+PathTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSFileManager+PathTest.m"; sourceTree = "<group>"; };
F428FEFF0D48E55E00382ED1 /* GTMNSBezierPath+CGPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSBezierPath+CGPath.h"; sourceTree = "<group>"; };
F428FF000D48E55E00382ED1 /* GTMNSBezierPath+CGPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSBezierPath+CGPath.m"; sourceTree = "<group>"; };
F428FF010D48E55E00382ED1 /* GTMNSBezierPath+CGPathTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSBezierPath+CGPathTest.m"; sourceTree = "<group>"; };
- F428FF020D48E55E00382ED1 /* GTMNSBezierPath+CGPathTest.tif */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMNSBezierPath+CGPathTest.tif"; sourceTree = "<group>"; };
F42E08210D19987200D5DDE0 /* UnitTest - Foundation.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "UnitTest - Foundation.octest"; sourceTree = BUILT_PRODUCTS_DIR; };
- F42E086D0D199A5B00D5DDE0 /* GTM.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GTM.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ F42E086D0D199A5B00D5DDE0 /* GoogleToolboxForMac.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GoogleToolboxForMac.framework; sourceTree = BUILT_PRODUCTS_DIR; };
F42E086E0D199A5B00D5DDE0 /* GTM-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = "GTM-Info.plist"; sourceTree = "<group>"; };
F42E089B0D199B1800D5DDE0 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = /System/Library/Frameworks/SenTestingKit.framework; sourceTree = "<absolute>"; };
F42E09A80D19A5E300D5DDE0 /* GTMNSWorkspace+Theme.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSWorkspace+Theme.h"; sourceTree = "<group>"; };
@@ -180,8 +271,6 @@
F47A79870D746EE9002302AB /* GTMScriptRunnerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMScriptRunnerTest.m; sourceTree = "<group>"; };
F47F1C0D0D490BC000925B8F /* GTMNSBezierPath+Shading.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSBezierPath+Shading.h"; sourceTree = "<group>"; };
F47F1C0E0D490BC000925B8F /* GTMNSBezierPath+Shading.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSBezierPath+Shading.m"; sourceTree = "<group>"; };
- F47F1C0F0D490BC000925B8F /* GTMNSBezierPath+ShadingTest.10.4.tif */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMNSBezierPath+ShadingTest.10.4.tif"; sourceTree = "<group>"; };
- F47F1C100D490BC000925B8F /* GTMNSBezierPath+ShadingTest.10.5.tif */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMNSBezierPath+ShadingTest.10.5.tif"; sourceTree = "<group>"; };
F47F1C110D490BC000925B8F /* GTMNSBezierPath+ShadingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSBezierPath+ShadingTest.m"; sourceTree = "<group>"; };
F47F1C740D490E5C00925B8F /* GTMShading.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMShading.h; sourceTree = "<group>"; };
F47F1CAC0D4910FD00925B8F /* GTMNSColor+Theme.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSColor+Theme.h"; sourceTree = "<group>"; };
@@ -190,6 +279,7 @@
F47F1D2D0D4914AD00925B8F /* GTMCalculatedRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMCalculatedRange.h; sourceTree = "<group>"; };
F47F1D2E0D4914AD00925B8F /* GTMCalculatedRange.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMCalculatedRange.m; sourceTree = "<group>"; };
F47F1D2F0D4914AD00925B8F /* GTMCalculatedRangeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMCalculatedRangeTest.m; sourceTree = "<group>"; };
+ F48B91040D94487D00D45044 /* libgcov_readme.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = libgcov_readme.html; sourceTree = "<group>"; };
F48FE2410D197F9A009257D2 /* DebugTigerOrLater.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = DebugTigerOrLater.xcconfig; sourceTree = "<group>"; };
F48FE2420D197F9A009257D2 /* DebugUnittest.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = DebugUnittest.xcconfig; sourceTree = "<group>"; };
F48FE2430D197F9A009257D2 /* LoadableBundle.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = LoadableBundle.xcconfig; sourceTree = "<group>"; };
@@ -207,7 +297,6 @@
F48FE2810D198D0E009257D2 /* GTMNSBezierPath+RoundRect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSBezierPath+RoundRect.h"; sourceTree = "<group>"; };
F48FE2820D198D0E009257D2 /* GTMNSBezierPath+RoundRect.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSBezierPath+RoundRect.m"; sourceTree = "<group>"; };
F48FE2830D198D0E009257D2 /* GTMNSBezierPath+RoundRectTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSBezierPath+RoundRectTest.m"; sourceTree = "<group>"; };
- F48FE2840D198D0E009257D2 /* GTMNSBezierPath+RoundRectTest.tif */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMNSBezierPath+RoundRectTest.tif"; sourceTree = "<group>"; };
F48FE28D0D198D24009257D2 /* GTMGarbageCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMGarbageCollection.h; sourceTree = "<group>"; };
F48FE28E0D198D24009257D2 /* GTMNSString+HTML.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSString+HTML.h"; sourceTree = "<group>"; };
F48FE28F0D198D24009257D2 /* GTMNSString+HTML.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+HTML.m"; sourceTree = "<group>"; };
@@ -217,21 +306,42 @@
F48FE2930D198D24009257D2 /* GTMSystemVersion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMSystemVersion.m; sourceTree = "<group>"; };
F48FE29B0D198D36009257D2 /* GTMNSObject+UnitTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSObject+UnitTesting.h"; sourceTree = "<group>"; };
F48FE29C0D198D36009257D2 /* GTMNSObject+UnitTesting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSObject+UnitTesting.m"; sourceTree = "<group>"; };
- F48FE29D0D198D36009257D2 /* GTMNSView+UnitTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSView+UnitTesting.h"; sourceTree = "<group>"; };
- F48FE29E0D198D36009257D2 /* GTMNSView+UnitTesting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSView+UnitTesting.m"; sourceTree = "<group>"; };
F48FE29F0D198D36009257D2 /* GTMSenTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMSenTestCase.h; sourceTree = "<group>"; };
F48FE2E10D198E4C009257D2 /* GTMSystemVersionTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMSystemVersionTest.m; sourceTree = "<group>"; };
F4C978090D5B79C7001C29A6 /* ReleaseNotes.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ReleaseNotes.txt; sourceTree = "<group>"; };
+ F4CA854E0DAFAAB600B4AB10 /* xcconfigs-readme.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "xcconfigs-readme.txt"; sourceTree = "<group>"; };
+ F4FF22770D9D4835003880AC /* GTMDebugSelectorValidation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMDebugSelectorValidation.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
+ 8B45A0250DA4696C001148C5 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8B45A2070DA46DFC001148C5 /* ScreenSaver.framework in Frameworks */,
+ 8B45A2040DA46DF6001148C5 /* Cocoa.framework in Frameworks */,
+ 8B45A0B80DA46A2F001148C5 /* SenTestingKit.framework in Frameworks */,
+ 8B45A03A0DA46A2A001148C5 /* Foundation.framework in Frameworks */,
+ 8B45A19A0DA46AAA001148C5 /* QuartzCore.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 8B45A2880DA49B99001148C5 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8B45A2B30DA49CA9001148C5 /* Cocoa.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
F42E081C0D19987200D5DDE0 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
F42E08640D199A2F00D5DDE0 /* Foundation.framework in Frameworks */,
- F42E08800D199AB500D5DDE0 /* GTM.framework in Frameworks */,
+ F42E08800D199AB500D5DDE0 /* GoogleToolboxForMac.framework in Frameworks */,
F42E089D0D199B1800D5DDE0 /* SenTestingKit.framework in Frameworks */,
+ 8BC046B90DAE8C4B00C2D1CA /* ApplicationServices.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -251,7 +361,7 @@
buildActionMask = 2147483647;
files = (
F42E08610D199A2B00D5DDE0 /* Cocoa.framework in Frameworks */,
- F42E087F0D199AB400D5DDE0 /* GTM.framework in Frameworks */,
+ F42E087F0D199AB400D5DDE0 /* GoogleToolboxForMac.framework in Frameworks */,
F42E089C0D199B1800D5DDE0 /* SenTestingKit.framework in Frameworks */,
8B2A9BED0D82714A00599386 /* ScreenSaver.framework in Frameworks */,
);
@@ -265,7 +375,9 @@
children = (
F48FE2640D198C1E009257D2 /* UnitTest - AppKit.octest */,
F42E08210D19987200D5DDE0 /* UnitTest - Foundation.octest */,
- F42E086D0D199A5B00D5DDE0 /* GTM.framework */,
+ F42E086D0D199A5B00D5DDE0 /* GoogleToolboxForMac.framework */,
+ 8B45A0280DA4696C001148C5 /* UnitTest - UnitTesting.octest */,
+ 8B45A28A0DA49B99001148C5 /* GTMUIUnitTestingHarness.app */,
);
name = Products;
sourceTree = "<group>";
@@ -273,15 +385,17 @@
0867D691FE84028FC02AAC07 /* GTM */ = {
isa = PBXGroup;
children = (
- 32DBCF5E0370ADEE00C91783 /* GTM_Prefix.pch */,
F4C978090D5B79C7001C29A6 /* ReleaseNotes.txt */,
+ 8B1A16050D90344B00CA1E8E /* GTMDefines.h */,
F48FE26F0D198CBA009257D2 /* AppKit */,
F48FE2720D198CCE009257D2 /* Foundation */,
+ F4FF22760D9D47FB003880AC /* DebugUtils */,
F48FE2770D198CEA009257D2 /* UnitTesting */,
F48FE23E0D197F70009257D2 /* XcodeConfig */,
+ F48B91030D94485500D45044 /* TigerGcov */,
0867D69AFE84028FC02AAC07 /* External Frameworks and Libraries */,
- F43E4F6C0D4E60C50041161F /* libz.dylib */,
034768DFFF38A50411DB9C8B /* Products */,
+ 32DBCF5E0370ADEE00C91783 /* GTM_Prefix.pch */,
F42E086E0D199A5B00D5DDE0 /* GTM-Info.plist */,
F48FE26E0D198CAD009257D2 /* UnitTest-Info.plist */,
);
@@ -291,26 +405,43 @@
0867D69AFE84028FC02AAC07 /* External Frameworks and Libraries */ = {
isa = PBXGroup;
children = (
+ 8B45A1990DA46AAA001148C5 /* QuartzCore.framework */,
+ F43E4F6C0D4E60C50041161F /* libz.dylib */,
8B2A9BEB0D82714A00599386 /* ScreenSaver.framework */,
0867D6A5FE840307C02AAC07 /* AppKit.framework */,
F42E09AD0D19A62F00D5DDE0 /* Carbon.framework */,
1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */,
0867D69BFE84028FC02AAC07 /* Foundation.framework */,
+ 8BC046B80DAE8C4B00C2D1CA /* ApplicationServices.framework */,
F42E089B0D199B1800D5DDE0 /* SenTestingKit.framework */,
);
name = "External Frameworks and Libraries";
sourceTree = "<group>";
};
+ 8B45A2A20DA49C47001148C5 /* GTMUIUnitTestingHarness */ = {
+ isa = PBXGroup;
+ children = (
+ 8B45A2A50DA49C47001148C5 /* MainMenu.nib */,
+ 8B45A2A70DA49C47001148C5 /* Info.plist */,
+ 8B45A2A80DA49C47001148C5 /* main.m */,
+ );
+ path = GTMUIUnitTestingHarness;
+ sourceTree = "<group>";
+ };
+ F48B91030D94485500D45044 /* TigerGcov */ = {
+ isa = PBXGroup;
+ children = (
+ F48B91040D94487D00D45044 /* libgcov_readme.html */,
+ );
+ path = TigerGcov;
+ sourceTree = "<group>";
+ };
F48FE23E0D197F70009257D2 /* XcodeConfig */ = {
isa = PBXGroup;
children = (
- F48FE2410D197F9A009257D2 /* DebugTigerOrLater.xcconfig */,
- F48FE2420D197F9A009257D2 /* DebugUnittest.xcconfig */,
- F48FE2430D197F9A009257D2 /* LoadableBundle.xcconfig */,
- F48FE2440D197F9A009257D2 /* ReleaseTigerOrLater.xcconfig */,
- F48FE2450D197F9A009257D2 /* ReleaseUnittest.xcconfig */,
- F48FE2460D197F9A009257D2 /* SharedLibrary.xcconfig */,
- F48FE2470D197F9A009257D2 /* StaticLibrary.xcconfig */,
+ F4CA854E0DAFAAB600B4AB10 /* xcconfigs-readme.txt */,
+ F4CA852B0DAFA92A00B4AB10 /* Project */,
+ F4CA852E0DAFA92F00B4AB10 /* Target */,
);
path = XcodeConfig;
sourceTree = "<group>";
@@ -320,9 +451,6 @@
children = (
F48FE27C0D198D0E009257D2 /* GTMDelegatingTableColumn.h */,
F48FE27D0D198D0E009257D2 /* GTMDelegatingTableColumn.m */,
- F48FE27E0D198D0E009257D2 /* GTMGeometryUtils.h */,
- F48FE27F0D198D0E009257D2 /* GTMGeometryUtils.m */,
- F48FE2800D198D0E009257D2 /* GTMGeometryUtilsTest.m */,
F43E44770D4918B20041161F /* GTMLinearRGBShading.h */,
F43E44780D4918B20041161F /* GTMLinearRGBShading.m */,
F43E44790D4918B20041161F /* GTMLinearRGBShadingTest.m */,
@@ -332,15 +460,14 @@
F428FEFF0D48E55E00382ED1 /* GTMNSBezierPath+CGPath.h */,
F428FF000D48E55E00382ED1 /* GTMNSBezierPath+CGPath.m */,
F428FF010D48E55E00382ED1 /* GTMNSBezierPath+CGPathTest.m */,
- F428FF020D48E55E00382ED1 /* GTMNSBezierPath+CGPathTest.tif */,
+ 8B7AD4980DABBB5800B84F4A /* GTMNSBezierPath+CGPathTest.tiff */,
F48FE2810D198D0E009257D2 /* GTMNSBezierPath+RoundRect.h */,
F48FE2820D198D0E009257D2 /* GTMNSBezierPath+RoundRect.m */,
F48FE2830D198D0E009257D2 /* GTMNSBezierPath+RoundRectTest.m */,
- F48FE2840D198D0E009257D2 /* GTMNSBezierPath+RoundRectTest.tif */,
+ 8B7AD4990DABBB5800B84F4A /* GTMNSBezierPath+RoundRectTest.tiff */,
F47F1C0D0D490BC000925B8F /* GTMNSBezierPath+Shading.h */,
F47F1C0E0D490BC000925B8F /* GTMNSBezierPath+Shading.m */,
- F47F1C0F0D490BC000925B8F /* GTMNSBezierPath+ShadingTest.10.4.tif */,
- F47F1C100D490BC000925B8F /* GTMNSBezierPath+ShadingTest.10.5.tif */,
+ 8B7AD4970DABBB5800B84F4A /* GTMNSBezierPath+ShadingTest.10.5.tiff */,
F47F1C110D490BC000925B8F /* GTMNSBezierPath+ShadingTest.m */,
F47F1CAC0D4910FD00925B8F /* GTMNSColor+Theme.h */,
F47F1CAD0D4910FD00925B8F /* GTMNSColor+Theme.m */,
@@ -359,10 +486,16 @@
F48FE2720D198CCE009257D2 /* Foundation */ = {
isa = PBXGroup;
children = (
+ F48FE27E0D198D0E009257D2 /* GTMGeometryUtils.h */,
+ F48FE27F0D198D0E009257D2 /* GTMGeometryUtils.m */,
+ F48FE2800D198D0E009257D2 /* GTMGeometryUtilsTest.m */,
F47F1D2D0D4914AD00925B8F /* GTMCalculatedRange.h */,
F47F1D2E0D4914AD00925B8F /* GTMCalculatedRange.m */,
F47F1D2F0D4914AD00925B8F /* GTMCalculatedRangeTest.m */,
F48FE28D0D198D24009257D2 /* GTMGarbageCollection.h */,
+ 8B6F32040DA34A1B0052CA40 /* GTMObjC2Runtime.m */,
+ 8B6F32050DA34A1B0052CA40 /* GTMObjC2RuntimeTest.m */,
+ 8B6F32060DA34A1B0052CA40 /* GTMObjC2Runtime.h */,
F43E4DD60D4E56320041161F /* GTMNSEnumerator+Filter.h */,
F43E4DD70D4E56320041161F /* GTMNSEnumerator+Filter.m */,
F43E4DD80D4E56320041161F /* GTMNSEnumerator+FilterTest.m */,
@@ -375,6 +508,9 @@
F43E4C250D4E361D0041161F /* GTMNSString+XML.h */,
F43E4C260D4E361D0041161F /* GTMNSString+XML.m */,
F43E4C270D4E361D0041161F /* GTMNSString+XMLTest.m */,
+ 8BC04CD10DB003BE00C2D1CA /* GTMNSString+Utilities.h */,
+ 8BC04CD20DB003BE00C2D1CA /* GTMNSString+Utilities.m */,
+ 8BC04CD50DB003C300C2D1CA /* GTMNSString+UtilitiesTest.m */,
F43E4E5E0D4E5EC90041161F /* GTMNSData+zlib.h */,
F43E4E5F0D4E5EC90041161F /* GTMNSData+zlib.m */,
F43E4E600D4E5EC90041161F /* GTMNSData+zlibTest.m */,
@@ -395,15 +531,65 @@
F48FE2770D198CEA009257D2 /* UnitTesting */ = {
isa = PBXGroup;
children = (
+ 8BC04D140DB0061300C2D1CA /* RunMacOSUnitTests.sh */,
+ 8B55479A0DB3B7A50014CC1C /* GTMAppKit+UnitTesting.h */,
+ 8B55479B0DB3B7A50014CC1C /* GTMAppKit+UnitTesting.m */,
+ 8B726BD00D91C0860090C251 /* GTMCALayer+UnitTesting.m */,
+ 8B726BD10D91C0860090C251 /* GTMCALayer+UnitTesting.h */,
F48FE29B0D198D36009257D2 /* GTMNSObject+UnitTesting.h */,
F48FE29C0D198D36009257D2 /* GTMNSObject+UnitTesting.m */,
- F48FE29D0D198D36009257D2 /* GTMNSView+UnitTesting.h */,
- F48FE29E0D198D36009257D2 /* GTMNSView+UnitTesting.m */,
+ 8B1A14E90D900BC800CA1E8E /* GTMNSObject+BindingUnitTesting.m */,
+ 8B1A14EA0D900BC800CA1E8E /* GTMNSObject+BindingUnitTesting.h */,
+ 8B45A2670DA498A0001148C5 /* GTMUnitTestingUtilities.h */,
+ 8B45A2680DA498A0001148C5 /* GTMUnitTestingUtilities.m */,
F48FE29F0D198D36009257D2 /* GTMSenTestCase.h */,
+ 8B45A2E00DA51ABC001148C5 /* GTMUnitTestingTest.h */,
+ 8B45A2E10DA51ABC001148C5 /* GTMUnitTestingTest.m */,
+ 8B7AD4AD0DABBFEE00B84F4A /* GTMUnitTestingBindingTest.m */,
+ 8B45A2DE0DA51A7E001148C5 /* GTMUnitTestingTest.nib */,
+ 8B45A6B90DA67DD5001148C5 /* GTMUnitTestingImage.gtmUTState */,
+ 8B45A5F50DA5EB9F001148C5 /* GTMUnitTestingWindow.gtmUTState */,
+ 8B45A5F60DA5EB9F001148C5 /* GTMUnitTestingTestApp.gtmUTState */,
+ 8BEEA90A0DA7446300894774 /* GTMUnitTestingImage.tiff */,
+ 8BEEA90B0DA7446300894774 /* GTMUnitTestingWindow.tiff */,
+ 8BEEA90C0DA7446300894774 /* GTMUnitTestingView.tiff */,
+ 8B45A2A20DA49C47001148C5 /* GTMUIUnitTestingHarness */,
);
path = UnitTesting;
sourceTree = "<group>";
};
+ F4CA852B0DAFA92A00B4AB10 /* Project */ = {
+ isa = PBXGroup;
+ children = (
+ F48FE2410D197F9A009257D2 /* DebugTigerOrLater.xcconfig */,
+ F48FE2440D197F9A009257D2 /* ReleaseTigerOrLater.xcconfig */,
+ );
+ path = Project;
+ sourceTree = "<group>";
+ };
+ F4CA852E0DAFA92F00B4AB10 /* Target */ = {
+ isa = PBXGroup;
+ children = (
+ F48FE2420D197F9A009257D2 /* DebugUnittest.xcconfig */,
+ F48FE2430D197F9A009257D2 /* LoadableBundle.xcconfig */,
+ F48FE2450D197F9A009257D2 /* ReleaseUnittest.xcconfig */,
+ F48FE2460D197F9A009257D2 /* SharedLibrary.xcconfig */,
+ F48FE2470D197F9A009257D2 /* StaticLibrary.xcconfig */,
+ );
+ path = Target;
+ sourceTree = "<group>";
+ };
+ F4FF22760D9D47FB003880AC /* DebugUtils */ = {
+ isa = PBXGroup;
+ children = (
+ F4FF22770D9D4835003880AC /* GTMDebugSelectorValidation.h */,
+ 8B6F31EF0DA347720052CA40 /* GTMMethodCheck.m */,
+ 8B6F31F40DA3489B0052CA40 /* GTMMethodCheck.h */,
+ 8B6F31F10DA347720052CA40 /* GTMMethodCheckTest.m */,
+ );
+ path = DebugUtils;
+ sourceTree = "<group>";
+ };
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
@@ -433,12 +619,53 @@
F47A79880D746EE9002302AB /* GTMScriptRunner.h in Headers */,
F413908F0D75F63C00F72B31 /* GTMNSFileManager+Path.h in Headers */,
8B2A9B220D8270DA00599386 /* GTMNSWorkspace+ScreenSaver.h in Headers */,
+ F424F75F0D9AF019000B87EF /* GTMDefines.h in Headers */,
+ F4FF22780D9D4835003880AC /* GTMDebugSelectorValidation.h in Headers */,
+ F4CA854F0DAFAAF600B4AB10 /* GTMObjC2Runtime.h in Headers */,
+ 8BC04CD30DB003BE00C2D1CA /* GTMNSString+Utilities.h in Headers */,
+ 8BC04CD80DB003D800C2D1CA /* GTMMethodCheck.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
+ 8B45A0270DA4696C001148C5 /* UnitTest - UnitTesting */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 8B45A02D0DA4696D001148C5 /* Build configuration list for PBXNativeTarget "UnitTest - UnitTesting" */;
+ buildPhases = (
+ 8B45A0230DA4696C001148C5 /* Resources */,
+ 8B45A0240DA4696C001148C5 /* Sources */,
+ 8B45A0250DA4696C001148C5 /* Frameworks */,
+ 8B45A0260DA4696C001148C5 /* ShellScript */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 8B45A2D40DA51A0E001148C5 /* PBXTargetDependency */,
+ );
+ name = "UnitTest - UnitTesting";
+ productName = "UnitTest - UnitTesting";
+ productReference = 8B45A0280DA4696C001148C5 /* UnitTest - UnitTesting.octest */;
+ productType = "com.apple.product-type.bundle";
+ };
+ 8B45A2890DA49B99001148C5 /* UIUnitTestingHarness */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 8B45A2910DA49B9A001148C5 /* Build configuration list for PBXNativeTarget "UIUnitTestingHarness" */;
+ buildPhases = (
+ 8B45A2860DA49B99001148C5 /* Resources */,
+ 8B45A2870DA49B99001148C5 /* Sources */,
+ 8B45A2880DA49B99001148C5 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = UIUnitTestingHarness;
+ productName = UnitTestingTestHarness;
+ productReference = 8B45A28A0DA49B99001148C5 /* GTMUIUnitTestingHarness.app */;
+ productType = "com.apple.product-type.application";
+ };
F42E08110D19987200D5DDE0 /* UnitTest - Foundation */ = {
isa = PBXNativeTarget;
buildConfigurationList = F42E081E0D19987200D5DDE0 /* Build configuration list for PBXNativeTarget "UnitTest - Foundation" */;
@@ -473,7 +700,7 @@
);
name = GTM;
productName = GTM;
- productReference = F42E086D0D199A5B00D5DDE0 /* GTM.framework */;
+ productReference = F42E086D0D199A5B00D5DDE0 /* GoogleToolboxForMac.framework */;
productType = "com.apple.product-type.framework";
};
F48FE2630D198C1E009257D2 /* UnitTest - AppKit */ = {
@@ -512,11 +739,35 @@
F472042B0D33BEAF00E9F378 /* All UnitTests */,
F48FE2630D198C1E009257D2 /* UnitTest - AppKit */,
F42E08110D19987200D5DDE0 /* UnitTest - Foundation */,
+ 8B45A0270DA4696C001148C5 /* UnitTest - UnitTesting */,
+ 8B45A2890DA49B99001148C5 /* UIUnitTestingHarness */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
+ 8B45A0230DA4696C001148C5 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8B45A2DF0DA51A7E001148C5 /* GTMUnitTestingTest.nib in Resources */,
+ 8B45A5F80DA5EB9F001148C5 /* GTMUnitTestingWindow.gtmUTState in Resources */,
+ 8B45A5F90DA5EB9F001148C5 /* GTMUnitTestingTestApp.gtmUTState in Resources */,
+ 8B45A6BB0DA67DD5001148C5 /* GTMUnitTestingImage.gtmUTState in Resources */,
+ 8BEEA90D0DA7446300894774 /* GTMUnitTestingImage.tiff in Resources */,
+ 8BEEA90E0DA7446300894774 /* GTMUnitTestingWindow.tiff in Resources */,
+ 8BEEA90F0DA7446300894774 /* GTMUnitTestingView.tiff in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 8B45A2860DA49B99001148C5 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8B45A2AA0DA49C47001148C5 /* MainMenu.nib in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
F42E08140D19987200D5DDE0 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -535,16 +786,28 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- F42E08300D19991400D5DDE0 /* GTMNSBezierPath+RoundRectTest.tif in Resources */,
- F428FF0A0D48E57300382ED1 /* GTMNSBezierPath+CGPathTest.tif in Resources */,
- F47F1C190D490BD200925B8F /* GTMNSBezierPath+ShadingTest.10.4.tif in Resources */,
- F47F1C1A0D490BD200925B8F /* GTMNSBezierPath+ShadingTest.10.5.tif in Resources */,
+ 8B7AD49A0DABBB5800B84F4A /* GTMNSBezierPath+ShadingTest.10.5.tiff in Resources */,
+ 8B7AD49B0DABBB5800B84F4A /* GTMNSBezierPath+CGPathTest.tiff in Resources */,
+ 8B7AD49C0DABBB5800B84F4A /* GTMNSBezierPath+RoundRectTest.tiff in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
+ 8B45A0260DA4696C001148C5 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "# Run the unit tests in this test bundle.\n\"${SRCROOT}/UnitTesting/RunMacOSUnitTests.sh\"";
+ };
F42E081D0D19987200D5DDE0 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -556,7 +819,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n";
+ shellScript = "# Run the unit tests in this test bundle.\n\"${SRCROOT}/UnitTesting/RunMacOSUnitTests.sh\"";
};
F48FE2620D198C1E009257D2 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
@@ -569,11 +832,36 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n";
+ shellScript = "# Run the unit tests in this test bundle.\n\"${SRCROOT}/UnitTesting/RunMacOSUnitTests.sh\"\n";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
+ 8B45A0240DA4696C001148C5 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8B45A0D50DA46A57001148C5 /* GTMNSObject+UnitTesting.m in Sources */,
+ 8B45A0D60DA46A57001148C5 /* GTMNSObject+BindingUnitTesting.m in Sources */,
+ 8B45A21A0DA46E1D001148C5 /* GTMGeometryUtils.m in Sources */,
+ 8B45A21D0DA46E2C001148C5 /* GTMObjC2Runtime.m in Sources */,
+ 8B45A22C0DA46E51001148C5 /* GTMSystemVersion.m in Sources */,
+ 8B45A2E20DA51ABC001148C5 /* GTMUnitTestingTest.m in Sources */,
+ 8B7AD4AE0DABBFEE00B84F4A /* GTMUnitTestingBindingTest.m in Sources */,
+ 8BC04CDF0DB004A100C2D1CA /* GTMMethodCheck.m in Sources */,
+ 8B55479C0DB3B7A50014CC1C /* GTMAppKit+UnitTesting.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 8B45A2870DA49B99001148C5 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8B45A2AC0DA49C47001148C5 /* main.m in Sources */,
+ 8B45A2D00DA51A01001148C5 /* GTMUnitTestingUtilities.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
F42E08160D19987200D5DDE0 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -586,6 +874,12 @@
F437F5620D50BC1D00F5C3A4 /* GTMRegexTest.m in Sources */,
F47A798B0D746EFC002302AB /* GTMScriptRunnerTest.m in Sources */,
F41390920D75F64D00F72B31 /* GTMNSFileManager+PathTest.m in Sources */,
+ F424F7010D9AA02B000B87EF /* GTMNSData+zlibTest.m in Sources */,
+ 8B6F32080DA34A1B0052CA40 /* GTMObjC2RuntimeTest.m in Sources */,
+ 8B6F32160DA34C830052CA40 /* GTMMethodCheckTest.m in Sources */,
+ 8BC045C20DAE899100C2D1CA /* GTMGeometryUtilsTest.m in Sources */,
+ 8BC04CD60DB003C300C2D1CA /* GTMNSString+UtilitiesTest.m in Sources */,
+ 8BC04CDE0DB004A000C2D1CA /* GTMMethodCheck.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -612,6 +906,9 @@
F47A79890D746EE9002302AB /* GTMScriptRunner.m in Sources */,
F41390900D75F63C00F72B31 /* GTMNSFileManager+Path.m in Sources */,
8B2A9B200D8270DA00599386 /* GTMNSWorkspace+ScreenSaver.m in Sources */,
+ 8B45A21E0DA46E34001148C5 /* GTMObjC2Runtime.m in Sources */,
+ 8BC04CD40DB003BE00C2D1CA /* GTMNSString+Utilities.m in Sources */,
+ 8BC04CD70DB003CD00C2D1CA /* GTMMethodCheck.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -619,25 +916,34 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- F42E082E0D19991400D5DDE0 /* GTMGeometryUtilsTest.m in Sources */,
F42E082F0D19991400D5DDE0 /* GTMNSBezierPath+RoundRectTest.m in Sources */,
F42E09450D199BA400D5DDE0 /* GTMNSObject+UnitTesting.m in Sources */,
- F42E09460D199BA400D5DDE0 /* GTMNSView+UnitTesting.m in Sources */,
F42E0B0A0D19A6FB00D5DDE0 /* GTMNSWorkspace+ThemeTest.m in Sources */,
F43DCEC70D47BEA000959A62 /* GTMLoginItemsTest.m in Sources */,
F428FF090D48E57300382ED1 /* GTMNSBezierPath+CGPathTest.m in Sources */,
F47F1C1B0D490BD200925B8F /* GTMNSBezierPath+ShadingTest.m in Sources */,
F47F1CB60D49110900925B8F /* GTMNSColor+ThemeTest.m in Sources */,
F43E447F0D4918BC0041161F /* GTMLinearRGBShadingTest.m in Sources */,
- F43E4E660D4E5ED40041161F /* GTMNSData+zlibTest.m in Sources */,
8B2A9B230D8270DA00599386 /* GTMNSWorkspace+ScreenSaver.m in Sources */,
8B2A9B240D8270DA00599386 /* GTMNSWorkspace+ScreenSaverTest.m in Sources */,
+ 8BC04CDD0DB004A000C2D1CA /* GTMMethodCheck.m in Sources */,
+ 8B5547B90DB3BB220014CC1C /* GTMAppKit+UnitTesting.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
+ 8B45A0390DA46A20001148C5 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 8B45A0270DA4696C001148C5 /* UnitTest - UnitTesting */;
+ targetProxy = 8B45A0380DA46A20001148C5 /* PBXContainerItemProxy */;
+ };
+ 8B45A2D40DA51A0E001148C5 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 8B45A2890DA49B99001148C5 /* UIUnitTestingHarness */;
+ targetProxy = 8B45A2D30DA51A0E001148C5 /* PBXContainerItemProxy */;
+ };
F42E08770D199A9B00D5DDE0 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = F42E086C0D199A5B00D5DDE0 /* GTM */;
@@ -660,6 +966,17 @@
};
/* End PBXTargetDependency section */
+/* Begin PBXVariantGroup section */
+ 8B45A2A50DA49C47001148C5 /* MainMenu.nib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 8B45A2A60DA49C47001148C5 /* English */,
+ );
+ name = MainMenu.nib;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
/* Begin XCBuildConfiguration section */
1DEB918208733D990010E9CD /* Debug */ = {
isa = XCBuildConfiguration;
@@ -679,6 +996,72 @@
};
name = Release;
};
+ 8B45A02A0DA4696D001148C5 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F48FE2420D197F9A009257D2 /* DebugUnittest.xcconfig */;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(value)",
+ "$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)",
+ );
+ INFOPLIST_FILE = "UnitTest-Info.plist";
+ PRODUCT_NAME = "UnitTest - UnitTesting";
+ TEST_HOST = "$(ACTION)/$(CONFIGURATION)/GTMUIUnitTestingHarness.app/Contents/MacOS/GTMUIUnitTestingHarness";
+ };
+ name = Debug;
+ };
+ 8B45A02B0DA4696D001148C5 /* Debug-gcov */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F48FE2420D197F9A009257D2 /* DebugUnittest.xcconfig */;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(value)",
+ "$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)",
+ );
+ INFOPLIST_FILE = "UnitTest-Info.plist";
+ PRODUCT_NAME = "UnitTest - UnitTesting";
+ TEST_HOST = "$(ACTION)/$(CONFIGURATION)/GTMUIUnitTestingHarness.app/Contents/MacOS/GTMUIUnitTestingHarness";
+ };
+ name = "Debug-gcov";
+ };
+ 8B45A02C0DA4696D001148C5 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F48FE2450D197F9A009257D2 /* ReleaseUnittest.xcconfig */;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(value)",
+ "$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)",
+ );
+ INFOPLIST_FILE = "UnitTest-Info.plist";
+ PRODUCT_NAME = "UnitTest - UnitTesting";
+ TEST_HOST = "$(ACTION)/$(CONFIGURATION)/GTMUIUnitTestingHarness.app/Contents/MacOS/GTMUIUnitTestingHarness";
+ };
+ name = Release;
+ };
+ 8B45A28E0DA49B99001148C5 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INFOPLIST_FILE = UnitTesting/GTMUIUnitTestingHarness/Info.plist;
+ PRODUCT_NAME = GTMUIUnitTestingHarness;
+ };
+ name = Debug;
+ };
+ 8B45A28F0DA49B99001148C5 /* Debug-gcov */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INFOPLIST_FILE = UnitTesting/GTMUIUnitTestingHarness/Info.plist;
+ PRODUCT_NAME = GTMUIUnitTestingHarness;
+ };
+ name = "Debug-gcov";
+ };
+ 8B45A2900DA49B99001148C5 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INFOPLIST_FILE = UnitTesting/GTMUIUnitTestingHarness/Info.plist;
+ PRODUCT_NAME = GTMUIUnitTestingHarness;
+ };
+ name = Release;
+ };
F42E081F0D19987200D5DDE0 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = F48FE2420D197F9A009257D2 /* DebugUnittest.xcconfig */;
@@ -686,13 +1069,9 @@
FRAMEWORK_SEARCH_PATHS = (
"$(value)",
"$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)",
- "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1)",
);
- FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1 = "\"$(DEVELOPER_FRAMEWORKS_DIR)\"";
- GCC_PREFIX_HEADER = GTM_Prefix.pch;
INFOPLIST_FILE = "UnitTest-Info.plist";
PRODUCT_NAME = "UnitTest - Foundation";
- WRAPPER_EXTENSION = octest;
};
name = Debug;
};
@@ -703,13 +1082,9 @@
FRAMEWORK_SEARCH_PATHS = (
"$(value)",
"$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)",
- "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1)",
);
- FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1 = "\"$(DEVELOPER_FRAMEWORKS_DIR)\"";
- GCC_PREFIX_HEADER = GTM_Prefix.pch;
INFOPLIST_FILE = "UnitTest-Info.plist";
PRODUCT_NAME = "UnitTest - Foundation";
- WRAPPER_EXTENSION = octest;
};
name = Release;
};
@@ -722,7 +1097,7 @@
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = "GTM-Info.plist";
INSTALL_PATH = "@loader_path/../Frameworks";
- PRODUCT_NAME = GTM;
+ PRODUCT_NAME = GoogleToolboxForMac;
};
name = Debug;
};
@@ -735,7 +1110,7 @@
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = "GTM-Info.plist";
INSTALL_PATH = "@loader_path/../Frameworks";
- PRODUCT_NAME = GTM;
+ PRODUCT_NAME = GoogleToolboxForMac;
};
name = Release;
};
@@ -761,6 +1136,72 @@
};
name = Release;
};
+ F48B91060D94489300D45044 /* Debug-gcov */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F48FE2410D197F9A009257D2 /* DebugTigerOrLater.xcconfig */;
+ buildSettings = {
+ GCC_GENERATE_TEST_COVERAGE_FILES = YES;
+ GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = GTM_Prefix.pch;
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(SRCROOT)/TigerGcov",
+ );
+ OTHER_LDFLAGS = "-lgcov";
+ };
+ name = "Debug-gcov";
+ };
+ F48B91070D94489300D45044 /* Debug-gcov */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F48FE2430D197F9A009257D2 /* LoadableBundle.xcconfig */;
+ buildSettings = {
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ FRAMEWORK_VERSION = A;
+ INFOPLIST_FILE = "GTM-Info.plist";
+ INSTALL_PATH = "@loader_path/../Frameworks";
+ PRODUCT_NAME = GoogleToolboxForMac;
+ };
+ name = "Debug-gcov";
+ };
+ F48B91080D94489300D45044 /* Debug-gcov */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ PRODUCT_NAME = "All UnitTests";
+ };
+ name = "Debug-gcov";
+ };
+ F48B91090D94489300D45044 /* Debug-gcov */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F48FE2420D197F9A009257D2 /* DebugUnittest.xcconfig */;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(value)",
+ "$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)",
+ );
+ INFOPLIST_FILE = "UnitTest-Info.plist";
+ PRODUCT_NAME = "UnitTest - AppKit";
+ };
+ name = "Debug-gcov";
+ };
+ F48B910A0D94489300D45044 /* Debug-gcov */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F48FE2420D197F9A009257D2 /* DebugUnittest.xcconfig */;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(value)",
+ "$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)",
+ );
+ INFOPLIST_FILE = "UnitTest-Info.plist";
+ PRODUCT_NAME = "UnitTest - Foundation";
+ };
+ name = "Debug-gcov";
+ };
F48FE2670D198C1F009257D2 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = F48FE2420D197F9A009257D2 /* DebugUnittest.xcconfig */;
@@ -768,13 +1209,9 @@
FRAMEWORK_SEARCH_PATHS = (
"$(value)",
"$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)",
- "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1)",
);
- FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1 = "\"$(DEVELOPER_FRAMEWORKS_DIR)\"";
- GCC_PREFIX_HEADER = GTM_Prefix.pch;
INFOPLIST_FILE = "UnitTest-Info.plist";
PRODUCT_NAME = "UnitTest - AppKit";
- WRAPPER_EXTENSION = octest;
};
name = Debug;
};
@@ -785,13 +1222,9 @@
FRAMEWORK_SEARCH_PATHS = (
"$(value)",
"$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)",
- "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1)",
);
- FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1 = "\"$(DEVELOPER_FRAMEWORKS_DIR)\"";
- GCC_PREFIX_HEADER = GTM_Prefix.pch;
INFOPLIST_FILE = "UnitTest-Info.plist";
PRODUCT_NAME = "UnitTest - AppKit";
- WRAPPER_EXTENSION = octest;
};
name = Release;
};
@@ -802,15 +1235,37 @@
isa = XCConfigurationList;
buildConfigurations = (
1DEB918208733D990010E9CD /* Debug */,
+ F48B91060D94489300D45044 /* Debug-gcov */,
1DEB918308733D990010E9CD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ 8B45A02D0DA4696D001148C5 /* Build configuration list for PBXNativeTarget "UnitTest - UnitTesting" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 8B45A02A0DA4696D001148C5 /* Debug */,
+ 8B45A02B0DA4696D001148C5 /* Debug-gcov */,
+ 8B45A02C0DA4696D001148C5 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 8B45A2910DA49B9A001148C5 /* Build configuration list for PBXNativeTarget "UIUnitTestingHarness" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 8B45A28E0DA49B99001148C5 /* Debug */,
+ 8B45A28F0DA49B99001148C5 /* Debug-gcov */,
+ 8B45A2900DA49B99001148C5 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
F42E081E0D19987200D5DDE0 /* Build configuration list for PBXNativeTarget "UnitTest - Foundation" */ = {
isa = XCConfigurationList;
buildConfigurations = (
F42E081F0D19987200D5DDE0 /* Debug */,
+ F48B910A0D94489300D45044 /* Debug-gcov */,
F42E08200D19987200D5DDE0 /* Release */,
);
defaultConfigurationIsVisible = 0;
@@ -820,6 +1275,7 @@
isa = XCConfigurationList;
buildConfigurations = (
F42E08700D199A5C00D5DDE0 /* Debug */,
+ F48B91070D94489300D45044 /* Debug-gcov */,
F42E08710D199A5C00D5DDE0 /* Release */,
);
defaultConfigurationIsVisible = 0;
@@ -829,6 +1285,7 @@
isa = XCConfigurationList;
buildConfigurations = (
F47204350D33BEDF00E9F378 /* Debug */,
+ F48B91080D94489300D45044 /* Debug-gcov */,
F47204360D33BEDF00E9F378 /* Release */,
);
defaultConfigurationIsVisible = 0;
@@ -838,6 +1295,7 @@
isa = XCConfigurationList;
buildConfigurations = (
F48FE2670D198C1F009257D2 /* Debug */,
+ F48B91090D94489300D45044 /* Debug-gcov */,
F48FE2680D198C1F009257D2 /* Release */,
);
defaultConfigurationIsVisible = 0;
diff --git a/GTMDefines.h b/GTMDefines.h
new file mode 100644
index 0000000..b8cd1ec
--- /dev/null
+++ b/GTMDefines.h
@@ -0,0 +1,91 @@
+//
+// GTMDefines.h
+//
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// CPP symbols that can be overridden in a prefix to control how the toolbox
+// is compiled.
+// ----------------------------------------------------------------------------
+
+// _GTMDevLog & _GTMDevAssert
+//
+// _GTMDevLog & _GTMDevAssert are meant to be a very lightweight shell for
+// developer level errors. This implementation simply macros to NSLog/NSAssert.
+// It is not intended to be a general logging/reporting system.
+//
+// Please see http://code.google.com/p/google-toolbox-for-mac/wiki/DevLogNAssert
+// for a little more background on the usage of these macros.
+//
+// _GTMDevLog log some error/problem in debug builds
+// _GTMDevAssert assert if conditon isn't met w/in a method/function
+// in all builds.
+//
+// To replace this system, just provide different macro definitions in your
+// prefix header. Remember, any implementation you provide *must* be thread
+// safe since this could be called by anything in what ever situtation it has
+// been placed in.
+//
+
+// We only define the simple macros if nothing else has defined this.
+#ifndef _GTMDevLog
+
+#ifdef DEBUG
+ #define _GTMDevLog(...) NSLog(__VA_ARGS__)
+#else
+ #define _GTMDevLog(...) do { } while (0)
+#endif
+
+// we directly invoke the NSAssert handler so we can pass on the varargs
+// (NSAssert doesn't have a macro we can use that takes varargs)
+#if !defined(NS_BLOCK_ASSERTIONS)
+#define _GTMDevAssert(condition, ...) \
+ do { \
+ if (!(condition)) { \
+ [[NSAssertionHandler currentHandler] \
+ handleFailureInFunction:[NSString stringWithCString:__PRETTY_FUNCTION__] \
+ file:[NSString stringWithCString:__FILE__] \
+ lineNumber:__LINE__ \
+ description:__VA_ARGS__]; \
+ } \
+ } while(0)
+#else // !defined(NS_BLOCK_ASSERTIONS)
+#define _GTMDevAssert(condition, ...) do { } while (0)
+#endif // !defined(NS_BLOCK_ASSERTIONS)
+
+#endif // _GTMDevLog
+
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// CPP symbols defined based on the project settings so the GTM code has
+// simple things to test against w/o scattering the knowledge of project
+// setting through all the code.
+// ----------------------------------------------------------------------------
+
+// Provide a single constant CPP symbol that all of GTM uses for ifdefing
+// iPhone code.
+#include <TargetConditionals.h>
+#if TARGET_OS_IPHONE // iPhone SDK
+ // For iPhone specific stuff
+ #define GTM_IPHONE_SDK 1
+#else
+ // For MacOS specific stuff
+ #define GTM_MACOS_SDK 1
+#endif
+
diff --git a/GTMiPhone-Info.plist b/GTMiPhone-Info.plist
new file mode 100644
index 0000000..cd3c096
--- /dev/null
+++ b/GTMiPhone-Info.plist
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleDisplayName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>com.yourcompany.${PRODUCT_NAME:identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+</dict>
+</plist>
diff --git a/GTMiPhone.xcodeproj/project.pbxproj b/GTMiPhone.xcodeproj/project.pbxproj
new file mode 100755
index 0000000..aa2f713
--- /dev/null
+++ b/GTMiPhone.xcodeproj/project.pbxproj
@@ -0,0 +1,436 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 45;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 1D3623EC0D0F72F000981E51 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D3623EB0D0F72F000981E51 /* CoreGraphics.framework */; };
+ 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; };
+ 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; };
+ 8B308BCE0DAD0B8400183556 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B308BCD0DAD0B8400183556 /* QuartzCore.framework */; };
+ 8B5547CA0DB3BBF20014CC1C /* GTMUIKit+UnitTesting.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B5547C70DB3BBF20014CC1C /* GTMUIKit+UnitTesting.m */; };
+ 8B5547CB0DB3BBF20014CC1C /* GTMUIKit+UnitTestingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B5547C90DB3BBF20014CC1C /* GTMUIKit+UnitTestingTest.m */; };
+ 8BC0480F0DAE928A00C2D1CA /* GTMCalculatedRange.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC047780DAE928A00C2D1CA /* GTMCalculatedRange.m */; };
+ 8BC048100DAE928A00C2D1CA /* GTMCalculatedRangeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC047790DAE928A00C2D1CA /* GTMCalculatedRangeTest.m */; };
+ 8BC048130DAE928A00C2D1CA /* GTMNSData+zlib.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC0477F0DAE928A00C2D1CA /* GTMNSData+zlib.m */; };
+ 8BC048140DAE928A00C2D1CA /* GTMNSData+zlibTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC047800DAE928A00C2D1CA /* GTMNSData+zlibTest.m */; };
+ 8BC048150DAE928A00C2D1CA /* GTMNSEnumerator+Filter.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC047820DAE928A00C2D1CA /* GTMNSEnumerator+Filter.m */; };
+ 8BC048160DAE928A00C2D1CA /* GTMNSEnumerator+FilterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC047830DAE928A00C2D1CA /* GTMNSEnumerator+FilterTest.m */; };
+ 8BC048170DAE928A00C2D1CA /* GTMNSFileManager+Path.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC047850DAE928A00C2D1CA /* GTMNSFileManager+Path.m */; };
+ 8BC048180DAE928A00C2D1CA /* GTMNSFileManager+PathTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC047860DAE928A00C2D1CA /* GTMNSFileManager+PathTest.m */; };
+ 8BC048190DAE928A00C2D1CA /* GTMNSString+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC047880DAE928A00C2D1CA /* GTMNSString+HTML.m */; };
+ 8BC0481A0DAE928A00C2D1CA /* GTMNSString+HTMLTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC047890DAE928A00C2D1CA /* GTMNSString+HTMLTest.m */; };
+ 8BC0481B0DAE928A00C2D1CA /* GTMNSString+XML.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC0478B0DAE928A00C2D1CA /* GTMNSString+XML.m */; };
+ 8BC0481C0DAE928A00C2D1CA /* GTMNSString+XMLTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC0478C0DAE928A00C2D1CA /* GTMNSString+XMLTest.m */; };
+ 8BC0481F0DAE928A00C2D1CA /* GTMRegex.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC047920DAE928A00C2D1CA /* GTMRegex.m */; };
+ 8BC048200DAE928A00C2D1CA /* GTMRegexTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC047930DAE928A00C2D1CA /* GTMRegexTest.m */; };
+ 8BC048250DAE928A00C2D1CA /* GTMMethodCheck.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC0479D0DAE928A00C2D1CA /* GTMMethodCheck.m */; };
+ 8BC048260DAE928A00C2D1CA /* GTMMethodCheckTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC0479E0DAE928A00C2D1CA /* GTMMethodCheckTest.m */; };
+ 8BC048270DAE928A00C2D1CA /* GTMCALayer+UnitTesting.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC047A10DAE928A00C2D1CA /* GTMCALayer+UnitTesting.m */; };
+ 8BC048580DAE928A00C2D1CA /* GTMIPhoneUnitTestMain.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC047DD0DAE928A00C2D1CA /* GTMIPhoneUnitTestMain.m */; };
+ 8BC048600DAE928A00C2D1CA /* GTMNSObject+UnitTesting.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC047ED0DAE928A00C2D1CA /* GTMNSObject+UnitTesting.m */; };
+ 8BC048650DAE928A00C2D1CA /* GTMSenTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC047F70DAE928A00C2D1CA /* GTMSenTestCase.m */; };
+ 8BC0486B0DAE928A00C2D1CA /* GTMUIViewUnitTestingTest.gtmUTState in Resources */ = {isa = PBXBuildFile; fileRef = 8BC048000DAE928A00C2D1CA /* GTMUIViewUnitTestingTest.gtmUTState */; };
+ 8BC0486C0DAE928A00C2D1CA /* GTMUIViewUnitTestingTest.png in Resources */ = {isa = PBXBuildFile; fileRef = 8BC048010DAE928A00C2D1CA /* GTMUIViewUnitTestingTest.png */; };
+ 8BC04A720DAF144700C2D1CA /* GTMSystemVersionTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC04A710DAF144700C2D1CA /* GTMSystemVersionTest.m */; };
+ 8BC04A750DAF145200C2D1CA /* GTMSystemVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC04A740DAF145200C2D1CA /* GTMSystemVersion.m */; };
+ 8BC04C470DAFFCF300C2D1CA /* GTMNSString+Utilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC04C450DAFFCF300C2D1CA /* GTMNSString+Utilities.m */; };
+ 8BC04C480DAFFCF300C2D1CA /* GTMNSString+UtilitiesTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BC04C460DAFFCF300C2D1CA /* GTMNSString+UtilitiesTest.m */; };
+ 8BC04D480DB0088500C2D1CA /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BC04D470DB0088500C2D1CA /* libz.dylib */; };
+ 8BC04DE80DB023D400C2D1CA /* ReleaseNotes.txt in Resources */ = {isa = PBXBuildFile; fileRef = 8BC04DE70DB023D400C2D1CA /* ReleaseNotes.txt */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+ 1D3623EB0D0F72F000981E51 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
+ 1D6058910D05DD3D006BFB54 /* GTMiPhoneTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GTMiPhoneTest.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
+ 32CA4F630368D1EE00C91783 /* GTMiPhone_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMiPhone_Prefix.pch; sourceTree = "<group>"; };
+ 8B308AF40DAD070C00183556 /* RunIPhoneUnitTest.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = RunIPhoneUnitTest.sh; path = UnitTesting/RunIPhoneUnitTest.sh; sourceTree = "<group>"; };
+ 8B308BCD0DAD0B8400183556 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
+ 8B5547C70DB3BBF20014CC1C /* GTMUIKit+UnitTesting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMUIKit+UnitTesting.m"; sourceTree = "<group>"; };
+ 8B5547C80DB3BBF20014CC1C /* GTMUIKit+UnitTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMUIKit+UnitTesting.h"; sourceTree = "<group>"; };
+ 8B5547C90DB3BBF20014CC1C /* GTMUIKit+UnitTestingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMUIKit+UnitTestingTest.m"; sourceTree = "<group>"; };
+ 8BC047750DAE926E00C2D1CA /* GTMDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMDefines.h; sourceTree = "<group>"; };
+ 8BC047770DAE928A00C2D1CA /* GTMCalculatedRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMCalculatedRange.h; sourceTree = "<group>"; };
+ 8BC047780DAE928A00C2D1CA /* GTMCalculatedRange.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMCalculatedRange.m; sourceTree = "<group>"; };
+ 8BC047790DAE928A00C2D1CA /* GTMCalculatedRangeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMCalculatedRangeTest.m; sourceTree = "<group>"; };
+ 8BC0477A0DAE928A00C2D1CA /* GTMGarbageCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMGarbageCollection.h; sourceTree = "<group>"; };
+ 8BC0477E0DAE928A00C2D1CA /* GTMNSData+zlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSData+zlib.h"; sourceTree = "<group>"; };
+ 8BC0477F0DAE928A00C2D1CA /* GTMNSData+zlib.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSData+zlib.m"; sourceTree = "<group>"; };
+ 8BC047800DAE928A00C2D1CA /* GTMNSData+zlibTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSData+zlibTest.m"; sourceTree = "<group>"; };
+ 8BC047810DAE928A00C2D1CA /* GTMNSEnumerator+Filter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSEnumerator+Filter.h"; sourceTree = "<group>"; };
+ 8BC047820DAE928A00C2D1CA /* GTMNSEnumerator+Filter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSEnumerator+Filter.m"; sourceTree = "<group>"; };
+ 8BC047830DAE928A00C2D1CA /* GTMNSEnumerator+FilterTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSEnumerator+FilterTest.m"; sourceTree = "<group>"; };
+ 8BC047840DAE928A00C2D1CA /* GTMNSFileManager+Path.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSFileManager+Path.h"; sourceTree = "<group>"; };
+ 8BC047850DAE928A00C2D1CA /* GTMNSFileManager+Path.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSFileManager+Path.m"; sourceTree = "<group>"; };
+ 8BC047860DAE928A00C2D1CA /* GTMNSFileManager+PathTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSFileManager+PathTest.m"; sourceTree = "<group>"; };
+ 8BC047870DAE928A00C2D1CA /* GTMNSString+HTML.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSString+HTML.h"; sourceTree = "<group>"; };
+ 8BC047880DAE928A00C2D1CA /* GTMNSString+HTML.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+HTML.m"; sourceTree = "<group>"; };
+ 8BC047890DAE928A00C2D1CA /* GTMNSString+HTMLTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+HTMLTest.m"; sourceTree = "<group>"; };
+ 8BC0478A0DAE928A00C2D1CA /* GTMNSString+XML.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSString+XML.h"; sourceTree = "<group>"; };
+ 8BC0478B0DAE928A00C2D1CA /* GTMNSString+XML.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+XML.m"; sourceTree = "<group>"; };
+ 8BC0478C0DAE928A00C2D1CA /* GTMNSString+XMLTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+XMLTest.m"; sourceTree = "<group>"; };
+ 8BC0478D0DAE928A00C2D1CA /* GTMObjC2Runtime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMObjC2Runtime.h; sourceTree = "<group>"; };
+ 8BC047900DAE928A00C2D1CA /* GTMObjectSingleton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMObjectSingleton.h; sourceTree = "<group>"; };
+ 8BC047910DAE928A00C2D1CA /* GTMRegex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMRegex.h; sourceTree = "<group>"; };
+ 8BC047920DAE928A00C2D1CA /* GTMRegex.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMRegex.m; sourceTree = "<group>"; };
+ 8BC047930DAE928A00C2D1CA /* GTMRegexTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMRegexTest.m; sourceTree = "<group>"; };
+ 8BC0479B0DAE928A00C2D1CA /* GTMDebugSelectorValidation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMDebugSelectorValidation.h; sourceTree = "<group>"; };
+ 8BC0479C0DAE928A00C2D1CA /* GTMMethodCheck.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMMethodCheck.h; sourceTree = "<group>"; };
+ 8BC0479D0DAE928A00C2D1CA /* GTMMethodCheck.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMMethodCheck.m; sourceTree = "<group>"; };
+ 8BC0479E0DAE928A00C2D1CA /* GTMMethodCheckTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMMethodCheckTest.m; sourceTree = "<group>"; };
+ 8BC047A00DAE928A00C2D1CA /* GTMCALayer+UnitTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMCALayer+UnitTesting.h"; sourceTree = "<group>"; };
+ 8BC047A10DAE928A00C2D1CA /* GTMCALayer+UnitTesting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMCALayer+UnitTesting.m"; sourceTree = "<group>"; };
+ 8BC047DD0DAE928A00C2D1CA /* GTMIPhoneUnitTestMain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMIPhoneUnitTestMain.m; sourceTree = "<group>"; };
+ 8BC047EC0DAE928A00C2D1CA /* GTMNSObject+UnitTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSObject+UnitTesting.h"; sourceTree = "<group>"; };
+ 8BC047ED0DAE928A00C2D1CA /* GTMNSObject+UnitTesting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSObject+UnitTesting.m"; sourceTree = "<group>"; };
+ 8BC047F60DAE928A00C2D1CA /* GTMSenTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMSenTestCase.h; sourceTree = "<group>"; };
+ 8BC047F70DAE928A00C2D1CA /* GTMSenTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMSenTestCase.m; sourceTree = "<group>"; };
+ 8BC048000DAE928A00C2D1CA /* GTMUIViewUnitTestingTest.gtmUTState */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = GTMUIViewUnitTestingTest.gtmUTState; sourceTree = "<group>"; };
+ 8BC048010DAE928A00C2D1CA /* GTMUIViewUnitTestingTest.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = GTMUIViewUnitTestingTest.png; sourceTree = "<group>"; };
+ 8BC0480E0DAE928A00C2D1CA /* RunIPhoneUnitTest.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = RunIPhoneUnitTest.sh; sourceTree = "<group>"; };
+ 8BC049850DAEC59100C2D1CA /* DebugiPhone.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = DebugiPhone.xcconfig; sourceTree = "<group>"; };
+ 8BC049890DAEC59100C2D1CA /* ReleaseiPhone.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ReleaseiPhone.xcconfig; sourceTree = "<group>"; };
+ 8BC0498F0DAEC59100C2D1CA /* CodeCoverage.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = CodeCoverage.xcconfig; sourceTree = "<group>"; };
+ 8BC04A6F0DAF144200C2D1CA /* GTMSystemVersion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMSystemVersion.h; sourceTree = "<group>"; };
+ 8BC04A710DAF144700C2D1CA /* GTMSystemVersionTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMSystemVersionTest.m; sourceTree = "<group>"; };
+ 8BC04A740DAF145200C2D1CA /* GTMSystemVersion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMSystemVersion.m; sourceTree = "<group>"; };
+ 8BC04C440DAFFCF300C2D1CA /* GTMNSString+Utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSString+Utilities.h"; sourceTree = "<group>"; };
+ 8BC04C450DAFFCF300C2D1CA /* GTMNSString+Utilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+Utilities.m"; sourceTree = "<group>"; };
+ 8BC04C460DAFFCF300C2D1CA /* GTMNSString+UtilitiesTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+UtilitiesTest.m"; sourceTree = "<group>"; };
+ 8BC04D470DB0088500C2D1CA /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
+ 8BC04DE70DB023D400C2D1CA /* ReleaseNotes.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ReleaseNotes.txt; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 1D60588F0D05DD3D006BFB54 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */,
+ 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */,
+ 1D3623EC0D0F72F000981E51 /* CoreGraphics.framework in Frameworks */,
+ 8B308BCE0DAD0B8400183556 /* QuartzCore.framework in Frameworks */,
+ 8BC04D480DB0088500C2D1CA /* libz.dylib in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 19C28FACFE9D520D11CA2CBB /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 1D6058910D05DD3D006BFB54 /* GTMiPhoneTest.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = {
+ isa = PBXGroup;
+ children = (
+ 8BC04DE70DB023D400C2D1CA /* ReleaseNotes.txt */,
+ 8BC049840DAEC59100C2D1CA /* XcodeConfig */,
+ 8B308AF40DAD070C00183556 /* RunIPhoneUnitTest.sh */,
+ 8BC047750DAE926E00C2D1CA /* GTMDefines.h */,
+ 8BC047760DAE928A00C2D1CA /* Foundation */,
+ 8BC0479A0DAE928A00C2D1CA /* DebugUtils */,
+ 8BC0479F0DAE928A00C2D1CA /* UnitTesting */,
+ 32CA4F630368D1EE00C91783 /* GTMiPhone_Prefix.pch */,
+ 29B97323FDCFA39411CA2CEA /* Frameworks */,
+ 19C28FACFE9D520D11CA2CBB /* Products */,
+ );
+ name = CustomTemplate;
+ sourceTree = "<group>";
+ };
+ 29B97323FDCFA39411CA2CEA /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 8BC04D470DB0088500C2D1CA /* libz.dylib */,
+ 8B308BCD0DAD0B8400183556 /* QuartzCore.framework */,
+ 1D3623EB0D0F72F000981E51 /* CoreGraphics.framework */,
+ 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */,
+ 1D30AB110D05D00D00671497 /* Foundation.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ 8BC047760DAE928A00C2D1CA /* Foundation */ = {
+ isa = PBXGroup;
+ children = (
+ 8BC047770DAE928A00C2D1CA /* GTMCalculatedRange.h */,
+ 8BC047780DAE928A00C2D1CA /* GTMCalculatedRange.m */,
+ 8BC047790DAE928A00C2D1CA /* GTMCalculatedRangeTest.m */,
+ 8BC0477A0DAE928A00C2D1CA /* GTMGarbageCollection.h */,
+ 8BC0477E0DAE928A00C2D1CA /* GTMNSData+zlib.h */,
+ 8BC0477F0DAE928A00C2D1CA /* GTMNSData+zlib.m */,
+ 8BC047800DAE928A00C2D1CA /* GTMNSData+zlibTest.m */,
+ 8BC04C440DAFFCF300C2D1CA /* GTMNSString+Utilities.h */,
+ 8BC04C450DAFFCF300C2D1CA /* GTMNSString+Utilities.m */,
+ 8BC04C460DAFFCF300C2D1CA /* GTMNSString+UtilitiesTest.m */,
+ 8BC047810DAE928A00C2D1CA /* GTMNSEnumerator+Filter.h */,
+ 8BC047820DAE928A00C2D1CA /* GTMNSEnumerator+Filter.m */,
+ 8BC047830DAE928A00C2D1CA /* GTMNSEnumerator+FilterTest.m */,
+ 8BC047840DAE928A00C2D1CA /* GTMNSFileManager+Path.h */,
+ 8BC047850DAE928A00C2D1CA /* GTMNSFileManager+Path.m */,
+ 8BC047860DAE928A00C2D1CA /* GTMNSFileManager+PathTest.m */,
+ 8BC047870DAE928A00C2D1CA /* GTMNSString+HTML.h */,
+ 8BC047880DAE928A00C2D1CA /* GTMNSString+HTML.m */,
+ 8BC047890DAE928A00C2D1CA /* GTMNSString+HTMLTest.m */,
+ 8BC0478A0DAE928A00C2D1CA /* GTMNSString+XML.h */,
+ 8BC0478B0DAE928A00C2D1CA /* GTMNSString+XML.m */,
+ 8BC0478C0DAE928A00C2D1CA /* GTMNSString+XMLTest.m */,
+ 8BC0478D0DAE928A00C2D1CA /* GTMObjC2Runtime.h */,
+ 8BC047900DAE928A00C2D1CA /* GTMObjectSingleton.h */,
+ 8BC047910DAE928A00C2D1CA /* GTMRegex.h */,
+ 8BC047920DAE928A00C2D1CA /* GTMRegex.m */,
+ 8BC047930DAE928A00C2D1CA /* GTMRegexTest.m */,
+ 8BC04A740DAF145200C2D1CA /* GTMSystemVersion.m */,
+ 8BC04A6F0DAF144200C2D1CA /* GTMSystemVersion.h */,
+ 8BC04A710DAF144700C2D1CA /* GTMSystemVersionTest.m */,
+ );
+ path = Foundation;
+ sourceTree = "<group>";
+ };
+ 8BC0479A0DAE928A00C2D1CA /* DebugUtils */ = {
+ isa = PBXGroup;
+ children = (
+ 8BC0479B0DAE928A00C2D1CA /* GTMDebugSelectorValidation.h */,
+ 8BC0479C0DAE928A00C2D1CA /* GTMMethodCheck.h */,
+ 8BC0479D0DAE928A00C2D1CA /* GTMMethodCheck.m */,
+ 8BC0479E0DAE928A00C2D1CA /* GTMMethodCheckTest.m */,
+ );
+ path = DebugUtils;
+ sourceTree = "<group>";
+ };
+ 8BC0479F0DAE928A00C2D1CA /* UnitTesting */ = {
+ isa = PBXGroup;
+ children = (
+ 8BC047A00DAE928A00C2D1CA /* GTMCALayer+UnitTesting.h */,
+ 8BC047A10DAE928A00C2D1CA /* GTMCALayer+UnitTesting.m */,
+ 8B5547C70DB3BBF20014CC1C /* GTMUIKit+UnitTesting.m */,
+ 8B5547C80DB3BBF20014CC1C /* GTMUIKit+UnitTesting.h */,
+ 8B5547C90DB3BBF20014CC1C /* GTMUIKit+UnitTestingTest.m */,
+ 8BC047DD0DAE928A00C2D1CA /* GTMIPhoneUnitTestMain.m */,
+ 8BC047EC0DAE928A00C2D1CA /* GTMNSObject+UnitTesting.h */,
+ 8BC047ED0DAE928A00C2D1CA /* GTMNSObject+UnitTesting.m */,
+ 8BC047F60DAE928A00C2D1CA /* GTMSenTestCase.h */,
+ 8BC047F70DAE928A00C2D1CA /* GTMSenTestCase.m */,
+ 8BC048000DAE928A00C2D1CA /* GTMUIViewUnitTestingTest.gtmUTState */,
+ 8BC048010DAE928A00C2D1CA /* GTMUIViewUnitTestingTest.png */,
+ 8BC0480E0DAE928A00C2D1CA /* RunIPhoneUnitTest.sh */,
+ );
+ path = UnitTesting;
+ sourceTree = "<group>";
+ };
+ 8BC049840DAEC59100C2D1CA /* XcodeConfig */ = {
+ isa = PBXGroup;
+ children = (
+ 8BC04F020DB15A5300C2D1CA /* Project */,
+ 8BC0498E0DAEC59100C2D1CA /* subconfig */,
+ );
+ path = XcodeConfig;
+ sourceTree = "<group>";
+ };
+ 8BC0498E0DAEC59100C2D1CA /* subconfig */ = {
+ isa = PBXGroup;
+ children = (
+ 8BC0498F0DAEC59100C2D1CA /* CodeCoverage.xcconfig */,
+ );
+ path = subconfig;
+ sourceTree = "<group>";
+ };
+ 8BC04F020DB15A5300C2D1CA /* Project */ = {
+ isa = PBXGroup;
+ children = (
+ 8BC049850DAEC59100C2D1CA /* DebugiPhone.xcconfig */,
+ 8BC049890DAEC59100C2D1CA /* ReleaseiPhone.xcconfig */,
+ );
+ path = Project;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 1D6058900D05DD3D006BFB54 /* GTMiPhoneUnitTesting */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "GTMiPhoneUnitTesting" */;
+ buildPhases = (
+ 1D60588D0D05DD3D006BFB54 /* Resources */,
+ 1D60588E0D05DD3D006BFB54 /* Sources */,
+ 1D60588F0D05DD3D006BFB54 /* Frameworks */,
+ 8B308AF70DAD072100183556 /* ShellScript */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = GTMiPhoneUnitTesting;
+ productName = GTMiPhoneUIUnitTesting;
+ productReference = 1D6058910D05DD3D006BFB54 /* GTMiPhoneTest.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 29B97313FDCFA39411CA2CEA /* Project object */ = {
+ isa = PBXProject;
+ buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "GTMiPhone" */;
+ compatibilityVersion = "Xcode 3.1";
+ hasScannedForEncodings = 1;
+ mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 1D6058900D05DD3D006BFB54 /* GTMiPhoneUnitTesting */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 1D60588D0D05DD3D006BFB54 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8BC0486B0DAE928A00C2D1CA /* GTMUIViewUnitTestingTest.gtmUTState in Resources */,
+ 8BC0486C0DAE928A00C2D1CA /* GTMUIViewUnitTestingTest.png in Resources */,
+ 8BC04DE80DB023D400C2D1CA /* ReleaseNotes.txt in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 8B308AF70DAD072100183556 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${SRCROOT}/UnitTesting/RunIPhoneUnitTest.sh\"";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 1D60588E0D05DD3D006BFB54 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8BC0480F0DAE928A00C2D1CA /* GTMCalculatedRange.m in Sources */,
+ 8BC048100DAE928A00C2D1CA /* GTMCalculatedRangeTest.m in Sources */,
+ 8BC048130DAE928A00C2D1CA /* GTMNSData+zlib.m in Sources */,
+ 8BC048140DAE928A00C2D1CA /* GTMNSData+zlibTest.m in Sources */,
+ 8BC048150DAE928A00C2D1CA /* GTMNSEnumerator+Filter.m in Sources */,
+ 8BC048160DAE928A00C2D1CA /* GTMNSEnumerator+FilterTest.m in Sources */,
+ 8BC048170DAE928A00C2D1CA /* GTMNSFileManager+Path.m in Sources */,
+ 8BC048180DAE928A00C2D1CA /* GTMNSFileManager+PathTest.m in Sources */,
+ 8BC048190DAE928A00C2D1CA /* GTMNSString+HTML.m in Sources */,
+ 8BC0481A0DAE928A00C2D1CA /* GTMNSString+HTMLTest.m in Sources */,
+ 8BC0481B0DAE928A00C2D1CA /* GTMNSString+XML.m in Sources */,
+ 8BC0481C0DAE928A00C2D1CA /* GTMNSString+XMLTest.m in Sources */,
+ 8BC0481F0DAE928A00C2D1CA /* GTMRegex.m in Sources */,
+ 8BC048200DAE928A00C2D1CA /* GTMRegexTest.m in Sources */,
+ 8BC048250DAE928A00C2D1CA /* GTMMethodCheck.m in Sources */,
+ 8BC048260DAE928A00C2D1CA /* GTMMethodCheckTest.m in Sources */,
+ 8BC048270DAE928A00C2D1CA /* GTMCALayer+UnitTesting.m in Sources */,
+ 8BC048580DAE928A00C2D1CA /* GTMIPhoneUnitTestMain.m in Sources */,
+ 8BC048600DAE928A00C2D1CA /* GTMNSObject+UnitTesting.m in Sources */,
+ 8BC048650DAE928A00C2D1CA /* GTMSenTestCase.m in Sources */,
+ 8BC04A720DAF144700C2D1CA /* GTMSystemVersionTest.m in Sources */,
+ 8BC04A750DAF145200C2D1CA /* GTMSystemVersion.m in Sources */,
+ 8BC04C470DAFFCF300C2D1CA /* GTMNSString+Utilities.m in Sources */,
+ 8BC04C480DAFFCF300C2D1CA /* GTMNSString+UtilitiesTest.m in Sources */,
+ 8B5547CA0DB3BBF20014CC1C /* GTMUIKit+UnitTesting.m in Sources */,
+ 8B5547CB0DB3BBF20014CC1C /* GTMUIKit+UnitTestingTest.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ 1D6058940D05DD3E006BFB54 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INFOPLIST_FILE = "GTMiPhone-Info.plist";
+ PRODUCT_NAME = GTMiPhoneTest;
+ };
+ name = Debug;
+ };
+ 1D6058950D05DD3E006BFB54 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INFOPLIST_FILE = "GTMiPhone-Info.plist";
+ PRODUCT_NAME = GTMiPhoneTest;
+ };
+ name = Release;
+ };
+ 8BC0497D0DAEC48600C2D1CA /* Debug-gcov */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 8BC049850DAEC59100C2D1CA /* DebugiPhone.xcconfig */;
+ buildSettings = {
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ GCC_PREFIX_HEADER = GTMiPhone_Prefix.pch;
+ };
+ name = "Debug-gcov";
+ };
+ 8BC0497E0DAEC48600C2D1CA /* Debug-gcov */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 8BC0498F0DAEC59100C2D1CA /* CodeCoverage.xcconfig */;
+ buildSettings = {
+ INFOPLIST_FILE = "GTMiPhone-Info.plist";
+ PRODUCT_NAME = GTMiPhoneTest;
+ };
+ name = "Debug-gcov";
+ };
+ C01FCF4F08A954540054247B /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 8BC049850DAEC59100C2D1CA /* DebugiPhone.xcconfig */;
+ buildSettings = {
+ GCC_PREFIX_HEADER = GTMiPhone_Prefix.pch;
+ };
+ name = Debug;
+ };
+ C01FCF5008A954540054247B /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 8BC049890DAEC59100C2D1CA /* ReleaseiPhone.xcconfig */;
+ buildSettings = {
+ GCC_PREFIX_HEADER = GTMiPhone_Prefix.pch;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "GTMiPhoneUnitTesting" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 1D6058940D05DD3E006BFB54 /* Debug */,
+ 8BC0497E0DAEC48600C2D1CA /* Debug-gcov */,
+ 1D6058950D05DD3E006BFB54 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ C01FCF4E08A954540054247B /* Build configuration list for PBXProject "GTMiPhone" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ C01FCF4F08A954540054247B /* Debug */,
+ 8BC0497D0DAEC48600C2D1CA /* Debug-gcov */,
+ C01FCF5008A954540054247B /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
+}
diff --git a/GTMiPhone_Prefix.pch b/GTMiPhone_Prefix.pch
new file mode 100644
index 0000000..4c13635
--- /dev/null
+++ b/GTMiPhone_Prefix.pch
@@ -0,0 +1,20 @@
+//
+// 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.
+//
+
+#ifdef __OBJC__
+ #import <Foundation/Foundation.h>
+ #import <UIKit/UIKit.h>
+#endif
diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt
index 8adc4e0..f86bfe6 100644
--- a/ReleaseNotes.txt
+++ b/ReleaseNotes.txt
@@ -17,8 +17,13 @@ Changes since 1.0.0
- Changed name on API in NSString+XML and added another api to make this a
litte more clear. (thanks Kent)
-- Found and fixed a bug in the regex enumerators that was causing them to
- incorrectly walk a string when using '^' in an expression.
+- GTMRegex
+ - Found and fixed a bug in the enumerators that was causing them to
+ incorrectly walk a string when using '^' in an expression.
+ - Added helpers for substring tests and unittests for the new apis.
+ - Added initializer that takes an outError to allow the collection of any
+ pattern parsing error message (in case the pattern came from a user and
+ complete error information is needed to message the user).
- Added GTMScriptRunner for spawning scripts.
@@ -26,6 +31,59 @@ Changes since 1.0.0
- Added GTMNSWorkspace+ScreenSaver
+- Added GTMNSString+Data
+
+- added a common header (GTMDefines) for any common defines so the conditionals
+ are all in one place
+
+- Support for things compiling against the iPhone SDK
+ - Everything in the GTMiPhone project works in the iPhone
+ - Added iPhone xcconfig files
+ - Added iPhone unittests (See below)
+
+- More work on the UI unittests
+ - support pretty much any part of a UI
+ - support for CALayers
+ - full support for the iPhone
+ - the iPhone uses the same macro set at OCUnit, but has its own runtime
+ for running tests.
+ - extended capabilities of UIUnitTesting to be more flexible and give better
+ error reporting for states.
+
+- Renamed the actual framework to "GoogleToolboxForMac.framework" (it should
+ have matched the project on code.google.com from the start)
+
+- added a Debug-gcov target that will product debug bits w/ code coverage
+ support to check unittests, etc.
+
+- GTMDebugSelectorValidation to provide something to include in class impls
+ to get validation of object/selector pair(s) being implemented so you don't
+ have to wait for a runtime invocation failures. (especially useful for
+ things that take a success and failure selector so one doesn't always get
+ called)
+
+- added _GTMDevLog (really in GTMDefines) that are a set of macros that can be
+ used for logging. This allows any project to redefine them to direct logging
+ into its runtime needs.
+
+- Moved GTMGeometryUtils into Foundation from AppKit
+
+- Removed several HI* calls from GTMGeometryUtils as Carbon UI in general is
+ deprecated.
+
+- Xcode configs
+ - changed the layout to make it a little easier to tell how to use them.
+ - added Leopard or later configs
+
+- Unittest coverage greatly increased
+
+- Added RunMacOSUnitTests shell script. We run this script for starting up our
+ unittests because it turns on a variety of "enhancements" (such as zombies,
+ scribbling etc) to encourage our unittests to fail for us. It also will run
+ the unittests using the _debug frameworks if you have them.
+
+ https://connect.apple.com/cgi-bin/WebObjects/MemberSite.woa/wa/getSoftware?bundleID=19915
+
Release 1.0.0
14-January-2008
diff --git a/TigerGcov/libgcov.a b/TigerGcov/libgcov.a
new file mode 100644
index 0000000..f45a58d
--- /dev/null
+++ b/TigerGcov/libgcov.a
Binary files differ
diff --git a/TigerGcov/libgcov_readme.html b/TigerGcov/libgcov_readme.html
new file mode 100644
index 0000000..fc381a7
--- /dev/null
+++ b/TigerGcov/libgcov_readme.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>libgcov_readme.html</title>
+ <meta http-equiv="REFRESH" content="0;url=http://code.google.com/p/google-toolbox-for-mac/wiki/TigerGcov"></HEAD>
+</head>
+<body>
+
+</body>
+</html>
diff --git a/UnitTesting/GTMNSView+UnitTesting.h b/UnitTesting/GTMAppKit+UnitTesting.h
index fcda16b..5db9ebb 100644..100755
--- a/UnitTesting/GTMNSView+UnitTesting.h
+++ b/UnitTesting/GTMAppKit+UnitTesting.h
@@ -1,14 +1,8 @@
//
-// GTMNSView+UnitTesting.h
-//
-// Code for making unit testing of graphics/UI easier. Generally you
-// will only want to look at the macros:
-// GTMAssertDrawingEqualToFile
-// GTMAssertViewRepEqualToFile
-// and the protocol GTMUnitTestViewDrawer. When using these routines
-// make sure you are using device colors and not calibrated/generic colors
-// or else your test graphics WILL NOT match across devices/graphics cards.
-//
+// GTMAppKit+UnitTesting.m
+//
+// Categories for making unit testing of graphics/UI easier.
+//
// Copyright 2006-2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
@@ -24,12 +18,40 @@
// the License.
//
-#import <Cocoa/Cocoa.h>
+#import <AppKit/AppKit.h>
#import "GTMNSObject+UnitTesting.h"
+// Categories for making unit testing of graphics/UI easier.
+// Allows you to take a state/images of instances of AppKit classes.
+// See GTMNSObject+UnitTesting.h for details.
+
+@interface NSApplication (GTMUnitTestingAdditions)
+@end
+
+@interface NSWindow (GTMUnitTestingAdditions) <GTMUnitTestingImaging>
+@end
+
+@interface NSControl (GTMUnitTestingAdditions)
+@end
+
+@interface NSTextField (GTMUnitTestingAdditions)
+@end
+
+@interface NSCell (GTMUnitTestingAdditions)
+@end
+
+@interface NSImage (GTMUnitTestingAdditions) <GTMUnitTestingImaging>
+@end
+
+@interface NSMenu (GTMUnitTestingAdditions)
+@end
+
+@interface NSMenuItem (GTMUnitTestingAdditions)
+@end
+
@protocol GTMUnitTestViewDrawer;
-/// Fails when the |a1|'s drawing in an area |a2| does not equal the TIFF file named |a3|.
+// Fails when the |a1|'s drawing in an area |a2| does not equal the image file named |a3|.
// See the description of the GTMAssertViewRepEqualToFile macro
// to understand how |a3| is found and written out.
// See the description of the GTMUnitTestView for a better idea
@@ -40,7 +62,7 @@
// a1: The object that implements the GTMUnitTestViewDrawer protocol
// that is doing the drawing.
// a2: The size of the drawing
-// a3: The name of the TIFF file to check against.
+// a3: The name of the image file to check against.
// Do not include the extension
// a4: contextInfo to pass to drawer
// description: A format string as in the printf() function.
@@ -57,33 +79,17 @@
void *a4ContextInfo = (a4); \
NSRect frame = NSMakeRect(0, 0, a2Size.width, a2Size.height); \
GTMUnitTestView *view = [[[GTMUnitTestView alloc] initWithFrame:frame drawer:a1Object contextInfo:a4ContextInfo] autorelease]; \
- GTMAssertObjectImageEqualToTIFFNamed(view, a3String, STComposeString(description, ##__VA_ARGS__)); \
+ GTMAssertObjectImageEqualToImageNamed(view, a3String, STComposeString(description, ##__VA_ARGS__)); \
} while(0)
// Category for making unit testing of graphics/UI easier.
-/// Allows you to take a state of a view. Supports both image and state.
+// Allows you to take a state of a view. Supports both image and state.
// See NSObject+UnitTesting.h for details.
-@interface NSView (GTMUnitTestingAdditions) <GTMUnitTestingEncoding>
-
-/// Returns an image containing a representation suitable for use in comparing against a master image.
-//
-// NB this means that all colors should be device based.
-//
-// Returns:
-// an image of the object
-- (NSImage*)unitTestImage;
-
-/// Encodes the state of an object in a manner suitable for comparing against a master state file
-// This enables us to determine whether the object is in a suitable state.
-//
-// Arguments:
-// inCoder - the coder to encode our state into
-- (void)unitTestEncodeState:(NSCoder*)inCoder;
-
-/// Returns whether unitTestEncodeState should recurse into subviews
+@interface NSView (GTMUnitTestingAdditions) <GTMUnitTestingImaging>
+// Returns whether unitTestEncodeState should recurse into subviews
//
-// Dan Waylonis discovered that if you have "Full keyboard access" in the
+// If you have "Full keyboard access" in the
// Keyboard & Mouse > Keyboard Shortcuts preferences pane set to "Text boxes
// and Lists only" that Apple adds a set of subviews to NSTextFields. So in the
// case of NSTextFields we don't want to recurse into their subviews. There may
@@ -93,11 +99,12 @@
//
// Returns:
// should unitTestEncodeState pick up subview state.
-- (BOOL)shouldEncodeStateRecurseIntoSubviews;
+- (BOOL)gtm_shouldEncodeStateForSubviews;
@end
-/// A view that allows you to delegate out drawing using the formal GTMUnitTestViewDelegate protocol
+// A view that allows you to delegate out drawing using the formal
+// GTMUnitTestViewDelegate protocol
// This is useful when writing up unit tests for visual elements.
// Your test will often end up looking like this:
// - (void)testFoo {
@@ -108,6 +115,7 @@
// it's content using |self|'s unitTestViewDrawRect method and compares it to
// the contents of the file Foo.tif to make sure it's valid
@interface GTMUnitTestView : NSView {
+ @private
id<GTMUnitTestViewDrawer> drawer_; // delegate for doing drawing (STRONG)
void* contextInfo_; // info passed in by user for them to use when drawing
}
@@ -132,7 +140,6 @@
//
// Args:
// rect: the area to draw.
-- (void)unitTestViewDrawRect:(NSRect)rect contextInfo:(void*)contextInfo;
+- (void)gtm_unitTestViewDrawRect:(NSRect)rect contextInfo:(void*)contextInfo;
@end
-/// \endcond
diff --git a/UnitTesting/GTMAppKit+UnitTesting.m b/UnitTesting/GTMAppKit+UnitTesting.m
new file mode 100755
index 0000000..85f03a2
--- /dev/null
+++ b/UnitTesting/GTMAppKit+UnitTesting.m
@@ -0,0 +1,304 @@
+//
+// GTMAppKit+UnitTesting.m
+//
+// Categories for making unit testing of graphics/UI easier.
+//
+// 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 "GTMDefines.h"
+#import "GTMAppKit+UnitTesting.h"
+#import "GTMGeometryUtils.h"
+#import "GTMMethodCheck.h"
+
+@implementation NSApplication (GMUnitTestingAdditions)
+GTM_METHOD_CHECK(NSObject, gtm_unitTestEncodeState:); // COV_NF_LINE
+
+- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
+ [super gtm_unitTestEncodeState:inCoder];
+ [inCoder encodeInt:[[self mainWindow] windowNumber]
+ forKey:@"ApplicationMainWindow"];
+
+ // Descend down into the windows allowing them to store their state
+ NSEnumerator *windowEnum = [[self windows] objectEnumerator];
+ NSWindow *window = nil;
+ int i = 0;
+ while ((window = [windowEnum nextObject])) {
+ [inCoder encodeObject:window forKey:[NSString stringWithFormat:@"Window %d", i]];
+ i = i + 1;
+ }
+
+ // and encode the menu bar
+ NSMenu *mainMenu = [self mainMenu];
+ if (mainMenu) {
+ [inCoder encodeObject:mainMenu forKey:@"MenuBar"];
+ }
+}
+@end
+
+@implementation NSWindow (GMUnitTestingAdditions)
+
+- (CGImageRef)gtm_createUnitTestImage {
+ return [[[self contentView] superview] gtm_createUnitTestImage];
+}
+
+- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
+ [super gtm_unitTestEncodeState:inCoder];
+ [inCoder encodeObject:[self title] forKey:@"WindowTitle"];
+ [inCoder encodeBool:[self isVisible] forKey:@"WindowIsVisible"];
+ // Do not record if window is key, because users running unit tests
+ // and clicking around to other apps, could change this mid test causing
+ // issues.
+ // [inCoder encodeBool:[self isKeyWindow] forKey:@"WindowIsKey"];
+ [inCoder encodeBool:[self isMainWindow] forKey:@"WindowIsMain"];
+ [inCoder encodeObject:[self contentView] forKey:@"WindowContent"];
+}
+
+@end
+
+@implementation NSControl (GTMUnitTestingAdditions)
+
+// Encodes the state of an object in a manner suitable for comparing
+// against a master state file so we can determine whether the
+// object is in a suitable state.
+//
+// Arguments:
+// inCoder - the coder to encode our state into
+- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
+ [super gtm_unitTestEncodeState:inCoder];
+ [inCoder encodeObject:[self class] forKey:@"ControlType"];
+ [inCoder encodeObject:[self objectValue] forKey:@"ControlValue"];
+ [inCoder encodeObject:[self selectedCell] forKey:@"ControlSelectedCell"];
+ [inCoder encodeInt:[self tag] forKey:@"ControlTag"];
+ [inCoder encodeBool:[self isEnabled] forKey:@"ControlIsEnabled"];
+}
+
+@end
+
+@implementation NSTextField (GTMUnitTestingAdditions)
+
+- (BOOL)gtm_shouldEncodeStateForSubviews {
+ return NO;
+}
+
+@end
+
+@implementation NSCell (GTMUnitTestingAdditions)
+
+// Encodes the state of an object in a manner suitable for comparing
+// against a master state file so we can determine whether the
+// object is in a suitable state.
+//
+// Arguments:
+// inCoder - the coder to encode our state into
+- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
+ [super gtm_unitTestEncodeState:inCoder];
+ BOOL isImageCell = NO;
+ if ([self hasValidObjectValue]) {
+ id val = [self objectValue];
+ [inCoder encodeObject:val forKey:@"CellValue"];
+ isImageCell = [val isKindOfClass:[NSImage class]];
+ }
+ if (!isImageCell) {
+ // Image cells have a title that includes addresses that aren't going
+ // to be constant, so we don't encode them. All the info we need
+ // is going to be in the CellValue encoding.
+ [inCoder encodeObject:[self title] forKey:@"CellTitle"];
+ }
+ [inCoder encodeInt:[self state] forKey:@"CellState"];
+ [inCoder encodeInt:[self tag] forKey:@"CellTag"];
+}
+
+@end
+
+@implementation NSImage (GTMUnitTestingAdditions)
+
+- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
+ [super gtm_unitTestEncodeState:inCoder];
+ [inCoder encodeObject:NSStringFromSize([self size]) forKey:@"ImageSize"];
+ [inCoder encodeObject:[self name] forKey:@"ImageName"];
+}
+
+- (CGImageRef)gtm_createUnitTestImage {
+ // Create up a context
+ NSSize size = [self size];
+ NSRect rect = GTMNSRectOfSize(size);
+ CGContextRef contextRef = [self gtm_createUnitTestBitmapContextOfSize:GTMNSSizeToCGSize(size)
+ data:NULL];
+ NSGraphicsContext *bitmapContext = [NSGraphicsContext graphicsContextWithGraphicsPort:contextRef
+ flipped:NO];
+ _GTMDevAssert(bitmapContext, @"Couldn't create ns bitmap context");
+
+ [NSGraphicsContext saveGraphicsState];
+ [NSGraphicsContext setCurrentContext:bitmapContext];
+ [self drawInRect:rect fromRect:rect operation:NSCompositeCopy fraction:1.0];
+
+ CGImageRef image = CGBitmapContextCreateImage(contextRef);
+ CFRelease(contextRef);
+ [NSGraphicsContext restoreGraphicsState];
+ return image;
+}
+
+@end
+
+@implementation NSMenu (GTMUnitTestingAdditions)
+
+// Encodes the state of an object in a manner suitable for comparing
+// against a master state file so we can determine whether the
+// object is in a suitable state.
+//
+// Arguments:
+// inCoder - the coder to encode our state into
+- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
+ [super gtm_unitTestEncodeState:inCoder];
+ [inCoder encodeObject:[self title] forKey:@"MenuTitle"];
+
+ // Descend down into the menuitems allowing them to store their state
+ NSEnumerator *menuItemEnum = [[self itemArray] objectEnumerator];
+ NSMenuItem *menuItem = nil;
+ for (int i = 0; (menuItem = [menuItemEnum nextObject]); ++i) {
+ [inCoder encodeObject:menuItem forKey:[NSString stringWithFormat:@"MenuItem %d", i]];
+ }
+}
+
+@end
+
+@implementation NSMenuItem (GTMUnitTestingAdditions)
+
+- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
+ [super gtm_unitTestEncodeState:inCoder];
+ [inCoder encodeObject:[self title] forKey:@"MenuItemTitle"];
+ [inCoder encodeObject:[self keyEquivalent] forKey:@"MenuItemKeyEquivalent"];
+ [inCoder encodeBool:[self isSeparatorItem] forKey:@"MenuItemIsSeparator"];
+ [inCoder encodeInt:[self state] forKey:@"MenuItemState"];
+ [inCoder encodeBool:[self isEnabled] forKey:@"MenuItemIsEnabled"];
+ [inCoder encodeBool:[self isAlternate] forKey:@"MenuItemIsAlternate"];
+ [inCoder encodeObject:[self toolTip] forKey:@"MenuItemTooltip"];
+ [inCoder encodeInt:[self tag] forKey:@"MenuItemTag"];
+ [inCoder encodeInt:[self indentationLevel] forKey:@"MenuItemIndentationLevel"];
+
+ // Do our submenu if neccessary
+ if ([self hasSubmenu]) {
+ [inCoder encodeObject:[self submenu] forKey:@"MenuItemSubmenu"];
+ }
+}
+
+@end
+
+// A view that allows you to delegate out drawing using the formal
+// GTMUnitTestViewDelegate protocol above. This is useful when writing up unit
+// tests for visual elements.
+// Your test will often end up looking like this:
+// - (void)testFoo {
+// GTMAssertDrawingEqualToFile(self, NSMakeSize(200, 200), @"Foo", nil, nil);
+// }
+// and your testSuite will also implement the unitTestViewDrawRect method to do
+// it's actual drawing. The above creates a view of size 200x200 that draws
+// it's content using |self|'s unitTestViewDrawRect method and compares it to
+// the contents of the file Foo.tif to make sure it's valid
+@implementation GTMUnitTestView
+
+- (id)initWithFrame:(NSRect)frame
+ drawer:(id<GTMUnitTestViewDrawer>)drawer
+ contextInfo:(void*)contextInfo {
+ self = [super initWithFrame:frame];
+ if (self != nil) {
+ drawer_ = [drawer retain];
+ contextInfo_ = contextInfo;
+ }
+ return self;
+}
+
+- (void) dealloc {
+ [drawer_ release];
+ [super dealloc];
+}
+
+
+- (void)drawRect:(NSRect)rect {
+ [drawer_ gtm_unitTestViewDrawRect:rect contextInfo:contextInfo_];
+}
+
+
+@end
+
+@implementation NSView (GTMUnitTestingAdditions)
+
+// Returns an image containing a representation of the object
+// suitable for use in comparing against a master image.
+// Does all of it's drawing with smoothfonts and antialiasing off
+// to avoid issues with font smoothing settings and antialias differences
+// between ppc and x86.
+//
+// Returns:
+// an image of the object
+- (CGImageRef)gtm_createUnitTestImage {
+ // Create up a context
+ NSRect bounds = [self bounds];
+ CGContextRef contextRef = [self gtm_createUnitTestBitmapContextOfSize:GTMNSSizeToCGSize(bounds.size)
+ data:NULL];
+ NSGraphicsContext *bitmapContext = [NSGraphicsContext graphicsContextWithGraphicsPort:contextRef
+ flipped:NO];
+ _GTMDevAssert(bitmapContext, @"Couldn't create ns bitmap context");
+
+ // Save our state and turn off font smoothing and antialias.
+ CGContextSaveGState(contextRef);
+ CGContextSetShouldSmoothFonts(contextRef, false);
+ CGContextSetShouldAntialias(contextRef, false);
+ [self displayRectIgnoringOpacity:bounds inContext:bitmapContext];
+
+ CGImageRef image = CGBitmapContextCreateImage(contextRef);
+ CFRelease(contextRef);
+ return image;
+}
+
+// Returns whether gtm_unitTestEncodeState should recurse into subviews
+// of a particular view.
+// If you have "Full keyboard access" in the
+// Keyboard & Mouse > Keyboard Shortcuts preferences pane set to "Text boxes
+// and Lists only" that Apple adds a set of subviews to NSTextFields. So in the
+// case of NSTextFields we don't want to recurse into their subviews. There may
+// be other cases like this, so instead of specializing gtm_unitTestEncodeState: to
+// look for NSTextFields, NSTextFields will just not allow us to recurse into
+// their subviews.
+//
+// Returns:
+// should gtm_unitTestEncodeState pick up subview state.
+- (BOOL)gtm_shouldEncodeStateForSubviews {
+ return YES;
+}
+
+// Encodes the state of an object in a manner suitable for comparing
+// against a master state file so we can determine whether the
+// object is in a suitable state.
+//
+// Arguments:
+// inCoder - the coder to encode our state into
+- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
+ [super gtm_unitTestEncodeState:inCoder];
+ [inCoder encodeBool:[self isHidden] forKey:@"ViewIsHidden"];
+ if ([self gtm_shouldEncodeStateForSubviews]) {
+ NSEnumerator *subviewEnum = [[self subviews] objectEnumerator];
+ NSView *subview = nil;
+ int i = 0;
+ while ((subview = [subviewEnum nextObject])) {
+ [inCoder encodeObject:subview forKey:[NSString stringWithFormat:@"ViewSubView %d", i]];
+ i = i + 1;
+ }
+ }
+}
+
+@end
+
diff --git a/UnitTesting/GTMCALayer+UnitTesting.h b/UnitTesting/GTMCALayer+UnitTesting.h
new file mode 100644
index 0000000..b757ba9
--- /dev/null
+++ b/UnitTesting/GTMCALayer+UnitTesting.h
@@ -0,0 +1,46 @@
+//
+// GTMCALayer+UnitTesting.h
+//
+// Code for making unit testing of graphics/UI easier. Generally you
+// will only want to look at the macros:
+// GTMAssertDrawingEqualToFile
+// GTMAssertViewRepEqualToFile
+// and the protocol GTMUnitTestCALayerDrawer. When using these routines
+// make sure you are using device colors and not calibrated/generic colors
+// or else your test graphics WILL NOT match across devices/graphics cards.
+//
+// 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 <QuartzCore/QuartzCore.h>
+#import "GTMNSObject+UnitTesting.h"
+
+// Category for making unit testing of graphics/UI easier.
+
+// Allows you to take a state of a view. Supports both image and state.
+// See GTMNSObject+UnitTesting.h for details.
+@interface CALayer (GTMUnitTestingAdditions) <GTMUnitTestingImaging>
+// Returns whether gtm_unitTestEncodeState should recurse into sublayers
+//
+// Returns:
+// should gtm_unitTestEncodeState pick up sublayer state.
+- (BOOL)gtm_shouldEncodeStateForSublayers;
+@end
+
+@interface NSObject (GTMCALayerUnitTestingDelegateMethods)
+// Delegate method that allows a delegate for a layer to
+// decide whether we should recurse
+- (BOOL)gtm_shouldEncodeStateForSublayersOfLayer:(CALayer*)layer;
+@end
diff --git a/UnitTesting/GTMCALayer+UnitTesting.m b/UnitTesting/GTMCALayer+UnitTesting.m
new file mode 100644
index 0000000..355f956
--- /dev/null
+++ b/UnitTesting/GTMCALayer+UnitTesting.m
@@ -0,0 +1,89 @@
+//
+// GTMCALayer+UnitTesting.m
+//
+// Category for making unit testing of graphics/UI easier.
+// Allows you to save a view out to a image file, and compare a view
+// with a previously stored representation to make sure it hasn't changed.
+//
+// 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 "GTMCALayer+UnitTesting.h"
+
+@implementation CALayer (GTMUnitTestingAdditions)
+
+// Returns an image containing a representation of the object
+// suitable for use in comparing against a master image.
+// NB this means that all colors should be from "NSDevice" color space
+// Does all of it's drawing with smoothfonts and antialiasing off
+// to avoid issues with font smoothing settings and antialias differences
+// between ppc and x86.
+//
+// Returns:
+// an image of the object
+- (CGImageRef)gtm_createUnitTestImage {
+ CGRect bounds = [self bounds];
+ CGSize size = CGSizeMake(CGRectGetWidth(bounds), CGRectGetHeight(bounds));
+ CGContextRef context = [self gtm_createUnitTestBitmapContextOfSize:size
+ data:NULL];
+ _GTMDevAssert(context, @"Couldn't create context");
+
+ // iPhone renders are flipped
+ CGAffineTransform transform = CGAffineTransformMakeTranslation(0, size.height);
+ transform = CGAffineTransformScale(transform, 1.0, -1.0);
+ CGContextConcatCTM(context, transform);
+
+ [self renderInContext:context];
+ CGImageRef image = CGBitmapContextCreateImage(context);
+ CFRelease(context);
+ return image;
+}
+
+// Encodes the state of an object in a manner suitable for comparing
+// against a master state file so we can determine whether the
+// object is in a suitable state.
+//
+// Arguments:
+// inCoder - the coder to encode our state into
+- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
+ [super gtm_unitTestEncodeState:inCoder];
+ [inCoder encodeBool:[self isHidden] forKey:@"LayerIsHidden"];
+ [inCoder encodeBool:[self isDoubleSided] forKey:@"LayerIsDoublesided"];
+ [inCoder encodeBool:[self isOpaque] forKey:@"LayerIsOpaque"];
+ [inCoder encodeFloat:[self opacity] forKey:@"LayerOpacity"];
+ // TODO: There is a ton more we can add here. What are we interested in?
+ if ([self gtm_shouldEncodeStateForSublayers]) {
+ int i = 0;
+ for (CALayer *subLayer in [self sublayers]) {
+ [inCoder encodeObject:subLayer
+ forKey:[NSString stringWithFormat:@"CALayerSubLayer %d", i]];
+ i = i + 1;
+ }
+ }
+}
+
+// Returns whether gtm_unitTestEncodeState should recurse into sublayers
+//
+// Returns:
+// should gtm_unitTestEncodeState pick up sublayer state.
+- (BOOL)gtm_shouldEncodeStateForSublayers {
+ BOOL value = YES;
+ if([self.delegate respondsToSelector:@selector(gtm_shouldEncodeStateForSublayersOfLayer:)]) {
+ value = [self.delegate gtm_shouldEncodeStateForSublayersOfLayer:self];
+ }
+ return value;
+}
+
+@end
diff --git a/UnitTesting/GTMIPhoneUnitTestMain.m b/UnitTesting/GTMIPhoneUnitTestMain.m
new file mode 100755
index 0000000..2af060b
--- /dev/null
+++ b/UnitTesting/GTMIPhoneUnitTestMain.m
@@ -0,0 +1,181 @@
+//
+// GTMIPhoneUnitTestMain.m
+//
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import "GTMDefines.h"
+#if !GTM_IPHONE_SDK
+ #error GTMIPhoneUnitTestMain for iPhone only
+#endif
+#import <objc/runtime.h>
+#import <stdio.h>
+#import <UIKit/UIKit.h>
+#import "GTMSenTestCase.h"
+
+// Used for sorting methods below
+static int MethodSort(const void *a, const void *b) {
+ const char *nameA = sel_getName(method_getName(*(Method*)a));
+ const char *nameB = sel_getName(method_getName(*(Method*)b));
+ return strcmp(nameA, nameB);
+}
+
+@interface UIApplication (iPhoneUnitTestAdditions)
+// "Private" method that we need
+- (void)terminate;
+@end
+
+@interface GTMIPhoneUnitTestDelegate : NSObject
+@end
+
+@implementation GTMIPhoneUnitTestDelegate
+
+// Return YES if class is subclass (1 or more generations) of SenTestCase
+- (BOOL)isTestFixture:(Class)class {
+ BOOL iscase = NO;
+ Class testCaseClass = [SenTestCase class];
+ Class superclass;
+ for (superclass = class;
+ !iscase && superclass;
+ superclass = class_getSuperclass(superclass)) {
+ iscase = superclass == testCaseClass ? YES : NO;
+ }
+ return iscase;
+}
+
+// Log an error out to console in a way that Xcode will recognize it.
+- (void)printError:(NSString *)error {
+ if ([error rangeOfString:@"error:"].location == NSNotFound) {
+ fprintf(stderr, "error: %s\n", [error UTF8String]);
+ } else {
+ fprintf(stderr, "%s\n", [error UTF8String]);
+ }
+ fflush(stderr);
+}
+
+// Run through all the registered classes and run test methods on any
+// that are subclasses of SenTestCase.
+- (void)applicationDidFinishLaunching:(UIApplication *)application {
+ int count = objc_getClassList(NULL, 0);
+ Class *classes = (Class*)malloc(sizeof(Class) * count);
+ _GTMDevAssert(classes, @"Couldn't allocate class list");
+ objc_getClassList(classes, count);
+ int suiteSuccesses = 0;
+ int suiteFailures = 0;
+ int suiteTotal = 0;
+ NSString *suiteName = [[NSBundle mainBundle] bundlePath];
+ NSDate *suiteStartDate = [NSDate date];
+ NSString *suiteStartString = [NSString stringWithFormat:@"Test Suite '%@' started at %@\n",
+ suiteName, suiteStartDate];
+ fprintf(stderr, [suiteStartString UTF8String]);
+ fflush(stderr);
+ int i, j;
+ for (i = 0; i < count; ++i) {
+ Class currClass = classes[i];
+ if ([self isTestFixture:currClass]) {
+ NSDate *fixtureStartDate = [NSDate date];
+ NSString *fixtureName = NSStringFromClass(currClass);
+ NSString *fixtureStartString = [NSString stringWithFormat:@"Test Suite '%@' started at %@\n",
+ fixtureName, fixtureStartDate];
+ int fixtureSuccesses = 0;
+ int fixtureFailures = 0;
+ int fixtureTotal = 0;
+ fprintf(stderr, [fixtureStartString UTF8String]);
+ fflush(stderr);
+ id testcase = [[currClass alloc] init];
+ _GTMDevAssert(testcase, @"Unable to instantiate Test Suite: '%@'\n",
+ fixtureName);
+ unsigned int methodCount;
+ Method *methods = class_copyMethodList(currClass, &methodCount);
+ // Sort our methods so they are called in Alphabetical order just
+ // because we can.
+ qsort(methods, methodCount, sizeof(Method), MethodSort);
+ for (j = 0; j < methodCount; ++j) {
+ Method currMethod = methods[j];
+ SEL sel = method_getName(currMethod);
+ const char *name = sel_getName(sel);
+ // If it starts with test, run it.
+ if (strstr(name, "test") == name) {
+ fixtureTotal += 1;
+ NSDate *caseStartDate = [NSDate date];
+ BOOL failed = NO;
+ @try {
+ // Wrap things in autorelease pools because they may
+ // have an STMacro in their dealloc which may get called
+ // when the pool is cleaned up
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ @try {
+ [testcase setUp];
+ [testcase performSelector:sel];
+ [testcase tearDown];
+ fixtureSuccesses += 1;
+ } @catch (NSException *exception) {
+ fixtureFailures += 1;
+ failed = YES;
+ [self printError:[exception reason]];
+ }
+ [pool release];
+ } @catch (NSException *exception) {
+ fixtureFailures += 1;
+ failed = YES;
+ [self printError:[exception reason]];
+ }
+ NSTimeInterval caseEndTime = [[NSDate date] timeIntervalSinceDate:caseStartDate];
+ NSString *caseEndString = [NSString stringWithFormat:@"Test Case '-[%@ %s]' %s (%0.3f seconds).\n",
+ fixtureName, name,
+ failed ? "failed" : "passed", caseEndTime];
+ fprintf(stderr, [caseEndString UTF8String]);
+ fflush(stderr);
+ }
+ }
+ if (methods) {
+ free(methods);
+ }
+ [testcase release];
+ NSDate *fixtureEndDate = [NSDate date];
+ NSTimeInterval fixtureEndTime = [fixtureEndDate timeIntervalSinceDate:fixtureStartDate];
+ NSString *fixtureEndString = [NSString stringWithFormat:@"Test Suite '%@' finished at %@.\n"
+ "Executed %d tests, with %d failures (%d unexpected) in %0.3f seconds\n",
+ fixtureName, fixtureEndDate, fixtureTotal,
+ fixtureFailures, fixtureFailures, fixtureEndTime];
+
+ fprintf(stderr, [fixtureEndString UTF8String]);
+ fflush(stderr);
+ suiteTotal += fixtureTotal;
+ suiteSuccesses += fixtureSuccesses;
+ suiteFailures += fixtureFailures;
+ }
+ }
+ NSDate *suiteEndDate = [NSDate date];
+ NSTimeInterval suiteEndTime = [suiteEndDate timeIntervalSinceDate:suiteStartDate];
+ NSString *suiteEndString = [NSString stringWithFormat:@"Test Suite '%@' finished at %@.\n"
+ "Executed %d tests, with %d failures (%d unexpected) in %0.3f seconds\n",
+ suiteName, suiteEndDate, suiteTotal,
+ suiteFailures, suiteFailures, suiteEndTime];
+ fprintf(stderr, [suiteEndString UTF8String]);
+ fflush(stderr);
+
+ // Using private call to end our tests
+ [[UIApplication sharedApplication] terminate];
+}
+
+@end
+
+int main(int argc, char *argv[]) {
+ NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+ int retVal = UIApplicationMain(argc, argv, nil, @"GTMIPhoneUnitTestDelegate");
+ [pool release];
+ return retVal;
+}
diff --git a/UnitTesting/GTMNSObject+BindingUnitTesting.h b/UnitTesting/GTMNSObject+BindingUnitTesting.h
new file mode 100644
index 0000000..947221a
--- /dev/null
+++ b/UnitTesting/GTMNSObject+BindingUnitTesting.h
@@ -0,0 +1,103 @@
+//
+// GTMNSObject+BindingUnitTesting.h
+//
+// Utilities for doing advanced unittesting with object bindings.
+//
+// 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>
+
+// Utility functions for GTMTestExposedBindings Macro. Don't use it directly
+// but use the macro below instead
+BOOL GTMDoExposedBindingsFunctionCorrectly(NSObject *object,
+ NSArray **errors);
+
+// Tests the setters and getters for exposed bindings
+// For objects that expose bindings, this tests them for you, saving you from
+// having to write a whole pile of set/get test code if you add binding support.
+// You will need to implement valueClassForBinding: for your bindings,
+// and you may possibly want to implement unitTestExposedBindingsToIgnore
+// and unitTestExposedBindingsTestValues. See descriptions of those
+// methods below for details.
+// Implemented as a macro to match the rest of the SenTest macros.
+//
+// Args:
+// a1: The object to be checked.
+// description: A format string as in the printf() function.
+// Can be nil or an empty string but must be present.
+// ...: A variable number of arguments to the format string. Can be absent.
+//
+#define GTMTestExposedBindings(a1, description, ...) \
+do { \
+ NSObject *a1Object = (a1); \
+ NSArray *errors = nil; \
+ BOOL isGood = GTMDoExposedBindingsFunctionCorrectly(a1Object, &errors); \
+ if (!isGood) { \
+ NSEnumerator *errorEnum = [errors objectEnumerator]; \
+ NSString *failString; \
+ while ((failString = [errorEnum nextObject])) { \
+ if (description) { \
+ STFail(@"%@: %@", failString, STComposeString(description, ##__VA_ARGS__)); \
+ } else { \
+ STFail(@"%@", failString); \
+ } \
+ } \
+ } \
+} while(0)
+
+@interface NSObject (GTMBindingUnitTestingAdditions)
+// Allows you to ignore certain bindings when running GTMTestExposedBindings
+// If you have bindings you want to ignore, add them to the array returned
+// by this method. The standard way to implement this would be:
+// - (NSMutableArray*)unitTestExposedBindingsToIgnore {
+// NSMutableArray *array = [super unitTestExposedBindingsToIgnore];
+// [array addObject:@"bindingToIgnore1"];
+// ...
+// return array;
+// }
+// The NSObject implementation by default will ignore NSFontBoldBinding,
+// NSFontFamilyNameBinding, NSFontItalicBinding, NSFontNameBinding and
+// NSFontSizeBinding if your exposed bindings contains NSFontBinding because
+// the NSFont*Bindings are NOT KVC/KVO compliant.
+- (NSMutableArray*)gtm_unitTestExposedBindingsToIgnore;
+
+// Allows you to set up test values for your different bindings.
+// if you have certain values you want to test against your bindings, add
+// them to the dictionary returned by this method. The dictionary is a "value" key
+// and an "expected return" object.
+// The standard way to implement this would be:
+// - (NSMutableDictionary*)gtm_unitTestExposedBindingsTestValues:(NSString*)binding {
+// NSMutableDictionary *dict = [super unitTestExposedBindingsTestValues:binding];
+// if ([binding isEqualToString:@"myBinding"]) {
+// [dict setObject:[[[MySpecialBindingValueSet alloc] init] autorelease]
+// forKey:[[[MySpecialBindingValueGet alloc] init] autorelease]];
+// ...
+// else if ([binding isEqualToString:@"myBinding2"]) {
+// ...
+// }
+// return array;
+// }
+// The NSObject implementation handles many of the default bindings, and
+// gives you a reasonable set of test values to start.
+// See the implementation for the current list of bindings, and values that we
+// set for those bindings.
+- (NSMutableDictionary*)gtm_unitTestExposedBindingsTestValues:(NSString*)binding;
+
+// A special version of isEqualTo to test whether two binding values are equal
+// by default it calls directly to isEqualTo: but can be overridden for special
+// cases (like NSImages) where the standard isEqualTo: isn't sufficient.
+- (BOOL)gtm_unitTestIsEqualTo:(id)value;
+@end
diff --git a/UnitTesting/GTMNSObject+BindingUnitTesting.m b/UnitTesting/GTMNSObject+BindingUnitTesting.m
new file mode 100644
index 0000000..03d723d
--- /dev/null
+++ b/UnitTesting/GTMNSObject+BindingUnitTesting.m
@@ -0,0 +1,440 @@
+//
+// GTMNSObject+BindingUnitTesting.m
+//
+// An informal protocol for doing advanced binding unittesting with objects.
+//
+// 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 "GTMDefines.h"
+#import "GTMNSObject+BindingUnitTesting.h"
+
+BOOL GTMDoExposedBindingsFunctionCorrectly(NSObject *object,
+ NSArray **errors) {
+ NSMutableArray *errorArray = [NSMutableArray array];
+ if (errors) {
+ *errors = nil;
+ }
+ NSArray *bindings = [object exposedBindings];
+ if ([bindings count]) {
+ NSArray *bindingsToIgnore = [object gtm_unitTestExposedBindingsToIgnore];
+ NSEnumerator *bindingsEnum = [bindings objectEnumerator];
+ NSString *bindingKey;
+ while ((bindingKey = [bindingsEnum nextObject])) {
+ if (![bindingsToIgnore containsObject:bindingKey]) {
+ Class theClass = [object valueClassForBinding:bindingKey];
+ if (!theClass) {
+ [errorArray addObject:[NSString stringWithFormat:@"%@ should have valueClassForBinding '%@'",
+ object, bindingKey]];
+ continue;
+ }
+ @try {
+ @try {
+ [object valueForKey:bindingKey];
+ }
+ @catch (NSException *e) {
+ _GTMDevLog(@"%@ is not key value coding compliant for key %@", object, bindingKey);
+ continue;
+ } // COV_NF_LINE - compiler bug
+ NSDictionary *testValues = [object gtm_unitTestExposedBindingsTestValues:bindingKey];
+ NSEnumerator *testEnum = [testValues keyEnumerator];
+ id testValue;
+ while ((testValue = [testEnum nextObject])) {
+ [object setValue:testValue forKey:bindingKey];
+ id value = [object valueForKey:bindingKey];
+ id desiredValue = [testValues objectForKey:testValue];
+ if (![desiredValue gtm_unitTestIsEqualTo:value]) {
+ [errorArray addObject:[NSString stringWithFormat:@"%@ unequal to %@ for binding '%@'",
+ value, desiredValue, bindingKey]];
+ continue;
+ }
+ }
+ }
+ @catch(NSException *e) {
+ [errorArray addObject:[NSString stringWithFormat:@"%@:%@-> Binding %@",
+ [e name], [e reason], bindingKey]];
+ } // COV_NF_LINE - compiler bug
+ }
+ }
+ } else {
+ [errorArray addObject:[NSString stringWithFormat:@"%@ does not have any exposed bindings",
+ object]];
+ }
+ if (errors) {
+ *errors = errorArray;
+ }
+ return [errorArray count] == 0;
+}
+
+// Utility for simplifying unitTestExposedBindingsTestValues implementations
+@interface NSMutableDictionary (GTMUnitTestingAdditions)
+// Sets an object and a key to the same value in a dictionary.
+- (void)gtm_setObjectAndKey:(id)objectAndKey;
+@end
+
+@implementation NSObject (GTMBindingUnitTestingAdditions)
+
+- (NSMutableArray*)gtm_unitTestExposedBindingsToIgnore {
+ NSMutableArray *array = [NSMutableArray arrayWithObject:NSValueBinding];
+ if ([[self exposedBindings] containsObject:NSFontBinding]) {
+ NSString *fontBindings[] = { NSFontBoldBinding, NSFontFamilyNameBinding,
+ NSFontItalicBinding, NSFontNameBinding, NSFontSizeBinding };
+ for (size_t i = 0; i < sizeof(fontBindings) / sizeof(NSString*); ++i) {
+ [array addObject:fontBindings[i]];
+ }
+ }
+ return array;
+}
+
+- (NSMutableDictionary*)gtm_unitTestExposedBindingsTestValues:(NSString*)binding {
+
+ NSMutableDictionary *dict = [NSMutableDictionary dictionary];
+ id value = [self valueForKey:binding];
+
+ // Always test identity if possible
+ if (value) {
+ [dict gtm_setObjectAndKey:value];
+ }
+
+ // Now some default test values for a variety of bindings to make
+ // sure that we cover all the bases and save other people writing lots of
+ // duplicate test code.
+
+ // If anybody can think of more to add, please go nuts.
+ if ([binding isEqualToString:NSAlignmentBinding]) {
+ [dict gtm_setObjectAndKey:[NSNumber numberWithInt:NSLeftTextAlignment]];
+ [dict gtm_setObjectAndKey:[NSNumber numberWithInt:NSRightTextAlignment]];
+ [dict gtm_setObjectAndKey:[NSNumber numberWithInt:NSCenterTextAlignment]];
+ [dict gtm_setObjectAndKey:[NSNumber numberWithInt:NSJustifiedTextAlignment]];
+ NSNumber *natural = [NSNumber numberWithInt:NSNaturalTextAlignment];
+ [dict gtm_setObjectAndKey:natural];
+ [dict setObject:natural forKey:[NSNumber numberWithInt:500]];
+ [dict setObject:natural forKey:[NSNumber numberWithInt:-1]];
+ } else if ([binding isEqualToString:NSAlternateImageBinding] ||
+ [binding isEqualToString:NSImageBinding] ||
+ [binding isEqualToString:NSMixedStateImageBinding] ||
+ [binding isEqualToString:NSOffStateImageBinding] ||
+ [binding isEqualToString:NSOnStateImageBinding]) {
+ // This handles all image bindings
+ [dict gtm_setObjectAndKey:[NSImage imageNamed:@"NSApplicationIcon"]];
+ } else if ([binding isEqualToString:NSAnimateBinding] ||
+ [binding isEqualToString:NSDocumentEditedBinding] ||
+ [binding isEqualToString:NSEditableBinding] ||
+ [binding isEqualToString:NSEnabledBinding] ||
+ [binding isEqualToString:NSHiddenBinding] ||
+ [binding isEqualToString:NSVisibleBinding] ||
+ [binding isEqualToString:NSIsIndeterminateBinding] ||
+ // NSTranparentBinding 10.5 only
+ [binding isEqualToString:@"transparent"]) {
+ // This handles all bool value bindings
+ [dict gtm_setObjectAndKey:[NSNumber numberWithBool:YES]];
+ [dict gtm_setObjectAndKey:[NSNumber numberWithBool:NO]];
+ } else if ([binding isEqualToString:NSAlternateTitleBinding] ||
+ [binding isEqualToString:NSHeaderTitleBinding] ||
+ [binding isEqualToString:NSLabelBinding] ||
+ [binding isEqualToString:NSTitleBinding] ||
+ [binding isEqualToString:NSToolTipBinding]) {
+ // This handles all string value bindings
+ [dict gtm_setObjectAndKey:@"happy"];
+ [dict gtm_setObjectAndKey:@""];
+
+ // Test some non-ascii roman text
+ char a_not_alpha[] = { 'A', 0xE2, 0x89, 0xA2, 0xCE, 0x91, '.', 0x00 };
+ [dict gtm_setObjectAndKey:[NSString stringWithUTF8String:a_not_alpha]];
+ // Test some korean
+ char hangugo[] = { 0xED, 0x95, 0x9C, 0xEA, 0xB5,
+ 0xAD, 0xEC, 0x96, 0xB4, 0x00 };
+ [dict gtm_setObjectAndKey:[NSString stringWithUTF8String:hangugo]];
+ // Test some japanese
+ char nihongo[] = { 0xE6, 0x97, 0xA5, 0xE6, 0x9C,
+ 0xAC, 0xE8, 0xAA, 0x9E, 0x00 };
+ [dict gtm_setObjectAndKey:[NSString stringWithUTF8String:nihongo]];
+ // Test some arabic
+ char arabic[] = { 0xd9, 0x83, 0xd8, 0xa7, 0xd9, 0x83, 0xd8, 0xa7, 0x00 };
+ [dict gtm_setObjectAndKey:[NSString stringWithUTF8String:arabic]];
+ } else if ([binding isEqualToString:NSRepresentedFilenameBinding]) {
+ // This handles all path bindings
+ [dict gtm_setObjectAndKey:@"/happy"];
+ [dict gtm_setObjectAndKey:@"/"];
+
+ // Test some non-ascii roman text
+ char a_not_alpha[] = { '/', 'A', 0xE2, 0x89, 0xA2, 0xCE, 0x91, '.', 0x00 };
+ [dict gtm_setObjectAndKey:[NSString stringWithUTF8String:a_not_alpha]];
+ // Test some korean
+ char hangugo[] = { '/', 0xED, 0x95, 0x9C, 0xEA, 0xB5,
+ 0xAD, 0xEC, 0x96, 0xB4, 0x00 };
+ [dict gtm_setObjectAndKey:[NSString stringWithUTF8String:hangugo]];
+ // Test some japanese
+ char nihongo[] = { '/', 0xE6, 0x97, 0xA5, 0xE6, 0x9C,
+ 0xAC, 0xE8, 0xAA, 0x9E, 0x00 };
+ [dict gtm_setObjectAndKey:[NSString stringWithUTF8String:nihongo]];
+ // Test some arabic
+ char arabic[] = { '/', 0xd9, 0x83, 0xd8, 0xa7, 0xd9, 0x83, 0xd8, 0xa7, 0x00 };
+ [dict gtm_setObjectAndKey:[NSString stringWithUTF8String:arabic]];
+ } else if ([binding isEqualToString:NSMaximumRecentsBinding] ||
+ [binding isEqualToString:NSRowHeightBinding]) {
+ // This handles all int value bindings
+ [dict gtm_setObjectAndKey:[NSNumber numberWithInt:0]];
+ [dict gtm_setObjectAndKey:[NSNumber numberWithInt:-1]];
+ [dict gtm_setObjectAndKey:[NSNumber numberWithInt:INT16_MAX]];
+ [dict gtm_setObjectAndKey:[NSNumber numberWithInt:INT16_MIN]];
+ } else if ([binding isEqualToString:NSMaxValueBinding] ||
+ [binding isEqualToString:NSMaxWidthBinding] ||
+ [binding isEqualToString:NSMinValueBinding] ||
+ [binding isEqualToString:NSMinWidthBinding] ||
+ [binding isEqualToString:NSContentWidthBinding] ||
+ [binding isEqualToString:NSContentHeightBinding] ||
+ [binding isEqualToString:NSWidthBinding] ||
+ [binding isEqualToString:NSAnimationDelayBinding]) {
+ // This handles all float value bindings
+ [dict gtm_setObjectAndKey:[NSNumber numberWithFloat:0]];
+ [dict gtm_setObjectAndKey:[NSNumber numberWithFloat:FLT_MAX]];
+ [dict gtm_setObjectAndKey:[NSNumber numberWithFloat:-FLT_MAX]];
+ [dict gtm_setObjectAndKey:[NSNumber numberWithFloat:FLT_MIN]];
+ [dict gtm_setObjectAndKey:[NSNumber numberWithFloat:-FLT_MIN]];
+ [dict gtm_setObjectAndKey:[NSNumber numberWithFloat:FLT_EPSILON]];
+ [dict gtm_setObjectAndKey:[NSNumber numberWithFloat:-FLT_EPSILON]];
+ } else if ([binding isEqualToString:NSTextColorBinding]) {
+ // This handles all color value bindings
+ [dict gtm_setObjectAndKey:[NSColor colorWithCalibratedWhite:1.0 alpha:1.0]];
+ [dict gtm_setObjectAndKey:[NSColor colorWithCalibratedWhite:1.0 alpha:0.0]];
+ [dict gtm_setObjectAndKey:[NSColor colorWithCalibratedWhite:1.0 alpha:0.5]];
+ [dict gtm_setObjectAndKey:[NSColor colorWithCalibratedRed:0.5 green:0.5
+ blue:0.5 alpha:0.5]];
+ [dict gtm_setObjectAndKey:[NSColor colorWithDeviceCyan:0.25 magenta:0.25
+ yellow:0.25 black:0.25
+ alpha:0.25]];
+ } else if ([binding isEqualToString:NSFontBinding]) {
+ // This handles all font value bindings
+ [dict gtm_setObjectAndKey:[NSFont boldSystemFontOfSize:[NSFont systemFontSize]]];
+ [dict gtm_setObjectAndKey:[NSFont toolTipsFontOfSize:[NSFont smallSystemFontSize]]];
+ [dict gtm_setObjectAndKey:[NSFont labelFontOfSize:144.0]];
+ } else if ([binding isEqualToString:NSRecentSearchesBinding] ||
+ [binding isEqualToString:NSSortDescriptorsBinding]) {
+ // This handles all array value bindings
+ [dict gtm_setObjectAndKey:[NSArray array]];
+ } else if ([binding isEqualToString:NSTargetBinding]) {
+ [dict gtm_setObjectAndKey:[NSNull null]];
+ } else {
+ _GTMDevLog(@"Skipped Binding: %@ for %@", binding, self); // COV_NF_LINE
+ }
+ return dict;
+}
+
+- (BOOL)gtm_unitTestIsEqualTo:(id)value {
+ return [self isEqualTo:value];
+}
+
+@end
+
+@implementation NSMutableDictionary (GTMUnitTestingAdditions)
+// Sets an object and a key to the same value in a dictionary.
+- (void)gtm_setObjectAndKey:(id)objectAndKey {
+ [self setObject:objectAndKey forKey:objectAndKey];
+}
+@end
+
+#pragma mark -
+#pragma mark All the special AppKit Bindings issues below
+
+@interface NSImage (GTMBindingUnitTestingAdditions)
+@end
+
+@implementation NSImage (GTMBindingUnitTestingAdditions)
+- (BOOL)gtm_unitTestIsEqualTo:(id)value {
+ // NSImage just does pointer equality in the default isEqualTo implementation
+ // we need something a little more heavy duty that actually compares the
+ // images internally.
+ return [[self TIFFRepresentation] isEqualTo:[value TIFFRepresentation]];
+}
+@end
+
+@interface NSScroller (GTMBindingUnitTestingAdditions)
+@end
+
+@implementation NSScroller (GTMBindingUnitTestingAdditions)
+- (NSMutableArray*)gtm_unitTestExposedBindingsToIgnore {
+ NSMutableArray *array = [super gtm_unitTestExposedBindingsToIgnore];
+ // rdar://5849154 - NSScroller exposes binding with no value class for NSValueBinding
+ [array addObject:NSValueBinding];
+ // rdar://5849236 - NSScroller exposes binding for NSFontBinding
+ [array addObject:NSFontBinding];
+ return array;
+}
+@end
+
+@interface NSTextField (GTMBindingUnitTestingAdditions)
+@end
+
+@implementation NSTextField (GTMBindingUnitTestingAdditions)
+
+- (NSMutableArray*)gtm_unitTestExposedBindingsToIgnore {
+ NSMutableArray *array = [super gtm_unitTestExposedBindingsToIgnore];
+ // Not KVC Compliant
+ for (int i = 0; i < 10; i++) {
+ [array addObject:[NSString stringWithFormat:@"displayPatternValue%d", i]];
+ }
+ return array;
+}
+
+- (NSMutableDictionary*)gtm_unitTestExposedBindingsTestValues:(NSString*)binding {
+ NSMutableDictionary *dict = [super gtm_unitTestExposedBindingsTestValues:binding];
+ if ([binding isEqualToString:NSAlignmentBinding]) {
+ // rdar://5851487 - If NSAlignmentBinding for a NSTextField is set to -1 and then got it returns 7
+ [dict setObject:[NSNumber numberWithInt:7] forKey:[NSNumber numberWithInt:-1]];
+ }
+ return dict;
+}
+@end
+
+@interface NSSearchField (GTMBindingUnitTestingAdditions)
+@end
+
+@implementation NSSearchField (GTMBindingUnitTestingAdditions)
+
+- (NSMutableDictionary*)gtm_unitTestExposedBindingsTestValues:(NSString*)binding {
+ NSMutableDictionary *dict = [super gtm_unitTestExposedBindingsTestValues:binding];
+ if ([binding isEqualToString:NSAlignmentBinding]) {
+ // rdar://5851491 - Setting NSAlignmentBinding of search field to NSCenterTextAlignment broken
+ [dict setObject:[NSNumber numberWithInt:NSNaturalTextAlignment]
+ forKey:[NSNumber numberWithInt:NSCenterTextAlignment]];
+ }
+ return dict;
+}
+
+- (NSMutableArray*)gtm_unitTestExposedBindingsToIgnore {
+ NSMutableArray *array = [super gtm_unitTestExposedBindingsToIgnore];
+ // Not KVC Compliant
+ [array addObject:NSPredicateBinding];
+ return array;
+}
+
+@end
+
+@interface NSWindow (GTMBindingUnitTestingAdditions)
+@end
+
+@implementation NSWindow (GTMBindingUnitTestingAdditions)
+
+- (NSMutableArray*)gtm_unitTestExposedBindingsToIgnore {
+ NSMutableArray *array = [super gtm_unitTestExposedBindingsToIgnore];
+ // Not KVC Compliant
+ [array addObject:NSContentWidthBinding];
+ [array addObject:NSContentHeightBinding];
+ for (int i = 0; i < 10; i++) {
+ [array addObject:[NSString stringWithFormat:@"displayPatternTitle%d", i]];
+ }
+ return array;
+}
+
+@end
+
+@interface NSBox (GTMBindingUnitTestingAdditions)
+@end
+
+@implementation NSBox (GTMBindingUnitTestingAdditions)
+
+- (NSMutableArray*)gtm_unitTestExposedBindingsToIgnore {
+ NSMutableArray *array = [super gtm_unitTestExposedBindingsToIgnore];
+ // Not KVC Compliant
+ for (int i = 0; i < 10; i++) {
+ [array addObject:[NSString stringWithFormat:@"displayPatternTitle%d", i]];
+ }
+ return array;
+}
+
+@end
+
+@interface NSTableView (GTMBindingUnitTestingAdditions)
+@end
+
+@implementation NSTableView (GTMBindingUnitTestingAdditions)
+
+- (NSMutableArray*)gtm_unitTestExposedBindingsToIgnore {
+ NSMutableArray *array = [super gtm_unitTestExposedBindingsToIgnore];
+ // rdar://5849684 - NSTableView should throw exception when attempting to set NSFontBinding
+ [array addObject:NSFontBinding];
+ // Not KVC Compliant
+ [array addObject:NSContentBinding];
+ [array addObject:NSDoubleClickTargetBinding];
+ [array addObject:NSDoubleClickArgumentBinding];
+ [array addObject:NSSelectionIndexesBinding];
+ return array;
+}
+
+@end
+
+@interface NSTextView (GTMBindingUnitTestingAdditions)
+@end
+
+@implementation NSTextView (GTMBindingUnitTestingAdditions)
+
+- (NSMutableArray*)gtm_unitTestExposedBindingsToIgnore {
+ NSMutableArray *array = [super gtm_unitTestExposedBindingsToIgnore];
+ //rdar://5849335 - NSTextView only partially KVC compliant for key NSAttributedStringBinding
+ [array addObject:NSAttributedStringBinding];
+ // Not KVC Compliant
+ [array addObject:NSDataBinding];
+ [array addObject:NSValueURLBinding];
+ [array addObject:NSValuePathBinding];
+ return array;
+}
+
+@end
+
+@interface NSTabView (GTMBindingUnitTestingAdditions)
+@end
+
+@implementation NSTabView (GTMBindingUnitTestingAdditions)
+
+- (NSMutableArray*)gtm_unitTestExposedBindingsToIgnore {
+ NSMutableArray *array = [super gtm_unitTestExposedBindingsToIgnore];
+ // rdar://5849248 - NSTabView exposes binding with no value class for NSSelectedIdentifierBinding
+ [array addObject:NSSelectedIdentifierBinding];
+ // Not KVC Compliant
+ [array addObject:NSSelectedIndexBinding];
+ [array addObject:NSSelectedLabelBinding];
+ return array;
+}
+
+@end
+
+@interface NSButton (GTMBindingUnitTestingAdditions)
+@end
+
+@implementation NSButton (GTMBindingUnitTestingAdditions)
+
+- (NSMutableArray*)gtm_unitTestExposedBindingsToIgnore {
+ NSMutableArray *array = [super gtm_unitTestExposedBindingsToIgnore];
+ // Not KVC Compliant
+ [array addObject:NSArgumentBinding];
+ return array;
+}
+
+@end
+
+@interface NSProgressIndicator (GTMBindingUnitTestingAdditions)
+@end
+
+@implementation NSProgressIndicator (GTMBindingUnitTestingAdditions)
+
+- (NSMutableArray*)gtm_unitTestExposedBindingsToIgnore {
+ NSMutableArray *array = [super gtm_unitTestExposedBindingsToIgnore];
+ // Not KVC Compliant
+ [array addObject:NSAnimateBinding];
+ return array;
+}
+
+@end
diff --git a/UnitTesting/GTMNSObject+UnitTesting.h b/UnitTesting/GTMNSObject+UnitTesting.h
index 73a2c9a..c06e001 100644
--- a/UnitTesting/GTMNSObject+UnitTesting.h
+++ b/UnitTesting/GTMNSObject+UnitTesting.h
@@ -18,91 +18,80 @@
// the License.
//
-#include <Cocoa/Cocoa.h>
+#import "GTMDefines.h"
+#import <Foundation/Foundation.h>
-/// Fails when image of |a1| does not equal image in TIFF file named |a2|
+#if GTM_MACOS_SDK
+#import <ApplicationServices/ApplicationServices.h>
+#endif
+
+#import "GTMSenTestCase.h"
+
+// Utility functions for GTMAssert* Macros. Don't use them directly
+// but use the macros below instead
+BOOL GTMIsObjectImageEqualToImageNamed(id object,
+ NSString *filename,
+ NSString **error);
+BOOL GTMIsObjectStateEqualToStateNamed(id object,
+ NSString *filename,
+ NSString **error);
+
+// Fails when image of |a1| does not equal image in image file named |a2|
//
// Generates a failure when the unittest image of |a1| is not equal to the
-// image stored in the TIFF file named |a2|, or |a2| does not exist in the
+// image stored in the image file named |a2|, or |a2| does not exist in the
// executable code's bundle.
-// If |a2| does not exist in the executable code's bundle, we save a TIFF
-// representation of |a1| on the desktop with name |a2|. This can then be
-// included in the bundle as the master to test against.
-// If |a2| != |a1|, we save a TIFF representation of |a1| on the desktop
-// with name |a2|_Failed so that we can compare the two files to see what
-// has changed.
-// See pathForTIFFNamed to see how name is searched for.
+// If |a2| does not exist in the executable code's bundle, we save a image
+// representation of |a1| in the save directory with name |a2|. This can then
+// be included in the bundle as the master to test against.
+// If |a2| != |a1|, we save a image representation of |a1| in the save
+// directory named |a2|_Failed and a file named |a2|_Failed_Diff showing the
+// diff in red so that we can see what has changed.
+// See pathForImageNamed to see how name is searched for.
+// The save directory is specified by +gtm_setUnitTestSaveToDirectory, and is
+// the desktop by default.
// Implemented as a macro to match the rest of the SenTest macros.
//
// Args:
-// a1: The object to be checked. Must implement the -unitTestImage method.
-// a2: The name of the TIFF file to check against.
+// a1: The object to be checked. Must implement the -createUnitTestImage method.
+// a2: The name of the image file to check against.
// Do not include the extension
// description: A format string as in the printf() function.
// Can be nil or an empty string but must be present.
// ...: A variable number of arguments to the format string. Can be absent.
//
-#define GTMAssertObjectImageEqualToTIFFNamed(a1, a2, description, ...) \
+#define GTMAssertObjectImageEqualToImageNamed(a1, a2, description, ...) \
do { \
- NSObject* a1Object = (a1); \
+ id a1Object = (a1); \
NSString* a2String = (a2); \
NSString *failString = nil; \
- BOOL isGood = [a1Object respondsToSelector:@selector(unitTestImage)]; \
- if (isGood) { \
- if (![a1Object areSystemSettingsValidForDoingImage]) { \
- break; \
- } \
- NSString *aPath = [a1Object pathForTIFFNamed:a2String]; \
- isGood = aPath != nil; \
- if (isGood) { \
- isGood = [a1Object compareWithTIFFAt:aPath]; \
- } \
- if (!isGood) { \
- if (aPath != nil) { \
- a2String = [a2String stringByAppendingString:@"_Failed"]; \
- } \
- BOOL aSaved = [a1Object saveToTIFFNamed:a2String]; \
- if (NO == aSaved) {\
- if (aPath == nil) { \
- failString = [NSString stringWithFormat:@"File %@ did not exist in bundle. Tried to save %@ to desktop and failed.", a2String, a2String]; \
- } else { \
- failString = [NSString stringWithFormat:@"Object image different than file %@. Tried to save to desktop as %@ and failed.", aPath, a2String]; \
- } \
- } else { \
- if (aPath == nil) { \
- failString = [NSString stringWithFormat:@"File %@ did not exist in bundle. Saved to ~/Desktop/%@", a2String, a2String]; \
- } else { \
- failString = [NSString stringWithFormat:@"Object image different than file %@. Saved image to desktop as %@.", aPath, a2String]; \
- } \
- } \
- } \
- } else { \
- failString = @"Object does not respond to -unitTestImage"; \
- } \
+ BOOL isGood = GTMIsObjectImageEqualToImageNamed(a1Object, a2String, &failString); \
if (!isGood) { \
- if (nil != description) { \
- STFail(@"%@: %@", STComposeString(description, ##__VA_ARGS__), failString); \
+ if (description) { \
+ STFail(@"%@: %@", failString, STComposeString(description, ##__VA_ARGS__)); \
} else { \
STFail(@"%@", failString); \
} \
} \
} while(0)
-/// Fails when state of |a1| does not equal state in file |a2|
+// Fails when state of |a1| does not equal state in file |a2|
//
// Generates a failure when the unittest state of |a1| is not equal to the
// state stored in the state file named |a2|, or |a2| does not exist in the
// executable code's bundle.
// If |a2| does not exist in the executable code's bundle, we save a state
-// representation of |a1| on the desktop with name |a2|. This can then be
-// included in the bundle as the master to test against.
-// If |a2| != |a1|, we save a state representation of |a1| on the desktop
-// with name |a2|_Failed so that we can compare the two files to see what
-// has changed.
+// representation of |a1| in the save directiry with name |a2|. This can then
+// be included in the bundle as the master to test against.
+// If |a2| != |a1|, we save a state representation of |a1| in the save
+// directory with name |a2|_Failed so that we can compare the two files to see
+// what has changed.
+// The save directory is specified by +gtm_setUnitTestSaveToDirectory, and is
+// the desktop by default.
// Implemented as a macro to match the rest of the SenTest macros.
//
// Args:
-// a1: The object to be checked. Must implement the -unitTestImage method.
+// a1: The object to be checked. Must implement the -createUnitTestImage method.
// a2: The name of the state file to check against.
// Do not include the extension
// description: A format string as in the printf() function.
@@ -111,97 +100,39 @@ do { \
//
#define GTMAssertObjectStateEqualToStateNamed(a1, a2, description, ...) \
do { \
- NSObject* a1Object = (a1); \
+ id a1Object = (a1); \
NSString* a2String = (a2); \
NSString *failString = nil; \
- BOOL isGood = [a1Object respondsToSelector:@selector(unitTestEncodeState:)]; \
- if (isGood) { \
- NSString *aPath = [a1Object pathForStateNamed:a2String]; \
- isGood = aPath != nil; \
- if (isGood) { \
- isGood = [a1Object compareWithStateAt:aPath]; \
- } \
- if (!isGood) { \
- if (aPath != nil) { \
- a2String = [a2String stringByAppendingString:@"_Failed"]; \
- } \
- BOOL aSaved = [a1Object saveToStateNamed:a2String]; \
- if (NO == aSaved) {\
- if (aPath == nil) { \
- failString = [NSString stringWithFormat:@"File %@ did not exist in bundle. Tried to save %@ to desktop and failed.", a2String, a2String]; \
- } else { \
- failString = [NSString stringWithFormat:@"Object state different than file %@. Tried to save to desktop as %@ and failed.", aPath, a2String]; \
- } \
- } else { \
- if (aPath == nil) { \
- failString = [NSString stringWithFormat:@"File %@ did not exist in bundle. Saved to ~/Desktop/%@", a2String, a2String]; \
- } else { \
- failString = [NSString stringWithFormat:@"Object state different than file %@. Saved image to desktop as %@.", aPath, a2String]; \
- } \
- } \
- } \
- } else { \
- failString = @"Object does not respond to -unitTestEncodeState:"; \
- } \
+ BOOL isGood = GTMIsObjectStateEqualToStateNamed(a1Object, a2String, &failString); \
if (!isGood) { \
- if (nil != description) { \
- STFail(@"%@: %@", STComposeString(description, ##__VA_ARGS__), failString); \
+ if (description) { \
+ STFail(@"%@: %@", failString, STComposeString(description, ##__VA_ARGS__)); \
} else { \
STFail(@"%@", failString); \
} \
} \
-} while(0)
+} while(0);
-/// test both GTMAssertObjectImageEqualToTIFFNamed and GTMAssertObjectStateEqualToStateNamed
+// test both GTMAssertObjectImageEqualToImageNamed and GTMAssertObjectStateEqualToStateNamed
//
// Combines the above two macros into a single ubermacro for comparing
// both state and image. When only the best will do...
#define GTMAssertObjectEqualToStateAndImageNamed(a1, a2, description, ...) \
do { \
- GTMAssertObjectImageEqualToTIFFNamed(a1, a2, description, ##__VA_ARGS__); \
+ GTMAssertObjectImageEqualToImageNamed(a1, a2, description, ##__VA_ARGS__); \
GTMAssertObjectStateEqualToStateNamed(a1, a2, description, ##__VA_ARGS__); \
} while (0)
-/// Tests the setters and getters for exposed bindings
-// For objects that expose bindings, this tests them for you, saving you from
-// having to write a whole pile of set/get test code if you add binding support.
-// You will need to implement valueClassForBinding: for your bindings,
-// and you may possibly want to implement unitTestExposedBindingsToIgnore
-// and unitTestExposedBindingsTestValues. See descriptions of those
-// methods below for details.
-// Implemented as a macro to match the rest of the SenTest macros.
+// GTMUnitTestingImaging protocol is for objects which need to save their
+// image for using with the unit testing categories
+@protocol GTMUnitTestingImaging
+// Create a CGImageRef containing a representation suitable for use in
+// comparing against a master image.
//
-// Args:
-// a1: The object to be checked.
-// description: A format string as in the printf() function.
-// Can be nil or an empty string but must be present.
-// ...: A variable number of arguments to the format string. Can be absent.
-//
-#define GTMTestExposedBindings(a1, description, ...) \
-do { \
- NSArray *bindings = [a1 exposedBindings]; \
- if (bindings) { \
- NSArray *bindingsToIgnore = [a1 unitTestExposedBindingsToIgnore]; \
- NSEnumerator *bindingsEnum = [bindings objectEnumerator]; \
- NSString *bindingKey; \
- while ((bindingKey = [bindingsEnum nextObject])) { \
- if (![bindingsToIgnore containsObject:bindingKey]) { \
- Class theClass = [a1 valueClassForBinding:bindingKey]; \
- STAssertNotNil(theClass, @"Should have valueClassForBinding %@", bindingKey); \
- NSDictionary *testValues = [a1 unitTestExposedBindingsTestValues:bindingKey]; \
- NSEnumerator *testEnum = [testValues keyEnumerator]; \
- id testValue; \
- while ((testValue = [testEnum nextObject])) { \
- [a1 setValue:testValue forKey:bindingKey]; \
- id value = [a1 valueForKey:bindingKey]; \
- STAssertEqualObjects([testValues objectForKey:testValue], value, description, ##__VA_ARGS__); \
- } \
- } \
- } \
- } \
-} while(0)
-
-/// \cond Protocols
+// Returns:
+// an CGImageRef of the object. Caller must release
+- (CGImageRef)gtm_createUnitTestImage;
+@end
// GTMUnitTestingEncoding protocol is for objects which need to save their
// "state" for using with the unit testing categories
@@ -213,29 +144,18 @@ do { \
//
// Arguments:
// inCoder - the coder to encode our state into
-- (void)unitTestEncodeState:(NSCoder*)inCoder;
+- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder;
@end
-/// Category for saving and comparing object state and image for unit tests
+// Category for saving and comparing object state and image for unit tests
//
// The GTMUnitTestAdditions category gives object the ability to store their
// state for use in unittesting in two different manners.
-// 1) Objects can elect to save their "image" as a TIFF that we can compare at
-// runtime to a TIFF on file to make sure that the representation hasn't
+// 1) Objects can elect to save their "image" that we can compare at
+// runtime to an image file to make sure that the representation hasn't
// changed. All views and Windows can save their image. In the case of Windows,
// they are "bluescreened" so that any transparent areas can be compared between
-// machines. For this to work, the appearance must be set to "Aqua blue" In the
-// case of NSWindows and NSScreens, we do a screen capture operation to capture
-// their image. In these cases, font smoothing settings must be set consistently
-// across machines. The current standard is
-// Font Smoothing Style: Standard - Best For CRT
-// Turn Off Text Smoothing For Font Sizes: 8 And Smaller
-// If you do not have these settings, any unit tests depending on them will not
-// be executed, and a warning will be logged.
-// Also, we need to be careful about avoiding ColorSync. In most cases the
-// unittesting system handles this for you. If you are running into troubles
-// make sure that you are using device colors, and not calibrated colors
-// wherever you are doing drawing.
+// machines.
// 2) Objects can elect to save their "state". State is the attributes that we
// want to verify when running unit tests. Applications, Windows, Views,
// Controls and Cells currently return a variety of state information. If you
@@ -245,131 +165,155 @@ do { \
// information if appropriate via the unitTestEncoderDidEncode:inCoder: delegate
// method.
// To compare state/image in your unit tests, you can use the three macros above
-// GTMAssertObjectStateEqualToStateNamed, GTMAssertObjectImageEqualToTIFFNamed and
+// GTMAssertObjectStateEqualToStateNamed, GTMAssertObjectImageEqualToImageNamed and
// GTMAssertObjectEqualToStateAndImageNamed.
@interface NSObject (GTMUnitTestingAdditions) <GTMUnitTestingEncoding>
-/// Returns an image containing a representation suitable for use in comparing against a master image.
-//
-// NB this means that all colors should be
-// device based, as colorsynced colors will be different on different devices.
+// Allows you to control where the unit test utilities save any files
+// (image or state) that they create on your behalf. By default they
+// will save to the desktop.
++ (void)gtm_setUnitTestSaveToDirectory:(NSString*)path;
++ (NSString *)gtm_getUnitTestSaveToDirectory;
+
+// Create a CGColorSpaceRef appropriate for using in creating a unit test image
+// iPhone uses device colorspace.
+// Returns:
+// an CGColorSpaceRef of the object. Caller must release
+- (CGColorSpaceRef)gtm_createUnitTestColorspace;
+
+// Create a CGBitmapContextRef appropriate for using in creating a unit test
+// image. If data is non-NULL, returns the buffer that the bitmap is
+// using for it's underlying storage. You must free this buffer using
+// free. If data is NULL, uses it's own internal storage.
+// In either case, it will be filled with transparency.
//
// Returns:
-// an image of the object
-- (NSImage*)unitTestImage;
-
-/// Checks to see that system settings are valid for doing an image comparison.
-// The main issue is that we make sure that we are set to using Blue Aqua as
-// our appearance.
-// Instead of directly overriding this, a unit test can just use:
-// needsAquaBlueAppearanceForDoingImage
-// needsScrollBarArrowsLowerRightForDoingImage
-// to enable those tests w/in this base implementation.
-// The other issues are for NSScreen and NSWindow images as they are affected by
-// the font smoothing settings in the system preferences. For things to work
-// these settings must be set to:
-// Font Smoothing Style: Standard - Best For CRT
-// Turn Off Text Smoothing For Font Sizes: 8 And Smaller
+// an CGContextRef of the object. Caller must release
+- (CGContextRef)gtm_createUnitTestBitmapContextOfSize:(CGSize)size
+ data:(unsigned char **)data;
+
+// Checks to see that system settings are valid for doing an image comparison.
+// Most of these are set by our unit test app. See the unit test app main.m
+// for details.
//
// Returns:
// YES if we can do image comparisons for this object type.
-- (BOOL)areSystemSettingsValidForDoingImage;
+- (BOOL)gtm_areSystemSettingsValidForDoingImage;
+
+// Return the type of image to work with. Only valid types on the iPhone
+// are kUTTypeJPEG and kUTTypePNG. MacOS supports several more.
+- (NSString*)gtm_imageUTI;
-/// Checks if this test needs the AquaBlue Appearance for doing the image comparison.
-// If the test uses the appearance colors, this should be overriden to return
-// YES (ie-default is no). This provides a hook so the unittest can be skipped
-// if the running user's settings aren't the "standard" for the UI unitttests.
+// Return the extension to be used for saving unittest images
//
-// Returns:
-// YES if this test needs the AquaBlue Appearance.
-- (BOOL)needsAquaBlueAppearanceForDoingImage;
+// Returns
+// An extension (e.g. "png")
+- (NSString*)gtm_imageExtension;
-/// Checks if this test needs the ScrollBarArrows LowerRight for doing the image comparison.
-// If the test uses the scrollbar drawing, this should be overriden to return
-// YES (ie-default is no). This provides a hook so the unittest can be skipped
-// if the running user's settings aren't the "standard" for the UI unitttests.
+// Return image data in the format expected for gtm_imageExtension
+// So for a "png" extension I would expect "png" data
//
-// Returns:
-// YES if this test needs the ScrollBarArrows LowerRight.
-- (BOOL)needsScrollBarArrowsLowerRightForDoingImage;
+// Returns
+// NSData for image
+- (NSData*)gtm_imageDataForImage:(CGImageRef)image;
-/// Save the unitTestImage to a TIFF file with name |name| at ~/Desktop/|name|.tif.
-// The TIFF will be compressed with LZW.
+// Save the unitTestImage to a image file with name
+// |name|.arch.OSVersionMajor.OSVersionMinor.OSVersionBugfix.extension
+// in the save folder (desktop by default)
//
// Args:
-// name: The name for the TIFF file you would like saved.
+// name: The name for the image file you would like saved.
//
// Returns:
// YES if the file was successfully saved.
//
-- (BOOL)saveToTIFFNamed:(NSString*)name;
+- (BOOL)gtm_saveToImageNamed:(NSString*)name;
-/// Save unitTestImage of |self| to a TIFF file at path |path|.
-// The TIFF will be compressed with LZW. All non-drawn areas will be transparent.
+// Save unitTestImage of |self| to an image file at path |path|.
+// All non-drawn areas will be transparent.
//
// Args:
-// name: The name for the TIFF file you would like saved.
+// name: The name for the image file you would like saved.
//
// Returns:
// YES if the file was successfully saved.
//
-- (BOOL)saveToTIFFAt:(NSString*)path;
+- (BOOL)gtm_saveToImageAt:(NSString*)path;
-/// Compares unitTestImage of |self| to the TIFF located at |path|
+// Compares unitTestImage of |self| to the image located at |path|
//
// Args:
-// path: the path to the TIFF file you want to compare against.
+// path: the path to the image file you want to compare against.
+// If diff is non-nil, it will contain an auto-released diff of the images.
//
// Returns:
// YES if they are equal, NO is they are not
+// If diff is non-nil, it will contain a diff of the images. Must
+// be released by caller.
//
-- (BOOL)compareWithTIFFNamed:(NSString*)name;
+- (BOOL)gtm_compareWithImageAt:(NSString*)path diffImage:(CGImageRef*)diff;
-/// Compares unitTestImage of |self| to the TIFF located at |path|
+// Find the path for a image by name in your bundle.
+// Searches for the following:
+// "name.arch.OSVersionMajor.OSVersionMinor.OSVersionBugfix.extension"
+// "name.OSVersionMajor.OSVersionMinor.OSVersionBugfix.arch.extension"
+// "name.arch.OSVersionMajor.OSVersionMinor.extension"
+// "name.OSVersionMajor.OSVersionMinor.arch.extension"
+// "name.arch.OSVersionMajor.extension"
+// "name.OSVersionMajor.arch.extension"
+// "name.arch.extension"
+// "name.OSVersionMajor.OSVersionMinor.OSVersionBugfix.extension"
+// "name.OSVersionMajor.OSVersionMinorextension"
+// "name.OSVersionMajor.extension"
+// "name.extension"
+// Do not include the extension on your name.
//
// Args:
-// path: the path to the TIFF file you want to compare against.
+// name: The name for the image file you would like to find.
//
// Returns:
-// YES if they are equal, NO is they are not
+// the path if the image exists in your bundle
+// or nil if no image to be found
//
-- (BOOL)compareWithTIFFAt:(NSString*)path;
+- (NSString *)gtm_pathForImageNamed:(NSString*)name;
-/// Find the path for a TIFF by name in your bundle.
-// Searches for the following:
-// "name.tif",
-// "name.arch.tif",
-// "name.arch.OSVersionMajor.tif"
-// "name.arch.OSVersionMajor.OSVersionMinor.tif"
-// "name.arch.OSVersionMajor.OSVersionMinor.OSVersion.bugfix.tif"
-// "name.arch.OSVersionMajor.tif"
-// "name.OSVersionMajor.arch.tif"
-// "name.OSVersionMajor.OSVersionMinor.arch.tif"
-// "name.OSVersionMajor.OSVersionMinor.OSVersion.bugfix.arch.tif"
-// "name.OSVersionMajor.tif"
-// "name.OSVersionMajor.OSVersionMinor.tif"
-// "name.OSVersionMajor.OSVersionMinor.OSVersion.bugfix.tif"
-// Do not include the ".tif" extension on your name.
+// Generates a CGImageRef from the image at |path|
+// Args:
+// path: The path to the image.
+//
+// Returns:
+// A CGImageRef that you own, or nil if no image at path
+- (CGImageRef)gtm_createImageUsingPath:(NSString*)path;
+
+// Generates a path for a image in the save directory, which is desktop
+// by default.
+// Path will be:
+// SaveDir/|name|.arch.OSVersionMajor.OSVersionMinor.OSVersionBugfix.extension
//
// Args:
-// name: The name for the TIFF file you would like to find.
+// name: The name for the image file you would like to generate a path for.
//
// Returns:
-// the path if the TIFF exists in your bundle
-// or nil if no TIFF to be found
+// the path
//
-- (NSString *)pathForTIFFNamed:(NSString*)name;
-
+- (NSString *)gtm_saveToPathForImageNamed:(NSString*)name;
-/// Gives us a LZW compressed representation of unitTestImage of |self|.
+// Gives us a representation of unitTestImage of |self|.
//
// Returns:
-// a LZW compressed TIFF if successful
+// a representation if successful
// nil if failed
//
-- (NSData *)TIFFRepresentation;
+- (NSData *)gtm_imageRepresentation;
+// Return the extension to be used for saving unittest states
+//
+// Returns
+// An extension (e.g. "gtmUTState")
+- (NSString*)gtm_stateExtension;
-/// Save the encoded unit test state to a .gtmUTState file with name |name| at ~/Desktop/|name|.gtmUTState.
+// Save the encoded unit test state to a state file with name
+// |name|.arch.OSVersionMajor.OSVersionMinor.OSVersionBugfix.extension
+// in the save folder (desktop by default)
//
// Args:
// name: The name for the state file you would like saved.
@@ -377,9 +321,9 @@ do { \
// Returns:
// YES if the file was successfully saved.
//
-- (BOOL)saveToStateNamed:(NSString*)name;
+- (BOOL)gtm_saveToStateNamed:(NSString*)name;
-/// Save encoded unit test state of |self| to a .gtmUTState file at path |path|.
+// Save encoded unit test state of |self| to a state file at path |path|.
//
// Args:
// name: The name for the state file you would like saved.
@@ -387,19 +331,9 @@ do { \
// Returns:
// YES if the file was successfully saved.
//
-- (BOOL)saveToStateAt:(NSString*)path;
+- (BOOL)gtm_saveToStateAt:(NSString*)path;
-/// Compares encoded unit test state of |self| to the .gtmUTState named |name|
-//
-// Args:
-// name: the name of the state file you want to compare against.
-//
-// Returns:
-// YES if they are equal, NO is they are not
-//
-- (BOOL)compareWithStateNamed:(NSString*)name;
-
-/// Compares encoded unit test state of |self| to the .gtmUTState located at |path|.
+// Compares encoded unit test state of |self| to the state file located at |path|
//
// Args:
// path: the path to the state file you want to compare against.
@@ -407,23 +341,23 @@ do { \
// Returns:
// YES if they are equal, NO is they are not
//
-- (BOOL)compareWithStateAt:(NSString*)path;
+- (BOOL)gtm_compareWithStateAt:(NSString*)path;
+
-/// Find the path for a state by name in your bundle.
+// Find the path for a state by name in your bundle.
// Searches for:
-// "name.gtmUTState",
-// "name.arch.gtmUTState",
-// "name.arch.OSVersionMajor.gtmUTState"
-// "name.arch.OSVersionMajor.OSVersionMinor.gtmUTState"
-// "name.arch.OSVersionMajor.OSVersionMinor.OSVersion.bugfix.gtmUTState"
-// "name.arch.OSVersionMajor.gtmUTState"
-// "name.OSVersionMajor.arch.gtmUTState"
-// "name.OSVersionMajor.OSVersionMinor.arch.gtmUTState"
-// "name.OSVersionMajor.OSVersionMinor.OSVersion.bugfix.arch.gtmUTState"
-// "name.OSVersionMajor.gtmUTState"
-// "name.OSVersionMajor.OSVersionMinor.gtmUTState"
-// "name.OSVersionMajor.OSVersionMinor.OSVersion.bugfix.gtmUTState"
-// Do not include the ".gtmUTState" extension on your name.
+// "name.arch.OSVersionMajor.OSVersionMinor.OSVersionBugfix.extension"
+// "name.OSVersionMajor.OSVersionMinor.OSVersionBugfix.arch.extension"
+// "name.arch.OSVersionMajor.OSVersionMinor.extension"
+// "name.OSVersionMajor.OSVersionMinor.arch.extension"
+// "name.arch.OSVersionMajor.extension"
+// "name.OSVersionMajor.arch.extension"
+// "name.arch.extension"
+// "name.OSVersionMajor.OSVersionMinor.OSVersionBugfix.extension"
+// "name.OSVersionMajor.OSVersionMinor.extension"
+// "name.OSVersionMajor.extension"
+// "name.extension"
+// Do not include the extension on your name.
//
// Args:
// name: The name for the state file you would like to find.
@@ -432,19 +366,29 @@ do { \
// the path if the state exists in your bundle
// or nil if no state to be found
//
-- (NSString *)pathForStateNamed:(NSString*)name;
+- (NSString *)gtm_pathForStateNamed:(NSString*)name;
+// Generates a path for a state in the save directory, which is desktop
+// by default.
+// Path will be:
+// SaveDir/|name|.arch.OSVersionMajor.OSVersionMinor.OSVersionBugfix.extension
+//
+// Args:
+// name: The name for the state file you would like to generate a path for.
+//
+// Returns:
+// the path
+//
+- (NSString *)gtm_saveToPathForStateNamed:(NSString*)name;
-/// Gives us the encoded unit test state for |self|
+// Gives us the encoded unit test state for |self|
//
// Returns:
// the encoded state if successful
// nil if failed
//
-- (NSDictionary *)stateRepresentation;
-
+- (NSDictionary *)gtm_stateRepresentation;
-/// Encodes the state of an object
// Encodes the state of an object in a manner suitable for comparing
// against a master state file so we can determine whether the
// object is in a suitable state. Encode data in the coder in the same
@@ -452,52 +396,7 @@ do { \
//
// Arguments:
// inCoder - the coder to encode our state into
-- (void)unitTestEncodeState:(NSCoder*)inCoder;
-
-/// Allows you to ignore certain bindings when running GTMTestExposedBindings
-// If you have bindings you want to ignore, add them to the array returned
-// by this method. The standard way to implement this would be:
-// - (NSMutableArray*)unitTestExposedBindingsToIgnore {
-// NSMutableArray *array = [super unitTestExposedBindingsToIgnore];
-// [array addObject:@"bindingToIgnore1"];
-// ...
-// return array;
-// }
-// The NSObject implementation by default will ignore NSFontBoldBinding,
-// NSFontFamilyNameBinding, NSFontItalicBinding, NSFontNameBinding and
-// NSFontSizeBinding if your exposed bindings contains NSFontBinding because
-// the NSFont*Bindings are NOT KVC/KVO compliant, and they just happen to work
-// through what can only be described as magic :)
-- (NSMutableArray*)unitTestExposedBindingsToIgnore;
-
-/// Allows you to set up test values for your different bindings.
-// if you have certain values you want to test against your bindings, add
-// them to the dictionary returned by this method. The dictionary is a "value" key
-// and an "expected return" object.
-// The standard way to implement this would be:
-// - (NSMutableDictionary*)unitTestExposedBindingsTestValues:(NSString*)binding {
-// NSMutableDictionary *dict = [super unitTestExposedBindingsTestValues:binding];
-// if ([binding isEqualToString:@"myBinding"]) {
-// [dict setObject:[[[MySpecialBindingValueSet alloc] init] autorelease]
-// forKey:[[[MySpecialBindingValueGet alloc] init] autorelease]];
-// [dict setObjectAndKey:[[[MySpecialBindingValue alloc] init] autorelease]];
-// ...
-// else if ([binding isEqualToString:@"myBinding2"]) {
-// ...
-// }
-// return array;
-// }
-// The NSObject implementation handles many of the default bindings, and
-// gives you a reasonable set of test values to start.
-// See the implementation for the current list of bindings, and values that we
-// set for those bindings.
-- (NSMutableDictionary*)unitTestExposedBindingsTestValues:(NSString*)binding;
-@end
-
-// Utility for simplifying unitTestExposedBindingsTestValues implementations
-@interface NSMutableDictionary (GTMUnitTestingAdditions)
-// Sets an object and a key to the same value in a dictionary.
-- (void)setObjectAndKey:(id)objectAndKey;
+- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder;
@end
// Informal protocol for delegates that wanst to be able to add state info
@@ -506,11 +405,9 @@ do { \
// Delegate function for unit test objects that have delegates. Delegates have
// the option of encoding more data into the coder to store their state for
// unittest usage.
-- (void)unitTestEncoderDidEncode:(id)sender inCoder:(NSCoder*)inCoder;
+- (void)gtm_unitTestEncoderWillEncode:(id)sender inCoder:(NSCoder*)inCoder;
@end
-/// \endcond
-
// Whenever an object is encoded by the unit test encoder, it send out a
// notification so that objects who want to add data to the encoded objects unit
// test state can do so. The Coder will be in the userInfo dictionary for the
@@ -520,15 +417,3 @@ extern NSString *const GTMUnitTestingEncodedObjectNotification;
// Key for finding the encoder in the userInfo dictionary for
// GTMUnitTestingEncodedObjectNotification notifications.
extern NSString *const GTMUnitTestingEncoderKey;
-
-
-/// Support for Pulse automated builds
-@interface NSObject (GTMUnitTestingPulseAdditions)
-
-// Determine if the current unittest is running under Pulse
-- (BOOL)isRunningUnderPulse;
-
-// Get the current base directory for Pulse
-- (NSString *)pulseBaseDirectory;
-
-@end
diff --git a/UnitTesting/GTMNSObject+UnitTesting.m b/UnitTesting/GTMNSObject+UnitTesting.m
index 5be6b9f..6aff70c 100644
--- a/UnitTesting/GTMNSObject+UnitTesting.m
+++ b/UnitTesting/GTMNSObject+UnitTesting.m
@@ -18,16 +18,25 @@
// the License.
//
-#include <Carbon/Carbon.h>
-#include <mach-o/arch.h>
+#import <mach-o/arch.h>
#import "GTMNSObject+UnitTesting.h"
-#import "GTMNSWorkspace+Theme.h"
#import "GTMSystemVersion.h"
+#import "GTMGarbageCollection.h"
+
+#if GTM_IPHONE_SDK
+#import <UIKit/UIKit.h>
+#endif
NSString *const GTMUnitTestingEncodedObjectNotification = @"GTMUnitTestingEncodedObjectNotification";
NSString *const GTMUnitTestingEncoderKey = @"GTMUnitTestingEncoderKey";
+#if GTM_IPHONE_SDK
+// No UTIs on iPhone. Only two we need.
+const CFStringRef kUTTypePNG = CFSTR("public.png");
+const CFStringRef kUTTypeJPEG = CFSTR("public.jpeg");
+#endif
+
// This class exists so that we can locate our bundle using [NSBundle
// bundleForClass:]. We don't use [NSBundle mainBundle] because when we are
// being run as a unit test, we aren't the mainBundle
@@ -41,6 +50,129 @@ NSString *const GTMUnitTestingEncoderKey = @"GTMUnitTestingEncoderKey";
// Nothing here. We're just interested in the name for finding our bundle.
@end
+BOOL GTMIsObjectImageEqualToImageNamed(id object,
+ NSString* filename,
+ NSString **error) {
+ NSString *failString = nil;
+ if (error) {
+ *error = nil;
+ }
+ BOOL isGood = [object respondsToSelector:@selector(gtm_createUnitTestImage)];
+ if (isGood) {
+ if ([object gtm_areSystemSettingsValidForDoingImage]) {
+ NSString *aPath = [object gtm_pathForImageNamed:filename];
+ CGImageRef diff = nil;
+ isGood = aPath != nil;
+ if (isGood) {
+ isGood = [object gtm_compareWithImageAt:aPath diffImage:&diff];
+ }
+ if (!isGood) {
+ if (aPath) {
+ filename = [filename stringByAppendingString:@"_Failed"];
+ }
+ BOOL aSaved = [object gtm_saveToImageNamed:filename];
+ NSString *fileNameWithExtension = [NSString stringWithFormat:@"%@.%@",
+ filename, [object gtm_imageExtension]];
+ NSString *fullSavePath = [object gtm_saveToPathForImageNamed:filename];
+ if (NO == aSaved) {
+ if (!aPath) {
+ failString = [NSString stringWithFormat:@"File %@ did not exist in bundle. "
+ "Tried to save as %@ and failed.",
+ fileNameWithExtension, fullSavePath];
+ } else {
+ failString = [NSString stringWithFormat:@"Object image different than file %@. "
+ "Tried to save as %@ and failed.",
+ aPath, fullSavePath];
+ }
+ } else {
+ if (!aPath) {
+ failString = [NSString stringWithFormat:@"File %@ did not exist in bundle. "
+ "Saved to %@", fileNameWithExtension, fullSavePath];
+ } else {
+ NSString *diffPath = [filename stringByAppendingString:@"_Diff"];
+ diffPath = [object gtm_saveToPathForImageNamed:diffPath];
+ NSData *data = nil;
+ if (diff) {
+ data = [object gtm_imageDataForImage:diff];
+ CFRelease(diff);
+ }
+ if ([data writeToFile:diffPath atomically:YES]) {
+ failString = [NSString stringWithFormat:@"Object image different "
+ "than file %@. Saved image to %@. Saved diff to %@",
+ aPath, fullSavePath, diffPath];
+ } else {
+ failString = [NSString stringWithFormat:@"Object image different "
+ "than file %@. Saved image to %@. Unable to save "
+ "diff. Most likely the image and diff are "
+ "different sizes.",
+ aPath, fullSavePath];
+ }
+ }
+ }
+ }
+ } else {
+ failString = @"systemSettings not valid for taking image"; // COV_NF_LINE
+ }
+ } else {
+ failString = @"Object does not conform to GTMUnitTestingImaging protocol";
+ }
+ if (error) {
+ *error = failString;
+ }
+ return isGood;
+}
+
+BOOL GTMIsObjectStateEqualToStateNamed(id object,
+ NSString* filename,
+ NSString **error) {
+ NSString *failString = nil;
+ if (error) {
+ *error = nil;
+ }
+ BOOL isGood = [object conformsToProtocol:@protocol(GTMUnitTestingEncoding)];
+ if (isGood) {
+ NSString *aPath = [object gtm_pathForStateNamed:filename];
+ isGood = aPath != nil;
+ if (isGood) {
+ isGood = [object gtm_compareWithStateAt:aPath];
+ }
+ if (!isGood) {
+ if (aPath) {
+ filename = [filename stringByAppendingString:@"_Failed"];
+ }
+ BOOL aSaved = [object gtm_saveToStateNamed:filename];
+ NSString *fileNameWithExtension = [NSString stringWithFormat:@"%@.%@",
+ filename, [object gtm_stateExtension]];
+ NSString *fullSavePath = [object gtm_saveToPathForStateNamed:filename];
+ if (NO == aSaved) {
+ if (!aPath) {
+ failString = [NSString stringWithFormat:@"File %@ did not exist in bundle. "
+ "Tried to save as %@ and failed.",
+ fileNameWithExtension, fullSavePath];
+ } else {
+ failString = [NSString stringWithFormat:@"Object state different than file %@. "
+ "Tried to save as %@ and failed.",
+ aPath, fullSavePath];
+ }
+ } else {
+ if (!aPath) {
+ failString = [NSString stringWithFormat:@"File %@ did not exist in bundle. "
+ "Saved to %@", fileNameWithExtension, fullSavePath];
+ } else {
+ failString = [NSString stringWithFormat:@"Object state different than file %@. "
+ "Saved to %@", aPath, fullSavePath];
+ }
+ }
+ }
+ } else {
+ failString = @"Object does not conform to GTMUnitTestingEncoding protocol";
+ }
+ if (error) {
+ *error = failString;
+ }
+ return isGood;
+}
+
@interface NSObject (GTMUnitTestingAdditionsPrivate)
/// Find the path for a file named name.extension in your bundle.
// Searches for the following:
@@ -66,9 +198,10 @@ NSString *const GTMUnitTestingEncoderKey = @"GTMUnitTestingEncoderKey";
// the path if the file exists in your bundle
// or nil if no file is found
//
-- (NSString *)pathForFileNamed:(NSString*)name extension:(NSString*)extension;
-- (NSString *)saveToPathForFileNamed:(NSString*)name
- extension:(NSString*)extension;
+- (NSString *)gtm_pathForFileNamed:(NSString*)name extension:(NSString*)extension;
+- (NSString *)gtm_saveToPathForFileNamed:(NSString*)name
+ extension:(NSString*)extension;
+- (CGImageRef)gtm_createUnitTestImage;
@end
// This is a keyed coder for storing unit test state data. It is used only by
@@ -85,6 +218,13 @@ NSString *const GTMUnitTestingEncoderKey = @"GTMUnitTestingEncoderKey";
- (NSDictionary*)dictionary;
@end
+// Small utility function for checking to see if a is b +/- 1.
+static inline BOOL almostEqual(unsigned char a, unsigned char b) {
+ unsigned char diff = a > b ? a - b : b - a;
+ BOOL notEqual = diff < 2;
+ return notEqual;
+}
+
@implementation GTMUnitTestingKeyedCoder
// Set up storage for coder. Stores type and version.
@@ -95,7 +235,7 @@ NSString *const GTMUnitTestingEncoderKey = @"GTMUnitTestingEncoderKey";
- (id)init {
self = [super init];
if (self != nil) {
- dictionary_ = [[NSMutableDictionary alloc] initWithCapacity:0];
+ dictionary_ = [[NSMutableDictionary alloc] initWithCapacity:2];
[dictionary_ setObject:@"GTMUnitTestingArchive" forKey:@"$GTMArchive"];
// Version number can be changed here.
@@ -117,14 +257,14 @@ NSString *const GTMUnitTestingEncoderKey = @"GTMUnitTestingEncoderKey";
// Arguments:
// key - key to check for in dictionary
- (void)checkForKey:(NSString*)key {
- NSAssert1(![dictionary_ objectForKey:key], @"Key already exists for %@", key);
+ _GTMDevAssert(![dictionary_ objectForKey:key], @"Key already exists for %@", key);
}
// Key routine for the encoder. We store objects in our dictionary based on
// their key. As we encode objects we send out notifications to let other
// classes doing tests add their specific data to the base types. If we can't
-// encode the object (it doesn't support unitTestEncodeState) and we don't get
-// any info back from the notifier, we attempt to store it's description.
+// encode the object (it doesn't support gtm_unitTestEncodeState) and we don't
+// get any info back from the notifier, we attempt to store it's description.
//
// Arguments:
// objv - object to be encoded
@@ -139,10 +279,10 @@ NSString *const GTMUnitTestingEncoderKey = @"GTMUnitTestingEncoderKey";
NSMutableDictionary *curDictionary = dictionary_;
dictionary_ = [[NSMutableDictionary alloc] initWithCapacity:0];
- // If objv responds to unitTestEncodeState get it to record
+ // If objv responds to gtm_unitTestEncodeState get it to record
// its data.
- if ([objv respondsToSelector:@selector(unitTestEncodeState:)]) {
- [objv unitTestEncodeState:self];
+ if ([objv respondsToSelector:@selector(gtm_unitTestEncodeState:)]) {
+ [objv gtm_unitTestEncodeState:self];
}
// We then send out a notification to let other folks
@@ -164,7 +304,7 @@ NSString *const GTMUnitTestingEncoderKey = @"GTMUnitTestingEncoderKey";
if (description && [description rangeOfString:@"0x"].length == 0) {
[curDictionary setObject:description forKey:key];
} else {
- NSAssert1(NO, @"Unable to encode forKey: %@", key);
+ _GTMDevAssert(NO, @"Unable to encode forKey: %@", key); // COV_NF_LINE
}
}
[dictionary_ release];
@@ -176,10 +316,6 @@ NSString *const GTMUnitTestingEncoderKey = @"GTMUnitTestingEncoderKey";
// Arguments:
// *v - value to encode
// key - key to encode it in
-- (void)encodeConditionalObject:(id)objv forKey:(NSString *)key {
- [self checkForKey:key];
- [self encodeObject:(id)objv forKey:key];
-}
- (void)encodeBool:(BOOL)boolv forKey:(NSString *)key {
[self checkForKey:key];
@@ -211,9 +347,13 @@ NSString *const GTMUnitTestingEncoderKey = @"GTMUnitTestingEncoderKey";
[dictionary_ setObject:[NSNumber numberWithDouble:realv] forKey:key];
}
-- (void)encodeBytes:(const uint8_t *)bytesp length:(unsigned)lenv forKey:(NSString *)key {
+- (void)encodeBytes:(const uint8_t *)bytesp
+ length:(unsigned)lenv
+ forKey:(NSString *)key {
[self checkForKey:key];
- [dictionary_ setObject:[NSData dataWithBytesNoCopy:(uint8_t*)bytesp length:lenv] forKey:key];
+ [dictionary_ setObject:[NSData dataWithBytes:(uint8_t*)bytesp
+ length:lenv]
+ forKey:key];
}
// Get our storage back as an NSDictionary
@@ -226,11 +366,41 @@ NSString *const GTMUnitTestingEncoderKey = @"GTMUnitTestingEncoderKey";
@end
+static NSString *gGTMUnitTestSaveToDirectory = nil;
@implementation NSObject (GTMUnitTestingAdditions)
-// GTM_METHOD_CHECK(NSWorkspace, themeAppearance);
-// GTM_METHOD_CHECK(NSWorkspace, themeScrollBarArrowStyle);
++ (void)gtm_setUnitTestSaveToDirectory:(NSString*)path {
+ @synchronized([self class]) {
+ [gGTMUnitTestSaveToDirectory autorelease];
+ gGTMUnitTestSaveToDirectory = [path copy];
+ }
+}
+
++ (NSString *)gtm_getUnitTestSaveToDirectory {
+ NSString *result = nil;
+ @synchronized([self class]) {
+ if (!gGTMUnitTestSaveToDirectory) {
+#if GTM_IPHONE_SDK
+ // Developer build, use their home directory Desktop.
+ gGTMUnitTestSaveToDirectory = [[[[[NSHomeDirectory() stringByDeletingLastPathComponent]
+ stringByDeletingLastPathComponent]
+ stringByDeletingLastPathComponent]
+ stringByDeletingLastPathComponent]
+ stringByAppendingPathComponent:@"Desktop"];
+#else
+ NSArray *desktopDirs = NSSearchPathForDirectoriesInDomains(NSDesktopDirectory,
+ NSUserDomainMask,
+ YES);
+ gGTMUnitTestSaveToDirectory = [desktopDirs objectAtIndex:0];
+#endif
+ [gGTMUnitTestSaveToDirectory retain];
+ }
+ result = gGTMUnitTestSaveToDirectory;
+ }
+
+ return result;
+}
/// Find the path for a file named name.extension in your bundle.
// Searches for the following:
@@ -256,36 +426,48 @@ NSString *const GTMUnitTestingEncoderKey = @"GTMUnitTestingEncoderKey";
// the path if the file exists in your bundle
// or nil if no file is found
//
-- (NSString *)pathForFileNamed:(NSString*)name extension:(NSString*)extension {
+- (NSString *)gtm_pathForFileNamed:(NSString*)name
+ extension:(NSString*)extension {
NSString *thePath = nil;
Class bundleClass = [GTMUnitTestingAdditionsBundleFinder class];
NSBundle *myBundle = [NSBundle bundleForClass:bundleClass];
- NSAssert3(myBundle, @"Couldn't find bundle for class: %@ searching for file:%@.%@",
+ _GTMDevAssert(myBundle, @"Couldn't find bundle for class: %@ searching for file:%@.%@",
NSStringFromClass(bundleClass), name, extension);
-
- // Extensions
- NSString *extensions[2];
- const NXArchInfo *localInfo = NXGetLocalArchInfo();
- NSAssert(localInfo && localInfo->name, @"Couldn't get NXArchInfo");
- extensions[0] = [NSString stringWithUTF8String:localInfo->name];
- extensions[1] = @"";
-
// System Version
long major, minor, bugFix;
[GTMSystemVersion getMajor:&major minor:&minor bugFix:&bugFix];
NSString *systemVersions[4];
- systemVersions[0] = [NSString stringWithFormat:@".%d.%d.%d", major, minor, bugFix];
+ systemVersions[0] = [NSString stringWithFormat:@".%d.%d.%d",
+ major, minor, bugFix];
systemVersions[1] = [NSString stringWithFormat:@".%d.%d", major, minor];
systemVersions[2] = [NSString stringWithFormat:@".%d", major];
systemVersions[3] = @"";
+ NSString *extensions[2];
+#if GTM_IPHONE_SDK
+ extensions[0] = @".iPhone";
+#else
+ const NXArchInfo *localInfo = NXGetLocalArchInfo();
+ _GTMDevAssert(localInfo && localInfo->name, @"Couldn't get NXArchInfo");
+ const NXArchInfo *genericInfo = NXGetArchInfoFromCpuType(localInfo->cputype, 0);
+ _GTMDevAssert(genericInfo && genericInfo->name, @"Couldn't get generic NXArchInfo");
+ extensions[0] = [NSString stringWithFormat:@".%s", genericInfo->name];
+#endif
+ extensions[1] = @"";
+ int i,j;
// Note that we are searching for the most exact match first.
- for (int i = 0; !thePath && i < sizeof(extensions) / sizeof(*extensions); ++i) {
- for (int j = 0; !thePath && j < sizeof(systemVersions) / sizeof(*systemVersions); j++) {
- NSString *fullName = [NSString stringWithFormat:@"%@%@%@", name, extensions[i], systemVersions[j]];
+ for (i = 0;
+ !thePath && i < sizeof(extensions) / sizeof(*extensions);
+ ++i) {
+ for (j = 0;
+ !thePath && j < sizeof(systemVersions) / sizeof(*systemVersions);
+ j++) {
+ NSString *fullName = [NSString stringWithFormat:@"%@%@%@",
+ name, extensions[i], systemVersions[j]];
thePath = [myBundle pathForResource:fullName ofType:extension];
if (thePath) break;
- fullName = [NSString stringWithFormat:@"%@%@%@", name, systemVersions[j], extensions[i]];
+ fullName = [NSString stringWithFormat:@"%@%@%@",
+ name, systemVersions[j], extensions[i]];
thePath = [myBundle pathForResource:fullName ofType:extension];
}
}
@@ -293,206 +475,387 @@ NSString *const GTMUnitTestingEncoderKey = @"GTMUnitTestingEncoderKey";
return thePath;
}
-- (NSString *)saveToPathForFileNamed:(NSString*)name
- extension:(NSString*)extension {
- NSString *newPath = nil;
+- (NSString *)gtm_saveToPathForFileNamed:(NSString*)name
+ extension:(NSString*)extension {
+ char const *system;
+#if GTM_IPHONE_SDK
+ system = "iPhone";
+#else
const NXArchInfo *localInfo = NXGetLocalArchInfo();
- NSAssert(localInfo && localInfo->name, @"Couldn't get NXArchInfo");
+ _GTMDevAssert(localInfo && localInfo->name, @"Couldn't get NXArchInfo");
+ const NXArchInfo *genericInfo = NXGetArchInfoFromCpuType(localInfo->cputype, 0);
+ _GTMDevAssert(genericInfo && genericInfo->name, @"Couldn't get generic NXArchInfo");
+ system = genericInfo->name;
+#endif
long major, minor, bugFix;
[GTMSystemVersion getMajor:&major minor:&minor bugFix:&bugFix];
NSString *fullName = [NSString stringWithFormat:@"%@.%s.%d.%d.%d",
- name, localInfo->name, major, minor, bugFix];
+ name, system, major, minor, bugFix];
- // Is this build under Pulse?
- if ([self isRunningUnderPulse]) {
- // Use the Pulse base directory
- newPath = [[[self pulseBaseDirectory]
- stringByAppendingPathComponent:fullName]
- stringByAppendingPathExtension:extension];
- } else {
- // Developer build, use their home directory Desktop.
- newPath = [[[NSHomeDirectory()
- stringByAppendingPathComponent:@"Desktop"]
- stringByAppendingPathComponent:fullName]
- stringByAppendingPathExtension:extension];
- }
- return newPath;
+ NSString *basePath = [[self class] gtm_getUnitTestSaveToDirectory];
+ return [[basePath stringByAppendingPathComponent:fullName]
+ stringByAppendingPathExtension:extension];
}
-
+
#pragma mark UnitTestImage
-// Returns an image containing a representation of the object suitable for use
-// in comparing against a master image.
-// NB this means that all colors should be device based, as colorsynced colors
-// will be different on different devices.
-//
+// Create a CGColorSpaceRef appropriate for using in creating a unit test image
+// iPhone uses device colorspace.
// Returns:
-// an image of the object
-- (NSImage*)unitTestImage {
- // Must be overridden by subclasses
- [NSException raise:NSInternalInconsistencyException
- format:@"%@ must override -%@",
- NSStringFromClass([self class]),
- NSStringFromSelector(_cmd)];
+// an CGColorSpaceRef of the object. Caller must release
+- (CGColorSpaceRef)gtm_createUnitTestColorspace {
+#if GTM_IPHONE_SDK
+ return CGColorSpaceCreateDeviceRGB();
+#else
+ return CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
+#endif
+}
- return nil; // appease the compiler
+// Create a CGBitmapContextRef appropriate for using in creating a unit test
+// image. If data is non-NULL, returns the buffer that the bitmap is
+// using for it's underlying storage. You must free this buffer using
+// free. If data is NULL, uses it's own internal storage.
+//
+// Returns:
+// an CGContextRef of the object. Caller must release
+- (CGContextRef)gtm_createUnitTestBitmapContextOfSize:(CGSize)size
+ data:(unsigned char**)data {
+ CGContextRef context = NULL;
+ size_t height = size.height;
+ size_t width = size.width;
+ size_t bytesPerRow = width * 4;
+ size_t bitsPerComponent = 8;
+ CGColorSpaceRef cs = [self gtm_createUnitTestColorspace];
+ _GTMDevAssert(cs, @"Couldn't create colorspace");
+ CGBitmapInfo info = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault;
+ if (data) {
+ *data = calloc(bytesPerRow, height);
+ _GTMDevAssert(*data, @"Couldn't create bitmap");
+ }
+ context = CGBitmapContextCreate(data ? *data : NULL, width, height,
+ bitsPerComponent, bytesPerRow, cs, info);
+ _GTMDevAssert(context, @"Couldn't create an context");
+ if (!data) {
+ CGContextClearRect(context, CGRectMake(0, 0, size.width, size.height));
+ }
+ CGContextSetRenderingIntent(context, kCGRenderingIntentRelativeColorimetric);
+ CGContextSetInterpolationQuality(context, kCGInterpolationNone);
+ CGContextSetShouldAntialias(context, NO);
+ CGContextSetAllowsAntialiasing(context, NO);
+ CGContextSetShouldSmoothFonts(context, NO);
+ CFRelease(cs);
+ return context;
}
// Checks to see that system settings are valid for doing an image comparison.
-// The main issue is that we make sure that we are set to using Blue Aqua as
-// our appearance and that the scroll arrows are set correctly.
-//
+// To be overridden by subclasses.
// Returns:
// YES if we can do image comparisons for this object type.
-- (BOOL)areSystemSettingsValidForDoingImage {
- NSWorkspace *ws = [NSWorkspace sharedWorkspace];
- BOOL isGood = YES;
-
- if ([self needsAquaBlueAppearanceForDoingImage] &&
- ![[ws gtm_themeAppearance] isEqualToString:(NSString *)kThemeAppearanceAquaBlue]) {
- NSLog(@"Cannot do image test as appearance is not blue. "
- "Please set it in the Appearance System Preference.");
- isGood = NO;
+- (BOOL)gtm_areSystemSettingsValidForDoingImage {
+ return YES;
+}
+
+- (NSString *)gtm_imageUTI {
+#if GTM_IPHONE_SDK
+ return (NSString*)kUTTypePNG;
+#else
+ // Currently can't use PNG on Leopard. (10.5.2)
+ // Radar:5844618 PNG importer/exporter in ImageIO is lossy
+ return (NSString*)kUTTypeTIFF;
+#endif
+}
+
+// Return the extension to be used for saving unittest images
+//
+// Returns
+// An extension (e.g. "png")
+- (NSString*)gtm_imageExtension {
+ NSString *uti = [self gtm_imageUTI];
+#if GTM_IPHONE_SDK
+ if ([uti isEqualToString:(NSString*)kUTTypePNG]) {
+ return @"png";
+ } else if ([uti isEqualToString:(NSString*)kUTTypeJPEG]) {
+ return @"jpg";
+ } else {
+ _GTMDevAssert(NO, @"Illegal UTI for iPhone");
}
+ return nil;
+#else
+ CFStringRef extension = UTTypeCopyPreferredTagWithClass((CFStringRef)uti,
+ kUTTagClassFilenameExtension);
+ _GTMDevAssert(extension, @"No extension for uti: %@", uti);
- if ([self needsScrollBarArrowsLowerRightForDoingImage] &&
- [ws gtm_themeScrollBarArrowStyle] != kThemeScrollBarArrowsLowerRight) {
- NSLog(@"Cannot do image test as scroll bar arrows are not together"
- "bottom right. Please set it in the Appearance System Preference.");
- isGood = NO;
+ return [GTMNSMakeCollectable(extension) autorelease];
+#endif
+}
+
+// Return image data in the format expected for gtm_imageExtension
+// So for a "png" extension I would expect "png" data
+//
+// Returns
+// NSData for image
+- (NSData*)gtm_imageDataForImage:(CGImageRef)image {
+ NSData *data = nil;
+#if GTM_IPHONE_SDK
+ // iPhone support
+ UIImage *uiImage = [UIImage imageWithCGImage:image];
+ NSString *uti = [self gtm_imageUTI];
+ if ([uti isEqualToString:(NSString*)kUTTypePNG]) {
+ data = UIImagePNGRepresentation(uiImage);
+ } else if ([uti isEqualToString:(NSString*)kUTTypeJPEG]) {
+ data = UIImageJPEGRepresentation(uiImage, 1.0f);
+ } else {
+ _GTMDevAssert(NO, @"Illegal UTI for iPhone");
}
+#else
+ data = [NSMutableData data];
+ CGImageDestinationRef dest = CGImageDestinationCreateWithData((CFMutableDataRef)data,
+ (CFStringRef)[self gtm_imageUTI],
+ 1,
+ NULL);
+ // LZW Compression for TIFF
+ NSDictionary *tiffDict = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:NSTIFFCompressionLZW]
+ forKey:(NSString*)kCGImagePropertyTIFFCompression];
+ NSDictionary *destProps = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithFloat:1.0],
+ (NSString*)kCGImageDestinationLossyCompressionQuality,
+ tiffDict,
+ (NSString*)kCGImagePropertyTIFFDictionary,
+ nil];
+ CGImageDestinationAddImage(dest, image, (CFDictionaryRef)destProps);
+ CGImageDestinationFinalize(dest);
+ CFRelease(dest);
+#endif
+ return data;
- return isGood;
}
-// Defaults to the appearance not mattering, individual tests override.
-- (BOOL)needsAquaBlueAppearanceForDoingImage {
- return NO;
-}
-
-// Defaults to the arrows not mattering, individual tests override.
-- (BOOL)needsScrollBarArrowsLowerRightForDoingImage {
- return NO;
-}
-
-// Save the unitTestImage to a TIFF file with name |name| at
-// ~/Desktop/|name|.tif. The TIFF will be compressed with LZW.
+// Save the unitTestImage to an image file with name |name| at
+// ~/Desktop/|name|.extension.
//
// Note: When running under Pulse automation output is redirected to the
// Pulse base directory.
//
// Args:
-// name: The name for the TIFF file you would like saved.
+// name: The name for the image file you would like saved.
//
// Returns:
// YES if the file was successfully saved.
//
-- (BOOL)saveToTIFFNamed:(NSString*)name {
- NSString *newPath = [self saveToPathForFileNamed:name extension:@"tif"];
- return [self saveToTIFFAt:newPath];
+- (BOOL)gtm_saveToImageNamed:(NSString*)name {
+ NSString *newPath = [self gtm_saveToPathForImageNamed:name];
+ return [self gtm_saveToImageAt:newPath];
}
-// Save unitTestImage of |self| to a TIFF file at path |path|.
-// The TIFF will be compressed with LZW.
+// Save unitTestImage of |self| to an image file at path |path|.
//
// Args:
-// name: The name for the TIFF file you would like saved.
+// name: The name for the image file you would like saved.
//
// Returns:
// YES if the file was successfully saved.
//
-- (BOOL)saveToTIFFAt:(NSString*)path {
+- (BOOL)gtm_saveToImageAt:(NSString*)path {
if (!path) return NO;
- NSData *data = [self TIFFRepresentation];
+ NSData *data = [self gtm_imageRepresentation];
return [data writeToFile:path atomically:YES];
}
-// Compares unitTestImage of |self| to the TIFF located at |path|
-//
-// Args:
-// path: the path to the TIFF file you want to compare against.
-//
-// Returns:
-// YES if they are equal, NO is they are not
+// Generates a CGImageRef from the image at |path|
+// Args:
+// path: The path to the image.
//
-- (BOOL)compareWithTIFFNamed:(NSString*)name {
- NSString *path = [self pathForTIFFNamed:name];
- return [self compareWithTIFFAt:path];
+// Returns:
+// A CGImageRef that you own, or nil if no image at path
+- (CGImageRef)gtm_createImageUsingPath:(NSString*)path {
+ CGImageRef imageRef = nil;
+#if GTM_IPHONE_SDK
+ UIImage *image = [UIImage imageWithContentsOfFile:path];
+ if (image) {
+ imageRef = CGImageRetain(image.CGImage);
+ }
+#else
+ CFURLRef url = CFURLCreateWithFileSystemPath(NULL, (CFStringRef)path,
+ kCFURLPOSIXPathStyle, NO);
+ if (url) {
+ CGImageSourceRef imageSource = CGImageSourceCreateWithURL(url, NULL);
+ CFRelease(url);
+ if (imageSource) {
+ imageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);
+ CFRelease(imageSource);
+ }
+ }
+#endif
+ return imageRef;
}
-// Compares unitTestImage of |self| to the TIFF located at |path|
+/// Compares unitTestImage of |self| to the image located at |path|
//
// Args:
-// path: the path to the TIFF file you want to compare against.
+// path: the path to the image file you want to compare against.
+// If diff is non-nil, it will contain an auto-released diff of the images.
//
// Returns:
// YES if they are equal, NO is they are not
+// If diff is non-nil, it will contain an auto-released diff of the images.
//
-- (BOOL)compareWithTIFFAt:(NSString*)path {
+- (BOOL)gtm_compareWithImageAt:(NSString*)path diffImage:(CGImageRef*)diff {
BOOL answer = NO;
- NSData *fileData = [NSData dataWithContentsOfFile:path];
- if (fileData) {
- NSData *imageData = [self TIFFRepresentation];
- if (imageData) {
- NSBitmapImageRep *fileRep = [NSBitmapImageRep imageRepWithData:fileData];
- if (fileRep) {
- NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:imageData];
- if (imageRep) {
- NSSize fileSize = [fileRep size];
- NSSize imageSize = [imageRep size];
- if (NSEqualSizes(fileSize,imageSize)) {
- // if all the sizes are equal, run through the bytes and compare
- // them for equality.
- answer = YES;
- for (int row = 0; row < fileSize.height; row++) {
- for (int col = 0; col < fileSize.width && answer == YES; col++) {
- NSColor *imageColor = [imageRep colorAtX:col y:row];
- NSColor *fileColor = [fileRep colorAtX:col y:row];
-
- answer = [imageColor isEqual:fileColor];
- }
- }
+ if (diff) {
+ *diff = nil;
+ }
+ CGImageRef fileRep = [self gtm_createImageUsingPath:path];
+ _GTMDevAssert(fileRep, @"Unable to create imagerep from %@", path);
+
+ CGImageRef imageRep = [self gtm_createUnitTestImage];
+ _GTMDevAssert(imageRep, @"Unable to create imagerep for %@", self);
+
+ size_t fileHeight = CGImageGetHeight(fileRep);
+ size_t fileWidth = CGImageGetWidth(fileRep);
+ size_t imageHeight = CGImageGetHeight(imageRep);
+ size_t imageWidth = CGImageGetWidth(imageRep);
+ if (fileHeight == imageHeight && fileWidth == imageWidth) {
+ // if all the sizes are equal, run through the bytes and compare
+ // them for equality.
+ // Do an initial fast check, if this fails and the caller wants a
+ // diff, we'll do the slow path and create the diff. The diff path
+ // could be optimized, but probably not necessary at this point.
+ answer = YES;
+
+ CGSize imageSize = CGSizeMake(fileWidth, fileHeight);
+ CGRect imageRect = CGRectMake(0, 0, fileWidth, fileHeight);
+ unsigned char *fileData;
+ unsigned char *imageData;
+ CGContextRef fileContext = [self gtm_createUnitTestBitmapContextOfSize:imageSize
+ data:&fileData];
+ _GTMDevAssert(fileContext, @"Unable to create filecontext");
+ CGContextDrawImage(fileContext, imageRect, fileRep);
+ CGContextRef imageContext =[self gtm_createUnitTestBitmapContextOfSize:imageSize
+ data:&imageData];
+ _GTMDevAssert(imageContext, @"Unable to create imageContext");
+ CGContextDrawImage(imageContext, imageRect, imageRep);
+
+ size_t fileBytesPerRow = CGBitmapContextGetBytesPerRow(fileContext);
+ size_t imageBytesPerRow = CGBitmapContextGetBytesPerRow(imageContext);
+ size_t row, col;
+
+ _GTMDevAssert(imageWidth * 4 <= imageBytesPerRow,
+ @"We expect image data to be 32bit RGBA");
+
+ for (row = 0; row < fileHeight && answer; row++) {
+ answer = memcmp(fileData + fileBytesPerRow * row,
+ imageData + imageBytesPerRow * row,
+ imageWidth * 4) == 0;
+ }
+ if (!answer && diff) {
+ answer = YES;
+ unsigned char *diffData;
+ CGContextRef diffContext = [self gtm_createUnitTestBitmapContextOfSize:imageSize
+ data:&diffData];
+ _GTMDevAssert(diffContext, @"Can't make diff context");
+ size_t diffRowBytes = CGBitmapContextGetBytesPerRow(diffContext);
+ for (row = 0; row < imageHeight; row++) {
+ unsigned long *imageRow = (unsigned long*)(imageData + imageBytesPerRow * row);
+ unsigned long *fileRow = (unsigned long*)(fileData + fileBytesPerRow * row);
+ unsigned long* diffRow = (unsigned long*)(diffData + diffRowBytes * row);
+ for (col = 0; col < imageWidth; col++) {
+ unsigned long imageColor = imageRow[col];
+ unsigned long fileColor = fileRow[col];
+
+ unsigned char imageAlpha = imageColor & 0xF;
+ unsigned char imageBlue = imageColor >> 8 & 0xF;
+ unsigned char imageGreen = imageColor >> 16 & 0xF;
+ unsigned char imageRed = imageColor >> 24 & 0xF;
+ unsigned char fileAlpha = fileColor & 0xF;
+ unsigned char fileBlue = fileColor >> 8 & 0xF;
+ unsigned char fileGreen = fileColor >> 16 & 0xF;
+ unsigned char fileRed = fileColor >> 24 & 0xF;
+
+ // Check to see if color is almost right.
+ // No matter how hard I've tried, I've still gotten occasionally
+ // screwed over by colorspaces not mapping correctly, and small
+ // sampling errors coming in. This appears to work for most cases.
+ // Almost equal is defined to check within 1% on all components.
+ BOOL equal = almostEqual(imageRed, fileRed) &&
+ almostEqual(imageGreen, fileGreen) &&
+ almostEqual(imageBlue, fileBlue) &&
+ almostEqual(imageAlpha, fileAlpha);
+ answer &= equal;
+ if (diff) {
+ unsigned long newColor;
+ if (equal) {
+ newColor = (((unsigned long)imageRed) << 24) +
+ (((unsigned long)imageGreen) << 16) +
+ (((unsigned long)imageBlue) << 8) +
+ (((unsigned long)imageAlpha) / 2);
+ } else {
+ newColor = 0xFF0000FF;
+ }
+ diffRow[col] = newColor;
}
}
}
- }
+ *diff = CGBitmapContextCreateImage(diffContext);
+ free(diffData);
+ CFRelease(diffContext);
+ free(fileData);
+ CFRelease(fileContext);
+ free(imageData);
+ CFRelease(imageContext);
+ }
+ CFRelease(imageRep);
+ CFRelease(fileRep);
}
return answer;
}
-
-// Find the path for a TIFF by name in your bundle.
-// Do not include the ".tif" extension on your name.
+
+// Find the path for an image by name in your bundle.
+// Do not include the extension on your name.
//
// Args:
-// name: The name for the TIFF file you would like to find.
+// name: The name for the image file you would like to find.
//
// Returns:
-// the path if the TIFF exists in your bundle
-// or nil if no TIFF to be found
+// the path if the image exists in your bundle
+// or nil if no image to be found
//
-- (NSString *)pathForTIFFNamed:(NSString*)name {
- return [self pathForFileNamed:name extension:@"tif"];
+- (NSString *)gtm_pathForImageNamed:(NSString*)name {
+ return [self gtm_pathForFileNamed:name
+ extension:[self gtm_imageExtension]];
+}
+
+- (NSString *)gtm_saveToPathForImageNamed:(NSString*)name {
+ return [self gtm_saveToPathForFileNamed:name
+ extension:[self gtm_imageExtension]];
}
-// Gives us a LZW compressed representation of unitTestImage of |self|.
+// Gives us a representation of unitTestImage of |self|.
//
// Returns:
-// a LZW compressed TIFF if successful
+// a representation of image if successful
// nil if failed
//
-- (NSData *)TIFFRepresentation {
- NSImage *image = [self unitTestImage];
- // factor is ignored unless compression style is NSJPEGCompression
- return [image TIFFRepresentationUsingCompression:NSTIFFCompressionLZW factor:0.0f];
+- (NSData *)gtm_imageRepresentation {
+ CGImageRef imageRep = [self gtm_createUnitTestImage];
+ NSData *data = [self gtm_imageDataForImage:imageRep];
+ _GTMDevAssert(data, @"unable to create %@ from %@", [self gtm_imageExtension], self);
+ CFRelease(imageRep);
+ return data;
}
#pragma mark UnitTestState
-static NSString* const kGTMStateFileExtension = @"gtmUTState";
+// Return the extension to be used for saving unittest states
+//
+// Returns
+// An extension (e.g. "gtmUTState")
+- (NSString*)gtm_stateExtension {
+ return @"gtmUTState";
+}
-// Save the encoded unit test state to a .gtmUTState file with name |name| at
-// ~/Desktop/|name|.gtmUTState.
+// Save the encoded unit test state to a state file with name |name| at
+// ~/Desktop/|name|.extension.
//
// Note: When running under Pulse automation output is redirected to the
// Pulse base directory.
@@ -503,13 +866,12 @@ static NSString* const kGTMStateFileExtension = @"gtmUTState";
// Returns:
// YES if the file was successfully saved.
//
-- (BOOL)saveToStateNamed:(NSString*)name {
- NSString *newPath = [self saveToPathForFileNamed:name
- extension:kGTMStateFileExtension];
- return [self saveToStateAt:newPath];
+- (BOOL)gtm_saveToStateNamed:(NSString*)name {
+ NSString *newPath = [self gtm_saveToPathForStateNamed:name];
+ return [self gtm_saveToStateAt:newPath];
}
-// Save encoded unit test state of |self| to a .gtmUTState file at path |path|.
+// Save encoded unit test state of |self| to a state file at path |path|.
//
// Args:
// name: The name for the state file you would like saved.
@@ -517,27 +879,13 @@ static NSString* const kGTMStateFileExtension = @"gtmUTState";
// Returns:
// YES if the file was successfully saved.
//
-- (BOOL)saveToStateAt:(NSString*)path {
+- (BOOL)gtm_saveToStateAt:(NSString*)path {
if (!path) return NO;
- NSDictionary *dictionary = [self stateRepresentation];
+ NSDictionary *dictionary = [self gtm_stateRepresentation];
return [dictionary writeToFile:path atomically:YES];
}
-// Compares encoded unit test state of |self| to the .gtmUTState named |name|
-//
-// Args:
-// name: the name of the state file you want to compare against.
-//
-// Returns:
-// YES if they are equal, NO is they are not
-//
-- (BOOL)compareWithStateNamed:(NSString*)name {
- NSString *path = [self pathForStateNamed:name];
- return [self compareWithStateAt:path];
-
-}
-
-// Compares encoded unit test state of |self| to the .gtmUTState located at
+// Compares encoded unit test state of |self| to the state file located at
// |path|
//
// Args:
@@ -546,15 +894,15 @@ static NSString* const kGTMStateFileExtension = @"gtmUTState";
// Returns:
// YES if they are equal, NO is they are not
//
-- (BOOL)compareWithStateAt:(NSString*)path {
+- (BOOL)gtm_compareWithStateAt:(NSString*)path {
NSDictionary *masterDict = [NSDictionary dictionaryWithContentsOfFile:path];
- NSAssert1(masterDict, @"Unable to create dictionary from %@", path);
- NSDictionary *selfDict = [self stateRepresentation];
- return [selfDict isEqualTo: masterDict];
+ _GTMDevAssert(masterDict, @"Unable to create dictionary from %@", path);
+ NSDictionary *selfDict = [self gtm_stateRepresentation];
+ return [selfDict isEqual: masterDict];
}
// Find the path for a state by name in your bundle.
-// Do not include the ".gtmUTState" extension.
+// Do not include the extension.
//
// Args:
// name: The name for the state file you would like to find.
@@ -563,8 +911,13 @@ static NSString* const kGTMStateFileExtension = @"gtmUTState";
// the path if the state exists in your bundle
// or nil if no state to be found
//
-- (NSString *)pathForStateNamed:(NSString*)name {
- return [self pathForFileNamed:name extension:kGTMStateFileExtension];
+- (NSString *)gtm_pathForStateNamed:(NSString*)name {
+ return [self gtm_pathForFileNamed:name extension:[self gtm_stateExtension]];
+}
+
+- (NSString *)gtm_saveToPathForStateNamed:(NSString*)name {
+ return [self gtm_saveToPathForFileNamed:name
+ extension:[self gtm_stateExtension]];
}
// Gives us the encoded unit test state |self|
@@ -573,12 +926,13 @@ static NSString* const kGTMStateFileExtension = @"gtmUTState";
// the encoded state if successful
// nil if failed
//
-- (NSDictionary *)stateRepresentation {
+- (NSDictionary *)gtm_stateRepresentation {
NSDictionary *dictionary = nil;
if ([self conformsToProtocol:@protocol(GTMUnitTestingEncoding)]) {
id<GTMUnitTestingEncoding> encoder = (id<GTMUnitTestingEncoding>)self;
- GTMUnitTestingKeyedCoder *archiver = [[[GTMUnitTestingKeyedCoder alloc] init] autorelease];
- [encoder unitTestEncodeState:archiver];
+ GTMUnitTestingKeyedCoder *archiver;
+ archiver = [[[GTMUnitTestingKeyedCoder alloc] init] autorelease];
+ [encoder gtm_unitTestEncodeState:archiver];
dictionary = [archiver dictionary];
}
return dictionary;
@@ -591,140 +945,20 @@ static NSString* const kGTMStateFileExtension = @"gtmUTState";
//
// Arguments:
// inCoder - the coder to encode our state into
-- (void)unitTestEncodeState:(NSCoder*)inCoder {
- // Currently does nothing, but all impls of unitTestEncodeState
- // should be calling [super unitTestEncodeState] as their first action.
-}
-
-- (NSMutableArray*)unitTestExposedBindingsToIgnore {
- NSMutableArray *array;
- if ([[self exposedBindings] containsObject:NSFontBinding]) {
- array = [NSMutableArray arrayWithObjects:
- NSFontBoldBinding, NSFontFamilyNameBinding, NSFontItalicBinding,
- NSFontNameBinding, NSFontSizeBinding, nil];
- } else {
- array = [NSMutableArray array];
- }
- return array;
-}
-
-- (NSMutableDictionary*)unitTestExposedBindingsTestValues:(NSString*)binding {
- // Always test identity
- id value = [self valueForKey:binding];
- if (!value) {
- value = [NSNull null];
- }
- NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObject:value
- forKey:value];
-
- // Now some default test values for a variety of bindings to make
- // sure that we cover all the bases and save other people writing lots of
- // duplicate test code.
+- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
+ // All impls of gtm_unitTestEncodeState
+ // should be calling [super gtm_unitTestEncodeState] as their first action.
+ _GTMDevAssert([inCoder isKindOfClass:[GTMUnitTestingKeyedCoder class]],
+ @"Coder must be of kind GTMUnitTestingKeyedCoder");
- // If anybody can think of more to add, please go nuts.
- if ([binding isEqualToString:NSAlignmentBinding]) {
- [dict setObjectAndKey:[NSNumber numberWithInt:NSLeftTextAlignment]];
- [dict setObjectAndKey:[NSNumber numberWithInt:NSRightTextAlignment]];
- [dict setObjectAndKey:[NSNumber numberWithInt:NSCenterTextAlignment]];
- [dict setObjectAndKey:[NSNumber numberWithInt:NSJustifiedTextAlignment]];
- NSNumber *natural = [NSNumber numberWithInt:NSNaturalTextAlignment];
- [dict setObjectAndKey:natural];
- [dict setObject:natural forKey:[NSNumber numberWithInt:500]];
- [dict setObject:natural forKey:[NSNumber numberWithInt:-1]];
- } else if ([binding isEqualToString:NSAlternateImageBinding] ||
- [binding isEqualToString:NSImageBinding] ||
- [binding isEqualToString:NSMixedStateImageBinding] ||
- [binding isEqualToString:NSOffStateImageBinding] ||
- [binding isEqualToString:NSOnStateImageBinding]) {
- // This handles all image bindings
- [dict setObjectAndKey:[NSImage imageNamed:@"NSApplicationIcon"]];
- [dict setObjectAndKey:[NSNull null]];
- } else if ([binding isEqualToString:NSAnimateBinding] ||
- [binding isEqualToString:NSDocumentEditedBinding] ||
- [binding isEqualToString:NSEditableBinding] ||
- [binding isEqualToString:NSEnabledBinding] ||
- [binding isEqualToString:NSHiddenBinding] ||
- [binding isEqualToString:NSVisibleBinding]) {
- // This handles all bool value bindings
- [dict setObjectAndKey:[NSNumber numberWithBool:YES]];
- [dict setObjectAndKey:[NSNumber numberWithBool:NO]];
- } else if ([binding isEqualToString:NSAlternateTitleBinding] ||
- [binding isEqualToString:NSHeaderTitleBinding] ||
- [binding isEqualToString:NSLabelBinding] ||
- [binding isEqualToString:NSRepresentedFilenameBinding] ||
- [binding isEqualToString:NSTitleBinding] ||
- [binding isEqualToString:NSToolTipBinding]) {
- // This handles all string value bindings
- [dict setObjectAndKey:@"happy"];
- [dict setObjectAndKey:[NSNull null]];
- // Test some non-ascii roman text
- char a_not_alpha[] = { 'A', 0xE2, 0x89, 0xA2, 0xCE, 0x91, '.', 0x00 };
- [dict setObjectAndKey:[NSString stringWithUTF8String:a_not_alpha]];
- // Test some korean
- char hangugo[]
- = { 0xED, 0x95, 0x9C, 0xEA, 0xB5, 0xAD, 0xEC, 0x96, 0xB4, 0x00 };
- [dict setObjectAndKey:[NSString stringWithUTF8String:hangugo]];
- // Test some japanese
- char nihongo[]
- = { 0xE6, 0x97, 0xA5, 0xE6, 0x9C, 0xAC, 0xE8, 0xAA, 0x9E, 0x00 };
- [dict setObjectAndKey:[NSString stringWithUTF8String:nihongo]];
- // Test some arabic (right to left baby! ;-)
- char arabic[] = { 0xd9, 0x83, 0xd8, 0xa7, 0xd9, 0x83, 0xd8, 0xa7, 0x00 };
- [dict setObjectAndKey:[NSString stringWithUTF8String:arabic]];
- } else if ([binding isEqualToString:NSMaximumRecentsBinding] ||
- [binding isEqualToString:NSMaxValueBinding] ||
- [binding isEqualToString:NSMaxWidthBinding] ||
- [binding isEqualToString:NSMinValueBinding] ||
- [binding isEqualToString:NSMinWidthBinding] ||
- [binding isEqualToString:NSRecentSearchesBinding] ||
- [binding isEqualToString:NSRowHeightBinding] ||
- [binding isEqualToString:NSWidthBinding]) {
- // This handles all int value bindings
- [dict setObjectAndKey:[NSNumber numberWithInt:0]];
- [dict setObjectAndKey:[NSNumber numberWithInt:-1]];
- [dict setObjectAndKey:[NSNumber numberWithInt:INT16_MAX]];
- [dict setObjectAndKey:[NSNumber numberWithInt:INT16_MIN]];
- } else if ([binding isEqualToString:NSTextColorBinding]) {
- // This handles all color value bindings
- [dict setObjectAndKey:[NSColor colorWithDeviceWhite:1.0 alpha:1.0]];
- [dict setObjectAndKey:[NSColor colorWithDeviceWhite:1.0 alpha:0.0]];
- [dict setObjectAndKey:[NSColor colorWithDeviceWhite:1.0 alpha:0.5]];
- [dict setObjectAndKey:[NSColor colorWithCalibratedRed:0.5 green:0.5 blue:0.5 alpha:0.5]];
- [dict setObjectAndKey:[NSColor colorWithDeviceCyan:0.25 magenta:0.25 yellow:0.25 black:0.25 alpha:0.25]];
- } else if ([binding isEqualToString:NSFontBinding]) {
- // This handles all font value bindings
- [dict setObjectAndKey:[NSFont boldSystemFontOfSize:[NSFont systemFontSize]]];
- [dict setObjectAndKey:[NSFont toolTipsFontOfSize:[NSFont smallSystemFontSize]]];
- [dict setObjectAndKey:[NSFont labelFontOfSize:144.0]];
+ // If the object has a delegate, give it a chance to respond
+ if ([self respondsToSelector:@selector(delegate)]) {
+ id delegate = [self performSelector:@selector(delegate)];
+ if (delegate &&
+ [delegate respondsToSelector:@selector(gtm_unitTestEncoderWillEncode:inCoder:)]) {
+ [delegate gtm_unitTestEncoderWillEncode:self inCoder:inCoder];
+ }
}
- return dict;
-}
-
-@end
-
-@implementation NSMutableDictionary (GTMUnitTestingAdditions)
-// Sets an object and a key to the same value in a dictionary.
-- (void)setObjectAndKey:(id)objectAndKey {
- [self setObject:objectAndKey forKey:objectAndKey];
-}
-@end
-
-
-@implementation NSObject (GTMUnitTestingPulseAdditions)
-
-- (BOOL)isRunningUnderPulse {
-
- if ([[[NSProcessInfo processInfo] environment] objectForKey:@"PULSE_BUILD_NUMBER"]) return YES;
- return NO;
-
-}
-
-- (NSString *)pulseBaseDirectory {
-
- return [[[NSProcessInfo processInfo] environment] objectForKey:@"PULSE_BASE_DIR"];
-
}
@end
-
-
diff --git a/UnitTesting/GTMNSView+UnitTesting.m b/UnitTesting/GTMNSView+UnitTesting.m
deleted file mode 100644
index dd3e423..0000000
--- a/UnitTesting/GTMNSView+UnitTesting.m
+++ /dev/null
@@ -1,135 +0,0 @@
-//
-// GTMNSView+UnitTesting.m
-//
-// Category for making unit testing of graphics/UI easier.
-// Allows you to save a view out to a TIFF file, and compare a view
-// with a previously stored representation to make sure it hasn't changed.
-//
-// 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 "GTMNSView+UnitTesting.h"
-#import <SenTestingKit/SenTestingKit.h>
-#import "GTMGeometryUtils.h"
-
-// A view that allows you to delegate out drawing using the formal
-// GTMUnitTestViewDelegate protocol above. This is useful when writing up unit
-// tests for visual elements.
-// Your test will often end up looking like this:
-// - (void)testFoo {
-// GTMAssertDrawingEqualToFile(self, NSMakeSize(200, 200), @"Foo", nil, nil);
-// }
-// and your testSuite will also implement the unitTestViewDrawRect method to do
-// it's actual drawing. The above creates a view of size 200x200 that draws
-// it's content using |self|'s unitTestViewDrawRect method and compares it to
-// the contents of the file Foo.tif to make sure it's valid
-@implementation GTMUnitTestView
-
-- (id)initWithFrame:(NSRect)frame drawer:(id<GTMUnitTestViewDrawer>)drawer contextInfo:(void*)contextInfo{
- self = [super initWithFrame:frame];
- if (self != nil) {
- drawer_ = [drawer retain];
- contextInfo_ = contextInfo;
- }
- return self;
-}
-
-- (void) dealloc {
- [drawer_ release];
- [super dealloc];
-}
-
-
-- (void)drawRect:(NSRect)rect {
- [drawer_ unitTestViewDrawRect:rect contextInfo:contextInfo_];
-}
-
-
-@end
-
-@implementation NSView (GTMUnitTestingAdditions)
-
-// Returns an image containing a representation of the object
-// suitable for use in comparing against a master image.
-// NB this means that all colors should be from "NSDevice" color space
-// Does all of it's drawing with smoothfonts and antialiasing off
-// to avoid issues with font smoothing settings and antialias differences
-// between ppc and x86.
-//
-// Returns:
-// an image of the object
-- (NSImage*)unitTestImage {
- // Create up a context
- NSBitmapImageRep *imageRep = [self bitmapImageRepForCachingDisplayInRect:[self bounds]];
- NSGraphicsContext *bitmapContext = [NSGraphicsContext graphicsContextWithBitmapImageRep:imageRep];
-
- // Store Current Context and switch to bitmap context
- [NSGraphicsContext saveGraphicsState];
- [NSGraphicsContext setCurrentContext: bitmapContext];
- CGContextRef contextRef = (CGContextRef)[bitmapContext graphicsPort];
-
- // Save our state and turn off font smoothing and antialias.
- CGContextSaveGState(contextRef);
- CGContextSetShouldSmoothFonts(contextRef, false);
- CGContextSetShouldAntialias(contextRef, false);
- CGContextClearRect(contextRef, GTMNSRectToCGRect([self bounds]));
- [self displayRectIgnoringOpacity:[self bounds] inContext:bitmapContext];
-
- // Clean up and create image
- CGContextRestoreGState(contextRef);
- [NSGraphicsContext restoreGraphicsState];
- NSImage *image = [[[NSImage alloc] init] autorelease];
- [image addRepresentation:imageRep];
- return image;
-}
-
-// Returns whether unitTestEncodeState should recurse into subviews
-// of a particular view.
-// Dan Waylonis discovered that if you have "Full keyboard access" in the
-// Keyboard & Mouse > Keyboard Shortcuts preferences pane set to "Text boxes
-// and Lists only" that Apple adds a set of subviews to NSTextFields. So in the
-// case of NSTextFields we don't want to recurse into their subviews. There may
-// be other cases like this, so instead of specializing unitTestEncodeState: to
-// look for NSTextFields, NSTextFields will just not allow us to recurse into
-// their subviews.
-//
-// Returns:
-// should unitTestEncodeState pick up subview state.
-- (BOOL)shouldEncodeStateRecurseIntoSubviews {
- return YES;
-}
-
-// Encodes the state of an object in a manner suitable for comparing
-// against a master state file so we can determine whether the
-// object is in a suitable state.
-//
-// Arguments:
-// inCoder - the coder to encode our state into
-- (void)unitTestEncodeState:(NSCoder*)inCoder {
- [super unitTestEncodeState:inCoder];
- [inCoder encodeBool:[self isHidden] forKey:@"ViewIsHidden"];
- if ([self shouldEncodeStateRecurseIntoSubviews]) {
- NSEnumerator *subviewEnum = [[self subviews] objectEnumerator];
- NSView *subview = nil;
- int i = 0;
- while ((subview = [subviewEnum nextObject])) {
- [inCoder encodeObject:subview forKey:[NSString stringWithFormat:@"ViewSubView %d", i]];
- i = i + 1;
- }
- }
-}
-
-@end
-
diff --git a/UnitTesting/GTMSenTestCase.h b/UnitTesting/GTMSenTestCase.h
index 75adc13..758f6d4 100644
--- a/UnitTesting/GTMSenTestCase.h
+++ b/UnitTesting/GTMSenTestCase.h
@@ -16,11 +16,49 @@
// the License.
//
+// Portions of this file fall under the following license, marked with
+// SENTE_BEGIN - SENTE_END
+//
+// Copyright (c) 1997-2005, Sen:te (Sente SA). All rights reserved.
+//
+// Use of this source code is governed by the following license:
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// (1) Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// (2) Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL Sente SA OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Note: this license is equivalent to the FreeBSD license.
+//
+// This notice may not be removed from this file.
+
// Some extra test case macros that would have been convenient for SenTestingKit
// to provide. I didn't stick GTM in front of the Macro names, so that they would
// be easy to remember.
+#import "GTMDefines.h"
+
+#if (!GTM_IPHONE_SDK)
#import <SenTestingKit/SenTestingKit.h>
+#else
+#import <Foundation/Foundation.h>
+NSString *STComposeString(NSString *, ...);
+#endif
// Generates a failure when a1 != noErr
// Args:
@@ -151,36 +189,34 @@ do { \
// ...: A variable number of arguments to the format string. Can be absent.
#define STAssertNotEquals(a1, a2, description, ...) \
do { \
- @try {\
- if (@encode(typeof(a1)) != @encode(typeof(a2))) { \
- [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
+ @try {\
+ if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \
+ [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
atLine:__LINE__ \
withDescription:[@"Type mismatch -- " stringByAppendingString:STComposeString(description, ##__VA_ARGS__)]]]; \
- } \
- else { \
- typeof(a1) a1value = (a1); \
- typeof(a2) a2value = (a2); \
- NSValue *a1encoded = [NSValue value:&a1value withObjCType:@encode(typeof(a1))]; \
- NSValue *a2encoded = [NSValue value:&a2value withObjCType:@encode(typeof(a2))]; \
- if ([a1encoded isEqualToValue:a2encoded]) { \
+ } else { \
+ __typeof__(a1) a1value = (a1); \
+ __typeof__(a2) a2value = (a2); \
+ NSValue *a1encoded = [NSValue value:&a1value withObjCType:@encode(__typeof__(a1))]; \
+ NSValue *a2encoded = [NSValue value:&a2value withObjCType:@encode(__typeof__(a2))]; \
+ if ([a1encoded isEqualToValue:a2encoded]) { \
NSString *_expression = [NSString stringWithFormat:@"(%s) != (%s)", #a1, #a2]; \
if (description) { \
_expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \
} \
- [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
+ [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
atLine:__LINE__ \
- withDescription:_expression]]; \
- } \
- } \
+ withDescription:_expression]]; \
+ } \
} \
- @catch (id anException) {\
- [self failWithException:[NSException \
- failureInRaise:[NSString stringWithFormat:@"(%s) != (%s)", #a1, #a2] \
- exception:anException \
- inFile:[NSString stringWithUTF8String:__FILE__] \
- atLine:__LINE__ \
- withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
- }\
+ } \
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) != (%s)", #a1, #a2] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
} while(0)
// Generates a failure when a1 is equal to a2. This test is for objects.
@@ -195,8 +231,8 @@ do { \
@try {\
id a1value = (a1); \
id a2value = (a2); \
- if ( (@encode(typeof(a1value)) == @encode(id)) && \
- (@encode(typeof(a2value)) == @encode(id)) && \
+ if ( (@encode(__typeof__(a1value)) == @encode(id)) && \
+ (@encode(__typeof__(a2value)) == @encode(id)) && \
![(id)a1value isEqual:(id)a2value] ) continue; \
NSString *_expression = [NSString stringWithFormat:@"%s('%@') != %s('%@')", #a1, [a1 description], #a2, [a2 description]]; \
if (desc) { \
@@ -226,34 +262,33 @@ do { \
#define STAssertOperation(a1, a2, op, description, ...) \
do { \
@try {\
- if (@encode(typeof(a1)) != @encode(typeof(a2))) { \
- [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
+ if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \
+ [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
atLine:__LINE__ \
withDescription:[@"Type mismatch -- " stringByAppendingString:STComposeString(description, ##__VA_ARGS__)]]]; \
- } \
- else { \
- typeof(a1) a1value = (a1); \
- typeof(a2) a2value = (a2); \
- if (!(a1value op a2value)) { \
+ } else { \
+ __typeof__(a1) a1value = (a1); \
+ __typeof__(a2) a2value = (a2); \
+ if (!(a1value op a2value)) { \
double a1DoubleValue = a1value; \
double a2DoubleValue = a2value; \
NSString *_expression = [NSString stringWithFormat:@"%s (%lg) %s %s (%lg)", #a1, a1DoubleValue, #op, #a2, a2DoubleValue]; \
if (description) { \
_expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \
} \
- [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
+ [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
atLine:__LINE__ \
- withDescription:_expression]]; \
- } \
- } \
+ withDescription:_expression]]; \
+ } \
+ } \
} \
@catch (id anException) {\
- [self failWithException:[NSException \
- failureInRaise:[NSString stringWithFormat:@"(%s) %s (%s)", #a1, #op, #a2] \
- exception:anException \
- inFile:[NSString stringWithUTF8String:__FILE__] \
- atLine:__LINE__ \
- withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ [self failWithException:[NSException \
+ failureInRaise:[NSString stringWithFormat:@"(%s) %s (%s)", #a1, #op, #a2] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
}\
} while(0)
@@ -314,27 +349,27 @@ do { \
// ...: A variable number of arguments to the format string. Can be absent.
#define STAssertEqualStrings(a1, a2, description, ...) \
do { \
- @try {\
- id a1value = (a1); \
- id a2value = (a2); \
- if (a1value == a2value) continue; \
- if ([a1value isKindOfClass:[NSString class]] && \
- [a2value isKindOfClass:[NSString class]] && \
- [a1value compare:a2value options:0] == NSOrderedSame) continue; \
- [self failWithException:[NSException failureInEqualityBetweenObject: a1value \
- andObject: a2value \
- inFile: [NSString stringWithUTF8String:__FILE__] \
- atLine: __LINE__ \
- withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
- }\
- @catch (id anException) {\
- [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \
- exception:anException \
- inFile:[NSString stringWithUTF8String:__FILE__] \
- atLine:__LINE__ \
- withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
- }\
- } while(0)
+ @try {\
+ id a1value = (a1); \
+ id a2value = (a2); \
+ if (a1value == a2value) continue; \
+ if ([a1value isKindOfClass:[NSString class]] && \
+ [a2value isKindOfClass:[NSString class]] && \
+ [a1value compare:a2value options:0] == NSOrderedSame) continue; \
+ [self failWithException:[NSException failureInEqualityBetweenObject: a1value \
+ andObject: a2value \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while(0)
// Generates a failure when string a1 is equal to string a2. This call
// differs from STAssertEqualObjects in that strings that are different in
@@ -349,26 +384,26 @@ do { \
// ...: A variable number of arguments to the format string. Can be absent.
#define STAssertNotEqualStrings(a1, a2, description, ...) \
do { \
- @try {\
- id a1value = (a1); \
- id a2value = (a2); \
- if ([a1value isKindOfClass:[NSString class]] && \
- [a2value isKindOfClass:[NSString class]] && \
- [a1value compare:a2value options:0] != NSOrderedSame) continue; \
- [self failWithException:[NSException failureInEqualityBetweenObject: a1value \
- andObject: a2value \
- inFile: [NSString stringWithUTF8String:__FILE__] \
- atLine: __LINE__ \
- withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
- }\
- @catch (id anException) {\
- [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) != (%s)", #a1, #a2] \
- exception:anException \
- inFile:[NSString stringWithUTF8String:__FILE__] \
- atLine:__LINE__ \
- withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
- }\
- } while(0)
+ @try {\
+ id a1value = (a1); \
+ id a2value = (a2); \
+ if ([a1value isKindOfClass:[NSString class]] && \
+ [a2value isKindOfClass:[NSString class]] && \
+ [a1value compare:a2value options:0] != NSOrderedSame) continue; \
+ [self failWithException:[NSException failureInEqualityBetweenObject: a1value \
+ andObject: a2value \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) != (%s)", #a1, #a2] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while(0)
// Generates a failure when c-string a1 is not equal to c-string a2.
// Args:
@@ -379,25 +414,25 @@ do { \
// ...: A variable number of arguments to the format string. Can be absent.
#define STAssertEqualCStrings(a1, a2, description, ...) \
do { \
- @try {\
- const char* a1value = (a1); \
- const char* a2value = (a2); \
- if (a1value == a2value) continue; \
- if (strcmp(a1value, a2value) == 0) continue; \
- [self failWithException:[NSException failureInEqualityBetweenObject: [NSString stringWithUTF8String:a1value] \
- andObject: [NSString stringWithUTF8String:a2value] \
- inFile: [NSString stringWithUTF8String:__FILE__] \
- atLine: __LINE__ \
- withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
- }\
- @catch (id anException) {\
- [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \
- exception:anException \
- inFile:[NSString stringWithUTF8String:__FILE__] \
- atLine:__LINE__ \
- withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
- }\
- } while(0)
+ @try {\
+ const char* a1value = (a1); \
+ const char* a2value = (a2); \
+ if (a1value == a2value) continue; \
+ if (strcmp(a1value, a2value) == 0) continue; \
+ [self failWithException:[NSException failureInEqualityBetweenObject: [NSString stringWithUTF8String:a1value] \
+ andObject: [NSString stringWithUTF8String:a2value] \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while(0)
// Generates a failure when c-string a1 is equal to c-string a2.
// Args:
@@ -408,22 +443,552 @@ do { \
// ...: A variable number of arguments to the format string. Can be absent.
#define STAssertNotEqualCStrings(a1, a2, description, ...) \
do { \
- @try {\
- const char* a1value = (a1); \
- const char* a2value = (a2); \
- if (strcmp(a1value, a2value) != 0) continue; \
- [self failWithException:[NSException failureInEqualityBetweenObject: [NSString stringWithUTF8String:a1value] \
- andObject: [NSString stringWithUTF8String:a2value] \
- inFile: [NSString stringWithUTF8String:__FILE__] \
- atLine: __LINE__ \
- withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
- }\
- @catch (id anException) {\
- [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) != (%s)", #a1, #a2] \
- exception:anException \
- inFile:[NSString stringWithUTF8String:__FILE__] \
- atLine:__LINE__ \
- withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
- }\
- } while(0)
+ @try {\
+ const char* a1value = (a1); \
+ const char* a2value = (a2); \
+ if (strcmp(a1value, a2value) != 0) continue; \
+ [self failWithException:[NSException failureInEqualityBetweenObject: [NSString stringWithUTF8String:a1value] \
+ andObject: [NSString stringWithUTF8String:a2value] \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) != (%s)", #a1, #a2] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while(0)
+
+#if GTM_IPHONE_SDK
+
+// SENTE_BEGIN
+/*" Generates a failure when !{ [a1 isEqualTo:a2] } is false
+ (or one is nil and the other is not).
+ _{a1 The object on the left.}
+ _{a2 The object on the right.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+"*/
+#define STAssertEqualObjects(a1, a2, description, ...) \
+do { \
+ @try {\
+ id a1value = (a1); \
+ id a2value = (a2); \
+ if (a1value == a2value) continue; \
+ if ( (@encode(__typeof__(a1value)) == @encode(id)) && \
+ (@encode(__typeof__(a2value)) == @encode(id)) && \
+ [(id)a1value isEqual: (id)a2value] ) continue; \
+ [self failWithException:[NSException failureInEqualityBetweenObject: a1value \
+ andObject: a2value \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while(0)
+
+
+/*" Generates a failure when a1 is not equal to a2. This test is for
+ C scalars, structs and unions.
+ _{a1 The argument on the left.}
+ _{a2 The argument on the right.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+"*/
+#define STAssertEquals(a1, a2, description, ...) \
+do { \
+ @try {\
+ if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \
+ [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:[@"Type mismatch -- " stringByAppendingString:STComposeString(description, ##__VA_ARGS__)]]]; \
+ } else { \
+ __typeof__(a1) a1value = (a1); \
+ __typeof__(a2) a2value = (a2); \
+ NSValue *a1encoded = [NSValue value:&a1value withObjCType: @encode(__typeof__(a1))]; \
+ NSValue *a2encoded = [NSValue value:&a2value withObjCType: @encode(__typeof__(a2))]; \
+ if (![a1encoded isEqualToValue:a2encoded]) { \
+ [self failWithException:[NSException failureInEqualityBetweenValue: a1encoded \
+ andValue: a2encoded \
+ withAccuracy: nil \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
+ } \
+ } \
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while(0)
+
+#define STAbsoluteDifference(left,right) (MAX(left,right)-MIN(left,right))
+
+
+/*" Generates a failure when a1 is not equal to a2 within + or - accuracy is false.
+ This test is for scalars such as floats and doubles where small differences
+ could make these items not exactly equal, but also works for all scalars.
+ _{a1 The scalar on the left.}
+ _{a2 The scalar on the right.}
+ _{accuracy The maximum difference between a1 and a2 for these values to be
+ considered equal.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+"*/
+
+#define STAssertEqualsWithAccuracy(a1, a2, accuracy, description, ...) \
+do { \
+ @try {\
+ if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \
+ [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:[@"Type mismatch -- " stringByAppendingString:STComposeString(description, ##__VA_ARGS__)]]]; \
+ } else { \
+ __typeof__(a1) a1value = (a1); \
+ __typeof__(a2) a2value = (a2); \
+ __typeof__(accuracy) accuracyvalue = (accuracy); \
+ if (STAbsoluteDifference(a1value, a2value) > accuracyvalue) { \
+ NSValue *a1encoded = [NSValue value:&a1value withObjCType:@encode(__typeof__(a1))]; \
+ NSValue *a2encoded = [NSValue value:&a2value withObjCType:@encode(__typeof__(a2))]; \
+ NSValue *accuracyencoded = [NSValue value:&accuracyvalue withObjCType:@encode(__typeof__(accuracy))]; \
+ [self failWithException:[NSException failureInEqualityBetweenValue: a1encoded \
+ andValue: a2encoded \
+ withAccuracy: accuracyencoded \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
+ } \
+ } \
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while(0)
+
+
+
+/*" Generates a failure unconditionally.
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+"*/
+#define STFail(description, ...) \
+[self failWithException:[NSException failureInFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]
+
+
+
+/*" Generates a failure when a1 is not nil.
+ _{a1 An object.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+"*/
+#define STAssertNil(a1, description, ...) \
+do { \
+ @try {\
+ id a1value = (a1); \
+ if (a1value != nil) { \
+ NSString *_a1 = [NSString stringWithUTF8String: #a1]; \
+ NSString *_expression = [NSString stringWithFormat:@"((%@) == nil)", _a1]; \
+ [self failWithException:[NSException failureInCondition: _expression \
+ isTrue: NO \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
+ }\
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == nil fails", #a1] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while(0)
+
+
+/*" Generates a failure when a1 is nil.
+ _{a1 An object.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+"*/
+#define STAssertNotNil(a1, description, ...) \
+do { \
+ @try {\
+ id a1value = (a1); \
+ if (a1value == nil) { \
+ NSString *_a1 = [NSString stringWithUTF8String: #a1]; \
+ NSString *_expression = [NSString stringWithFormat:@"((%@) != nil)", _a1]; \
+ [self failWithException:[NSException failureInCondition: _expression \
+ isTrue: NO \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
+ }\
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) != nil fails", #a1] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while(0)
+
+
+/*" Generates a failure when expression evaluates to false.
+ _{expr The expression that is tested.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+"*/
+#define STAssertTrue(expr, description, ...) \
+do { \
+ BOOL _evaluatedExpression = (expr);\
+ if (!_evaluatedExpression) {\
+ NSString *_expression = [NSString stringWithUTF8String: #expr];\
+ [self failWithException:[NSException failureInCondition: _expression \
+ isTrue: NO \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
+} while (0)
+
+
+/*" Generates a failure when expression evaluates to false and in addition will
+ generate error messages if an exception is encountered.
+ _{expr The expression that is tested.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+"*/
+#define STAssertTrueNoThrow(expr, description, ...) \
+do { \
+ @try {\
+ BOOL _evaluatedExpression = (expr);\
+ if (!_evaluatedExpression) {\
+ NSString *_expression = [NSString stringWithUTF8String: #expr];\
+ [self failWithException:[NSException failureInCondition: _expression \
+ isTrue: NO \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
+ } \
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) ", #expr] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while (0)
+
+
+/*" Generates a failure when the expression evaluates to true.
+ _{expr The expression that is tested.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+"*/
+#define STAssertFalse(expr, description, ...) \
+do { \
+ BOOL _evaluatedExpression = (expr);\
+ if (_evaluatedExpression) {\
+ NSString *_expression = [NSString stringWithUTF8String: #expr];\
+ [self failWithException:[NSException failureInCondition: _expression \
+ isTrue: YES \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
+} while (0)
+
+
+/*" Generates a failure when the expression evaluates to true and in addition
+ will generate error messages if an exception is encountered.
+ _{expr The expression that is tested.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+"*/
+#define STAssertFalseNoThrow(expr, description, ...) \
+do { \
+ @try {\
+ BOOL _evaluatedExpression = (expr);\
+ if (_evaluatedExpression) {\
+ NSString *_expression = [NSString stringWithUTF8String: #expr];\
+ [self failWithException:[NSException failureInCondition: _expression \
+ isTrue: YES \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
+ } \
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"!(%s) ", #expr] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while (0)
+
+
+/*" Generates a failure when expression does not throw an exception.
+ _{expression The expression that is evaluated.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.
+"*/
+#define STAssertThrows(expr, description, ...) \
+do { \
+ @try { \
+ (expr);\
+ } \
+ @catch (id anException) { \
+ continue; \
+ }\
+ [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \
+ exception: nil \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+} while (0)
+
+
+/*" Generates a failure when expression does not throw an exception of a
+ specific class.
+ _{expression The expression that is evaluated.}
+ _{specificException The specified class of the exception.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+"*/
+#define STAssertThrowsSpecific(expr, specificException, description, ...) \
+do { \
+ @try { \
+ (expr);\
+ } \
+ @catch (specificException *anException) { \
+ continue; \
+ }\
+ @catch (id anException) {\
+ NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\
+ [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \
+ exception: anException \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \
+ continue; \
+ }\
+ NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\
+ [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \
+ exception: nil \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \
+} while (0)
+
+
+/*" Generates a failure when expression does not throw an exception of a
+ specific class with a specific name. Useful for those frameworks like
+ AppKit or Foundation that throw generic NSException w/specific names
+ (NSInvalidArgumentException, etc).
+ _{expression The expression that is evaluated.}
+ _{specificException The specified class of the exception.}
+ _{aName The name of the specified exception.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+
+"*/
+#define STAssertThrowsSpecificNamed(expr, specificException, aName, description, ...) \
+do { \
+ @try { \
+ (expr);\
+ } \
+ @catch (specificException *anException) { \
+ if ([aName isEqualToString: [anException name]]) continue; \
+ NSString *_descrip = STComposeString(@"(Expected exception: %@ (name: %@)) %@", NSStringFromClass([specificException class]), aName, description);\
+ [self failWithException: \
+ [NSException failureInRaise: [NSString stringWithUTF8String:#expr] \
+ exception: anException \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \
+ continue; \
+ }\
+ @catch (id anException) {\
+ NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\
+ [self failWithException: \
+ [NSException failureInRaise: [NSString stringWithUTF8String:#expr] \
+ exception: anException \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \
+ continue; \
+ }\
+ NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\
+ [self failWithException: \
+ [NSException failureInRaise: [NSString stringWithUTF8String:#expr] \
+ exception: nil \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \
+} while (0)
+
+
+/*" Generates a failure when expression does throw an exception.
+ _{expression The expression that is evaluated.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+"*/
+#define STAssertNoThrow(expr, description, ...) \
+do { \
+ @try { \
+ (expr);\
+ } \
+ @catch (id anException) { \
+ [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \
+ exception: anException \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while (0)
+
+
+/*" Generates a failure when expression does throw an exception of the specitied
+ class. Any other exception is okay (i.e. does not generate a failure).
+ _{expression The expression that is evaluated.}
+ _{specificException The specified class of the exception.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+"*/
+#define STAssertNoThrowSpecific(expr, specificException, description, ...) \
+do { \
+ @try { \
+ (expr);\
+ } \
+ @catch (specificException *anException) { \
+ [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \
+ exception: anException \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+ @catch (id anythingElse) {\
+ ; \
+ }\
+} while (0)
+
+
+/*" Generates a failure when expression does throw an exception of a
+ specific class with a specific name. Useful for those frameworks like
+ AppKit or Foundation that throw generic NSException w/specific names
+ (NSInvalidArgumentException, etc).
+ _{expression The expression that is evaluated.}
+ _{specificException The specified class of the exception.}
+ _{aName The name of the specified exception.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+
+"*/
+#define STAssertNoThrowSpecificNamed(expr, specificException, aName, description, ...) \
+do { \
+ @try { \
+ (expr);\
+ } \
+ @catch (specificException *anException) { \
+ if ([aName isEqualToString: [anException name]]) { \
+ NSString *_descrip = STComposeString(@"(Expected exception: %@ (name: %@)) %@", NSStringFromClass([specificException class]), aName, description);\
+ [self failWithException: \
+ [NSException failureInRaise: [NSString stringWithUTF8String:#expr] \
+ exception: anException \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \
+ } \
+ continue; \
+ }\
+ @catch (id anythingElse) {\
+ ; \
+ }\
+} while (0)
+
+
+
+@interface NSException (GTMSenTestAdditions)
++ (NSException *)failureInFile:(NSString *)filename
+ atLine:(int)lineNumber
+ withDescription:(NSString *)formatString, ...;
++ (NSException *)failureInCondition:(NSString *)condition
+ isTrue:(BOOL)isTrue
+ inFile:(NSString *)filename
+ atLine:(int)lineNumber
+ withDescription:(NSString *)formatString, ...;
++ (NSException *)failureInEqualityBetweenObject:(id)left
+ andObject:(id)right
+ inFile:(NSString *)filename
+ atLine:(int)lineNumber
+ withDescription:(NSString *)formatString, ...;
++ (NSException *)failureInEqualityBetweenValue:(NSValue *)left
+ andValue:(NSValue *)right
+ withAccuracy:(NSValue *)accuracy
+ inFile:(NSString *)filename
+ atLine:(int) ineNumber
+ withDescription:(NSString *)formatString, ...;
++ (NSException *)failureInRaise:(NSString *)expression
+ inFile:(NSString *)filename
+ atLine:(int)lineNumber
+ withDescription:(NSString *)formatString, ...;
++ (NSException *)failureInRaise:(NSString *)expression
+ exception:(NSException *)exception
+ inFile:(NSString *)filename
+ atLine:(int)lineNumber
+ withDescription:(NSString *)formatString, ...;
+@end
+
+// SENTE_END
+
+@interface NSObject (GTMSenTestAdditions)
+- (void)failWithException:(NSException*)exception;
+@end
+
+@interface SenTestCase : NSObject
+- (void) setUp;
+- (void) tearDown;
+@end
+
+CF_EXPORT NSString * const SenTestFailureException;
+
+#endif // GTM_IPHONE_SDK
diff --git a/UnitTesting/GTMSenTestCase.m b/UnitTesting/GTMSenTestCase.m
new file mode 100644
index 0000000..9b6cf17
--- /dev/null
+++ b/UnitTesting/GTMSenTestCase.m
@@ -0,0 +1,140 @@
+//
+// GTMSenTestCase.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 "GTMSenTestCase.h"
+
+#if GTM_IPHONE_SDK
+
+#import <stdarg.h>
+
+@implementation NSException (GTMSenTestAdditions)
+
++ (NSException *)failureInFile:(NSString *)filename
+ atLine:(int)lineNumber
+ withDescription:(NSString *)formatString, ... {
+ va_list vl;
+ va_start(vl, formatString);
+ NSString *reason = [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
+ va_end(vl);
+ reason = [NSString stringWithFormat:@"%@:%d: error: %@", filename, lineNumber, reason];
+ return [NSException exceptionWithName:SenTestFailureException
+ reason:reason
+ userInfo:nil];
+}
+
++ (NSException *)failureInCondition:(NSString *)condition
+ isTrue:(BOOL)isTrue
+ inFile:(NSString *)filename
+ atLine:(int)lineNumber
+ withDescription:(NSString *)formatString, ... {
+ va_list vl;
+ va_start(vl, formatString);
+ NSString *reason = [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
+ va_end(vl);
+ reason = [NSString stringWithFormat:@"condition '%@' is %s : %@",
+ condition, isTrue ? "TRUE" : "FALSE", reason];
+ return [self failureInFile:filename atLine:lineNumber withDescription:reason];
+}
+
++ (NSException *)failureInEqualityBetweenObject:(id)left
+ andObject:(id)right
+ inFile:(NSString *)filename
+ atLine:(int)lineNumber
+ withDescription:(NSString *)formatString, ... {
+ va_list vl;
+ va_start(vl, formatString);
+ NSString *reason = [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
+ va_end(vl);
+ reason = [NSString stringWithFormat:@"%@ != %@ : %@",
+ left, right, reason];
+ return [self failureInFile:filename atLine:lineNumber withDescription:reason];
+}
+
++ (NSException *)failureInEqualityBetweenValue:(NSValue *)left
+ andValue:(NSValue *)right
+ withAccuracy:(NSValue *)accuracy
+ inFile:(NSString *)filename
+ atLine:(int)lineNumber
+ withDescription:(NSString *)formatString, ... {
+ va_list vl;
+ va_start(vl, formatString);
+ NSString *reason = [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
+ va_end(vl);
+ reason = [NSString stringWithFormat:@"%@ != %@ with accuracy %@ : %@",
+ left, right, accuracy, reason];
+ return [self failureInFile:filename atLine:lineNumber withDescription:reason];
+}
+
++ (NSException *)failureInRaise:(NSString *)expression
+ inFile:(NSString *)filename
+ atLine:(int)lineNumber
+ withDescription:(NSString *)formatString, ... {
+ va_list vl;
+ va_start(vl, formatString);
+ NSString *reason = [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
+ va_end(vl);
+ reason = [NSString stringWithFormat:@"failure in raise %@ : %@",
+ expression, reason];
+ return [self failureInFile:filename atLine:lineNumber withDescription:reason];
+}
+
++ (NSException *)failureInRaise:(NSString *)expression
+ exception:(NSException *)exception
+ inFile:(NSString *)filename
+ atLine:(int)lineNumber
+ withDescription:(NSString *)formatString, ... {
+ va_list vl;
+ va_start(vl, formatString);
+ NSString *reason = [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
+ va_end(vl);
+ reason = [NSString stringWithFormat:@"failure in raise %@ (%@) : %@",
+ expression, exception, reason];
+ return [self failureInFile:filename atLine:lineNumber withDescription:reason];
+}
+
+@end
+
+@implementation NSObject (GTMSenTestAdditions)
+- (void)failWithException:(NSException*)exception {
+ [exception raise];
+}
+
+@end
+
+NSString *STComposeString(NSString *formatString, ...) {
+ NSString *reason = @"";
+ if (formatString) {
+ va_list vl;
+ va_start(vl, formatString);
+ reason = [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
+ va_end(vl);
+ }
+ return reason;
+}
+
+NSString * const SenTestFailureException = @"SenTestFailureException";
+
+@implementation SenTestCase
+- (void) setUp {
+}
+
+- (void) tearDown {
+}
+@end
+
+#endif
diff --git a/UnitTesting/GTMUIKit+UnitTesting.h b/UnitTesting/GTMUIKit+UnitTesting.h
new file mode 100644
index 0000000..3c231cf
--- /dev/null
+++ b/UnitTesting/GTMUIKit+UnitTesting.h
@@ -0,0 +1,128 @@
+//
+// GTMUIKit+UnitTesting.h
+//
+// Code for making unit testing of graphics/UI easier. Generally you
+// will only want to look at the macros:
+// GTMAssertDrawingEqualToFile
+// GTMAssertViewRepEqualToFile
+// and the protocol GTMUnitTestViewDrawer. When using these routines
+// make sure you are using device colors and not calibrated/generic colors
+// or else your test graphics WILL NOT match across devices/graphics cards.
+//
+// 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 <UIKit/UIKit.h>
+#import "GTMNSObject+UnitTesting.h"
+
+@protocol GTMUnitTestViewDrawer;
+
+// Fails when the |a1|'s drawing in an area |a2| does not equal the image file named |a3|.
+// See the description of the GTMAssertViewRepEqualToFile macro
+// to understand how |a3| is found and written out.
+// See the description of the GTMUnitTestView for a better idea
+// how the view works.
+// Implemented as a macro to match the rest of the SenTest macros.
+//
+// Args:
+// a1: The object that implements the GTMUnitTestViewDrawer protocol
+// that is doing the drawing.
+// a2: The size of the drawing
+// a3: The name of the image file to check against.
+// Do not include the extension
+// a4: contextInfo to pass to drawer
+// description: A format string as in the printf() function.
+// Can be nil or an empty string but must be present.
+// ...: A variable number of arguments to the format string. Can be absent.
+//
+
+#define GTMAssertDrawingEqualToFile(a1, a2, a3, a4, description, ...) \
+ do { \
+ id<GTMUnitTestViewDrawer> a1Object = (a1); \
+ CGSize a2Size = (a2); \
+ NSString* a3String = (a3); \
+ void *a4ContextInfo = (a4); \
+ CGRect frame = CGRectMake(0, 0, a2Size.width, a2Size.height); \
+ GTMUnitTestView *view = [[[GTMUnitTestView alloc] initWithFrame:frame drawer:a1Object contextInfo:a4ContextInfo] autorelease]; \
+ GTMAssertObjectImageEqualToImageNamed(view, a3String, STComposeString(description, ##__VA_ARGS__)); \
+ } while(0)
+
+// Category for making unit testing of graphics/UI easier.
+
+// Allows you to take a state of a view. Supports both image and state.
+// See GTMNSObject+UnitTesting.h for details.
+@interface UIView (GTMUnitTestingAdditions) <GTMUnitTestingImaging>
+
+// Returns an image containing a representation suitable for use in comparing against a master image.
+//
+// Returns:
+// an image of the object
+- (CGImageRef)gtm_createUnitTestImage;
+
+// Encodes the state of an object in a manner suitable for comparing against a master state file
+// This enables us to determine whether the object is in a suitable state.
+//
+// Arguments:
+// inCoder - the coder to encode our state into
+- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder;
+
+// Returns whether gtm_unitTestEncodeState should recurse into subviews
+//
+// Returns:
+// should gtm_unitTestEncodeState pick up subview state.
+- (BOOL)gtm_shouldEncodeStateForSubviews;
+@end
+
+// A view that allows you to delegate out drawing using the formal
+// GTMUnitTestViewDelegate protocol
+// This is useful when writing up unit tests for visual elements.
+// Your test will often end up looking like this:
+// - (void)testFoo {
+// GTMAssertDrawingEqualToFile(self, CGSizeMake(200, 200), @"Foo", nil, nil);
+// }
+// and your testSuite will also implement the unitTestViewDrawRect method to do
+// it's actual drawing. The above creates a view of size 200x200 that draws
+// it's content using |self|'s unitTestViewDrawRect method and compares it to
+// the contents of the file Foo.tif to make sure it's valid
+@interface GTMUnitTestView : UIView {
+ @private
+ id<GTMUnitTestViewDrawer> drawer_; // delegate for doing drawing (STRONG)
+ void* contextInfo_; // info passed in by user for them to use when drawing
+}
+
+// Create a GTMUnitTestView.
+//
+// Args:
+// rect: the area to draw.
+// drawer: the object that will do the drawing via the GTMUnitTestViewDrawer
+// protocol
+// contextInfo:
+- (id)initWithFrame:(CGRect)frame drawer:(id<GTMUnitTestViewDrawer>)drawer contextInfo:(void*)contextInfo;
+
+@end
+
+/// \cond Protocols
+
+// Formal protocol for doing unit testing of views. See description of
+// GTMUnitTestView for details.
+@protocol GTMUnitTestViewDrawer <NSObject>
+
+// Draw the view. Equivalent to drawRect on a standard UIView.
+//
+// Args:
+// rect: the area to draw.
+- (void)gtm_unitTestViewDrawRect:(CGRect)rect contextInfo:(void*)contextInfo;
+
+@end
diff --git a/UnitTesting/GTMUIKit+UnitTesting.m b/UnitTesting/GTMUIKit+UnitTesting.m
new file mode 100644
index 0000000..7b98886
--- /dev/null
+++ b/UnitTesting/GTMUIKit+UnitTesting.m
@@ -0,0 +1,118 @@
+//
+// GTMUIKit+UnitTesting.m
+//
+// Category for making unit testing of graphics/UI easier.
+// Allows you to save a view out to a image file, and compare a view
+// with a previously stored representation to make sure it hasn't changed.
+//
+// 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 "GTMUIKit+UnitTesting.h"
+#import "GTMCALayer+UnitTesting.h"
+#import "GTMDefines.h"
+
+#if !GTM_IPHONE_SDK
+#error This file is for iPhone use only
+#endif // GTM_IPHONE_SDK
+
+// A view that allows you to delegate out drawing using the formal
+// GTMUnitTestViewDelegate protocol above. This is useful when writing up unit
+// tests for visual elements.
+// Your test will often end up looking like this:
+// - (void)testFoo {
+// GTMAssertDrawingEqualToFile(self, CGSizeMake(200, 200), @"Foo", nil, nil);
+// }
+// and your testSuite will also implement the unitTestViewDrawRect method to do
+// it's actual drawing. The above creates a view of size 200x200 that draws
+// it's content using |self|'s unitTestViewDrawRect method and compares it to
+// the contents of the file Foo.tif to make sure it's valid
+@implementation GTMUnitTestView
+
+- (id)initWithFrame:(CGRect)frame
+ drawer:(id<GTMUnitTestViewDrawer>)drawer
+ contextInfo:(void*)contextInfo{
+ self = [super initWithFrame:frame];
+ if (self != nil) {
+ drawer_ = [drawer retain];
+ contextInfo_ = contextInfo;
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [drawer_ release];
+ [super dealloc];
+}
+
+- (void)drawRect:(CGRect)rect {
+ [drawer_ gtm_unitTestViewDrawRect:rect contextInfo:contextInfo_];
+}
+
+@end
+
+@implementation UIView (GTMUnitTestingAdditions)
+
+// Returns an image containing a representation of the object
+// suitable for use in comparing against a master image.
+// NB this means that all colors should be from "NSDevice" color space
+// Does all of it's drawing with smoothfonts and antialiasing off
+// to avoid issues with font smoothing settings and antialias differences
+// between ppc and x86.
+//
+// Returns:
+// an image of the object
+- (CGImageRef)gtm_createUnitTestImage {
+ CALayer* layer = [self layer];
+ return [layer gtm_createUnitTestImage];
+}
+
+// Encodes the state of an object in a manner suitable for comparing
+// against a master state file so we can determine whether the
+// object is in a suitable state.
+//
+// Arguments:
+// inCoder - the coder to encode our state into
+- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
+ [super gtm_unitTestEncodeState:inCoder];
+ [inCoder encodeBool:[self isHidden] forKey:@"ViewIsHidden"];
+ CALayer* layer = [self layer];
+ if (layer) {
+ [layer gtm_unitTestEncodeState:inCoder];
+ }
+ if ([self gtm_shouldEncodeStateForSubviews]) {
+ NSEnumerator *subviewEnum = [[self subviews] objectEnumerator];
+ UIView *subview = nil;
+ int i = 0;
+ while ((subview = [subviewEnum nextObject])) {
+ [inCoder encodeObject:subview
+ forKey:[NSString stringWithFormat:@"ViewSubView %d", i]];
+ i = i + 1;
+ }
+ }
+}
+
+// Returns whether gtm_unitTestEncodeState should recurse into subviews
+//
+// Returns:
+// should gtm_unitTestEncodeState pick up subview state.
+- (BOOL)gtm_shouldEncodeStateForSubviews {
+ return YES;
+}
+
+- (BOOL)gtm_shouldEncodeStateForSublayersOfLayer:(CALayer*)layer {
+ return NO;
+}
+@end
diff --git a/UnitTesting/GTMUIKit+UnitTestingTest.m b/UnitTesting/GTMUIKit+UnitTestingTest.m
new file mode 100644
index 0000000..d39ba9e
--- /dev/null
+++ b/UnitTesting/GTMUIKit+UnitTestingTest.m
@@ -0,0 +1,58 @@
+//
+// GTMUIKit+UnitTestingTest.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 <CoreGraphics/CoreGraphics.h>
+#import "GTMUIKit+UnitTesting.h"
+#import "GTMSenTestCase.h"
+
+@interface GTMUIView_UnitTestingTest : SenTestCase <GTMUnitTestViewDrawer>
+@end
+
+@implementation GTMUIView_UnitTestingTest
+
+- (void)testDrawing {
+ GTMAssertDrawingEqualToFile(self,
+ CGSizeMake(200,200),
+ @"GTMUIViewUnitTestingTest",
+ [UIApplication sharedApplication],
+ nil);
+}
+
+- (void)testState {
+ UIView *view = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)] autorelease];
+ UIView *subview = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)] autorelease];
+ [view addSubview:subview];
+ GTMAssertObjectStateEqualToStateNamed(view, @"GTMUIViewUnitTestingTest", nil);
+}
+
+- (void)gtm_unitTestViewDrawRect:(CGRect)rect contextInfo:(void*)contextInfo {
+ UIApplication *app = [UIApplication sharedApplication];
+ STAssertEqualObjects(app,
+ contextInfo,
+ @"Should be a UIApplication");
+ CGPoint center = CGPointMake(CGRectGetMidX(rect),
+ CGRectGetMidY(rect));
+ rect = CGRectMake(center.x - 50, center.y - 50, 100, 100);
+ CGContextRef context = UIGraphicsGetCurrentContext();
+ CGContextAddEllipseInRect(context, rect);
+ CGContextSetLineWidth(context, 5);
+ [[UIColor redColor] set];
+ CGContextStrokePath(context);
+}
+
+@end
diff --git a/UnitTesting/GTMUIUnitTestingHarness/English.lproj/MainMenu.nib/designable.nib b/UnitTesting/GTMUIUnitTestingHarness/English.lproj/MainMenu.nib/designable.nib
new file mode 100644
index 0000000..e198162
--- /dev/null
+++ b/UnitTesting/GTMUIUnitTestingHarness/English.lproj/MainMenu.nib/designable.nib
@@ -0,0 +1,1973 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.02">
+ <data>
+ <int key="IBDocument.SystemTarget">0</int>
+ <string key="IBDocument.SystemVersion">9C31</string>
+ <string key="IBDocument.InterfaceBuilderVersion">644</string>
+ <string key="IBDocument.AppKitVersion">949.26</string>
+ <string key="IBDocument.HIToolboxVersion">352.00</string>
+ <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <integer value="57"/>
+ </object>
+ <object class="NSArray" key="IBDocument.PluginDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilderKit</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.RootObjects" id="1048">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomObject" id="1021">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSCustomObject" id="1014">
+ <string key="NSClassName">FirstResponder</string>
+ </object>
+ <object class="NSCustomObject" id="1050">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSMenu" id="649796088">
+ <string key="NSTitle">AMainMenu</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="694149608">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">NewApplication</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <object class="NSCustomResource" key="NSOnImage" id="35465992">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">NSMenuCheckmark</string>
+ </object>
+ <object class="NSCustomResource" key="NSMixedImage" id="513340101">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">NSMenuMixedState</string>
+ </object>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="110575045">
+ <string key="NSTitle">NewApplication</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="238522557">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">About NewApplication</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="304266470">
+ <reference key="NSMenu" ref="110575045"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="609285721">
+ <reference key="NSMenu" ref="110575045"/>
+ <string type="base64-UTF8" key="NSTitle">UHJlZmVyZW5jZXPigKY</string>
+ <string key="NSKeyEquiv">,</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="481834944">
+ <reference key="NSMenu" ref="110575045"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="1046388886">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Services</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="752062318">
+ <string key="NSTitle">Services</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <string key="NSName">_NSServicesMenu</string>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="646227648">
+ <reference key="NSMenu" ref="110575045"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="755159360">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Hide NewApplication</string>
+ <string key="NSKeyEquiv">h</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="342932134">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Hide Others</string>
+ <string key="NSKeyEquiv">h</string>
+ <int key="NSKeyEquivModMask">1572864</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="908899353">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Show All</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="1056857174">
+ <reference key="NSMenu" ref="110575045"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="632727374">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Quit NewApplication</string>
+ <string key="NSKeyEquiv">q</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ </object>
+ <string key="NSName">_NSAppleMenu</string>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="379814623">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">File</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="720053764">
+ <string key="NSTitle">File</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="705341025">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">New</string>
+ <string key="NSKeyEquiv">n</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="722745758">
+ <reference key="NSMenu" ref="720053764"/>
+ <string type="base64-UTF8" key="NSTitle">T3BlbuKApg</string>
+ <string key="NSKeyEquiv">o</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="1025936716">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Open Recent</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="1065607017">
+ <string key="NSTitle">Open Recent</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="759406840">
+ <reference key="NSMenu" ref="1065607017"/>
+ <string key="NSTitle">Clear Menu</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ </object>
+ <string key="NSName">_NSRecentDocumentsMenu</string>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="425164168">
+ <reference key="NSMenu" ref="720053764"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="776162233">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Close</string>
+ <string key="NSKeyEquiv">w</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="1023925487">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Save</string>
+ <string key="NSKeyEquiv">s</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="117038363">
+ <reference key="NSMenu" ref="720053764"/>
+ <string type="base64-UTF8" key="NSTitle">U2F2ZSBBc+KApg</string>
+ <string key="NSKeyEquiv">S</string>
+ <int key="NSKeyEquivModMask">1179648</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="579971712">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Revert to Saved</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="1010469920">
+ <reference key="NSMenu" ref="720053764"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="294629803">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Page Setup...</string>
+ <string key="NSKeyEquiv">P</string>
+ <int key="NSKeyEquivModMask">1179648</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ <string key="NSToolTip"/>
+ </object>
+ <object class="NSMenuItem" id="49223823">
+ <reference key="NSMenu" ref="720053764"/>
+ <string type="base64-UTF8" key="NSTitle">UHJpbnTigKY</string>
+ <string key="NSKeyEquiv">p</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="952259628">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">Edit</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="789758025">
+ <string key="NSTitle">Edit</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="1058277027">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Undo</string>
+ <string key="NSKeyEquiv">z</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="790794224">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Redo</string>
+ <string key="NSKeyEquiv">Z</string>
+ <int key="NSKeyEquivModMask">1179648</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="1040322652">
+ <reference key="NSMenu" ref="789758025"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="296257095">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Cut</string>
+ <string key="NSKeyEquiv">x</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="860595796">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Copy</string>
+ <string key="NSKeyEquiv">c</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="29853731">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Paste</string>
+ <string key="NSKeyEquiv">v</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="437104165">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Delete</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="583158037">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Select All</string>
+ <string key="NSKeyEquiv">a</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="212016141">
+ <reference key="NSMenu" ref="789758025"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="892235320">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Find</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="963351320">
+ <string key="NSTitle">Find</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="447796847">
+ <reference key="NSMenu" ref="963351320"/>
+ <string type="base64-UTF8" key="NSTitle">RmluZOKApg</string>
+ <string key="NSKeyEquiv">f</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ <int key="NSTag">1</int>
+ </object>
+ <object class="NSMenuItem" id="326711663">
+ <reference key="NSMenu" ref="963351320"/>
+ <string key="NSTitle">Find Next</string>
+ <string key="NSKeyEquiv">g</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ <int key="NSTag">2</int>
+ </object>
+ <object class="NSMenuItem" id="270902937">
+ <reference key="NSMenu" ref="963351320"/>
+ <string key="NSTitle">Find Previous</string>
+ <string key="NSKeyEquiv">G</string>
+ <int key="NSKeyEquivModMask">1179648</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ <int key="NSTag">3</int>
+ </object>
+ <object class="NSMenuItem" id="159080638">
+ <reference key="NSMenu" ref="963351320"/>
+ <string key="NSTitle">Use Selection for Find</string>
+ <string key="NSKeyEquiv">e</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ <int key="NSTag">7</int>
+ </object>
+ <object class="NSMenuItem" id="88285865">
+ <reference key="NSMenu" ref="963351320"/>
+ <string key="NSTitle">Jump to Selection</string>
+ <string key="NSKeyEquiv">j</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="972420730">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Spelling and Grammar</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="769623530">
+ <string key="NSTitle">Spelling and Grammar</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="679648819">
+ <reference key="NSMenu" ref="769623530"/>
+ <string type="base64-UTF8" key="NSTitle">U2hvdyBTcGVsbGluZ+KApg</string>
+ <string key="NSKeyEquiv">:</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="96193923">
+ <reference key="NSMenu" ref="769623530"/>
+ <string key="NSTitle">Check Spelling</string>
+ <string key="NSKeyEquiv">;</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="948374510">
+ <reference key="NSMenu" ref="769623530"/>
+ <string key="NSTitle">Check Spelling While Typing</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="967646866">
+ <reference key="NSMenu" ref="769623530"/>
+ <string key="NSTitle">Check Grammar With Spelling</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="507821607">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Substitutions</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="698887838">
+ <string key="NSTitle">Substitutions</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="605118523">
+ <reference key="NSMenu" ref="698887838"/>
+ <string key="NSTitle">Smart Copy/Paste</string>
+ <string key="NSKeyEquiv">f</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ <int key="NSTag">1</int>
+ </object>
+ <object class="NSMenuItem" id="197661976">
+ <reference key="NSMenu" ref="698887838"/>
+ <string key="NSTitle">Smart Quotes</string>
+ <string key="NSKeyEquiv">g</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ <int key="NSTag">2</int>
+ </object>
+ <object class="NSMenuItem" id="708854459">
+ <reference key="NSMenu" ref="698887838"/>
+ <string key="NSTitle">Smart Links</string>
+ <string key="NSKeyEquiv">G</string>
+ <int key="NSKeyEquivModMask">1179648</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ <int key="NSTag">3</int>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="676164635">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Speech</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="785027613">
+ <string key="NSTitle">Speech</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="731782645">
+ <reference key="NSMenu" ref="785027613"/>
+ <string key="NSTitle">Start Speaking</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="680220178">
+ <reference key="NSMenu" ref="785027613"/>
+ <string key="NSTitle">Stop Speaking</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="626404410">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">Format</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="502084290">
+ <string key="NSTitle">Format</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="519768076">
+ <reference key="NSMenu" ref="502084290"/>
+ <string key="NSTitle">Show Fonts</string>
+ <string key="NSKeyEquiv">t</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="1028416764">
+ <reference key="NSMenu" ref="502084290"/>
+ <string key="NSTitle">Show Colors</string>
+ <string key="NSKeyEquiv">C</string>
+ <int key="NSKeyEquivModMask">1179648</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="586577488">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">View</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="466310130">
+ <string key="NSTitle">View</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="102151532">
+ <reference key="NSMenu" ref="466310130"/>
+ <string key="NSTitle">Show Toolbar</string>
+ <string key="NSKeyEquiv">t</string>
+ <int key="NSKeyEquivModMask">1572864</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ <object class="NSMenuItem" id="237841660">
+ <reference key="NSMenu" ref="466310130"/>
+ <string type="base64-UTF8" key="NSTitle">Q3VzdG9taXplIFRvb2xiYXLigKY</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="391199113">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">Help</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="374024848">
+ <string key="NSTitle">Help</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="238773614">
+ <reference key="NSMenu" ref="374024848"/>
+ <string key="NSTitle">NewApplication Help</string>
+ <string key="NSKeyEquiv">?</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="513340101"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ <string key="NSName">_NSMainMenu</string>
+ </object>
+ </object>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <object class="NSMutableArray" key="connectionRecords">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">print:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="49223823"/>
+ </object>
+ <int key="connectionID">86</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">runPageLayout:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="294629803"/>
+ </object>
+ <int key="connectionID">87</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">clearRecentDocuments:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="759406840"/>
+ </object>
+ <int key="connectionID">127</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">orderFrontStandardAboutPanel:</string>
+ <reference key="source" ref="1021"/>
+ <reference key="destination" ref="238522557"/>
+ </object>
+ <int key="connectionID">142</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performClose:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="776162233"/>
+ </object>
+ <int key="connectionID">193</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleContinuousSpellChecking:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="948374510"/>
+ </object>
+ <int key="connectionID">222</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">undo:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1058277027"/>
+ </object>
+ <int key="connectionID">223</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">copy:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="860595796"/>
+ </object>
+ <int key="connectionID">224</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">checkSpelling:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="96193923"/>
+ </object>
+ <int key="connectionID">225</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">paste:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="29853731"/>
+ </object>
+ <int key="connectionID">226</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">stopSpeaking:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="680220178"/>
+ </object>
+ <int key="connectionID">227</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">cut:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="296257095"/>
+ </object>
+ <int key="connectionID">228</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">showGuessPanel:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="679648819"/>
+ </object>
+ <int key="connectionID">230</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">redo:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="790794224"/>
+ </object>
+ <int key="connectionID">231</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">selectAll:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="583158037"/>
+ </object>
+ <int key="connectionID">232</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">startSpeaking:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="731782645"/>
+ </object>
+ <int key="connectionID">233</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">delete:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="437104165"/>
+ </object>
+ <int key="connectionID">235</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performFindPanelAction:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="447796847"/>
+ </object>
+ <int key="connectionID">241</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">centerSelectionInVisibleArea:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="88285865"/>
+ </object>
+ <int key="connectionID">245</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleGrammarChecking:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="967646866"/>
+ </object>
+ <int key="connectionID">347</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleSmartInsertDelete:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="605118523"/>
+ </object>
+ <int key="connectionID">355</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleAutomaticQuoteSubstitution:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="197661976"/>
+ </object>
+ <int key="connectionID">356</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleAutomaticLinkDetection:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="708854459"/>
+ </object>
+ <int key="connectionID">357</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">showHelp:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="238773614"/>
+ </object>
+ <int key="connectionID">360</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">orderFrontColorPanel:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1028416764"/>
+ </object>
+ <int key="connectionID">361</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">saveDocument:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1023925487"/>
+ </object>
+ <int key="connectionID">362</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">saveDocumentAs:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="117038363"/>
+ </object>
+ <int key="connectionID">363</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">revertDocumentToSaved:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="579971712"/>
+ </object>
+ <int key="connectionID">364</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">runToolbarCustomizationPalette:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="237841660"/>
+ </object>
+ <int key="connectionID">365</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleToolbarShown:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="102151532"/>
+ </object>
+ <int key="connectionID">366</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">hide:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="755159360"/>
+ </object>
+ <int key="connectionID">367</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">hideOtherApplications:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="342932134"/>
+ </object>
+ <int key="connectionID">368</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">terminate:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="632727374"/>
+ </object>
+ <int key="connectionID">369</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">unhideAllApplications:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="908899353"/>
+ </object>
+ <int key="connectionID">370</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">newDocument:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="705341025"/>
+ </object>
+ <int key="connectionID">373</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">openDocument:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="722745758"/>
+ </object>
+ <int key="connectionID">374</int>
+ </object>
+ </object>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <object class="NSArray" key="orderedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <object class="NSArray" key="object" id="1049">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="children" ref="1048"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="1021"/>
+ <reference key="parent" ref="1049"/>
+ <string type="base64-UTF8" key="objectName">RmlsZSdzIE93bmVyA</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="1014"/>
+ <reference key="parent" ref="1049"/>
+ <string key="objectName">First Responder</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-3</int>
+ <reference key="object" ref="1050"/>
+ <reference key="parent" ref="1049"/>
+ <string key="objectName">Application</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">29</int>
+ <reference key="object" ref="649796088"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="694149608"/>
+ <reference ref="391199113"/>
+ <reference ref="952259628"/>
+ <reference ref="379814623"/>
+ <reference ref="586577488"/>
+ <reference ref="626404410"/>
+ </object>
+ <reference key="parent" ref="1049"/>
+ <string key="objectName">MainMenu</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">56</int>
+ <reference key="object" ref="694149608"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="110575045"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">103</int>
+ <reference key="object" ref="391199113"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="374024848"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ <string key="objectName">1</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">217</int>
+ <reference key="object" ref="952259628"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="789758025"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">83</int>
+ <reference key="object" ref="379814623"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="720053764"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">81</int>
+ <reference key="object" ref="720053764"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1023925487"/>
+ <reference ref="117038363"/>
+ <reference ref="49223823"/>
+ <reference ref="722745758"/>
+ <reference ref="705341025"/>
+ <reference ref="1025936716"/>
+ <reference ref="294629803"/>
+ <reference ref="776162233"/>
+ <reference ref="425164168"/>
+ <reference ref="579971712"/>
+ <reference ref="1010469920"/>
+ </object>
+ <reference key="parent" ref="379814623"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">75</int>
+ <reference key="object" ref="1023925487"/>
+ <reference key="parent" ref="720053764"/>
+ <string key="objectName">3</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">80</int>
+ <reference key="object" ref="117038363"/>
+ <reference key="parent" ref="720053764"/>
+ <string key="objectName">8</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">78</int>
+ <reference key="object" ref="49223823"/>
+ <reference key="parent" ref="720053764"/>
+ <string key="objectName">6</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">72</int>
+ <reference key="object" ref="722745758"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">82</int>
+ <reference key="object" ref="705341025"/>
+ <reference key="parent" ref="720053764"/>
+ <string key="objectName">9</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">124</int>
+ <reference key="object" ref="1025936716"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1065607017"/>
+ </object>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">77</int>
+ <reference key="object" ref="294629803"/>
+ <reference key="parent" ref="720053764"/>
+ <string key="objectName">5</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">73</int>
+ <reference key="object" ref="776162233"/>
+ <reference key="parent" ref="720053764"/>
+ <string key="objectName">1</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">79</int>
+ <reference key="object" ref="425164168"/>
+ <reference key="parent" ref="720053764"/>
+ <string key="objectName">7</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">112</int>
+ <reference key="object" ref="579971712"/>
+ <reference key="parent" ref="720053764"/>
+ <string key="objectName">10</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">74</int>
+ <reference key="object" ref="1010469920"/>
+ <reference key="parent" ref="720053764"/>
+ <string key="objectName">2</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">125</int>
+ <reference key="object" ref="1065607017"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="759406840"/>
+ </object>
+ <reference key="parent" ref="1025936716"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">126</int>
+ <reference key="object" ref="759406840"/>
+ <reference key="parent" ref="1065607017"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">205</int>
+ <reference key="object" ref="789758025"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="437104165"/>
+ <reference ref="583158037"/>
+ <reference ref="1058277027"/>
+ <reference ref="212016141"/>
+ <reference ref="296257095"/>
+ <reference ref="29853731"/>
+ <reference ref="860595796"/>
+ <reference ref="1040322652"/>
+ <reference ref="790794224"/>
+ <reference ref="892235320"/>
+ <reference ref="972420730"/>
+ <reference ref="676164635"/>
+ <reference ref="507821607"/>
+ </object>
+ <reference key="parent" ref="952259628"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">202</int>
+ <reference key="object" ref="437104165"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">198</int>
+ <reference key="object" ref="583158037"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">207</int>
+ <reference key="object" ref="1058277027"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">214</int>
+ <reference key="object" ref="212016141"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">199</int>
+ <reference key="object" ref="296257095"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">203</int>
+ <reference key="object" ref="29853731"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">197</int>
+ <reference key="object" ref="860595796"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">206</int>
+ <reference key="object" ref="1040322652"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">215</int>
+ <reference key="object" ref="790794224"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">218</int>
+ <reference key="object" ref="892235320"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="963351320"/>
+ </object>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">216</int>
+ <reference key="object" ref="972420730"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="769623530"/>
+ </object>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">200</int>
+ <reference key="object" ref="769623530"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="948374510"/>
+ <reference ref="96193923"/>
+ <reference ref="679648819"/>
+ <reference ref="967646866"/>
+ </object>
+ <reference key="parent" ref="972420730"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">219</int>
+ <reference key="object" ref="948374510"/>
+ <reference key="parent" ref="769623530"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">201</int>
+ <reference key="object" ref="96193923"/>
+ <reference key="parent" ref="769623530"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">204</int>
+ <reference key="object" ref="679648819"/>
+ <reference key="parent" ref="769623530"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">220</int>
+ <reference key="object" ref="963351320"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="270902937"/>
+ <reference ref="88285865"/>
+ <reference ref="159080638"/>
+ <reference ref="326711663"/>
+ <reference ref="447796847"/>
+ </object>
+ <reference key="parent" ref="892235320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">213</int>
+ <reference key="object" ref="270902937"/>
+ <reference key="parent" ref="963351320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">210</int>
+ <reference key="object" ref="88285865"/>
+ <reference key="parent" ref="963351320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">221</int>
+ <reference key="object" ref="159080638"/>
+ <reference key="parent" ref="963351320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">208</int>
+ <reference key="object" ref="326711663"/>
+ <reference key="parent" ref="963351320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">209</int>
+ <reference key="object" ref="447796847"/>
+ <reference key="parent" ref="963351320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">106</int>
+ <reference key="object" ref="374024848"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="238773614"/>
+ </object>
+ <reference key="parent" ref="391199113"/>
+ <string key="objectName">2</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">111</int>
+ <reference key="object" ref="238773614"/>
+ <reference key="parent" ref="374024848"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">57</int>
+ <reference key="object" ref="110575045"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="238522557"/>
+ <reference ref="755159360"/>
+ <reference ref="908899353"/>
+ <reference ref="632727374"/>
+ <reference ref="646227648"/>
+ <reference ref="609285721"/>
+ <reference ref="481834944"/>
+ <reference ref="304266470"/>
+ <reference ref="1046388886"/>
+ <reference ref="1056857174"/>
+ <reference ref="342932134"/>
+ </object>
+ <reference key="parent" ref="694149608"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">58</int>
+ <reference key="object" ref="238522557"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">134</int>
+ <reference key="object" ref="755159360"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">150</int>
+ <reference key="object" ref="908899353"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">136</int>
+ <reference key="object" ref="632727374"/>
+ <reference key="parent" ref="110575045"/>
+ <string key="objectName">1111</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">144</int>
+ <reference key="object" ref="646227648"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">129</int>
+ <reference key="object" ref="609285721"/>
+ <reference key="parent" ref="110575045"/>
+ <string key="objectName">121</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">143</int>
+ <reference key="object" ref="481834944"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">236</int>
+ <reference key="object" ref="304266470"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">131</int>
+ <reference key="object" ref="1046388886"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="752062318"/>
+ </object>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">149</int>
+ <reference key="object" ref="1056857174"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">145</int>
+ <reference key="object" ref="342932134"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">130</int>
+ <reference key="object" ref="752062318"/>
+ <reference key="parent" ref="1046388886"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">295</int>
+ <reference key="object" ref="586577488"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="466310130"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">296</int>
+ <reference key="object" ref="466310130"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="102151532"/>
+ <reference ref="237841660"/>
+ </object>
+ <reference key="parent" ref="586577488"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">297</int>
+ <reference key="object" ref="102151532"/>
+ <reference key="parent" ref="466310130"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">298</int>
+ <reference key="object" ref="237841660"/>
+ <reference key="parent" ref="466310130"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">299</int>
+ <reference key="object" ref="626404410"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="502084290"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">300</int>
+ <reference key="object" ref="502084290"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="519768076"/>
+ <reference ref="1028416764"/>
+ </object>
+ <reference key="parent" ref="626404410"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">344</int>
+ <reference key="object" ref="519768076"/>
+ <reference key="parent" ref="502084290"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">345</int>
+ <reference key="object" ref="1028416764"/>
+ <reference key="parent" ref="502084290"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">211</int>
+ <reference key="object" ref="676164635"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="785027613"/>
+ </object>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">212</int>
+ <reference key="object" ref="785027613"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="680220178"/>
+ <reference ref="731782645"/>
+ </object>
+ <reference key="parent" ref="676164635"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">195</int>
+ <reference key="object" ref="680220178"/>
+ <reference key="parent" ref="785027613"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">196</int>
+ <reference key="object" ref="731782645"/>
+ <reference key="parent" ref="785027613"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">346</int>
+ <reference key="object" ref="967646866"/>
+ <reference key="parent" ref="769623530"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">348</int>
+ <reference key="object" ref="507821607"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="698887838"/>
+ </object>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">349</int>
+ <reference key="object" ref="698887838"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="605118523"/>
+ <reference ref="197661976"/>
+ <reference ref="708854459"/>
+ </object>
+ <reference key="parent" ref="507821607"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">350</int>
+ <reference key="object" ref="605118523"/>
+ <reference key="parent" ref="698887838"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">351</int>
+ <reference key="object" ref="197661976"/>
+ <reference key="parent" ref="698887838"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">354</int>
+ <reference key="object" ref="708854459"/>
+ <reference key="parent" ref="698887838"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="flattenedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>-1.IBPluginDependency</string>
+ <string>-2.IBPluginDependency</string>
+ <string>-3.IBPluginDependency</string>
+ <string>103.IBPluginDependency</string>
+ <string>103.ImportedFromIB2</string>
+ <string>106.IBEditorWindowLastContentRect</string>
+ <string>106.IBPluginDependency</string>
+ <string>106.ImportedFromIB2</string>
+ <string>106.editorWindowContentRectSynchronizationRect</string>
+ <string>111.IBPluginDependency</string>
+ <string>111.ImportedFromIB2</string>
+ <string>112.IBPluginDependency</string>
+ <string>112.ImportedFromIB2</string>
+ <string>124.IBPluginDependency</string>
+ <string>124.ImportedFromIB2</string>
+ <string>125.IBPluginDependency</string>
+ <string>125.ImportedFromIB2</string>
+ <string>125.editorWindowContentRectSynchronizationRect</string>
+ <string>126.IBPluginDependency</string>
+ <string>126.ImportedFromIB2</string>
+ <string>129.IBPluginDependency</string>
+ <string>129.ImportedFromIB2</string>
+ <string>130.IBPluginDependency</string>
+ <string>130.ImportedFromIB2</string>
+ <string>130.editorWindowContentRectSynchronizationRect</string>
+ <string>131.IBPluginDependency</string>
+ <string>131.ImportedFromIB2</string>
+ <string>134.IBPluginDependency</string>
+ <string>134.ImportedFromIB2</string>
+ <string>136.IBPluginDependency</string>
+ <string>136.ImportedFromIB2</string>
+ <string>143.IBPluginDependency</string>
+ <string>143.ImportedFromIB2</string>
+ <string>144.IBPluginDependency</string>
+ <string>144.ImportedFromIB2</string>
+ <string>145.IBPluginDependency</string>
+ <string>145.ImportedFromIB2</string>
+ <string>149.IBPluginDependency</string>
+ <string>149.ImportedFromIB2</string>
+ <string>150.IBPluginDependency</string>
+ <string>150.ImportedFromIB2</string>
+ <string>195.IBPluginDependency</string>
+ <string>195.ImportedFromIB2</string>
+ <string>196.IBPluginDependency</string>
+ <string>196.ImportedFromIB2</string>
+ <string>197.IBPluginDependency</string>
+ <string>197.ImportedFromIB2</string>
+ <string>198.IBPluginDependency</string>
+ <string>198.ImportedFromIB2</string>
+ <string>199.IBPluginDependency</string>
+ <string>199.ImportedFromIB2</string>
+ <string>200.IBPluginDependency</string>
+ <string>200.ImportedFromIB2</string>
+ <string>200.editorWindowContentRectSynchronizationRect</string>
+ <string>201.IBPluginDependency</string>
+ <string>201.ImportedFromIB2</string>
+ <string>202.IBPluginDependency</string>
+ <string>202.ImportedFromIB2</string>
+ <string>203.IBPluginDependency</string>
+ <string>203.ImportedFromIB2</string>
+ <string>204.IBPluginDependency</string>
+ <string>204.ImportedFromIB2</string>
+ <string>205.IBPluginDependency</string>
+ <string>205.ImportedFromIB2</string>
+ <string>205.editorWindowContentRectSynchronizationRect</string>
+ <string>206.IBPluginDependency</string>
+ <string>206.ImportedFromIB2</string>
+ <string>207.IBPluginDependency</string>
+ <string>207.ImportedFromIB2</string>
+ <string>208.IBPluginDependency</string>
+ <string>208.ImportedFromIB2</string>
+ <string>209.IBPluginDependency</string>
+ <string>209.ImportedFromIB2</string>
+ <string>210.IBPluginDependency</string>
+ <string>210.ImportedFromIB2</string>
+ <string>211.IBPluginDependency</string>
+ <string>211.ImportedFromIB2</string>
+ <string>212.IBPluginDependency</string>
+ <string>212.ImportedFromIB2</string>
+ <string>212.editorWindowContentRectSynchronizationRect</string>
+ <string>213.IBPluginDependency</string>
+ <string>213.ImportedFromIB2</string>
+ <string>214.IBPluginDependency</string>
+ <string>214.ImportedFromIB2</string>
+ <string>215.IBPluginDependency</string>
+ <string>215.ImportedFromIB2</string>
+ <string>216.IBPluginDependency</string>
+ <string>216.ImportedFromIB2</string>
+ <string>217.IBPluginDependency</string>
+ <string>217.ImportedFromIB2</string>
+ <string>218.IBPluginDependency</string>
+ <string>218.ImportedFromIB2</string>
+ <string>219.IBPluginDependency</string>
+ <string>219.ImportedFromIB2</string>
+ <string>220.IBPluginDependency</string>
+ <string>220.ImportedFromIB2</string>
+ <string>220.editorWindowContentRectSynchronizationRect</string>
+ <string>221.IBPluginDependency</string>
+ <string>221.ImportedFromIB2</string>
+ <string>236.IBPluginDependency</string>
+ <string>236.ImportedFromIB2</string>
+ <string>29.IBEditorWindowLastContentRect</string>
+ <string>29.IBPluginDependency</string>
+ <string>29.ImportedFromIB2</string>
+ <string>29.WindowOrigin</string>
+ <string>29.editorWindowContentRectSynchronizationRect</string>
+ <string>295.IBPluginDependency</string>
+ <string>296.IBEditorWindowLastContentRect</string>
+ <string>296.IBPluginDependency</string>
+ <string>296.editorWindowContentRectSynchronizationRect</string>
+ <string>297.IBPluginDependency</string>
+ <string>298.IBPluginDependency</string>
+ <string>299.IBPluginDependency</string>
+ <string>300.IBPluginDependency</string>
+ <string>300.editorWindowContentRectSynchronizationRect</string>
+ <string>344.IBPluginDependency</string>
+ <string>345.IBPluginDependency</string>
+ <string>346.IBPluginDependency</string>
+ <string>346.ImportedFromIB2</string>
+ <string>348.IBPluginDependency</string>
+ <string>348.ImportedFromIB2</string>
+ <string>349.IBPluginDependency</string>
+ <string>349.ImportedFromIB2</string>
+ <string>349.editorWindowContentRectSynchronizationRect</string>
+ <string>350.IBPluginDependency</string>
+ <string>350.ImportedFromIB2</string>
+ <string>351.IBPluginDependency</string>
+ <string>351.ImportedFromIB2</string>
+ <string>354.IBPluginDependency</string>
+ <string>354.ImportedFromIB2</string>
+ <string>56.IBPluginDependency</string>
+ <string>56.ImportedFromIB2</string>
+ <string>57.IBEditorWindowLastContentRect</string>
+ <string>57.IBPluginDependency</string>
+ <string>57.ImportedFromIB2</string>
+ <string>57.editorWindowContentRectSynchronizationRect</string>
+ <string>58.IBPluginDependency</string>
+ <string>58.ImportedFromIB2</string>
+ <string>72.IBPluginDependency</string>
+ <string>72.ImportedFromIB2</string>
+ <string>73.IBPluginDependency</string>
+ <string>73.ImportedFromIB2</string>
+ <string>74.IBPluginDependency</string>
+ <string>74.ImportedFromIB2</string>
+ <string>75.IBPluginDependency</string>
+ <string>75.ImportedFromIB2</string>
+ <string>77.IBPluginDependency</string>
+ <string>77.ImportedFromIB2</string>
+ <string>78.IBPluginDependency</string>
+ <string>78.ImportedFromIB2</string>
+ <string>79.IBPluginDependency</string>
+ <string>79.ImportedFromIB2</string>
+ <string>80.IBPluginDependency</string>
+ <string>80.ImportedFromIB2</string>
+ <string>81.IBEditorWindowLastContentRect</string>
+ <string>81.IBPluginDependency</string>
+ <string>81.ImportedFromIB2</string>
+ <string>81.editorWindowContentRectSynchronizationRect</string>
+ <string>82.IBPluginDependency</string>
+ <string>82.ImportedFromIB2</string>
+ <string>83.IBPluginDependency</string>
+ <string>83.ImportedFromIB2</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilderKit</string>
+ <string>com.apple.InterfaceBuilderKit</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1" id="9"/>
+ <string>{{417, 1069}, {216, 23}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{596, 852}, {216, 23}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{522, 812}, {146, 23}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{436, 809}, {64, 6}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{608, 612}, {275, 83}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{187, 434}, {243, 243}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{608, 612}, {167, 43}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{608, 612}, {241, 103}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{0, 1092}, {407, 20}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{74, 862}</string>
+ <string>{{6, 978}, {478, 20}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{296, 1049}, {234, 43}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{475, 832}, {234, 43}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{231, 634}, {176, 43}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{608, 612}, {215, 63}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{12, 909}, {245, 183}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{23, 794}, {245, 183}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{144, 889}, {199, 203}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{145, 474}, {199, 203}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="unlocalizedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="activeLocalization"/>
+ <object class="NSMutableDictionary" key="localizations">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="sourceID"/>
+ <int key="maxID">374</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <object class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSApplication</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">UnitTesting/GTMNSApplication+UnitTesting.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSMenu</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">UnitTesting/GTMNSMenu+UnitTesting.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSMenuItem</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">UnitTesting/GTMNSMenuItem+UnitTesting.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">AppKit/GTMDelegatingTableColumn.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">UnitTesting/GTMNSObject+BindingUnitTesting.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">UnitTesting/GTMNSObject+UnitTesting.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">UnitTesting/GTMSenTestCase.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">UnitTesting/GTMNSView+UnitTesting.h</string>
+ </object>
+ </object>
+ </object>
+ </object>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.LastKnownRelativeProjectPath">../../../GTM.xcodeproj</string>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ </data>
+</archive>
diff --git a/UnitTesting/GTMUIUnitTestingHarness/English.lproj/MainMenu.nib/keyedobjects.nib b/UnitTesting/GTMUIUnitTestingHarness/English.lproj/MainMenu.nib/keyedobjects.nib
new file mode 100644
index 0000000..318a7e9
--- /dev/null
+++ b/UnitTesting/GTMUIUnitTestingHarness/English.lproj/MainMenu.nib/keyedobjects.nib
Binary files differ
diff --git a/UnitTesting/GTMUIUnitTestingHarness/Info.plist b/UnitTesting/GTMUIUnitTestingHarness/Info.plist
new file mode 100644
index 0000000..fe3e712
--- /dev/null
+++ b/UnitTesting/GTMUIUnitTestingHarness/Info.plist
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>com.google.GTMUIUnitTestingHarness</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
diff --git a/UnitTesting/GTMUIUnitTestingHarness/main.m b/UnitTesting/GTMUIUnitTestingHarness/main.m
new file mode 100644
index 0000000..8fb364d
--- /dev/null
+++ b/UnitTesting/GTMUIUnitTestingHarness/main.m
@@ -0,0 +1,27 @@
+//
+// main.m
+// GTMUnitTestingTest
+//
+// Copyright 2006-2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+
+#import <Cocoa/Cocoa.h>
+#import "GTMUnitTestingUtilities.h"
+
+int main(int argc, char *argv[]) {
+ [GTMUnitTestingUtilities setUpForUIUnitTestsIfBeingTested];
+ return NSApplicationMain(argc, (const char **) argv);
+}
diff --git a/UnitTesting/GTMUIViewUnitTestingTest.gtmUTState b/UnitTesting/GTMUIViewUnitTestingTest.gtmUTState
new file mode 100644
index 0000000..87ae9e5
--- /dev/null
+++ b/UnitTesting/GTMUIViewUnitTestingTest.gtmUTState
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>$GTMArchive</key>
+ <string>GTMUnitTestingArchive</string>
+ <key>$GTMVersion</key>
+ <integer>1</integer>
+ <key>LayerIsDoublesided</key>
+ <true/>
+ <key>LayerIsHidden</key>
+ <false/>
+ <key>LayerIsOpaque</key>
+ <true/>
+ <key>LayerOpacity</key>
+ <real>1</real>
+ <key>ViewIsHidden</key>
+ <false/>
+ <key>ViewSubView 0</key>
+ <dict>
+ <key>LayerIsDoublesided</key>
+ <true/>
+ <key>LayerIsHidden</key>
+ <false/>
+ <key>LayerIsOpaque</key>
+ <true/>
+ <key>LayerOpacity</key>
+ <real>1</real>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+</dict>
+</plist>
diff --git a/UnitTesting/GTMUIViewUnitTestingTest.png b/UnitTesting/GTMUIViewUnitTestingTest.png
new file mode 100644
index 0000000..03fd9f0
--- /dev/null
+++ b/UnitTesting/GTMUIViewUnitTestingTest.png
Binary files differ
diff --git a/UnitTesting/GTMUnitTestingBindingTest.m b/UnitTesting/GTMUnitTestingBindingTest.m
new file mode 100644
index 0000000..c0ef93c
--- /dev/null
+++ b/UnitTesting/GTMUnitTestingBindingTest.m
@@ -0,0 +1,117 @@
+//
+// GTMUnitTestingBindingTest.m
+//
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import <SenTestingKit/SenTestingKit.h>
+#import "GTMUnitTestingTest.h"
+#import "GTMNSObject+BindingUnitTesting.h"
+
+@interface GTMUnitTestingBindingTest : SenTestCase {
+ int expectedFailureCount_;
+}
+@end
+
+@interface GTMUnitTestingBindingBadClass : NSObject
+@end
+
+@implementation GTMUnitTestingBindingTest
+
+// Iterates through all of our subviews testing the exposed bindings
+- (void)doSubviewBindingTest:(NSView*)view {
+ NSArray *subviews = [view subviews];
+ NSEnumerator *subviewEnum = [subviews objectEnumerator];
+ NSView *subview;
+ while ((subview = [subviewEnum nextObject])) {
+ GTMTestExposedBindings(subview, @"testing %@", subview);
+ [self doSubviewBindingTest:subview];
+ }
+}
+
+- (void)testBindings {
+ // Get our window to work with and test it's bindings
+ GTMUnitTestingTestController *testWindowController
+ = [[GTMUnitTestingTestController alloc] initWithWindowNibName:@"GTMUnitTestingTest"];
+ NSWindow *window = [testWindowController window];
+ GTMTestExposedBindings(window, @"Window failed binding test");
+ [self doSubviewBindingTest:[window contentView]];
+ [testWindowController release];
+
+ // Run a test against something with no bindings.
+ // We're expecting a failure here.
+ expectedFailureCount_ = 1;
+ GTMTestExposedBindings(@"foo", @"testing no bindings");
+ STAssertEquals(expectedFailureCount_, 0, @"Didn't get expected failures testing bindings");
+
+ // Run test against some with bad bindings.
+ // We're expecting failures here.
+ expectedFailureCount_ = 4;
+ GTMUnitTestingBindingBadClass *bad = [[[GTMUnitTestingBindingBadClass alloc] init] autorelease];
+ GTMTestExposedBindings(bad, @"testing bad bindings");
+ STAssertEquals(expectedFailureCount_, 0, @"Didn't get expected failures testing bad bindings");
+}
+
+- (void)failWithException:(NSException *)anException {
+ if (expectedFailureCount_ > 0) {
+ expectedFailureCount_ -= 1;
+ } else {
+ [super failWithException:anException]; // COV_NF_LINE - not expecting exception
+ }
+}
+
+@end
+
+// Forces several error cases in our binding tests to test them
+@implementation GTMUnitTestingBindingBadClass
+
+NSString *const kGTMKeyWithNoClass = @"keyWithNoClass";
+NSString *const kGTMKeyWithNoValue = @"keyWithNoValue";
+NSString *const kGTMKeyWeCantSet = @"keyWeCantSet";
+NSString *const kGTMKeyThatIsntEqual = @"keyThatIsntEqual";
+
+- (NSArray *)exposedBindings {
+ return [NSArray arrayWithObjects:kGTMKeyWithNoClass,
+ kGTMKeyWithNoValue,
+ kGTMKeyWeCantSet,
+ kGTMKeyThatIsntEqual,
+ nil];
+}
+
+- (NSMutableDictionary*)gtm_unitTestExposedBindingsTestValues:(NSString*)binding {
+
+ NSMutableDictionary *dict = [NSMutableDictionary dictionary];
+ [dict setObject:kGTMKeyThatIsntEqual forKey:kGTMKeyThatIsntEqual];
+ return dict;
+}
+
+- (Class)valueClassForBinding:(NSString*)binding {
+ return [binding isEqualTo:kGTMKeyWithNoClass] ? nil : [NSString class];
+}
+
+- (id)valueForKey:(NSString*)binding {
+ if ([binding isEqualTo:kGTMKeyWithNoValue]) {
+ [NSException raise:NSUndefinedKeyException format:nil];
+ }
+ return @"foo";
+}
+
+- (void)setValue:(id)value forKey:(NSString*)binding {
+ if ([binding isEqualTo:kGTMKeyWeCantSet]) {
+ [NSException raise:NSUndefinedKeyException format:nil];
+ }
+}
+@end
+
diff --git a/UnitTesting/GTMUnitTestingImage.gtmUTState b/UnitTesting/GTMUnitTestingImage.gtmUTState
new file mode 100644
index 0000000..969ddf6
--- /dev/null
+++ b/UnitTesting/GTMUnitTestingImage.gtmUTState
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>$GTMArchive</key>
+ <string>GTMUnitTestingArchive</string>
+ <key>$GTMVersion</key>
+ <integer>1</integer>
+ <key>BoolTest</key>
+ <true/>
+ <key>BytesTest</key>
+ <data>
+ Qnl0ZXNUZXN0
+ </data>
+ <key>DoubleTest</key>
+ <real>1</real>
+ <key>FloatTest</key>
+ <real>1</real>
+ <key>ImageName</key>
+ <string>NSApplicationIcon</string>
+ <key>ImageSize</key>
+ <string>{128, 128}</string>
+ <key>Int32Test</key>
+ <integer>1</integer>
+ <key>Int64Test</key>
+ <integer>1</integer>
+ <key>IntTest</key>
+ <integer>1</integer>
+</dict>
+</plist>
diff --git a/UnitTesting/GTMUnitTestingImage.tiff b/UnitTesting/GTMUnitTestingImage.tiff
new file mode 100644
index 0000000..4d08297
--- /dev/null
+++ b/UnitTesting/GTMUnitTestingImage.tiff
Binary files differ
diff --git a/UnitTesting/GTMUnitTestingTest.h b/UnitTesting/GTMUnitTestingTest.h
new file mode 100644
index 0000000..ea9521b
--- /dev/null
+++ b/UnitTesting/GTMUnitTestingTest.h
@@ -0,0 +1,29 @@
+//
+// GTMUnitTestingTest.h
+//
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import <AppKit/AppKit.h>
+
+// GTMUnitTestingTestController controller so that initWithWindowNibName can
+// find the appropriate bundle to load our nib from. See [GTMUnitTestingTest
+// -testUnitTestingFramework] for more info
+@interface GTMUnitTestingTestController : NSWindowController {
+ IBOutlet NSTextField *field_;
+}
+
+- (NSTextField *)textField;
+@end
diff --git a/UnitTesting/GTMUnitTestingTest.m b/UnitTesting/GTMUnitTestingTest.m
new file mode 100644
index 0000000..108bf56
--- /dev/null
+++ b/UnitTesting/GTMUnitTestingTest.m
@@ -0,0 +1,265 @@
+//
+// GTMUnitTestingTest.m
+//
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import <SenTestingKit/SenTestingKit.h>
+#import "GTMUnitTestingTest.h"
+#import "GTMNSApplication+UnitTesting.h"
+#import "GTMNSView+UnitTesting.h"
+
+NSString *const kGTMWindowNibName = @"GTMUnitTestingTest";
+NSString *const kGTMWindowSaveFileName = @"GTMUnitTestingWindow";
+
+@interface GTMUnitTestingTest : SenTestCase {
+ int expectedFailureCount_;
+}
+@end
+
+// GTMUnitTestingTest support classes
+@interface GTMUnitTestingView : NSObject <GTMUnitTestViewDrawer> {
+ BOOL goodContext_;
+}
+- (BOOL)hadGoodContext;
+@end
+
+@interface GTMUnitTestingDelegate : NSObject {
+ BOOL didEncode_;
+}
+- (BOOL)didEncode;
+@end
+
+@interface GTMUnitTestingProxyTest : NSProxy
+@end
+
+@implementation GTMUnitTestingTest
+
+// Brings up the window defined in the nib and takes a snapshot of it.
+// We use the "empty" GTMUnitTestingTestController controller so that
+// initWithWindowNibName can find the appropriate bundle to load our nib from.
+// For some reason when running unit tests, with all the injecting going on
+// the nib loader can get confused as to where it should load a nib from.
+// Having a NSWindowController subclass in the same bundle as the nib seems
+// to help the nib loader find the nib, and any custom classes that are attached
+// to it.
+- (void)testUnitTestingFramework {
+ // set up our delegates so we can test delegate handling
+ GTMUnitTestingDelegate *appDelegate = [[GTMUnitTestingDelegate alloc] init];
+ [NSApp setDelegate:appDelegate];
+
+ // Get our window
+ GTMUnitTestingTestController *testWindowController
+ = [[GTMUnitTestingTestController alloc] initWithWindowNibName:kGTMWindowNibName];
+ NSWindow *window = [testWindowController window];
+ // Test the app state. This will cover windows and menus
+ GTMAssertObjectStateEqualToStateNamed(NSApp,
+ @"GTMUnitTestingTestApp",
+ @"Testing the app state");
+
+ // Test the window image and state
+ GTMAssertObjectEqualToStateAndImageNamed(window,
+ kGTMWindowSaveFileName,
+ @"Testing the window image and state");
+
+ // Verify that all of our delegate encoders got called
+ STAssertTrue([appDelegate didEncode], @"app delegate didn't get called?");
+
+ // Clean up
+ [NSApp setDelegate:nil];
+ [appDelegate release];
+ [window close];
+}
+
+- (void)testViewUnitTesting {
+ GTMUnitTestingView *unitTestingView = [[GTMUnitTestingView alloc] init];
+ GTMAssertDrawingEqualToFile(unitTestingView,
+ NSMakeSize(200,200),
+ @"GTMUnitTestingView",
+ NSApp,
+ @"Testing view drawing");
+ STAssertTrue([unitTestingView hadGoodContext], @"bad context?");
+ [unitTestingView release];
+}
+
+- (void)testImageUnitTesting {
+ NSImage *image = [NSImage imageNamed:@"NSApplicationIcon"];
+ GTMUnitTestingDelegate *imgDelegate = [[GTMUnitTestingDelegate alloc] init];
+ [image setDelegate:imgDelegate];
+ GTMAssertObjectEqualToStateAndImageNamed(image,
+ @"GTMUnitTestingImage",
+ @"Testing NSImage image and state");
+ STAssertTrue([imgDelegate didEncode], @"imgDelegate didn't get called?");
+ [image setDelegate:nil];
+ [imgDelegate release];
+}
+
+- (void)testFailures {
+ NSString *const bogusTestName = @"GTMUnitTestTestingFailTest";
+ NSString *tempDir = NSTemporaryDirectory();
+ STAssertNotNil(tempDir, @"No Temp Dir?");
+ NSString *originalPath = [NSObject gtm_getUnitTestSaveToDirectory];
+ STAssertNotNil(originalPath, @"No save dir?");
+ [NSObject gtm_setUnitTestSaveToDirectory:tempDir];
+ STAssertEqualObjects(tempDir, [NSObject gtm_getUnitTestSaveToDirectory],
+ @"Save to dir not set?");
+ NSString *statePath = [self gtm_saveToPathForStateNamed:bogusTestName];
+ STAssertNotNil(statePath, @"no state path?");
+ NSString *imagePath = [self gtm_saveToPathForImageNamed:bogusTestName];
+ STAssertNotNil(imagePath, @"no image path?");
+ GTMUnitTestingTestController *testWindowController
+ = [[GTMUnitTestingTestController alloc] initWithWindowNibName:kGTMWindowNibName];
+ NSWindow *window = [testWindowController window];
+
+ // Test against a golden master filename that doesn't exist
+ expectedFailureCount_ = 2;
+ GTMAssertObjectEqualToStateAndImageNamed(window,
+ bogusTestName,
+ @"Creating image and state files");
+ STAssertEquals(expectedFailureCount_, 0,
+ @"Didn't get expected failures creating files");
+
+ // Change our image and state and verify failures
+ [[testWindowController textField] setStringValue:@"Foo"];
+ expectedFailureCount_ = 2;
+ GTMAssertObjectEqualToStateAndImageNamed(window,
+ kGTMWindowSaveFileName,
+ @"Testing the window image and state");
+ STAssertEquals(expectedFailureCount_, 0,
+ @"Didn't get expected failures testing files");
+
+ // Now change the size of our image and verify failures
+ NSRect oldFrame = [window frame];
+ NSRect newFrame = oldFrame;
+ newFrame.size.width += 1;
+ [window setFrame:newFrame display:YES];
+ expectedFailureCount_ = 1;
+ GTMAssertObjectImageEqualToImageNamed(window,
+ kGTMWindowSaveFileName,
+ @"Testing the changed window size");
+ [window setFrame:oldFrame display:YES];
+
+ // Set our unit test save dir to a bogus directory and
+ // run the tests again.
+ [NSObject gtm_setUnitTestSaveToDirectory:@"/zim/blatz/foo/bob/bar"];
+ expectedFailureCount_ = 2;
+ GTMAssertObjectEqualToStateAndImageNamed(window,
+ kGTMWindowSaveFileName,
+ @"Testing the window image and state");
+ STAssertEquals(expectedFailureCount_, 0,
+ @"Didn't get expected failures testing files");
+ expectedFailureCount_ = 2;
+ GTMAssertObjectEqualToStateAndImageNamed(window,
+ @"GTMUnitTestingWindowDoesntExist",
+ @"Testing the window image and state");
+ STAssertEquals(expectedFailureCount_, 0,
+ @"Didn't get expected failures testing files");
+
+ // Reset our unit test save dir
+ [NSObject gtm_setUnitTestSaveToDirectory:nil];
+
+ // Test against something that doesn't have an image
+ expectedFailureCount_ = 1;
+ GTMAssertObjectImageEqualToImageNamed(@"a string",
+ @"GTMStringsDontHaveImages",
+ @"Testing that strings should fail");
+ STAssertEquals(expectedFailureCount_, 0, @"Didn't get expected failures testing files");
+
+ // Test against something that doesn't implement our support
+ expectedFailureCount_ = 1;
+ GTMUnitTestingProxyTest *proxy = [[GTMUnitTestingProxyTest alloc] init];
+ GTMAssertObjectStateEqualToStateNamed(proxy,
+ @"NSProxiesDontDoState",
+ @"Testing that NSProxy should fail");
+ STAssertEquals(expectedFailureCount_, 0, @"Didn't get expected failures testing proxy");
+ [proxy release];
+
+ [window close];
+}
+
+- (void)failWithException:(NSException *)anException {
+ if (expectedFailureCount_ > 0) {
+ expectedFailureCount_ -= 1;
+ } else {
+ [super failWithException:anException]; // COV_NF_LINE - not expecting exception
+ }
+}
+
+
+@end
+
+@implementation GTMUnitTestingTestController
+- (NSTextField *)textField {
+ return field_;
+}
+
+- (void)dealloc {
+ NSWindow *window = [self window];
+ int count = [window retainCount];
+
+ // Spinning the run loop here to get rid of the window. Stupid issue
+ // where there's a delayed selector holding a retain count on our window
+ // rdar://5851458 - Closing a window with a NSTextView in it should get rid of it immediately
+ while (count == [window retainCount]) {
+ [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
+ }
+ [super dealloc];
+}
+@end
+
+@implementation GTMUnitTestingDelegate
+
+- (void)gtm_unitTestEncoderWillEncode:(id)sender inCoder:(NSCoder*)inCoder {
+ // Test various encodings
+ [inCoder encodeBool:YES forKey:@"BoolTest"];
+ [inCoder encodeInt:1 forKey:@"IntTest"];
+ [inCoder encodeInt32:1 forKey:@"Int32Test"];
+ [inCoder encodeInt64:1 forKey:@"Int64Test"];
+ [inCoder encodeFloat:1.0f forKey:@"FloatTest"];
+ [inCoder encodeDouble:1.0 forKey:@"DoubleTest"];
+ [inCoder encodeBytes:(uint8_t*)"BytesTest" length:9 forKey:@"BytesTest"];
+ didEncode_ = YES;
+}
+
+- (BOOL)didEncode {
+ return didEncode_;
+}
+@end
+
+@implementation GTMUnitTestingView
+
+- (void)gtm_unitTestViewDrawRect:(NSRect)rect contextInfo:(void*)contextInfo {
+ [[NSColor redColor] set];
+ NSRectFill(rect);
+ goodContext_ = [(id)contextInfo isEqualTo:NSApp];
+}
+
+- (BOOL)hadGoodContext {
+ return goodContext_;
+}
+@end
+
+// GTMUnitTestingProxyTest is for testing the case where we don't conform to
+// the GTMUnitTestingEncoding protocol.
+@implementation GTMUnitTestingProxyTest
+- (id)init {
+ return self;
+}
+
+- (BOOL)conformsToProtocol:(Protocol *)protocol {
+ return NO;
+}
+
+@end
diff --git a/UnitTesting/GTMUnitTestingTest.nib/classes.nib b/UnitTesting/GTMUnitTestingTest.nib/classes.nib
new file mode 100644
index 0000000..cc51f67
--- /dev/null
+++ b/UnitTesting/GTMUnitTestingTest.nib/classes.nib
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IBClasses</key>
+ <array>
+ <dict>
+ <key>CLASS</key>
+ <string>NSView</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ <key>SUPERCLASS</key>
+ <string>NSResponder</string>
+ </dict>
+ <dict>
+ <key>CLASS</key>
+ <string>NSApplication</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ <key>SUPERCLASS</key>
+ <string>NSResponder</string>
+ </dict>
+ <dict>
+ <key>CLASS</key>
+ <string>NSTextField</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ <key>SUPERCLASS</key>
+ <string>NSControl</string>
+ </dict>
+ <dict>
+ <key>CLASS</key>
+ <string>NSMenu</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ <key>SUPERCLASS</key>
+ <string>NSObject</string>
+ </dict>
+ <dict>
+ <key>CLASS</key>
+ <string>NSControl</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ <key>SUPERCLASS</key>
+ <string>NSView</string>
+ </dict>
+ <dict>
+ <key>CLASS</key>
+ <string>NSObject</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ </dict>
+ <dict>
+ <key>CLASS</key>
+ <string>NSCell</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ <key>SUPERCLASS</key>
+ <string>NSObject</string>
+ </dict>
+ <dict>
+ <key>CLASS</key>
+ <string>NSWindow</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ <key>SUPERCLASS</key>
+ <string>NSResponder</string>
+ </dict>
+ <dict>
+ <key>CLASS</key>
+ <string>GTMUnitTestingTestController</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ <key>OUTLETS</key>
+ <dict>
+ <key>field_</key>
+ <string>NSTextField</string>
+ </dict>
+ <key>SUPERCLASS</key>
+ <string>NSWindowController</string>
+ </dict>
+ </array>
+ <key>IBVersion</key>
+ <string>1</string>
+</dict>
+</plist>
diff --git a/UnitTesting/GTMUnitTestingTest.nib/info.nib b/UnitTesting/GTMUnitTestingTest.nib/info.nib
new file mode 100644
index 0000000..3fb1618
--- /dev/null
+++ b/UnitTesting/GTMUnitTestingTest.nib/info.nib
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IBFramework Version</key>
+ <string>644</string>
+ <key>IBLastKnownRelativeProjectPath</key>
+ <string>../GTM.xcodeproj</string>
+ <key>IBOldestOS</key>
+ <integer>5</integer>
+ <key>IBOpenObjects</key>
+ <array>
+ <integer>2</integer>
+ </array>
+ <key>IBSystem Version</key>
+ <string>9C31</string>
+ <key>targetFramework</key>
+ <string>IBCocoaFramework</string>
+</dict>
+</plist>
diff --git a/UnitTesting/GTMUnitTestingTest.nib/keyedobjects.nib b/UnitTesting/GTMUnitTestingTest.nib/keyedobjects.nib
new file mode 100644
index 0000000..d3104e2
--- /dev/null
+++ b/UnitTesting/GTMUnitTestingTest.nib/keyedobjects.nib
Binary files differ
diff --git a/UnitTesting/GTMUnitTestingTestApp.gtmUTState b/UnitTesting/GTMUnitTestingTestApp.gtmUTState
new file mode 100644
index 0000000..1b7c4e5
--- /dev/null
+++ b/UnitTesting/GTMUnitTestingTestApp.gtmUTState
@@ -0,0 +1,1879 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>$GTMArchive</key>
+ <string>GTMUnitTestingArchive</string>
+ <key>$GTMVersion</key>
+ <integer>1</integer>
+ <key>ApplicationMainWindow</key>
+ <integer>0</integer>
+ <key>BoolTest</key>
+ <true/>
+ <key>BytesTest</key>
+ <data>
+ Qnl0ZXNUZXN0
+ </data>
+ <key>DoubleTest</key>
+ <real>1</real>
+ <key>FloatTest</key>
+ <real>1</real>
+ <key>Int32Test</key>
+ <integer>1</integer>
+ <key>Int64Test</key>
+ <integer>1</integer>
+ <key>IntTest</key>
+ <integer>1</integer>
+ <key>MenuBar</key>
+ <dict>
+ <key>MenuItem 0</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemSubmenu</key>
+ <dict>
+ <key>MenuItem 0</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>About NewApplication</string>
+ </dict>
+ <key>MenuItem 1</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <false/>
+ <key>MenuItemIsSeparator</key>
+ <true/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string></string>
+ </dict>
+ <key>MenuItem 10</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>q</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Quit NewApplication</string>
+ </dict>
+ <key>MenuItem 2</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>,</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Preferences…</string>
+ </dict>
+ <key>MenuItem 3</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <false/>
+ <key>MenuItemIsSeparator</key>
+ <true/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string></string>
+ </dict>
+ <key>MenuItem 4</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemSubmenu</key>
+ <dict>
+ <key>MenuTitle</key>
+ <string>Services</string>
+ </dict>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Services</string>
+ </dict>
+ <key>MenuItem 5</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <false/>
+ <key>MenuItemIsSeparator</key>
+ <true/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string></string>
+ </dict>
+ <key>MenuItem 6</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>h</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Hide NewApplication</string>
+ </dict>
+ <key>MenuItem 7</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>h</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Hide Others</string>
+ </dict>
+ <key>MenuItem 8</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Show All</string>
+ </dict>
+ <key>MenuItem 9</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <false/>
+ <key>MenuItemIsSeparator</key>
+ <true/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string></string>
+ </dict>
+ <key>MenuTitle</key>
+ <string>Apple</string>
+ </dict>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string></string>
+ </dict>
+ <key>MenuItem 1</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemSubmenu</key>
+ <dict>
+ <key>MenuItem 0</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>n</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>New</string>
+ </dict>
+ <key>MenuItem 1</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>o</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Open…</string>
+ </dict>
+ <key>MenuItem 10</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>P</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Page Setup...</string>
+ <key>MenuItemTooltip</key>
+ <string></string>
+ </dict>
+ <key>MenuItem 11</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>p</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Print…</string>
+ </dict>
+ <key>MenuItem 2</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemSubmenu</key>
+ <dict>
+ <key>MenuItem 0</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Clear Menu</string>
+ </dict>
+ <key>MenuTitle</key>
+ <string>Open Recent</string>
+ </dict>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Open Recent</string>
+ </dict>
+ <key>MenuItem 3</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <false/>
+ <key>MenuItemIsSeparator</key>
+ <true/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string></string>
+ </dict>
+ <key>MenuItem 4</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>w</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Close</string>
+ </dict>
+ <key>MenuItem 5</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <true/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>w</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Close All</string>
+ </dict>
+ <key>MenuItem 6</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>s</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Save</string>
+ </dict>
+ <key>MenuItem 7</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>S</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Save As…</string>
+ </dict>
+ <key>MenuItem 8</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Revert to Saved</string>
+ </dict>
+ <key>MenuItem 9</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <false/>
+ <key>MenuItemIsSeparator</key>
+ <true/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string></string>
+ </dict>
+ <key>MenuTitle</key>
+ <string>File</string>
+ </dict>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>File</string>
+ </dict>
+ <key>MenuItem 2</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemSubmenu</key>
+ <dict>
+ <key>MenuItem 0</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>z</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Undo</string>
+ </dict>
+ <key>MenuItem 1</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>Z</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Redo</string>
+ </dict>
+ <key>MenuItem 10</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemSubmenu</key>
+ <dict>
+ <key>MenuItem 0</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>:</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Show Spelling…</string>
+ </dict>
+ <key>MenuItem 1</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>;</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Check Spelling</string>
+ </dict>
+ <key>MenuItem 2</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Check Spelling While Typing</string>
+ </dict>
+ <key>MenuItem 3</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Check Grammar With Spelling</string>
+ </dict>
+ <key>MenuTitle</key>
+ <string>Spelling and Grammar</string>
+ </dict>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Spelling and Grammar</string>
+ </dict>
+ <key>MenuItem 11</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemSubmenu</key>
+ <dict>
+ <key>MenuItem 0</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>1</integer>
+ <key>MenuItemTitle</key>
+ <string>Smart Copy/Paste</string>
+ </dict>
+ <key>MenuItem 1</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>2</integer>
+ <key>MenuItemTitle</key>
+ <string>Smart Quotes</string>
+ </dict>
+ <key>MenuItem 2</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>3</integer>
+ <key>MenuItemTitle</key>
+ <string>Smart Links</string>
+ </dict>
+ <key>MenuTitle</key>
+ <string>Substitutions</string>
+ </dict>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Substitutions</string>
+ </dict>
+ <key>MenuItem 12</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemSubmenu</key>
+ <dict>
+ <key>MenuItem 0</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Start Speaking</string>
+ </dict>
+ <key>MenuItem 1</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Stop Speaking</string>
+ </dict>
+ <key>MenuTitle</key>
+ <string>Speech</string>
+ </dict>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Speech</string>
+ </dict>
+ <key>MenuItem 13</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <false/>
+ <key>MenuItemIsSeparator</key>
+ <true/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string></string>
+ </dict>
+ <key>MenuItem 14</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Special Characters…</string>
+ </dict>
+ <key>MenuItem 2</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <false/>
+ <key>MenuItemIsSeparator</key>
+ <true/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string></string>
+ </dict>
+ <key>MenuItem 3</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>x</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Cut</string>
+ </dict>
+ <key>MenuItem 4</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>c</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Copy</string>
+ </dict>
+ <key>MenuItem 5</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>v</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Paste</string>
+ </dict>
+ <key>MenuItem 6</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Delete</string>
+ </dict>
+ <key>MenuItem 7</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>a</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Select All</string>
+ </dict>
+ <key>MenuItem 8</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <false/>
+ <key>MenuItemIsSeparator</key>
+ <true/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string></string>
+ </dict>
+ <key>MenuItem 9</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemSubmenu</key>
+ <dict>
+ <key>MenuItem 0</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>f</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>1</integer>
+ <key>MenuItemTitle</key>
+ <string>Find…</string>
+ </dict>
+ <key>MenuItem 1</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>g</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>2</integer>
+ <key>MenuItemTitle</key>
+ <string>Find Next</string>
+ </dict>
+ <key>MenuItem 2</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>G</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>3</integer>
+ <key>MenuItemTitle</key>
+ <string>Find Previous</string>
+ </dict>
+ <key>MenuItem 3</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>e</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>7</integer>
+ <key>MenuItemTitle</key>
+ <string>Use Selection for Find</string>
+ </dict>
+ <key>MenuItem 4</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>j</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Jump to Selection</string>
+ </dict>
+ <key>MenuTitle</key>
+ <string>Find</string>
+ </dict>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Find</string>
+ </dict>
+ <key>MenuTitle</key>
+ <string>Edit</string>
+ </dict>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Edit</string>
+ </dict>
+ <key>MenuItem 3</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemSubmenu</key>
+ <dict>
+ <key>MenuItem 0</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>t</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Show Fonts</string>
+ </dict>
+ <key>MenuItem 1</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>C</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Show Colors</string>
+ </dict>
+ <key>MenuTitle</key>
+ <string>Format</string>
+ </dict>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Format</string>
+ </dict>
+ <key>MenuItem 4</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemSubmenu</key>
+ <dict>
+ <key>MenuItem 0</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>t</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Show Toolbar</string>
+ </dict>
+ <key>MenuItem 1</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Customize Toolbar…</string>
+ </dict>
+ <key>MenuTitle</key>
+ <string>View</string>
+ </dict>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>View</string>
+ </dict>
+ <key>MenuItem 5</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string></string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemSubmenu</key>
+ <dict>
+ <key>MenuItem 0</key>
+ <dict>
+ <key>MenuItemIndentationLevel</key>
+ <integer>0</integer>
+ <key>MenuItemIsAlternate</key>
+ <false/>
+ <key>MenuItemIsEnabled</key>
+ <true/>
+ <key>MenuItemIsSeparator</key>
+ <false/>
+ <key>MenuItemKeyEquivalent</key>
+ <string>?</string>
+ <key>MenuItemState</key>
+ <integer>0</integer>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>NewApplication Help</string>
+ </dict>
+ <key>MenuTitle</key>
+ <string>Help</string>
+ </dict>
+ <key>MenuItemTag</key>
+ <integer>0</integer>
+ <key>MenuItemTitle</key>
+ <string>Help</string>
+ </dict>
+ <key>MenuTitle</key>
+ <string>AMainMenu</string>
+ </dict>
+ <key>Window 0</key>
+ <dict>
+ <key>WindowContent</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ <key>ViewSubView 0</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ <key>ViewSubView 0</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ <key>ViewSubView 0</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSTableView</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>ViewSubView 1</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <false/>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSScroller</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 2</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <false/>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSScroller</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 3</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ <key>ViewSubView 0</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>ViewSubView 4</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>ViewSubView 1</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 10</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ <key>ViewSubView 0</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ <key>ViewSubView 0</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>ViewSubView 1</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <false/>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSScroller</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 2</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <false/>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSScroller</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>ViewSubView 11</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ <key>ViewSubView 0</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>ViewSubView 2</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlSelectedCell</key>
+ <dict>
+ <key>CellState</key>
+ <integer>0</integer>
+ <key>CellTag</key>
+ <integer>0</integer>
+ <key>CellTitle</key>
+ <string>HaHa</string>
+ <key>CellValue</key>
+ <string>HaHa</string>
+ </dict>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSTextField</string>
+ <key>ControlValue</key>
+ <string>HaHa</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 3</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlSelectedCell</key>
+ <dict>
+ <key>CellState</key>
+ <integer>0</integer>
+ <key>CellTag</key>
+ <integer>0</integer>
+ <key>CellTitle</key>
+ <string>Still Haven't Found What I'm Searching For</string>
+ <key>CellValue</key>
+ <string>Still Haven't Found What I'm Searching For</string>
+ </dict>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSSearchField</string>
+ <key>ControlValue</key>
+ <string>Still Haven't Found What I'm Searching For</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 4</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlSelectedCell</key>
+ <dict>
+ <key>CellState</key>
+ <integer>1</integer>
+ <key>CellTag</key>
+ <integer>0</integer>
+ <key>CellTitle</key>
+ <string>Foo</string>
+ <key>CellValue</key>
+ <string>Foo</string>
+ </dict>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSTextField</string>
+ <key>ControlValue</key>
+ <string>Foo</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 5</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlSelectedCell</key>
+ <dict>
+ <key>CellState</key>
+ <integer>1</integer>
+ <key>CellTag</key>
+ <integer>0</integer>
+ <key>CellTitle</key>
+ <string>CheckMate!</string>
+ <key>CellValue</key>
+ <string>1</string>
+ </dict>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSButton</string>
+ <key>ControlValue</key>
+ <string>1</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 6</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlSelectedCell</key>
+ <dict>
+ <key>CellState</key>
+ <integer>1</integer>
+ <key>CellTag</key>
+ <integer>0</integer>
+ <key>CellTitle</key>
+ <string></string>
+ <key>CellValue</key>
+ <string>50</string>
+ </dict>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSSlider</string>
+ <key>ControlValue</key>
+ <string>50</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 7</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlSelectedCell</key>
+ <dict>
+ <key>CellState</key>
+ <integer>0</integer>
+ <key>CellTag</key>
+ <integer>0</integer>
+ <key>CellTitle</key>
+ <string>Cancel</string>
+ <key>CellValue</key>
+ <string>0</string>
+ </dict>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSButton</string>
+ <key>ControlValue</key>
+ <string>0</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 8</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSColorWell</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 9</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>WindowIsMain</key>
+ <false/>
+ <key>WindowIsVisible</key>
+ <false/>
+ <key>WindowTitle</key>
+ <string>Window</string>
+ </dict>
+ <key>Window 1</key>
+ <dict>
+ <key>WindowContent</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ <key>ViewSubView 0</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ <key>ViewSubView 0</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ <key>ViewSubView 0</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSTableView</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>ViewSubView 1</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <false/>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSScroller</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 2</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <false/>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSScroller</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 3</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ <key>ViewSubView 0</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>ViewSubView 4</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>ViewSubView 1</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 10</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ <key>ViewSubView 0</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ <key>ViewSubView 0</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>ViewSubView 1</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <false/>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSScroller</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 2</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <false/>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSScroller</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>ViewSubView 11</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ <key>ViewSubView 0</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>ViewSubView 2</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlSelectedCell</key>
+ <dict>
+ <key>CellState</key>
+ <integer>0</integer>
+ <key>CellTag</key>
+ <integer>0</integer>
+ <key>CellTitle</key>
+ <string>HaHa</string>
+ <key>CellValue</key>
+ <string>HaHa</string>
+ </dict>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSTextField</string>
+ <key>ControlValue</key>
+ <string>HaHa</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 3</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlSelectedCell</key>
+ <dict>
+ <key>CellState</key>
+ <integer>0</integer>
+ <key>CellTag</key>
+ <integer>0</integer>
+ <key>CellTitle</key>
+ <string>Still Haven't Found What I'm Searching For</string>
+ <key>CellValue</key>
+ <string>Still Haven't Found What I'm Searching For</string>
+ </dict>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSSearchField</string>
+ <key>ControlValue</key>
+ <string>Still Haven't Found What I'm Searching For</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 4</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlSelectedCell</key>
+ <dict>
+ <key>CellState</key>
+ <integer>1</integer>
+ <key>CellTag</key>
+ <integer>0</integer>
+ <key>CellTitle</key>
+ <string>Once upon a time</string>
+ <key>CellValue</key>
+ <string>Once upon a time</string>
+ </dict>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSTextField</string>
+ <key>ControlValue</key>
+ <string>Once upon a time</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 5</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlSelectedCell</key>
+ <dict>
+ <key>CellState</key>
+ <integer>1</integer>
+ <key>CellTag</key>
+ <integer>0</integer>
+ <key>CellTitle</key>
+ <string>CheckMate!</string>
+ <key>CellValue</key>
+ <string>1</string>
+ </dict>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSButton</string>
+ <key>ControlValue</key>
+ <string>1</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 6</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlSelectedCell</key>
+ <dict>
+ <key>CellState</key>
+ <integer>1</integer>
+ <key>CellTag</key>
+ <integer>0</integer>
+ <key>CellTitle</key>
+ <string></string>
+ <key>CellValue</key>
+ <string>50</string>
+ </dict>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSSlider</string>
+ <key>ControlValue</key>
+ <string>50</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 7</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlSelectedCell</key>
+ <dict>
+ <key>CellState</key>
+ <integer>0</integer>
+ <key>CellTag</key>
+ <integer>0</integer>
+ <key>CellTitle</key>
+ <string>Cancel</string>
+ <key>CellValue</key>
+ <string>0</string>
+ </dict>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSButton</string>
+ <key>ControlValue</key>
+ <string>0</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 8</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSColorWell</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 9</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>WindowIsMain</key>
+ <false/>
+ <key>WindowIsVisible</key>
+ <true/>
+ <key>WindowTitle</key>
+ <string>Window</string>
+ </dict>
+</dict>
+</plist>
diff --git a/UnitTesting/GTMUnitTestingUtilities.h b/UnitTesting/GTMUnitTestingUtilities.h
new file mode 100644
index 0000000..17ec766
--- /dev/null
+++ b/UnitTesting/GTMUnitTestingUtilities.h
@@ -0,0 +1,42 @@
+//
+// GTMUnitTestingUtilities.h
+//
+// Copyright 2006-2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import <objc/objc.h>
+
+// Collection of utilities for unit testing
+@interface GTMUnitTestingUtilities : NSObject
+
+// Returns YES if we are currently being unittested.
++ (BOOL)areWeBeingUnitTested;
+
+// Sets up the user interface so that we can run consistent UI unittests on
+// it. This includes setting scroll bar types, setting selection colors
+// setting color spaces etc so that everything is consistent across machines.
+// This should be called in main, before NSApplicationMain is called.
++ (void)setUpForUIUnitTests;
+
+// Syntactic sugar combining the above, and wrapping them in an
+// NSAutoreleasePool so that your main can look like this:
+// int main(int argc, const char *argv[]) {
+// [UnitTestingUtilities setUpForUIUnitTestsIfBeingTested];
+// return NSApplicationMain(argc, argv);
+// }
++ (void)setUpForUIUnitTestsIfBeingTested;
+
+@end
+
diff --git a/UnitTesting/GTMUnitTestingUtilities.m b/UnitTesting/GTMUnitTestingUtilities.m
new file mode 100644
index 0000000..7c1b915
--- /dev/null
+++ b/UnitTesting/GTMUnitTestingUtilities.m
@@ -0,0 +1,172 @@
+//
+// GTMUnitTestingUtilities.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 "GTMUnitTestingUtilities.h"
+#import <AppKit/AppKit.h>
+#import "GTMDefines.h"
+
+// The Users profile before we change it on them
+static CMProfileRef gCurrentColorProfile = NULL;
+
+// Compares two color profiles
+static BOOL AreCMProfilesEqual(CMProfileRef a, CMProfileRef b);
+// Stores the user's color profile away, and changes over to generic.
+static void SetColorProfileToGenericRGB();
+// Restores the users profile.
+static void RestoreColorProfile(void);
+
+@implementation GTMUnitTestingUtilities
+
+// Returns YES if we are currently being unittested.
++ (BOOL)areWeBeingUnitTested {
+ BOOL answer = NO;
+
+ // Check to see if the SenTestProbe class is linked in before we call it.
+ Class SenTestProbeClass = NSClassFromString(@"SenTestProbe");
+ if (SenTestProbeClass != Nil) {
+ // Doing this little dance so we don't actually have to link
+ // SenTestingKit in
+ SEL selector = NSSelectorFromString(@"isTesting");
+ NSMethodSignature *sig = [SenTestProbeClass methodSignatureForSelector:selector];
+ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig];
+ [invocation setSelector:selector];
+ [invocation invokeWithTarget:SenTestProbeClass];
+ [invocation getReturnValue:&answer];
+ }
+ return answer;
+}
+
+// Sets up the user interface so that we can run consistent UI unittests on it.
++ (void)setUpForUIUnitTests {
+ // Give some names to undocumented defaults values
+ static const int MediumFontSmoothing = 2;
+ static const int BlueTintedAppearance = 1;
+
+ // This sets up some basic values that we want as our defaults for doing pixel
+ // based user interface tests. These defaults only apply to the unit test app,
+ // except or the color profile which will be set system wide, and then
+ // restored when the tests complete.
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+ // Scroll arrows together bottom
+ [defaults setObject:@"DoubleMax" forKey:@"AppleScrollBarVariant"];
+ // Smallest font size to CG should perform antialiasing on
+ [defaults setInteger:4 forKey:@"AppleAntiAliasingThreshold"];
+ // Type of smoothing
+ [defaults setInteger:MediumFontSmoothing forKey:@"AppleFontSmoothing"];
+ // Blue aqua
+ [defaults setInteger:BlueTintedAppearance forKey:@"AppleAquaColorVariant"];
+ // Standard highlight colors
+ [defaults setObject:@"0.709800 0.835300 1.000000"
+ forKey:@"AppleHighlightColor"];
+ [defaults setObject:@"0.500000 0.500000 0.500000"
+ forKey:@"AppleOtherHighlightColor"];
+ // Use english plz
+ [defaults setObject:[NSArray arrayWithObject:@"en"] forKey:@"AppleLanguages"];
+ // How fast should we draw sheets. This speeds up the sheet tests considerably
+ [defaults setFloat:.001 forKey:@"NSWindowResizeTime"];
+ // Switch over the screen profile to "generic rgb". This installs an
+ // atexit handler to return our profile back when we are done.
+ SetColorProfileToGenericRGB();
+}
+
++ (void)setUpForUIUnitTestsIfBeingTested {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ if ([self areWeBeingUnitTested]) {
+ [self setUpForUIUnitTests];
+ }
+ [pool release];
+}
+@end
+
+
+BOOL AreCMProfilesEqual(CMProfileRef a, CMProfileRef b) {
+ BOOL equal = YES;
+ if (a != b) {
+ CMProfileMD5 aMD5;
+ CMProfileMD5 bMD5;
+ CMError aMD5Err = CMGetProfileMD5(a, aMD5);
+ CMError bMD5Err = CMGetProfileMD5(b, bMD5);
+ equal = (!aMD5Err &&
+ !bMD5Err &&
+ !memcmp(aMD5, bMD5, sizeof(CMProfileMD5))) ? YES : NO;
+ }
+ return equal;
+}
+
+static void RestoreColorProfile(void) {
+ if (gCurrentColorProfile) {
+ CGDirectDisplayID displayID = CGMainDisplayID();
+ int error = CMSetProfileByAVID((UInt32)displayID, gCurrentColorProfile);
+ if (error) {
+ // COV_NF_START
+ // No way to force this case in a unittest.
+ _GTMDevLog(@"Failed to restore previous color profile! "
+ "You may need to open System Preferences : Displays : Color "
+ "and manually restore your color settings. (Error: %i)", error);
+ // COV_NF_END
+ } else {
+ _GTMDevLog(@"Color profile restored");
+ }
+ gCurrentColorProfile = NULL;
+ }
+}
+
+void SetColorProfileToGenericRGB() {
+ NSColorSpace *genericSpace = [NSColorSpace genericRGBColorSpace];
+ CMProfileRef genericProfile = (CMProfileRef)[genericSpace colorSyncProfile];
+ CMProfileRef previousProfile;
+ CGDirectDisplayID displayID = CGMainDisplayID();
+ CMError error = CMGetProfileByAVID((UInt32)displayID, &previousProfile);
+ if (error) {
+ // COV_NF_START
+ // No way to force this case in a unittest.
+ _GTMDevLog(@"Failed to get current color profile. "
+ "I will not be able to restore your current profile, thus I'm "
+ "not changing it. Many unit tests may fail as a result. (Error: %i)",
+ error);
+ return;
+ // COV_NF_END
+ }
+ if (AreCMProfilesEqual(genericProfile, previousProfile)) {
+ return;
+ }
+ CFStringRef previousProfileName;
+ CFStringRef genericProfileName;
+ CMCopyProfileDescriptionString(previousProfile, &previousProfileName);
+ CMCopyProfileDescriptionString(genericProfile, &genericProfileName);
+
+ _GTMDevLog(@"Temporarily changing your system color profile from \"%@\" to \"%@\".",
+ previousProfileName, genericProfileName);
+ _GTMDevLog(@"This allows the pixel-based unit-tests to have consistent color "
+ "values across all machines.");
+ _GTMDevLog(@"The colors on your screen will change for the duration of the testing.");
+
+
+ if ((error = CMSetProfileByAVID((UInt32)displayID, genericProfile))) {
+ // COV_NF_START
+ // No way to force this case in a unittest.
+ _GTMDevLog(@"Failed to set color profile to \"%@\"! Many unit tests will fail as "
+ "a result. (Error: %i)", genericProfileName, error);
+ // COV_NF_END
+ } else {
+ gCurrentColorProfile = previousProfile;
+ atexit(RestoreColorProfile);
+ }
+ CFRelease(previousProfileName);
+ CFRelease(genericProfileName);
+}
diff --git a/UnitTesting/GTMUnitTestingView.tiff b/UnitTesting/GTMUnitTestingView.tiff
new file mode 100644
index 0000000..228df73
--- /dev/null
+++ b/UnitTesting/GTMUnitTestingView.tiff
Binary files differ
diff --git a/UnitTesting/GTMUnitTestingWindow.gtmUTState b/UnitTesting/GTMUnitTestingWindow.gtmUTState
new file mode 100644
index 0000000..27dd08e
--- /dev/null
+++ b/UnitTesting/GTMUnitTestingWindow.gtmUTState
@@ -0,0 +1,291 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>$GTMArchive</key>
+ <string>GTMUnitTestingArchive</string>
+ <key>$GTMVersion</key>
+ <integer>1</integer>
+ <key>WindowContent</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ <key>ViewSubView 0</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ <key>ViewSubView 0</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ <key>ViewSubView 0</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSTableView</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>ViewSubView 1</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <false/>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSScroller</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 2</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <false/>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSScroller</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 3</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ <key>ViewSubView 0</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>ViewSubView 4</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>ViewSubView 1</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 10</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ <key>ViewSubView 0</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ <key>ViewSubView 0</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>ViewSubView 1</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <false/>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSScroller</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 2</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <false/>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSScroller</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>ViewSubView 11</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ <key>ViewSubView 0</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>ViewSubView 2</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlSelectedCell</key>
+ <dict>
+ <key>CellState</key>
+ <integer>0</integer>
+ <key>CellTag</key>
+ <integer>0</integer>
+ <key>CellTitle</key>
+ <string>HaHa</string>
+ <key>CellValue</key>
+ <string>HaHa</string>
+ </dict>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSTextField</string>
+ <key>ControlValue</key>
+ <string>HaHa</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 3</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlSelectedCell</key>
+ <dict>
+ <key>CellState</key>
+ <integer>0</integer>
+ <key>CellTag</key>
+ <integer>0</integer>
+ <key>CellTitle</key>
+ <string>Still Haven't Found What I'm Searching For</string>
+ <key>CellValue</key>
+ <string>Still Haven't Found What I'm Searching For</string>
+ </dict>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSSearchField</string>
+ <key>ControlValue</key>
+ <string>Still Haven't Found What I'm Searching For</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 4</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlSelectedCell</key>
+ <dict>
+ <key>CellState</key>
+ <integer>1</integer>
+ <key>CellTag</key>
+ <integer>0</integer>
+ <key>CellTitle</key>
+ <string>Once upon a time</string>
+ <key>CellValue</key>
+ <string>Once upon a time</string>
+ </dict>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSTextField</string>
+ <key>ControlValue</key>
+ <string>Once upon a time</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 5</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlSelectedCell</key>
+ <dict>
+ <key>CellState</key>
+ <integer>1</integer>
+ <key>CellTag</key>
+ <integer>0</integer>
+ <key>CellTitle</key>
+ <string>CheckMate!</string>
+ <key>CellValue</key>
+ <string>1</string>
+ </dict>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSButton</string>
+ <key>ControlValue</key>
+ <string>1</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 6</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlSelectedCell</key>
+ <dict>
+ <key>CellState</key>
+ <integer>1</integer>
+ <key>CellTag</key>
+ <integer>0</integer>
+ <key>CellTitle</key>
+ <string></string>
+ <key>CellValue</key>
+ <string>50</string>
+ </dict>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSSlider</string>
+ <key>ControlValue</key>
+ <string>50</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 7</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlSelectedCell</key>
+ <dict>
+ <key>CellState</key>
+ <integer>0</integer>
+ <key>CellTag</key>
+ <integer>0</integer>
+ <key>CellTitle</key>
+ <string>Cancel</string>
+ <key>CellValue</key>
+ <string>0</string>
+ </dict>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSButton</string>
+ <key>ControlValue</key>
+ <string>0</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 8</key>
+ <dict>
+ <key>ControlIsEnabled</key>
+ <true/>
+ <key>ControlTag</key>
+ <integer>0</integer>
+ <key>ControlType</key>
+ <string>NSColorWell</string>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ <key>ViewSubView 9</key>
+ <dict>
+ <key>ViewIsHidden</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>WindowIsMain</key>
+ <false/>
+ <key>WindowIsVisible</key>
+ <true/>
+ <key>WindowTitle</key>
+ <string>Window</string>
+</dict>
+</plist>
diff --git a/UnitTesting/GTMUnitTestingWindow.tiff b/UnitTesting/GTMUnitTestingWindow.tiff
new file mode 100644
index 0000000..63f5649
--- /dev/null
+++ b/UnitTesting/GTMUnitTestingWindow.tiff
Binary files differ
diff --git a/UnitTesting/RunIPhoneUnitTest.sh b/UnitTesting/RunIPhoneUnitTest.sh
new file mode 100755
index 0000000..50709d3
--- /dev/null
+++ b/UnitTesting/RunIPhoneUnitTest.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+# RunIPhoneUnitTest.sh
+# 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.
+#
+# Runs all unittests through the iPhone simulator
+
+export DYLD_ROOT_PATH="$SDKROOT"
+export DYLD_FRAMEWORK_PATH="$CONFIGURATION_BUILD_DIR"
+export IPHONE_SIMULATOR_ROOT="$SDKROOT"
+export CFFIXED_USER_HOME="$USER_LIBRARY_DIR/Application Support/iPhone Simulator/User"
+"$TARGET_BUILD_DIR/$EXECUTABLE_PATH" -RegisterForSystemEvents
+exit 0
diff --git a/UnitTesting/RunMacOSUnitTests.sh b/UnitTesting/RunMacOSUnitTests.sh
new file mode 100755
index 0000000..2c8440a
--- /dev/null
+++ b/UnitTesting/RunMacOSUnitTests.sh
@@ -0,0 +1,41 @@
+#
+# RunUnitTests.sh
+# 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.
+#
+# Run the unit tests in this test bundle.
+# Set up some env variables to make things as likely to crash as possible.
+# See http://developer.apple.com/technotes/tn2004/tn2124.html for details.
+#
+
+export MallocScribble=YES
+export MallocPreScribble=YES
+export MallocGuardEdges=YES
+# CFZombieLevel disabled because it doesn't play well with the
+# security framework
+# export CFZombieLevel=3
+export NSAutoreleaseFreedObjectCheckEnabled=YES
+export NSZombieEnabled=YES
+export OBJC_DEBUG_FRAGILE_SUPERCLASSES=YES
+
+# If we have debug libraries on the machine, we'll use them
+# unless a target has specifically turned them off
+if [ ! $GTM_NO_DEBUG_FRAMEWORKS ]; then
+ if [ -f "/System/Library/Frameworks/CoreFoundation.framework/Versions/Current/CoreFoundation_debug" ]; then
+ echo ---- Using _debug frameworks ----
+ export DYLD_IMAGE_SUFFIX=_debug
+ fi
+fi
+
+"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests"
diff --git a/XcodeConfig/Project/DebugLeopardOrLater.xcconfig b/XcodeConfig/Project/DebugLeopardOrLater.xcconfig
new file mode 100644
index 0000000..7bc3257
--- /dev/null
+++ b/XcodeConfig/Project/DebugLeopardOrLater.xcconfig
@@ -0,0 +1,34 @@
+//
+// DebugLeopardOrLater.xcconfig
+//
+// Xcode configuration file for building a Debug target on Leopard or later.
+//
+// This is a _Configuration_ Xcode config file for use in the "Based on" popup
+// of the project configuration editor. Do _not_ use this as the config base
+// and individual Xcode target, there are other configuration files for that
+// purpose.
+//
+// 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.
+//
+
+// Pull in our general Google settings
+#include "../subconfig/General.xcconfig"
+
+// Leopard or later
+#include "../subconfig/LeopardOrLater.xcconfig"
+
+// Debug settings
+#include "../subconfig/Debug.xcconfig"
+
diff --git a/XcodeConfig/DebugTigerOrLater.xcconfig b/XcodeConfig/Project/DebugTigerOrLater.xcconfig
index 7a78a13..76d2e27 100644
--- a/XcodeConfig/DebugTigerOrLater.xcconfig
+++ b/XcodeConfig/Project/DebugTigerOrLater.xcconfig
@@ -24,11 +24,11 @@
//
// Pull in our general Google settings
-#include "subconfig/General.xcconfig"
+#include "../subconfig/General.xcconfig"
// Tiger or later
-#include "subconfig/TigerOrLater.xcconfig"
+#include "../subconfig/TigerOrLater.xcconfig"
// Debug settings
-#include "subconfig/Debug.xcconfig"
+#include "../subconfig/Debug.xcconfig"
diff --git a/XcodeConfig/Project/DebugiPhone.xcconfig b/XcodeConfig/Project/DebugiPhone.xcconfig
new file mode 100644
index 0000000..45c5ba5
--- /dev/null
+++ b/XcodeConfig/Project/DebugiPhone.xcconfig
@@ -0,0 +1,33 @@
+//
+// DebugiPhoneSimulator.xcconfig
+//
+// Xcode configuration file for building a Debug target on iPhone
+//
+// 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.
+
+//
+// This is a _Configuration_ Xcode config file for use in the "Based on" popup
+// of the project configuration editor. Do _not_ use this as the config base
+// and individual Xcode target, there are other configuration files for that
+// purpose.
+
+// Pull in our general Google settings
+#include "../subconfig/General.xcconfig"
+
+// iPhone settings
+#include "../subconfig/iPhone.xcconfig"
+
+// Release settings
+#include "../subconfig/Debug.xcconfig"
diff --git a/XcodeConfig/Project/ReleaseLeopardOrLater.xcconfig b/XcodeConfig/Project/ReleaseLeopardOrLater.xcconfig
new file mode 100644
index 0000000..52f0383
--- /dev/null
+++ b/XcodeConfig/Project/ReleaseLeopardOrLater.xcconfig
@@ -0,0 +1,33 @@
+//
+// ReleaseLeopardOrLater.xcconfig
+//
+// Xcode configuration file for building a Release target on Leopard or later.
+//
+// This is a _Configuration_ Xcode config file for use in the "Based on" popup
+// of the project configuration editor. Do _not_ use this as the config base
+// and individual Xcode target, there are other configuration files for that
+// purpose.
+//
+// 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.
+//
+
+// Pull in our general Google settings
+#include "../subconfig/General.xcconfig"
+
+// Leopard or later
+#include "../subconfig/LeopardOrLater.xcconfig"
+
+// Release settings
+#include "../subconfig/Release.xcconfig"
diff --git a/XcodeConfig/ReleaseTigerOrLater.xcconfig b/XcodeConfig/Project/ReleaseTigerOrLater.xcconfig
index ee30238..d14e739 100644
--- a/XcodeConfig/ReleaseTigerOrLater.xcconfig
+++ b/XcodeConfig/Project/ReleaseTigerOrLater.xcconfig
@@ -24,10 +24,10 @@
//
// Pull in our general Google settings
-#include "subconfig/General.xcconfig"
+#include "../subconfig/General.xcconfig"
// Tiger or later
-#include "subconfig/TigerOrLater.xcconfig"
+#include "../subconfig/TigerOrLater.xcconfig"
// Release settings
-#include "subconfig/Release.xcconfig"
+#include "../subconfig/Release.xcconfig"
diff --git a/XcodeConfig/Project/ReleaseiPhone.xcconfig b/XcodeConfig/Project/ReleaseiPhone.xcconfig
new file mode 100644
index 0000000..e61d1b6
--- /dev/null
+++ b/XcodeConfig/Project/ReleaseiPhone.xcconfig
@@ -0,0 +1,33 @@
+//
+// ReleaseAspenSimulator.xcconfig
+//
+// Xcode configuration file for building a Release target on iPhone
+//
+// 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.
+
+//
+// This is a _Configuration_ Xcode config file for use in the "Based on" popup
+// of the project configuration editor. Do _not_ use this as the config base
+// and individual Xcode target, there are other configuration files for that
+// purpose.
+
+// Pull in our general Google settings
+#include "../subconfig/General.xcconfig"
+
+// iPhone Settings.
+#include "../subconfig/iPhone.xcconfig"
+
+// Release settings
+#include "../subconfig/Release.xcconfig"
diff --git a/XcodeConfig/DebugUnittest.xcconfig b/XcodeConfig/Target/DebugUnittest.xcconfig
index c32c63d..3f4d342 100644
--- a/XcodeConfig/DebugUnittest.xcconfig
+++ b/XcodeConfig/Target/DebugUnittest.xcconfig
@@ -23,7 +23,7 @@
//
// Unittests are loadable bundles
-#include "subconfig/Unittest.xcconfig"
+#include "../subconfig/Unittest.xcconfig"
// See comments in ReleaseUnittest.xcconfig regarding BUNDLE_LOADER and
// how it should be set if you are running into link errors. \ No newline at end of file
diff --git a/XcodeConfig/LoadableBundle.xcconfig b/XcodeConfig/Target/LoadableBundle.xcconfig
index 78ed39a..78ed39a 100644
--- a/XcodeConfig/LoadableBundle.xcconfig
+++ b/XcodeConfig/Target/LoadableBundle.xcconfig
diff --git a/XcodeConfig/ReleaseUnittest.xcconfig b/XcodeConfig/Target/ReleaseUnittest.xcconfig
index 9ec14de..f9a85d3 100644
--- a/XcodeConfig/ReleaseUnittest.xcconfig
+++ b/XcodeConfig/Target/ReleaseUnittest.xcconfig
@@ -23,7 +23,7 @@
//
// Unittests are loadable bundles
-#include "subconfig/Unittest.xcconfig"
+#include "../subconfig/Unittest.xcconfig"
// When running OCUnit tests in Release mode the unittests BUNDLE_LOADER
// is probably stripped (or at least it will be if they are using our
diff --git a/XcodeConfig/SharedLibrary.xcconfig b/XcodeConfig/Target/SharedLibrary.xcconfig
index 7593392..7593392 100644
--- a/XcodeConfig/SharedLibrary.xcconfig
+++ b/XcodeConfig/Target/SharedLibrary.xcconfig
diff --git a/XcodeConfig/StaticLibrary.xcconfig b/XcodeConfig/Target/StaticLibrary.xcconfig
index f459e83..f459e83 100644
--- a/XcodeConfig/StaticLibrary.xcconfig
+++ b/XcodeConfig/Target/StaticLibrary.xcconfig
diff --git a/XcodeConfig/subconfig/General.xcconfig b/XcodeConfig/subconfig/General.xcconfig
index b9f7313..b6a9a02 100644
--- a/XcodeConfig/subconfig/General.xcconfig
+++ b/XcodeConfig/subconfig/General.xcconfig
@@ -2,7 +2,7 @@
// General.xcconfig
//
// Xcode configuration file for general build settings applicable to all
-// Google projects and targets.
+// projects and targets.
//
// Copyright 2006-2008 Google Inc.
//
@@ -20,7 +20,7 @@
//
// Build for PPC and Intel
-ARCHS = ppc i386
+ARCHS = i386 ppc
// Zerolink prevents link warnings so turn it off
ZERO_LINK = NO
diff --git a/XcodeConfig/subconfig/LeopardOrLater.xcconfig b/XcodeConfig/subconfig/LeopardOrLater.xcconfig
new file mode 100644
index 0000000..b2828d7
--- /dev/null
+++ b/XcodeConfig/subconfig/LeopardOrLater.xcconfig
@@ -0,0 +1,28 @@
+//
+// LeopardOrLater.xcconfig
+//
+// Xcode configuration file for projects targeting 10.4 Tiger or later. These
+// settings produce a Universal binary compatible with 10.4 for PPC and Intel.
+//
+// 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.
+//
+
+// Default SDK and minimum OS version is 10.5
+SDKROOT = ${DEVELOPER_SDK_DIR}/MacOSX10.5.sdk
+MACOSX_DEPLOYMENT_TARGET = 10.5
+GCC_VERSION = 4.0
+
+// On Leopard use Obj-C fast dispatch
+GCC_FAST_OBJC_DISPATCH = YES
diff --git a/XcodeConfig/subconfig/TigerOrLater.xcconfig b/XcodeConfig/subconfig/TigerOrLater.xcconfig
index d46e1a2..d8f7a3d 100644
--- a/XcodeConfig/subconfig/TigerOrLater.xcconfig
+++ b/XcodeConfig/subconfig/TigerOrLater.xcconfig
@@ -1,9 +1,8 @@
//
// TigerOrLater.xcconfig
//
-// Xcode configuration file for Google applications targeting 10.4 Tiger or
-// later. These settings produce a Universal binary compatible with 10.4 for
-// PPC and Intel.
+// Xcode configuration file for projects targeting 10.4 Tiger or later. These
+// settings produce a Universal binary compatible with 10.4 for PPC and Intel.
//
// Copyright 2006-2008 Google Inc.
//
diff --git a/XcodeConfig/subconfig/Unittest.xcconfig b/XcodeConfig/subconfig/Unittest.xcconfig
index a35dc61..96121ee 100644
--- a/XcodeConfig/subconfig/Unittest.xcconfig
+++ b/XcodeConfig/subconfig/Unittest.xcconfig
@@ -1,11 +1,8 @@
//
// Unittest.xcconfig
//
-// Xcode configuration file for a unittest target.
-//
-// This is a _Target_ config file, for use in the "Based on" popup of the
-// settings dialog for a target. Do not attempt to apply this as the base
-// of an Xcode configuration in the project settings dialog.
+// Xcode configuration file for a basic unittest targets. Use the debug or
+// release build specific configs in the Target folder.
//
// Copyright 2006-2008 Google Inc.
//
@@ -23,7 +20,7 @@
//
// Unittests are loadable bundles
-#include "../LoadableBundle.xcconfig"
+#include "../Target/LoadableBundle.xcconfig"
// Force C99 dialect with GNU extensions (needed for OCUnit)
GCC_C_LANGUAGE_STANDARD = gnu99
@@ -31,3 +28,6 @@ GCC_C_LANGUAGE_STANDARD = gnu99
// Deploment postprocessing is what triggers Xcode to strip, we don't strip
// unittests
DEPLOYMENT_POSTPROCESSING = NO
+
+// Most common unittests will be objective-c
+WRAPPER_EXTENSION = octest
diff --git a/XcodeConfig/subconfig/iPhone.xcconfig b/XcodeConfig/subconfig/iPhone.xcconfig
new file mode 100644
index 0000000..42445f1
--- /dev/null
+++ b/XcodeConfig/subconfig/iPhone.xcconfig
@@ -0,0 +1,37 @@
+//
+// iPhone.xcconfig
+//
+// Xcode configuration file for building a Debug target on iPhone
+//
+// 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.
+
+// Build for ARM
+ARCHS = armv6
+
+// Build only the active architecture
+ONLY_ACTIVE_ARCH = YES
+
+// Code signing. Should be overridden if releasing
+CODE_SIGN_IDENTITY[sdk=iphoneos*] = iPhone Developer
+
+// Default SDK and minimum OS version is the iphone SDK.
+SDKROOT = iphoneos2.0
+
+MACOSX_DEPLOYMENT_TARGET = 10.5
+GCC_VERSION = 4.0
+
+// On iPhone use Obj-C fast dispatch
+GCC_FAST_OBJC_DISPATCH = YES
+
diff --git a/XcodeConfig/xcconfigs-readme.txt b/XcodeConfig/xcconfigs-readme.txt
new file mode 100644
index 0000000..23fde4b
--- /dev/null
+++ b/XcodeConfig/xcconfigs-readme.txt
@@ -0,0 +1,12 @@
+
+Configs w/in Project folder are configs expected to be set at the project level.
+
+Configs w/in the Target folder are configs expected to be set on the per target
+level.
+
+Configs w/in subconfig are not meant to be used directly; they are referenced by
+the other configs.
+
+
+Remember: when using the configs at any given layer, make sure you set them for
+each build configuration.