From cdf070c8d76ffc4eaa24e8671756cbbe9ceb2890 Mon Sep 17 00:00:00 2001 From: thomasvl Date: Mon, 14 Apr 2008 17:21:02 +0000 Subject: 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) --- AppKit/GTMGeometryUtils.h | 560 ------ AppKit/GTMGeometryUtils.m | 152 -- AppKit/GTMGeometryUtilsTest.m | 323 ---- AppKit/GTMLinearRGBShading.m | 2 +- AppKit/GTMNSBezierPath+CGPath.m | 3 +- AppKit/GTMNSBezierPath+CGPathTest.m | 21 +- AppKit/GTMNSBezierPath+CGPathTest.tif | Bin 1090 -> 0 bytes AppKit/GTMNSBezierPath+CGPathTest.tiff | Bin 0 -> 2558 bytes AppKit/GTMNSBezierPath+RoundRectTest.m | 25 +- AppKit/GTMNSBezierPath+RoundRectTest.tif | Bin 6982 -> 0 bytes AppKit/GTMNSBezierPath+RoundRectTest.tiff | Bin 0 -> 8036 bytes AppKit/GTMNSBezierPath+ShadingTest.10.4.tif | Bin 12964 -> 0 bytes AppKit/GTMNSBezierPath+ShadingTest.10.5.tif | Bin 17982 -> 0 bytes AppKit/GTMNSBezierPath+ShadingTest.10.5.tiff | Bin 0 -> 19450 bytes AppKit/GTMNSBezierPath+ShadingTest.m | 4 +- AppKit/GTMNSColor+Theme.m | 9 +- AppKit/GTMNSColor+ThemeTest.m | 21 +- AppKit/GTMNSWorkspace+ScreenSaver.m | 17 +- AppKit/GTMNSWorkspace+Theme.m | 2 +- DebugUtils/GTMDebugSelectorValidation.h | 86 + DebugUtils/GTMMethodCheck.h | 88 + DebugUtils/GTMMethodCheck.m | 144 ++ DebugUtils/GTMMethodCheckTest.m | 69 + Foundation/GTMCalculatedRange.h | 2 +- Foundation/GTMCalculatedRangeTest.m | 1 - Foundation/GTMGeometryUtils.h | 377 ++++ Foundation/GTMGeometryUtils.m | 104 ++ Foundation/GTMGeometryUtilsTest.m | 181 ++ Foundation/GTMNSData+zlib.m | 82 +- Foundation/GTMNSData+zlibTest.m | 118 +- Foundation/GTMNSEnumerator+Filter.m | 54 +- Foundation/GTMNSEnumerator+FilterTest.m | 101 +- Foundation/GTMNSFileManager+Path.h | 3 +- Foundation/GTMNSFileManager+PathTest.m | 211 ++- Foundation/GTMNSString+HTML.m | 65 +- Foundation/GTMNSString+HTMLTest.m | 1 - Foundation/GTMNSString+Utilities.h | 41 + Foundation/GTMNSString+Utilities.m | 48 + Foundation/GTMNSString+UtilitiesTest.m | 62 + Foundation/GTMNSString+XML.m | 51 +- Foundation/GTMNSString+XMLTest.m | 42 +- Foundation/GTMObjC2Runtime.h | 54 + Foundation/GTMObjC2Runtime.m | 148 ++ Foundation/GTMObjC2RuntimeTest.m | 385 ++++ Foundation/GTMObjectSingleton.h | 6 +- Foundation/GTMRegex.h | 41 + Foundation/GTMRegex.m | 132 +- Foundation/GTMRegexTest.m | 185 +- Foundation/GTMScriptRunner.m | 20 +- Foundation/GTMScriptRunnerTest.m | 163 +- Foundation/GTMSystemVersion.h | 23 +- Foundation/GTMSystemVersion.m | 107 +- Foundation/GTMSystemVersionTest.m | 32 +- GTM.xcodeproj/project.pbxproj | 588 +++++- GTMDefines.h | 91 + GTMiPhone-Info.plist | 26 + GTMiPhone.xcodeproj/project.pbxproj | 436 +++++ GTMiPhone_Prefix.pch | 20 + ReleaseNotes.txt | 62 +- TigerGcov/libgcov.a | Bin 0 -> 35048 bytes TigerGcov/libgcov_readme.html | 11 + UnitTesting/GTMAppKit+UnitTesting.h | 145 ++ UnitTesting/GTMAppKit+UnitTesting.m | 304 +++ UnitTesting/GTMCALayer+UnitTesting.h | 46 + UnitTesting/GTMCALayer+UnitTesting.m | 89 + UnitTesting/GTMIPhoneUnitTestMain.m | 181 ++ UnitTesting/GTMNSObject+BindingUnitTesting.h | 103 + UnitTesting/GTMNSObject+BindingUnitTesting.m | 440 +++++ UnitTesting/GTMNSObject+UnitTesting.h | 513 ++--- UnitTesting/GTMNSObject+UnitTesting.m | 886 +++++---- UnitTesting/GTMNSView+UnitTesting.h | 138 -- UnitTesting/GTMNSView+UnitTesting.m | 135 -- UnitTesting/GTMSenTestCase.h | 803 ++++++-- UnitTesting/GTMSenTestCase.m | 140 ++ UnitTesting/GTMUIKit+UnitTesting.h | 128 ++ UnitTesting/GTMUIKit+UnitTesting.m | 118 ++ UnitTesting/GTMUIKit+UnitTestingTest.m | 58 + .../English.lproj/MainMenu.nib/designable.nib | 1973 ++++++++++++++++++++ .../English.lproj/MainMenu.nib/keyedobjects.nib | Bin 0 -> 16183 bytes UnitTesting/GTMUIUnitTestingHarness/Info.plist | 28 + UnitTesting/GTMUIUnitTestingHarness/main.m | 27 + UnitTesting/GTMUIViewUnitTestingTest.gtmUTState | 33 + UnitTesting/GTMUIViewUnitTestingTest.png | Bin 0 -> 3214 bytes UnitTesting/GTMUnitTestingBindingTest.m | 117 ++ UnitTesting/GTMUnitTestingImage.gtmUTState | 30 + UnitTesting/GTMUnitTestingImage.tiff | Bin 0 -> 67190 bytes UnitTesting/GTMUnitTestingTest.h | 29 + UnitTesting/GTMUnitTestingTest.m | 265 +++ UnitTesting/GTMUnitTestingTest.nib/classes.nib | 86 + UnitTesting/GTMUnitTestingTest.nib/info.nib | 20 + .../GTMUnitTestingTest.nib/keyedobjects.nib | Bin 0 -> 15667 bytes UnitTesting/GTMUnitTestingTestApp.gtmUTState | 1879 +++++++++++++++++++ UnitTesting/GTMUnitTestingUtilities.h | 42 + UnitTesting/GTMUnitTestingUtilities.m | 172 ++ UnitTesting/GTMUnitTestingView.tiff | Bin 0 -> 161670 bytes UnitTesting/GTMUnitTestingWindow.gtmUTState | 291 +++ UnitTesting/GTMUnitTestingWindow.tiff | Bin 0 -> 21226 bytes UnitTesting/RunIPhoneUnitTest.sh | 24 + UnitTesting/RunMacOSUnitTests.sh | 41 + XcodeConfig/DebugTigerOrLater.xcconfig | 34 - XcodeConfig/DebugUnittest.xcconfig | 29 - XcodeConfig/LoadableBundle.xcconfig | 30 - XcodeConfig/Project/DebugLeopardOrLater.xcconfig | 34 + XcodeConfig/Project/DebugTigerOrLater.xcconfig | 34 + XcodeConfig/Project/DebugiPhone.xcconfig | 33 + XcodeConfig/Project/ReleaseLeopardOrLater.xcconfig | 33 + XcodeConfig/Project/ReleaseTigerOrLater.xcconfig | 33 + XcodeConfig/Project/ReleaseiPhone.xcconfig | 33 + XcodeConfig/ReleaseTigerOrLater.xcconfig | 33 - XcodeConfig/ReleaseUnittest.xcconfig | 37 - XcodeConfig/SharedLibrary.xcconfig | 29 - XcodeConfig/StaticLibrary.xcconfig | 30 - XcodeConfig/Target/DebugUnittest.xcconfig | 29 + XcodeConfig/Target/LoadableBundle.xcconfig | 30 + XcodeConfig/Target/ReleaseUnittest.xcconfig | 37 + XcodeConfig/Target/SharedLibrary.xcconfig | 29 + XcodeConfig/Target/StaticLibrary.xcconfig | 30 + XcodeConfig/subconfig/General.xcconfig | 4 +- XcodeConfig/subconfig/LeopardOrLater.xcconfig | 28 + XcodeConfig/subconfig/TigerOrLater.xcconfig | 5 +- XcodeConfig/subconfig/Unittest.xcconfig | 12 +- XcodeConfig/subconfig/iPhone.xcconfig | 37 + XcodeConfig/xcconfigs-readme.txt | 12 + 123 files changed, 12840 insertions(+), 2719 deletions(-) delete mode 100644 AppKit/GTMGeometryUtils.h delete mode 100644 AppKit/GTMGeometryUtils.m delete mode 100644 AppKit/GTMGeometryUtilsTest.m delete mode 100644 AppKit/GTMNSBezierPath+CGPathTest.tif create mode 100644 AppKit/GTMNSBezierPath+CGPathTest.tiff delete mode 100644 AppKit/GTMNSBezierPath+RoundRectTest.tif create mode 100644 AppKit/GTMNSBezierPath+RoundRectTest.tiff delete mode 100644 AppKit/GTMNSBezierPath+ShadingTest.10.4.tif delete mode 100644 AppKit/GTMNSBezierPath+ShadingTest.10.5.tif create mode 100644 AppKit/GTMNSBezierPath+ShadingTest.10.5.tiff create mode 100644 DebugUtils/GTMDebugSelectorValidation.h create mode 100644 DebugUtils/GTMMethodCheck.h create mode 100644 DebugUtils/GTMMethodCheck.m create mode 100644 DebugUtils/GTMMethodCheckTest.m create mode 100644 Foundation/GTMGeometryUtils.h create mode 100644 Foundation/GTMGeometryUtils.m create mode 100644 Foundation/GTMGeometryUtilsTest.m create mode 100644 Foundation/GTMNSString+Utilities.h create mode 100644 Foundation/GTMNSString+Utilities.m create mode 100644 Foundation/GTMNSString+UtilitiesTest.m create mode 100644 Foundation/GTMObjC2Runtime.h create mode 100644 Foundation/GTMObjC2Runtime.m create mode 100644 Foundation/GTMObjC2RuntimeTest.m create mode 100644 GTMDefines.h create mode 100644 GTMiPhone-Info.plist create mode 100755 GTMiPhone.xcodeproj/project.pbxproj create mode 100644 GTMiPhone_Prefix.pch create mode 100644 TigerGcov/libgcov.a create mode 100644 TigerGcov/libgcov_readme.html create mode 100755 UnitTesting/GTMAppKit+UnitTesting.h create mode 100755 UnitTesting/GTMAppKit+UnitTesting.m create mode 100644 UnitTesting/GTMCALayer+UnitTesting.h create mode 100644 UnitTesting/GTMCALayer+UnitTesting.m create mode 100755 UnitTesting/GTMIPhoneUnitTestMain.m create mode 100644 UnitTesting/GTMNSObject+BindingUnitTesting.h create mode 100644 UnitTesting/GTMNSObject+BindingUnitTesting.m delete mode 100644 UnitTesting/GTMNSView+UnitTesting.h delete mode 100644 UnitTesting/GTMNSView+UnitTesting.m create mode 100644 UnitTesting/GTMSenTestCase.m create mode 100644 UnitTesting/GTMUIKit+UnitTesting.h create mode 100644 UnitTesting/GTMUIKit+UnitTesting.m create mode 100644 UnitTesting/GTMUIKit+UnitTestingTest.m create mode 100644 UnitTesting/GTMUIUnitTestingHarness/English.lproj/MainMenu.nib/designable.nib create mode 100644 UnitTesting/GTMUIUnitTestingHarness/English.lproj/MainMenu.nib/keyedobjects.nib create mode 100644 UnitTesting/GTMUIUnitTestingHarness/Info.plist create mode 100644 UnitTesting/GTMUIUnitTestingHarness/main.m create mode 100644 UnitTesting/GTMUIViewUnitTestingTest.gtmUTState create mode 100644 UnitTesting/GTMUIViewUnitTestingTest.png create mode 100644 UnitTesting/GTMUnitTestingBindingTest.m create mode 100644 UnitTesting/GTMUnitTestingImage.gtmUTState create mode 100644 UnitTesting/GTMUnitTestingImage.tiff create mode 100644 UnitTesting/GTMUnitTestingTest.h create mode 100644 UnitTesting/GTMUnitTestingTest.m create mode 100644 UnitTesting/GTMUnitTestingTest.nib/classes.nib create mode 100644 UnitTesting/GTMUnitTestingTest.nib/info.nib create mode 100644 UnitTesting/GTMUnitTestingTest.nib/keyedobjects.nib create mode 100644 UnitTesting/GTMUnitTestingTestApp.gtmUTState create mode 100644 UnitTesting/GTMUnitTestingUtilities.h create mode 100644 UnitTesting/GTMUnitTestingUtilities.m create mode 100644 UnitTesting/GTMUnitTestingView.tiff create mode 100644 UnitTesting/GTMUnitTestingWindow.gtmUTState create mode 100644 UnitTesting/GTMUnitTestingWindow.tiff create mode 100755 UnitTesting/RunIPhoneUnitTest.sh create mode 100755 UnitTesting/RunMacOSUnitTests.sh delete mode 100644 XcodeConfig/DebugTigerOrLater.xcconfig delete mode 100644 XcodeConfig/DebugUnittest.xcconfig delete mode 100644 XcodeConfig/LoadableBundle.xcconfig create mode 100644 XcodeConfig/Project/DebugLeopardOrLater.xcconfig create mode 100644 XcodeConfig/Project/DebugTigerOrLater.xcconfig create mode 100644 XcodeConfig/Project/DebugiPhone.xcconfig create mode 100644 XcodeConfig/Project/ReleaseLeopardOrLater.xcconfig create mode 100644 XcodeConfig/Project/ReleaseTigerOrLater.xcconfig create mode 100644 XcodeConfig/Project/ReleaseiPhone.xcconfig delete mode 100644 XcodeConfig/ReleaseTigerOrLater.xcconfig delete mode 100644 XcodeConfig/ReleaseUnittest.xcconfig delete mode 100644 XcodeConfig/SharedLibrary.xcconfig delete mode 100644 XcodeConfig/StaticLibrary.xcconfig create mode 100644 XcodeConfig/Target/DebugUnittest.xcconfig create mode 100644 XcodeConfig/Target/LoadableBundle.xcconfig create mode 100644 XcodeConfig/Target/ReleaseUnittest.xcconfig create mode 100644 XcodeConfig/Target/SharedLibrary.xcconfig create mode 100644 XcodeConfig/Target/StaticLibrary.xcconfig create mode 100644 XcodeConfig/subconfig/LeopardOrLater.xcconfig create mode 100644 XcodeConfig/subconfig/iPhone.xcconfig create mode 100644 XcodeConfig/xcconfigs-readme.txt diff --git a/AppKit/GTMGeometryUtils.h b/AppKit/GTMGeometryUtils.h deleted file mode 100644 index 1dd274f..0000000 --- a/AppKit/GTMGeometryUtils.h +++ /dev/null @@ -1,560 +0,0 @@ -// -// GTMGeometryUtils.h -// -// Utilities for geometrical utilities such as conversions -// between different types. -// -// Copyright 2006-2008 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// - -#include -#include - - -#pragma mark Miscellaneous - -/// Calculate the distance between two points. -// -// Args: -// pt1 first point -// pt2 second point -// -// Returns: -// Distance -CG_INLINE float GTMDistanceBetweenPoints(NSPoint pt1, NSPoint pt2) { - float dX = pt1.x - pt2.x; - float dY = pt1.y - pt2.y; - return sqrtf(dX * dX + dY * dY); -} - -/// 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; -/// NSPoints are relative to 0,0 in lower left -// -// Args: -// inPoint: CGPoint to convert -// -// Returns: -// Converted NSPoint -CG_INLINE NSPoint GTMCGPointToNSPoint(CGPoint inPoint) { - return NSMakePoint(inPoint.x, inPoint.y); -} - -/// Quickly convert from a NSPoint to a CGPoint. -// -/// CGPoints are relative to 0,0 in lower left; -/// NSPoints are relative to 0,0 in lower left -// -// Args: -// inPoint: NSPoint to convert -// -// Returns: -// Converted CGPoint -CG_INLINE CGPoint GTMNSPointToCGPoint(NSPoint inPoint) { - return CGPointMake(inPoint.x, inPoint.y); -} - -/// 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; -/// CGRect are relative to 0,0 in lower left -// -// Args: -// inRect: CGRect to convert -// -// Returns: -// Converted NSRect -CG_INLINE NSRect GTMCGRectToNSRect(CGRect inRect) { - return NSMakeRect(inRect.origin.x,inRect.origin.y,inRect.size.width,inRect.size.height); -} - -/// Convert from a NSRect to a CGRect. -// -/// NSRect are relative to 0,0 in lower left; -/// CGRect are relative to 0,0 in lower left -// -// Args: -// inRect: NSRect to convert -// -// Returns: -// Converted CGRect -CG_INLINE CGRect GTMNSRectToCGRect(NSRect inRect) { - return CGRectMake(inRect.origin.x,inRect.origin.y,inRect.size.width,inRect.size.height); -} - -/// 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 - -/// Convert from a CGSize to an NSSize. -// -// Args: -// inSize: CGSize to convert -// -// Returns: -// Converted NSSize -CG_INLINE NSSize GTMCGSizeToNSSize(CGSize inSize) { - return NSMakeSize(inSize.width, inSize.height); -} - -/// Convert from a NSSize to a CGSize. -// -// Args: -// inSize: NSSize to convert -// -// Returns: -// Converted CGSize -CG_INLINE CGSize GTMNSSizeToCGSize(NSSize inSize) { - return CGSizeMake(inSize.width, inSize.height); -} - -#pragma mark - -#pragma mark Point On Rect - -/// Return middle of left side of rectangle -// -// Args: -// rect - rectangle -// -// Returns: -// point located in the middle of left side of rect -CG_INLINE NSPoint GTMNSMidLeft(NSRect rect) { - return NSMakePoint(NSMinX(rect), NSMidY(rect)); -} - -/// Return middle of right side of rectangle -// -// Args: -// rect - rectangle -// -// Returns: -// point located in the middle of right side of rect -CG_INLINE NSPoint GTMNSMidRight(NSRect rect) { - return NSMakePoint(NSMaxX(rect), NSMidY(rect)); -} - -/// Return middle of top side of rectangle -// -// Args: -// rect - rectangle -// -// Returns: -// point located in the middle of top side of rect -CG_INLINE NSPoint GTMNSMidTop(NSRect rect) { - return NSMakePoint(NSMidX(rect), NSMaxY(rect)); -} - -/// Return middle of bottom side of rectangle -// -// Args: -// rect - rectangle -// -// Returns: -// point located in the middle of bottom side of rect -CG_INLINE NSPoint GTMNSMidBottom(NSRect rect) { - return NSMakePoint(NSMidX(rect), NSMinY(rect)); -} - -/// Return center of rectangle -// -// Args: -// rect - rectangle -// -// Returns: -// point located in the center of rect -CG_INLINE NSPoint GTMNSCenter(NSRect rect) { - return NSMakePoint(NSMidX(rect), NSMidY(rect)); -} - -/// Return middle of left side of rectangle -// -// Args: -// rect - rectangle -// -// Returns: -// point located in the middle of left side of rect -CG_INLINE CGPoint GTMCGMidLeft(CGRect rect) { - return CGPointMake(CGRectGetMinX(rect), CGRectGetMidY(rect)); -} - -/// Return middle of right side of rectangle -// -// Args: -// rect - rectangle -// -// Returns: -// point located in the middle of right side of rect -CG_INLINE CGPoint GTMCGMidRight(CGRect rect) { - return CGPointMake(CGRectGetMaxX(rect), CGRectGetMidY(rect)); -} - -/// Return middle of top side of rectangle -// -// Args: -// rect - rectangle -// -// Returns: -// point located in the middle of top side of rect -CG_INLINE CGPoint GTMCGMidTop(CGRect rect) { - return CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect)); -} - -/// Return middle of bottom side of rectangle -// -// Args: -// rect - rectangle -// -// Returns: -// point located in the middle of bottom side of rect -CG_INLINE CGPoint GTMCGMidBottom(CGRect rect) { - return CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect)); -} - -/// Return center of rectangle -// -// Args: -// rect - rectangle -// -// Returns: -// point located in the center of rect -CG_INLINE CGPoint GTMCGCenter(CGRect rect) { - return CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect)); -} - -#pragma mark - -#pragma mark Rect-Size Conversion - -/// Return size of rectangle -// -// Args: -// rect - rectangle -// -// Returns: -// size of rectangle -CG_INLINE NSSize GTMNSRectSize(NSRect rect) { - return NSMakeSize(NSWidth(rect), NSHeight(rect)); -} - -/// Return size of rectangle -// -// Args: -// rect - rectangle -// -// Returns: -// size of rectangle -CG_INLINE CGSize GTMCGRectSize(CGRect rect) { - return CGSizeMake(CGRectGetWidth(rect), CGRectGetHeight(rect)); -} - -/// Return rectangle of size -// -// Args: -// size - size -// -// Returns: -// rectangle of size (origin 0,0) -CG_INLINE NSRect GTMNSRectOfSize(NSSize size) { - return NSMakeRect(0.0f, 0.0f, size.width, size.height); -} - -/// Return rectangle of size -// -// Args: -// size - size -// -// Returns: -// rectangle of size (origin 0,0) -CG_INLINE CGRect GTMCGRectOfSize(CGSize size) { - return CGRectMake(0.0f, 0.0f, size.width, size.height); -} - -#pragma mark - -#pragma mark Rect Scaling and Alignment - -/// Scales an NSRect -// -// Args: -// inRect: Rect to scale -// xScale: fraction to scale (1.0 is 100%) -// yScale: fraction to scale (1.0 is 100%) -// -// Returns: -// Converted Rect -CG_INLINE NSRect GTMNSRectScale(NSRect inRect, float xScale, float yScale) { - return NSMakeRect(inRect.origin.x, inRect.origin.y, - inRect.size.width * xScale, inRect.size.height * yScale); -} - -/// Scales an CGRect -// -// Args: -// inRect: Rect to scale -// xScale: fraction to scale (1.0 is 100%) -// yScale: fraction to scale (1.0 is 100%) -// -// Returns: -// Converted Rect -CG_INLINE CGRect GTMCGRectScale(CGRect inRect, float xScale, float yScale) { - return CGRectMake(inRect.origin.x, inRect.origin.y, - inRect.size.width * xScale, inRect.size.height * yScale); -} - -/// Align rectangles -// -// Args: -// alignee - rect to be aligned -// aligner - rect to be aligned from -NSRect GTMAlignRectangles(NSRect alignee, NSRect aligner, - NSImageAlignment alignment); - -/// Align rectangles -// -// Args: -// alignee - rect to be aligned -// aligner - rect to be aligned from -// alignment - way to align the rectangles -CG_INLINE CGRect GTMCGAlignRectangles(CGRect alignee, CGRect aligner, - NSImageAlignment alignment) { - return GTMNSRectToCGRect(GTMAlignRectangles(GTMCGRectToNSRect(alignee), - GTMCGRectToNSRect(aligner), - alignment)); -} - -/// Scale rectangle -// -// Args: -// scalee - rect to be scaled -// size - size to scale to -// scaling - way to scale the rectangle -NSRect GTMScaleRectangleToSize(NSRect scalee, NSSize size, - NSImageScaling scaling); - -/// Scale rectangle -// -// Args: -// scalee - rect to be scaled -// size - size to scale to -// scaling - way to scale the rectangle -CG_INLINE CGRect GTMCGScaleRectangleToSize(CGRect scalee, CGSize size, - NSImageScaling scaling) { - return GTMNSRectToCGRect(GTMScaleRectangleToSize(GTMCGRectToNSRect(scalee), - GTMCGSizeToNSSize(size), - scaling)); -} - diff --git a/AppKit/GTMGeometryUtils.m b/AppKit/GTMGeometryUtils.m deleted file mode 100644 index efc543e..0000000 --- a/AppKit/GTMGeometryUtils.m +++ /dev/null @@ -1,152 +0,0 @@ -// -// GTMGeometryUtils.m -// -// Copyright 2006-2008 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// - -#import "GTMGeometryUtils.h" - -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: -// alignee - rect to be aligned -// aligner - rect to be aligned to -// alignment - alignment to be applied to alignee based on aligner - -NSRect GTMAlignRectangles(NSRect alignee, NSRect aligner, NSImageAlignment alignment) { - switch (alignment) { - case NSImageAlignTop: - 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: - alignee.origin.x = aligner.origin.x; - alignee.origin.y = aligner.origin.y + NSHeight(aligner) - NSHeight(alignee); - break; - - case NSImageAlignTopRight: - alignee.origin.x = aligner.origin.x + NSWidth(aligner) - NSWidth(alignee); - alignee.origin.y = aligner.origin.y + NSHeight(aligner) - NSHeight(alignee); - break; - - case NSImageAlignLeft: - alignee.origin.x = aligner.origin.x; - alignee.origin.y = aligner.origin.y + (NSHeight(aligner) * .5f - NSHeight(alignee) * .5f); - break; - - case NSImageAlignBottomLeft: - alignee.origin.x = aligner.origin.x; - alignee.origin.y = aligner.origin.y; - break; - - case NSImageAlignBottom: - alignee.origin.x = aligner.origin.x + (NSWidth(aligner) * .5f - NSWidth(alignee) * .5f); - alignee.origin.y = aligner.origin.y; - break; - - case NSImageAlignBottomRight: - alignee.origin.x = aligner.origin.x + NSWidth(aligner) - NSWidth(alignee); - alignee.origin.y = aligner.origin.y; - break; - - case NSImageAlignRight: - 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: - alignee.origin.x = aligner.origin.x + (NSWidth(aligner) * .5f - NSWidth(alignee) * .5f); - alignee.origin.y = aligner.origin.y + (NSHeight(aligner) * .5f - NSHeight(alignee) * .5f); - break; - } - return alignee; -} - -NSRect GTMScaleRectangleToSize(NSRect scalee, NSSize size, NSImageScaling scaling) { - switch (scaling) { - case NSScaleProportionally: { - float height = NSHeight(scalee); - float width = NSWidth(scalee); - if (isnormal(height) && isnormal(width) && - (height > size.height || width > size.width)) { - float horiz = size.width / width; - float vert = size.height / height; - float newScale = horiz < vert ? horiz : vert; - scalee = GTMNSRectScale(scalee, newScale, newScale); - } - break; - } - - case NSScaleToFit: - scalee.size = size; - break; - - case NSScaleNone: - default: - // Do nothing - break; - } - return scalee; -} 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 -#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 #import "GTMNSBezierPath+CGPath.h" -#import "GTMNSView+UnitTesting.h" +#import "GTMAppKit+UnitTesting.h" +#import "GTMSenTestCase.h" @interface GTMNSBezierPath_CGPathTest : SenTestCase @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 Binary files a/AppKit/GTMNSBezierPath+CGPathTest.tif and /dev/null differ diff --git a/AppKit/GTMNSBezierPath+CGPathTest.tiff b/AppKit/GTMNSBezierPath+CGPathTest.tiff new file mode 100644 index 0000000..98ec8f8 Binary files /dev/null and b/AppKit/GTMNSBezierPath+CGPathTest.tiff 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 #import "GTMNSBezierPath+RoundRect.h" -#import "GTMNSView+UnitTesting.h" +#import "GTMAppKit+UnitTesting.h" @interface GTMNSBezierPath_RoundRectTest : SenTestCase @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 Binary files a/AppKit/GTMNSBezierPath+RoundRectTest.tif and /dev/null differ diff --git a/AppKit/GTMNSBezierPath+RoundRectTest.tiff b/AppKit/GTMNSBezierPath+RoundRectTest.tiff new file mode 100644 index 0000000..c41ab04 Binary files /dev/null and b/AppKit/GTMNSBezierPath+RoundRectTest.tiff differ diff --git a/AppKit/GTMNSBezierPath+ShadingTest.10.4.tif b/AppKit/GTMNSBezierPath+ShadingTest.10.4.tif deleted file mode 100644 index 44a09b5..0000000 Binary files a/AppKit/GTMNSBezierPath+ShadingTest.10.4.tif and /dev/null differ diff --git a/AppKit/GTMNSBezierPath+ShadingTest.10.5.tif b/AppKit/GTMNSBezierPath+ShadingTest.10.5.tif deleted file mode 100644 index 6d4570a..0000000 Binary files a/AppKit/GTMNSBezierPath+ShadingTest.10.5.tif and /dev/null differ diff --git a/AppKit/GTMNSBezierPath+ShadingTest.10.5.tiff b/AppKit/GTMNSBezierPath+ShadingTest.10.5.tiff new file mode 100644 index 0000000..040cb0d Binary files /dev/null and b/AppKit/GTMNSBezierPath+ShadingTest.10.5.tiff 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 #import "GTMLinearRGBShading.h" -#import "GTMNSView+UnitTesting.h" +#import "GTMAppKit+UnitTesting.h" #import "GTMNSBezierPath+Shading.h" @interface GTMNSBezierPath_ShadingTest : SenTestCase @@ -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 #import #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 +#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 +#import +#import + +/// 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 + +// 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 +#import /// 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 #import "GTMCalculatedRange.h" #import "GTMSenTestCase.h" diff --git a/Foundation/GTMGeometryUtils.h b/Foundation/GTMGeometryUtils.h new file mode 100644 index 0000000..32c8745 --- /dev/null +++ b/Foundation/GTMGeometryUtils.h @@ -0,0 +1,377 @@ +// +// GTMGeometryUtils.h +// +// Utilities for geometrical utilities such as conversions +// between different types. +// +// Copyright 2006-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#include + +typedef enum { + GTMScaleProportionally = 0, // Fit proportionally + GTMScaleToFit, // Forced fit (distort if necessary) + GTMScaleNone // Don't scale (clip) +} GTMScaling; + +typedef enum { + GTMRectAlignCenter = 0, + GTMRectAlignTop, + GTMRectAlignTopLeft, + GTMRectAlignTopRight, + GTMRectAlignLeft, + GTMRectAlignBottom, + GTMRectAlignBottomLeft, + GTMRectAlignBottomRight, + GTMRectAlignRight +} GTMRectAlignment; + +#pragma mark Miscellaneous + +/// Calculate the distance between two points. +// +// Args: +// pt1 first point +// pt2 second point +// +// Returns: +// Distance +CG_INLINE float GTMDistanceBetweenPoints(NSPoint pt1, NSPoint pt2) { + float dX = pt1.x - pt2.x; + float dY = pt1.y - pt2.y; + return sqrtf(dX * dX + dY * dY); +} + +#pragma mark - +#pragma mark Point Conversion + +/// Quickly convert from a CGPoint to a NSPoint. +// +/// CGPoints are relative to 0,0 in lower left; +/// NSPoints are relative to 0,0 in lower left +// +// Args: +// inPoint: CGPoint to convert +// +// Returns: +// Converted NSPoint +CG_INLINE NSPoint GTMCGPointToNSPoint(CGPoint inPoint) { + return NSMakePoint(inPoint.x, inPoint.y); +} + +/// Quickly convert from a NSPoint to a CGPoint. +// +/// CGPoints are relative to 0,0 in lower left; +/// NSPoints are relative to 0,0 in lower left +// +// Args: +// inPoint: NSPoint to convert +// +// Returns: +// Converted CGPoint +CG_INLINE CGPoint GTMNSPointToCGPoint(NSPoint inPoint) { + return CGPointMake(inPoint.x, inPoint.y); +} + +#pragma mark - +#pragma mark Rect Conversion + +/// Convert from a CGRect to a NSRect. +// +/// NSRect are relative to 0,0 in lower left; +/// CGRect are relative to 0,0 in lower left +// +// Args: +// inRect: CGRect to convert +// +// Returns: +// Converted NSRect +CG_INLINE NSRect GTMCGRectToNSRect(CGRect inRect) { + return NSMakeRect(inRect.origin.x,inRect.origin.y,inRect.size.width,inRect.size.height); +} + +/// Convert from a NSRect to a CGRect. +// +/// NSRect are relative to 0,0 in lower left; +/// CGRect are relative to 0,0 in lower left +// +// Args: +// inRect: NSRect to convert +// +// Returns: +// Converted CGRect +CG_INLINE CGRect GTMNSRectToCGRect(NSRect inRect) { + return CGRectMake(inRect.origin.x,inRect.origin.y,inRect.size.width,inRect.size.height); +} + +#pragma mark - +#pragma mark Size Conversion + +/// Convert from a CGSize to an NSSize. +// +// Args: +// inSize: CGSize to convert +// +// Returns: +// Converted NSSize +CG_INLINE NSSize GTMCGSizeToNSSize(CGSize inSize) { + return NSMakeSize(inSize.width, inSize.height); +} + +/// Convert from a NSSize to a CGSize. +// +// Args: +// inSize: NSSize to convert +// +// Returns: +// Converted CGSize +CG_INLINE CGSize GTMNSSizeToCGSize(NSSize inSize) { + return CGSizeMake(inSize.width, inSize.height); +} + +#pragma mark - +#pragma mark Point On Rect + +/// Return middle of left side of rectangle +// +// Args: +// rect - rectangle +// +// Returns: +// point located in the middle of left side of rect +CG_INLINE NSPoint GTMNSMidLeft(NSRect rect) { + return NSMakePoint(NSMinX(rect), NSMidY(rect)); +} + +/// Return middle of right side of rectangle +// +// Args: +// rect - rectangle +// +// Returns: +// point located in the middle of right side of rect +CG_INLINE NSPoint GTMNSMidRight(NSRect rect) { + return NSMakePoint(NSMaxX(rect), NSMidY(rect)); +} + +/// Return middle of top side of rectangle +// +// Args: +// rect - rectangle +// +// Returns: +// point located in the middle of top side of rect +CG_INLINE NSPoint GTMNSMidTop(NSRect rect) { + return NSMakePoint(NSMidX(rect), NSMaxY(rect)); +} + +/// Return middle of bottom side of rectangle +// +// Args: +// rect - rectangle +// +// Returns: +// point located in the middle of bottom side of rect +CG_INLINE NSPoint GTMNSMidBottom(NSRect rect) { + return NSMakePoint(NSMidX(rect), NSMinY(rect)); +} + +/// Return center of rectangle +// +// Args: +// rect - rectangle +// +// Returns: +// point located in the center of rect +CG_INLINE NSPoint GTMNSCenter(NSRect rect) { + return NSMakePoint(NSMidX(rect), NSMidY(rect)); +} + +/// Return middle of left side of rectangle +// +// Args: +// rect - rectangle +// +// Returns: +// point located in the middle of left side of rect +CG_INLINE CGPoint GTMCGMidLeft(CGRect rect) { + return CGPointMake(CGRectGetMinX(rect), CGRectGetMidY(rect)); +} + +/// Return middle of right side of rectangle +// +// Args: +// rect - rectangle +// +// Returns: +// point located in the middle of right side of rect +CG_INLINE CGPoint GTMCGMidRight(CGRect rect) { + return CGPointMake(CGRectGetMaxX(rect), CGRectGetMidY(rect)); +} + +/// Return middle of top side of rectangle +// +// Args: +// rect - rectangle +// +// Returns: +// point located in the middle of top side of rect +CG_INLINE CGPoint GTMCGMidTop(CGRect rect) { + return CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect)); +} + +/// Return middle of bottom side of rectangle +// +// Args: +// rect - rectangle +// +// Returns: +// point located in the middle of bottom side of rect +CG_INLINE CGPoint GTMCGMidBottom(CGRect rect) { + return CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect)); +} + +/// Return center of rectangle +// +// Args: +// rect - rectangle +// +// Returns: +// point located in the center of rect +CG_INLINE CGPoint GTMCGCenter(CGRect rect) { + return CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect)); +} + +#pragma mark - +#pragma mark Rect-Size Conversion + +/// Return size of rectangle +// +// Args: +// rect - rectangle +// +// Returns: +// size of rectangle +CG_INLINE NSSize GTMNSRectSize(NSRect rect) { + return NSMakeSize(NSWidth(rect), NSHeight(rect)); +} + +/// Return size of rectangle +// +// Args: +// rect - rectangle +// +// Returns: +// size of rectangle +CG_INLINE CGSize GTMCGRectSize(CGRect rect) { + return CGSizeMake(CGRectGetWidth(rect), CGRectGetHeight(rect)); +} + +/// Return rectangle of size +// +// Args: +// size - size +// +// Returns: +// rectangle of size (origin 0,0) +CG_INLINE NSRect GTMNSRectOfSize(NSSize size) { + return NSMakeRect(0.0f, 0.0f, size.width, size.height); +} + +/// Return rectangle of size +// +// Args: +// size - size +// +// Returns: +// rectangle of size (origin 0,0) +CG_INLINE CGRect GTMCGRectOfSize(CGSize size) { + return CGRectMake(0.0f, 0.0f, size.width, size.height); +} + +#pragma mark - +#pragma mark Rect Scaling and Alignment + +/// Scales an NSRect +// +// Args: +// inRect: Rect to scale +// xScale: fraction to scale (1.0 is 100%) +// yScale: fraction to scale (1.0 is 100%) +// +// Returns: +// Converted Rect +CG_INLINE NSRect GTMNSRectScale(NSRect inRect, float xScale, float yScale) { + return NSMakeRect(inRect.origin.x, inRect.origin.y, + inRect.size.width * xScale, inRect.size.height * yScale); +} + +/// Scales an CGRect +// +// Args: +// inRect: Rect to scale +// xScale: fraction to scale (1.0 is 100%) +// yScale: fraction to scale (1.0 is 100%) +// +// Returns: +// Converted Rect +CG_INLINE CGRect GTMCGRectScale(CGRect inRect, float xScale, float yScale) { + return CGRectMake(inRect.origin.x, inRect.origin.y, + inRect.size.width * xScale, inRect.size.height * yScale); +} + +/// Align rectangles +// +// Args: +// alignee - rect to be aligned +// aligner - rect to be aligned from +NSRect GTMAlignRectangles(NSRect alignee, NSRect aligner, + GTMRectAlignment alignment); + +/// Align rectangles +// +// Args: +// alignee - rect to be aligned +// aligner - rect to be aligned from +// alignment - way to align the rectangles +CG_INLINE CGRect GTMCGAlignRectangles(CGRect alignee, CGRect aligner, + GTMRectAlignment alignment) { + return GTMNSRectToCGRect(GTMAlignRectangles(GTMCGRectToNSRect(alignee), + GTMCGRectToNSRect(aligner), + alignment)); +} + +/// Scale rectangle +// +// Args: +// scalee - rect to be scaled +// size - size to scale to +// scaling - way to scale the rectangle +NSRect GTMScaleRectangleToSize(NSRect scalee, NSSize size, + GTMScaling scaling); + +/// Scale rectangle +// +// Args: +// scalee - rect to be scaled +// size - size to scale to +// scaling - way to scale the rectangle +CG_INLINE CGRect GTMCGScaleRectangleToSize(CGRect scalee, CGSize size, + GTMScaling scaling) { + return GTMNSRectToCGRect(GTMScaleRectangleToSize(GTMCGRectToNSRect(scalee), + GTMCGSizeToNSSize(size), + scaling)); +} diff --git a/Foundation/GTMGeometryUtils.m b/Foundation/GTMGeometryUtils.m new file mode 100644 index 0000000..f5b38dc --- /dev/null +++ b/Foundation/GTMGeometryUtils.m @@ -0,0 +1,104 @@ +// +// GTMGeometryUtils.m +// +// Copyright 2006-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMGeometryUtils.h" + +/// Align rectangles +// +// Args: +// alignee - rect to be aligned +// aligner - rect to be aligned to +// alignment - alignment to be applied to alignee based on aligner + +NSRect GTMAlignRectangles(NSRect alignee, NSRect aligner, GTMRectAlignment alignment) { + switch (alignment) { + case GTMRectAlignTop: + alignee.origin.x = aligner.origin.x + (NSWidth(aligner) * .5f - NSWidth(alignee) * .5f); + alignee.origin.y = aligner.origin.y + NSHeight(aligner) - NSHeight(alignee); + break; + + case GTMRectAlignTopLeft: + alignee.origin.x = aligner.origin.x; + alignee.origin.y = aligner.origin.y + NSHeight(aligner) - NSHeight(alignee); + break; + + case GTMRectAlignTopRight: + alignee.origin.x = aligner.origin.x + NSWidth(aligner) - NSWidth(alignee); + alignee.origin.y = aligner.origin.y + NSHeight(aligner) - NSHeight(alignee); + break; + + case GTMRectAlignLeft: + alignee.origin.x = aligner.origin.x; + alignee.origin.y = aligner.origin.y + (NSHeight(aligner) * .5f - NSHeight(alignee) * .5f); + break; + + case GTMRectAlignBottomLeft: + alignee.origin.x = aligner.origin.x; + alignee.origin.y = aligner.origin.y; + break; + + case GTMRectAlignBottom: + alignee.origin.x = aligner.origin.x + (NSWidth(aligner) * .5f - NSWidth(alignee) * .5f); + alignee.origin.y = aligner.origin.y; + break; + + case GTMRectAlignBottomRight: + alignee.origin.x = aligner.origin.x + NSWidth(aligner) - NSWidth(alignee); + alignee.origin.y = aligner.origin.y; + break; + + case GTMRectAlignRight: + alignee.origin.x = aligner.origin.x + NSWidth(aligner) - NSWidth(alignee); + alignee.origin.y = aligner.origin.y + (NSHeight(aligner) * .5f - NSHeight(alignee) * .5f); + break; + + default: + case GTMRectAlignCenter: + alignee.origin.x = aligner.origin.x + (NSWidth(aligner) * .5f - NSWidth(alignee) * .5f); + alignee.origin.y = aligner.origin.y + (NSHeight(aligner) * .5f - NSHeight(alignee) * .5f); + break; + } + return alignee; +} + +NSRect GTMScaleRectangleToSize(NSRect scalee, NSSize size, GTMScaling scaling) { + switch (scaling) { + case GTMScaleProportionally: { + float height = NSHeight(scalee); + float width = NSWidth(scalee); + if (isnormal(height) && isnormal(width) && + (height > size.height || width > size.width)) { + float horiz = size.width / width; + float vert = size.height / height; + float newScale = horiz < vert ? horiz : vert; + scalee = GTMNSRectScale(scalee, newScale, newScale); + } + break; + } + + case GTMScaleToFit: + scalee.size = size; + break; + + case GTMScaleNone: + default: + // Do nothing + break; + } + return scalee; +} diff --git a/Foundation/GTMGeometryUtilsTest.m b/Foundation/GTMGeometryUtilsTest.m new file mode 100644 index 0000000..2fb0c68 --- /dev/null +++ b/Foundation/GTMGeometryUtilsTest.m @@ -0,0 +1,181 @@ +// +// GTMGeometryUtilsTest.m +// +// Copyright 2006-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMSenTestCase.h" +#import "GTMGeometryUtils.h" + +@interface GTMGeometryUtilsTest : SenTestCase +@end + +@implementation GTMGeometryUtilsTest + +- (void)testGTMCGPointToNSPoint { + CGPoint cgPoint = CGPointMake(15.1,6.2); + NSPoint nsPoint = GTMCGPointToNSPoint(cgPoint); + STAssertTrue(CGPointEqualToPoint(*(CGPoint*)&nsPoint, cgPoint), nil); +} + +- (void)testGTMNSPointToCGPoint { + NSPoint nsPoint = NSMakePoint(10.2,1.5); + CGPoint cgPoint = GTMNSPointToCGPoint(nsPoint); + STAssertTrue(CGPointEqualToPoint(cgPoint, *(CGPoint*)&nsPoint), nil); +} + +- (void)testGTMCGRectToNSRect { + CGRect cgRect = CGRectMake(1.5,2.4,10.6,11.7); + NSRect nsRect = GTMCGRectToNSRect(cgRect); + STAssertTrue(CGRectEqualToRect(cgRect, *(CGRect*)&nsRect), nil); +} + + +- (void)testGTMNSRectToCGRect { + NSRect nsRect = NSMakeRect(4.6,3.2,22.1,45.0); + CGRect cgRect = GTMNSRectToCGRect(nsRect); + STAssertTrue(CGRectEqualToRect(cgRect, *(CGRect*)&nsRect), nil); +} + +- (void)testGTMCGSizeToNSSize { + CGSize cgSize = {5,6}; + NSSize nsSize = GTMCGSizeToNSSize(cgSize); + STAssertTrue(CGSizeEqualToSize(cgSize, *(CGSize*)&nsSize), nil); +} + +- (void)testGTMNSSizeToCGSize { + NSSize nsSize = {22,15}; + CGSize cgSize = GTMNSSizeToCGSize(nsSize); + STAssertTrue(CGSizeEqualToSize(cgSize, *(CGSize*)&nsSize), nil); +} + +- (void)testGTMDistanceBetweenPoints { + NSPoint pt1 = NSMakePoint(0, 0); + NSPoint pt2 = NSMakePoint(3, 4); + STAssertEquals(GTMDistanceBetweenPoints(pt1, pt2), 5.0f, nil); + STAssertEquals(GTMDistanceBetweenPoints(pt2, pt1), 5.0f, nil); + pt1 = NSMakePoint(1, 1); + pt2 = NSMakePoint(1, 1); + STAssertEquals(GTMDistanceBetweenPoints(pt1, pt2), 0.0f, nil); +} + +- (void)testGTMAlignRectangles { + typedef struct { + NSPoint expectedOrigin; + GTMRectAlignment alignment; + } TestData; + + TestData data[] = { + { {1,2}, GTMRectAlignTop }, + { {0,2}, GTMRectAlignTopLeft }, + { {2,2}, GTMRectAlignTopRight }, + { {0,1}, GTMRectAlignLeft }, + { {1,0}, GTMRectAlignBottom }, + { {0,0}, GTMRectAlignBottomLeft }, + { {2,0}, GTMRectAlignBottomRight }, + { {2,1}, GTMRectAlignRight }, + { {1,1}, GTMRectAlignCenter }, + }; + + NSRect rect1 = NSMakeRect(0, 0, 4, 4); + NSRect rect2 = NSMakeRect(0, 0, 2, 2); + + for (int i = 0; i < sizeof(data) / sizeof(TestData); i++) { + NSRect expectedRect; + expectedRect.origin = data[i].expectedOrigin; + expectedRect.size = NSMakeSize(2, 2); + NSRect outRect = GTMAlignRectangles(rect2, rect1, data[i].alignment); + STAssertEquals(outRect, expectedRect, nil); + } +} + +- (void)testGTMPointsOnRect { + NSRect rect = NSMakeRect(0, 0, 2, 2); + CGRect cgRect = GTMNSRectToCGRect(rect); + + NSPoint point = GTMNSMidLeft(rect); + CGPoint cgPoint = GTMCGMidLeft(cgRect); + STAssertEquals(point.x, cgPoint.x, nil); + STAssertEquals(point.y, cgPoint.y, nil); + STAssertEqualsWithAccuracy(point.y, 1.0f, 0.01f, nil); + STAssertEqualsWithAccuracy(point.x, 0.0f, 0.01f, nil); + + point = GTMNSMidRight(rect); + cgPoint = GTMCGMidRight(cgRect); + STAssertEquals(point.x, cgPoint.x, nil); + STAssertEquals(point.y, cgPoint.y, nil); + STAssertEqualsWithAccuracy(point.y, 1.0f, 0.01f, nil); + STAssertEqualsWithAccuracy(point.x, 2.0f, 0.01f, nil); + + point = GTMNSMidTop(rect); + cgPoint = GTMCGMidTop(cgRect); + STAssertEquals(point.x, cgPoint.x, nil); + STAssertEquals(point.y, cgPoint.y, nil); + STAssertEqualsWithAccuracy(point.y, 2.0f, 0.01f, nil); + STAssertEqualsWithAccuracy(point.x, 1.0f, 0.01f, nil); + + point = GTMNSMidBottom(rect); + cgPoint = GTMCGMidBottom(cgRect); + STAssertEquals(point.x, cgPoint.x, nil); + STAssertEquals(point.y, cgPoint.y, nil); + STAssertEqualsWithAccuracy(point.y, 0.0f, 0.01f, nil); + STAssertEqualsWithAccuracy(point.x, 1.0f, 0.01f, nil); +} + +- (void)testGTMRectScaling { + NSRect rect = NSMakeRect(1.0f, 2.0f, 5.0f, 10.0f); + NSRect rect2 = NSMakeRect(1.0f, 2.0f, 1.0f, 12.0f); + STAssertEquals(GTMNSRectScale(rect, 0.2f, 1.2f), + rect2, nil); + STAssertEquals(GTMCGRectScale(GTMNSRectToCGRect(rect), 0.2f, 1.2f), + GTMNSRectToCGRect(rect2), nil); +} + +- (void)testGTMScaleRectangleToSize { + NSRect rect = NSMakeRect(0.0f, 0.0f, 10.0f, 10.0f); + typedef struct { + NSSize size_; + NSSize newSize_; + } Test; + Test tests[] = { + { { 5.0, 10.0 }, { 5.0, 5.0 } }, + { { 10.0, 5.0 }, { 5.0, 5.0 } }, + { { 10.0, 10.0 }, { 10.0, 10.0 } }, + { { 11.0, 11.0, }, { 10.0, 10.0 } }, + { { 5.0, 2.0 }, { 2.0, 2.0 } }, + { { 2.0, 5.0 }, { 2.0, 2.0 } }, + { { 2.0, 2.0 }, { 2.0, 2.0 } }, + { { 0.0, 10.0 }, { 0.0, 0.0 } } + }; + + for (size_t i = 0; i < sizeof(tests) / sizeof(Test); ++i) { + NSRect result = GTMScaleRectangleToSize(rect, tests[i].size_, + GTMScaleProportionally); + STAssertEquals(result, GTMNSRectOfSize(tests[i].newSize_), @"failed on test %z", i); + } + + NSRect result = GTMScaleRectangleToSize(NSZeroRect, tests[0].size_, + GTMScaleProportionally); + STAssertEquals(result, NSZeroRect, nil); + + result = GTMScaleRectangleToSize(rect, tests[0].size_, + GTMScaleToFit); + STAssertEquals(result, GTMNSRectOfSize(tests[0].size_), nil); + + result = GTMScaleRectangleToSize(rect, tests[0].size_, + GTMScaleNone); + STAssertEquals(result, rect, nil); +} +@end diff --git a/Foundation/GTMNSData+zlib.m b/Foundation/GTMNSData+zlib.m index be84114..514477f 100644 --- a/Foundation/GTMNSData+zlib.m +++ b/Foundation/GTMNSData+zlib.m @@ -18,6 +18,7 @@ #import "GTMNSData+zlib.h" #import +#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 #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 +#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 +#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 #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 + +@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 +#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"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_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'zz zzzzz"; + NSString *string2 = + [NSString stringWithFormat:@"zzzzz\"z&z'zz 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 +#import + +// 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 +#import + +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 + +@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 +- (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(®exData_, [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:®Match + 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:®Match + 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 - #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 #import #import +#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 +#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 -#import + +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 +#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 = ""; }; 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 32DBCF5E0370ADEE00C91783 /* GTM_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTM_Prefix.pch; sourceTree = ""; }; + 8B1A14E90D900BC800CA1E8E /* GTMNSObject+BindingUnitTesting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSObject+BindingUnitTesting.m"; sourceTree = ""; }; + 8B1A14EA0D900BC800CA1E8E /* GTMNSObject+BindingUnitTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSObject+BindingUnitTesting.h"; sourceTree = ""; }; + 8B1A16050D90344B00CA1E8E /* GTMDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMDefines.h; sourceTree = ""; }; 8B2A9B1D0D8270DA00599386 /* GTMNSWorkspace+ScreenSaver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSWorkspace+ScreenSaver.m"; sourceTree = ""; }; 8B2A9B1E0D8270DA00599386 /* GTMNSWorkspace+ScreenSaverTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSWorkspace+ScreenSaverTest.m"; sourceTree = ""; }; 8B2A9B1F0D8270DA00599386 /* GTMNSWorkspace+ScreenSaver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSWorkspace+ScreenSaver.h"; sourceTree = ""; }; 8B2A9BEB0D82714A00599386 /* ScreenSaver.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ScreenSaver.framework; path = /System/Library/Frameworks/ScreenSaver.framework; sourceTree = ""; }; + 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 = ""; }; + 8B45A2670DA498A0001148C5 /* GTMUnitTestingUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMUnitTestingUtilities.h; sourceTree = ""; }; + 8B45A2680DA498A0001148C5 /* GTMUnitTestingUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMUnitTestingUtilities.m; sourceTree = ""; }; + 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 = ""; }; + 8B45A2A70DA49C47001148C5 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 8B45A2A80DA49C47001148C5 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 8B45A2DE0DA51A7E001148C5 /* GTMUnitTestingTest.nib */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; path = GTMUnitTestingTest.nib; sourceTree = ""; }; + 8B45A2E00DA51ABC001148C5 /* GTMUnitTestingTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMUnitTestingTest.h; sourceTree = ""; }; + 8B45A2E10DA51ABC001148C5 /* GTMUnitTestingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMUnitTestingTest.m; sourceTree = ""; }; + 8B45A5F50DA5EB9F001148C5 /* GTMUnitTestingWindow.gtmUTState */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = GTMUnitTestingWindow.gtmUTState; sourceTree = ""; }; + 8B45A5F60DA5EB9F001148C5 /* GTMUnitTestingTestApp.gtmUTState */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = GTMUnitTestingTestApp.gtmUTState; sourceTree = ""; }; + 8B45A6B90DA67DD5001148C5 /* GTMUnitTestingImage.gtmUTState */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = GTMUnitTestingImage.gtmUTState; sourceTree = ""; }; + 8B55479A0DB3B7A50014CC1C /* GTMAppKit+UnitTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMAppKit+UnitTesting.h"; sourceTree = ""; }; + 8B55479B0DB3B7A50014CC1C /* GTMAppKit+UnitTesting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMAppKit+UnitTesting.m"; sourceTree = ""; }; + 8B6F31EF0DA347720052CA40 /* GTMMethodCheck.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMMethodCheck.m; sourceTree = ""; }; + 8B6F31F10DA347720052CA40 /* GTMMethodCheckTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMMethodCheckTest.m; sourceTree = ""; }; + 8B6F31F40DA3489B0052CA40 /* GTMMethodCheck.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMMethodCheck.h; sourceTree = ""; }; + 8B6F32040DA34A1B0052CA40 /* GTMObjC2Runtime.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMObjC2Runtime.m; sourceTree = ""; }; + 8B6F32050DA34A1B0052CA40 /* GTMObjC2RuntimeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMObjC2RuntimeTest.m; sourceTree = ""; }; + 8B6F32060DA34A1B0052CA40 /* GTMObjC2Runtime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMObjC2Runtime.h; sourceTree = ""; }; + 8B726BD00D91C0860090C251 /* GTMCALayer+UnitTesting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMCALayer+UnitTesting.m"; sourceTree = ""; }; + 8B726BD10D91C0860090C251 /* GTMCALayer+UnitTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMCALayer+UnitTesting.h"; sourceTree = ""; }; + 8B7AD4970DABBB5800B84F4A /* GTMNSBezierPath+ShadingTest.10.5.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMNSBezierPath+ShadingTest.10.5.tiff"; sourceTree = ""; }; + 8B7AD4980DABBB5800B84F4A /* GTMNSBezierPath+CGPathTest.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMNSBezierPath+CGPathTest.tiff"; sourceTree = ""; }; + 8B7AD4990DABBB5800B84F4A /* GTMNSBezierPath+RoundRectTest.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMNSBezierPath+RoundRectTest.tiff"; sourceTree = ""; }; + 8B7AD4AD0DABBFEE00B84F4A /* GTMUnitTestingBindingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMUnitTestingBindingTest.m; sourceTree = ""; }; + 8BC046B80DAE8C4B00C2D1CA /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = /System/Library/Frameworks/ApplicationServices.framework; sourceTree = ""; }; + 8BC04CD10DB003BE00C2D1CA /* GTMNSString+Utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSString+Utilities.h"; sourceTree = ""; }; + 8BC04CD20DB003BE00C2D1CA /* GTMNSString+Utilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+Utilities.m"; sourceTree = ""; }; + 8BC04CD50DB003C300C2D1CA /* GTMNSString+UtilitiesTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+UtilitiesTest.m"; sourceTree = ""; }; + 8BC04D140DB0061300C2D1CA /* RunMacOSUnitTests.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = RunMacOSUnitTests.sh; sourceTree = ""; }; + 8BEEA90A0DA7446300894774 /* GTMUnitTestingImage.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = GTMUnitTestingImage.tiff; sourceTree = ""; }; + 8BEEA90B0DA7446300894774 /* GTMUnitTestingWindow.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = GTMUnitTestingWindow.tiff; sourceTree = ""; }; + 8BEEA90C0DA7446300894774 /* GTMUnitTestingView.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = GTMUnitTestingView.tiff; sourceTree = ""; }; F413908C0D75F63C00F72B31 /* GTMNSFileManager+Path.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSFileManager+Path.h"; sourceTree = ""; }; F413908D0D75F63C00F72B31 /* GTMNSFileManager+Path.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSFileManager+Path.m"; sourceTree = ""; }; F413908E0D75F63C00F72B31 /* GTMNSFileManager+PathTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSFileManager+PathTest.m"; sourceTree = ""; }; F428FEFF0D48E55E00382ED1 /* GTMNSBezierPath+CGPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSBezierPath+CGPath.h"; sourceTree = ""; }; F428FF000D48E55E00382ED1 /* GTMNSBezierPath+CGPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSBezierPath+CGPath.m"; sourceTree = ""; }; F428FF010D48E55E00382ED1 /* GTMNSBezierPath+CGPathTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSBezierPath+CGPathTest.m"; sourceTree = ""; }; - F428FF020D48E55E00382ED1 /* GTMNSBezierPath+CGPathTest.tif */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMNSBezierPath+CGPathTest.tif"; sourceTree = ""; }; 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 = ""; }; F42E089B0D199B1800D5DDE0 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = /System/Library/Frameworks/SenTestingKit.framework; sourceTree = ""; }; F42E09A80D19A5E300D5DDE0 /* GTMNSWorkspace+Theme.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSWorkspace+Theme.h"; sourceTree = ""; }; @@ -180,8 +271,6 @@ F47A79870D746EE9002302AB /* GTMScriptRunnerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMScriptRunnerTest.m; sourceTree = ""; }; F47F1C0D0D490BC000925B8F /* GTMNSBezierPath+Shading.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSBezierPath+Shading.h"; sourceTree = ""; }; F47F1C0E0D490BC000925B8F /* GTMNSBezierPath+Shading.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSBezierPath+Shading.m"; sourceTree = ""; }; - F47F1C0F0D490BC000925B8F /* GTMNSBezierPath+ShadingTest.10.4.tif */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMNSBezierPath+ShadingTest.10.4.tif"; sourceTree = ""; }; - F47F1C100D490BC000925B8F /* GTMNSBezierPath+ShadingTest.10.5.tif */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMNSBezierPath+ShadingTest.10.5.tif"; sourceTree = ""; }; F47F1C110D490BC000925B8F /* GTMNSBezierPath+ShadingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSBezierPath+ShadingTest.m"; sourceTree = ""; }; F47F1C740D490E5C00925B8F /* GTMShading.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMShading.h; sourceTree = ""; }; F47F1CAC0D4910FD00925B8F /* GTMNSColor+Theme.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSColor+Theme.h"; sourceTree = ""; }; @@ -190,6 +279,7 @@ F47F1D2D0D4914AD00925B8F /* GTMCalculatedRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMCalculatedRange.h; sourceTree = ""; }; F47F1D2E0D4914AD00925B8F /* GTMCalculatedRange.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMCalculatedRange.m; sourceTree = ""; }; F47F1D2F0D4914AD00925B8F /* GTMCalculatedRangeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMCalculatedRangeTest.m; sourceTree = ""; }; + F48B91040D94487D00D45044 /* libgcov_readme.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = libgcov_readme.html; sourceTree = ""; }; F48FE2410D197F9A009257D2 /* DebugTigerOrLater.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = DebugTigerOrLater.xcconfig; sourceTree = ""; }; F48FE2420D197F9A009257D2 /* DebugUnittest.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = DebugUnittest.xcconfig; sourceTree = ""; }; F48FE2430D197F9A009257D2 /* LoadableBundle.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = LoadableBundle.xcconfig; sourceTree = ""; }; @@ -207,7 +297,6 @@ F48FE2810D198D0E009257D2 /* GTMNSBezierPath+RoundRect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSBezierPath+RoundRect.h"; sourceTree = ""; }; F48FE2820D198D0E009257D2 /* GTMNSBezierPath+RoundRect.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSBezierPath+RoundRect.m"; sourceTree = ""; }; F48FE2830D198D0E009257D2 /* GTMNSBezierPath+RoundRectTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSBezierPath+RoundRectTest.m"; sourceTree = ""; }; - F48FE2840D198D0E009257D2 /* GTMNSBezierPath+RoundRectTest.tif */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMNSBezierPath+RoundRectTest.tif"; sourceTree = ""; }; F48FE28D0D198D24009257D2 /* GTMGarbageCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMGarbageCollection.h; sourceTree = ""; }; F48FE28E0D198D24009257D2 /* GTMNSString+HTML.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSString+HTML.h"; sourceTree = ""; }; F48FE28F0D198D24009257D2 /* GTMNSString+HTML.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+HTML.m"; sourceTree = ""; }; @@ -217,21 +306,42 @@ F48FE2930D198D24009257D2 /* GTMSystemVersion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMSystemVersion.m; sourceTree = ""; }; F48FE29B0D198D36009257D2 /* GTMNSObject+UnitTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSObject+UnitTesting.h"; sourceTree = ""; }; F48FE29C0D198D36009257D2 /* GTMNSObject+UnitTesting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSObject+UnitTesting.m"; sourceTree = ""; }; - F48FE29D0D198D36009257D2 /* GTMNSView+UnitTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSView+UnitTesting.h"; sourceTree = ""; }; - F48FE29E0D198D36009257D2 /* GTMNSView+UnitTesting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSView+UnitTesting.m"; sourceTree = ""; }; F48FE29F0D198D36009257D2 /* GTMSenTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMSenTestCase.h; sourceTree = ""; }; F48FE2E10D198E4C009257D2 /* GTMSystemVersionTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMSystemVersionTest.m; sourceTree = ""; }; F4C978090D5B79C7001C29A6 /* ReleaseNotes.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ReleaseNotes.txt; sourceTree = ""; }; + F4CA854E0DAFAAB600B4AB10 /* xcconfigs-readme.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "xcconfigs-readme.txt"; sourceTree = ""; }; + F4FF22770D9D4835003880AC /* GTMDebugSelectorValidation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMDebugSelectorValidation.h; sourceTree = ""; }; /* 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 = ""; @@ -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 = ""; }; + 8B45A2A20DA49C47001148C5 /* GTMUIUnitTestingHarness */ = { + isa = PBXGroup; + children = ( + 8B45A2A50DA49C47001148C5 /* MainMenu.nib */, + 8B45A2A70DA49C47001148C5 /* Info.plist */, + 8B45A2A80DA49C47001148C5 /* main.m */, + ); + path = GTMUIUnitTestingHarness; + sourceTree = ""; + }; + F48B91030D94485500D45044 /* TigerGcov */ = { + isa = PBXGroup; + children = ( + F48B91040D94487D00D45044 /* libgcov_readme.html */, + ); + path = TigerGcov; + sourceTree = ""; + }; 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 = ""; @@ -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 = ""; }; + F4CA852B0DAFA92A00B4AB10 /* Project */ = { + isa = PBXGroup; + children = ( + F48FE2410D197F9A009257D2 /* DebugTigerOrLater.xcconfig */, + F48FE2440D197F9A009257D2 /* ReleaseTigerOrLater.xcconfig */, + ); + path = Project; + sourceTree = ""; + }; + F4CA852E0DAFA92F00B4AB10 /* Target */ = { + isa = PBXGroup; + children = ( + F48FE2420D197F9A009257D2 /* DebugUnittest.xcconfig */, + F48FE2430D197F9A009257D2 /* LoadableBundle.xcconfig */, + F48FE2450D197F9A009257D2 /* ReleaseUnittest.xcconfig */, + F48FE2460D197F9A009257D2 /* SharedLibrary.xcconfig */, + F48FE2470D197F9A009257D2 /* StaticLibrary.xcconfig */, + ); + path = Target; + sourceTree = ""; + }; + F4FF22760D9D47FB003880AC /* DebugUtils */ = { + isa = PBXGroup; + children = ( + F4FF22770D9D4835003880AC /* GTMDebugSelectorValidation.h */, + 8B6F31EF0DA347720052CA40 /* GTMMethodCheck.m */, + 8B6F31F40DA3489B0052CA40 /* GTMMethodCheck.h */, + 8B6F31F10DA347720052CA40 /* GTMMethodCheckTest.m */, + ); + path = DebugUtils; + sourceTree = ""; + }; /* 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 = ""; + }; +/* 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 +#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 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + CFBundleIdentifier + com.yourcompany.${PRODUCT_NAME:identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + + 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 = ""; }; + 8B308AF40DAD070C00183556 /* RunIPhoneUnitTest.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = RunIPhoneUnitTest.sh; path = UnitTesting/RunIPhoneUnitTest.sh; sourceTree = ""; }; + 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 = ""; }; + 8B5547C80DB3BBF20014CC1C /* GTMUIKit+UnitTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMUIKit+UnitTesting.h"; sourceTree = ""; }; + 8B5547C90DB3BBF20014CC1C /* GTMUIKit+UnitTestingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMUIKit+UnitTestingTest.m"; sourceTree = ""; }; + 8BC047750DAE926E00C2D1CA /* GTMDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMDefines.h; sourceTree = ""; }; + 8BC047770DAE928A00C2D1CA /* GTMCalculatedRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMCalculatedRange.h; sourceTree = ""; }; + 8BC047780DAE928A00C2D1CA /* GTMCalculatedRange.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMCalculatedRange.m; sourceTree = ""; }; + 8BC047790DAE928A00C2D1CA /* GTMCalculatedRangeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMCalculatedRangeTest.m; sourceTree = ""; }; + 8BC0477A0DAE928A00C2D1CA /* GTMGarbageCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMGarbageCollection.h; sourceTree = ""; }; + 8BC0477E0DAE928A00C2D1CA /* GTMNSData+zlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSData+zlib.h"; sourceTree = ""; }; + 8BC0477F0DAE928A00C2D1CA /* GTMNSData+zlib.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSData+zlib.m"; sourceTree = ""; }; + 8BC047800DAE928A00C2D1CA /* GTMNSData+zlibTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSData+zlibTest.m"; sourceTree = ""; }; + 8BC047810DAE928A00C2D1CA /* GTMNSEnumerator+Filter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSEnumerator+Filter.h"; sourceTree = ""; }; + 8BC047820DAE928A00C2D1CA /* GTMNSEnumerator+Filter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSEnumerator+Filter.m"; sourceTree = ""; }; + 8BC047830DAE928A00C2D1CA /* GTMNSEnumerator+FilterTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSEnumerator+FilterTest.m"; sourceTree = ""; }; + 8BC047840DAE928A00C2D1CA /* GTMNSFileManager+Path.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSFileManager+Path.h"; sourceTree = ""; }; + 8BC047850DAE928A00C2D1CA /* GTMNSFileManager+Path.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSFileManager+Path.m"; sourceTree = ""; }; + 8BC047860DAE928A00C2D1CA /* GTMNSFileManager+PathTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSFileManager+PathTest.m"; sourceTree = ""; }; + 8BC047870DAE928A00C2D1CA /* GTMNSString+HTML.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSString+HTML.h"; sourceTree = ""; }; + 8BC047880DAE928A00C2D1CA /* GTMNSString+HTML.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+HTML.m"; sourceTree = ""; }; + 8BC047890DAE928A00C2D1CA /* GTMNSString+HTMLTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+HTMLTest.m"; sourceTree = ""; }; + 8BC0478A0DAE928A00C2D1CA /* GTMNSString+XML.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSString+XML.h"; sourceTree = ""; }; + 8BC0478B0DAE928A00C2D1CA /* GTMNSString+XML.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+XML.m"; sourceTree = ""; }; + 8BC0478C0DAE928A00C2D1CA /* GTMNSString+XMLTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+XMLTest.m"; sourceTree = ""; }; + 8BC0478D0DAE928A00C2D1CA /* GTMObjC2Runtime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMObjC2Runtime.h; sourceTree = ""; }; + 8BC047900DAE928A00C2D1CA /* GTMObjectSingleton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMObjectSingleton.h; sourceTree = ""; }; + 8BC047910DAE928A00C2D1CA /* GTMRegex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMRegex.h; sourceTree = ""; }; + 8BC047920DAE928A00C2D1CA /* GTMRegex.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMRegex.m; sourceTree = ""; }; + 8BC047930DAE928A00C2D1CA /* GTMRegexTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMRegexTest.m; sourceTree = ""; }; + 8BC0479B0DAE928A00C2D1CA /* GTMDebugSelectorValidation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMDebugSelectorValidation.h; sourceTree = ""; }; + 8BC0479C0DAE928A00C2D1CA /* GTMMethodCheck.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMMethodCheck.h; sourceTree = ""; }; + 8BC0479D0DAE928A00C2D1CA /* GTMMethodCheck.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMMethodCheck.m; sourceTree = ""; }; + 8BC0479E0DAE928A00C2D1CA /* GTMMethodCheckTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMMethodCheckTest.m; sourceTree = ""; }; + 8BC047A00DAE928A00C2D1CA /* GTMCALayer+UnitTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMCALayer+UnitTesting.h"; sourceTree = ""; }; + 8BC047A10DAE928A00C2D1CA /* GTMCALayer+UnitTesting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMCALayer+UnitTesting.m"; sourceTree = ""; }; + 8BC047DD0DAE928A00C2D1CA /* GTMIPhoneUnitTestMain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMIPhoneUnitTestMain.m; sourceTree = ""; }; + 8BC047EC0DAE928A00C2D1CA /* GTMNSObject+UnitTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSObject+UnitTesting.h"; sourceTree = ""; }; + 8BC047ED0DAE928A00C2D1CA /* GTMNSObject+UnitTesting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSObject+UnitTesting.m"; sourceTree = ""; }; + 8BC047F60DAE928A00C2D1CA /* GTMSenTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMSenTestCase.h; sourceTree = ""; }; + 8BC047F70DAE928A00C2D1CA /* GTMSenTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMSenTestCase.m; sourceTree = ""; }; + 8BC048000DAE928A00C2D1CA /* GTMUIViewUnitTestingTest.gtmUTState */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = GTMUIViewUnitTestingTest.gtmUTState; sourceTree = ""; }; + 8BC048010DAE928A00C2D1CA /* GTMUIViewUnitTestingTest.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = GTMUIViewUnitTestingTest.png; sourceTree = ""; }; + 8BC0480E0DAE928A00C2D1CA /* RunIPhoneUnitTest.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = RunIPhoneUnitTest.sh; sourceTree = ""; }; + 8BC049850DAEC59100C2D1CA /* DebugiPhone.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = DebugiPhone.xcconfig; sourceTree = ""; }; + 8BC049890DAEC59100C2D1CA /* ReleaseiPhone.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ReleaseiPhone.xcconfig; sourceTree = ""; }; + 8BC0498F0DAEC59100C2D1CA /* CodeCoverage.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = CodeCoverage.xcconfig; sourceTree = ""; }; + 8BC04A6F0DAF144200C2D1CA /* GTMSystemVersion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMSystemVersion.h; sourceTree = ""; }; + 8BC04A710DAF144700C2D1CA /* GTMSystemVersionTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMSystemVersionTest.m; sourceTree = ""; }; + 8BC04A740DAF145200C2D1CA /* GTMSystemVersion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMSystemVersion.m; sourceTree = ""; }; + 8BC04C440DAFFCF300C2D1CA /* GTMNSString+Utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSString+Utilities.h"; sourceTree = ""; }; + 8BC04C450DAFFCF300C2D1CA /* GTMNSString+Utilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+Utilities.m"; sourceTree = ""; }; + 8BC04C460DAFFCF300C2D1CA /* GTMNSString+UtilitiesTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+UtilitiesTest.m"; sourceTree = ""; }; + 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 = ""; }; +/* 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 = ""; + }; + 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 = ""; + }; + 29B97323FDCFA39411CA2CEA /* Frameworks */ = { + isa = PBXGroup; + children = ( + 8BC04D470DB0088500C2D1CA /* libz.dylib */, + 8B308BCD0DAD0B8400183556 /* QuartzCore.framework */, + 1D3623EB0D0F72F000981E51 /* CoreGraphics.framework */, + 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */, + 1D30AB110D05D00D00671497 /* Foundation.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 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 = ""; + }; + 8BC0479A0DAE928A00C2D1CA /* DebugUtils */ = { + isa = PBXGroup; + children = ( + 8BC0479B0DAE928A00C2D1CA /* GTMDebugSelectorValidation.h */, + 8BC0479C0DAE928A00C2D1CA /* GTMMethodCheck.h */, + 8BC0479D0DAE928A00C2D1CA /* GTMMethodCheck.m */, + 8BC0479E0DAE928A00C2D1CA /* GTMMethodCheckTest.m */, + ); + path = DebugUtils; + sourceTree = ""; + }; + 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 = ""; + }; + 8BC049840DAEC59100C2D1CA /* XcodeConfig */ = { + isa = PBXGroup; + children = ( + 8BC04F020DB15A5300C2D1CA /* Project */, + 8BC0498E0DAEC59100C2D1CA /* subconfig */, + ); + path = XcodeConfig; + sourceTree = ""; + }; + 8BC0498E0DAEC59100C2D1CA /* subconfig */ = { + isa = PBXGroup; + children = ( + 8BC0498F0DAEC59100C2D1CA /* CodeCoverage.xcconfig */, + ); + path = subconfig; + sourceTree = ""; + }; + 8BC04F020DB15A5300C2D1CA /* Project */ = { + isa = PBXGroup; + children = ( + 8BC049850DAEC59100C2D1CA /* DebugiPhone.xcconfig */, + 8BC049890DAEC59100C2D1CA /* ReleaseiPhone.xcconfig */, + ); + path = Project; + sourceTree = ""; + }; +/* 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 + #import +#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 Binary files /dev/null and b/TigerGcov/libgcov.a 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 @@ + + + + libgcov_readme.html + + + + + + diff --git a/UnitTesting/GTMAppKit+UnitTesting.h b/UnitTesting/GTMAppKit+UnitTesting.h new file mode 100755 index 0000000..5db9ebb --- /dev/null +++ b/UnitTesting/GTMAppKit+UnitTesting.h @@ -0,0 +1,145 @@ +// +// 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 +#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) +@end + +@interface NSControl (GTMUnitTestingAdditions) +@end + +@interface NSTextField (GTMUnitTestingAdditions) +@end + +@interface NSCell (GTMUnitTestingAdditions) +@end + +@interface NSImage (GTMUnitTestingAdditions) +@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 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 a1Object = (a1); \ + NSSize a2Size = (a2); \ + NSString* a3String = (a3); \ + void *a4ContextInfo = (a4); \ + NSRect frame = NSMakeRect(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 NSObject+UnitTesting.h for details. +@interface NSView (GTMUnitTestingAdditions) +// Returns whether unitTestEncodeState should recurse into subviews +// +// 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)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, 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 +@interface GTMUnitTestView : NSView { + @private + id 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:(NSRect)frame drawer:(id)drawer contextInfo:(void*)contextInfo; +@end + +/// \cond Protocols + +// Formal protocol for doing unit testing of views. See description of +// GTMUnitTestView for details. +@protocol GTMUnitTestViewDrawer + +// Draw the view. Equivalent to drawRect on a standard NSView. +// +// Args: +// rect: the area to draw. +- (void)gtm_unitTestViewDrawRect:(NSRect)rect contextInfo:(void*)contextInfo; +@end + 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)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 +#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) +// 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 +#import +#import +#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 + +// 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 +#import "GTMDefines.h" +#import -/// Fails when image of |a1| does not equal image in TIFF file named |a2| +#if GTM_MACOS_SDK +#import +#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) -/// 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 -#include +#import #import "GTMNSObject+UnitTesting.h" -#import "GTMNSWorkspace+Theme.h" #import "GTMSystemVersion.h" +#import "GTMGarbageCollection.h" + +#if GTM_IPHONE_SDK +#import +#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 encoder = (id)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.h b/UnitTesting/GTMNSView+UnitTesting.h deleted file mode 100644 index fcda16b..0000000 --- a/UnitTesting/GTMNSView+UnitTesting.h +++ /dev/null @@ -1,138 +0,0 @@ -// -// 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. -// -// 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 -#import "GTMNSObject+UnitTesting.h" - -@protocol GTMUnitTestViewDrawer; - -/// Fails when the |a1|'s drawing in an area |a2| does not equal the TIFF 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 TIFF 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 a1Object = (a1); \ - NSSize a2Size = (a2); \ - NSString* a3String = (a3); \ - 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__)); \ - } 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 NSObject+UnitTesting.h for details. -@interface NSView (GTMUnitTestingAdditions) - -/// 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 -// -// 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; - -@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, 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 -@interface GTMUnitTestView : NSView { - id 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:(NSRect)frame drawer:(id)drawer contextInfo:(void*)contextInfo; -@end - -/// \cond Protocols - -// Formal protocol for doing unit testing of views. See description of -// GTMUnitTestView for details. -@protocol GTMUnitTestViewDrawer - -// Draw the view. Equivalent to drawRect on a standard NSView. -// -// Args: -// rect: the area to draw. -- (void)unitTestViewDrawRect:(NSRect)rect contextInfo:(void*)contextInfo; -@end - -/// \endcond 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 -#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)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 +#else +#import +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 + +@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 +#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 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) + +// 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 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)drawer contextInfo:(void*)contextInfo; + +@end + +/// \cond Protocols + +// Formal protocol for doing unit testing of views. See description of +// GTMUnitTestView for details. +@protocol GTMUnitTestViewDrawer + +// 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)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 +#import "GTMUIKit+UnitTesting.h" +#import "GTMSenTestCase.h" + +@interface GTMUIView_UnitTestingTest : SenTestCase +@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 @@ + + + + 0 + 9C31 + 644 + 949.26 + 352.00 + + YES + + + + YES + com.apple.InterfaceBuilderKit + com.apple.InterfaceBuilder.CocoaPlugin + + + YES + + NSApplication + + + FirstResponder + + + NSApplication + + + AMainMenu + + YES + + + NewApplication + + 1048576 + 2147483647 + + NSImage + NSMenuCheckmark + + + NSImage + NSMenuMixedState + + submenuAction: + + NewApplication + + YES + + + About NewApplication + + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + UHJlZmVyZW5jZXPigKY + , + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Services + + 1048576 + 2147483647 + + + submenuAction: + + Services + + YES + + _NSServicesMenu + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Hide NewApplication + h + 1048576 + 2147483647 + + + + + + Hide Others + h + 1572864 + 2147483647 + + + + + + Show All + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Quit NewApplication + q + 1048576 + 2147483647 + + + + + _NSAppleMenu + + + + + File + + 1048576 + 2147483647 + + + submenuAction: + + File + + YES + + + New + n + 1048576 + 2147483647 + + + + + + T3BlbuKApg + o + 1048576 + 2147483647 + + + + + + Open Recent + + 1048576 + 2147483647 + + + submenuAction: + + Open Recent + + YES + + + Clear Menu + + 1048576 + 2147483647 + + + + + _NSRecentDocumentsMenu + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Close + w + 1048576 + 2147483647 + + + + + + Save + s + 1048576 + 2147483647 + + + + + + U2F2ZSBBc+KApg + S + 1179648 + 2147483647 + + + + + + Revert to Saved + + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Page Setup... + P + 1179648 + 2147483647 + + + + + + + UHJpbnTigKY + p + 1048576 + 2147483647 + + + + + + + + + Edit + + 1048576 + 2147483647 + + + submenuAction: + + Edit + + YES + + + Undo + z + 1048576 + 2147483647 + + + + + + Redo + Z + 1179648 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Cut + x + 1048576 + 2147483647 + + + + + + Copy + c + 1048576 + 2147483647 + + + + + + Paste + v + 1048576 + 2147483647 + + + + + + Delete + + 1048576 + 2147483647 + + + + + + Select All + a + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Find + + 1048576 + 2147483647 + + + submenuAction: + + Find + + YES + + + RmluZOKApg + f + 1048576 + 2147483647 + + + 1 + + + + Find Next + g + 1048576 + 2147483647 + + + 2 + + + + Find Previous + G + 1179648 + 2147483647 + + + 3 + + + + Use Selection for Find + e + 1048576 + 2147483647 + + + 7 + + + + Jump to Selection + j + 1048576 + 2147483647 + + + + + + + + + Spelling and Grammar + + 1048576 + 2147483647 + + + submenuAction: + + Spelling and Grammar + + YES + + + U2hvdyBTcGVsbGluZ+KApg + : + 1048576 + 2147483647 + + + + + + Check Spelling + ; + 1048576 + 2147483647 + + + + + + Check Spelling While Typing + + 1048576 + 2147483647 + + + + + + Check Grammar With Spelling + + 1048576 + 2147483647 + + + + + + + + + Substitutions + + 1048576 + 2147483647 + + + submenuAction: + + Substitutions + + YES + + + Smart Copy/Paste + f + 1048576 + 2147483647 + + + 1 + + + + Smart Quotes + g + 1048576 + 2147483647 + + + 2 + + + + Smart Links + G + 1179648 + 2147483647 + + + 3 + + + + + + + Speech + + 1048576 + 2147483647 + + + submenuAction: + + Speech + + YES + + + Start Speaking + + 1048576 + 2147483647 + + + + + + Stop Speaking + + 1048576 + 2147483647 + + + + + + + + + + + + Format + + 1048576 + 2147483647 + + + submenuAction: + + Format + + YES + + + Show Fonts + t + 1048576 + 2147483647 + + + + + + Show Colors + C + 1179648 + 2147483647 + + + + + + + + + View + + 1048576 + 2147483647 + + + submenuAction: + + View + + YES + + + Show Toolbar + t + 1572864 + 2147483647 + + + + + + Q3VzdG9taXplIFRvb2xiYXLigKY + + 1048576 + 2147483647 + + + + + + + + + Help + + 1048576 + 2147483647 + + + submenuAction: + + Help + + YES + + + NewApplication Help + ? + 1048576 + 2147483647 + + + + + + + + _NSMainMenu + + + + + YES + + + print: + + + + 86 + + + + runPageLayout: + + + + 87 + + + + clearRecentDocuments: + + + + 127 + + + + orderFrontStandardAboutPanel: + + + + 142 + + + + performClose: + + + + 193 + + + + toggleContinuousSpellChecking: + + + + 222 + + + + undo: + + + + 223 + + + + copy: + + + + 224 + + + + checkSpelling: + + + + 225 + + + + paste: + + + + 226 + + + + stopSpeaking: + + + + 227 + + + + cut: + + + + 228 + + + + showGuessPanel: + + + + 230 + + + + redo: + + + + 231 + + + + selectAll: + + + + 232 + + + + startSpeaking: + + + + 233 + + + + delete: + + + + 235 + + + + performFindPanelAction: + + + + 241 + + + + centerSelectionInVisibleArea: + + + + 245 + + + + toggleGrammarChecking: + + + + 347 + + + + toggleSmartInsertDelete: + + + + 355 + + + + toggleAutomaticQuoteSubstitution: + + + + 356 + + + + toggleAutomaticLinkDetection: + + + + 357 + + + + showHelp: + + + + 360 + + + + orderFrontColorPanel: + + + + 361 + + + + saveDocument: + + + + 362 + + + + saveDocumentAs: + + + + 363 + + + + revertDocumentToSaved: + + + + 364 + + + + runToolbarCustomizationPalette: + + + + 365 + + + + toggleToolbarShown: + + + + 366 + + + + hide: + + + + 367 + + + + hideOtherApplications: + + + + 368 + + + + terminate: + + + + 369 + + + + unhideAllApplications: + + + + 370 + + + + newDocument: + + + + 373 + + + + openDocument: + + + + 374 + + + + + YES + + 0 + + YES + + + + + + -2 + + + RmlsZSdzIE93bmVyA + + + -1 + + + First Responder + + + -3 + + + Application + + + 29 + + + YES + + + + + + + + + MainMenu + + + 56 + + + YES + + + + + + 103 + + + YES + + + + 1 + + + 217 + + + YES + + + + + + 83 + + + YES + + + + + + 81 + + + YES + + + + + + + + + + + + + + + + 75 + + + 3 + + + 80 + + + 8 + + + 78 + + + 6 + + + 72 + + + + + 82 + + + 9 + + + 124 + + + YES + + + + + + 77 + + + 5 + + + 73 + + + 1 + + + 79 + + + 7 + + + 112 + + + 10 + + + 74 + + + 2 + + + 125 + + + YES + + + + + + 126 + + + + + 205 + + + YES + + + + + + + + + + + + + + + + + + 202 + + + + + 198 + + + + + 207 + + + + + 214 + + + + + 199 + + + + + 203 + + + + + 197 + + + + + 206 + + + + + 215 + + + + + 218 + + + YES + + + + + + 216 + + + YES + + + + + + 200 + + + YES + + + + + + + + + 219 + + + + + 201 + + + + + 204 + + + + + 220 + + + YES + + + + + + + + + + 213 + + + + + 210 + + + + + 221 + + + + + 208 + + + + + 209 + + + + + 106 + + + YES + + + + 2 + + + 111 + + + + + 57 + + + YES + + + + + + + + + + + + + + + + 58 + + + + + 134 + + + + + 150 + + + + + 136 + + + 1111 + + + 144 + + + + + 129 + + + 121 + + + 143 + + + + + 236 + + + + + 131 + + + YES + + + + + + 149 + + + + + 145 + + + + + 130 + + + + + 295 + + + YES + + + + + + 296 + + + YES + + + + + + + 297 + + + + + 298 + + + + + 299 + + + YES + + + + + + 300 + + + YES + + + + + + + 344 + + + + + 345 + + + + + 211 + + + YES + + + + + + 212 + + + YES + + + + + + + 195 + + + + + 196 + + + + + 346 + + + + + 348 + + + YES + + + + + + 349 + + + YES + + + + + + + + 350 + + + + + 351 + + + + + 354 + + + + + + + YES + + YES + -1.IBPluginDependency + -2.IBPluginDependency + -3.IBPluginDependency + 103.IBPluginDependency + 103.ImportedFromIB2 + 106.IBEditorWindowLastContentRect + 106.IBPluginDependency + 106.ImportedFromIB2 + 106.editorWindowContentRectSynchronizationRect + 111.IBPluginDependency + 111.ImportedFromIB2 + 112.IBPluginDependency + 112.ImportedFromIB2 + 124.IBPluginDependency + 124.ImportedFromIB2 + 125.IBPluginDependency + 125.ImportedFromIB2 + 125.editorWindowContentRectSynchronizationRect + 126.IBPluginDependency + 126.ImportedFromIB2 + 129.IBPluginDependency + 129.ImportedFromIB2 + 130.IBPluginDependency + 130.ImportedFromIB2 + 130.editorWindowContentRectSynchronizationRect + 131.IBPluginDependency + 131.ImportedFromIB2 + 134.IBPluginDependency + 134.ImportedFromIB2 + 136.IBPluginDependency + 136.ImportedFromIB2 + 143.IBPluginDependency + 143.ImportedFromIB2 + 144.IBPluginDependency + 144.ImportedFromIB2 + 145.IBPluginDependency + 145.ImportedFromIB2 + 149.IBPluginDependency + 149.ImportedFromIB2 + 150.IBPluginDependency + 150.ImportedFromIB2 + 195.IBPluginDependency + 195.ImportedFromIB2 + 196.IBPluginDependency + 196.ImportedFromIB2 + 197.IBPluginDependency + 197.ImportedFromIB2 + 198.IBPluginDependency + 198.ImportedFromIB2 + 199.IBPluginDependency + 199.ImportedFromIB2 + 200.IBPluginDependency + 200.ImportedFromIB2 + 200.editorWindowContentRectSynchronizationRect + 201.IBPluginDependency + 201.ImportedFromIB2 + 202.IBPluginDependency + 202.ImportedFromIB2 + 203.IBPluginDependency + 203.ImportedFromIB2 + 204.IBPluginDependency + 204.ImportedFromIB2 + 205.IBPluginDependency + 205.ImportedFromIB2 + 205.editorWindowContentRectSynchronizationRect + 206.IBPluginDependency + 206.ImportedFromIB2 + 207.IBPluginDependency + 207.ImportedFromIB2 + 208.IBPluginDependency + 208.ImportedFromIB2 + 209.IBPluginDependency + 209.ImportedFromIB2 + 210.IBPluginDependency + 210.ImportedFromIB2 + 211.IBPluginDependency + 211.ImportedFromIB2 + 212.IBPluginDependency + 212.ImportedFromIB2 + 212.editorWindowContentRectSynchronizationRect + 213.IBPluginDependency + 213.ImportedFromIB2 + 214.IBPluginDependency + 214.ImportedFromIB2 + 215.IBPluginDependency + 215.ImportedFromIB2 + 216.IBPluginDependency + 216.ImportedFromIB2 + 217.IBPluginDependency + 217.ImportedFromIB2 + 218.IBPluginDependency + 218.ImportedFromIB2 + 219.IBPluginDependency + 219.ImportedFromIB2 + 220.IBPluginDependency + 220.ImportedFromIB2 + 220.editorWindowContentRectSynchronizationRect + 221.IBPluginDependency + 221.ImportedFromIB2 + 236.IBPluginDependency + 236.ImportedFromIB2 + 29.IBEditorWindowLastContentRect + 29.IBPluginDependency + 29.ImportedFromIB2 + 29.WindowOrigin + 29.editorWindowContentRectSynchronizationRect + 295.IBPluginDependency + 296.IBEditorWindowLastContentRect + 296.IBPluginDependency + 296.editorWindowContentRectSynchronizationRect + 297.IBPluginDependency + 298.IBPluginDependency + 299.IBPluginDependency + 300.IBPluginDependency + 300.editorWindowContentRectSynchronizationRect + 344.IBPluginDependency + 345.IBPluginDependency + 346.IBPluginDependency + 346.ImportedFromIB2 + 348.IBPluginDependency + 348.ImportedFromIB2 + 349.IBPluginDependency + 349.ImportedFromIB2 + 349.editorWindowContentRectSynchronizationRect + 350.IBPluginDependency + 350.ImportedFromIB2 + 351.IBPluginDependency + 351.ImportedFromIB2 + 354.IBPluginDependency + 354.ImportedFromIB2 + 56.IBPluginDependency + 56.ImportedFromIB2 + 57.IBEditorWindowLastContentRect + 57.IBPluginDependency + 57.ImportedFromIB2 + 57.editorWindowContentRectSynchronizationRect + 58.IBPluginDependency + 58.ImportedFromIB2 + 72.IBPluginDependency + 72.ImportedFromIB2 + 73.IBPluginDependency + 73.ImportedFromIB2 + 74.IBPluginDependency + 74.ImportedFromIB2 + 75.IBPluginDependency + 75.ImportedFromIB2 + 77.IBPluginDependency + 77.ImportedFromIB2 + 78.IBPluginDependency + 78.ImportedFromIB2 + 79.IBPluginDependency + 79.ImportedFromIB2 + 80.IBPluginDependency + 80.ImportedFromIB2 + 81.IBEditorWindowLastContentRect + 81.IBPluginDependency + 81.ImportedFromIB2 + 81.editorWindowContentRectSynchronizationRect + 82.IBPluginDependency + 82.ImportedFromIB2 + 83.IBPluginDependency + 83.ImportedFromIB2 + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilderKit + com.apple.InterfaceBuilderKit + com.apple.InterfaceBuilder.CocoaPlugin + + {{417, 1069}, {216, 23}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{596, 852}, {216, 23}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{522, 812}, {146, 23}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{436, 809}, {64, 6}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{608, 612}, {275, 83}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{187, 434}, {243, 243}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{608, 612}, {167, 43}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{608, 612}, {241, 103}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{0, 1092}, {407, 20}} + com.apple.InterfaceBuilder.CocoaPlugin + + {74, 862} + {{6, 978}, {478, 20}} + com.apple.InterfaceBuilder.CocoaPlugin + {{296, 1049}, {234, 43}} + com.apple.InterfaceBuilder.CocoaPlugin + {{475, 832}, {234, 43}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{231, 634}, {176, 43}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{608, 612}, {215, 63}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{12, 909}, {245, 183}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{23, 794}, {245, 183}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{144, 889}, {199, 203}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{145, 474}, {199, 203}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + + + + YES + + YES + + + YES + + + + + YES + + YES + + + YES + + + + 374 + + + + YES + + NSApplication + + IBProjectSource + UnitTesting/GTMNSApplication+UnitTesting.h + + + + NSMenu + + IBProjectSource + UnitTesting/GTMNSMenu+UnitTesting.h + + + + NSMenuItem + + IBProjectSource + UnitTesting/GTMNSMenuItem+UnitTesting.h + + + + NSObject + + IBProjectSource + AppKit/GTMDelegatingTableColumn.h + + + + NSObject + + IBProjectSource + UnitTesting/GTMNSObject+BindingUnitTesting.h + + + + NSObject + + IBProjectSource + UnitTesting/GTMNSObject+UnitTesting.h + + + + NSObject + + IBProjectSource + UnitTesting/GTMSenTestCase.h + + + + NSView + + IBProjectSource + UnitTesting/GTMNSView+UnitTesting.h + + + + + 0 + ../../../GTM.xcodeproj + 3 + + 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 Binary files /dev/null and b/UnitTesting/GTMUIUnitTestingHarness/English.lproj/MainMenu.nib/keyedobjects.nib 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 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + CFBundleIdentifier + com.google.GTMUIUnitTestingHarness + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + 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 +#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 @@ + + + + + $GTMArchive + GTMUnitTestingArchive + $GTMVersion + 1 + LayerIsDoublesided + + LayerIsHidden + + LayerIsOpaque + + LayerOpacity + 1 + ViewIsHidden + + ViewSubView 0 + + LayerIsDoublesided + + LayerIsHidden + + LayerIsOpaque + + LayerOpacity + 1 + ViewIsHidden + + + + diff --git a/UnitTesting/GTMUIViewUnitTestingTest.png b/UnitTesting/GTMUIViewUnitTestingTest.png new file mode 100644 index 0000000..03fd9f0 Binary files /dev/null and b/UnitTesting/GTMUIViewUnitTestingTest.png 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 +#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 @@ + + + + + $GTMArchive + GTMUnitTestingArchive + $GTMVersion + 1 + BoolTest + + BytesTest + + Qnl0ZXNUZXN0 + + DoubleTest + 1 + FloatTest + 1 + ImageName + NSApplicationIcon + ImageSize + {128, 128} + Int32Test + 1 + Int64Test + 1 + IntTest + 1 + + diff --git a/UnitTesting/GTMUnitTestingImage.tiff b/UnitTesting/GTMUnitTestingImage.tiff new file mode 100644 index 0000000..4d08297 Binary files /dev/null and b/UnitTesting/GTMUnitTestingImage.tiff 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 + +// 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 +#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 { + 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 @@ + + + + + IBClasses + + + CLASS + NSView + LANGUAGE + ObjC + SUPERCLASS + NSResponder + + + CLASS + NSApplication + LANGUAGE + ObjC + SUPERCLASS + NSResponder + + + CLASS + NSTextField + LANGUAGE + ObjC + SUPERCLASS + NSControl + + + CLASS + NSMenu + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSControl + LANGUAGE + ObjC + SUPERCLASS + NSView + + + CLASS + NSObject + LANGUAGE + ObjC + + + CLASS + NSCell + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSWindow + LANGUAGE + ObjC + SUPERCLASS + NSResponder + + + CLASS + GTMUnitTestingTestController + LANGUAGE + ObjC + OUTLETS + + field_ + NSTextField + + SUPERCLASS + NSWindowController + + + IBVersion + 1 + + 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 @@ + + + + + IBFramework Version + 644 + IBLastKnownRelativeProjectPath + ../GTM.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 2 + + IBSystem Version + 9C31 + targetFramework + IBCocoaFramework + + diff --git a/UnitTesting/GTMUnitTestingTest.nib/keyedobjects.nib b/UnitTesting/GTMUnitTestingTest.nib/keyedobjects.nib new file mode 100644 index 0000000..d3104e2 Binary files /dev/null and b/UnitTesting/GTMUnitTestingTest.nib/keyedobjects.nib 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 @@ + + + + + $GTMArchive + GTMUnitTestingArchive + $GTMVersion + 1 + ApplicationMainWindow + 0 + BoolTest + + BytesTest + + Qnl0ZXNUZXN0 + + DoubleTest + 1 + FloatTest + 1 + Int32Test + 1 + Int64Test + 1 + IntTest + 1 + MenuBar + + MenuItem 0 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemSubmenu + + MenuItem 0 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + About NewApplication + + MenuItem 1 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + + + MenuItem 10 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + q + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Quit NewApplication + + MenuItem 2 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + , + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Preferences… + + MenuItem 3 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + + + MenuItem 4 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemSubmenu + + MenuTitle + Services + + MenuItemTag + 0 + MenuItemTitle + Services + + MenuItem 5 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + + + MenuItem 6 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + h + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Hide NewApplication + + MenuItem 7 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + h + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Hide Others + + MenuItem 8 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Show All + + MenuItem 9 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + + + MenuTitle + Apple + + MenuItemTag + 0 + MenuItemTitle + + + MenuItem 1 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemSubmenu + + MenuItem 0 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + n + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + New + + MenuItem 1 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + o + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Open… + + MenuItem 10 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + P + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Page Setup... + MenuItemTooltip + + + MenuItem 11 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + p + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Print… + + MenuItem 2 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemSubmenu + + MenuItem 0 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Clear Menu + + MenuTitle + Open Recent + + MenuItemTag + 0 + MenuItemTitle + Open Recent + + MenuItem 3 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + + + MenuItem 4 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + w + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Close + + MenuItem 5 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + w + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Close All + + MenuItem 6 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + s + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Save + + MenuItem 7 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + S + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Save As… + + MenuItem 8 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Revert to Saved + + MenuItem 9 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + + + MenuTitle + File + + MenuItemTag + 0 + MenuItemTitle + File + + MenuItem 2 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemSubmenu + + MenuItem 0 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + z + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Undo + + MenuItem 1 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + Z + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Redo + + MenuItem 10 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemSubmenu + + MenuItem 0 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + : + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Show Spelling… + + MenuItem 1 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + ; + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Check Spelling + + MenuItem 2 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Check Spelling While Typing + + MenuItem 3 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Check Grammar With Spelling + + MenuTitle + Spelling and Grammar + + MenuItemTag + 0 + MenuItemTitle + Spelling and Grammar + + MenuItem 11 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemSubmenu + + MenuItem 0 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemTag + 1 + MenuItemTitle + Smart Copy/Paste + + MenuItem 1 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemTag + 2 + MenuItemTitle + Smart Quotes + + MenuItem 2 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemTag + 3 + MenuItemTitle + Smart Links + + MenuTitle + Substitutions + + MenuItemTag + 0 + MenuItemTitle + Substitutions + + MenuItem 12 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemSubmenu + + MenuItem 0 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Start Speaking + + MenuItem 1 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Stop Speaking + + MenuTitle + Speech + + MenuItemTag + 0 + MenuItemTitle + Speech + + MenuItem 13 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + + + MenuItem 14 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Special Characters… + + MenuItem 2 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + + + MenuItem 3 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + x + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Cut + + MenuItem 4 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + c + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Copy + + MenuItem 5 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + v + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Paste + + MenuItem 6 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Delete + + MenuItem 7 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + a + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Select All + + MenuItem 8 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + + + MenuItem 9 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemSubmenu + + MenuItem 0 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + f + MenuItemState + 0 + MenuItemTag + 1 + MenuItemTitle + Find… + + MenuItem 1 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + g + MenuItemState + 0 + MenuItemTag + 2 + MenuItemTitle + Find Next + + MenuItem 2 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + G + MenuItemState + 0 + MenuItemTag + 3 + MenuItemTitle + Find Previous + + MenuItem 3 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + e + MenuItemState + 0 + MenuItemTag + 7 + MenuItemTitle + Use Selection for Find + + MenuItem 4 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + j + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Jump to Selection + + MenuTitle + Find + + MenuItemTag + 0 + MenuItemTitle + Find + + MenuTitle + Edit + + MenuItemTag + 0 + MenuItemTitle + Edit + + MenuItem 3 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemSubmenu + + MenuItem 0 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + t + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Show Fonts + + MenuItem 1 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + C + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Show Colors + + MenuTitle + Format + + MenuItemTag + 0 + MenuItemTitle + Format + + MenuItem 4 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemSubmenu + + MenuItem 0 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + t + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Show Toolbar + + MenuItem 1 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + Customize Toolbar… + + MenuTitle + View + + MenuItemTag + 0 + MenuItemTitle + View + + MenuItem 5 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + + MenuItemState + 0 + MenuItemSubmenu + + MenuItem 0 + + MenuItemIndentationLevel + 0 + MenuItemIsAlternate + + MenuItemIsEnabled + + MenuItemIsSeparator + + MenuItemKeyEquivalent + ? + MenuItemState + 0 + MenuItemTag + 0 + MenuItemTitle + NewApplication Help + + MenuTitle + Help + + MenuItemTag + 0 + MenuItemTitle + Help + + MenuTitle + AMainMenu + + Window 0 + + WindowContent + + ViewIsHidden + + ViewSubView 0 + + ViewIsHidden + + ViewSubView 0 + + ViewIsHidden + + ViewSubView 0 + + ControlIsEnabled + + ControlTag + 0 + ControlType + NSTableView + ViewIsHidden + + + + ViewSubView 1 + + ControlIsEnabled + + ControlTag + 0 + ControlType + NSScroller + ViewIsHidden + + + ViewSubView 2 + + ControlIsEnabled + + ControlTag + 0 + ControlType + NSScroller + ViewIsHidden + + + ViewSubView 3 + + ViewIsHidden + + ViewSubView 0 + + ViewIsHidden + + + + ViewSubView 4 + + ViewIsHidden + + + + ViewSubView 1 + + ViewIsHidden + + + ViewSubView 10 + + ViewIsHidden + + ViewSubView 0 + + ViewIsHidden + + ViewSubView 0 + + ViewIsHidden + + + + ViewSubView 1 + + ControlIsEnabled + + ControlTag + 0 + ControlType + NSScroller + ViewIsHidden + + + ViewSubView 2 + + ControlIsEnabled + + ControlTag + 0 + ControlType + NSScroller + ViewIsHidden + + + + ViewSubView 11 + + ViewIsHidden + + ViewSubView 0 + + ViewIsHidden + + + + ViewSubView 2 + + ControlIsEnabled + + ControlSelectedCell + + CellState + 0 + CellTag + 0 + CellTitle + HaHa + CellValue + HaHa + + ControlTag + 0 + ControlType + NSTextField + ControlValue + HaHa + ViewIsHidden + + + ViewSubView 3 + + ControlIsEnabled + + ControlSelectedCell + + CellState + 0 + CellTag + 0 + CellTitle + Still Haven't Found What I'm Searching For + CellValue + Still Haven't Found What I'm Searching For + + ControlTag + 0 + ControlType + NSSearchField + ControlValue + Still Haven't Found What I'm Searching For + ViewIsHidden + + + ViewSubView 4 + + ControlIsEnabled + + ControlSelectedCell + + CellState + 1 + CellTag + 0 + CellTitle + Foo + CellValue + Foo + + ControlTag + 0 + ControlType + NSTextField + ControlValue + Foo + ViewIsHidden + + + ViewSubView 5 + + ControlIsEnabled + + ControlSelectedCell + + CellState + 1 + CellTag + 0 + CellTitle + CheckMate! + CellValue + 1 + + ControlTag + 0 + ControlType + NSButton + ControlValue + 1 + ViewIsHidden + + + ViewSubView 6 + + ControlIsEnabled + + ControlSelectedCell + + CellState + 1 + CellTag + 0 + CellTitle + + CellValue + 50 + + ControlTag + 0 + ControlType + NSSlider + ControlValue + 50 + ViewIsHidden + + + ViewSubView 7 + + ControlIsEnabled + + ControlSelectedCell + + CellState + 0 + CellTag + 0 + CellTitle + Cancel + CellValue + 0 + + ControlTag + 0 + ControlType + NSButton + ControlValue + 0 + ViewIsHidden + + + ViewSubView 8 + + ControlIsEnabled + + ControlTag + 0 + ControlType + NSColorWell + ViewIsHidden + + + ViewSubView 9 + + ViewIsHidden + + + + WindowIsMain + + WindowIsVisible + + WindowTitle + Window + + Window 1 + + WindowContent + + ViewIsHidden + + ViewSubView 0 + + ViewIsHidden + + ViewSubView 0 + + ViewIsHidden + + ViewSubView 0 + + ControlIsEnabled + + ControlTag + 0 + ControlType + NSTableView + ViewIsHidden + + + + ViewSubView 1 + + ControlIsEnabled + + ControlTag + 0 + ControlType + NSScroller + ViewIsHidden + + + ViewSubView 2 + + ControlIsEnabled + + ControlTag + 0 + ControlType + NSScroller + ViewIsHidden + + + ViewSubView 3 + + ViewIsHidden + + ViewSubView 0 + + ViewIsHidden + + + + ViewSubView 4 + + ViewIsHidden + + + + ViewSubView 1 + + ViewIsHidden + + + ViewSubView 10 + + ViewIsHidden + + ViewSubView 0 + + ViewIsHidden + + ViewSubView 0 + + ViewIsHidden + + + + ViewSubView 1 + + ControlIsEnabled + + ControlTag + 0 + ControlType + NSScroller + ViewIsHidden + + + ViewSubView 2 + + ControlIsEnabled + + ControlTag + 0 + ControlType + NSScroller + ViewIsHidden + + + + ViewSubView 11 + + ViewIsHidden + + ViewSubView 0 + + ViewIsHidden + + + + ViewSubView 2 + + ControlIsEnabled + + ControlSelectedCell + + CellState + 0 + CellTag + 0 + CellTitle + HaHa + CellValue + HaHa + + ControlTag + 0 + ControlType + NSTextField + ControlValue + HaHa + ViewIsHidden + + + ViewSubView 3 + + ControlIsEnabled + + ControlSelectedCell + + CellState + 0 + CellTag + 0 + CellTitle + Still Haven't Found What I'm Searching For + CellValue + Still Haven't Found What I'm Searching For + + ControlTag + 0 + ControlType + NSSearchField + ControlValue + Still Haven't Found What I'm Searching For + ViewIsHidden + + + ViewSubView 4 + + ControlIsEnabled + + ControlSelectedCell + + CellState + 1 + CellTag + 0 + CellTitle + Once upon a time + CellValue + Once upon a time + + ControlTag + 0 + ControlType + NSTextField + ControlValue + Once upon a time + ViewIsHidden + + + ViewSubView 5 + + ControlIsEnabled + + ControlSelectedCell + + CellState + 1 + CellTag + 0 + CellTitle + CheckMate! + CellValue + 1 + + ControlTag + 0 + ControlType + NSButton + ControlValue + 1 + ViewIsHidden + + + ViewSubView 6 + + ControlIsEnabled + + ControlSelectedCell + + CellState + 1 + CellTag + 0 + CellTitle + + CellValue + 50 + + ControlTag + 0 + ControlType + NSSlider + ControlValue + 50 + ViewIsHidden + + + ViewSubView 7 + + ControlIsEnabled + + ControlSelectedCell + + CellState + 0 + CellTag + 0 + CellTitle + Cancel + CellValue + 0 + + ControlTag + 0 + ControlType + NSButton + ControlValue + 0 + ViewIsHidden + + + ViewSubView 8 + + ControlIsEnabled + + ControlTag + 0 + ControlType + NSColorWell + ViewIsHidden + + + ViewSubView 9 + + ViewIsHidden + + + + WindowIsMain + + WindowIsVisible + + WindowTitle + Window + + + 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 + +// 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 +#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 Binary files /dev/null and b/UnitTesting/GTMUnitTestingView.tiff 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 @@ + + + + + $GTMArchive + GTMUnitTestingArchive + $GTMVersion + 1 + WindowContent + + ViewIsHidden + + ViewSubView 0 + + ViewIsHidden + + ViewSubView 0 + + ViewIsHidden + + ViewSubView 0 + + ControlIsEnabled + + ControlTag + 0 + ControlType + NSTableView + ViewIsHidden + + + + ViewSubView 1 + + ControlIsEnabled + + ControlTag + 0 + ControlType + NSScroller + ViewIsHidden + + + ViewSubView 2 + + ControlIsEnabled + + ControlTag + 0 + ControlType + NSScroller + ViewIsHidden + + + ViewSubView 3 + + ViewIsHidden + + ViewSubView 0 + + ViewIsHidden + + + + ViewSubView 4 + + ViewIsHidden + + + + ViewSubView 1 + + ViewIsHidden + + + ViewSubView 10 + + ViewIsHidden + + ViewSubView 0 + + ViewIsHidden + + ViewSubView 0 + + ViewIsHidden + + + + ViewSubView 1 + + ControlIsEnabled + + ControlTag + 0 + ControlType + NSScroller + ViewIsHidden + + + ViewSubView 2 + + ControlIsEnabled + + ControlTag + 0 + ControlType + NSScroller + ViewIsHidden + + + + ViewSubView 11 + + ViewIsHidden + + ViewSubView 0 + + ViewIsHidden + + + + ViewSubView 2 + + ControlIsEnabled + + ControlSelectedCell + + CellState + 0 + CellTag + 0 + CellTitle + HaHa + CellValue + HaHa + + ControlTag + 0 + ControlType + NSTextField + ControlValue + HaHa + ViewIsHidden + + + ViewSubView 3 + + ControlIsEnabled + + ControlSelectedCell + + CellState + 0 + CellTag + 0 + CellTitle + Still Haven't Found What I'm Searching For + CellValue + Still Haven't Found What I'm Searching For + + ControlTag + 0 + ControlType + NSSearchField + ControlValue + Still Haven't Found What I'm Searching For + ViewIsHidden + + + ViewSubView 4 + + ControlIsEnabled + + ControlSelectedCell + + CellState + 1 + CellTag + 0 + CellTitle + Once upon a time + CellValue + Once upon a time + + ControlTag + 0 + ControlType + NSTextField + ControlValue + Once upon a time + ViewIsHidden + + + ViewSubView 5 + + ControlIsEnabled + + ControlSelectedCell + + CellState + 1 + CellTag + 0 + CellTitle + CheckMate! + CellValue + 1 + + ControlTag + 0 + ControlType + NSButton + ControlValue + 1 + ViewIsHidden + + + ViewSubView 6 + + ControlIsEnabled + + ControlSelectedCell + + CellState + 1 + CellTag + 0 + CellTitle + + CellValue + 50 + + ControlTag + 0 + ControlType + NSSlider + ControlValue + 50 + ViewIsHidden + + + ViewSubView 7 + + ControlIsEnabled + + ControlSelectedCell + + CellState + 0 + CellTag + 0 + CellTitle + Cancel + CellValue + 0 + + ControlTag + 0 + ControlType + NSButton + ControlValue + 0 + ViewIsHidden + + + ViewSubView 8 + + ControlIsEnabled + + ControlTag + 0 + ControlType + NSColorWell + ViewIsHidden + + + ViewSubView 9 + + ViewIsHidden + + + + WindowIsMain + + WindowIsVisible + + WindowTitle + Window + + diff --git a/UnitTesting/GTMUnitTestingWindow.tiff b/UnitTesting/GTMUnitTestingWindow.tiff new file mode 100644 index 0000000..63f5649 Binary files /dev/null and b/UnitTesting/GTMUnitTestingWindow.tiff 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/DebugTigerOrLater.xcconfig b/XcodeConfig/DebugTigerOrLater.xcconfig deleted file mode 100644 index 7a78a13..0000000 --- a/XcodeConfig/DebugTigerOrLater.xcconfig +++ /dev/null @@ -1,34 +0,0 @@ -// -// DebugTigerOrLater.xcconfig -// -// Xcode configuration file for building a Debug target on Tiger or later. -// -// This is a _Configuration_ Xcode config file for use in the "Based on" popup -// of the project configuration editor. Do _not_ use this as the config base -// 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" - -// Tiger or later -#include "subconfig/TigerOrLater.xcconfig" - -// Debug settings -#include "subconfig/Debug.xcconfig" - diff --git a/XcodeConfig/DebugUnittest.xcconfig b/XcodeConfig/DebugUnittest.xcconfig deleted file mode 100644 index c32c63d..0000000 --- a/XcodeConfig/DebugUnittest.xcconfig +++ /dev/null @@ -1,29 +0,0 @@ -// -// DebugUnittest.xcconfig -// -// Xcode configuration file for a debug 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. -// -// 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. -// - -// Unittests are loadable bundles -#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/LoadableBundle.xcconfig deleted file mode 100644 index 78ed39a..0000000 --- a/XcodeConfig/LoadableBundle.xcconfig +++ /dev/null @@ -1,30 +0,0 @@ -// -// LoadableBundle.xcconfig -// -// Xcode configuration file for a loadable bundle. Usually a Cocoa plugin or -// similar. -// -// This is a _Target_ config file, for use in the "Based on" popup of the -// settings dialog for a target. Do not attempt to apply this as the base -// of an Xcode configuration in the project settings dialog. -// -// Copyright 2006-2008 Google Inc. -// -// 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. -// - -// Bundles should not have their external symbols stripped. -STRIP_STYLE = non-global - -// Bundles need to be position independent -GCC_DYNAMIC_NO_PIC = NO 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/Project/DebugTigerOrLater.xcconfig b/XcodeConfig/Project/DebugTigerOrLater.xcconfig new file mode 100644 index 0000000..76d2e27 --- /dev/null +++ b/XcodeConfig/Project/DebugTigerOrLater.xcconfig @@ -0,0 +1,34 @@ +// +// DebugTigerOrLater.xcconfig +// +// Xcode configuration file for building a Debug target on Tiger or later. +// +// This is a _Configuration_ Xcode config file for use in the "Based on" popup +// of the project configuration editor. Do _not_ use this as the config base +// 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" + +// Tiger or later +#include "../subconfig/TigerOrLater.xcconfig" + +// Debug settings +#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/Project/ReleaseTigerOrLater.xcconfig b/XcodeConfig/Project/ReleaseTigerOrLater.xcconfig new file mode 100644 index 0000000..d14e739 --- /dev/null +++ b/XcodeConfig/Project/ReleaseTigerOrLater.xcconfig @@ -0,0 +1,33 @@ +// +// ReleaseTigerOrLater.xcconfig +// +// Xcode configuration file for building a Release target on Tiger or later. +// +// This is a _Configuration_ Xcode config file for use in the "Based on" popup +// of the project configuration editor. Do _not_ use this as the config base +// 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" + +// Tiger or later +#include "../subconfig/TigerOrLater.xcconfig" + +// Release settings +#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/ReleaseTigerOrLater.xcconfig b/XcodeConfig/ReleaseTigerOrLater.xcconfig deleted file mode 100644 index ee30238..0000000 --- a/XcodeConfig/ReleaseTigerOrLater.xcconfig +++ /dev/null @@ -1,33 +0,0 @@ -// -// ReleaseTigerOrLater.xcconfig -// -// Xcode configuration file for building a Release target on Tiger or later. -// -// This is a _Configuration_ Xcode config file for use in the "Based on" popup -// of the project configuration editor. Do _not_ use this as the config base -// 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" - -// Tiger or later -#include "subconfig/TigerOrLater.xcconfig" - -// Release settings -#include "subconfig/Release.xcconfig" diff --git a/XcodeConfig/ReleaseUnittest.xcconfig b/XcodeConfig/ReleaseUnittest.xcconfig deleted file mode 100644 index 9ec14de..0000000 --- a/XcodeConfig/ReleaseUnittest.xcconfig +++ /dev/null @@ -1,37 +0,0 @@ -// -// ReleaseUnittest.xcconfig -// -// Xcode configuration file for a release 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. -// -// 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. -// - -// Unittests are loadable bundles -#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 -// config files). In that case the Unittest will fail to link because -// the symbols have been removed from the bundle's loader's symbol table. -// This flag tells the unittest to trust that the values will be available -// at runtime (or error out) and not to force an error at link time. -// Do NOT set BUNDLE_LOADER (Bundle Loader) setting in a release unittest target -// because you will run into interesting link issues -// "indirect symbol table entry n past the end of the symbol table" -OTHER_LDFLAGS = $(OTHER_LDFLAGS) -undefined dynamic_lookup diff --git a/XcodeConfig/SharedLibrary.xcconfig b/XcodeConfig/SharedLibrary.xcconfig deleted file mode 100644 index 7593392..0000000 --- a/XcodeConfig/SharedLibrary.xcconfig +++ /dev/null @@ -1,29 +0,0 @@ -// -// SharedLibrary.xcconfig -// -// Xcode configuration file for a shared library. -// -// This is a _Target_ config file, for use in the "Based on" popup of the -// settings dialog for a target. Do not attempt to apply this as the base -// of an Xcode configuration in the project settings dialog. -// -// Copyright 2006-2008 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// - -// Dynamic libs need to be position independent -GCC_DYNAMIC_NO_PIC = NO - -// Dynamic libs should not have their external symbols stripped. -STRIP_STYLE = non-global \ No newline at end of file diff --git a/XcodeConfig/StaticLibrary.xcconfig b/XcodeConfig/StaticLibrary.xcconfig deleted file mode 100644 index f459e83..0000000 --- a/XcodeConfig/StaticLibrary.xcconfig +++ /dev/null @@ -1,30 +0,0 @@ -// -// StaticLibrary.xcconfig -// -// Xcode configuration file for a static library. -// -// This is a _Target_ config file, for use in the "Based on" popup of the -// settings dialog for a target. Do not attempt to apply this as the base -// of an Xcode configuration in the project settings dialog. -// -// Copyright 2006-2008 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// - -// Static libs can be included in bundles so make them position independent -GCC_DYNAMIC_NO_PIC = NO - -// Static libs should not have their internal globals or external symbols -// stripped. -STRIP_STYLE = debugging diff --git a/XcodeConfig/Target/DebugUnittest.xcconfig b/XcodeConfig/Target/DebugUnittest.xcconfig new file mode 100644 index 0000000..3f4d342 --- /dev/null +++ b/XcodeConfig/Target/DebugUnittest.xcconfig @@ -0,0 +1,29 @@ +// +// DebugUnittest.xcconfig +// +// Xcode configuration file for a debug 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. +// +// 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. +// + +// Unittests are loadable bundles +#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/Target/LoadableBundle.xcconfig b/XcodeConfig/Target/LoadableBundle.xcconfig new file mode 100644 index 0000000..78ed39a --- /dev/null +++ b/XcodeConfig/Target/LoadableBundle.xcconfig @@ -0,0 +1,30 @@ +// +// LoadableBundle.xcconfig +// +// Xcode configuration file for a loadable bundle. Usually a Cocoa plugin or +// similar. +// +// This is a _Target_ config file, for use in the "Based on" popup of the +// settings dialog for a target. Do not attempt to apply this as the base +// of an Xcode configuration in the project settings dialog. +// +// Copyright 2006-2008 Google Inc. +// +// 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. +// + +// Bundles should not have their external symbols stripped. +STRIP_STYLE = non-global + +// Bundles need to be position independent +GCC_DYNAMIC_NO_PIC = NO diff --git a/XcodeConfig/Target/ReleaseUnittest.xcconfig b/XcodeConfig/Target/ReleaseUnittest.xcconfig new file mode 100644 index 0000000..f9a85d3 --- /dev/null +++ b/XcodeConfig/Target/ReleaseUnittest.xcconfig @@ -0,0 +1,37 @@ +// +// ReleaseUnittest.xcconfig +// +// Xcode configuration file for a release 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. +// +// 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. +// + +// Unittests are loadable bundles +#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 +// config files). In that case the Unittest will fail to link because +// the symbols have been removed from the bundle's loader's symbol table. +// This flag tells the unittest to trust that the values will be available +// at runtime (or error out) and not to force an error at link time. +// Do NOT set BUNDLE_LOADER (Bundle Loader) setting in a release unittest target +// because you will run into interesting link issues +// "indirect symbol table entry n past the end of the symbol table" +OTHER_LDFLAGS = $(OTHER_LDFLAGS) -undefined dynamic_lookup diff --git a/XcodeConfig/Target/SharedLibrary.xcconfig b/XcodeConfig/Target/SharedLibrary.xcconfig new file mode 100644 index 0000000..7593392 --- /dev/null +++ b/XcodeConfig/Target/SharedLibrary.xcconfig @@ -0,0 +1,29 @@ +// +// SharedLibrary.xcconfig +// +// Xcode configuration file for a shared library. +// +// This is a _Target_ config file, for use in the "Based on" popup of the +// settings dialog for a target. Do not attempt to apply this as the base +// of an Xcode configuration in the project settings dialog. +// +// Copyright 2006-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +// Dynamic libs need to be position independent +GCC_DYNAMIC_NO_PIC = NO + +// Dynamic libs should not have their external symbols stripped. +STRIP_STYLE = non-global \ No newline at end of file diff --git a/XcodeConfig/Target/StaticLibrary.xcconfig b/XcodeConfig/Target/StaticLibrary.xcconfig new file mode 100644 index 0000000..f459e83 --- /dev/null +++ b/XcodeConfig/Target/StaticLibrary.xcconfig @@ -0,0 +1,30 @@ +// +// StaticLibrary.xcconfig +// +// Xcode configuration file for a static library. +// +// This is a _Target_ config file, for use in the "Based on" popup of the +// settings dialog for a target. Do not attempt to apply this as the base +// of an Xcode configuration in the project settings dialog. +// +// Copyright 2006-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +// Static libs can be included in bundles so make them position independent +GCC_DYNAMIC_NO_PIC = NO + +// Static libs should not have their internal globals or external symbols +// stripped. +STRIP_STYLE = debugging 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. -- cgit v1.2.3