aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar thomasvl <thomasvl@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2008-06-13 19:21:50 +0000
committerGravatar thomasvl <thomasvl@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2008-06-13 19:21:50 +0000
commitc53ec5520e39096e0804ce8d89a21378c0904481 (patch)
treed36a0055b59b1376d86c4ba4a01f9c479c2101a7
parent80d493da05c8d461d74bfaa919ffc487be03ffe6 (diff)
Landing a log of AppleScript/AppleEvent support code.
Landing GTMHTTPServer as a simple server but mainly for use in unittesting. _GTMCompileAssert for doing compile time assertions to GTMDefines.h Lots of improvments for UnitTesting, Dave's gonna put up a wiki page shortly with the full details of what can be done.
-rw-r--r--AppKit/GTMLinearRGBShading.m16
-rw-r--r--AppKit/GTMLinearRGBShadingTest.m32
-rw-r--r--AppKit/GTMLoginItems.m16
-rw-r--r--AppKit/GTMLoginItemsTest.m2
-rw-r--r--AppKit/GTMNSBezierPath+CGPathTest.m7
-rw-r--r--AppKit/GTMNSBezierPath+RoundRectTest.m6
-rw-r--r--AppKit/GTMNSBezierPath+Shading.m4
-rw-r--r--AppKit/GTMNSBezierPath+ShadingTest.m7
-rw-r--r--AppKit/GTMNSWorkspace+ScreenSaver.m8
-rw-r--r--AppKit/GTMNSWorkspace+ScreenSaverTest.m4
-rwxr-xr-xBuildScripts/BuildAllSDKs.sh54
-rw-r--r--BuildingAndUsing.txt18
-rw-r--r--DebugUtils/GTMDebugSelectorValidation.h22
-rw-r--r--DebugUtils/GTMDevLog.m (renamed from GTMiPhone_Prefix.pch)24
-rw-r--r--DebugUtils/GTMMethodCheck.m13
-rw-r--r--DebugUtils/GTMMethodCheckTest.m28
-rw-r--r--Foundation/GTMBase64Test.m2
-rw-r--r--Foundation/GTMCalculatedRange.m31
-rw-r--r--Foundation/GTMCalculatedRangeTest.m2
-rw-r--r--Foundation/GTMFourCharCode.h51
-rw-r--r--Foundation/GTMFourCharCode.m98
-rw-r--r--Foundation/GTMFourCharCodeTest.m82
-rw-r--r--Foundation/GTMGeometryUtils.h326
-rw-r--r--Foundation/GTMGeometryUtils.m34
-rw-r--r--Foundation/GTMGeometryUtilsTest.m128
-rw-r--r--Foundation/GTMHTTPFetcher.m9
-rw-r--r--Foundation/GTMHTTPFetcherTest.m124
-rw-r--r--Foundation/GTMHTTPServer.h133
-rw-r--r--Foundation/GTMHTTPServer.m584
-rw-r--r--Foundation/GTMHTTPServerTest.m573
-rw-r--r--Foundation/GTMNSAppleEvent+HandlerTest.applescript39
-rw-r--r--Foundation/GTMNSAppleEventDescriptor+Foundation.h91
-rw-r--r--Foundation/GTMNSAppleEventDescriptor+Foundation.m488
-rw-r--r--Foundation/GTMNSAppleEventDescriptor+FoundationTest.m619
-rw-r--r--Foundation/GTMNSAppleEventDescriptor+Handler.h40
-rw-r--r--Foundation/GTMNSAppleEventDescriptor+Handler.m130
-rw-r--r--Foundation/GTMNSAppleEventDescriptor+HandlerTest.m70
-rw-r--r--Foundation/GTMNSAppleScript+Handler.h59
-rw-r--r--Foundation/GTMNSAppleScript+Handler.m283
-rw-r--r--Foundation/GTMNSAppleScript+HandlerTest.m331
-rw-r--r--Foundation/GTMNSData+zlibTest.m136
-rw-r--r--Foundation/GTMNSDictionary+URLArguments.m2
-rw-r--r--Foundation/GTMNSDictionary+URLArgumentsTest.m2
-rw-r--r--Foundation/GTMNSEnumerator+Filter.m34
-rw-r--r--Foundation/GTMNSEnumerator+FilterTest.m2
-rw-r--r--Foundation/GTMNSFileManager+PathTest.m10
-rw-r--r--Foundation/GTMNSString+HTML.m16
-rw-r--r--Foundation/GTMNSString+HTMLTest.m2
-rw-r--r--Foundation/GTMNSString+URLArgumentsTest.m2
-rw-r--r--Foundation/GTMNSString+XML.m12
-rw-r--r--Foundation/GTMNSString+XMLTest.m2
-rw-r--r--Foundation/GTMObjC2RuntimeTest.m2
-rw-r--r--Foundation/GTMRegex.m21
-rw-r--r--Foundation/GTMRegexTest.m9
-rw-r--r--Foundation/GTMScriptRunnerTest.m28
-rw-r--r--Foundation/GTMStackTrace.c2
-rw-r--r--Foundation/GTMStackTraceTest.m2
-rw-r--r--Foundation/GTMSystemVersionTest.m2
-rwxr-xr-xFoundation/TestData/GTMHTTPFetcherTestServer274
-rw-r--r--GTM.xcodeproj/project.pbxproj320
-rw-r--r--GTMDefines.h30
-rw-r--r--GTM_Prefix.pch17
-rw-r--r--GTMiPhone-Info.plist4
-rw-r--r--GTMiPhone.xcodeproj/project.pbxproj28
-rw-r--r--ReleaseNotes.txt19
-rw-r--r--UnitTesting/GTMAppKit+UnitTesting.h9
-rw-r--r--UnitTesting/GTMIPhoneUnitTestDelegate.h29
-rw-r--r--UnitTesting/GTMIPhoneUnitTestDelegate.m160
-rw-r--r--UnitTesting/GTMIPhoneUnitTestMain.m161
-rw-r--r--UnitTesting/GTMNSObject+UnitTesting.h2
-rw-r--r--UnitTesting/GTMNSObject+UnitTesting.m46
-rw-r--r--UnitTesting/GTMSenTestCase.h20
-rw-r--r--UnitTesting/GTMSenTestCase.m75
-rw-r--r--UnitTesting/GTMTestHTTPServer.h39
-rw-r--r--UnitTesting/GTMTestHTTPServer.m166
-rw-r--r--UnitTesting/GTMUIKit+UnitTesting.h4
-rw-r--r--UnitTesting/GTMUnitTestDevLog.h70
-rw-r--r--UnitTesting/GTMUnitTestDevLog.m154
-rw-r--r--UnitTesting/GTMUnitTestingBindingTest.m4
-rw-r--r--UnitTesting/GTMUnitTestingTest.m14
-rw-r--r--UnitTesting/GTMUnitTestingUtilities.h1
-rw-r--r--UnitTesting/GTMUnitTestingUtilities.m2
-rw-r--r--XcodeConfig/Project/DebugLeopardOrLater.xcconfig2
-rw-r--r--XcodeConfig/Project/DebugTigerOrLater.xcconfig2
-rw-r--r--XcodeConfig/Project/DebugiPhone.xcconfig2
-rw-r--r--XcodeConfig/Project/ReleaseLeopardOrLater.xcconfig2
-rw-r--r--XcodeConfig/Project/ReleaseTigerOrLater.xcconfig2
-rw-r--r--XcodeConfig/Project/ReleaseiPhone.xcconfig2
-rw-r--r--XcodeConfig/xcconfigs-readme.txt34
89 files changed, 5522 insertions, 1046 deletions
diff --git a/AppKit/GTMLinearRGBShading.m b/AppKit/GTMLinearRGBShading.m
index 18af0e0..ef13986 100644
--- a/AppKit/GTMLinearRGBShading.m
+++ b/AppKit/GTMLinearRGBShading.m
@@ -88,28 +88,28 @@ static void cShadeFunction(void *info, const CGFloat *inPos, CGFloat *outVals);
// Calculate a linear value based on our stops
- (id)valueAtPosition:(CGFloat)position {
- NSUInteger index = 0;
+ NSUInteger positionIndex = 0;
NSUInteger colorCount = [self stopCount];
CGFloat stop1Position = 0.0;
- NSColor *stop1Color = [self stopAtIndex:index position:&stop1Position];
- index += 1;
+ NSColor *stop1Color = [self stopAtIndex:positionIndex position:&stop1Position];
+ positionIndex += 1;
CGFloat stop2Position = 0.0;
NSColor *stop2Color = nil;
NSColor *theColor = nil;
if (colorCount > 1) {
- stop2Color = [self stopAtIndex:index position:&stop2Position];
- index += 1;
+ stop2Color = [self stopAtIndex:positionIndex position:&stop2Position];
+ positionIndex += 1;
} else {
// if we only have one value, that's what we return
stop2Position = stop1Position;
stop2Color = stop1Color;
}
- while (index < colorCount && stop2Position < position) {
+ while (positionIndex < colorCount && stop2Position < position) {
stop1Color = stop2Color;
stop1Position = stop2Position;
- stop2Color = [self stopAtIndex:index position:&stop2Position];
- index += 1;
+ stop2Color = [self stopAtIndex:positionIndex position:&stop2Position];
+ positionIndex += 1;
}
if (position <= stop1Position) {
diff --git a/AppKit/GTMLinearRGBShadingTest.m b/AppKit/GTMLinearRGBShadingTest.m
index 119fa79..cb65572 100644
--- a/AppKit/GTMLinearRGBShadingTest.m
+++ b/AppKit/GTMLinearRGBShadingTest.m
@@ -20,7 +20,7 @@
#import "GTMSenTestCase.h"
#import "GTMLinearRGBShading.h"
-@interface GTMLinearRGBShadingTest : SenTestCase
+@interface GTMLinearRGBShadingTest : GTMTestCase
@end
@implementation GTMLinearRGBShadingTest
@@ -36,10 +36,10 @@
STAssertNotNil(theShading,nil);
STAssertEquals([theShading stopCount], (NSUInteger)2, nil);
CGFloat *theColor = (CGFloat*)[theShading valueAtPosition: 0.5];
- STAssertTrue(theColor[0] == [purple redComponent] &&
- theColor[1] == [purple greenComponent] &&
- theColor[2] == [purple blueComponent] &&
- theColor[3] == [purple alphaComponent], nil);
+ STAssertEqualsWithAccuracy(theColor[0], [purple redComponent], 0.001, nil);
+ STAssertEqualsWithAccuracy(theColor[1], [purple greenComponent], 0.001, nil);
+ STAssertEqualsWithAccuracy(theColor[2], [purple blueComponent], 0.001, nil);
+ STAssertEqualsWithAccuracy(theColor[3], [purple alphaComponent], 0.001, nil);
}
- (void)testShadingWith {
@@ -49,11 +49,12 @@
CGFloat thePositions[kColorCount];
const CGFloat kColorIncrement = 1.0 / kColorCount;
for (NSUInteger i = 0; i < kColorCount; i++) {
- thePositions[i] = kColorIncrement * i;
- theColors[i] = [NSColor colorWithCalibratedRed:kColorIncrement * i
- green:kColorIncrement * i
- blue:kColorIncrement * i
- alpha:kColorIncrement * i];
+ CGFloat newValue = kColorIncrement * i;
+ thePositions[i] = newValue;
+ theColors[i] = [NSColor colorWithCalibratedRed:newValue
+ green:newValue
+ blue:newValue
+ alpha:newValue];
}
GTMLinearRGBShading *theShading =
[GTMLinearRGBShading shadingWithColors:theColors
@@ -61,11 +62,12 @@
atPositions:thePositions
count:kColorCount];
for (NSUInteger i = 0; i < kColorCount; i++) {
- CGFloat *theColor = (CGFloat*)[theShading valueAtPosition: kColorIncrement * i];
- STAssertTrue(theColor[0] == kColorIncrement * i &&
- theColor[1] == kColorIncrement * i &&
- theColor[2] == kColorIncrement * i &&
- theColor[3] == kColorIncrement * i, nil);
+ CGFloat newValue = kColorIncrement * i;
+ CGFloat *theColor = (CGFloat*)[theShading valueAtPosition:newValue];
+ STAssertEqualsWithAccuracy(theColor[0], newValue, 0.001, nil);
+ STAssertEqualsWithAccuracy(theColor[1], newValue, 0.001, nil);
+ STAssertEqualsWithAccuracy(theColor[2], newValue, 0.001, nil);
+ STAssertEqualsWithAccuracy(theColor[3], newValue, 0.001, nil);
}
}
diff --git a/AppKit/GTMLoginItems.m b/AppKit/GTMLoginItems.m
index cc34c11..61a2120 100644
--- a/AppKit/GTMLoginItems.m
+++ b/AppKit/GTMLoginItems.m
@@ -136,18 +136,18 @@ NSString * const kGTMLoginItemsHiddenKey = @"Hide";
+ (BOOL)pathInLoginItems:(NSString *)path {
NSArray *loginItems = [self loginItems:nil];
- NSInteger index = [self indexOfLoginItemWithValue:path
- forKey:kGTMLoginItemsPathKey
- loginItems:loginItems];
- return (index != NSNotFound) ? YES : NO;
+ NSInteger itemIndex = [self indexOfLoginItemWithValue:path
+ forKey:kGTMLoginItemsPathKey
+ loginItems:loginItems];
+ return (itemIndex != NSNotFound) ? YES : NO;
}
+ (BOOL)itemWithNameInLoginItems:(NSString *)name {
NSArray *loginItems = [self loginItems:nil];
- NSInteger index = [self indexOfLoginItemWithValue:name
- forKey:kGTMLoginItemsNameKey
- loginItems:loginItems];
- return (index != NSNotFound) ? YES : NO;
+ NSInteger itemIndex = [self indexOfLoginItemWithValue:name
+ forKey:kGTMLoginItemsNameKey
+ loginItems:loginItems];
+ return (itemIndex != NSNotFound) ? YES : NO;
}
+ (void)addPathToLoginItems:(NSString*)path hide:(BOOL)hide {
diff --git a/AppKit/GTMLoginItemsTest.m b/AppKit/GTMLoginItemsTest.m
index 05a93a1..ec08a85 100644
--- a/AppKit/GTMLoginItemsTest.m
+++ b/AppKit/GTMLoginItemsTest.m
@@ -26,7 +26,7 @@
#define TESTS_ENABLED 0
-@interface GTMLoginItemsTest : SenTestCase
+@interface GTMLoginItemsTest : GTMTestCase
@end
#if TESTS_ENABLED
diff --git a/AppKit/GTMNSBezierPath+CGPathTest.m b/AppKit/GTMNSBezierPath+CGPathTest.m
index e22ee07..eb86353 100644
--- a/AppKit/GTMNSBezierPath+CGPathTest.m
+++ b/AppKit/GTMNSBezierPath+CGPathTest.m
@@ -23,13 +23,16 @@
#import "GTMAppKit+UnitTesting.h"
#import "GTMSenTestCase.h"
-@interface GTMNSBezierPath_CGPathTest : SenTestCase<GTMUnitTestViewDrawer>
+@interface GTMNSBezierPath_CGPathTest : GTMTestCase<GTMUnitTestViewDrawer>
@end
@implementation GTMNSBezierPath_CGPathTest
- (void)testCreateCGPath {
- GTMAssertDrawingEqualToFile(self, NSMakeSize(100, 100), @"GTMNSBezierPath+CGPathTest", nil, nil);
+ GTMAssertDrawingEqualToImageNamed(self,
+ NSMakeSize(100, 100),
+ @"GTMNSBezierPath+CGPathTest",
+ nil, nil);
}
diff --git a/AppKit/GTMNSBezierPath+RoundRectTest.m b/AppKit/GTMNSBezierPath+RoundRectTest.m
index c67c4b3..ad64fd4 100644
--- a/AppKit/GTMNSBezierPath+RoundRectTest.m
+++ b/AppKit/GTMNSBezierPath+RoundRectTest.m
@@ -22,14 +22,14 @@
#import "GTMNSBezierPath+RoundRect.h"
#import "GTMAppKit+UnitTesting.h"
-@interface GTMNSBezierPath_RoundRectTest : SenTestCase<GTMUnitTestViewDrawer>
+@interface GTMNSBezierPath_RoundRectTest : GTMTestCase<GTMUnitTestViewDrawer>
@end
@implementation GTMNSBezierPath_RoundRectTest
- (void)testRoundRects {
- GTMAssertDrawingEqualToFile(self, NSMakeSize(330, 430),
- @"GTMNSBezierPath+RoundRectTest", nil, nil);
+ GTMAssertDrawingEqualToImageNamed(self, NSMakeSize(330, 430),
+ @"GTMNSBezierPath+RoundRectTest", nil, nil);
}
// Draws all of our tests so that we can compare this to our stored TIFF file.
diff --git a/AppKit/GTMNSBezierPath+Shading.m b/AppKit/GTMNSBezierPath+Shading.m
index 4ab9ee3..eff8dfb 100644
--- a/AppKit/GTMNSBezierPath+Shading.m
+++ b/AppKit/GTMNSBezierPath+Shading.m
@@ -132,9 +132,9 @@
NSPoint newPoint = pointB;
CGFloat x = (pointB.x - pointA.x);
CGFloat y = (pointB.y - pointA.y);
- if (x == 0.0) {
+ if (fpclassify(x) == FP_ZERO) {
newPoint.y += length;
- } else if (y == 0.0) {
+ } else if (fpclassify(y) == FP_ZERO) {
newPoint.x += length;
} else {
#if CGFLOAT_IS_DOUBLE
diff --git a/AppKit/GTMNSBezierPath+ShadingTest.m b/AppKit/GTMNSBezierPath+ShadingTest.m
index 9fa7a8f..5634cf3 100644
--- a/AppKit/GTMNSBezierPath+ShadingTest.m
+++ b/AppKit/GTMNSBezierPath+ShadingTest.m
@@ -24,16 +24,17 @@
#import "GTMAppKit+UnitTesting.h"
#import "GTMNSBezierPath+Shading.h"
-@interface GTMNSBezierPath_ShadingTest : SenTestCase<GTMUnitTestViewDrawer>
+@interface GTMNSBezierPath_ShadingTest : GTMTestCase<GTMUnitTestViewDrawer>
@end
@implementation GTMNSBezierPath_ShadingTest
- (void)testShadings {
- GTMAssertDrawingEqualToFile(self, NSMakeSize(200, 200), @"GTMNSBezierPath+ShadingTest", nil, nil);
+ GTMAssertDrawingEqualToImageNamed(self,
+ NSMakeSize(200, 200),
+ @"GTMNSBezierPath+ShadingTest", nil, nil);
}
-
- (void)gtm_unitTestViewDrawRect:(NSRect)rect contextInfo:(void*)contextInfo {
NSColor *theColorArray[] = { [NSColor blueColor],
diff --git a/AppKit/GTMNSWorkspace+ScreenSaver.m b/AppKit/GTMNSWorkspace+ScreenSaver.m
index 9e1eb6a..7ca5d70 100644
--- a/AppKit/GTMNSWorkspace+ScreenSaver.m
+++ b/AppKit/GTMNSWorkspace+ScreenSaver.m
@@ -20,6 +20,7 @@
#import <ScreenSaver/ScreenSaver.h>
#import "GTMNSWorkspace+ScreenSaver.h"
#import "GTMDefines.h"
+#import "GTMGarbageCollection.h"
// Interesting class descriptions extracted from ScreenSaver.framework using
// class-dump. Note that these are "not documented".
@@ -114,15 +115,14 @@
kProcessDictionaryIncludeAllInformationMask);
require(cfProcessInfo, CantGetFrontProcess);
-
- NSString *bundlePath = [(NSDictionary*)cfProcessInfo objectForKey:@"BundlePath"];
-
+ NSDictionary *processInfo = [GTMNSMakeCollectable(cfProcessInfo) autorelease];
+ NSString *bundlePath = [processInfo objectForKey:@"BundlePath"];
+
// ScreenSaverEngine is the frontmost app if the screen saver is actually
// running Security Agent is the frontmost app if the "enter password"
// dialog is showing
answer = [bundlePath hasSuffix:@"ScreenSaverEngine.app"] ||
[bundlePath hasSuffix:@"SecurityAgent.app"];
- CFRelease(cfProcessInfo);
// COV_NF_END
}
CantGetFrontProcess:
diff --git a/AppKit/GTMNSWorkspace+ScreenSaverTest.m b/AppKit/GTMNSWorkspace+ScreenSaverTest.m
index 2dfcbb9..923efe9 100644
--- a/AppKit/GTMNSWorkspace+ScreenSaverTest.m
+++ b/AppKit/GTMNSWorkspace+ScreenSaverTest.m
@@ -15,10 +15,10 @@
// License for the specific language governing permissions and limitations under
// the License.
//
-#import <SenTestingKit/SenTestingKit.h>
+#import "GTMSenTestCase.h"
#import "GTMNSWorkspace+ScreenSaver.h"
-@interface GTMNSWorkspace_ScreenSaverTest : SenTestCase
+@interface GTMNSWorkspace_ScreenSaverTest : GTMTestCase
@end
diff --git a/BuildScripts/BuildAllSDKs.sh b/BuildScripts/BuildAllSDKs.sh
new file mode 100755
index 0000000..0c9bbe8
--- /dev/null
+++ b/BuildScripts/BuildAllSDKs.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+# BuildAllSDKs.sh
+#
+# This script builds both the Tiger and Leopard versions of the requested
+# target in the current basic config (debug, release, debug-gcov). This script
+# should be run from the same directory as the GTM Xcode project file.
+#
+# 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.
+
+PROJECT_TARGET="$1"
+
+XCODEBUILD="${DEVELOPER_BIN_DIR}/xcodebuild"
+REQUESTED_BUILD_STYLE=$(echo "${BUILD_STYLE}" | sed "s/.*OrLater-\(.*\)/\1/")
+# See if we were told to clean instead of build.
+PROJECT_ACTION="build"
+if [ "${ACTION}" == "clean" ]; then
+ PROJECT_ACTION="clean"
+fi
+
+# helper for doing a build
+function doIt {
+ local myProject=$1
+ local myTarget=$2
+ local myConfig=$3
+ echo "note: Starting ${PROJECT_ACTION} of ${myTarget} from ${myProject} in ${myConfig}"
+ ${XCODEBUILD} -project "${myProject}" \
+ -target "${myTarget}" \
+ -configuration "${myConfig}" \
+ "${PROJECT_ACTION}"
+ buildResult=$?
+ if [ $buildResult -ne 0 ]; then
+ echo "Error: ** ${PROJECT_ACTION} Failed **"
+ exit $buildResult
+ fi
+ echo "note: Done ${PROJECT_ACTION}"
+}
+
+# now build tiger and then leopard
+doIt GTM.xcodeproj "${PROJECT_TARGET}" "TigerOrLater-${REQUESTED_BUILD_STYLE}"
+doIt GTM.xcodeproj "${PROJECT_TARGET}" "LeopardOrLater-${REQUESTED_BUILD_STYLE}"
+
+# TODO(iphone if right tool chain?)
diff --git a/BuildingAndUsing.txt b/BuildingAndUsing.txt
new file mode 100644
index 0000000..67bbd79
--- /dev/null
+++ b/BuildingAndUsing.txt
@@ -0,0 +1,18 @@
+The GTM project provides a Framework target and Unit Testing targets.
+
+Unit Testing Targets -
+
+These targets are there to make sure all of GTM is working, and reflect the
+hierarchy of the source tree.
+
+Building the "All UnitTests" target will build all of the unittests for the
+current configuration included in the GTM package. We require that "All
+UnitTests" builds cleanly before we will accept any code submissions.
+
+Framework Target -
+
+These targets are there to make it easy for anyone to quickly add GTM sources
+to their own project to start using it or playing around with it. However,
+the intent is for a developer to manually add the sources to their project in
+the end to reduce the total size of their product (since they only have to
+include the parts of GTM they need).
diff --git a/DebugUtils/GTMDebugSelectorValidation.h b/DebugUtils/GTMDebugSelectorValidation.h
index 82d00e8..19d8b8e 100644
--- a/DebugUtils/GTMDebugSelectorValidation.h
+++ b/DebugUtils/GTMDebugSelectorValidation.h
@@ -5,6 +5,8 @@
// function that takes an object and selector to invoke, you should call:
//
// GTMAssertSelectorNilOrImplementedWithArguments(obj, sel, @encode(arg1type), ..., NULL)
+// or
+// GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments(obj, sel, @encode(returnType), @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
@@ -31,12 +33,12 @@
#import <stdarg.h>
#import "GTMDefines.h"
-static void GTMAssertSelectorNilOrImplementedWithArguments(id obj, SEL sel, ...) {
-
+static void GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments(id obj, SEL sel, const char *retType, ...) {
+
// verify that the object's selector is implemented with the proper
// number and type of arguments
va_list argList;
- va_start(argList, sel);
+ va_start(argList, retType);
if (obj && sel) {
// check that the selector is implemented
@@ -71,14 +73,28 @@ static void GTMAssertSelectorNilOrImplementedWithArguments(id obj, SEL sel, ...)
NSStringFromClass([obj class]),
NSStringFromSelector(sel),
(argCount - 2));
+
+ // if asked, validate the return type
+ if (retType && (strcmp("gtm_skip_return_test", retType) != 0)) {
+ const char *foundRetType = [sig methodReturnType];
+ _GTMDevAssert(0 == strncmp(foundRetType, retType, strlen(retType)),
+ @"\"%@\" selector \"%@\" return type should be type %s",
+ NSStringFromClass([obj class]),
+ NSStringFromSelector(sel),
+ retType);
+ }
}
va_end(argList);
}
+#define GTMAssertSelectorNilOrImplementedWithArguments(obj, sel, ...) \
+ GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments((obj), (sel), "gtm_skip_return_test", __VA_ARGS__)
+
#else // DEBUG
// make it go away if not debug
+#define GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments(obj, sel, retType, ...) do { } while (0)
#define GTMAssertSelectorNilOrImplementedWithArguments(obj, sel, ...) do { } while (0)
#endif // DEBUG
diff --git a/GTMiPhone_Prefix.pch b/DebugUtils/GTMDevLog.m
index 4c13635..af30713 100644
--- a/GTMiPhone_Prefix.pch
+++ b/DebugUtils/GTMDevLog.m
@@ -1,5 +1,7 @@
//
-// Copyright 2007-2008 Google Inc.
+// GTMDevLog.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
@@ -14,7 +16,19 @@
// the License.
//
-#ifdef __OBJC__
- #import <Foundation/Foundation.h>
- #import <UIKit/UIKit.h>
-#endif
+#include "GTMUnitTestDevLog.h"
+
+// This is the logging function that is called by default when building
+// GTMFramework. If it can find GTMUnitTestDevLog class it will use it,
+// otherwise it falls onto NSLog.
+void _GTMUnittestDevLog(NSString *format, ...) {
+ Class devLogClass = NSClassFromString(@"GTMUnitTestDevLog");
+ va_list argList;
+ va_start(argList, format);
+ if (devLogClass) {
+ [devLogClass log:format args:argList];
+ } else {
+ NSLogv(format, argList);
+ }
+ va_end(argList);
+}
diff --git a/DebugUtils/GTMMethodCheck.m b/DebugUtils/GTMMethodCheck.m
index f91b1a9..300022b 100644
--- a/DebugUtils/GTMMethodCheck.m
+++ b/DebugUtils/GTMMethodCheck.m
@@ -41,9 +41,14 @@ static BOOL ConformsToNSObjectProtocol(Class cls) {
// 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)) {
+ // On iPhone we check Object as well
+ if ((strncmp(className, "NS", 2) == 0)
+ || (strncmp(className, "_NS", 3) == 0)
+ || (strcmp(className, "CFObject") == 0)
+#if GTM_IPHONE_SDK
+ || (strcmp(className, "Object") == 0)
+#endif
+ ) {
return YES;
}
@@ -88,10 +93,12 @@ void GTMMethodCheckMethodChecker(void) {
// @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)) {
+ // COV_NF_START
_GTMDevLog(@"GTMMethodCheckMethodChecker: Class %s does not conform to "
"@protocol(NSObject), so won't be checked",
class_getName(cls));
continue;
+ // COV_NF_END
}
// Since we are looking for a class method (+xxGMMethodCheckMethod...)
// we need to query the isa pointer to see what methods it support, but
diff --git a/DebugUtils/GTMMethodCheckTest.m b/DebugUtils/GTMMethodCheckTest.m
index b5e7e6b..91b7300 100644
--- a/DebugUtils/GTMMethodCheckTest.m
+++ b/DebugUtils/GTMMethodCheckTest.m
@@ -20,33 +20,13 @@
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
+@interface GTMMethodCheckTest : GTMTestCase
++ (void)GTMMethodCheckTestClassMethod;
@end
@implementation GTMMethodCheckTest
-GTM_METHOD_CHECK(GTMMethodCheckTest, GTMMethodCheckTestMethod);
-GTM_METHOD_CHECK(GTMMethodCheckTest, GTMMethodCheckTestClassMethod);
+GTM_METHOD_CHECK(GTMMethodCheckTest, GTMMethodCheckTestMethod); // COV_NF_LINE
+GTM_METHOD_CHECK(GTMMethodCheckTest, GTMMethodCheckTestClassMethod); // COV_NF_LINE
- (void)GTMMethodCheckTestMethod {
}
diff --git a/Foundation/GTMBase64Test.m b/Foundation/GTMBase64Test.m
index 358c6ec..084ea1e 100644
--- a/Foundation/GTMBase64Test.m
+++ b/Foundation/GTMBase64Test.m
@@ -38,7 +38,7 @@ static BOOL NoEqualChar(NSData *data) {
return YES;
}
-@interface GTMBase64Test : SenTestCase
+@interface GTMBase64Test : GTMTestCase
@end
@implementation GTMBase64Test
diff --git a/Foundation/GTMCalculatedRange.m b/Foundation/GTMCalculatedRange.m
index 435ad65..218b811 100644
--- a/Foundation/GTMCalculatedRange.m
+++ b/Foundation/GTMCalculatedRange.m
@@ -30,6 +30,9 @@
- (CGFloat)position;
@end
+CG_INLINE BOOL FPEqual(CGFloat a, CGFloat b) {
+ return (fpclassify(a - b) == FP_ZERO);
+}
@implementation GTMCalculatedRangeStopPrivate
+ (id)stopWithObject:(id)item position:(CGFloat)inPosition {
@@ -77,52 +80,52 @@
}
- (void)insertStop:(id)item atPosition:(CGFloat)position {
- NSUInteger index = 0;
+ NSUInteger positionIndex = 0;
NSEnumerator *theEnumerator = [storage_ objectEnumerator];
GTMCalculatedRangeStopPrivate *theStop;
while (nil != (theStop = [theEnumerator nextObject])) {
if ([theStop position] < position) {
- index += 1;
+ positionIndex += 1;
}
- else if ([theStop position] == position) {
+ else if (FPEqual([theStop position], position)) {
// remove and stop the enum since we just modified the object
- [storage_ removeObjectAtIndex:index];
+ [storage_ removeObjectAtIndex:positionIndex];
break;
}
}
[storage_ insertObject:[GTMCalculatedRangeStopPrivate stopWithObject:item position:position]
- atIndex:index];
+ atIndex:positionIndex];
}
- (BOOL)removeStopAtPosition:(CGFloat)position {
- NSUInteger index = 0;
+ NSUInteger positionIndex = 0;
BOOL foundStop = NO;
NSEnumerator *theEnumerator = [storage_ objectEnumerator];
GTMCalculatedRangeStopPrivate *theStop;
while (nil != (theStop = [theEnumerator nextObject])) {
- if ([theStop position] == position) {
+ if (FPEqual([theStop position], position)) {
break;
} else {
- index += 1;
+ positionIndex += 1;
}
}
if (nil != theStop) {
- [self removeStopAtIndex:index];
+ [self removeStopAtIndex:positionIndex];
foundStop = YES;
}
return foundStop;
}
-- (void)removeStopAtIndex:(NSUInteger)index {
- [storage_ removeObjectAtIndex:index];
+- (void)removeStopAtIndex:(NSUInteger)positionIndex {
+ [storage_ removeObjectAtIndex:positionIndex];
}
- (NSUInteger)stopCount {
return [storage_ count];
}
-- (id)stopAtIndex:(NSUInteger)index position:(CGFloat*)outPosition {
- GTMCalculatedRangeStopPrivate *theStop = [storage_ objectAtIndex:index];
+- (id)stopAtIndex:(NSUInteger)positionIndex position:(CGFloat*)outPosition {
+ GTMCalculatedRangeStopPrivate *theStop = [storage_ objectAtIndex:positionIndex];
if (nil != outPosition) {
*outPosition = [theStop position];
}
@@ -134,7 +137,7 @@
GTMCalculatedRangeStopPrivate *theStop;
NSEnumerator *theEnumerator = [storage_ objectEnumerator];
while (nil != (theStop = [theEnumerator nextObject])) {
- if ([theStop position] == position) {
+ if (FPEqual([theStop position], position)) {
theValue = [theStop item];
break;
}
diff --git a/Foundation/GTMCalculatedRangeTest.m b/Foundation/GTMCalculatedRangeTest.m
index 1d716c8..1790072 100644
--- a/Foundation/GTMCalculatedRangeTest.m
+++ b/Foundation/GTMCalculatedRangeTest.m
@@ -19,7 +19,7 @@
#import "GTMCalculatedRange.h"
#import "GTMSenTestCase.h"
-@interface GTMCalculatedRangeTest : SenTestCase {
+@interface GTMCalculatedRangeTest : GTMTestCase {
GTMCalculatedRange *range_;
}
@end
diff --git a/Foundation/GTMFourCharCode.h b/Foundation/GTMFourCharCode.h
new file mode 100644
index 0000000..90853b5
--- /dev/null
+++ b/Foundation/GTMFourCharCode.h
@@ -0,0 +1,51 @@
+//
+// GTMFourCharCode
+// Wrapper for FourCharCodes
+//
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import <Foundation/Foundation.h>
+
+// FourCharCodes are OSTypes, ResTypes etc. This class wraps them if
+// you need to store them in dictionaries etc.
+@interface GTMFourCharCode : NSObject <NSCopying, NSCoding> {
+ FourCharCode code_;
+}
+
+// returns a string for a FourCharCode
++ (id)stringWithFourCharCode:(FourCharCode)code;
+
+// String must be 4 chars or less, or you will get nil back.
++ (id)fourCharCodeWithString:(NSString*)string;
++ (id)fourCharCodeWithFourCharCode:(FourCharCode)code;
+
+// String must be 4 chars or less, or you will get nil back.
+- (id)initWithString:(NSString*)string;
+
+// Designated Initializer
+- (id)initWithFourCharCode:(FourCharCode)code;
+
+// Returns 'APPL' for "APPL"
+- (FourCharCode)fourCharCode;
+
+// For FourCharCode of 'APPL' returns "APPL". For 1 returns "\0\0\0\1"
+- (NSString*)stringValue;
+
+// For FourCharCode of "APPL" returns an NSNumber with 1095782476 (0x4150504C).
+// For 1 returns 1.
+- (NSNumber*)numberValue;
+
+@end
diff --git a/Foundation/GTMFourCharCode.m b/Foundation/GTMFourCharCode.m
new file mode 100644
index 0000000..6b67cae
--- /dev/null
+++ b/Foundation/GTMFourCharCode.m
@@ -0,0 +1,98 @@
+//
+// GTMFourCharCode.m
+// Wrapper for FourCharCodes
+//
+// 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 "GTMFourCharCode.h"
+#import "GTMGarbageCollection.h"
+#import <CoreServices/CoreServices.h>
+
+@implementation GTMFourCharCode
+
++ (id)stringWithFourCharCode:(FourCharCode)code {
+ return [GTMNSMakeCollectable(UTCreateStringForOSType(code)) autorelease];
+}
+
++ (id)fourCharCodeWithString:(NSString*)string {
+ return [[[self alloc] initWithString:string] autorelease];
+}
+
++ (id)fourCharCodeWithFourCharCode:(FourCharCode)code {
+ return [[[self alloc] initWithFourCharCode:code] autorelease];
+}
+
+- (id)initWithString:(NSString*)string {
+ NSUInteger length = [string length];
+ if (length == 0 || length > 4) {
+ [self release];
+ return nil;
+ } else {
+ return [self initWithFourCharCode:UTGetOSTypeFromString((CFStringRef)string)];
+ }
+}
+
+- (id)initWithFourCharCode:(FourCharCode)code {
+ if ((self = [super init])) {
+ code_ = code;
+ }
+ return self;
+}
+
+- (id)initWithCoder:(NSCoder *)aDecoder {
+ if ((self = [super init])) {
+ code_ = [aDecoder decodeInt32ForKey:@"FourCharCode"];
+ }
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder {
+ [aCoder encodeInt32:code_ forKey:@"FourCharCode"];
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+ return [[[self class] alloc] initWithFourCharCode:code_];
+}
+
+- (BOOL)isEqual:(id)object {
+ return [object isKindOfClass:[self class]] && [object fourCharCode] == code_;
+}
+
+- (NSUInteger)hash {
+ return (NSUInteger)code_;
+}
+
+- (NSString *)description {
+ return [NSString stringWithFormat:@"%@ - %@ (0x%X)",
+ [self class],
+ [self stringValue],
+ code_];
+}
+
+- (FourCharCode)fourCharCode {
+ return code_;
+}
+
+- (NSString*)stringValue {
+ return [GTMNSMakeCollectable(UTCreateStringForOSType(code_)) autorelease];
+}
+
+- (NSNumber*)numberValue {
+ return [NSNumber numberWithUnsignedInt:code_];
+}
+
+@end
diff --git a/Foundation/GTMFourCharCodeTest.m b/Foundation/GTMFourCharCodeTest.m
new file mode 100644
index 0000000..f91b8f2
--- /dev/null
+++ b/Foundation/GTMFourCharCodeTest.m
@@ -0,0 +1,82 @@
+//
+// GTMFourCharCodeTest.m
+//
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import "GTMSenTestCase.h"
+#import "GTMFourCharCode.h"
+
+@interface GTMFourCharCodeTest : GTMTestCase
+@end
+
+@implementation GTMFourCharCodeTest
+
+const FourCharCode kGTMHighMacOSRomanCode = 0xA5A8A9AA; // '•®©™'
+
+- (void)testFourCharCode {
+ GTMFourCharCode *fcc = [GTMFourCharCode fourCharCodeWithString:@"APPL"];
+ STAssertNotNil(fcc, nil);
+ STAssertEqualObjects([fcc stringValue], @"APPL", nil);
+ STAssertEqualObjects([fcc numberValue], [NSNumber numberWithUnsignedInt:'APPL'], nil);
+ STAssertEquals([fcc fourCharCode], (FourCharCode)'APPL', nil);
+
+ STAssertEqualObjects([fcc description], @"GTMFourCharCode - APPL (0x4150504C)", nil);
+ STAssertEquals([fcc hash], (NSUInteger)'APPL', nil);
+
+ GTMFourCharCode *fcc2 = [GTMFourCharCode fourCharCodeWithFourCharCode:kGTMHighMacOSRomanCode];
+ STAssertNotNil(fcc2, nil);
+ STAssertEqualObjects([fcc2 stringValue], @"•®©™", nil);
+ STAssertEqualObjects([fcc2 numberValue], [NSNumber numberWithUnsignedInt:kGTMHighMacOSRomanCode], nil);
+ STAssertEquals([fcc2 fourCharCode], (FourCharCode)kGTMHighMacOSRomanCode, nil);
+
+ STAssertNotEqualObjects(fcc, fcc2, nil);
+
+ NSData *data = [NSKeyedArchiver archivedDataWithRootObject:fcc];
+ STAssertNotNil(data, nil);
+ fcc2 = (GTMFourCharCode*)[NSKeyedUnarchiver unarchiveObjectWithData:data];
+ STAssertNotNil(fcc2, nil);
+ STAssertEqualObjects(fcc, fcc2, nil);
+
+ fcc = [[[GTMFourCharCode alloc] initWithFourCharCode:'\?\?\?\?'] autorelease];
+ STAssertNotNil(fcc, nil);
+ STAssertEqualObjects([fcc stringValue], @"????", nil);
+ STAssertEqualObjects([fcc numberValue], [NSNumber numberWithUnsignedInt:'\?\?\?\?'], nil);
+ STAssertEquals([fcc fourCharCode], (FourCharCode)'\?\?\?\?', nil);
+
+ fcc = [[[GTMFourCharCode alloc] initWithString:@"????"] autorelease];
+ STAssertNotNil(fcc, nil);
+ STAssertEqualObjects([fcc stringValue], @"????", nil);
+ STAssertEqualObjects([fcc numberValue], [NSNumber numberWithUnsignedInt:'\?\?\?\?'], nil);
+ STAssertEquals([fcc fourCharCode], (FourCharCode)'\?\?\?\?', nil);
+
+ fcc = [GTMFourCharCode fourCharCodeWithFourCharCode:1];
+ STAssertNotNil(fcc, nil);
+ STAssertEqualObjects([fcc stringValue], @"\0\0\0\1", nil);
+ STAssertEqualObjects([fcc numberValue], [NSNumber numberWithUnsignedInt:1], nil);
+ STAssertEquals([fcc fourCharCode], (FourCharCode)1, nil);
+
+
+ fcc = [GTMFourCharCode fourCharCodeWithString:@"BADDSTRING"];
+ STAssertNil(fcc, nil);
+}
+
+- (void)testStringWithCode {
+ STAssertEqualObjects([GTMFourCharCode stringWithFourCharCode:'APPL'], @"APPL", nil);
+ STAssertEqualObjects([GTMFourCharCode stringWithFourCharCode:1], @"\0\0\0\1", nil);
+ STAssertEqualObjects([GTMFourCharCode stringWithFourCharCode:kGTMHighMacOSRomanCode], @"•®©™", nil);
+}
+
+@end
diff --git a/Foundation/GTMGeometryUtils.h b/Foundation/GTMGeometryUtils.h
index a58ac13..7d54cf2 100644
--- a/Foundation/GTMGeometryUtils.h
+++ b/Foundation/GTMGeometryUtils.h
@@ -42,7 +42,125 @@ enum {
};
typedef NSUInteger GTMRectAlignment;
-#pragma mark Miscellaneous
+#pragma mark -
+#pragma mark CG - Point On Rect
+/// Return middle of min X side of rectangle
+//
+// Args:
+// rect - rectangle
+//
+// Returns:
+// point located in the middle of min X side of rect
+CG_INLINE CGPoint GTMCGMidMinX(CGRect rect) {
+ return CGPointMake(CGRectGetMinX(rect), CGRectGetMidY(rect));
+}
+
+/// Return middle of max X side of rectangle
+//
+// Args:
+// rect - rectangle
+//
+// Returns:
+// point located in the middle of max X side of rect
+CG_INLINE CGPoint GTMCGMidMaxX(CGRect rect) {
+ return CGPointMake(CGRectGetMaxX(rect), CGRectGetMidY(rect));
+}
+
+/// Return middle of max Y side of rectangle
+//
+// Args:
+// rect - rectangle
+//
+// Returns:
+// point located in the middle of max Y side of rect
+CG_INLINE CGPoint GTMCGMidMaxY(CGRect rect) {
+ return CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
+}
+
+/// Return middle of min Y side of rectangle
+//
+// Args:
+// rect - rectangle
+//
+// Returns:
+// point located in the middle of min Y side of rect
+CG_INLINE CGPoint GTMCGMidMinY(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 CG - Rect-Size Conversion
+
+/// 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 CGRect GTMCGRectOfSize(CGSize size) {
+ return CGRectMake(0.0f, 0.0f, size.width, size.height);
+}
+
+#pragma mark -
+#pragma mark CG - Rect Scaling and Alignment
+
+/// 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, CGFloat xScale, CGFloat 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
+// alignment - way to align the rectangles
+CGRect GTMCGAlignRectangles(CGRect alignee, CGRect aligner,
+ GTMRectAlignment alignment);
+/// Scale rectangle
+//
+// Args:
+// scalee - rect to be scaled
+// size - size to scale to
+// scaling - way to scale the rectangle
+CGRect GTMCGScaleRectangleToSize(CGRect scalee, CGSize size,
+ GTMScaling scaling);
+
+#pragma mark -
+#pragma mark CG - Miscellaneous
/// Calculate the distance between two points.
//
@@ -52,7 +170,7 @@ typedef NSUInteger GTMRectAlignment;
//
// Returns:
// Distance
-CG_INLINE CGFloat GTMDistanceBetweenPoints(NSPoint pt1, NSPoint pt2) {
+CG_INLINE CGFloat GTMCGDistanceBetweenPoints(CGPoint pt1, CGPoint pt2) {
CGFloat dX = pt1.x - pt2.x;
CGFloat dY = pt1.y - pt2.y;
#if CGFLOAT_IS_DOUBLE
@@ -62,8 +180,14 @@ CG_INLINE CGFloat GTMDistanceBetweenPoints(NSPoint pt1, NSPoint pt2) {
#endif
}
+#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
+// iPhone does not have NSTypes defined, only CGTypes. So no NSRect, NSPoint etc.
+
#pragma mark -
-#pragma mark Point Conversion
+// All of the conversion routines below are basically copied from the
+// NSGeometry header in the 10.5 sdk.
+
+#pragma mark NS <-> CG Point Conversion
/// Quickly convert from a CGPoint to a NSPoint.
//
@@ -76,7 +200,9 @@ CG_INLINE CGFloat GTMDistanceBetweenPoints(NSPoint pt1, NSPoint pt2) {
// Returns:
// Converted NSPoint
CG_INLINE NSPoint GTMCGPointToNSPoint(CGPoint inPoint) {
- return NSMakePoint(inPoint.x, inPoint.y);
+ _GTMCompileAssert(sizeof(NSPoint) == sizeof(CGPoint), NSPoint_and_CGPoint_must_be_the_same_size);
+ union convertUnion {NSPoint ns; CGPoint cg;};
+ return ((union convertUnion *)&inPoint)->ns;
}
/// Quickly convert from a NSPoint to a CGPoint.
@@ -90,11 +216,13 @@ CG_INLINE NSPoint GTMCGPointToNSPoint(CGPoint inPoint) {
// Returns:
// Converted CGPoint
CG_INLINE CGPoint GTMNSPointToCGPoint(NSPoint inPoint) {
- return CGPointMake(inPoint.x, inPoint.y);
+ _GTMCompileAssert(sizeof(NSPoint) == sizeof(CGPoint), NSPoint_and_CGPoint_must_be_the_same_size);
+ union convertUnion {NSPoint ns; CGPoint cg;};
+ return ((union convertUnion *)&inPoint)->cg;
}
#pragma mark -
-#pragma mark Rect Conversion
+#pragma mark NS <-> CG Rect Conversion
/// Convert from a CGRect to a NSRect.
//
@@ -107,7 +235,9 @@ CG_INLINE CGPoint GTMNSPointToCGPoint(NSPoint inPoint) {
// Returns:
// Converted NSRect
CG_INLINE NSRect GTMCGRectToNSRect(CGRect inRect) {
- return NSMakeRect(inRect.origin.x,inRect.origin.y,inRect.size.width,inRect.size.height);
+ _GTMCompileAssert(sizeof(NSRect) == sizeof(CGRect), NSRect_and_CGRect_must_be_the_same_size);
+ union convertUnion {NSRect ns; CGRect cg;};
+ return ((union convertUnion *)&inRect)->ns;
}
/// Convert from a NSRect to a CGRect.
@@ -121,11 +251,14 @@ CG_INLINE NSRect GTMCGRectToNSRect(CGRect inRect) {
// Returns:
// Converted CGRect
CG_INLINE CGRect GTMNSRectToCGRect(NSRect inRect) {
- return CGRectMake(inRect.origin.x,inRect.origin.y,inRect.size.width,inRect.size.height);
+ _GTMCompileAssert(sizeof(NSRect) == sizeof(CGRect), NSRect_and_CGRect_must_be_the_same_size);
+ union convertUnion {NSRect ns; CGRect cg;};
+ return ((union convertUnion *)&inRect)->cg;
}
+
#pragma mark -
-#pragma mark Size Conversion
+#pragma mark NS <-> CG Size Conversion
/// Convert from a CGSize to an NSSize.
//
@@ -135,7 +268,9 @@ CG_INLINE CGRect GTMNSRectToCGRect(NSRect inRect) {
// Returns:
// Converted NSSize
CG_INLINE NSSize GTMCGSizeToNSSize(CGSize inSize) {
- return NSMakeSize(inSize.width, inSize.height);
+ _GTMCompileAssert(sizeof(NSSize) == sizeof(CGSize), NSSize_and_CGSize_must_be_the_same_size);
+ union convertUnion {NSSize ns; CGSize cg;};
+ return ((union convertUnion *)&inSize)->ns;
}
/// Convert from a NSSize to a CGSize.
@@ -146,53 +281,55 @@ CG_INLINE NSSize GTMCGSizeToNSSize(CGSize inSize) {
// Returns:
// Converted CGSize
CG_INLINE CGSize GTMNSSizeToCGSize(NSSize inSize) {
- return CGSizeMake(inSize.width, inSize.height);
+ _GTMCompileAssert(sizeof(NSSize) == sizeof(CGSize), NSSize_and_CGSize_must_be_the_same_size);
+ union convertUnion {NSSize ns; CGSize cg;};
+ return ((union convertUnion *)&inSize)->cg;
}
#pragma mark -
-#pragma mark Point On Rect
+#pragma mark NS - Point On Rect
-/// Return middle of left side of rectangle
+/// Return middle of min X side of rectangle
//
// Args:
// rect - rectangle
//
// Returns:
-// point located in the middle of left side of rect
-CG_INLINE NSPoint GTMNSMidLeft(NSRect rect) {
+// point located in the middle of min X side of rect
+CG_INLINE NSPoint GTMNSMidMinX(NSRect rect) {
return NSMakePoint(NSMinX(rect), NSMidY(rect));
}
-/// Return middle of right side of rectangle
+/// Return middle of max X side of rectangle
//
// Args:
// rect - rectangle
//
// Returns:
-// point located in the middle of right side of rect
-CG_INLINE NSPoint GTMNSMidRight(NSRect rect) {
+// point located in the middle of max X side of rect
+CG_INLINE NSPoint GTMNSMidMaxX(NSRect rect) {
return NSMakePoint(NSMaxX(rect), NSMidY(rect));
}
-/// Return middle of top side of rectangle
+/// Return middle of max Y side of rectangle
//
// Args:
// rect - rectangle
//
// Returns:
-// point located in the middle of top side of rect
-CG_INLINE NSPoint GTMNSMidTop(NSRect rect) {
+// point located in the middle of max Y side of rect
+CG_INLINE NSPoint GTMNSMidMaxY(NSRect rect) {
return NSMakePoint(NSMidX(rect), NSMaxY(rect));
}
-/// Return middle of bottom side of rectangle
+/// Return middle of min Y side of rectangle
//
// Args:
// rect - rectangle
//
// Returns:
-// point located in the middle of bottom side of rect
-CG_INLINE NSPoint GTMNSMidBottom(NSRect rect) {
+// point located in the middle of min Y side of rect
+CG_INLINE NSPoint GTMNSMidMinY(NSRect rect) {
return NSMakePoint(NSMidX(rect), NSMinY(rect));
}
@@ -207,63 +344,8 @@ 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
+#pragma mark NS - Rect-Size Conversion
/// Return size of rectangle
//
@@ -276,17 +358,6 @@ 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:
@@ -298,19 +369,8 @@ 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
+#pragma mark NS - Rect Scaling and Alignment
/// Scales an NSRect
//
@@ -326,40 +386,19 @@ CG_INLINE NSRect GTMNSRectScale(NSRect inRect, CGFloat xScale, CGFloat yScale) {
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, CGFloat xScale, CGFloat 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,
+CG_INLINE NSRect GTMNSAlignRectangles(NSRect alignee, NSRect aligner,
GTMRectAlignment alignment) {
- return GTMNSRectToCGRect(GTMAlignRectangles(GTMCGRectToNSRect(alignee),
- GTMCGRectToNSRect(aligner),
+ return GTMCGRectToNSRect(GTMCGAlignRectangles(GTMNSRectToCGRect(alignee),
+ GTMNSRectToCGRect(aligner),
alignment));
-}
+}
/// Scale rectangle
//
@@ -367,18 +406,27 @@ CG_INLINE CGRect GTMCGAlignRectangles(CGRect alignee, CGRect aligner,
// scalee - rect to be scaled
// size - size to scale to
// scaling - way to scale the rectangle
-NSRect GTMScaleRectangleToSize(NSRect scalee, NSSize size,
- GTMScaling scaling);
+CG_INLINE NSRect GTMNSScaleRectangleToSize(NSRect scalee, NSSize size,
+ GTMScaling scaling) {
+ return GTMCGRectToNSRect(GTMCGScaleRectangleToSize(GTMNSRectToCGRect(scalee),
+ GTMNSSizeToCGSize(size),
+ scaling));
+}
-/// Scale rectangle
+#pragma mark -
+#pragma mark NS - Miscellaneous
+
+/// Calculate the distance between two points.
//
// 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));
+// pt1 first point
+// pt2 second point
+//
+// Returns:
+// Distance
+CG_INLINE CGFloat GTMNSDistanceBetweenPoints(NSPoint pt1, NSPoint pt2) {
+ return GTMCGDistanceBetweenPoints(GTMNSPointToCGPoint(pt1),
+ GTMNSPointToCGPoint(pt2));
}
+
+#endif // (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
diff --git a/Foundation/GTMGeometryUtils.m b/Foundation/GTMGeometryUtils.m
index 0e893ff..9ac2933 100644
--- a/Foundation/GTMGeometryUtils.m
+++ b/Foundation/GTMGeometryUtils.m
@@ -25,26 +25,26 @@
// aligner - rect to be aligned to
// alignment - alignment to be applied to alignee based on aligner
-NSRect GTMAlignRectangles(NSRect alignee, NSRect aligner, GTMRectAlignment alignment) {
+CGRect GTMCGAlignRectangles(CGRect alignee, CGRect 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);
+ alignee.origin.x = aligner.origin.x + (CGRectGetWidth(aligner) * .5f - CGRectGetWidth(alignee) * .5f);
+ alignee.origin.y = aligner.origin.y + CGRectGetHeight(aligner) - CGRectGetHeight(alignee);
break;
case GTMRectAlignTopLeft:
alignee.origin.x = aligner.origin.x;
- alignee.origin.y = aligner.origin.y + NSHeight(aligner) - NSHeight(alignee);
+ alignee.origin.y = aligner.origin.y + CGRectGetHeight(aligner) - CGRectGetHeight(alignee);
break;
case GTMRectAlignTopRight:
- alignee.origin.x = aligner.origin.x + NSWidth(aligner) - NSWidth(alignee);
- alignee.origin.y = aligner.origin.y + NSHeight(aligner) - NSHeight(alignee);
+ alignee.origin.x = aligner.origin.x + CGRectGetWidth(aligner) - CGRectGetWidth(alignee);
+ alignee.origin.y = aligner.origin.y + CGRectGetHeight(aligner) - CGRectGetHeight(alignee);
break;
case GTMRectAlignLeft:
alignee.origin.x = aligner.origin.x;
- alignee.origin.y = aligner.origin.y + (NSHeight(aligner) * .5f - NSHeight(alignee) * .5f);
+ alignee.origin.y = aligner.origin.y + (CGRectGetHeight(aligner) * .5f - CGRectGetHeight(alignee) * .5f);
break;
case GTMRectAlignBottomLeft:
@@ -53,40 +53,40 @@ NSRect GTMAlignRectangles(NSRect alignee, NSRect aligner, GTMRectAlignment align
break;
case GTMRectAlignBottom:
- alignee.origin.x = aligner.origin.x + (NSWidth(aligner) * .5f - NSWidth(alignee) * .5f);
+ alignee.origin.x = aligner.origin.x + (CGRectGetWidth(aligner) * .5f - CGRectGetWidth(alignee) * .5f);
alignee.origin.y = aligner.origin.y;
break;
case GTMRectAlignBottomRight:
- alignee.origin.x = aligner.origin.x + NSWidth(aligner) - NSWidth(alignee);
+ alignee.origin.x = aligner.origin.x + CGRectGetWidth(aligner) - CGRectGetWidth(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);
+ alignee.origin.x = aligner.origin.x + CGRectGetWidth(aligner) - CGRectGetWidth(alignee);
+ alignee.origin.y = aligner.origin.y + (CGRectGetHeight(aligner) * .5f - CGRectGetHeight(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);
+ alignee.origin.x = aligner.origin.x + (CGRectGetWidth(aligner) * .5f - CGRectGetWidth(alignee) * .5f);
+ alignee.origin.y = aligner.origin.y + (CGRectGetHeight(aligner) * .5f - CGRectGetHeight(alignee) * .5f);
break;
}
return alignee;
}
-NSRect GTMScaleRectangleToSize(NSRect scalee, NSSize size, GTMScaling scaling) {
+CGRect GTMCGScaleRectangleToSize(CGRect scalee, CGSize size, GTMScaling scaling) {
switch (scaling) {
case GTMScaleProportionally: {
- CGFloat height = NSHeight(scalee);
- CGFloat width = NSWidth(scalee);
+ CGFloat height = CGRectGetHeight(scalee);
+ CGFloat width = CGRectGetWidth(scalee);
if (isnormal(height) && isnormal(width) &&
(height > size.height || width > size.width)) {
CGFloat horiz = size.width / width;
CGFloat vert = size.height / height;
CGFloat newScale = horiz < vert ? horiz : vert;
- scalee = GTMNSRectScale(scalee, newScale, newScale);
+ scalee = GTMCGRectScale(scalee, newScale, newScale);
}
break;
}
diff --git a/Foundation/GTMGeometryUtilsTest.m b/Foundation/GTMGeometryUtilsTest.m
index 606ea6b..8a81f0d 100644
--- a/Foundation/GTMGeometryUtilsTest.m
+++ b/Foundation/GTMGeometryUtilsTest.m
@@ -19,11 +19,12 @@
#import "GTMSenTestCase.h"
#import "GTMGeometryUtils.h"
-@interface GTMGeometryUtilsTest : SenTestCase
+@interface GTMGeometryUtilsTest : GTMTestCase
@end
@implementation GTMGeometryUtilsTest
+#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
- (void)testGTMCGPointToNSPoint {
CGPoint cgPoint = CGPointMake(15.1,6.2);
NSPoint nsPoint = GTMCGPointToNSPoint(cgPoint);
@@ -61,19 +62,48 @@
STAssertTrue(CGSizeEqualToSize(cgSize, *(CGSize*)&nsSize), nil);
}
+- (void)testGTMNSPointsOnRect {
+ NSRect rect = NSMakeRect(0, 0, 2, 2);
+
+ NSPoint point = GTMNSMidMinX(rect);
+ STAssertEqualsWithAccuracy(point.y, (CGFloat)1.0, (CGFloat)0.01, nil);
+ STAssertEqualsWithAccuracy(point.x, (CGFloat)0.0, (CGFloat)0.01, nil);
+
+ point = GTMNSMidMaxX(rect);
+ STAssertEqualsWithAccuracy(point.y, (CGFloat)1.0, (CGFloat)0.01, nil);
+ STAssertEqualsWithAccuracy(point.x, (CGFloat)2.0, (CGFloat)0.01, nil);
+
+ point = GTMNSMidMaxY(rect);
+ STAssertEqualsWithAccuracy(point.y, (CGFloat)2.0, (CGFloat)0.01, nil);
+ STAssertEqualsWithAccuracy(point.x, (CGFloat)1.0, (CGFloat)0.01, nil);
+
+ point = GTMNSMidMinY(rect);
+ STAssertEqualsWithAccuracy(point.y, (CGFloat)0.0, (CGFloat)0.01, nil);
+ STAssertEqualsWithAccuracy(point.x, (CGFloat)1.0, (CGFloat)0.01, nil);
+}
+
+- (void)testGTMNSRectScaling {
+ NSRect rect = NSMakeRect(1.0f, 2.0f, 5.0f, 10.0f);
+ NSRect rect2 = NSMakeRect((CGFloat)1.0, (CGFloat)2.0, (CGFloat)1.0, (CGFloat)12.0);
+ STAssertEquals(GTMNSRectScale(rect, (CGFloat)0.2, (CGFloat)1.2),
+ rect2, nil);
+}
+
+#endif // #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
+
- (void)testGTMDistanceBetweenPoints {
- NSPoint pt1 = NSMakePoint(0, 0);
- NSPoint pt2 = NSMakePoint(3, 4);
- STAssertEquals(GTMDistanceBetweenPoints(pt1, pt2), (CGFloat)5.0, nil);
- STAssertEquals(GTMDistanceBetweenPoints(pt2, pt1), (CGFloat)5.0, nil);
- pt1 = NSMakePoint(1, 1);
- pt2 = NSMakePoint(1, 1);
- STAssertEquals(GTMDistanceBetweenPoints(pt1, pt2), (CGFloat)0.0, nil);
+ CGPoint pt1 = CGPointMake(0, 0);
+ CGPoint pt2 = CGPointMake(3, 4);
+ STAssertEquals(GTMCGDistanceBetweenPoints(pt1, pt2), (CGFloat)5.0, nil);
+ STAssertEquals(GTMCGDistanceBetweenPoints(pt2, pt1), (CGFloat)5.0, nil);
+ pt1 = CGPointMake(1, 1);
+ pt2 = CGPointMake(1, 1);
+ STAssertEquals(GTMCGDistanceBetweenPoints(pt1, pt2), (CGFloat)0.0, nil);
}
- (void)testGTMAlignRectangles {
typedef struct {
- NSPoint expectedOrigin;
+ CGPoint expectedOrigin;
GTMRectAlignment alignment;
} TestData;
@@ -89,65 +119,51 @@
{ {1,1}, GTMRectAlignCenter },
};
- NSRect rect1 = NSMakeRect(0, 0, 4, 4);
- NSRect rect2 = NSMakeRect(0, 0, 2, 2);
+ CGRect rect1 = CGRectMake(0, 0, 4, 4);
+ CGRect rect2 = CGRectMake(0, 0, 2, 2);
+
+ CGRect expectedRect;
+ expectedRect.size = CGSizeMake(2, 2);
- for (int i = 0; i < sizeof(data) / sizeof(TestData); i++) {
- NSRect expectedRect;
+ for (size_t i = 0; i < sizeof(data) / sizeof(TestData); i++) {
expectedRect.origin = data[i].expectedOrigin;
- expectedRect.size = NSMakeSize(2, 2);
- NSRect outRect = GTMAlignRectangles(rect2, rect1, data[i].alignment);
+ CGRect outRect = GTMCGAlignRectangles(rect2, rect1, data[i].alignment);
STAssertEquals(outRect, expectedRect, nil);
}
}
-- (void)testGTMPointsOnRect {
- NSRect rect = NSMakeRect(0, 0, 2, 2);
- CGRect cgRect = GTMNSRectToCGRect(rect);
+- (void)testGTMCGPointsOnRect {
+ CGRect rect = CGRectMake(0, 0, 2, 2);
- NSPoint point = GTMNSMidLeft(rect);
- CGPoint cgPoint = GTMCGMidLeft(cgRect);
- STAssertEquals(point.x, cgPoint.x, nil);
- STAssertEquals(point.y, cgPoint.y, nil);
+ CGPoint point = GTMCGMidMinX(rect);
STAssertEqualsWithAccuracy(point.y, (CGFloat)1.0, (CGFloat)0.01, nil);
STAssertEqualsWithAccuracy(point.x, (CGFloat)0.0, (CGFloat)0.01, nil);
-
- point = GTMNSMidRight(rect);
- cgPoint = GTMCGMidRight(cgRect);
- STAssertEquals(point.x, cgPoint.x, nil);
- STAssertEquals(point.y, cgPoint.y, nil);
+
+ point = GTMCGMidMaxX(rect);
STAssertEqualsWithAccuracy(point.y, (CGFloat)1.0, (CGFloat)0.01, nil);
STAssertEqualsWithAccuracy(point.x, (CGFloat)2.0, (CGFloat)0.01, nil);
-
- point = GTMNSMidTop(rect);
- cgPoint = GTMCGMidTop(cgRect);
- STAssertEquals(point.x, cgPoint.x, nil);
- STAssertEquals(point.y, cgPoint.y, nil);
+
+ point = GTMCGMidMaxY(rect);
STAssertEqualsWithAccuracy(point.y, (CGFloat)2.0, (CGFloat)0.01, nil);
STAssertEqualsWithAccuracy(point.x, (CGFloat)1.0, (CGFloat)0.01, nil);
- point = GTMNSMidBottom(rect);
- cgPoint = GTMCGMidBottom(cgRect);
- STAssertEquals(point.x, cgPoint.x, nil);
- STAssertEquals(point.y, cgPoint.y, nil);
+ point = GTMCGMidMinY(rect);
STAssertEqualsWithAccuracy(point.y, (CGFloat)0.0, (CGFloat)0.01, nil);
STAssertEqualsWithAccuracy(point.x, (CGFloat)1.0, (CGFloat)0.01, nil);
}
-- (void)testGTMRectScaling {
- NSRect rect = NSMakeRect(1.0f, 2.0f, 5.0f, 10.0f);
- NSRect rect2 = NSMakeRect((CGFloat)1.0, (CGFloat)2.0, (CGFloat)1.0, (CGFloat)12.0);
- STAssertEquals(GTMNSRectScale(rect, (CGFloat)0.2, (CGFloat)1.2),
+- (void)testGTMCGRectScaling {
+ CGRect rect = CGRectMake(1.0f, 2.0f, 5.0f, 10.0f);
+ CGRect rect2 = CGRectMake((CGFloat)1.0, (CGFloat)2.0, (CGFloat)1.0, (CGFloat)12.0);
+ STAssertEquals(GTMCGRectScale(rect, (CGFloat)0.2, (CGFloat)1.2),
rect2, nil);
- STAssertEquals(GTMCGRectScale(GTMNSRectToCGRect(rect), (CGFloat)0.2, (CGFloat)1.2),
- GTMNSRectToCGRect(rect2), nil);
}
- (void)testGTMScaleRectangleToSize {
- NSRect rect = NSMakeRect(0.0f, 0.0f, 10.0f, 10.0f);
+ CGRect rect = CGRectMake(0.0f, 0.0f, 10.0f, 10.0f);
typedef struct {
- NSSize size_;
- NSSize newSize_;
+ CGSize size_;
+ CGSize newSize_;
} Test;
Test tests[] = {
{ { 5.0, 10.0 }, { 5.0, 5.0 } },
@@ -161,21 +177,21 @@
};
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);
+ CGRect result = GTMCGScaleRectangleToSize(rect, tests[i].size_,
+ GTMScaleProportionally);
+ STAssertEquals(result, GTMCGRectOfSize(tests[i].newSize_), @"failed on test %z", i);
}
- NSRect result = GTMScaleRectangleToSize(NSZeroRect, tests[0].size_,
- GTMScaleProportionally);
- STAssertEquals(result, NSZeroRect, nil);
+ CGRect result = GTMCGScaleRectangleToSize(CGRectZero, tests[0].size_,
+ GTMScaleProportionally);
+ STAssertEquals(result, CGRectZero, nil);
- result = GTMScaleRectangleToSize(rect, tests[0].size_,
- GTMScaleToFit);
- STAssertEquals(result, GTMNSRectOfSize(tests[0].size_), nil);
+ result = GTMCGScaleRectangleToSize(rect, tests[0].size_,
+ GTMScaleToFit);
+ STAssertEquals(result, GTMCGRectOfSize(tests[0].size_), nil);
- result = GTMScaleRectangleToSize(rect, tests[0].size_,
- GTMScaleNone);
+ result = GTMCGScaleRectangleToSize(rect, tests[0].size_,
+ GTMScaleNone);
STAssertEquals(result, rect, nil);
}
@end
diff --git a/Foundation/GTMHTTPFetcher.m b/Foundation/GTMHTTPFetcher.m
index 9853d0d..cac49df 100644
--- a/Foundation/GTMHTTPFetcher.m
+++ b/Foundation/GTMHTTPFetcher.m
@@ -65,6 +65,7 @@ const NSTimeInterval kDefaultMaxRetryInterval = 60. * 10.; // 10 minutes
- (NSArray *)cookiesForURL:(NSURL *)theURL inArray:(NSMutableArray *)cookieStorageArray;
- (void)handleCookiesForResponse:(NSURLResponse *)response;
- (BOOL)shouldRetryNowForStatus:(NSInteger)status error:(NSError *)error;
+- (void)retryTimerFired:(NSTimer *)timer;
- (void)destroyRetryTimer;
- (void)beginRetryTimer;
- (void)primeTimerWithNewTimeInterval:(NSTimeInterval)secs;
@@ -135,7 +136,7 @@ const NSTimeInterval kDefaultMaxRetryInterval = 60. * 10.; // 10 minutes
GTMAssertSelectorNilOrImplementedWithArguments(delegate, finishedSEL, @encode(GTMHTTPFetcher *), @encode(NSData *), NULL);
GTMAssertSelectorNilOrImplementedWithArguments(delegate, failedSEL, @encode(GTMHTTPFetcher *), @encode(NSError *), NULL);
GTMAssertSelectorNilOrImplementedWithArguments(delegate, receivedDataSEL_, @encode(GTMHTTPFetcher *), @encode(NSData *), NULL);
- GTMAssertSelectorNilOrImplementedWithArguments(delegate, retrySEL_, @encode(GTMHTTPFetcher *), @encode(BOOL), @encode(NSError *), NULL);
+ GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments(delegate, retrySEL_, @encode(BOOL), @encode(GTMHTTPFetcher *), @encode(BOOL), @encode(NSError *), NULL);
if (connection_ != nil) {
_GTMDevAssert(connection_ != nil,
@@ -244,7 +245,7 @@ const NSTimeInterval kDefaultMaxRetryInterval = 60. * 10.; // 10 minutes
delegate:self
startImmediately:NO];
- for (int idx = 0; idx < [runLoopModes count]; idx++) {
+ for (NSUInteger idx = 0; idx < [runLoopModes count]; idx++) {
NSString *mode = [runLoopModes objectAtIndex:idx];
[connection_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:mode];
}
@@ -1412,7 +1413,7 @@ static NSString* gLoggingProcessName = nil;
NSMutableData *mutableData = [NSMutableData dataWithData:data];
unsigned char *bytes = [mutableData mutableBytes];
- for (int idx = 0; idx < [mutableData length]; idx++) {
+ for (NSUInteger idx = 0; idx < [mutableData length]; idx++) {
if (bytes[idx] > 0x7F || bytes[idx] == 0) {
bytes[idx] = '_';
}
@@ -1436,7 +1437,7 @@ static NSString* gLoggingProcessName = nil;
// convert those into UTF-8
NSMutableArray *origParts = [NSMutableArray array];
NSUInteger offset = 0;
- for (int partIdx = 0; partIdx < [mungedParts count]; partIdx++) {
+ for (NSUInteger partIdx = 0; partIdx < [mungedParts count]; partIdx++) {
NSString *mungedPart = [mungedParts objectAtIndex:partIdx];
NSUInteger partSize = [mungedPart length];
diff --git a/Foundation/GTMHTTPFetcherTest.m b/Foundation/GTMHTTPFetcherTest.m
index 16c8d0f..3722e9b 100644
--- a/Foundation/GTMHTTPFetcherTest.m
+++ b/Foundation/GTMHTTPFetcherTest.m
@@ -16,12 +16,11 @@
// the License.
//
-#import <SenTestingKit/SenTestingKit.h>
-#import <unistd.h>
-#import "GTMHTTPFetcher.h"
#import "GTMSenTestCase.h"
+#import "GTMHTTPFetcher.h"
+#import "GTMTestHTTPServer.h"
-@interface GTMHTTPFetcherTest : SenTestCase {
+@interface GTMHTTPFetcherTest : GTMTestCase {
// these ivars are checked after fetches, and are reset by resetFetchResponse
NSData *fetchedData_;
NSError *fetcherError_;
@@ -31,10 +30,7 @@
// setup/teardown ivars
NSMutableDictionary *fetchHistory_;
- NSTask *server_; // python http server
- BOOL didServerLaunch_; // Tracks the state of our server
- BOOL didServerDie_;
- NSMutableData *launchBuffer_; // Storage for output from our server
+ GTMTestHTTPServer *testServer_;
}
@end
@@ -49,11 +45,18 @@
userData:(id)userData;
- (NSString *)fileURLStringToTestFileName:(NSString *)name;
+- (BOOL)countRetriesFetcher:(GTMHTTPFetcher *)fetcher
+ willRetry:(BOOL)suggestedWillRetry
+ forError:(NSError *)error;
+- (BOOL)fixRequestFetcher:(GTMHTTPFetcher *)fetcher
+ willRetry:(BOOL)suggestedWillRetry
+ forError:(NSError *)error;
+- (void)testFetcher:(GTMHTTPFetcher *)fetcher finishedWithData:(NSData *)data;
+- (void)testFetcher:(GTMHTTPFetcher *)fetcher failedWithError:(NSError *)error;
@end
@implementation GTMHTTPFetcherTest
-static const int kServerPortNumber = 54579;
static const NSTimeInterval kRunLoopInterval = 0.01;
// The bogus-fetch test can take >10s to pass. Pick something way higher
// to avoid failing.
@@ -61,96 +64,18 @@ static const NSTimeInterval kGiveUpInterval = 60.0; // bail on the test if 60 se
static NSString *const kValidFileName = @"GTMHTTPFetcherTestPage.html";
-- (void)gotData:(NSNotification*)notification {
- // our server sends out a string to confirm that it launched
- NSFileHandle *handle = [notification object];
- NSData *launchMessageData = [handle availableData];
- [launchBuffer_ appendData:launchMessageData];
- NSString *launchStr =
- [[[NSString alloc] initWithData:launchBuffer_
- encoding:NSUTF8StringEncoding] autorelease];
- didServerLaunch_ =
- [launchStr rangeOfString:@"started GTMHTTPFetcherTestServer"].location != NSNotFound;
- if (!didServerLaunch_) {
- _GTMDevLog(@"gotData launching httpserver: %@", launchStr);
- [handle readInBackgroundAndNotify];
- }
-}
-
-- (void)didDie:(NSNotification*)notification {
- _GTMDevLog(@"server died");
- didServerDie_ = YES;
-}
-
-
- (void)setUp {
fetchHistory_ = [[NSMutableDictionary alloc] init];
- // run the python http server, located in the Tests directory
NSBundle *testBundle = [NSBundle bundleForClass:[self class]];
STAssertNotNil(testBundle, nil);
+ NSString *docRoot = [testBundle pathForResource:@"GTMHTTPFetcherTestPage"
+ ofType:@"html"];
+ docRoot = [docRoot stringByDeletingLastPathComponent];
+ STAssertNotNil(docRoot, nil);
- NSString *serverPath =
- [testBundle pathForResource:@"GTMHTTPFetcherTestServer" ofType:@""];
- STAssertNotNil(serverPath, nil);
-
- NSArray *argArray = [NSArray arrayWithObjects:serverPath,
- @"-p", [NSString stringWithFormat:@"%d", kServerPortNumber],
- @"-r", [serverPath stringByDeletingLastPathComponent], nil];
-
- server_ = [[NSTask alloc] init];
- [server_ setArguments:argArray];
- [server_ setLaunchPath:@"/usr/bin/python"];
- [server_ setEnvironment:[NSDictionary dictionary]]; // don't inherit anything from us
-
- // pipes will be cleaned up when server_ is torn down.
- NSPipe *outputPipe = [NSPipe pipe];
- NSPipe *errorPipe = [NSPipe pipe];
-
- [server_ setStandardOutput:outputPipe];
- [server_ setStandardError:errorPipe];
-
- NSFileHandle *outputHandle = [outputPipe fileHandleForReading];
- NSFileHandle *errorHandle = [errorPipe fileHandleForReading];
-
- didServerLaunch_ = NO;
- didServerDie_ = NO;
-
- NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
- [center addObserver:self
- selector:@selector(gotData:)
- name:NSFileHandleDataAvailableNotification
- object:outputHandle];
- [center addObserver:self
- selector:@selector(gotData:)
- name:NSFileHandleDataAvailableNotification
- object:errorHandle];
- [center addObserver:self
- selector:@selector(didDie:)
- name:NSTaskDidTerminateNotification
- object:server_];
-
- [launchBuffer_ autorelease];
- launchBuffer_ = [[NSMutableData data] retain];
- [outputHandle waitForDataInBackgroundAndNotify];
- [errorHandle waitForDataInBackgroundAndNotify];
- [server_ launch];
-
- NSDate* giveUpDate = [NSDate dateWithTimeIntervalSinceNow:kGiveUpInterval];
- while ((!didServerDie_ && !didServerLaunch_) &&
- [giveUpDate timeIntervalSinceNow] > 0) {
- NSDate* loopIntervalDate =
- [NSDate dateWithTimeIntervalSinceNow:kRunLoopInterval];
- [[NSRunLoop currentRunLoop] runUntilDate:loopIntervalDate];
- }
-
- [center removeObserver:self];
-
- STAssertTrue(didServerLaunch_ && [server_ isRunning] && !didServerDie_,
- @"Python http server not launched.\n"
- "Args:%@\n"
- "Environment:%@\n",
- [argArray componentsJoinedByString:@" "], [server_ environment]);
+ testServer_ = [[GTMTestHTTPServer alloc] initWithDocRoot:docRoot];
+ STAssertNotNil(testServer_, @"failed to create a testing server");
}
- (void)resetFetchResponse {
@@ -170,12 +95,9 @@ static NSString *const kValidFileName = @"GTMHTTPFetcherTestPage.html";
}
- (void)tearDown {
- [server_ terminate];
- [server_ waitUntilExit];
- [server_ release];
- server_ = nil;
- [launchBuffer_ release];
- launchBuffer_ = nil;
+ [testServer_ release];
+ testServer_ = nil;
+
[self resetFetchResponse];
[fetchHistory_ release];
@@ -451,8 +373,7 @@ static NSString *const kValidFileName = @"GTMHTTPFetcherTestPage.html";
// return a localhost:port URL for the test file
NSString *urlString = [NSString stringWithFormat:@"http://localhost:%d/%@",
- kServerPortNumber, name];
-
+ [testServer_ port], name];
// we exclude the "?status=" that would indicate that the URL
// should cause a retryable error
@@ -540,4 +461,3 @@ static NSString *const kValidFileName = @"GTMHTTPFetcherTestPage.html";
}
@end
-
diff --git a/Foundation/GTMHTTPServer.h b/Foundation/GTMHTTPServer.h
new file mode 100644
index 0000000..70e3f78
--- /dev/null
+++ b/Foundation/GTMHTTPServer.h
@@ -0,0 +1,133 @@
+//
+// GTMHTTPServer.h
+//
+// This is a *very* *simple* webserver that can be built into something, it is
+// not meant to stand up a site, it sends all requests to its delegate for
+// processing on the main thread. It does not support pipelining, etc. It's
+// great for places where you need a simple webserver to unittest some code
+// that hits a server.
+//
+// NOTE: there are several TODOs left in here as markers for things that could
+// be done if one wanted to add more to this class.
+//
+// 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.
+//
+// Based a little on HTTPServer, part of the CocoaHTTPServer sample code
+// http://developer.apple.com/samplecode/CocoaHTTPServer/index.html
+//
+
+#import <Foundation/Foundation.h>
+#import "GTMDefines.h"
+
+// Global contants needed for errors from start
+
+#undef _EXTERN
+#undef _INITIALIZE_AS
+#ifdef GTMHTTPSERVER_DEFINE_GLOBALS
+#define _EXTERN
+#define _INITIALIZE_AS(x) =x
+#else
+#define _EXTERN extern
+#define _INITIALIZE_AS(x)
+#endif
+
+_EXTERN NSString* kGTMHTTPServerErrorDomain _INITIALIZE_AS(@"com.google.mactoolbox.HTTPServerDomain");
+enum {
+ kGTMHTTPServerSocketCreateFailedError = -100,
+ kGTMHTTPServerBindFailedError = -101,
+ kGTMHTTPServerListenFailedError = -102,
+ kGTMHTTPServerHandleCreateFailedError = -103,
+};
+
+@class GTMHTTPRequestMessage, GTMHTTPResponseMessage;
+
+// ----------------------------------------------------------------------------
+
+// See comment at top of file for the intened use of this class.
+@interface GTMHTTPServer : NSObject {
+ @private
+ id delegate_;
+ uint16_t port_;
+ BOOL localhostOnly_;
+ NSFileHandle *listenHandle_;
+ NSMutableArray *connections_;
+}
+
+// The delegate must support the httpServer:handleRequest: method in
+// NSObject(GTMHTTPServerDeletateMethods) below.
+- (id)initWithDelegate:(id)delegate;
+
+- (id)delegate;
+
+// Passing port zero will let one get assigned.
+- (uint16_t)port;
+- (void)setPort:(uint16_t)port;
+
+// Receive connections on the localHost loopback address only or on all
+// interfaces for this machine. The default is to only listen on localhost.
+- (BOOL)localhostOnly;
+- (void)setLocalhostOnly:(BOOL)yesno;
+
+// Start/Stop the web server. If there is an error starting up the server, |NO|
+// is returned, and the specific startup failure can be returned in |error| (see
+// above for the error domain and error codes). If the server is started, |YES|
+// is returned and the server's delegate is called for any requests that come
+// in.
+- (BOOL)start:(NSError **)error;
+- (void)stop;
+
+// returns the number of requests currently active in the server (i.e.-being
+// read in, sent replies).
+- (NSUInteger)activeRequestCount;
+
+@end
+
+@interface NSObject (GTMHTTPServerDeletateMethods)
+- (GTMHTTPResponseMessage *)httpServer:(GTMHTTPServer *)server
+ handleRequest:(GTMHTTPRequestMessage *)request;
+@end
+
+// ----------------------------------------------------------------------------
+
+// Encapsulates an http request, one of these is sent to the server's delegate
+// for each request.
+@interface GTMHTTPRequestMessage : NSObject {
+ @private
+ CFHTTPMessageRef message_;
+}
+- (NSString *)version;
+- (NSURL *)URL;
+- (NSString *)method;
+- (NSData *)body;
+- (NSDictionary *)allHeaderFieldValues;
+@end
+
+// ----------------------------------------------------------------------------
+
+// Encapsulates an http response, the server's delegate should return one for
+// each request received.
+@interface GTMHTTPResponseMessage : NSObject {
+ @private
+ CFHTTPMessageRef message_;
+}
++ (id)responseWithHTMLString:(NSString *)htmlString;
++ (id)responseWithBody:(NSData *)body
+ contentType:(NSString *)contentType
+ statusCode:(int)statusCode;
++ (id)emptyResponseWithCode:(int)statusCode;
+// TODO: class method for redirections?
+// TODO: add helper for expire/no-cache
+- (void)setValue:(NSString*)value forHeaderField:(NSString*)headerField;
+@end
diff --git a/Foundation/GTMHTTPServer.m b/Foundation/GTMHTTPServer.m
new file mode 100644
index 0000000..daa6a4e
--- /dev/null
+++ b/Foundation/GTMHTTPServer.m
@@ -0,0 +1,584 @@
+//
+// GTMHTTPServer.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.
+//
+// Based a little on HTTPServer, part of the CocoaHTTPServer sample code
+// http://developer.apple.com/samplecode/CocoaHTTPServer/index.html
+//
+
+#import <netinet/in.h>
+#import <sys/socket.h>
+#import <unistd.h>
+
+#define GTMHTTPSERVER_DEFINE_GLOBALS
+#import "GTMHTTPServer.h"
+#import "GTMDebugSelectorValidation.h"
+#import "GTMGarbageCollection.h"
+#import "GTMDefines.h"
+
+@interface GTMHTTPServer (PrivateMethods)
+- (void)acceptedConnectionNotification:(NSNotification *)notification;
+- (NSMutableDictionary *)newConnectionWithFileHandle:(NSFileHandle *)fileHandle;
+- (void)dataAvailableNotification:(NSNotification *)notification;
+- (NSMutableDictionary *)lookupConnection:(NSFileHandle *)fileHandle;
+- (void)closeConnection:(NSMutableDictionary *)connDict;
+- (void)sendResponseOnNewThread:(NSMutableDictionary *)connDict;
+- (void)sentResponse:(NSMutableDictionary *)connDict;
+@end
+
+// keys for our connection dictionaries
+static NSString *kFileHandle = @"FileHandle";
+static NSString *kRequest = @"Request";
+static NSString *kResponse = @"Response";
+
+@interface GTMHTTPRequestMessage (PrivateHelpers)
+- (BOOL)isHeaderComplete;
+- (BOOL)appendData:(NSData *)data;
+- (NSString *)headerFieldValueForKey:(NSString *)key;
+- (UInt32)contentLength;
+- (void)setBody:(NSData *)body;
+@end
+
+@interface GTMHTTPResponseMessage (PrivateMethods)
+- (id)initWithBody:(NSData *)body
+ contentType:(NSString *)contentType
+ statusCode:(int)statusCode;
+- (NSData*)serializedData;
+@end
+
+@implementation GTMHTTPServer
+
+- (id)init {
+ return [self initWithDelegate:nil];
+}
+
+- (id)initWithDelegate:(id)delegate {
+ self = [super init];
+ if (self) {
+ if (!delegate) {
+ _GTMDevLog(@"missing delegate");
+ [self release];
+ return nil;
+ }
+ delegate_ = delegate;
+ GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments(delegate_,
+ @selector(httpServer:handleRequest:),
+ // return type
+ @encode(GTMHTTPResponseMessage *),
+ // args
+ @encode(GTMHTTPServer *),
+ @encode(GTMHTTPRequestMessage *),
+ NULL);
+ localhostOnly_ = YES;
+ connections_ = [[NSMutableArray alloc] init];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [self stop];
+ [super dealloc];
+}
+
+- (void)finalize {
+ [self stop];
+ [super finalize];
+}
+
+- (id)delegate {
+ return delegate_;
+}
+
+- (uint16_t)port {
+ return port_;
+}
+
+- (void)setPort:(uint16_t)port {
+ port_ = port;
+}
+
+- (BOOL)localhostOnly {
+ return localhostOnly_;
+}
+
+- (void)setLocalhostOnly:(BOOL)yesno {
+ localhostOnly_ = yesno;
+}
+
+- (BOOL)start:(NSError **)error {
+ _GTMDevAssert(listenHandle_ == nil,
+ @"start called when we already have a listenHandle_");
+
+ if (error) *error = NULL;
+
+ NSInteger startFailureCode = 0;
+ int fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd <= 0) {
+ // COV_NF_START - we'd need to use up *all* sockets to test this?
+ startFailureCode = kGTMHTTPServerSocketCreateFailedError;
+ goto startFailed;
+ // COV_NF_END
+ }
+
+ // enable address reuse quicker after we are done w/ our socket
+ int yes = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (void *)&yes, (socklen_t)sizeof(yes)) != 0) {
+ _GTMDevLog(@"failed to mark the socket as reusable"); // COV_NF_LINE
+ }
+
+ // bind
+ struct sockaddr_in addr;
+ bzero(&addr, sizeof(addr));
+ addr.sin_len = sizeof(addr);
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port_);
+ if (localhostOnly_) {
+ addr.sin_addr.s_addr = htonl(0x7F000001);
+ } else {
+ // COV_NF_START - testing this could cause a leopard firewall prompt during tests.
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ // COV_NF_END
+ }
+ if (bind(fd, (struct sockaddr*)(&addr), (socklen_t)sizeof(addr)) != 0) {
+ startFailureCode = kGTMHTTPServerBindFailedError;
+ goto startFailed;
+ }
+
+ // collect the port back out
+ if (port_ == 0) {
+ socklen_t len = (socklen_t)sizeof(addr);
+ if (getsockname(fd, (struct sockaddr*)(&addr), &len) == 0) {
+ port_ = ntohs(addr.sin_port);
+ }
+ }
+
+ // tell it to listen for connections
+ if (listen(fd, 5) != 0) {
+ // COV_NF_START
+ startFailureCode = kGTMHTTPServerListenFailedError;
+ goto startFailed;
+ // COV_NF_END
+ }
+
+ // now use a filehandle to accept connections
+ listenHandle_ =
+ [[NSFileHandle alloc] initWithFileDescriptor:fd closeOnDealloc:YES];
+ if (listenHandle_ == nil) {
+ // COV_NF_START - we'd need to run out of memory to test this?
+ startFailureCode = kGTMHTTPServerHandleCreateFailedError;
+ goto startFailed;
+ // COV_NF_END
+ }
+
+ // setup notifications for connects
+ NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+ [center addObserver:self
+ selector:@selector(acceptedConnectionNotification:)
+ name:NSFileHandleConnectionAcceptedNotification
+ object:listenHandle_];
+ [listenHandle_ acceptConnectionInBackgroundAndNotify];
+
+ // TODO: maybe hit the delegate incase it wants to register w/ NSNetService,
+ // or just know we're up and running?
+
+ return YES;
+
+startFailed:
+ if (error) {
+ *error = [[[NSError alloc] initWithDomain:kGTMHTTPServerErrorDomain
+ code:startFailureCode
+ userInfo:nil] autorelease];
+ }
+ if (fd > 0) {
+ close(fd);
+ }
+ return NO;
+}
+
+- (void)stop {
+ if (listenHandle_) {
+ NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+ [center removeObserver:self
+ name:NSFileHandleConnectionAcceptedNotification
+ object:listenHandle_];
+ [listenHandle_ release];
+ listenHandle_ = nil;
+ // TODO: maybe hit the delegate in case it wants to unregister w/
+ // NSNetService, or just know we've stopped running?
+ }
+ [connections_ removeAllObjects];
+}
+
+- (NSUInteger)activeRequestCount {
+ return [connections_ count];
+}
+
+- (NSString *)description {
+ NSString *result =
+ [NSString stringWithFormat:@"%@<%p>{ port=%d localHostOnly=%@ status=%@ }",
+ [self class], self, port_, (localhostOnly_ ? @"YES" : @"NO"),
+ (listenHandle_ != nil ? @"Started" : @"Stopped") ];
+ return result;
+}
+
+
+@end
+
+@implementation GTMHTTPServer (PrivateMethods)
+
+- (void)acceptedConnectionNotification:(NSNotification *)notification {
+ NSDictionary *userInfo = [notification userInfo];
+ NSFileHandle *newConnection =
+ [userInfo objectForKey:NSFileHandleNotificationFileHandleItem];
+ _GTMDevAssert(newConnection != nil,
+ @"failed to get the connection in the notification: %@",
+ notification);
+
+ // make sure we accept more...
+ [listenHandle_ acceptConnectionInBackgroundAndNotify];
+
+ // TODO: could let the delegate look at the address, before we start working
+ // on it.
+
+ NSMutableDictionary *connDict =
+ [self newConnectionWithFileHandle:newConnection];
+ [connections_ addObject:connDict];
+}
+
+- (NSMutableDictionary *)newConnectionWithFileHandle:(NSFileHandle *)fileHandle {
+ NSMutableDictionary *result = [NSMutableDictionary dictionary];
+
+ [result setObject:fileHandle forKey:kFileHandle];
+
+ GTMHTTPRequestMessage *request =
+ [[[GTMHTTPRequestMessage alloc] init] autorelease];
+ [result setObject:request forKey:kRequest];
+
+ // setup for data notifications
+ NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+ [center addObserver:self
+ selector:@selector(dataAvailableNotification:)
+ name:NSFileHandleReadCompletionNotification
+ object:fileHandle];
+ [fileHandle readInBackgroundAndNotify];
+
+ return result;
+}
+
+- (void)dataAvailableNotification:(NSNotification *)notification {
+ NSFileHandle *connectionHandle = [notification object];
+ NSMutableDictionary *connDict = [self lookupConnection:connectionHandle];
+ if (connDict == nil) return; // we are no longer tracking this one
+
+ NSDictionary *userInfo = [notification userInfo];
+ NSData *readData = [userInfo objectForKey:NSFileHandleNotificationDataItem];
+ if ([readData length] == 0) {
+ // remote side closed
+ [self closeConnection:connDict];
+ return;
+ }
+
+ // Like Apple's sample, we just keep adding data until we get a full header
+ // and any referenced body.
+
+ GTMHTTPRequestMessage *request = [connDict objectForKey:kRequest];
+ [request appendData:readData];
+
+ // Is the header complete yet?
+ if (![request isHeaderComplete]) {
+ // more data...
+ [connectionHandle readInBackgroundAndNotify];
+ return;
+ }
+
+ // Do we have all the body?
+ UInt32 contentLength = [request contentLength];
+ NSData *body = [request body];
+ NSUInteger bodyLength = [body length];
+ if (contentLength > bodyLength) {
+ // need more data...
+ [connectionHandle readInBackgroundAndNotify];
+ return;
+ }
+
+ if (contentLength < bodyLength) {
+ // We got extra (probably someone trying to pipeline on us), trim
+ // and let the extra data go...
+ NSData *newBody = [NSData dataWithBytes:[body bytes]
+ length:contentLength];
+ [request setBody:newBody];
+ _GTMDevLog(@"Got %lu extra bytes on http request, ignoring them",
+ (unsigned long)(bodyLength - contentLength));
+ }
+
+ GTMHTTPResponseMessage *response = nil;
+ @try {
+ // Off to the delegate
+ response = [delegate_ httpServer:self handleRequest:request];
+ } @catch (NSException *e) {
+ _GTMDevLog(@"Exception trying to handle http request: %@", e);
+ }
+
+ if (!response) {
+ [self closeConnection:connDict];
+ return;
+ }
+
+ // We don't support connection reuse, so we add (force) the header to close
+ // every connection.
+ [response setValue:@"close" forHeaderField:@"Connection"];
+
+ // spawn thread to send reply (since we do a blocking send)
+ [connDict setObject:response forKey:kResponse];
+ [NSThread detachNewThreadSelector:@selector(sendResponseOnNewThread:)
+ toTarget:self
+ withObject:connDict];
+}
+
+- (NSMutableDictionary *)lookupConnection:(NSFileHandle *)fileHandle {
+ NSUInteger max = [connections_ count];
+ for (NSUInteger x = 0; x < max; ++x) {
+ NSMutableDictionary *connDict = [connections_ objectAtIndex:x];
+ if (fileHandle == [connDict objectForKey:kFileHandle]) {
+ return connDict;
+ }
+ }
+ return nil;
+}
+
+- (void)closeConnection:(NSMutableDictionary *)connDict {
+ // remove the notification
+ NSFileHandle *connectionHandle = [connDict objectForKey:kFileHandle];
+ NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+ [center removeObserver:self
+ name:NSFileHandleReadCompletionNotification
+ object:connectionHandle];
+
+ // remove it from the list
+ [connections_ removeObject:connDict];
+}
+
+- (void)sendResponseOnNewThread:(NSMutableDictionary *)connDict {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ @try {
+ GTMHTTPResponseMessage *response = [connDict objectForKey:kResponse];
+ NSFileHandle *connectionHandle = [connDict objectForKey:kFileHandle];
+ NSData *serialized = [response serializedData];
+ [connectionHandle writeData:serialized];
+ } @catch (NSException *e) {
+ // TODO: let the delegate know about the exception (but do it on the main
+ // thread)
+ _GTMDevLog(@"exception while sending reply: %@", e);
+ }
+
+ // back to the main thread to close things down
+ [self performSelectorOnMainThread:@selector(sentResponse:)
+ withObject:connDict
+ waitUntilDone:NO];
+
+ [pool release];
+}
+
+- (void)sentResponse:(NSMutableDictionary *)connDict {
+ // make sure we're still tracking this connection (in case server was stopped)
+ NSFileHandle *connection = [connDict objectForKey:kFileHandle];
+ NSMutableDictionary *connDict2 = [self lookupConnection:connection];
+ if (connDict != connDict2) return;
+
+ // TODO: message the delegate that it was sent
+
+ // close it down
+ [self closeConnection:connDict];
+}
+
+@end
+
+#pragma mark -
+
+@implementation GTMHTTPRequestMessage
+
+- (id)init {
+ self = [super init];
+ if (self) {
+ message_ = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, YES);
+ }
+ return self;
+}
+
+- (void)dealloc {
+ CFRelease(message_);
+ [super dealloc];
+}
+
+- (NSString *)version {
+ return [GTMNSMakeCollectable(CFHTTPMessageCopyVersion(message_)) autorelease];
+}
+
+- (NSURL *)URL {
+ return [GTMNSMakeCollectable(CFHTTPMessageCopyRequestURL(message_)) autorelease];
+}
+
+- (NSString *)method {
+ return [GTMNSMakeCollectable(CFHTTPMessageCopyRequestMethod(message_)) autorelease];
+}
+
+- (NSData *)body {
+ return [GTMNSMakeCollectable(CFHTTPMessageCopyBody(message_)) autorelease];
+}
+
+- (NSDictionary *)allHeaderFieldValues {
+ return GTMNSMakeCollectable(CFHTTPMessageCopyAllHeaderFields(message_));
+}
+
+- (NSString *)description {
+ CFStringRef desc = CFCopyDescription(message_);
+ NSString *result =
+ [NSString stringWithFormat:@"%@<%p>{ message=%@ }", [self class], self, desc];
+ CFRelease(desc);
+ return result;
+}
+
+@end
+
+@implementation GTMHTTPRequestMessage (PrivateHelpers)
+
+- (BOOL)isHeaderComplete {
+ return CFHTTPMessageIsHeaderComplete(message_) ? YES : NO;
+}
+
+- (BOOL)appendData:(NSData *)data {
+ return CFHTTPMessageAppendBytes(message_,
+ [data bytes], [data length]) ? YES : NO;
+}
+
+- (NSString *)headerFieldValueForKey:(NSString *)key {
+ CFStringRef value = NULL;
+ if (key) {
+ value = CFHTTPMessageCopyHeaderFieldValue(message_, (CFStringRef)key);
+ }
+ return [GTMNSMakeCollectable(value) autorelease];
+}
+
+- (UInt32)contentLength {
+ return [[self headerFieldValueForKey:@"Content-Length"] intValue];
+}
+
+- (void)setBody:(NSData *)body {
+ if (!body) {
+ body = [NSData data]; // COV_NF_LINE - can only happen in we fail to make the new data object
+ }
+ CFHTTPMessageSetBody(message_, (CFDataRef)body);
+}
+
+@end
+
+#pragma mark -
+
+@implementation GTMHTTPResponseMessage
+
+- (id)init {
+ return [self initWithBody:nil contentType:nil statusCode:0];
+}
+
+- (void)dealloc {
+ if (message_) {
+ CFRelease(message_);
+ }
+ [super dealloc];
+}
+
++ (id)responseWithHTMLString:(NSString *)htmlString {
+ return [self responseWithBody:[htmlString dataUsingEncoding:NSUTF8StringEncoding]
+ contentType:@"text/html; charset=UTF-8"
+ statusCode:200];
+}
+
++ (id)responseWithBody:(NSData *)body
+ contentType:(NSString *)contentType
+ statusCode:(int)statusCode {
+ return [[[[self class] alloc] initWithBody:body
+ contentType:contentType
+ statusCode:statusCode] autorelease];
+}
+
++ (id)emptyResponseWithCode:(int)statusCode {
+ return [[[[self class] alloc] initWithBody:nil
+ contentType:nil
+ statusCode:statusCode] autorelease];
+}
+
+- (void)setValue:(NSString*)value forHeaderField:(NSString*)headerField {
+ if ([headerField length] == 0) return;
+ if (value == nil) {
+ value = @"";
+ }
+ CFHTTPMessageSetHeaderFieldValue(message_,
+ (CFStringRef)headerField, (CFStringRef)value);
+}
+
+- (NSString *)description {
+ CFStringRef desc = CFCopyDescription(message_);
+ NSString *result =
+ [NSString stringWithFormat:@"%@<%p>{ message=%@ }", [self class], self, desc];
+ CFRelease(desc);
+ return result;
+}
+
+@end
+
+@implementation GTMHTTPResponseMessage (PrivateMethods)
+
+- (id)initWithBody:(NSData *)body
+ contentType:(NSString *)contentType
+ statusCode:(int)statusCode {
+ self = [super init];
+ if (self) {
+ if ((statusCode < 100) || (statusCode > 599)) {
+ [self release];
+ return nil;
+ }
+ message_ = CFHTTPMessageCreateResponse(kCFAllocatorDefault,
+ statusCode, NULL,
+ kCFHTTPVersion1_0);
+ if (!message_) {
+ // COV_NF_START
+ [self release];
+ return nil;
+ // COV_NF_END
+ }
+ NSUInteger bodyLength = 0;
+ if (body) {
+ bodyLength = [body length];
+ CFHTTPMessageSetBody(message_, (CFDataRef)body);
+ }
+ if ([contentType length] == 0) {
+ contentType = @"text/html";
+ }
+ NSString *bodyLenStr =
+ [NSString stringWithFormat:@"%lu", (unsigned long)bodyLength];
+ [self setValue:bodyLenStr forHeaderField:@"Content-Length"];
+ [self setValue:contentType forHeaderField:@"Content-Type"];
+ }
+ return self;
+}
+
+- (NSData *)serializedData {
+ return [GTMNSMakeCollectable(CFHTTPMessageCopySerializedMessage(message_)) autorelease];
+}
+
+@end
diff --git a/Foundation/GTMHTTPServerTest.m b/Foundation/GTMHTTPServerTest.m
new file mode 100644
index 0000000..d96d54e
--- /dev/null
+++ b/Foundation/GTMHTTPServerTest.m
@@ -0,0 +1,573 @@
+//
+// GTMHTTPServerTest.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 <netinet/in.h>
+#import <sys/socket.h>
+#import <unistd.h>
+#import "GTMSenTestCase.h"
+#import "GTMUnitTestDevLog.h"
+#import "GTMHTTPServer.h"
+#import "GTMRegex.h"
+
+@interface GTMHTTPServerTest : GTMTestCase {
+ NSData *fetchedData_;
+}
+@end
+
+@interface GTMHTTPServerTest (PrivateMethods)
+- (NSData *)fetchFromPort:(unsigned short)port
+ payload:(NSString *)payload
+ chunkSize:(NSUInteger)chunkSize;
+- (NSFileHandle *)fileHandleSendingToPort:(unsigned short)port
+ payload:(NSString *)payload
+ chunkSize:(NSUInteger)chunkSize;
+- (void)readFinished:(NSNotification *)notification;
+@end
+
+// helper class
+@interface TestServerDelegate : NSObject {
+ NSMutableArray *requests_;
+ NSMutableArray *responses_;
+}
++ (id)testServerDelegate;
+- (NSUInteger)requestCount;
+- (GTMHTTPRequestMessage *)popRequest;
+- (void)pushResponse:(GTMHTTPResponseMessage *)message;
+@end
+
+// helper that throws while handling its request
+@interface TestThrowingServerDelegate : TestServerDelegate
+// since this method ALWAYS throws, we can mark it as noreturn
+- (GTMHTTPResponseMessage *)httpServer:(GTMHTTPServer *)server
+ handleRequest:(GTMHTTPRequestMessage *)request __attribute__ ((noreturn));
+@end
+
+// The timings used for waiting for replies
+const NSTimeInterval kGiveUpInterval = 5.0;
+const NSTimeInterval kRunLoopInterval = 0.01;
+
+// the size we break writes up into to test the reading code and how long to
+// wait between writes.
+const NSUInteger kSendChunkSize = 12;
+const NSTimeInterval kSendChunkInterval = 0.05;
+
+// ----------------------------------------------------------------------------
+
+@implementation GTMHTTPServerTest
+
+- (void)testInit {
+ // bad delegates
+ [GTMUnitTestDevLog expectString:@"missing delegate"];
+ STAssertNil([[GTMHTTPServer alloc] init], nil);
+ [GTMUnitTestDevLog expectString:@"missing delegate"];
+ STAssertNil([[GTMHTTPServer alloc] initWithDelegate:nil], nil);
+
+ TestServerDelegate *delegate = [TestServerDelegate testServerDelegate];
+ STAssertNotNil(delegate, nil);
+ GTMHTTPServer *server =
+ [[[GTMHTTPServer alloc] initWithDelegate:delegate] autorelease];
+ STAssertNotNil(server, nil);
+
+ // some attributes
+
+ STAssertTrue([server delegate] == delegate, nil);
+
+ [server setLocalhostOnly:NO];
+ STAssertFalse([server localhostOnly], nil);
+ [server setLocalhostOnly:YES];
+ STAssertTrue([server localhostOnly], nil);
+
+ STAssertEquals([server port], (uint16_t)0, nil);
+ [server setPort:8080];
+ STAssertEquals([server port], (uint16_t)8080, nil);
+ [server setPort:80];
+ STAssertEquals([server port], (uint16_t)80, nil);
+
+ // description (atleast 10 chars)
+ STAssertGreaterThan([[server description] length], (NSUInteger)10, nil);
+}
+
+- (void)testStartStop {
+ TestServerDelegate *delegate1 = [TestServerDelegate testServerDelegate];
+ STAssertNotNil(delegate1, nil);
+ GTMHTTPServer *server1 =
+ [[[GTMHTTPServer alloc] initWithDelegate:delegate1] autorelease];
+ STAssertNotNil(server1, nil);
+ NSError *error = nil;
+ STAssertTrue([server1 start:&error], @"failed to start (error=%@)", error);
+ STAssertNil(error, @"error: %@", error);
+ STAssertGreaterThanOrEqual([server1 port], (uint16_t)1024,
+ @"how'd we get a reserved port?");
+
+ TestServerDelegate *delegate2 = [TestServerDelegate testServerDelegate];
+ STAssertNotNil(delegate2, nil);
+ GTMHTTPServer *server2 =
+ [[[GTMHTTPServer alloc] initWithDelegate:delegate2] autorelease];
+ STAssertNotNil(server2, nil);
+
+ // try the reserved port
+ [server2 setPort:666];
+ error = nil;
+ STAssertFalse([server2 start:&error], nil);
+ STAssertNotNil(error, nil);
+ STAssertEqualObjects([error domain], kGTMHTTPServerErrorDomain, nil);
+ STAssertEquals([error code], (NSInteger)kGTMHTTPServerBindFailedError,
+ @"port should have been reserved");
+
+ // try the same port
+ [server2 setPort:[server1 port]];
+ error = nil;
+ STAssertFalse([server2 start:&error], nil);
+ STAssertNotNil(error, nil);
+ STAssertEqualObjects([error domain], kGTMHTTPServerErrorDomain, nil);
+ STAssertEquals([error code], (NSInteger)kGTMHTTPServerBindFailedError,
+ @"port should have been in use");
+
+ // try a random port again so we really start (prove two can run at once)
+ [server2 setPort:0];
+ error = nil;
+ STAssertTrue([server2 start:&error], @"failed to start (error=%@)", error);
+ STAssertNil(error, @"error: %@", error);
+
+ // shut them down
+ [server1 stop];
+ [server2 stop];
+}
+
+- (void)testRequests {
+ TestServerDelegate *delegate = [TestServerDelegate testServerDelegate];
+ STAssertNotNil(delegate, nil);
+ GTMHTTPServer *server =
+ [[[GTMHTTPServer alloc] initWithDelegate:delegate] autorelease];
+ STAssertNotNil(server, nil);
+ NSError *error = nil;
+ STAssertTrue([server start:&error], @"failed to start (error=%@)", error);
+ STAssertNil(error, @"error: %@", error);
+
+ // a request to test all the fields of a request object
+
+ NSString *payload =
+ @"PUT /some/server/path HTTP/1.0\r\n"
+ @"Content-Length: 16\r\n"
+ @"Custom-Header: Custom_Value\r\n"
+ @"\r\n"
+ @"this is the body";
+ NSData *reply =
+ [self fetchFromPort:[server port] payload:payload chunkSize:kSendChunkSize];
+ STAssertNotNil(reply, nil);
+
+ GTMHTTPRequestMessage *request = [delegate popRequest];
+ STAssertEqualObjects([request version], @"HTTP/1.0", nil);
+ STAssertEqualObjects([[request URL] absoluteString], @"/some/server/path", nil);
+ STAssertEqualObjects([request method], @"PUT", nil);
+ STAssertEqualObjects([request body],
+ [@"this is the body" dataUsingEncoding:NSUTF8StringEncoding],
+ nil);
+ NSDictionary *allHeaders = [request allHeaderFieldValues];
+ STAssertNotNil(allHeaders, nil);
+ STAssertEquals([allHeaders count], (NSUInteger)2, nil);
+ STAssertEqualObjects([allHeaders objectForKey:@"Content-Length"],
+ @"16", nil);
+ STAssertEqualObjects([allHeaders objectForKey:@"Custom-Header"],
+ @"Custom_Value", nil);
+ STAssertGreaterThan([[request description] length], (NSUInteger)10, nil);
+
+ // test different request types (in simple form)
+
+ typedef struct {
+ NSString *method;
+ NSString *url;
+ } TestData;
+
+ TestData data[] = {
+ { @"GET", @"/foo/bar" },
+ { @"HEAD", @"/foo/baz" },
+ { @"POST", @"/foo" },
+ { @"PUT", @"/foo/spam" },
+ { @"DELETE", @"/fooby/doo" },
+ { @"TRACE", @"/something.html" },
+ { @"CONNECT", @"/spam" },
+ { @"OPTIONS", @"/wee/doggies" },
+ };
+
+ for (size_t i = 0; i < sizeof(data) / sizeof(TestData); i++) {
+ payload = [NSString stringWithFormat:@"%@ %@ HTTP/1.0\r\n\r\n",
+ data[i].method, data[i].url];
+ STAssertNotNil(payload, nil);
+ reply = [self fetchFromPort:[server port]
+ payload:payload
+ chunkSize:kSendChunkSize];
+ STAssertNotNil(reply, // just want a reply in this test
+ @"failed of method %@", data[i].method);
+ request = [delegate popRequest];
+ STAssertEqualObjects([[request URL] absoluteString], data[i].url,
+ @"urls didn't match for index %d", i);
+ STAssertEqualObjects([request method], data[i].method,
+ @"methods didn't match for index %d", i);
+ }
+
+ [server stop];
+}
+
+- (void)testResponses {
+
+ // some quick init tests for invalid things
+ STAssertNil([[GTMHTTPResponseMessage alloc] init], nil);
+ STAssertNil([GTMHTTPResponseMessage responseWithBody:nil
+ contentType:nil
+ statusCode:99],
+ nil);
+ STAssertNil([GTMHTTPResponseMessage responseWithBody:nil
+ contentType:nil
+ statusCode:602],
+ nil);
+
+ TestServerDelegate *delegate = [TestServerDelegate testServerDelegate];
+ STAssertNotNil(delegate, nil);
+ GTMHTTPServer *server =
+ [[[GTMHTTPServer alloc] initWithDelegate:delegate] autorelease];
+ STAssertNotNil(server, nil);
+ NSError *error = nil;
+ STAssertTrue([server start:&error], @"failed to start (error=%@)", error);
+ STAssertNil(error, @"error: %@", error);
+
+ // test the html helper
+
+ GTMHTTPResponseMessage *expectedResponse =
+ [GTMHTTPResponseMessage responseWithHTMLString:@"Success!"];
+ STAssertNotNil(expectedResponse, nil);
+ STAssertGreaterThan([[expectedResponse description] length],
+ (NSUInteger)0, nil);
+ [delegate pushResponse:expectedResponse];
+ NSData *responseData = [self fetchFromPort:[server port]
+ payload:@"GET /foo HTTP/1.0\r\n\r\n"
+ chunkSize:kSendChunkSize];
+ STAssertNotNil(responseData, nil);
+ NSString *responseString =
+ [[[NSString alloc] initWithData:responseData
+ encoding:NSUTF8StringEncoding] autorelease];
+ STAssertNotNil(responseString, nil);
+ STAssertTrue([responseString hasPrefix:@"HTTP/1.0 200 OK"], nil);
+ STAssertTrue([responseString hasSuffix:@"Success!"], @"should end w/ our data");
+ STAssertNotEquals([responseString rangeOfString:@"Content-Length: 8"].location,
+ (NSUInteger)NSNotFound, nil);
+ STAssertNotEquals([responseString rangeOfString:@"Content-Type: text/html; charset=UTF-8"].location,
+ (NSUInteger)NSNotFound, nil);
+
+ // test the plain code response
+
+ expectedResponse = [GTMHTTPResponseMessage emptyResponseWithCode:299];
+ STAssertNotNil(expectedResponse, nil);
+ STAssertGreaterThan([[expectedResponse description] length],
+ (NSUInteger)0, nil);
+ [delegate pushResponse:expectedResponse];
+ responseData = [self fetchFromPort:[server port]
+ payload:@"GET /foo HTTP/1.0\r\n\r\n"
+ chunkSize:kSendChunkSize];
+ STAssertNotNil(responseData, nil);
+ responseString =
+ [[[NSString alloc] initWithData:responseData
+ encoding:NSUTF8StringEncoding] autorelease];
+ STAssertNotNil(responseString, nil);
+ STAssertTrue([responseString hasPrefix:@"HTTP/1.0 299 "], nil);
+ STAssertNotEquals([responseString rangeOfString:@"Content-Length: 0"].location,
+ (NSUInteger)NSNotFound, nil);
+ STAssertNotEquals([responseString rangeOfString:@"Content-Type: text/html"].location,
+ (NSUInteger)NSNotFound, nil);
+
+ // test the general api w/ extra header add
+
+ expectedResponse =
+ [GTMHTTPResponseMessage responseWithBody:[@"FOO" dataUsingEncoding:NSUTF8StringEncoding]
+ contentType:@"some/type"
+ statusCode:298];
+ STAssertNotNil(expectedResponse, nil);
+ STAssertGreaterThan([[expectedResponse description] length],
+ (NSUInteger)0, nil);
+ [expectedResponse setValue:@"Custom_Value"
+ forHeaderField:@"Custom-Header"];
+ [expectedResponse setValue:nil
+ forHeaderField:@"Custom-Header2"];
+ [delegate pushResponse:expectedResponse];
+ responseData = [self fetchFromPort:[server port]
+ payload:@"GET /foo HTTP/1.0\r\n\r\n"
+ chunkSize:kSendChunkSize];
+ STAssertNotNil(responseData, nil);
+ responseString =
+ [[[NSString alloc] initWithData:responseData
+ encoding:NSUTF8StringEncoding] autorelease];
+ STAssertNotNil(responseString, nil);
+ STAssertTrue([responseString hasPrefix:@"HTTP/1.0 298"], nil);
+ STAssertTrue([responseString hasSuffix:@"FOO"], @"should end w/ our data");
+ STAssertNotEquals([responseString rangeOfString:@"Content-Length: 3"].location,
+ (NSUInteger)NSNotFound, nil);
+ STAssertNotEquals([responseString rangeOfString:@"Content-Type: some/type"].location,
+ (NSUInteger)NSNotFound, nil);
+ STAssertNotEquals([responseString rangeOfString:@"Custom-Header: Custom_Value"].location,
+ (NSUInteger)NSNotFound, nil);
+ STAssertNotEquals([responseString rangeOfString:@"Custom-Header2: "].location,
+ (NSUInteger)NSNotFound, nil);
+
+ [server stop];
+}
+
+- (void)testRequstEdgeCases {
+ // test all the odd things about requests
+
+ TestServerDelegate *delegate = [TestServerDelegate testServerDelegate];
+ STAssertNotNil(delegate, nil);
+ GTMHTTPServer *server =
+ [[[GTMHTTPServer alloc] initWithDelegate:delegate] autorelease];
+ STAssertNotNil(server, nil);
+ NSError *error = nil;
+ STAssertTrue([server start:&error], @"failed to start (error=%@)", error);
+ STAssertNil(error, @"error: %@", error);
+
+ // extra data (ie-pipelining)
+
+ NSString *payload =
+ @"GET /some/server/path HTTP/1.0\r\n"
+ @"\r\n"
+ @"GET /some/server/path/too HTTP/1.0\r\n"
+ @"\r\n";
+ // don't chunk this, we want to make sure both requests get to our server
+ [GTMUnitTestDevLog expectString:@"Got 38 extra bytes on http request, "
+ "ignoring them"];
+ NSData *reply =
+ [self fetchFromPort:[server port] payload:payload chunkSize:0];
+ STAssertNotNil(reply, nil);
+ STAssertEquals([delegate requestCount], (NSUInteger)1, nil);
+
+ // close w/o full request
+ {
+ // local pool so we can force our handle to close
+ NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init];
+ NSFileHandle *handle =
+ [self fileHandleSendingToPort:[server port]
+ payload:@"GET /some/server/path HTTP/"
+ chunkSize:kSendChunkSize];
+ STAssertNotNil(handle, nil);
+ // spin the run loop so reads the start of the request
+ NSDate* loopIntervalDate =
+ [NSDate dateWithTimeIntervalSinceNow:kRunLoopInterval];
+ [[NSRunLoop currentRunLoop] runUntilDate:loopIntervalDate];
+ // make sure we see the request at this point
+ STAssertEquals([server activeRequestCount], (NSUInteger)1,
+ @"should have started the request by now");
+ // drop the pool to close the connection
+ [localPool release];
+ // spin the run loop so it should see the close
+ loopIntervalDate = [NSDate dateWithTimeIntervalSinceNow:kRunLoopInterval];
+ [[NSRunLoop currentRunLoop] runUntilDate:loopIntervalDate];
+ // make sure we didn't get a request (1 is from test before) and make sure
+ // we don't have some in flight.
+ STAssertEquals([delegate requestCount], (NSUInteger)1,
+ @"shouldn't have gotten another request");
+ STAssertEquals([server activeRequestCount], (NSUInteger)0,
+ @"should have cleaned up the pending connection");
+ }
+
+}
+
+- (void)testExceptionDuringRequest {
+
+ TestServerDelegate *delegate = [TestThrowingServerDelegate testServerDelegate];
+ STAssertNotNil(delegate, nil);
+ GTMHTTPServer *server =
+ [[[GTMHTTPServer alloc] initWithDelegate:delegate] autorelease];
+ STAssertNotNil(server, nil);
+ NSError *error = nil;
+ STAssertTrue([server start:&error], @"failed to start (error=%@)", error);
+ STAssertNil(error, @"error: %@", error);
+ [GTMUnitTestDevLog expectString:@"Exception trying to handle http request: "
+ "To test our handling"];
+ NSData *responseData = [self fetchFromPort:[server port]
+ payload:@"GET /foo HTTP/1.0\r\n\r\n"
+ chunkSize:kSendChunkSize];
+ STAssertNotNil(responseData, nil);
+ STAssertEquals([responseData length], (NSUInteger)0, nil);
+ STAssertEquals([delegate requestCount], (NSUInteger)1, nil);
+ STAssertEquals([server activeRequestCount], (NSUInteger)0, nil);
+}
+
+@end
+
+// ----------------------------------------------------------------------------
+
+@implementation GTMHTTPServerTest (PrivateMethods)
+
+- (NSData *)fetchFromPort:(unsigned short)port
+ payload:(NSString *)payload
+ chunkSize:(NSUInteger)chunkSize {
+ fetchedData_ = nil;
+
+ NSFileHandle *handle = [self fileHandleSendingToPort:port
+ payload:payload
+ chunkSize:chunkSize];
+
+ NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+ [center addObserver:self
+ selector:@selector(readFinished:)
+ name:NSFileHandleReadToEndOfFileCompletionNotification
+ object:handle];
+ [handle readToEndOfFileInBackgroundAndNotify];
+
+ // wait for our reply
+ NSDate* giveUpDate = [NSDate dateWithTimeIntervalSinceNow:kGiveUpInterval];
+ while (!fetchedData_ && [giveUpDate timeIntervalSinceNow] > 0) {
+ NSDate* loopIntervalDate =
+ [NSDate dateWithTimeIntervalSinceNow:kRunLoopInterval];
+ [[NSRunLoop currentRunLoop] runUntilDate:loopIntervalDate];
+ }
+
+ [center removeObserver:self
+ name:NSFileHandleReadToEndOfFileCompletionNotification
+ object:handle];
+
+ NSData *result = [fetchedData_ autorelease];
+ fetchedData_ = nil;
+ return result;
+}
+
+- (NSFileHandle *)fileHandleSendingToPort:(unsigned short)port
+ payload:(NSString *)payload
+ chunkSize:(NSUInteger)chunkSize {
+ int fd = socket(AF_INET, SOCK_STREAM, 0);
+ STAssertGreaterThan(fd, 0, @"failed to create socket");
+
+ struct sockaddr_in addr;
+ bzero(&addr, sizeof(addr));
+ addr.sin_len = sizeof(addr);
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(0x7F000001);
+ int connectResult =
+ connect(fd, (struct sockaddr*)(&addr), (socklen_t)sizeof(addr));
+ STAssertEquals(connectResult, 0, nil);
+
+ NSFileHandle *handle =
+ [[[NSFileHandle alloc] initWithFileDescriptor:fd
+ closeOnDealloc:YES] autorelease];
+ STAssertNotNil(handle, nil);
+
+ NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];
+
+ // we can send in one block or in chunked mode
+ if (chunkSize > 0) {
+ // we don't write the data in one large block, instead of write it out
+ // in bits to help test the data collection code.
+ NSUInteger length = [payloadData length];
+ for (NSUInteger x = 0 ; x < length ; x += chunkSize) {
+ NSUInteger dataChunkSize = length - x;
+ if (dataChunkSize > chunkSize) {
+ dataChunkSize = chunkSize;
+ }
+ NSData *dataChunk
+ = [payloadData subdataWithRange:NSMakeRange(x, dataChunkSize)];
+ [handle writeData:dataChunk];
+ // delay after all but the last chunk to give it time to be read.
+ if ((x + chunkSize) < length) {
+ NSDate* loopIntervalDate =
+ [NSDate dateWithTimeIntervalSinceNow:kSendChunkInterval];
+ [[NSRunLoop currentRunLoop] runUntilDate:loopIntervalDate];
+ }
+ }
+ } else {
+ [handle writeData:payloadData];
+ }
+
+ return handle;
+}
+
+- (void)readFinished:(NSNotification *)notification {
+ NSDictionary *userInfo = [notification userInfo];
+ fetchedData_ =
+ [[userInfo objectForKey:NSFileHandleNotificationDataItem] retain];
+}
+
+@end
+
+// ----------------------------------------------------------------------------
+
+@implementation TestServerDelegate
+
+- (id)init {
+ self = [super init];
+ if (self) {
+ requests_ = [[NSMutableArray alloc] init];
+ responses_ = [[NSMutableArray alloc] init];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [requests_ release];
+ [responses_ release];
+ [super dealloc];
+}
+
++ (id)testServerDelegate {
+ return [[[[self class] alloc] init] autorelease];
+}
+
+- (NSUInteger)requestCount {
+ return [requests_ count];
+}
+
+- (GTMHTTPRequestMessage *)popRequest {
+ GTMHTTPRequestMessage *result = [[[requests_ lastObject] retain] autorelease];
+ [requests_ removeLastObject];
+ return result;
+}
+
+- (void)pushResponse:(GTMHTTPResponseMessage *)message {
+ [responses_ addObject:message];
+}
+
+- (GTMHTTPResponseMessage *)httpServer:(GTMHTTPServer *)server
+ handleRequest:(GTMHTTPRequestMessage *)request {
+ [requests_ addObject:request];
+
+ GTMHTTPResponseMessage *result = nil;
+ if ([responses_ count] > 0) {
+ result = [[[responses_ lastObject] retain] autorelease];
+ [responses_ removeLastObject];
+ } else {
+ result = [GTMHTTPResponseMessage responseWithHTMLString:@"success"];
+ }
+ return result;
+}
+
+@end
+
+// ----------------------------------------------------------------------------
+
+@implementation TestThrowingServerDelegate
+
+- (GTMHTTPResponseMessage *)httpServer:(GTMHTTPServer *)server
+ handleRequest:(GTMHTTPRequestMessage *)request {
+ // let the base do its normal work for counts, etc.
+ [super httpServer:server handleRequest:request];
+ NSException *exception =
+ [NSException exceptionWithName:@"InternalTestingException"
+ reason:@"To test our handling"
+ userInfo:nil];
+ @throw exception;
+}
+
+@end
diff --git a/Foundation/GTMNSAppleEvent+HandlerTest.applescript b/Foundation/GTMNSAppleEvent+HandlerTest.applescript
new file mode 100644
index 0000000..ec99433
--- /dev/null
+++ b/Foundation/GTMNSAppleEvent+HandlerTest.applescript
@@ -0,0 +1,39 @@
+--
+-- 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.
+--
+
+property foo : 1
+
+on test()
+end test
+
+on testReturnOne()
+ return 1
+end testReturnOne
+
+on testReturnParam(param)
+ return param
+end testReturnParam
+
+on testAddParams(param1, param2)
+ return param1 + param2
+end testAddParams
+
+on testAdd of a onto b given otherValue:d
+ return a + b + d
+end testAdd
+
+on open
+end open
diff --git a/Foundation/GTMNSAppleEventDescriptor+Foundation.h b/Foundation/GTMNSAppleEventDescriptor+Foundation.h
new file mode 100644
index 0000000..51a724f
--- /dev/null
+++ b/Foundation/GTMNSAppleEventDescriptor+Foundation.h
@@ -0,0 +1,91 @@
+//
+// NSAppleEventDescriptor+Foundation.h
+//
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import <Foundation/Foundation.h>
+#import "GTMDefines.h"
+
+// A category for dealing with NSAppleEventDescriptors and NSArrays.
+@interface NSAppleEventDescriptor (GTMAppleEventDescriptorArrayAdditions)
+// Used to register the types you know how to convert into
+// NSAppleEventDescriptors.
+// See examples in NSAppleEventDescriptor+String, NSAppleEventDescriptor+Number
+// etc.
+// Args:
+// selector - selector to call for any of the types in |types|
+// types - an std c array of types of length |count|
+// count - number of types in |types|
++ (void)gtm_registerSelector:(SEL)selector
+ forTypes:(DescType*)types
+ count:(NSUInteger)count;
+
+// Returns an NSObject for any NSAppleEventDescriptor
+// Uses types registerd by registerSelector:forTypes:count: to determine
+// what type of object to create. If it doesn't know a type, it attempts
+// to return [self stringValue].
+- (id)gtm_objectValue;
+
+// Return an NSArray for an AEList
+// Returns nil on failure.
+- (NSArray*)gtm_arrayValue;
+
+// Return an NSDictionary for an AERecord
+// Returns nil on failure.
+- (NSDictionary*)gtm_dictionaryValue;
+
+// Return an NSNull for a desc of typeNull
+// Returns nil on failure.
+- (NSNull*)gtm_nullValue;
+
+// Return a NSAppleEventDescriptor for a double value.
++ (NSAppleEventDescriptor*)gtm_descriptorWithDouble:(double)real;
+
+// Return a NSAppleEventDescriptor for a float value.
++ (NSAppleEventDescriptor*)gtm_descriptorWithFloat:(float)real;
+
+// Return a NSAppleEventDescriptor for a CGFloat value.
++ (NSAppleEventDescriptor*)gtm_descriptorWithCGFloat:(CGFloat)real;
+
+// Attempt to extract a double value. Returns NAN on error.
+- (double)gtm_doubleValue;
+
+// Attempt to extract a float value. Returns NAN on error.
+- (float)gtm_floatValue;
+
+// Attempt to extract a CGFloat value. Returns NAN on error.
+- (CGFloat)gtm_cgFloatValue;
+
+// Attempt to extract a NSNumber. Returns nil on error.
+- (NSNumber*)gtm_numberValue;
+
+@end
+
+@interface NSObject (GTMAppleEventDescriptorObjectAdditions)
+// A informal protocol that objects can override to return appleEventDescriptors
+// for their type. The default is to return [self description] rolled up
+// in an NSAppleEventDescriptor. Built in support for:
+// NSArray, NSDictionary, NSNull, NSString, NSNumber and NSProcessInfo
+- (NSAppleEventDescriptor*)gtm_appleEventDescriptor;
+@end
+
+@interface NSAppleEventDescriptor (GTMAppleEventDescriptorAdditions)
+// Allows you to send events.
+// Returns YES if send was successful.
+- (BOOL)gtm_sendEventWithMode:(AESendMode)mode
+ timeOut:(NSTimeInterval)timeout
+ reply:(NSAppleEventDescriptor**)reply;
+@end
diff --git a/Foundation/GTMNSAppleEventDescriptor+Foundation.m b/Foundation/GTMNSAppleEventDescriptor+Foundation.m
new file mode 100644
index 0000000..2b6acd4
--- /dev/null
+++ b/Foundation/GTMNSAppleEventDescriptor+Foundation.m
@@ -0,0 +1,488 @@
+//
+// GTMNSAppleEventDescriptor+Foundation.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 "GTMNSAppleEventDescriptor+Foundation.h"
+#import "GTMFourCharCode.h"
+#import <Carbon/Carbon.h> // Needed Solely For keyASUserRecordFields
+
+// Map of types to selectors.
+static NSMutableDictionary *gTypeMap = nil;
+
+@implementation NSAppleEventDescriptor (GTMAppleEventDescriptorArrayAdditions)
+
++ (void)gtm_registerSelector:(SEL)selector
+ forTypes:(DescType*)types
+ count:(NSUInteger)count {
+ if (selector && types && count > 0) {
+ @synchronized(self) {
+ if (!gTypeMap) {
+ gTypeMap = [[NSMutableDictionary alloc] init];
+ }
+ NSString *selString = NSStringFromSelector(selector);
+ for (NSUInteger i = 0; i < count; ++i) {
+ NSNumber *key = [NSNumber numberWithUnsignedInt:types[i]];
+ NSString *exists = [gTypeMap objectForKey:key];
+ if (exists) {
+ _GTMDevLog(@"%@ being replaced with %@ exists for type: %@",
+ exists, selString, key);
+ }
+ [gTypeMap setObject:selString forKey:key];
+ }
+ }
+ }
+}
+
+- (id)gtm_objectValue {
+ id value = nil;
+
+ // Check our registered types to see if we have anything
+ if (gTypeMap) {
+ @synchronized(gTypeMap) {
+ DescType type = [self descriptorType];
+ NSNumber *key = [NSNumber numberWithUnsignedInt:type];
+ NSString *selectorString = [gTypeMap objectForKey:key];
+ if (selectorString) {
+ SEL selector = NSSelectorFromString(selectorString);
+ value = [self performSelector:selector];
+ } else {
+ value = [self stringValue];
+ }
+ }
+ }
+ return value;
+}
+
+- (NSArray*)gtm_arrayValue {
+ NSUInteger count = [self numberOfItems];
+ NSAppleEventDescriptor *workingDesc = self;
+ if (count == 0) {
+ // Create a list to work with.
+ workingDesc = [self coerceToDescriptorType:typeAEList];
+ count = [workingDesc numberOfItems];
+ }
+ NSMutableArray *items = [NSMutableArray arrayWithCapacity:count];
+ for (NSUInteger i = 1; i <= count; ++i) {
+ NSAppleEventDescriptor *desc = [workingDesc descriptorAtIndex:i];
+ id value = [desc gtm_objectValue];
+ if (!value) {
+ _GTMDevLog(@"Unknown type of descriptor %@", [desc description]);
+ return nil;
+ }
+ [items addObject:value];
+ }
+ return items;
+}
+
+- (NSDictionary*)gtm_dictionaryValue {
+ NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
+ NSAppleEventDescriptor *userRecord = [self descriptorForKeyword:keyASUserRecordFields];
+ if (userRecord) {
+ NSEnumerator *userItems = [[userRecord gtm_arrayValue] objectEnumerator];
+ NSString *key;
+ while ((key = [userItems nextObject])) {
+ NSString *value = [userItems nextObject];
+ if (!value) {
+ _GTMDevLog(@"Got a key %@ with no value in %@", key, userItems);
+ return nil;
+ }
+ [dictionary setObject:value forKey:key];
+ }
+ } else {
+ NSUInteger count = [self numberOfItems];
+ for (NSUInteger i = 1; i <= count; ++i) {
+ AEKeyword key = [self keywordForDescriptorAtIndex:i];
+ NSAppleEventDescriptor *desc = [self descriptorForKeyword:key];
+ id value = [desc gtm_objectValue];
+ if (!value) {
+ _GTMDevLog(@"Unknown type of descriptor %@", [desc description]);
+ return nil;
+ }
+ [dictionary setObject:value
+ forKey:[GTMFourCharCode fourCharCodeWithFourCharCode:key]];
+ }
+ }
+ return dictionary;
+}
+
+- (NSNull*)gtm_nullValue {
+ return [NSNull null];
+}
+
++ (NSAppleEventDescriptor*)gtm_descriptorWithDouble:(double)real {
+ return [NSAppleEventDescriptor descriptorWithDescriptorType:typeIEEE64BitFloatingPoint
+ bytes:&real
+ length:sizeof(real)];
+}
+
++ (NSAppleEventDescriptor*)gtm_descriptorWithFloat:(float)real {
+ return [NSAppleEventDescriptor descriptorWithDescriptorType:typeIEEE32BitFloatingPoint
+ bytes:&real
+ length:sizeof(real)];
+}
+
+
++ (NSAppleEventDescriptor*)gtm_descriptorWithCGFloat:(CGFloat)real {
+#if CGFLOAT_IS_DOUBLE
+ return [self gtm_descriptorWithDouble:real];
+#else
+ return [self gtm_descriptorWithFloat:real];
+#endif
+}
+
+- (double)gtm_doubleValue {
+ double value = NAN;
+ NSNumber *number = [self gtm_numberValue];
+ if (number) {
+ value = [number doubleValue];
+ }
+ return value;
+}
+
+- (float)gtm_floatValue {
+ float value = NAN;
+ NSNumber *number = [self gtm_numberValue];
+ if (number) {
+ value = [number floatValue];
+ }
+ return value;
+}
+
+- (CGFloat)gtm_cgFloatValue {
+#if CGFLOAT_IS_DOUBLE
+ return [self gtm_doubleValue];
+#else
+ return [self gtm_floatValue];
+#endif
+}
+
+- (NSNumber*)gtm_numberValue {
+ typedef struct {
+ DescType type;
+ SEL selector;
+ } TypeSelectorMap;
+ TypeSelectorMap typeSelectorMap[] = {
+ { typeFalse, @selector(numberWithBool:) },
+ { typeTrue, @selector(numberWithBool:) },
+ { typeBoolean, @selector(numberWithBool:) },
+ { typeSInt16, @selector(numberWithShort:) },
+ { typeSInt32, @selector(numberWithInt:) },
+ { typeUInt32, @selector(numberWithUnsignedInt:) },
+ { typeSInt64, @selector(numberWithLongLong:) },
+ { typeIEEE32BitFloatingPoint, @selector(numberWithFloat:) },
+ { typeIEEE64BitFloatingPoint, @selector(numberWithDouble:) }
+ };
+ DescType type = [self descriptorType];
+ SEL selector = nil;
+ for (size_t i = 0; i < sizeof(typeSelectorMap) / sizeof(TypeSelectorMap); ++i) {
+ if (type == typeSelectorMap[i].type) {
+ selector = typeSelectorMap[i].selector;
+ break;
+ }
+ }
+ NSAppleEventDescriptor *desc = self;
+ if (!selector) {
+ // COV_NF_START - Don't know how to force this in a unittest
+ _GTMDevLog(@"Didn't get a valid selector?");
+ desc = [self coerceToDescriptorType:typeIEEE64BitFloatingPoint];
+ selector = @selector(numberWithDouble:);
+ // COV_NF_END
+ }
+ NSData *descData = [desc data];
+ const void *bytes = [descData bytes];
+ if (!bytes) {
+ // COV_NF_START - Don't know how to force this in a unittest
+ _GTMDevLog(@"Unable to get bytes from %@", desc);
+ return nil;
+ // COV_NF_END
+ }
+ Class numberClass = [NSNumber class];
+ NSMethodSignature *signature = [numberClass methodSignatureForSelector:selector];
+ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
+ [invocation setSelector:selector];
+ [invocation setArgument:(void*)bytes atIndex:2];
+ [invocation setTarget:numberClass];
+ [invocation invoke];
+ NSNumber *value = nil;
+ [invocation getReturnValue:&value];
+ return value;
+}
+
+@end
+
+@implementation NSObject (GTMAppleEventDescriptorObjectAdditions)
+- (NSAppleEventDescriptor*)gtm_appleEventDescriptor {
+ return [NSAppleEventDescriptor descriptorWithString:[self description]];
+}
+@end
+
+@implementation NSArray (GTMAppleEventDescriptorObjectAdditions)
+
++ (void)load {
+ DescType types[] = {
+ typeAEList,
+ };
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [NSAppleEventDescriptor gtm_registerSelector:@selector(gtm_arrayValue)
+ forTypes:types
+ count:sizeof(types)/sizeof(DescType)];
+ [pool release];
+}
+
+- (NSAppleEventDescriptor*)gtm_appleEventDescriptor {
+ NSAppleEventDescriptor *desc = [NSAppleEventDescriptor listDescriptor];
+ NSUInteger count = [self count];
+ for (NSUInteger i = 1; i <= count; ++i) {
+ id item = [self objectAtIndex:i-1];
+ NSAppleEventDescriptor *itemDesc = [item gtm_appleEventDescriptor];
+ if (!itemDesc) {
+ _GTMDevLog(@"Unable to create Apple Event Descriptor for %@", [self description]);
+ return nil;
+ }
+ [desc insertDescriptor:itemDesc atIndex:i];
+ }
+ return desc;
+}
+@end
+
+@implementation NSDictionary (GTMAppleEventDescriptorObjectAdditions)
+
++ (void)load {
+ DescType types[] = {
+ typeAERecord,
+ };
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [NSAppleEventDescriptor gtm_registerSelector:@selector(gtm_dictionaryValue)
+ forTypes:types
+ count:sizeof(types)/sizeof(DescType)];
+ [pool release];
+}
+
+- (NSAppleEventDescriptor*)gtm_appleEventDescriptor {
+ NSEnumerator* keys = [self keyEnumerator];
+ Class keyClass = nil;
+ id key = nil;
+ while ((key = [keys nextObject])) {
+ if (!keyClass) {
+ if ([key isKindOfClass:[GTMFourCharCode class]]) {
+ keyClass = [GTMFourCharCode class];
+ } else if ([key isKindOfClass:[NSString class]]) {
+ keyClass = [NSString class];
+ } else {
+ _GTMDevLog(@"Keys must be of type NSString or GTMFourCharCode: %@", key);
+ return nil;
+ }
+ }
+ if (![key isKindOfClass:keyClass]) {
+ _GTMDevLog(@"Keys must be homogenous (first key was of type %@) "
+ "and of type NSString or GTMFourCharCode: %@", keyClass, key);
+ return nil;
+ }
+ }
+ NSAppleEventDescriptor *desc = [NSAppleEventDescriptor recordDescriptor];
+ if ([keyClass isEqual:[NSString class]]) {
+ NSMutableArray *array = [NSMutableArray arrayWithCapacity:[self count] * 2];
+ keys = [self keyEnumerator];
+ while ((key = [keys nextObject])) {
+ [array addObject:key];
+ [array addObject:[self objectForKey:key]];
+ }
+ NSAppleEventDescriptor *userRecord = [array gtm_appleEventDescriptor];
+ if (!userRecord) {
+ return nil;
+ }
+ [desc setDescriptor:userRecord forKeyword:keyASUserRecordFields];
+ } else {
+ keys = [self keyEnumerator];
+ while ((key = [keys nextObject])) {
+ id value = [self objectForKey:key];
+ NSAppleEventDescriptor *valDesc = [value gtm_appleEventDescriptor];
+ if (!valDesc) {
+ return nil;
+ }
+ [desc setDescriptor:valDesc forKeyword:[key fourCharCode]];
+ }
+ }
+ return desc;
+}
+
+@end
+
+@implementation NSNull (GTMAppleEventDescriptorObjectAdditions)
++ (void)load {
+ DescType types[] = {
+ typeNull
+ };
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [NSAppleEventDescriptor gtm_registerSelector:@selector(gtm_nullValue)
+ forTypes:types
+ count:sizeof(types)/sizeof(DescType)];
+ [pool release];
+}
+
+- (NSAppleEventDescriptor*)gtm_appleEventDescriptor {
+ return [NSAppleEventDescriptor nullDescriptor];
+}
+@end
+
+@implementation NSString (GTMAppleEventDescriptorObjectAdditions)
+
++ (void)load {
+ DescType types[] = {
+ typeUTF16ExternalRepresentation,
+ typeUnicodeText,
+ typeUTF8Text,
+ typeCString,
+ typePString,
+ typeChar,
+ typeIntlText };
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [NSAppleEventDescriptor gtm_registerSelector:@selector(stringValue)
+ forTypes:types
+ count:sizeof(types)/sizeof(DescType)];
+ [pool release];
+}
+
+- (NSAppleEventDescriptor*)gtm_appleEventDescriptor {
+ return [NSAppleEventDescriptor descriptorWithString:self];
+}
+@end
+
+@implementation NSNumber (GTMAppleEventDescriptorObjectAdditions)
+
++ (void)load {
+ DescType types[] = {
+ typeTrue,
+ typeFalse,
+ typeBoolean,
+ typeSInt16,
+ typeSInt32,
+ typeUInt32,
+ typeSInt64,
+ typeIEEE32BitFloatingPoint,
+ typeIEEE64BitFloatingPoint };
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [NSAppleEventDescriptor gtm_registerSelector:@selector(gtm_numberValue)
+ forTypes:types
+ count:sizeof(types)/sizeof(DescType)];
+ [pool release];
+}
+
+- (NSAppleEventDescriptor*)gtm_appleEventDescriptor {
+ const char *type = [self objCType];
+ if (!type || strlen(type) != 1) return nil;
+
+ DescType desiredType = typeNull;
+ NSAppleEventDescriptor *desc = nil;
+ switch (type[0]) {
+ // COV_NF_START
+ // I can't seem to convince objcType to return something of this type
+ case 'B':
+ desc = [NSAppleEventDescriptor descriptorWithBoolean:[self boolValue]];
+ break;
+ // COV_NF_END
+
+ case 'c':
+ case 'C':
+ case 's':
+ case 'S':
+ desiredType = typeSInt16;
+ break;
+
+ case 'i':
+ case 'l':
+ desiredType = typeSInt32;
+ break;
+
+ // COV_NF_START
+ // I can't seem to convince objcType to return something of this type
+ case 'I':
+ case 'L':
+ desiredType = typeUInt32;
+ break;
+ // COV_NF_END
+
+ case 'q':
+ case 'Q':
+ desiredType = typeSInt64;
+ break;
+
+ case 'f':
+ desiredType = typeIEEE32BitFloatingPoint;
+ break;
+
+ case 'd':
+ default:
+ desiredType = typeIEEE64BitFloatingPoint;
+ break;
+ }
+
+ if (!desc) {
+ desc = [NSAppleEventDescriptor gtm_descriptorWithDouble:[self doubleValue]];
+ if (desc && desiredType != typeIEEE64BitFloatingPoint) {
+ desc = [desc coerceToDescriptorType:desiredType];
+ }
+ }
+ return desc;
+}
+
+@end
+
+@implementation NSProcessInfo (GTMAppleEventDescriptorObjectAdditions)
+
+- (NSAppleEventDescriptor*)gtm_appleEventDescriptor {
+ ProcessSerialNumber psn = { 0, kCurrentProcess };
+ return [NSAppleEventDescriptor descriptorWithDescriptorType:typeProcessSerialNumber
+ bytes:&psn
+ length:sizeof(ProcessSerialNumber)];
+}
+
+@end
+
+@implementation NSAppleEventDescriptor (GTMAppleEventDescriptorAdditions)
+
+- (BOOL)gtm_sendEventWithMode:(AESendMode)mode
+ timeOut:(NSTimeInterval)timeout
+ reply:(NSAppleEventDescriptor**)reply {
+ BOOL isGood = YES;
+ AppleEvent replyEvent = { typeNull, NULL };
+ OSStatus err = AESendMessage([self aeDesc], &replyEvent, mode, timeout * 60);
+ if (err) {
+ isGood = NO;
+ _GTMDevLog(@"Unable to send message: %@ %d", self, err);
+ replyEvent.descriptorType = typeNull;
+ replyEvent.dataHandle = NULL;
+ }
+ NSAppleEventDescriptor *replyDesc = [[[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&replyEvent] autorelease];
+ if (isGood) {
+ NSAppleEventDescriptor *errorDesc = [replyDesc descriptorForKeyword:keyErrorNumber];
+ if (errorDesc && [errorDesc int32Value]) {
+ isGood = NO;
+ }
+ }
+ if (reply) {
+ *reply = replyDesc;
+ }
+ return isGood;
+}
+
+@end
diff --git a/Foundation/GTMNSAppleEventDescriptor+FoundationTest.m b/Foundation/GTMNSAppleEventDescriptor+FoundationTest.m
new file mode 100644
index 0000000..c35c352
--- /dev/null
+++ b/Foundation/GTMNSAppleEventDescriptor+FoundationTest.m
@@ -0,0 +1,619 @@
+//
+// GTMNSAppleEventDescriptor+FoundationTest.m
+//
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import "GTMSenTestCase.h"
+#import <Carbon/Carbon.h>
+#import "GTMNSAppleEventDescriptor+Foundation.h"
+#import "GTMFourCharCode.h"
+#import "GTMUnitTestDevLog.h"
+
+@interface GTMNSAppleEventDescriptor_TestObject : NSObject
+@end
+
+@implementation GTMNSAppleEventDescriptor_TestObject
+
+- (NSAppleEventDescriptor*)gtm_appleEventDescriptor {
+ return nil;
+}
+
+@end
+
+@interface GTMNSAppleEventDescriptor_FoundationTest : GTMTestCase {
+ BOOL gotEvent_;
+}
+- (void)handleEvent:(NSAppleEventDescriptor*)event
+ withReply:(NSAppleEventDescriptor*)reply;
+- (void)handleEvent:(NSAppleEventDescriptor*)event
+ withError:(NSAppleEventDescriptor*)reply;
+
+@end
+
+@implementation GTMNSAppleEventDescriptor_FoundationTest
+- (void)testRegisterSelectorForTypesCount {
+ // Weird edge casey stuff.
+ // + (void)registerSelector:(SEL)selector
+ // forTypes:(DescType*)types count:(int)count
+ // is tested heavily by the other NSAppleEventDescriptor+foo categories.
+ DescType type;
+ [NSAppleEventDescriptor gtm_registerSelector:nil
+ forTypes:&type count:1];
+ [NSAppleEventDescriptor gtm_registerSelector:@selector(retain)
+ forTypes:nil count:1];
+ [NSAppleEventDescriptor gtm_registerSelector:@selector(retain)
+ forTypes:&type count:0];
+ // Test the duplicate case
+ [NSAppleEventDescriptor gtm_registerSelector:@selector(retain)
+ forTypes:&type count:1];
+ [GTMUnitTestDevLog expectPattern:@"retain being replaced with retain exists "
+ "for type: [0-9]+"];
+ [NSAppleEventDescriptor gtm_registerSelector:@selector(retain)
+ forTypes:&type count:1];
+}
+
+- (void)testObjectValue {
+ // - (void)testObjectValue is tested heavily by the other
+ // NSAppleEventDescriptor+foo categories.
+ long data = 1;
+ // v@#f is just a bogus descriptor type that we don't recognize.
+ NSAppleEventDescriptor *desc
+ = [NSAppleEventDescriptor descriptorWithDescriptorType:'v@#f'
+ bytes:&data
+ length:sizeof(data)];
+ id value = [desc gtm_objectValue];
+ STAssertNil(value, nil);
+}
+
+- (void)testAppleEventDescriptor {
+ // - (NSAppleEventDescriptor*)appleEventDescriptor is tested heavily by the
+ // other NSAppleEventDescriptor+foo categories.
+ NSAppleEventDescriptor *desc = [self gtm_appleEventDescriptor];
+ STAssertNotNil(desc, nil);
+ STAssertEquals([desc descriptorType], (DescType)typeUnicodeText, nil);
+}
+
+- (void)testDescriptorWithArrayAndArrayValue {
+ // Test empty array
+ NSAppleEventDescriptor *desc = [[NSArray array] gtm_appleEventDescriptor];
+ STAssertNotNil(desc, nil);
+ STAssertEquals([desc numberOfItems], (NSInteger)0, nil);
+
+ // Complex array
+ NSArray *array = [NSArray arrayWithObjects:
+ [NSNumber numberWithInt:4],
+ @"foo",
+ [NSNumber numberWithInt:2],
+ @"bar",
+ [NSArray arrayWithObjects:
+ @"bam",
+ [NSArray arrayWithObject:[NSNumber numberWithFloat:4.2f]],
+ nil],
+ nil];
+ STAssertNotNil(array, nil);
+ desc = [array gtm_appleEventDescriptor];
+ STAssertNotNil(desc, nil);
+ NSArray *array2 = [desc gtm_objectValue];
+ STAssertNotNil(array2, nil);
+ NSArray *array3 = [desc gtm_arrayValue];
+ STAssertNotNil(array3, nil);
+ STAssertTrue([array isEqualToArray:array2],
+ @"array: %@\narray2: %@\ndesc: %@",
+ [array description], [array2 description], [desc description]);
+ STAssertTrue([array2 isEqualToArray:array3],
+ @"array: %@\narray2: %@\ndesc: %@",
+ [array description], [array2 description], [desc description]);
+
+ // Test a single object
+ array = [NSArray arrayWithObject:@"foo"];
+ desc = [NSAppleEventDescriptor descriptorWithString:@"foo"];
+ STAssertNotNil(desc, nil);
+ array2 = [desc gtm_arrayValue];
+ STAssertTrue([array isEqualToArray:array2],
+ @"array: %@\narray2: %@\ndesc: %@",
+ [array description], [array2 description], [desc description]);
+
+ // Something that doesn't know how to register itself.
+ GTMNSAppleEventDescriptor_TestObject *obj
+ = [[[GTMNSAppleEventDescriptor_TestObject alloc] init] autorelease];
+ [GTMUnitTestDevLog expectPattern:@"Unable to create Apple Event Descriptor for .*"];
+ desc = [[NSArray arrayWithObject:obj] gtm_appleEventDescriptor];
+ STAssertNil(desc, @"Should be nil");
+
+ // A list containing something we don't know how to deal with
+ desc = [NSAppleEventDescriptor listDescriptor];
+ NSAppleEventDescriptor *desc2
+ = [NSAppleEventDescriptor descriptorWithDescriptorType:'@!@#'
+ bytes:&desc
+ length:sizeof(desc)];
+ [GTMUnitTestDevLog expectPattern:@"Unknown type of descriptor "
+ "<NSAppleEventDescriptor: '@!@#'\\(\\$[0-9A-F]*\\$\\)>"];
+ [desc insertDescriptor:desc2 atIndex:0];
+ array = [desc gtm_objectValue];
+ STAssertEquals([array count], (NSUInteger)0, @"Should have 0 items");
+}
+
+- (void)testDescriptorWithDictionaryAndDictionaryValue {
+ // Test empty dictionary
+ NSAppleEventDescriptor *desc
+ = [[NSDictionary dictionary] gtm_appleEventDescriptor];
+ STAssertNotNil(desc, nil);
+ STAssertEquals([desc numberOfItems], (NSInteger)0, nil);
+
+ // Complex dictionary
+ NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
+ @"fooobject",
+ @"fookey",
+ @"barobject",
+ @"barkey",
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ @"january",
+ [GTMFourCharCode fourCharCodeWithFourCharCode:cJanuary],
+ @"february",
+ [GTMFourCharCode fourCharCodeWithFourCharCode:cFebruary],
+ nil],
+ @"dictkey",
+ nil];
+ STAssertNotNil(dictionary, nil);
+ desc = [dictionary gtm_appleEventDescriptor];
+ STAssertNotNil(desc, nil);
+ NSDictionary *dictionary2 = [desc gtm_objectValue];
+ STAssertNotNil(dictionary2, nil);
+ NSDictionary *dictionary3 = [desc gtm_dictionaryValue];
+ STAssertNotNil(dictionary3, nil);
+ STAssertEqualObjects(dictionary, dictionary2,
+ @"desc: %@", [desc description]);
+ STAssertEqualObjects(dictionary2, dictionary3,
+ @"desc: %@", [desc description]);
+
+ // Something that doesn't know how to register itself.
+ GTMNSAppleEventDescriptor_TestObject *obj
+ = [[[GTMNSAppleEventDescriptor_TestObject alloc] init] autorelease];
+ [GTMUnitTestDevLog expectPattern:@"Unable to create Apple Event Descriptor for .*"];
+ desc = [[NSDictionary dictionaryWithObject:obj
+ forKey:@"foo"] gtm_appleEventDescriptor];
+ STAssertNil(desc, @"Should be nil");
+
+ GTMFourCharCode *fcc = [GTMFourCharCode fourCharCodeWithFourCharCode:cJanuary];
+ desc = [[NSDictionary dictionaryWithObject:obj
+ forKey:fcc] gtm_appleEventDescriptor];
+ STAssertNil(desc, @"Should be nil");
+
+ // A list containing something we don't know how to deal with
+ desc = [NSAppleEventDescriptor recordDescriptor];
+ NSAppleEventDescriptor *desc2
+ = [NSAppleEventDescriptor descriptorWithDescriptorType:'@!@#'
+ bytes:&desc
+ length:sizeof(desc)];
+ [desc setDescriptor:desc2 forKeyword:cJanuary];
+ [GTMUnitTestDevLog expectPattern:@"Unknown type of descriptor "
+ "<NSAppleEventDescriptor: '@!@#'\\(\\$[0-9A-F]+\\$\\)>"];
+ dictionary = [desc gtm_objectValue];
+ STAssertEquals([dictionary count], (NSUInteger)0, @"Should have 0 items");
+
+ // A bad dictionary
+ dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
+ @"foo",
+ [GTMFourCharCode fourCharCodeWithFourCharCode:'APPL'],
+ @"bam",
+ @"bar",
+ nil];
+ STAssertNotNil(dictionary, nil);
+ // I cannot use expectString here to the exact string because interestingly
+ // dictionaries in 64 bit enumerate in a different order from dictionaries
+ // on 32 bit. This is the closest pattern I can match.
+ [GTMUnitTestDevLog expectPattern:@"Keys must be homogenous .*"];
+ desc = [dictionary gtm_appleEventDescriptor];
+ STAssertNil(desc, nil);
+
+ // Another bad dictionary
+ dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
+ @"foo",
+ [NSNumber numberWithInt:4],
+ @"bam",
+ @"bar",
+ nil];
+ STAssertNotNil(dictionary, nil);
+ // I cannot use expectString here to the exact string because interestingly
+ // dictionaries in 64 bit enumerate in a different order from dictionaries
+ // on 32 bit. This is the closest pattern I can match.
+ [GTMUnitTestDevLog expectPattern:@"Keys must be .*"];
+ desc = [dictionary gtm_appleEventDescriptor];
+ STAssertNil(desc, nil);
+
+ // A bad descriptor
+ desc = [NSAppleEventDescriptor recordDescriptor];
+ STAssertNotNil(desc, @"");
+ NSArray *array = [NSArray arrayWithObjects:@"foo", @"bar", @"bam", nil];
+ STAssertNotNil(array, @"");
+ NSAppleEventDescriptor *userRecord = [array gtm_appleEventDescriptor];
+ STAssertNotNil(userRecord, @"");
+ [desc setDescriptor:userRecord forKeyword:keyASUserRecordFields];
+ [GTMUnitTestDevLog expectPattern:@"Got a key bam with no value in <.*"];
+ dictionary = [desc gtm_objectValue];
+ STAssertNil(dictionary, @"Should be nil");
+}
+
+- (void)testDescriptorWithNull {
+ // Test Null
+ NSNull *null = [NSNull null];
+ NSAppleEventDescriptor *desc = [null gtm_appleEventDescriptor];
+ STAssertNotNil(desc, nil);
+ NSNull *null2 = [desc gtm_objectValue];
+ STAssertNotNil(null2, nil);
+ NSNull *null3 = [desc gtm_nullValue];
+ STAssertNotNil(null2, nil);
+ STAssertEqualObjects(null, null2,
+ @"null: %@\null2: %@\ndesc: %@",
+ [null description], [null2 description],
+ [desc description]);
+ STAssertEqualObjects(null, null3,
+ @"null: %@\null3: %@\ndesc: %@",
+ [null description], [null3 description],
+ [desc description]);
+}
+
+- (void)testDescriptorWithString {
+ // Test empty String
+ NSAppleEventDescriptor *desc = [[NSString string] gtm_appleEventDescriptor];
+ STAssertNotNil(desc, nil);
+
+ // Test String
+ NSString *string = @"Ratatouille!";
+ desc = [string gtm_appleEventDescriptor];
+ STAssertNotNil(desc, nil);
+ NSString *string2 = [desc gtm_objectValue];
+ STAssertNotNil(string2, nil);
+ STAssertEqualObjects(string, string2,
+ @"string: %@\nstring: %@\ndesc: %@",
+ [string description], [string2 description], [desc description]);
+
+}
+
+- (void)testDescriptorWithNumberAndNumberValue {
+ // There's really no good way to make this into a loop sadly due
+ // to me having to pass a pointer of bytes to NSInvocation as an argument.
+ // I want the compiler to convert my int to the appropriate type.
+
+ NSNumber *original = [NSNumber numberWithBool:YES];
+ STAssertNotNil(original, @"Value: YES");
+ NSAppleEventDescriptor *desc = [original gtm_appleEventDescriptor];
+ STAssertNotNil(desc, @"Value: YES");
+ id returned = [desc gtm_objectValue];
+ STAssertNotNil(returned, @"Value: YES");
+ STAssertTrue([returned isKindOfClass:[NSNumber class]], @"Value: YES");
+ STAssertEqualObjects(original, returned, @"Value: YES");
+ desc = [desc coerceToDescriptorType:typeBoolean];
+ NSNumber *number = [desc gtm_numberValue];
+ STAssertEqualObjects(number, original, @"Value: YES");
+
+ original = [NSNumber numberWithBool:NO];
+ STAssertNotNil(original, @"Value: NO");
+ desc = [original gtm_appleEventDescriptor];
+ STAssertNotNil(desc, @"Value: NO");
+ returned = [desc gtm_objectValue];
+ STAssertNotNil(returned, @"Value: NO");
+ STAssertTrue([returned isKindOfClass:[NSNumber class]], @"Value: NO");
+ STAssertEqualObjects(original, returned, @"Value: NO");
+
+ sranddev();
+ double value = rand();
+
+ original = [NSNumber numberWithChar:value];
+ STAssertNotNil(original, @"Value: %g", value);
+ desc = [original gtm_appleEventDescriptor];
+ STAssertNotNil(desc, @"Value: %g", value);
+ returned = [desc gtm_objectValue];
+ STAssertNotNil(returned, @"Value: %g", value);
+ STAssertTrue([returned isKindOfClass:[NSNumber class]], @"Value: %g", value);
+ STAssertEqualObjects(original, returned, @"Value: %g", value);
+
+ value = rand();
+ original = [NSNumber numberWithUnsignedChar:value];
+ STAssertNotNil(original, @"Value: %g", value);
+ desc = [original gtm_appleEventDescriptor];
+ STAssertNotNil(desc, @"Value: %g", value);
+ returned = [desc gtm_objectValue];
+ STAssertNotNil(returned, @"Value: %g", value);
+ STAssertTrue([returned isKindOfClass:[NSNumber class]], @"Value: %g", value);
+ STAssertEqualObjects(original, returned, @"Value: %g", value);
+
+ value = rand();
+ original = [NSNumber numberWithShort:value];
+ STAssertNotNil(original, @"Value: %g", value);
+ desc = [original gtm_appleEventDescriptor];
+ STAssertNotNil(desc, @"Value: %g", value);
+ returned = [desc gtm_objectValue];
+ STAssertNotNil(returned, @"Value: %g", value);
+ STAssertTrue([returned isKindOfClass:[NSNumber class]], @"Value: %g", value);
+ STAssertEqualObjects(original, returned, @"Value: %g", value);
+
+ value = rand();
+ original = [NSNumber numberWithUnsignedShort:value];
+ STAssertNotNil(original, @"Value: %g", value);
+ desc = [original gtm_appleEventDescriptor];
+ STAssertNotNil(desc, @"Value: %g", value);
+ returned = [desc gtm_objectValue];
+ STAssertNotNil(returned, @"Value: %g", value);
+ STAssertTrue([returned isKindOfClass:[NSNumber class]], @"Value: %g", value);
+ STAssertEqualObjects(original, returned, @"Value: %g", value);
+
+ value = rand();
+ original = [NSNumber numberWithInt:(int)value];
+ STAssertNotNil(original, @"Value: %g", value);
+ desc = [original gtm_appleEventDescriptor];
+ STAssertNotNil(desc, @"Value: %g", value);
+ returned = [desc gtm_objectValue];
+ STAssertNotNil(returned, @"Value: %g", value);
+ STAssertTrue([returned isKindOfClass:[NSNumber class]], @"Value: %g", value);
+ STAssertEqualObjects(original, returned, @"Value: %g", value);
+
+ value = rand();
+ original = [NSNumber numberWithUnsignedInt:(unsigned int)value];
+ STAssertNotNil(original, @"Value: %g", value);
+ desc = [original gtm_appleEventDescriptor];
+ STAssertNotNil(desc, @"Value: %g", value);
+ returned = [desc gtm_objectValue];
+ STAssertNotNil(returned, @"Value: %g", value);
+ STAssertTrue([returned isKindOfClass:[NSNumber class]], @"Value: %g", value);
+ STAssertEqualObjects(original, returned, @"Value: %g", value);
+
+ value = rand();
+ original = [NSNumber numberWithLong:value];
+ STAssertNotNil(original, @"Value: %g", value);
+ desc = [original gtm_appleEventDescriptor];
+ STAssertNotNil(desc, @"Value: %g", value);
+ returned = [desc gtm_objectValue];
+ STAssertNotNil(returned, @"Value: %g", value);
+ STAssertTrue([returned isKindOfClass:[NSNumber class]], @"Value: %g", value);
+ STAssertEqualObjects(original, returned, @"Value: %g", value);
+
+ value = rand();
+ original = [NSNumber numberWithUnsignedLong:value];
+ STAssertNotNil(original, @"Value: %g", value);
+ desc = [original gtm_appleEventDescriptor];
+ STAssertNotNil(desc, @"Value: %g", value);
+ returned = [desc gtm_objectValue];
+ STAssertNotNil(returned, @"Value: %g", value);
+ STAssertTrue([returned isKindOfClass:[NSNumber class]], @"Value: %g", value);
+ STAssertEqualObjects(original, returned, @"Value: %g", value);
+
+ value = rand();
+ original = [NSNumber numberWithLongLong:value];
+ STAssertNotNil(original, @"Value: %g", value);
+ desc = [original gtm_appleEventDescriptor];
+ STAssertNotNil(desc, @"Value: %g", value);
+ returned = [desc gtm_objectValue];
+ STAssertNotNil(returned, @"Value: %g", value);
+ STAssertTrue([returned isKindOfClass:[NSNumber class]], @"Value: %g", value);
+ STAssertEqualObjects(original, returned, @"Value: %g", value);
+
+ value = rand();
+ original = [NSNumber numberWithUnsignedLongLong:value];
+ STAssertNotNil(original, @"Value: %g", value);
+ desc = [original gtm_appleEventDescriptor];
+ STAssertNotNil(desc, @"Value: %g", value);
+ returned = [desc gtm_objectValue];
+ STAssertNotNil(returned, @"Value: %g", value);
+ STAssertTrue([returned isKindOfClass:[NSNumber class]], @"Value: %g", value);
+ STAssertEqualObjects(original, returned, @"Value: %g", value);
+
+ float floatA = rand();
+ float floatB = rand();
+ value = floatA / floatB;
+ original = [NSNumber numberWithFloat:(float)value];
+ STAssertNotNil(original, @"Value: %g", value);
+ desc = [original gtm_appleEventDescriptor];
+ STAssertNotNil(desc, @"Value: %g", value);
+ returned = [desc gtm_objectValue];
+ STAssertNotNil(returned, @"Value: %g", value);
+ STAssertTrue([returned isKindOfClass:[NSNumber class]], @"Value: %g", value);
+ STAssertEqualObjects(original, returned, @"Value: %g", value);
+
+ double doubleA = rand();
+ double doubleB = rand();
+ value = doubleA / doubleB;
+ original = [NSNumber numberWithDouble:value];
+ STAssertNotNil(original, @"Value: %g", value);
+ desc = [original gtm_appleEventDescriptor];
+ STAssertNotNil(desc, @"Value: %g", value);
+ returned = [desc gtm_objectValue];
+ STAssertNotNil(returned, @"Value: %g", value);
+ STAssertTrue([returned isKindOfClass:[NSNumber class]], @"Value: %g", value);
+ STAssertEqualObjects(original, returned, @"Value: %g", value);
+
+ value = rand();
+ original = [NSNumber numberWithBool:value];
+ STAssertNotNil(original, @"Value: %g", value);
+ desc = [original gtm_appleEventDescriptor];
+ STAssertNotNil(desc, @"Value: %g", value);
+ returned = [desc gtm_objectValue];
+ STAssertNotNil(returned, @"Value: %g", value);
+ STAssertTrue([returned isKindOfClass:[NSNumber class]], @"Value: %g", value);
+ STAssertEqualObjects(original, returned, @"Value: %g", value);
+
+ value = NAN;
+ original = [NSNumber numberWithDouble:value];
+ STAssertNotNil(original, @"Value: %g", value);
+ desc = [original gtm_appleEventDescriptor];
+ STAssertNotNil(desc, @"Value: %g", value);
+ returned = [desc gtm_objectValue];
+ STAssertNotNil(returned, @"Value: %g", value);
+ STAssertTrue([returned isKindOfClass:[NSNumber class]], @"Value: %g", value);
+ STAssertEqualObjects(original, returned, @"Value: %g", value);
+
+ value = INFINITY;
+ original = [NSNumber numberWithDouble:value];
+ STAssertNotNil(original, @"Value: %g", value);
+ desc = [original gtm_appleEventDescriptor];
+ STAssertNotNil(desc, @"Value: %g", value);
+ returned = [desc gtm_objectValue];
+ STAssertNotNil(returned, @"Value: %g", value);
+ STAssertTrue([returned isKindOfClass:[NSNumber class]], @"Value: %g", value);
+ STAssertEqualObjects(original, returned, @"Value: %g", value);
+
+ value = -0.0;
+ original = [NSNumber numberWithDouble:value];
+ STAssertNotNil(original, @"Value: %g", value);
+ desc = [original gtm_appleEventDescriptor];
+ STAssertNotNil(desc, @"Value: %g", value);
+ returned = [desc gtm_objectValue];
+ STAssertNotNil(returned, @"Value: %g", value);
+ STAssertTrue([returned isKindOfClass:[NSNumber class]], @"Value: %g", value);
+ STAssertEqualObjects(original, returned, @"Value: %g", value);
+
+ value = -INFINITY;
+ original = [NSNumber numberWithDouble:value];
+ STAssertNotNil(original, @"Value: %g", value);
+ desc = [original gtm_appleEventDescriptor];
+ STAssertNotNil(desc, @"Value: %g", value);
+ returned = [desc gtm_objectValue];
+ STAssertNotNil(returned, @"Value: %g", value);
+ STAssertTrue([returned isKindOfClass:[NSNumber class]], @"Value: %g", value);
+ STAssertEqualObjects(original, returned, @"Value: %g", value);
+}
+
+- (void)testDescriptorWithDoubleAndDoubleValue {
+ sranddev();
+ for (int i = 0; i < 1000; ++i) {
+ double value1 = rand();
+ double value2 = rand();
+ double value = value1 / value2;
+ NSAppleEventDescriptor *desc
+ = [NSAppleEventDescriptor gtm_descriptorWithDouble:value];
+ STAssertNotNil(desc, @"Value: %g", value);
+ double returnedValue = [desc gtm_doubleValue];
+ STAssertEquals(value, returnedValue, @"Value: %g", value);
+ }
+
+ double specialCases[] = { 0.0f, __DBL_MIN__, __DBL_EPSILON__, INFINITY, NAN };
+ for (size_t i = 0; i < sizeof(specialCases) / sizeof(double); ++i) {
+ double value = specialCases[i];
+ NSAppleEventDescriptor *desc
+ = [NSAppleEventDescriptor gtm_descriptorWithDouble:value];
+ STAssertNotNil(desc, @"Value: %g", value);
+ double returnedValue = [desc gtm_doubleValue];
+ STAssertEquals(value, returnedValue, @"Value: %g", value);
+ }
+}
+
+- (void)testDescriptorWithFloatAndFloatValue {
+ sranddev();
+ for (int i = 0; i < 1000; ++i) {
+ float value1 = rand();
+ float value2 = rand();
+ float value = value1 / value2;
+ NSAppleEventDescriptor *desc
+ = [NSAppleEventDescriptor gtm_descriptorWithFloat:value];
+ STAssertNotNil(desc, @"Value: %f", value);
+ float returnedValue = [desc gtm_floatValue];
+ STAssertEquals(value, returnedValue, @"Value: %f", value);
+ }
+
+ float specialCases[] = { 0.0f, FLT_MIN, FLT_MAX, FLT_EPSILON, INFINITY, NAN };
+ for (size_t i = 0; i < sizeof(specialCases) / sizeof(float); ++i) {
+ float value = specialCases[i];
+ NSAppleEventDescriptor *desc
+ = [NSAppleEventDescriptor gtm_descriptorWithFloat:value];
+ STAssertNotNil(desc, @"Value: %f", value);
+ float returnedValue = [desc gtm_floatValue];
+ STAssertEquals(value, returnedValue, @"Value: %f", value);
+ }
+}
+
+- (void)testDescriptorWithCGFloatAndCGFloatValue {
+ sranddev();
+ for (int i = 0; i < 1000; ++i) {
+ CGFloat value1 = rand();
+ CGFloat value2 = rand();
+ CGFloat value = value1 / value2;
+ NSAppleEventDescriptor *desc
+ = [NSAppleEventDescriptor gtm_descriptorWithCGFloat:value];
+ STAssertNotNil(desc, @"Value: %g", (double)value);
+ CGFloat returnedValue = [desc gtm_cgFloatValue];
+ STAssertEquals(value, returnedValue, @"Value: %g", (double)value);
+ }
+
+ CGFloat specialCases[] = { 0.0f, CGFLOAT_MIN, CGFLOAT_MAX, NAN };
+ for (size_t i = 0; i < sizeof(specialCases) / sizeof(CGFloat); ++i) {
+ CGFloat value = specialCases[i];
+ NSAppleEventDescriptor *desc
+ = [NSAppleEventDescriptor gtm_descriptorWithCGFloat:value];
+ STAssertNotNil(desc, @"Value: %g", (double)value);
+ CGFloat returnedValue = [desc gtm_cgFloatValue];
+ STAssertEquals(value, returnedValue, @"Value: %g", (double)value);
+ }
+}
+
+- (void)handleEvent:(NSAppleEventDescriptor*)event
+ withReply:(NSAppleEventDescriptor*)reply {
+ gotEvent_ = YES;
+ NSAppleEventDescriptor *answer = [NSAppleEventDescriptor descriptorWithInt32:1];
+ [reply setDescriptor:answer forKeyword:keyDirectObject];
+}
+
+- (void)handleEvent:(NSAppleEventDescriptor*)event
+ withError:(NSAppleEventDescriptor*)error {
+ gotEvent_ = YES;
+ NSAppleEventDescriptor *answer = [NSAppleEventDescriptor descriptorWithInt32:1];
+ [error setDescriptor:answer forKeyword:keyErrorNumber];
+}
+
+- (void)testSend {
+ const AEEventClass eventClass = 'Fooz';
+ const AEEventID eventID = 'Ball';
+ NSAppleEventManager *mgr = [NSAppleEventManager sharedAppleEventManager];
+ [mgr setEventHandler:self
+ andSelector:@selector(handleEvent:withReply:)
+ forEventClass:eventClass
+ andEventID:'Ball'];
+ NSAppleEventDescriptor *currentProcess
+ = [[NSProcessInfo processInfo] gtm_appleEventDescriptor];
+ NSAppleEventDescriptor *event
+ = [NSAppleEventDescriptor appleEventWithEventClass:eventClass
+ eventID:eventID
+ targetDescriptor:currentProcess
+ returnID:kAutoGenerateReturnID
+ transactionID:kAnyTransactionID];
+ gotEvent_ = NO;
+ NSAppleEventDescriptor *reply;
+ BOOL goodEvent = [event gtm_sendEventWithMode:kAEWaitReply timeOut:60 reply:&reply];
+ [mgr removeEventHandlerForEventClass:eventClass andEventID:eventID];
+ STAssertTrue(goodEvent, @"bad event?");
+ STAssertTrue(gotEvent_, @"Handler not called");
+ NSAppleEventDescriptor *value = [reply descriptorForKeyword:keyDirectObject];
+ STAssertEquals([value int32Value], (SInt32)1, @"didn't get reply");
+
+
+ gotEvent_ = NO;
+ [GTMUnitTestDevLog expectString:@"Unable to send message: "
+ "<NSAppleEventDescriptor: 'Fooz'\\'Ball'{ }> -1708"];
+ goodEvent = [event gtm_sendEventWithMode:kAEWaitReply timeOut:60 reply:&reply];
+ STAssertFalse(goodEvent, @"good event?");
+ STAssertFalse(gotEvent_, @"Handler called?");
+
+ [mgr setEventHandler:self
+ andSelector:@selector(handleEvent:withError:)
+ forEventClass:eventClass
+ andEventID:eventID];
+ gotEvent_ = NO;
+ goodEvent = [event gtm_sendEventWithMode:kAEWaitReply timeOut:60 reply:&reply];
+ STAssertFalse(goodEvent, @"good event?");
+ STAssertTrue(gotEvent_, @"Handler not called?");
+ [mgr removeEventHandlerForEventClass:eventClass andEventID:eventID];
+}
+
+@end
diff --git a/Foundation/GTMNSAppleEventDescriptor+Handler.h b/Foundation/GTMNSAppleEventDescriptor+Handler.h
new file mode 100644
index 0000000..f2ac880
--- /dev/null
+++ b/Foundation/GTMNSAppleEventDescriptor+Handler.h
@@ -0,0 +1,40 @@
+//
+// GTMNSAppleEventDescriptor+Handler.h
+//
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import <Foundation/Foundation.h>
+#import "GTMDefines.h"
+
+@interface NSAppleEventDescriptor (GTMAppleEventDescriptorHandlerAdditions)
++ (id)gtm_descriptorWithPositionalHandler:(NSString*)handler
+ parametersArray:(NSArray*)params;
++ (id)gtm_descriptorWithPositionalHandler:(NSString*)handler
+ parametersDescriptor:(NSAppleEventDescriptor*)params;
++ (id)gtm_descriptorWithLabeledHandler:(NSString*)handler
+ labels:(AEKeyword*)labels
+ parameters:(id*)params
+ count:(NSUInteger)count;
+
+- (id)gtm_initWithPositionalHandler:(NSString*)handler
+ parametersArray:(NSArray*)params;
+- (id)gtm_initWithPositionalHandler:(NSString*)handler
+ parametersDescriptor:(NSAppleEventDescriptor*)params;
+- (id)gtm_initWithLabeledHandler:(NSString*)handler
+ labels:(AEKeyword*)labels
+ parameters:(id*)params
+ count:(NSUInteger)count;
+@end
diff --git a/Foundation/GTMNSAppleEventDescriptor+Handler.m b/Foundation/GTMNSAppleEventDescriptor+Handler.m
new file mode 100644
index 0000000..b5b2974
--- /dev/null
+++ b/Foundation/GTMNSAppleEventDescriptor+Handler.m
@@ -0,0 +1,130 @@
+//
+// NSAppleEventDescriptor+Handler.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 "GTMNSAppleEventDescriptor+Handler.h"
+#import "GTMNSAppleEventDescriptor+Foundation.h"
+#import "GTMMethodCheck.h"
+#import <Carbon/Carbon.h>
+
+@implementation NSAppleEventDescriptor (GTMAppleEventDescriptorHandlerAdditions)
+GTM_METHOD_CHECK(NSProcessInfo, gtm_appleEventDescriptor); // COV_NF_LINE
+
++ (id)gtm_descriptorWithPositionalHandler:(NSString*)handler
+ parametersArray:(NSArray*)params {
+ return [[[self alloc] gtm_initWithPositionalHandler:handler
+ parametersArray:params] autorelease];
+}
+
++ (id)gtm_descriptorWithPositionalHandler:(NSString*)handler
+ parametersDescriptor:(NSAppleEventDescriptor*)params {
+ return [[[self alloc] gtm_initWithPositionalHandler:handler
+ parametersDescriptor:params] autorelease];
+}
+
++ (id)gtm_descriptorWithLabeledHandler:(NSString*)handler
+ labels:(AEKeyword*)labels
+ parameters:(id*)params
+ count:(NSUInteger)count {
+ return [[[self alloc] gtm_initWithLabeledHandler:handler
+ labels:labels
+ parameters:params
+ count:count] autorelease];
+}
+
+- (id)gtm_initWithPositionalHandler:(NSString*)handler
+ parametersArray:(NSArray*)params {
+ return [self gtm_initWithPositionalHandler:handler
+ parametersDescriptor:[params gtm_appleEventDescriptor]];
+}
+
+- (id)gtm_initWithPositionalHandler:(NSString*)handler
+ parametersDescriptor:(NSAppleEventDescriptor*)params {
+ if ((self = [self initWithEventClass:kASAppleScriptSuite
+ eventID:kASSubroutineEvent
+ targetDescriptor:[[NSProcessInfo processInfo] gtm_appleEventDescriptor]
+ returnID:kAutoGenerateReturnID
+ transactionID:kAnyTransactionID])) {
+ // Create an NSAppleEventDescriptor with the method handler. Note that the
+ // name must be lowercase (even if it is uppercase in AppleScript).
+ // http://developer.apple.com/qa/qa2001/qa1111.html
+ // has details.
+ handler = [handler lowercaseString];
+ if (!handler) {
+ [self release];
+ return nil;
+ }
+ NSAppleEventDescriptor *handlerDesc
+ = [NSAppleEventDescriptor descriptorWithString:handler];
+ [self setParamDescriptor:handlerDesc forKeyword:keyASSubroutineName];
+ if (params) {
+ [self setParamDescriptor:params forKeyword:keyDirectObject];
+ }
+ }
+ return self;
+}
+
+
+- (id)gtm_initWithLabeledHandler:(NSString*)handler
+ labels:(AEKeyword*)labels
+ parameters:(id*)params
+ count:(NSUInteger)count {
+ if ((self = [self initWithEventClass:kASAppleScriptSuite
+ eventID:kASSubroutineEvent
+ targetDescriptor:[[NSProcessInfo processInfo] gtm_appleEventDescriptor]
+ returnID:kAutoGenerateReturnID
+ transactionID:kAnyTransactionID])) {
+ if (!handler) {
+ [self release];
+ return nil;
+ }
+ // Create an NSAppleEventDescriptor with the method handler. Note that the
+ // name must be lowercase (even if it is uppercase in AppleScript).
+ NSAppleEventDescriptor *handlerDesc
+ = [NSAppleEventDescriptor descriptorWithString:[handler lowercaseString]];
+ [self setParamDescriptor:handlerDesc forKeyword:keyASSubroutineName];
+ for (NSUInteger i = 0; i < count; i++) {
+ NSAppleEventDescriptor *paramDesc = [params[i] gtm_appleEventDescriptor];
+ if(labels[i] == keyASPrepositionGiven) {
+ if (![params[i] isKindOfClass:[NSDictionary class]]) {
+ _GTMDevLog(@"Must pass in dictionary for keyASPrepositionGiven "
+ "(got %@)", params[i]);
+ [self release];
+ self = nil;
+ break;
+ }
+ NSAppleEventDescriptor *userDesc
+ = [paramDesc descriptorForKeyword:keyASUserRecordFields];
+ if (!userDesc) {
+ _GTMDevLog(@"Dictionary for keyASPrepositionGiven must be a user "
+ "record field dictionary (got %@)", params[i]);
+ [self release];
+ self = nil;
+ break;
+ }
+ [self setParamDescriptor:userDesc
+ forKeyword:keyASUserRecordFields];
+ } else {
+ [self setParamDescriptor:paramDesc
+ forKeyword:labels[i]];
+ }
+ }
+ }
+ return self;
+}
+
+@end
diff --git a/Foundation/GTMNSAppleEventDescriptor+HandlerTest.m b/Foundation/GTMNSAppleEventDescriptor+HandlerTest.m
new file mode 100644
index 0000000..769e014
--- /dev/null
+++ b/Foundation/GTMNSAppleEventDescriptor+HandlerTest.m
@@ -0,0 +1,70 @@
+//
+// NSAppleEventDescriptor+HandlerTest.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 <Carbon/Carbon.h>
+#import "GTMSenTestCase.h"
+#import "GTMNSAppleEventDescriptor+Foundation.h"
+#import "GTMNSAppleEventDescriptor+Handler.h"
+#import "GTMUnitTestDevLog.h"
+
+@interface GTMNSAppleEventDescriptor_HandlerTest : GTMTestCase
+@end
+
+@implementation GTMNSAppleEventDescriptor_HandlerTest
+// Most of this gets tested by the NSAppleScript+Handler tests.
+- (void)testPositionalHandlers {
+ NSAppleEventDescriptor *desc
+ = [NSAppleEventDescriptor gtm_descriptorWithPositionalHandler:nil
+ parametersArray:[NSArray array]];
+ STAssertNil(desc, @"got a desc?");
+
+ desc = [NSAppleEventDescriptor gtm_descriptorWithPositionalHandler:@"happy"
+ parametersDescriptor:nil];
+ STAssertNotNil(desc, @"didn't get a desc?");
+
+ desc = [NSAppleEventDescriptor gtm_descriptorWithLabeledHandler:nil
+ labels:nil
+ parameters:nil
+ count:0];
+ STAssertNil(desc, @"got a desc?");
+
+ AEKeyword keys[] = { keyASPrepositionGiven };
+ NSString *string = @"foo";
+ [GTMUnitTestDevLog expectString:@"Must pass in dictionary for "
+ "keyASPrepositionGiven (got foo)"];
+ desc = [NSAppleEventDescriptor gtm_descriptorWithLabeledHandler:@"happy"
+ labels:keys
+ parameters:&string
+ count:1];
+ STAssertNil(desc, @"got a desc?");
+
+ NSDictionary *dict = [NSDictionary dictionaryWithObject:@"bart"
+ forKey:[NSNumber numberWithInt:4]];
+ [GTMUnitTestDevLog expectString:@"Keys must be of type NSString or "
+ "GTMFourCharCode: 4"];
+ [GTMUnitTestDevLog expectPattern:@"Dictionary for keyASPrepositionGiven must "
+ "be a user record field dictionary \\(got .*"];
+ desc = [NSAppleEventDescriptor gtm_descriptorWithLabeledHandler:@"happy"
+ labels:keys
+ parameters:&dict
+ count:1];
+ STAssertNil(desc, @"got a desc?");
+
+}
+
+@end
diff --git a/Foundation/GTMNSAppleScript+Handler.h b/Foundation/GTMNSAppleScript+Handler.h
new file mode 100644
index 0000000..ca3d6d2
--- /dev/null
+++ b/Foundation/GTMNSAppleScript+Handler.h
@@ -0,0 +1,59 @@
+//
+// GTMNSAppleScript+Handler.h
+//
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import <Foundation/Foundation.h>
+#import "GTMDefines.h"
+
+// A category for calling handlers in NSAppleScript
+@interface NSAppleScript(GTMAppleScriptHandlerAdditions)
+// This method allows us to call a specific handler in an AppleScript.
+// parameters are passed in left-right order 0-n.
+//
+// Args:
+// handler - name of the handler to call in the Applescript
+// params - the parameters to pass to the handler
+// error - in non-nil returns any error that may have occurred.
+//
+// Returns:
+// The result of the handler being called. nil on failure.
+- (NSAppleEventDescriptor*)gtm_executePositionalHandler:(NSString*)handler
+ parameters:(NSArray*)params
+ error:(NSDictionary**)error;
+
+
+- (NSAppleEventDescriptor*)gtm_executeLabeledHandler:(NSString*)handler
+ labels:(AEKeyword*)labels
+ parameters:(id*)params
+ count:(NSUInteger)count
+ error:(NSDictionary **)error;
+- (NSSet*)gtm_handlers;
+- (NSSet*)gtm_properties;
+- (BOOL)gtm_setValue:(id)value forProperty:(NSString*)property;
+- (id)gtm_valueForProperty:(NSString*)property;
+
+@end
+
+@interface NSAppleEventDescriptor(GTMAppleEventDescriptorScriptAdditions)
+
+// Return an NSAppleScript for a desc of typeScript
+// Returns nil on failure.
+- (NSAppleScript*)gtm_scriptValue;
+
+// Return a NSString with [eventClass][eventID] for typeEvent 'evnt'
+- (NSString*)gtm_eventValue;
+@end
diff --git a/Foundation/GTMNSAppleScript+Handler.m b/Foundation/GTMNSAppleScript+Handler.m
new file mode 100644
index 0000000..d3eeffd
--- /dev/null
+++ b/Foundation/GTMNSAppleScript+Handler.m
@@ -0,0 +1,283 @@
+//
+// NSAppleScript+Handler.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 <Carbon/Carbon.h>
+#import "GTMNSAppleScript+Handler.h"
+#import "GTMNSAppleEventDescriptor+Foundation.h"
+#import "GTMNSAppleEventDescriptor+Handler.h"
+#import "GTMFourCharCode.h"
+#import "GTMMethodCheck.h"
+
+// Some private methods that we need to call
+@interface NSAppleScript (NSPrivate)
++ (ComponentInstance)_defaultScriptingComponent;
+- (OSAID) _compiledScriptID;
+- (id)_initWithData:(NSData*)data error:(NSDictionary**)error;
+@end
+
+@interface NSMethodSignature (NSPrivate)
++ (id)signatureWithObjCTypes:(const char *)fp8;
+@end
+
+@implementation NSAppleScript(GTMAppleScriptHandlerAdditions)
+GTM_METHOD_CHECK(NSAppleEventDescriptor, gtm_descriptorWithPositionalHandler:parametersArray:); // COV_NF_LINE
+GTM_METHOD_CHECK(NSAppleEventDescriptor, gtm_descriptorWithLabeledHandler:labels:parameters:count:); // COV_NF_LINE
+GTM_METHOD_CHECK(NSAppleEventDescriptor, gtm_registerSelector:forTypes:count:); // COV_NF_LINE
+
++ (void)load {
+ DescType types[] = {
+ typeScript
+ };
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [NSAppleEventDescriptor gtm_registerSelector:@selector(gtm_scriptValue)
+ forTypes:types
+ count:sizeof(types)/sizeof(DescType)];
+
+ DescType types2[] = {
+ 'evnt' // No type code for this one
+ };
+
+ [NSAppleEventDescriptor gtm_registerSelector:@selector(gtm_eventValue)
+ forTypes:types2
+ count:sizeof(types2)/sizeof(DescType)];
+ [pool release];
+}
+
+- (OSAID)gtm_realIDAndComponent:(ComponentInstance*)component {
+ if (![self isCompiled]) {
+ NSDictionary *error;
+ if (![self compileAndReturnError:&error]) {
+ _GTMDevLog(@"Unable to compile script: %@ %@", self, error);
+ return kOSANullScript;
+ }
+ }
+ OSAID genericID = [self _compiledScriptID];
+ ComponentInstance genericComponent = [NSAppleScript _defaultScriptingComponent];
+ OSAError err = OSAGenericToRealID(genericComponent, &genericID, component);
+ if (err) {
+ _GTMDevLog(@"Unable to get real id script: %@ %@", self, err); // COV_NF_LINE
+ genericID = kOSANullScript; // COV_NF_LINE
+ }
+ return genericID;
+}
+
+- (NSAppleEventDescriptor*)gtm_executePositionalHandler:(NSString*)handler
+ parameters:(NSArray*)params
+ error:(NSDictionary**)error {
+ NSAppleEventDescriptor *event
+ = [NSAppleEventDescriptor gtm_descriptorWithPositionalHandler:handler
+ parametersArray:params];
+ return [self executeAppleEvent:event error:error];
+}
+
+- (NSAppleEventDescriptor*)gtm_executeLabeledHandler:(NSString*)handler
+ labels:(AEKeyword*)labels
+ parameters:(id*)params
+ count:(NSUInteger)count
+ error:(NSDictionary **)error {
+ NSAppleEventDescriptor *event
+ = [NSAppleEventDescriptor gtm_descriptorWithLabeledHandler:handler
+ labels:labels
+ parameters:params
+ count:count];
+ return [self executeAppleEvent:event error:error];
+}
+
+- (NSSet*)gtm_handlers {
+ AEDescList names = { typeNull, NULL };
+ NSArray *array = nil;
+ ComponentInstance component;
+ OSAID osaID = [self gtm_realIDAndComponent:&component];
+ OSAError err = OSAGetHandlerNames(component, kOSAModeNull, osaID, &names);
+ if (!err) {
+ NSAppleEventDescriptor *desc
+ = [[[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&names] autorelease];
+ array = [desc gtm_objectValue];
+ }
+ if (err) {
+ _GTMDevLog(@"Error getting handlers: %d", err);
+ }
+ return [NSSet setWithArray:array];
+}
+
+- (NSSet*)gtm_properties {
+ AEDescList names = { typeNull, NULL };
+ NSArray *array = nil;
+ ComponentInstance component;
+ OSAID osaID = [self gtm_realIDAndComponent:&component];
+ OSAError err = OSAGetPropertyNames(component, kOSAModeNull, osaID, &names);
+ if (!err) {
+ NSAppleEventDescriptor *desc
+ = [[[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&names] autorelease];
+ array = [desc gtm_objectValue];
+ }
+ if (err) {
+ _GTMDevLog(@"Error getting properties: %d", err);
+ }
+ return [NSSet setWithArray:array];
+}
+
+- (BOOL)gtm_setValue:(id)value forProperty:(NSString*)property {
+ BOOL wasGood = NO;
+ NSAppleEventDescriptor *desc = [value gtm_appleEventDescriptor];
+ NSAppleEventDescriptor *propertyName = [property gtm_appleEventDescriptor];
+ OSAError error = paramErr;
+ if (desc && propertyName) {
+ OSAID valueID = kOSANullScript;
+ ComponentInstance component;
+ OSAID scriptID = [self gtm_realIDAndComponent:&component];
+ error = OSACoerceFromDesc(component,
+ [desc aeDesc],
+ kOSAModeNull,
+ &valueID);
+ if (!error) {
+ error = OSASetProperty(component, kOSAModeNull,
+ scriptID, [propertyName aeDesc], valueID);
+ if (!error) {
+ wasGood = YES;
+ }
+ }
+ }
+ if (!wasGood) {
+ _GTMDevLog(@"Unable to setValue:%@ forProperty:%@ from %@ (%d)",
+ value, property, self, error);
+ }
+ return wasGood;
+}
+
+- (id)gtm_valueForProperty:(NSString*)property {
+ id value = nil;
+ NSAppleEventDescriptor *propertyName = [property gtm_appleEventDescriptor];
+ OSAError error = paramErr;
+ if (propertyName) {
+ ComponentInstance component;
+ OSAID scriptID = [self gtm_realIDAndComponent:&component];
+ OSAID valueID = kOSANullScript;
+ error = OSAGetProperty(component,
+ kOSAModeNull,
+ scriptID,
+ [propertyName aeDesc],
+ &valueID);
+ if (!error) {
+ AEDesc aeDesc;
+ error = OSACoerceToDesc(component,
+ valueID,
+ typeWildCard,
+ kOSAModeNull,
+ &aeDesc);
+ if (!error) {
+ NSAppleEventDescriptor *desc
+ = [[[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&aeDesc] autorelease];
+ value = [desc gtm_objectValue];
+ }
+ }
+ }
+ if (error) {
+ _GTMDevLog(@"Unable to get valueForProperty:%@ from %@ (%d)",
+ property, self, error);
+ }
+ return value;
+}
+
+- (NSAppleEventDescriptor*)gtm_appleEventDescriptor {
+ ComponentInstance component;
+ OSAID osaID = [self gtm_realIDAndComponent:&component];
+ AEDesc result = { typeNull, NULL };
+ NSAppleEventDescriptor *desc = nil;
+ OSAError err = OSACoerceToDesc(component,
+ osaID,
+ typeScript,
+ kOSAModeNull,
+ &result);
+ if (!err) {
+ desc = [[[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&result] autorelease];
+ } else {
+ _GTMDevLog(@"Unable to coerce script %d", err); // COV_NF_LINE
+ }
+ return desc;
+}
+
+- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
+ NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
+ if (!signature) {
+ NSMutableString *types = [NSMutableString stringWithString:@"@@:"];
+ NSString *selName = NSStringFromSelector(aSelector);
+ NSArray *selArray = [selName componentsSeparatedByString:@":"];
+ NSUInteger count = [selArray count];
+ for (NSUInteger i = 1; i < count; i++) {
+ [types appendString:@"@"];
+ }
+ signature = [NSMethodSignature signatureWithObjCTypes:[types UTF8String]];
+ }
+ return signature;
+}
+
+- (void)forwardInvocation:(NSInvocation *)invocation {
+ SEL sel = [invocation selector];
+ NSMutableString *handlerName = [NSStringFromSelector(sel) mutableCopy];
+ NSUInteger handlerOrigLength = [handlerName length];
+ [handlerName replaceOccurrencesOfString:@":"
+ withString:@""
+ options:0
+ range:NSMakeRange(0,handlerOrigLength)];
+ NSUInteger argCount = handlerOrigLength - [handlerName length];
+ NSMutableArray *args = [NSMutableArray arrayWithCapacity:argCount];
+ for (NSUInteger i = 0; i < argCount; ++i) {
+ id arg;
+ // +2 to ignore _sel and _cmd
+ [invocation getArgument:&arg atIndex:i + 2];
+ [args addObject:arg];
+ }
+ NSDictionary *error = nil;
+ NSAppleEventDescriptor *desc = [self gtm_executePositionalHandler:handlerName
+ parameters:args
+ error:&error];
+ if ([[invocation methodSignature] methodReturnLength] > 0) {
+ id returnValue = [desc gtm_objectValue];
+ [invocation setReturnValue:&returnValue];
+ }
+}
+@end
+
+@implementation NSAppleEventDescriptor(GMAppleEventDescriptorScriptAdditions)
+
+- (NSAppleScript*)gtm_scriptValue {
+ NSDictionary *error;
+ NSAppleScript *script = [[[NSAppleScript alloc] _initWithData:[self data]
+ error:&error] autorelease];
+ if (!script) {
+ _GTMDevLog(@"Unable to create script: %@", error); // COV_NF_LINE
+ }
+ return script;
+}
+
+- (NSString*)gtm_eventValue {
+ struct AEEventRecordStruct {
+ AEEventClass eventClass;
+ AEEventID eventID;
+ };
+ NSData *data = [self data];
+ const struct AEEventRecordStruct *record
+ = (const struct AEEventRecordStruct*)[data bytes];
+ NSString *eClass = [GTMFourCharCode stringWithFourCharCode:record->eventClass];
+ NSString *eID = [GTMFourCharCode stringWithFourCharCode:record->eventID];
+ return [eClass stringByAppendingString:eID];
+}
+@end
+
diff --git a/Foundation/GTMNSAppleScript+HandlerTest.m b/Foundation/GTMNSAppleScript+HandlerTest.m
new file mode 100644
index 0000000..5a9d52b
--- /dev/null
+++ b/Foundation/GTMNSAppleScript+HandlerTest.m
@@ -0,0 +1,331 @@
+//
+// NSAppleScript+HandlerTest.m
+//
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import "GTMSenTestCase.h"
+#import <Carbon/Carbon.h>
+#import "GTMNSAppleScript+Handler.h"
+#import "GTMNSAppleEventDescriptor+Foundation.h"
+#import "GTMUnitTestDevLog.h"
+
+@interface GTMNSAppleScript_HandlerTest : GTMTestCase {
+ NSAppleScript *script_;
+}
+@end
+
+@implementation GTMNSAppleScript_HandlerTest
+- (void)setUp {
+ NSBundle *bundle = [NSBundle bundleForClass:[GTMNSAppleScript_HandlerTest class]];
+ STAssertNotNil(bundle, nil);
+ NSString *path = [bundle pathForResource:@"GTMNSAppleEvent+HandlerTest"
+ ofType:@"scpt"
+ inDirectory:@"Scripts"];
+ STAssertNotNil(path, [bundle description]);
+ NSDictionary *error = nil;
+ script_ = [[NSAppleScript alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path]
+ error:&error];
+ STAssertNotNil(script_, [error description]);
+ STAssertNil(error, @"Error should be nil. Error = %@", [error description]);
+}
+
+- (void)tearDown {
+ [script_ release];
+ script_ = nil;
+}
+
+- (void)testHandlerNoParamsNoReturn {
+ NSDictionary *error = nil;
+ NSAppleEventDescriptor *desc = [script_ gtm_executePositionalHandler:@"test"
+ parameters:nil
+ error:&error];
+ STAssertNotNil(desc, [error description]);
+ STAssertNil(error, @"Error should be nil. Error = %@", [error description]);
+ STAssertEquals([desc descriptorType], (DescType)typeNull, nil);
+ desc = [script_ gtm_executePositionalHandler:@"test"
+ parameters:[NSArray array]
+ error:&error];
+ STAssertNotNil(desc, [error description]);
+ STAssertNil(error, @"Error should be nil. Error = %@", [error description]);
+ STAssertEquals([desc descriptorType], (DescType)typeNull, nil);
+
+ //Applescript doesn't appear to get upset about extra params
+ desc = [script_ gtm_executePositionalHandler:@"test"
+ parameters:[NSArray arrayWithObject:@"foo"]
+ error:&error];
+ STAssertNotNil(desc, [error description]);
+ STAssertNil(error, @"Error should be nil. Error = %@", [error description]);
+ STAssertEquals([desc descriptorType], (DescType)typeNull, nil);
+}
+
+- (void)testHandlerNoParamsWithReturn {
+ NSDictionary *error = nil;
+ NSAppleEventDescriptor *desc = [script_ gtm_executePositionalHandler:@"testReturnOne"
+ parameters:nil
+ error:&error];
+ STAssertNotNil(desc, [error description]);
+ STAssertNil(error, @"Error should be nil. Error = %@", [error description]);
+ STAssertEquals([desc descriptorType], (DescType)typeSInt32, nil);
+ STAssertEquals([desc int32Value], (SInt32)1, nil);
+ desc = [script_ gtm_executePositionalHandler:@"testReturnOne"
+ parameters:[NSArray array]
+ error:&error];
+ STAssertNotNil(desc, [error description]);
+ STAssertNil(error, @"Error should be nil. Error = %@", [error description]);
+ STAssertEquals([desc descriptorType], (DescType)typeSInt32, nil);
+ STAssertEquals([desc int32Value], (SInt32)1, nil);
+
+ //Applescript doesn't appear to get upset about extra params
+ desc = [script_ gtm_executePositionalHandler:@"testReturnOne"
+ parameters:[NSArray arrayWithObject:@"foo"]
+ error:&error];
+ STAssertNotNil(desc, [error description]);
+ STAssertNil(error, @"Error should be nil. Error = %@", [error description]);
+ STAssertEquals([desc descriptorType], (DescType)typeSInt32, nil);
+ STAssertEquals([desc int32Value], (SInt32)1, nil);
+}
+
+- (void)testHandlerOneParamWithReturn {
+ NSDictionary *error = nil;
+ // Note case change in executeHandler call
+ NSAppleEventDescriptor *desc = [script_ gtm_executePositionalHandler:@"testreturnParam"
+ parameters:nil
+ error:&error];
+ STAssertNil(desc, @"Desc should by nil %@", desc);
+ STAssertNotNil(error, nil);
+ error = nil;
+
+ desc = [script_ gtm_executePositionalHandler:@"testReturnParam"
+ parameters:[NSArray array]
+ error:&error];
+ STAssertNil(desc, @"Desc should by nil %@", desc);
+ STAssertNotNil(error, nil);
+ error = nil;
+
+ desc = [script_ gtm_executePositionalHandler:@"testReturnParam"
+ parameters:[NSArray arrayWithObject:@"foo"]
+ error:&error];
+ STAssertNotNil(desc, [error description]);
+ STAssertNil(error, @"Error should be nil. Error = %@", [error description]);
+ STAssertEquals([desc descriptorType], (DescType)typeUnicodeText, nil);
+ STAssertEqualObjects([desc gtm_objectValue], @"foo", nil);
+}
+
+- (void)testHandlerTwoParamsWithReturn {
+ NSDictionary *error = nil;
+ // Note case change in executeHandler call
+ // Test case and empty params
+ NSAppleEventDescriptor *desc = [script_ gtm_executePositionalHandler:@"testADDPArams"
+ parameters:nil
+ error:&error];
+ STAssertNil(desc, @"Desc should by nil %@", desc);
+ STAssertNotNil(error, nil);
+
+ // Test empty params
+ error = nil;
+ desc = [script_ gtm_executePositionalHandler:@"testAddParams"
+ parameters:[NSArray array]
+ error:&error];
+ STAssertNil(desc, @"Desc should by nil %@", desc);
+ STAssertNotNil(error, nil);
+
+ error = nil;
+ NSArray *args = [NSArray arrayWithObjects:
+ [NSNumber numberWithInt:1],
+ [NSNumber numberWithInt:2],
+ nil];
+ desc = [script_ gtm_executePositionalHandler:@"testAddParams"
+ parameters:args
+ error:&error];
+ STAssertNotNil(desc, [error description]);
+ STAssertNil(error, @"Error should be nil. Error = %@", [error description]);
+ STAssertEquals([desc descriptorType], (DescType)typeSInt32, nil);
+ STAssertEquals([desc int32Value], (SInt32)3, nil);
+
+ // Test bad params
+ error = nil;
+ args = [NSArray arrayWithObjects:
+ @"foo",
+ @"bar",
+ nil];
+ desc = [script_ gtm_executePositionalHandler:@"testAddParams"
+ parameters:args
+ error:&error];
+ STAssertNil(desc, @"Desc should by nil %@", desc);
+ STAssertNotNil(error, nil);
+
+ // Test too many params. Currently Applescript allows this so it should pass
+ error = nil;
+ args = [NSArray arrayWithObjects:
+ [NSNumber numberWithInt:1],
+ [NSNumber numberWithInt:2],
+ [NSNumber numberWithInt:3],
+ nil];
+ desc = [script_ gtm_executePositionalHandler:@"testAddParams"
+ parameters:args
+ error:&error];
+ STAssertNotNil(desc, [error description]);
+ STAssertNil(error, @"Error should be nil. Error = %@", [error description]);
+ STAssertEquals([desc descriptorType], (DescType)typeSInt32, nil);
+ STAssertEquals([desc int32Value], (SInt32)3, nil);}
+
+- (void)testLabeledHandler {
+ NSDictionary *error = nil;
+ AEKeyword labels[] = { keyDirectObject,
+ keyASPrepositionOnto,
+ keyASPrepositionGiven };
+ id params[3];
+ params[0] = [NSNumber numberWithInt:1];
+ params[1] = [NSNumber numberWithInt:3];
+ params[2] = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:4]
+ forKey:@"othervalue"];
+
+ NSAppleEventDescriptor *desc = [script_ gtm_executeLabeledHandler:@"testAdd"
+ labels:labels
+ parameters:params
+ count:sizeof(params) / sizeof(id)
+ error:&error];
+ STAssertNotNil(desc, [error description]);
+ STAssertNil(error, @"Error should be nil. Error = %@", [error description]);
+ STAssertEquals([desc descriptorType], (DescType)typeSInt32, nil);
+ STAssertEquals([desc int32Value], (SInt32)8, nil);
+
+ // Test too many params. Currently Applescript allows this so it should pass
+ AEKeyword labels2[] = { keyDirectObject,
+ keyASPrepositionOnto,
+ keyASPrepositionBetween,
+ keyASPrepositionGiven };
+ id params2[4];
+ params2[0] = [NSNumber numberWithInt:1];
+ params2[1] = [NSNumber numberWithInt:3];
+ params2[2] = [NSNumber numberWithInt:5];
+ params2[3] = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:4]
+ forKey:@"othervalue"];
+
+ error = nil;
+ desc = [script_ gtm_executeLabeledHandler:@"testAdd"
+ labels:labels2
+ parameters:params2
+ count:sizeof(params2) / sizeof(id)
+ error:&error];
+ STAssertNotNil(desc, [error description]);
+ STAssertNil(error, @"Error should be nil. Error = %@", [error description]);
+ STAssertEquals([desc descriptorType], (DescType)typeSInt32, nil);
+ STAssertEquals([desc int32Value], (SInt32)8, nil);}
+
+- (void)testHandlers {
+ NSSet *handlers = [script_ gtm_handlers];
+ NSSet *expected = [NSSet setWithObjects:
+ @"aevtodoc",
+ @"test",
+ @"testreturnone",
+ @"testreturnparam",
+ @"testaddparams",
+ @"testadd",
+ nil];
+ STAssertEqualObjects(handlers, expected, @"Unexpected handlers?");
+}
+
+- (void)testProperties {
+ NSSet *properties = [script_ gtm_properties];
+ NSSet *expected = [NSSet setWithObjects:
+ @"foo",
+ @"asdscriptuniqueidentifier",
+ nil];
+ STAssertEqualObjects(properties, expected, @"Unexpected properties?");
+ id value = [script_ gtm_valueForProperty:@"foo"];
+ STAssertEqualObjects(value, [NSNumber numberWithInt:1], @"bad property?");
+ BOOL goodSet = [script_ gtm_setValue:@"bar" forProperty:@"foo"];
+ STAssertTrue(goodSet, @"Couldn't set property");
+ value = [script_ gtm_valueForProperty:@"foo"];
+ STAssertEqualObjects(value, @"bar", @"bad property?");
+
+ [GTMUnitTestDevLog expectPattern:@"Unable to setValue:bar forProperty:"
+ "\\(null\\) from <NSAppleScript: 0x[0-9a-f]+> \\(-50\\)"];
+ goodSet = [script_ gtm_setValue:@"bar" forProperty:nil];
+ STAssertFalse(goodSet, @"Set property?");
+ [GTMUnitTestDevLog expectPattern:@"Unable to get valueForProperty:gargle "
+ "from <NSAppleScript: 0x[0-9a-f]+> \\(-1753\\)"];
+ value = [script_ gtm_valueForProperty:@"gargle"];
+ STAssertNil(value, @"Property named gargle?");
+}
+
+- (void)testFailures {
+ NSDictionary *error = nil;
+ NSAppleEventDescriptor *desc = [script_ gtm_executePositionalHandler:@"noSuchTest"
+ parameters:nil
+ error:&error];
+ STAssertNil(desc, nil);
+ STAssertNotNil(error, nil);
+
+ // Test with empty handler name
+ error = nil;
+ desc = [script_ gtm_executePositionalHandler:@""
+ parameters:[NSArray array]
+ error:&error];
+ STAssertNil(desc, nil);
+ STAssertNotNil(error, nil);
+
+ // Test with nil handler
+ error = nil;
+ desc = [script_ gtm_executePositionalHandler:nil
+ parameters:[NSArray array]
+ error:&error];
+ STAssertNil(desc, nil);
+ STAssertNotNil(error, nil);
+
+ // Test with nil handler and nil error
+ desc = [script_ gtm_executePositionalHandler:nil
+ parameters:nil
+ error:nil];
+ STAssertNil(desc, nil);
+
+ // Test with a bad script
+ NSAppleScript *script = [[[NSAppleScript alloc] initWithSource:@"david hasselhoff"] autorelease];
+ [GTMUnitTestDevLog expectPattern:@"Unable to compile script: .*"];
+ [GTMUnitTestDevLog expectPattern:@"Error getting handlers: -[0-9]+"];
+ NSSet *handlers = [script gtm_handlers];
+ STAssertEquals([handlers count], (NSUInteger)0, @"Should have no handlers");
+ [GTMUnitTestDevLog expectPattern:@"Unable to compile script: .*"];
+ [GTMUnitTestDevLog expectPattern:@"Error getting properties: -[0-9]+"];
+ NSSet *properties = [script gtm_properties];
+ STAssertEquals([properties count], (NSUInteger)0, @"Should have no properties");
+}
+
+- (void)testScriptDescriptors {
+ NSAppleEventDescriptor *desc = [script_ gtm_appleEventDescriptor];
+ STAssertNotNil(desc, @"Couldn't make a script desc");
+ NSAppleScript *script = [desc gtm_objectValue];
+ STAssertNotNil(script, @"Couldn't get a script back");
+ NSSet *handlers = [script gtm_handlers];
+ STAssertNotNil(handlers, @"Couldn't get handlers");
+}
+
+@protocol ScriptInterface
+- (id)test;
+- (id)testReturnParam:(id)param;
+- (id)testAddParams:(id)param1 :(id)param2;
+@end
+
+- (void)testForwarding {
+ id<ScriptInterface> foo = (id<ScriptInterface>)script_;
+ [foo test];
+ NSNumber *val = [foo testReturnParam:[NSNumber numberWithInt:2]];
+ STAssertEquals([val intValue], 2, @"should be 2");
+ val = [foo testAddParams:[NSNumber numberWithInt:2] :[NSNumber numberWithInt:3]];
+ STAssertEquals([val intValue], 5, @"should be 5");
+}
+@end
diff --git a/Foundation/GTMNSData+zlibTest.m b/Foundation/GTMNSData+zlibTest.m
index 1d09e35..1b6264c 100644
--- a/Foundation/GTMNSData+zlibTest.m
+++ b/Foundation/GTMNSData+zlibTest.m
@@ -17,12 +17,12 @@
//
#import "GTMSenTestCase.h"
-
+#import "GTMUnitTestDevLog.h"
#import "GTMNSData+zlib.h"
#import <stdlib.h> // for randiom/srandomdev
#import <zlib.h>
-@interface GTMNSData_zlibTest : SenTestCase
+@interface GTMNSData_zlibTest : GTMTestCase
@end
@@ -102,16 +102,26 @@ static BOOL HasGzipHeader(NSData *data) {
STAssertEqualObjects(data, dataPrime, nil);
// test non-compressed data data itself
+ [GTMUnitTestDevLog expectString:@"Error trying to inflate some of the "
+ "payload, error -3"];
STAssertNil([NSData gtm_dataByInflatingData:data], nil);
// test deflated data runs that end before they are done
+ [GTMUnitTestDevLog expect:[deflated length] - 1
+ casesOfString:@"Error trying to inflate some of the payload, "
+ "error -5"];
for (NSUInteger x = 1 ; x < [deflated length] ; ++x) {
- STAssertNil([NSData gtm_dataByInflatingBytes:[deflated bytes] length:x], nil);
+ STAssertNil([NSData gtm_dataByInflatingBytes:[deflated bytes]
+ length:x], nil);
}
// test gzipped data runs that end before they are done
+ [GTMUnitTestDevLog expect:[gzipped length] - 1
+ casesOfString:@"Error trying to inflate some of the payload, "
+ "error -5"];
for (NSUInteger x = 1 ; x < [gzipped length] ; ++x) {
- STAssertNil([NSData gtm_dataByInflatingBytes:[gzipped bytes] length:x], nil);
+ STAssertNil([NSData gtm_dataByInflatingBytes:[gzipped bytes]
+ length:x], nil);
}
// test extra data before the deflated/gzipped data (just to make sure we
@@ -121,7 +131,11 @@ static BOOL HasGzipHeader(NSData *data) {
[prefixedDeflated setLength:20];
FillWithRandom([prefixedDeflated mutableBytes], [prefixedDeflated length]);
[prefixedDeflated appendData:deflated];
+ [GTMUnitTestDevLog expectString:@"Error trying to inflate some of the "
+ "payload, error -3"];
STAssertNil([NSData gtm_dataByInflatingData:prefixedDeflated], nil);
+ [GTMUnitTestDevLog expectString:@"Error trying to inflate some of the "
+ "payload, error -3"];
STAssertNil([NSData gtm_dataByInflatingBytes:[prefixedDeflated bytes]
length:[prefixedDeflated length]],
nil);
@@ -130,7 +144,11 @@ static BOOL HasGzipHeader(NSData *data) {
[prefixedGzipped setLength:20];
FillWithRandom([prefixedGzipped mutableBytes], [prefixedGzipped length]);
[prefixedGzipped appendData:gzipped];
+ [GTMUnitTestDevLog expectString:@"Error trying to inflate some of the "
+ "payload, error -3"];
STAssertNil([NSData gtm_dataByInflatingData:prefixedGzipped], nil);
+ [GTMUnitTestDevLog expectString:@"Error trying to inflate some of the "
+ "payload, error -3"];
STAssertNil([NSData gtm_dataByInflatingBytes:[prefixedGzipped bytes]
length:[prefixedGzipped length]],
nil);
@@ -141,7 +159,11 @@ static BOOL HasGzipHeader(NSData *data) {
STAssertNotNil(suffixedDeflated, @"failed to alloc data block");
[suffixedDeflated appendData:deflated];
[suffixedDeflated appendBytes:[data bytes] length:20];
+ [GTMUnitTestDevLog expectString:@"thought we finished inflate w/o using "
+ "all input, 20 bytes left"];
STAssertNil([NSData gtm_dataByInflatingData:suffixedDeflated], nil);
+ [GTMUnitTestDevLog expectString:@"thought we finished inflate w/o using "
+ "all input, 20 bytes left"];
STAssertNil([NSData gtm_dataByInflatingBytes:[suffixedDeflated bytes]
length:[suffixedDeflated length]],
nil);
@@ -149,7 +171,11 @@ static BOOL HasGzipHeader(NSData *data) {
STAssertNotNil(suffixedGZipped, @"failed to alloc data block");
[suffixedGZipped appendData:gzipped];
[suffixedGZipped appendBytes:[data bytes] length:20];
+ [GTMUnitTestDevLog expectString:@"thought we finished inflate w/o using "
+ "all input, 20 bytes left"];
STAssertNil([NSData gtm_dataByInflatingData:suffixedGZipped], nil);
+ [GTMUnitTestDevLog expectString:@"thought we finished inflate w/o using "
+ "all input, 20 bytes left"];
STAssertNil([NSData gtm_dataByInflatingBytes:[suffixedGZipped bytes]
length:[suffixedGZipped length]],
nil);
@@ -173,24 +199,32 @@ static BOOL HasGzipHeader(NSData *data) {
FillWithRandom([data mutableBytes], [data length]);
// w/ *Bytes apis, default level
- NSData *deflated = [NSData gtm_dataByDeflatingBytes:[data bytes] length:[data length]];
+ NSData *deflated = [NSData gtm_dataByDeflatingBytes:[data bytes]
+ length:[data length]];
STAssertNotNil(deflated, @"failed to deflate data block");
- STAssertGreaterThan([deflated length], (NSUInteger)0, @"failed to deflate data block");
+ STAssertGreaterThan([deflated length],
+ (NSUInteger)0, @"failed to deflate data block");
STAssertFalse(HasGzipHeader(deflated), @"has gzip header on zlib data");
- NSData *dataPrime = [NSData gtm_dataByInflatingBytes:[deflated bytes] length:[deflated length]];
+ NSData *dataPrime = [NSData gtm_dataByInflatingBytes:[deflated bytes]
+ length:[deflated length]];
STAssertNotNil(dataPrime, @"failed to inflate data block");
- STAssertGreaterThan([dataPrime length], (NSUInteger)0, @"failed to inflate data block");
- STAssertEqualObjects(data, dataPrime, @"failed to round trip via *Bytes apis");
+ STAssertGreaterThan([dataPrime length],
+ (NSUInteger)0, @"failed to inflate data block");
+ STAssertEqualObjects(data,
+ dataPrime, @"failed to round trip via *Bytes apis");
// w/ *Data apis, default level
deflated = [NSData gtm_dataByDeflatingData:data];
STAssertNotNil(deflated, @"failed to deflate data block");
- STAssertGreaterThan([deflated length], (NSUInteger)0, @"failed to deflate data block");
+ STAssertGreaterThan([deflated length],
+ (NSUInteger)0, @"failed to deflate data block");
STAssertFalse(HasGzipHeader(deflated), @"has gzip header on zlib data");
dataPrime = [NSData gtm_dataByInflatingData:deflated];
STAssertNotNil(dataPrime, @"failed to inflate data block");
- STAssertGreaterThan([dataPrime length], (NSUInteger)0, @"failed to inflate data block");
- STAssertEqualObjects(data, dataPrime, @"failed to round trip via *Data apis");
+ STAssertGreaterThan([dataPrime length],
+ (NSUInteger)0, @"failed to inflate data block");
+ STAssertEqualObjects(data,
+ dataPrime, @"failed to round trip via *Data apis");
// loop over the compression levels
for (int level = 1 ; level < 9 ; ++level) {
@@ -199,22 +233,29 @@ static BOOL HasGzipHeader(NSData *data) {
length:[data length]
compressionLevel:level];
STAssertNotNil(deflated, @"failed to deflate data block");
- STAssertGreaterThan([deflated length], (NSUInteger)0, @"failed to deflate data block");
+ STAssertGreaterThan([deflated length],
+ (NSUInteger)0, @"failed to deflate data block");
STAssertFalse(HasGzipHeader(deflated), @"has gzip header on zlib data");
- dataPrime = [NSData gtm_dataByInflatingBytes:[deflated bytes] length:[deflated length]];
+ dataPrime = [NSData gtm_dataByInflatingBytes:[deflated bytes]
+ length:[deflated length]];
STAssertNotNil(dataPrime, @"failed to inflate data block");
- STAssertGreaterThan([dataPrime length], (NSUInteger)0, @"failed to inflate data block");
- STAssertEqualObjects(data, dataPrime, @"failed to round trip via *Bytes apis");
+ STAssertGreaterThan([dataPrime length],
+ (NSUInteger)0, @"failed to inflate data block");
+ STAssertEqualObjects(data,
+ dataPrime, @"failed to round trip via *Bytes apis");
// w/ *Data apis, using our level
deflated = [NSData gtm_dataByDeflatingData:data compressionLevel:level];
STAssertNotNil(deflated, @"failed to deflate data block");
- STAssertGreaterThan([deflated length], (NSUInteger)0, @"failed to deflate data block");
+ STAssertGreaterThan([deflated length],
+ (NSUInteger)0, @"failed to deflate data block");
STAssertFalse(HasGzipHeader(deflated), @"has gzip header on zlib data");
dataPrime = [NSData gtm_dataByInflatingData:deflated];
STAssertNotNil(dataPrime, @"failed to inflate data block");
- STAssertGreaterThan([dataPrime length], (NSUInteger)0, @"failed to inflate data block");
- STAssertEqualObjects(data, dataPrime, @"failed to round trip via *Data apis");
+ STAssertGreaterThan([dataPrime length],
+ (NSUInteger)0, @"failed to inflate data block");
+ STAssertEqualObjects(data,
+ dataPrime, @"failed to round trip via *Data apis");
}
[localPool release];
@@ -238,24 +279,34 @@ static BOOL HasGzipHeader(NSData *data) {
FillWithRandom([data mutableBytes], [data length]);
// w/ *Bytes apis, default level
- NSData *gzipped = [NSData gtm_dataByGzippingBytes:[data bytes] length:[data length]];
+ NSData *gzipped = [NSData gtm_dataByGzippingBytes:[data bytes]
+ length:[data length]];
STAssertNotNil(gzipped, @"failed to gzip data block");
- STAssertGreaterThan([gzipped length], (NSUInteger)0, @"failed to gzip data block");
- STAssertTrue(HasGzipHeader(gzipped), @"doesn't have gzip header on gzipped data");
- NSData *dataPrime = [NSData gtm_dataByInflatingBytes:[gzipped bytes] length:[gzipped length]];
+ STAssertGreaterThan([gzipped length],
+ (NSUInteger)0, @"failed to gzip data block");
+ STAssertTrue(HasGzipHeader(gzipped),
+ @"doesn't have gzip header on gzipped data");
+ NSData *dataPrime = [NSData gtm_dataByInflatingBytes:[gzipped bytes]
+ length:[gzipped length]];
STAssertNotNil(dataPrime, @"failed to inflate data block");
- STAssertGreaterThan([dataPrime length], (NSUInteger)0, @"failed to inflate data block");
- STAssertEqualObjects(data, dataPrime, @"failed to round trip via *Bytes apis");
+ STAssertGreaterThan([dataPrime length],
+ (NSUInteger)0, @"failed to inflate data block");
+ STAssertEqualObjects(data,
+ dataPrime, @"failed to round trip via *Bytes apis");
// w/ *Data apis, default level
gzipped = [NSData gtm_dataByGzippingData:data];
STAssertNotNil(gzipped, @"failed to gzip data block");
- STAssertGreaterThan([gzipped length], (NSUInteger)0, @"failed to gzip data block");
- STAssertTrue(HasGzipHeader(gzipped), @"doesn't have gzip header on gzipped data");
+ STAssertGreaterThan([gzipped length],
+ (NSUInteger)0, @"failed to gzip data block");
+ STAssertTrue(HasGzipHeader(gzipped),
+ @"doesn't have gzip header on gzipped data");
dataPrime = [NSData gtm_dataByInflatingData:gzipped];
STAssertNotNil(dataPrime, @"failed to inflate data block");
- STAssertGreaterThan([dataPrime length], (NSUInteger)0, @"failed to inflate data block");
- STAssertEqualObjects(data, dataPrime, @"failed to round trip via *Data apis");
+ STAssertGreaterThan([dataPrime length],
+ (NSUInteger)0, @"failed to inflate data block");
+ STAssertEqualObjects(data, dataPrime,
+ @"failed to round trip via *Data apis");
// loop over the compression levels
for (int level = 1 ; level < 9 ; ++level) {
@@ -264,22 +315,31 @@ static BOOL HasGzipHeader(NSData *data) {
length:[data length]
compressionLevel:level];
STAssertNotNil(gzipped, @"failed to gzip data block");
- STAssertGreaterThan([gzipped length], (NSUInteger)0, @"failed to gzip data block");
- STAssertTrue(HasGzipHeader(gzipped), @"doesn't have gzip header on gzipped data");
- dataPrime = [NSData gtm_dataByInflatingBytes:[gzipped bytes] length:[gzipped length]];
+ STAssertGreaterThan([gzipped length],
+ (NSUInteger)0, @"failed to gzip data block");
+ STAssertTrue(HasGzipHeader(gzipped),
+ @"doesn't have gzip header on gzipped data");
+ dataPrime = [NSData gtm_dataByInflatingBytes:[gzipped bytes]
+ length:[gzipped length]];
STAssertNotNil(dataPrime, @"failed to inflate data block");
- STAssertGreaterThan([dataPrime length], (NSUInteger)0, @"failed to inflate data block");
- STAssertEqualObjects(data, dataPrime, @"failed to round trip via *Bytes apis");
+ STAssertGreaterThan([dataPrime length],
+ (NSUInteger)0, @"failed to inflate data block");
+ STAssertEqualObjects(data, dataPrime,
+ @"failed to round trip via *Bytes apis");
// w/ *Data apis, using our level
gzipped = [NSData gtm_dataByGzippingData:data compressionLevel:level];
STAssertNotNil(gzipped, @"failed to gzip data block");
- STAssertGreaterThan([gzipped length], (NSUInteger)0, @"failed to gzip data block");
- STAssertTrue(HasGzipHeader(gzipped), @"doesn't have gzip header on gzipped data");
+ STAssertGreaterThan([gzipped length],
+ (NSUInteger)0, @"failed to gzip data block");
+ STAssertTrue(HasGzipHeader(gzipped),
+ @"doesn't have gzip header on gzipped data");
dataPrime = [NSData gtm_dataByInflatingData:gzipped];
STAssertNotNil(dataPrime, @"failed to inflate data block");
- STAssertGreaterThan([dataPrime length], (NSUInteger)0, @"failed to inflate data block");
- STAssertEqualObjects(data, dataPrime, @"failed to round trip via *Data apis");
+ STAssertGreaterThan([dataPrime length],
+ (NSUInteger)0, @"failed to inflate data block");
+ STAssertEqualObjects(data,
+ dataPrime, @"failed to round trip via *Data apis");
}
[localPool release];
diff --git a/Foundation/GTMNSDictionary+URLArguments.m b/Foundation/GTMNSDictionary+URLArguments.m
index d67572c..9b3d67e 100644
--- a/Foundation/GTMNSDictionary+URLArguments.m
+++ b/Foundation/GTMNSDictionary+URLArguments.m
@@ -22,7 +22,7 @@
@implementation NSDictionary (GTMNSDictionaryURLArgumentsAdditions)
-GTM_METHOD_CHECK(NSString, gtm_stringByEscapingForURLArgument);
+GTM_METHOD_CHECK(NSString, gtm_stringByEscapingForURLArgument); // COV_NF_LINE
- (NSString *)gtm_httpArgumentsString {
NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:[self count]];
diff --git a/Foundation/GTMNSDictionary+URLArgumentsTest.m b/Foundation/GTMNSDictionary+URLArgumentsTest.m
index 94d06b9..f01519e 100644
--- a/Foundation/GTMNSDictionary+URLArgumentsTest.m
+++ b/Foundation/GTMNSDictionary+URLArgumentsTest.m
@@ -20,7 +20,7 @@
#import "GTMNSDictionary+URLArguments.h"
#import "GTMDefines.h"
-@interface GTMNSDictionary_URLArgumentsTest : SenTestCase
+@interface GTMNSDictionary_URLArgumentsTest : GTMTestCase
@end
@implementation GTMNSDictionary_URLArgumentsTest
diff --git a/Foundation/GTMNSEnumerator+Filter.m b/Foundation/GTMNSEnumerator+Filter.m
index 0fda2ca..51d56f8 100644
--- a/Foundation/GTMNSEnumerator+Filter.m
+++ b/Foundation/GTMNSEnumerator+Filter.m
@@ -19,6 +19,12 @@
#import "GTMNSEnumerator+Filter.h"
#import "GTMDebugSelectorValidation.h"
#import "GTMDefines.h"
+#if GTM_IPHONE_SDK
+#import <objc/message.h>
+#import <objc/runtime.h>
+#else
+#import <objc/objc-runtime.h>
+#endif
// a private subclass of NSEnumerator that does all the work.
// public interface just returns one of these.
@@ -101,10 +107,13 @@
@implementation GTMEnumeratorFilter
// We must take care here, since Intel leaves junk in high bytes of return register
// for predicates that return BOOL.
+// For details see:
+// http://developer.apple.com/documentation/MacOSX/Conceptual/universal_binary/universal_binary_tips/chapter_5_section_23.html
+// and
+// http://www.red-sweater.com/blog/320/abusing-objective-c-with-class#comment-83187
- (BOOL)filterObject:(id)obj returning:(id *)resultp {
*resultp = obj;
- // intptr_t is an integer the same size as a pointer. <stdint.h>
- return (BOOL) (intptr_t) [obj performSelector:operation_ withObject:other_];
+ return ((BOOL (*)(id, SEL, id))objc_msgSend)(obj, operation_, other_);
}
@end
@@ -115,10 +124,13 @@
@implementation GTMEnumeratorTargetFilter
// We must take care here, since Intel leaves junk in high bytes of return register
// for predicates that return BOOL.
+// For details see:
+// http://developer.apple.com/documentation/MacOSX/Conceptual/universal_binary/universal_binary_tips/chapter_5_section_23.html
+// and
+// http://www.red-sweater.com/blog/320/abusing-objective-c-with-class#comment-83187
- (BOOL)filterObject:(id)obj returning:(id *)resultp {
*resultp = obj;
- // intptr_t is an integer the same size as a pointer. <stdint.h>
- return (BOOL) (intptr_t) [other_ performSelector:operation_ withObject:obj];
+ return ((BOOL (*)(id, SEL, id))objc_msgSend)(other_, operation_, obj);
}
@end
@@ -142,9 +154,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);
+ GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments(target, selector,
+ @encode(BOOL),
+ @encode(id),
+ NULL);
return [[[GTMEnumeratorTargetFilter alloc] initWithBase:self
sel:selector
object:target] autorelease];
@@ -153,9 +166,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);
+ GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments(target, selector,
+ @encode(id),
+ @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 224b8b0..ef0d955 100644
--- a/Foundation/GTMNSEnumerator+FilterTest.m
+++ b/Foundation/GTMNSEnumerator+FilterTest.m
@@ -19,7 +19,7 @@
#import "GTMSenTestCase.h"
#import "GTMNSEnumerator+Filter.h"
-@interface GTMNSEnumerator_FilterTest : SenTestCase
+@interface GTMNSEnumerator_FilterTest : GTMTestCase
@end
@implementation GTMNSEnumerator_FilterTest
diff --git a/Foundation/GTMNSFileManager+PathTest.m b/Foundation/GTMNSFileManager+PathTest.m
index 0f2eb35..ac36cac 100644
--- a/Foundation/GTMNSFileManager+PathTest.m
+++ b/Foundation/GTMNSFileManager+PathTest.m
@@ -19,7 +19,7 @@
#import "GTMSenTestCase.h"
#import "GTMNSFileManager+Path.h"
-@interface GTMNSFileManager_PathTest : SenTestCase {
+@interface GTMNSFileManager_PathTest : GTMTestCase {
NSString *baseDir_;
}
@end
@@ -131,7 +131,7 @@
@"a.txt", @"b.txt", @"c.rtf", @"d.m",
};
- for (int i = 0; i < sizeof(testDirs) / sizeof(NSString*); i++) {
+ for (size_t i = 0; i < sizeof(testDirs) / sizeof(NSString*); i++) {
NSString *testDir = nil;
if ([testDirs[i] length]) {
testDir = [baseDir_ stringByAppendingPathComponent:testDirs[i]];
@@ -139,7 +139,7 @@
} else {
testDir = baseDir_;
}
- for (int j = 0; j < sizeof(testFiles) / sizeof(NSString*); j++) {
+ for (size_t j = 0; j < sizeof(testFiles) / sizeof(NSString*); j++) {
NSString *testFile = [testDir stringByAppendingPathComponent:testFiles[j]];
STAssertTrue([@"test" writeToFile:testFile atomically:YES], nil);
}
@@ -147,13 +147,13 @@
// build set of the top level items
NSMutableArray *allFiles = [NSMutableArray array];
- for (int i = 0; i < sizeof(testDirs) / sizeof(NSString*); i++) {
+ for (size_t 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++) {
+ for (size_t j = 0; j < sizeof(testFiles) / sizeof(NSString*); j++) {
NSString *testFile = [baseDir_ stringByAppendingPathComponent:testFiles[j]];
[allFiles addObject:testFile];
}
diff --git a/Foundation/GTMNSString+HTML.m b/Foundation/GTMNSString+HTML.m
index 5178ba9..6853df0 100644
--- a/Foundation/GTMNSString+HTML.m
+++ b/Foundation/GTMNSString+HTML.m
@@ -357,8 +357,8 @@ static HTMLEscapeMap gUnicodeHTMLEscapeMap[] = {
// Utility function for Bsearching table above
static int EscapeMapCompare(const void *ucharVoid, const void *mapVoid) {
- unichar *uchar = (unichar*)ucharVoid;
- HTMLEscapeMap *map = (HTMLEscapeMap*)mapVoid;
+ const unichar *uchar = (const unichar*)ucharVoid;
+ const HTMLEscapeMap *map = (const HTMLEscapeMap*)mapVoid;
int val;
if (*uchar > map->uchar) {
val = 1;
@@ -387,18 +387,16 @@ static int EscapeMapCompare(const void *ucharVoid, const void *mapVoid) {
// it's so short that it isn't really worth trying to share.
const unichar *buffer = CFStringGetCharactersPtr((CFStringRef)self);
if (!buffer) {
- size_t memsize = length * sizeof(UniChar);
-
- // nope, alloc buffer and fetch the chars ourselves
- buffer = malloc(memsize);
- if (!buffer) {
+ // We want this buffer to be autoreleased.
+ NSMutableData *data = [NSMutableData dataWithLength:length * sizeof(UniChar)];
+ if (!data) {
// COV_NF_START - Memory fail case
_GTMDevLog(@"couldn't alloc buffer");
return nil;
// COV_NF_END
}
- [self getCharacters:(void*)buffer];
- [NSData dataWithBytesNoCopy:(void*)buffer length:memsize];
+ [self getCharacters:[data mutableBytes]];
+ buffer = [data bytes];
}
if (!buffer || !data2) {
diff --git a/Foundation/GTMNSString+HTMLTest.m b/Foundation/GTMNSString+HTMLTest.m
index a56c5a5..1c7baf0 100644
--- a/Foundation/GTMNSString+HTMLTest.m
+++ b/Foundation/GTMNSString+HTMLTest.m
@@ -19,7 +19,7 @@
#import "GTMSenTestCase.h"
#import "GTMNSString+HTML.h"
-@interface GTMNSString_HTMLTest : SenTestCase
+@interface GTMNSString_HTMLTest : GTMTestCase
@end
@implementation GTMNSString_HTMLTest
diff --git a/Foundation/GTMNSString+URLArgumentsTest.m b/Foundation/GTMNSString+URLArgumentsTest.m
index c87847e..90a5a81 100644
--- a/Foundation/GTMNSString+URLArgumentsTest.m
+++ b/Foundation/GTMNSString+URLArgumentsTest.m
@@ -19,7 +19,7 @@
#import "GTMSenTestCase.h"
#import "GTMNSString+URLArguments.h"
-@interface GTMNSString_URLArgumentsTest : SenTestCase
+@interface GTMNSString_URLArgumentsTest : GTMTestCase
@end
@implementation GTMNSString_URLArgumentsTest
diff --git a/Foundation/GTMNSString+XML.m b/Foundation/GTMNSString+XML.m
index bc2f130..909a8f1 100644
--- a/Foundation/GTMNSString+XML.m
+++ b/Foundation/GTMNSString+XML.m
@@ -113,18 +113,16 @@ static NSString *AutoreleasedCloneForXML(NSString *src, BOOL escaping) {
// it's so short that it isn't really worth trying to share.
const UniChar *buffer = CFStringGetCharactersPtr((CFStringRef)src);
if (!buffer) {
- size_t memsize = length * sizeof(UniChar);
-
- // nope, alloc buffer and fetch the chars ourselves
- buffer = malloc(memsize);
- if (!buffer) {
+ // We want this buffer to be autoreleased.
+ NSMutableData *data = [NSMutableData dataWithLength:length * sizeof(UniChar)];
+ if (!data) {
// COV_NF_START - Memory fail case
_GTMDevLog(@"couldn't alloc buffer");
return nil;
// COV_NF_END
}
- [src getCharacters:(void*)buffer];
- [NSData dataWithBytesNoCopy:(void*)buffer length:memsize];
+ [src getCharacters:[data mutableBytes]];
+ buffer = [data bytes];
}
const UniChar *goodRun = buffer;
diff --git a/Foundation/GTMNSString+XMLTest.m b/Foundation/GTMNSString+XMLTest.m
index f1e964d..dc157dc 100644
--- a/Foundation/GTMNSString+XMLTest.m
+++ b/Foundation/GTMNSString+XMLTest.m
@@ -21,7 +21,7 @@
#import "GTMNSString+XML.h"
-@interface GTMNSString_XMLTest : SenTestCase
+@interface GTMNSString_XMLTest : GTMTestCase
@end
@implementation GTMNSString_XMLTest
diff --git a/Foundation/GTMObjC2RuntimeTest.m b/Foundation/GTMObjC2RuntimeTest.m
index 2a7f354..6fb5bdf 100644
--- a/Foundation/GTMObjC2RuntimeTest.m
+++ b/Foundation/GTMObjC2RuntimeTest.m
@@ -34,7 +34,7 @@ AT_REQUIRED
+ (NSString*)class_required;
@end
-@interface GTMObjC2RuntimeTest : SenTestCase {
+@interface GTMObjC2RuntimeTest : GTMTestCase {
Class cls_;
}
@end
diff --git a/Foundation/GTMRegex.m b/Foundation/GTMRegex.m
index 92eb576..f183553 100644
--- a/Foundation/GTMRegex.m
+++ b/Foundation/GTMRegex.m
@@ -228,7 +228,7 @@ static NSString *const kReplacementPattern =
// make sure the match is the full string
return (regMatch.rm_so == 0) &&
- (regMatch.rm_eo == [str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
+ (regMatch.rm_eo == (regoff_t)[str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
}
- (NSArray *)subPatternsOfString:(NSString *)str {
@@ -252,7 +252,7 @@ static NSString *const kReplacementPattern =
// make sure the match is the full string
if ((regMatches[0].rm_so != 0) ||
- (regMatches[0].rm_eo != [str lengthOfBytesUsingEncoding:NSUTF8StringEncoding])) {
+ (regMatches[0].rm_eo != (regoff_t)[str lengthOfBytesUsingEncoding:NSUTF8StringEncoding])) {
// only matched a sub part of the string
return nil;
}
@@ -551,7 +551,7 @@ static NSString *const kReplacementPattern =
isMatch = YES; // if we have something saved, it was a pattern match
}
// have we reached the end?
- else if (curParseIndex_ >= [utf8StrBuf_ length]) {
+ else if (curParseIndex_ >= (regoff_t)[utf8StrBuf_ length]) {
// done, do nothing, we'll return nil
}
// do the search.
@@ -697,17 +697,20 @@ static NSString *const kReplacementPattern =
return [self subPatternString:0];
}
-- (NSString *)subPatternString:(NSUInteger)index {
- if ((index < 0) || (index > numRegMatches_))
+- (NSString *)subPatternString:(NSUInteger)patternIndex {
+ if (patternIndex > numRegMatches_)
return nil;
// pick off when it wasn't found
- if ((regMatches_[index].rm_so == -1) && (regMatches_[index].rm_eo == -1))
+ if ((regMatches_[patternIndex].rm_so == -1) &&
+ (regMatches_[patternIndex].rm_eo == -1))
return nil;
// fetch the string
- const char *base = (const char*)[utf8StrBuf_ bytes] + regMatches_[index].rm_so;
- regoff_t len = regMatches_[index].rm_eo - regMatches_[index].rm_so;
+ const char *base = (const char*)[utf8StrBuf_ bytes]
+ + regMatches_[patternIndex].rm_so;
+ regoff_t len = regMatches_[patternIndex].rm_eo
+ - regMatches_[patternIndex].rm_so;
return [[[NSString alloc] initWithBytes:base
length:(NSUInteger)len
encoding:NSUTF8StringEncoding] autorelease];
@@ -748,7 +751,7 @@ static NSString *const kReplacementPattern =
isMatch_ = isMatch;
// check the args
- if (!utf8StrBuf_ || !regMatches_ || (numRegMatches_ < 0)) {
+ if (!utf8StrBuf_ || !regMatches_) {
// COV_NF_START
// this could only happen something messed w/ our internal state.
[self release];
diff --git a/Foundation/GTMRegexTest.m b/Foundation/GTMRegexTest.m
index 033b560..6f41d60 100644
--- a/Foundation/GTMRegexTest.m
+++ b/Foundation/GTMRegexTest.m
@@ -18,6 +18,7 @@
#import "GTMSenTestCase.h"
#import "GTMRegex.h"
+#import "GTMUnitTestDevLog.h"
//
// NOTE:
@@ -26,10 +27,10 @@
// libregex, we just want to test our wrapper.
//
-@interface GTMRegexTest : SenTestCase
+@interface GTMRegexTest : GTMTestCase
@end
-@interface NSString_GTMRegexAdditions : SenTestCase
+@interface NSString_GTMRegexAdditions : GTMTestCase
@end
@implementation GTMRegexTest
@@ -57,7 +58,9 @@
STAssertNil([[[GTMRegex alloc] initWithPattern:nil] autorelease], nil);
STAssertNil([[[GTMRegex alloc] initWithPattern:nil
options:kGTMRegexOptionIgnoreCase] autorelease], nil);
+ [GTMUnitTestDevLog expectString:@"Invalid pattern \"(.\", error: \"parentheses not balanced\""];
STAssertNil([[[GTMRegex alloc] initWithPattern:@"(."] autorelease], nil);
+ [GTMUnitTestDevLog expectString:@"Invalid pattern \"(.\", error: \"parentheses not balanced\""];
STAssertNil([[[GTMRegex alloc] initWithPattern:@"(."
options:kGTMRegexOptionIgnoreCase] autorelease], nil);
// fail cases w/ error param
@@ -93,7 +96,9 @@
STAssertNil([GTMRegex regexWithPattern:nil], nil);
STAssertNil([GTMRegex regexWithPattern:nil
options:0], nil);
+ [GTMUnitTestDevLog expectString:@"Invalid pattern \"(.\", error: \"parentheses not balanced\""];
STAssertNil([GTMRegex regexWithPattern:@"(."], nil);
+ [GTMUnitTestDevLog expectString:@"Invalid pattern \"(.\", error: \"parentheses not balanced\""];
STAssertNil([GTMRegex regexWithPattern:@"(."
options:0], nil);
// fail cases (helper) w/ error param
diff --git a/Foundation/GTMScriptRunnerTest.m b/Foundation/GTMScriptRunnerTest.m
index 5229800..45378bc 100644
--- a/Foundation/GTMScriptRunnerTest.m
+++ b/Foundation/GTMScriptRunnerTest.m
@@ -20,8 +20,9 @@
#import <unistd.h>
#import "GTMSenTestCase.h"
#import "GTMScriptRunner.h"
+#import "GTMUnitTestDevLog.h"
-@interface GTMScriptRunnerTest : SenTestCase {
+@interface GTMScriptRunnerTest : GTMTestCase {
@private
NSString *shScript_;
NSString *perlScript_;
@@ -67,14 +68,17 @@
- (void)tearDown {
const char *path = [shScript_ fileSystemRepresentation];
- if (path)
+ if (path) {
unlink(path);
+ }
path = [perlScript_ fileSystemRepresentation];
- if (path)
+ if (path) {
unlink(path);
+ }
path = [shOutputScript_ fileSystemRepresentation];
- if (path)
+ if (path) {
unlink(path);
+ }
}
- (void)testShCommands {
@@ -184,30 +188,30 @@
output = [sr run:@"/usr/bin/env | wc -l"];
int numVars = [output intValue];
- STAssertTrue(numVars > 0, @"numVars should be positive");
+ STAssertGreaterThan(numVars, 0, @"numVars should be positive");
// By default the environment is wiped clean, however shells often add a few
// of their own env vars after things have been wiped. For example, sh will
// add about 3 env vars (PWD, _, and SHLVL).
- STAssertTrue(numVars < 5, @"Our env should be almost empty");
+ STAssertLessThan(numVars, 5, @"Our env should be almost empty");
NSDictionary *newEnv = [NSDictionary dictionaryWithObject:@"bar"
forKey:@"foo"];
[sr setEnvironment:newEnv];
output = [sr run:@"/usr/bin/env | wc -l"];
- STAssertTrue([output intValue] == numVars + 1,
+ STAssertEquals([output intValue], numVars + 1,
@"should have one more env var now");
[sr setEnvironment:nil];
output = [sr run:@"/usr/bin/env | wc -l"];
- STAssertTrue([output intValue] == numVars,
+ STAssertEquals([output intValue], numVars,
@"should be back down to %d vars", numVars);
NSDictionary *currVars = [[NSProcessInfo processInfo] environment];
[sr setEnvironment:currVars];
output = [sr run:@"/usr/bin/env | wc -l"];
- STAssertTrue([output intValue] == [currVars count],
+ STAssertEquals([output intValue], (int)[currVars count],
@"should be back down to %d vars", numVars);
}
@@ -339,6 +343,8 @@
STAssertNil([sr run:nil standardError:&err], nil);
STAssertNil(err, nil);
+ [GTMUnitTestDevLog expectString:@"Failed to launch interpreter "
+ "'/path/that/does/not/exists/interpreter' due to: launch path not accessible"];
STAssertNil([sr run:@"ls /" standardError:&err], nil);
STAssertNil(err, nil);
}
@@ -349,8 +355,12 @@
STAssertNotNil(sr, @"Script runner must not be nil");
NSString *err = nil;
+ [GTMUnitTestDevLog expectString:@"Failed to launch interpreter "
+ "'/path/that/does/not/exists/interpreter' due to: launch path not accessible"];
STAssertNil([sr runScript:shScript_ withArgs:nil standardError:&err], nil);
STAssertNil(err, nil);
+ [GTMUnitTestDevLog expectString:@"Failed to launch interpreter "
+ "'/path/that/does/not/exists/interpreter' due to: launch path not accessible"];
STAssertNil([sr runScript:@"/path/that/does/not/exists/foo/bar/baz"
withArgs:nil standardError:&err], nil);
STAssertNil(err, nil);
diff --git a/Foundation/GTMStackTrace.c b/Foundation/GTMStackTrace.c
index febf623..68e5c7d 100644
--- a/Foundation/GTMStackTrace.c
+++ b/Foundation/GTMStackTrace.c
@@ -76,7 +76,7 @@ CFStringRef GTMStackTraceCreate(void) {
CFMutableStringRef trace = CFStringCreateMutable(kCFAllocatorDefault, 0);
for (int i = 0; i < depth; i++) {
- Dl_info info = { 0 };
+ Dl_info info = { NULL, NULL, NULL, NULL };
dladdr(pcs[i], &info);
const char *symbol = info.dli_sname;
const char *fname = info.dli_fname;
diff --git a/Foundation/GTMStackTraceTest.m b/Foundation/GTMStackTraceTest.m
index 7845a2d..16c6273 100644
--- a/Foundation/GTMStackTraceTest.m
+++ b/Foundation/GTMStackTraceTest.m
@@ -21,7 +21,7 @@
#import "GTMStackTrace.h"
#import "GTMSenTestCase.h"
-@interface GTMStackTraceTest : SenTestCase
+@interface GTMStackTraceTest : GTMTestCase
@end
@implementation GTMStackTraceTest
diff --git a/Foundation/GTMSystemVersionTest.m b/Foundation/GTMSystemVersionTest.m
index 38c37eb..e95a33d 100644
--- a/Foundation/GTMSystemVersionTest.m
+++ b/Foundation/GTMSystemVersionTest.m
@@ -19,7 +19,7 @@
#import "GTMSenTestCase.h"
#import "GTMSystemVersion.h"
-@interface GTMSystemVersionTest : SenTestCase
+@interface GTMSystemVersionTest : GTMTestCase
@end
@implementation GTMSystemVersionTest
diff --git a/Foundation/TestData/GTMHTTPFetcherTestServer b/Foundation/TestData/GTMHTTPFetcherTestServer
deleted file mode 100755
index 838c110..0000000
--- a/Foundation/TestData/GTMHTTPFetcherTestServer
+++ /dev/null
@@ -1,274 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2006-2008 Google Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not
-# use this file except in compliance with the License. You may obtain a copy
-# of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations under
-# the License.
-#
-# A simple server for testing the http calls
-
-"""A simple BaseHTTPRequestHandler for unit testing GTM Network code
-
-This http server is for use by GTMHTTPFetcherTest.m in testing
-both authentication and object retrieval.
-
-Requests to the path /accounts/ClientLogin are assumed to be
-for login; other requests are for object retrieval
-"""
-
-__author__ = 'Google Inc.'
-
-import string
-import cgi
-import time
-import os
-import sys
-import re
-import mimetypes
-import socket
-from BaseHTTPServer import BaseHTTPRequestHandler
-from BaseHTTPServer import HTTPServer
-from optparse import OptionParser
-
-class ServerTimeoutException(Exception):
- pass
-
-
-class TimeoutServer(HTTPServer):
-
- """HTTP server for testing GTM network requests.
-
- This server will throw an exception if it receives no connections for
- several minutes. We use this to ensure that the server will be cleaned
- up if something goes wrong during the unit testing.
- """
-
- def get_request(self):
- self.socket.settimeout(120.0)
- result = None
- while result is None:
- try:
- result = self.socket.accept()
- except socket.timeout:
- raise ServerTimeoutException
- result[0].settimeout(None)
- return result
-
-
-class SimpleServer(BaseHTTPRequestHandler):
-
- """HTTP request handler for testing GTM network requests.
-
- This is an implementation of a request handler for BaseHTTPServer,
- specifically designed for GTM network code usage.
-
- Normal requests for GET/POST/PUT simply retrieve the file from the
- supplied path, starting in the current directory. A cookie called
- TestCookie is set by the response header, with the value of the filename
- requested.
-
- DELETE requests always succeed.
-
- Appending ?status=n results in a failure with status value n.
-
- Paths ending in .auth have the .auth extension stripped, and must have
- an authorization header of "GoogleLogin auth=GoodAuthToken" to succeed.
-
- Successful results have a Last-Modified header set; if that header's value
- ("thursday") is supplied in a request's "If-Modified-Since" header, the
- result is 304 (Not Modified).
-
- Requests to /accounts/ClientLogin will fail if supplied with a body
- containing Passwd=bad. If they contain logintoken and logincaptcha values,
- those must be logintoken=CapToken&logincaptch=good to succeed.
- """
-
- def do_GET(self):
- self.doAllRequests()
-
- def do_POST(self):
- self.doAllRequests()
-
- def do_PUT(self):
- self.doAllRequests()
-
- def do_DELETE(self):
- self.doAllRequests()
-
- def doAllRequests(self):
- # This method handles all expected incoming requests
- #
- # Requests to path /accounts/ClientLogin are assumed to be for signing in
- #
- # Other paths are for retrieving a local file. An .auth appended
- # to a file path will require authentication (meaning the Authorization
- # header must be present with the value "GoogleLogin auth=GoodAuthToken".)
- # If the token is present, the file (without uthe .auth at the end) will
- # be returned.
- #
- # HTTP Delete commands succeed but return no data.
- #
- # GData override headers (putting the verb in X-HTTP-Method-Override)
- # are supported.
- #
- # Any auth password is valid except "bad", which will fail, and "captcha",
- # which will fail unless the authentication request's post string includes
- # "logintoken=CapToken&logincaptcha=good"
-
- # We will use a readable default result string since it should never show up
- # in output
- resultString = "default GTMHTTPFetcherTestServer result\n";
- resultStatus = 0
- headerType = "text/plain"
- postString = ""
- modifiedDate = "thursday" # clients should treat dates as opaque, generally
-
- # auth queries and some others may include post data
- postLength = int(self.headers.getheader("Content-Length", "0"));
- if postLength > 0:
- postString = self.rfile.read(postLength)
-
- # auth queries and some GData queries include post data
- ifModifiedSince = self.headers.getheader("If-Modified-Since", "");
-
- # retrieve the auth header; require it if the file path ends
- # with the string ".auth"
- authorization = self.headers.getheader("Authorization", "")
- if self.path.endswith(".auth"):
- if authorization != "GoogleLogin auth=GoodAuthToken":
- self.send_error(401,"Unauthorized: %s" % self.path)
- return
- self.path = self.path[:-5] # remove the .auth at the end
-
- overrideHeader = self.headers.getheader("X-HTTP-Method-Override", "")
- httpCommand = self.command
- if httpCommand == "POST" and len(overrideHeader) > 0:
- httpCommand = overrideHeader
-
- try:
- if self.path.endswith("/accounts/ClientLogin"):
- #
- # it's a sign-in attempt; it's good unless the password is "bad" or
- # "captcha"
- #
-
- # use regular expression to find the password
- password = ""
- searchResult = re.search("(Passwd=)([^&\n]*)", postString)
- if searchResult:
- password = searchResult.group(2)
-
- if password == "bad":
- resultString = "Error=BadAuthentication\n"
- resultStatus = 403
-
- elif password == "captcha":
- logintoken = ""
- logincaptcha = ""
-
- # use regular expressions to find the captcha token and answer
- searchResult = re.search("(logintoken=)([^&\n]*)", postString);
- if searchResult:
- logintoken = searchResult.group(2)
-
- searchResult = re.search("(logincaptcha=)([^&\n]*)", postString);
- if searchResult:
- logincaptcha = searchResult.group(2)
-
- # if the captcha token is "CapToken" and the answer is "good"
- # then it's a valid sign in
- if (logintoken == "CapToken") and (logincaptcha == "good"):
- resultString = "SID=GoodSID\nLSID=GoodLSID\nAuth=GoodAuthToken\n"
- resultStatus = 200
- else:
- # incorrect captcha token or answer provided
- resultString = ("Error=CaptchaRequired\nCaptchaToken=CapToken"
- "\nCaptchaUrl=CapUrl\n")
- resultStatus = 403
-
- else:
- # valid username/password
- resultString = "SID=GoodSID\nLSID=GoodLSID\nAuth=GoodAuthToken\n"
- resultStatus = 200
-
- elif httpCommand == "DELETE":
- #
- # it's an object delete; read and return empty data
- #
- resultString = ""
- resultStatus = 200
- headerType = "text/plain"
-
- else:
- # queries that have something like "?status=456" should fail with the
- # status code
- searchResult = re.search("(status=)([0-9]+)", self.path)
- if searchResult:
- status = searchResult.group(2)
- self.send_error(int(status),
- "Test HTTP server status parameter: %s" % self.path)
- return
-
- # if the client gave us back our not modified date, then say there's no
- # change in the response
- if ifModifiedSince == modifiedDate:
- self.send_response(304) # Not Modified
- return
-
- else:
- #
- # it's a file fetch; read and return the data file
- #
- f = open("." + self.path)
- resultString = f.read()
- f.close()
- resultStatus = 200
- fileTypeInfo = mimetypes.guess_type("." + self.path)
- headerType = fileTypeInfo[0] # first part of the tuple is mime type
-
- self.send_response(resultStatus)
- self.send_header("Content-type", headerType)
- self.send_header("Last-Modified", modifiedDate)
-
- cookieValue = os.path.basename("." + self.path)
- self.send_header('Set-Cookie', 'TestCookie=%s' % cookieValue)
- self.end_headers()
- self.wfile.write(resultString)
-
- except IOError:
- self.send_error(404,"File Not Found: %s" % self.path)
-
-
-def main():
- try:
- parser = OptionParser()
- parser.add_option("-p", "--port", dest="port", help="Port to run server on",
- type="int", default="80")
- parser.add_option("-r", "--root", dest="root", help="Where to root server",
- default=".")
- (options, args) = parser.parse_args()
- os.chdir(options.root)
- server = TimeoutServer(("127.0.0.1", options.port), SimpleServer)
- sys.stdout.write("started GTMHTTPFetcherTestServer,"
- " serving files from root directory %s..." % os.getcwd());
- sys.stdout.flush();
- server.serve_forever()
- except KeyboardInterrupt:
- print "^C received, shutting down server"
- server.socket.close()
- except ServerTimeoutException:
- print "Too long since the last request, shutting down server"
- server.socket.close()
-
-
-if __name__ == "__main__":
- main()
diff --git a/GTM.xcodeproj/project.pbxproj b/GTM.xcodeproj/project.pbxproj
index 2407639..824ef32 100644
--- a/GTM.xcodeproj/project.pbxproj
+++ b/GTM.xcodeproj/project.pbxproj
@@ -22,6 +22,19 @@
};
/* End PBXAggregateTarget section */
+/* Begin PBXAppleScriptBuildPhase section */
+ 8B3345CC0DBF8A95009FD32C /* AppleScript */ = {
+ isa = PBXAppleScriptBuildPhase;
+ buildActionMask = 2147483647;
+ contextName = "";
+ files = (
+ 8B3345890DBF8A55009FD32C /* GTMNSAppleEvent+HandlerTest.applescript in AppleScript */,
+ );
+ isSharedContext = 0;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXAppleScriptBuildPhase section */
+
/* Begin PBXBuildFile section */
33C372A60DD8A88500E97817 /* GTMNSString+URLArguments.h in Headers */ = {isa = PBXBuildFile; fileRef = 33C372A40DD8A88500E97817 /* GTMNSString+URLArguments.h */; settings = {ATTRIBUTES = (Public, ); }; };
33C372A70DD8A88500E97817 /* GTMNSString+URLArguments.m in Sources */ = {isa = PBXBuildFile; fileRef = 33C372A50DD8A88500E97817 /* GTMNSString+URLArguments.m */; };
@@ -33,6 +46,14 @@
8B2A9B220D8270DA00599386 /* GTMNSWorkspace+ScreenSaver.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B2A9B1F0D8270DA00599386 /* GTMNSWorkspace+ScreenSaver.h */; settings = {ATTRIBUTES = (Public, ); }; };
8B2A9B240D8270DA00599386 /* GTMNSWorkspace+ScreenSaverTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B2A9B1E0D8270DA00599386 /* GTMNSWorkspace+ScreenSaverTest.m */; };
8B2A9BEC0D82714A00599386 /* ScreenSaver.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B2A9BEB0D82714A00599386 /* ScreenSaver.framework */; };
+ 8B2C21B50E00883F00B5ECB1 /* GTMObjC2Runtime.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F32040DA34A1B0052CA40 /* GTMObjC2Runtime.m */; };
+ 8B2C21B60E00884000B5ECB1 /* GTMObjC2Runtime.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F32040DA34A1B0052CA40 /* GTMObjC2Runtime.m */; };
+ 8B2C21B70E00885600B5ECB1 /* GTMObjC2Runtime.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F32040DA34A1B0052CA40 /* GTMObjC2Runtime.m */; };
+ 8B3344210DBF7A36009FD32C /* GTMNSAppleScript+HandlerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3344170DBF7A36009FD32C /* GTMNSAppleScript+HandlerTest.m */; };
+ 8B3344230DBF7A36009FD32C /* GTMNSAppleEventDescriptor+HandlerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B33441A0DBF7A36009FD32C /* GTMNSAppleEventDescriptor+HandlerTest.m */; };
+ 8B3344250DBF7A36009FD32C /* GTMNSAppleEventDescriptor+FoundationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B33441D0DBF7A36009FD32C /* GTMNSAppleEventDescriptor+FoundationTest.m */; };
+ 8B33455E0DBF8844009FD32C /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F42E09AD0D19A62F00D5DDE0 /* Carbon.framework */; };
+ 8B3345890DBF8A55009FD32C /* GTMNSAppleEvent+HandlerTest.applescript in AppleScript */ = {isa = PBXBuildFile; fileRef = 8B3344200DBF7A36009FD32C /* GTMNSAppleEvent+HandlerTest.applescript */; settings = {ATTRIBUTES = (Debug, ); }; };
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 */; };
@@ -60,13 +81,33 @@
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 */; };
+ 8B7DCB9B0DFF0E850017E983 /* GTMFourCharCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BE2836B0DED0F130035B3F8 /* GTMFourCharCode.m */; };
+ 8B7DCBA50DFF0EFF0017E983 /* GoogleToolboxForMac.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F42E086D0D199A5B00D5DDE0 /* GoogleToolboxForMac.framework */; };
+ 8B7DCBBD0DFF0F5D0017E983 /* GTMMethodCheck.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F31EF0DA347720052CA40 /* GTMMethodCheck.m */; };
+ 8B7DCBC10DFF0F7F0017E983 /* GTMMethodCheck.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F31EF0DA347720052CA40 /* GTMMethodCheck.m */; };
+ 8B7DCBC20DFF0F7F0017E983 /* GTMMethodCheck.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F31EF0DA347720052CA40 /* GTMMethodCheck.m */; };
+ 8B7DCBC30DFF0F7F0017E983 /* GTMMethodCheck.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F31EF0DA347720052CA40 /* GTMMethodCheck.m */; };
+ 8B7DCBC40DFF0F800017E983 /* GTMMethodCheck.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F31EF0DA347720052CA40 /* GTMMethodCheck.m */; };
+ 8B7DCBD20DFF16070017E983 /* GTMNSAppleScript+Handler.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3344180DBF7A36009FD32C /* GTMNSAppleScript+Handler.m */; };
+ 8B7DCBD30DFF16070017E983 /* GTMNSAppleEventDescriptor+Handler.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B33441B0DBF7A36009FD32C /* GTMNSAppleEventDescriptor+Handler.m */; };
+ 8B7DCBD40DFF16070017E983 /* GTMNSAppleEventDescriptor+Foundation.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B33441E0DBF7A36009FD32C /* GTMNSAppleEventDescriptor+Foundation.m */; };
+ 8B7DCBE20DFF18720017E983 /* GTMDevLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B7DCBE10DFF18720017E983 /* GTMDevLog.m */; };
+ 8B7DCBED0DFF1A4F0017E983 /* GTMUnitTestDevLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B7DCBEC0DFF1A4F0017E983 /* GTMUnitTestDevLog.m */; };
+ 8B7DCBEE0DFF1A4F0017E983 /* GTMUnitTestDevLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B7DCBEC0DFF1A4F0017E983 /* GTMUnitTestDevLog.m */; };
+ 8B7DCBEF0DFF1A4F0017E983 /* GTMUnitTestDevLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B7DCBEC0DFF1A4F0017E983 /* GTMUnitTestDevLog.m */; };
+ 8B7DCE190DFF39850017E983 /* GTMSenTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B7DCE180DFF39850017E983 /* GTMSenTestCase.m */; };
+ 8B7DCE1A0DFF39850017E983 /* GTMSenTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B7DCE180DFF39850017E983 /* GTMSenTestCase.m */; };
+ 8B7DCE1B0DFF39850017E983 /* GTMSenTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B7DCE180DFF39850017E983 /* GTMSenTestCase.m */; };
+ 8B7DCE6D0DFF459C0017E983 /* GTMHTTPFetcherTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F435E3930DC8CAAF0069CDE8 /* GTMHTTPFetcherTest.m */; };
+ 8B7DCEF10E002C210017E983 /* GTMDevLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B7DCBE10DFF18720017E983 /* GTMDevLog.m */; };
8BC045C20DAE899100C2D1CA /* GTMGeometryUtilsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F48FE2800D198D0E009257D2 /* GTMGeometryUtilsTest.m */; };
8BC046B90DAE8C4B00C2D1CA /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BC046B80DAE8C4B00C2D1CA /* ApplicationServices.framework */; };
- 8BC04CD70DB003CD00C2D1CA /* GTMMethodCheck.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F31EF0DA347720052CA40 /* GTMMethodCheck.m */; };
8BC04CD80DB003D800C2D1CA /* GTMMethodCheck.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B6F31F40DA3489B0052CA40 /* GTMMethodCheck.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 8BC04CDD0DB004A000C2D1CA /* GTMMethodCheck.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F31EF0DA347720052CA40 /* GTMMethodCheck.m */; };
- 8BC04CDE0DB004A000C2D1CA /* GTMMethodCheck.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F31EF0DA347720052CA40 /* GTMMethodCheck.m */; };
- 8BC04CDF0DB004A100C2D1CA /* GTMMethodCheck.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F31EF0DA347720052CA40 /* GTMMethodCheck.m */; };
+ 8BE281B00DEC7E930035B3F8 /* GTMNSAppleScript+Handler.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B3344190DBF7A36009FD32C /* GTMNSAppleScript+Handler.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 8BE281B10DEC7E930035B3F8 /* GTMNSAppleEventDescriptor+Handler.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B33441C0DBF7A36009FD32C /* GTMNSAppleEventDescriptor+Handler.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 8BE281B20DEC7E930035B3F8 /* GTMNSAppleEventDescriptor+Foundation.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B33441F0DBF7A36009FD32C /* GTMNSAppleEventDescriptor+Foundation.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 8BE2836F0DED0F130035B3F8 /* GTMFourCharCodeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BE2836C0DED0F130035B3F8 /* GTMFourCharCodeTest.m */; };
+ 8BE283730DED13AB0035B3F8 /* GTMFourCharCode.h in Headers */ = {isa = PBXBuildFile; fileRef = 8BE2836D0DED0F130035B3F8 /* GTMFourCharCode.h */; settings = {ATTRIBUTES = (Public, ); }; };
8BE869730DBE89C100749827 /* GTMNSBezierPath+RoundRectTest.x86_64.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 8BE869720DBE89C100749827 /* GTMNSBezierPath+RoundRectTest.x86_64.tiff */; };
8BEEA90D0DA7446300894774 /* GTMUnitTestingImage.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 8BEEA90A0DA7446300894774 /* GTMUnitTestingImage.tiff */; };
8BEEA90E0DA7446300894774 /* GTMUnitTestingWindow.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 8BEEA90B0DA7446300894774 /* GTMUnitTestingWindow.tiff */; };
@@ -116,9 +157,7 @@
F435E08A0DC63F6D0069CDE8 /* GTMHTTPFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = F435E0880DC63F6D0069CDE8 /* GTMHTTPFetcher.m */; };
F435E27F0DC7B0630069CDE8 /* GTMProgressMonitorInputStream.h in Headers */ = {isa = PBXBuildFile; fileRef = F435E27D0DC7B0630069CDE8 /* GTMProgressMonitorInputStream.h */; settings = {ATTRIBUTES = (Public, ); }; };
F435E2800DC7B0630069CDE8 /* GTMProgressMonitorInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = F435E27E0DC7B0630069CDE8 /* GTMProgressMonitorInputStream.m */; };
- F435E3940DC8CAAF0069CDE8 /* GTMHTTPFetcherTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F435E3930DC8CAAF0069CDE8 /* GTMHTTPFetcherTest.m */; };
F435E3BB0DC8D1980069CDE8 /* GTMHTTPFetcherTestPage.html in Resources */ = {isa = PBXBuildFile; fileRef = F435E3B90DC8D1720069CDE8 /* GTMHTTPFetcherTestPage.html */; };
- F435E3BC0DC8D1980069CDE8 /* GTMHTTPFetcherTestServer in Resources */ = {isa = PBXBuildFile; fileRef = F435E3BA0DC8D1720069CDE8 /* GTMHTTPFetcherTestServer */; };
F437F55D0D50BC0A00F5C3A4 /* GTMRegex.h in Headers */ = {isa = PBXBuildFile; fileRef = F437F55A0D50BC0A00F5C3A4 /* GTMRegex.h */; settings = {ATTRIBUTES = (Public, ); }; };
F437F55E0D50BC0A00F5C3A4 /* GTMRegex.m in Sources */ = {isa = PBXBuildFile; fileRef = F437F55B0D50BC0A00F5C3A4 /* GTMRegex.m */; };
F437F5620D50BC1D00F5C3A4 /* GTMRegexTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F437F55C0D50BC0A00F5C3A4 /* GTMRegexTest.m */; };
@@ -147,6 +186,10 @@
F47F1D300D4914AD00925B8F /* GTMCalculatedRange.h in Headers */ = {isa = PBXBuildFile; fileRef = F47F1D2D0D4914AD00925B8F /* GTMCalculatedRange.h */; settings = {ATTRIBUTES = (Public, ); }; };
F47F1D310D4914AD00925B8F /* GTMCalculatedRange.m in Sources */ = {isa = PBXBuildFile; fileRef = F47F1D2E0D4914AD00925B8F /* GTMCalculatedRange.m */; };
F47F1D350D4914B600925B8F /* GTMCalculatedRangeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F47F1D2F0D4914AD00925B8F /* GTMCalculatedRangeTest.m */; };
+ F4BC1C880DDDD45D00108B7D /* GTMHTTPServer.h in Headers */ = {isa = PBXBuildFile; fileRef = F4BC1C860DDDD45D00108B7D /* GTMHTTPServer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ F4BC1C890DDDD45D00108B7D /* GTMHTTPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = F4BC1C870DDDD45D00108B7D /* GTMHTTPServer.m */; };
+ F4BC1E8D0DE1FC4A00108B7D /* GTMHTTPServerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F4BC1E8C0DE1FC4A00108B7D /* GTMHTTPServerTest.m */; };
+ F4BC22D10DE4C39000108B7D /* GTMTestHTTPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = F4BC22D00DE4C39000108B7D /* GTMTestHTTPServer.m */; };
F4CA854F0DAFAAF600B4AB10 /* GTMObjC2Runtime.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B6F32060DA34A1B0052CA40 /* GTMObjC2Runtime.h */; settings = {ATTRIBUTES = (Public, ); }; };
F4FF22780D9D4835003880AC /* GTMDebugSelectorValidation.h in Headers */ = {isa = PBXBuildFile; fileRef = F4FF22770D9D4835003880AC /* GTMDebugSelectorValidation.h */; settings = {ATTRIBUTES = (Public, ); }; };
/* End PBXBuildFile section */
@@ -166,14 +209,14 @@
remoteGlobalIDString = 8B45A2890DA49B99001148C5;
remoteInfo = UIUnitTestingHarness;
};
- F41D254E0DB9067C00774EEB /* PBXContainerItemProxy */ = {
+ 8B7DCBA30DFF0EFB0017E983 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
proxyType = 1;
- remoteGlobalIDString = 8B45A0270DA4696C001148C5;
- remoteInfo = "UnitTest - UnitTesting";
+ remoteGlobalIDString = F42E086C0D199A5B00D5DDE0;
+ remoteInfo = GTM;
};
- F41D25510DB9068700774EEB /* PBXContainerItemProxy */ = {
+ F41D254E0DB9067C00774EEB /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
proxyType = 1;
@@ -228,6 +271,16 @@
8B2A9B1E0D8270DA00599386 /* GTMNSWorkspace+ScreenSaverTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSWorkspace+ScreenSaverTest.m"; sourceTree = "<group>"; };
8B2A9B1F0D8270DA00599386 /* GTMNSWorkspace+ScreenSaver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSWorkspace+ScreenSaver.h"; sourceTree = "<group>"; };
8B2A9BEB0D82714A00599386 /* ScreenSaver.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ScreenSaver.framework; path = /System/Library/Frameworks/ScreenSaver.framework; sourceTree = "<absolute>"; };
+ 8B3344170DBF7A36009FD32C /* GTMNSAppleScript+HandlerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSAppleScript+HandlerTest.m"; sourceTree = "<group>"; };
+ 8B3344180DBF7A36009FD32C /* GTMNSAppleScript+Handler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSAppleScript+Handler.m"; sourceTree = "<group>"; };
+ 8B3344190DBF7A36009FD32C /* GTMNSAppleScript+Handler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSAppleScript+Handler.h"; sourceTree = "<group>"; };
+ 8B33441A0DBF7A36009FD32C /* GTMNSAppleEventDescriptor+HandlerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSAppleEventDescriptor+HandlerTest.m"; sourceTree = "<group>"; };
+ 8B33441B0DBF7A36009FD32C /* GTMNSAppleEventDescriptor+Handler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSAppleEventDescriptor+Handler.m"; sourceTree = "<group>"; };
+ 8B33441C0DBF7A36009FD32C /* GTMNSAppleEventDescriptor+Handler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSAppleEventDescriptor+Handler.h"; sourceTree = "<group>"; };
+ 8B33441D0DBF7A36009FD32C /* GTMNSAppleEventDescriptor+FoundationTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSAppleEventDescriptor+FoundationTest.m"; sourceTree = "<group>"; };
+ 8B33441E0DBF7A36009FD32C /* GTMNSAppleEventDescriptor+Foundation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSAppleEventDescriptor+Foundation.m"; sourceTree = "<group>"; };
+ 8B33441F0DBF7A36009FD32C /* GTMNSAppleEventDescriptor+Foundation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSAppleEventDescriptor+Foundation.h"; sourceTree = "<group>"; };
+ 8B3344200DBF7A36009FD32C /* GTMNSAppleEvent+HandlerTest.applescript */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.applescript; path = "GTMNSAppleEvent+HandlerTest.applescript"; sourceTree = "<group>"; };
8B45A0280DA4696C001148C5 /* UnitTest - UnitTesting.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "UnitTest - UnitTesting.octest"; sourceTree = BUILT_PRODUCTS_DIR; };
8B45A1990DA46AAA001148C5 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = /System/Library/Frameworks/QuartzCore.framework; sourceTree = "<absolute>"; };
8B45A2670DA498A0001148C5 /* GTMUnitTestingUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMUnitTestingUtilities.h; sourceTree = "<group>"; };
@@ -256,8 +309,15 @@
8B7AD4980DABBB5800B84F4A /* GTMNSBezierPath+CGPathTest.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMNSBezierPath+CGPathTest.tiff"; sourceTree = "<group>"; };
8B7AD4990DABBB5800B84F4A /* GTMNSBezierPath+RoundRectTest.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMNSBezierPath+RoundRectTest.tiff"; sourceTree = "<group>"; };
8B7AD4AD0DABBFEE00B84F4A /* GTMUnitTestingBindingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMUnitTestingBindingTest.m; sourceTree = "<group>"; };
+ 8B7DCBE10DFF18720017E983 /* GTMDevLog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMDevLog.m; sourceTree = "<group>"; };
+ 8B7DCBEC0DFF1A4F0017E983 /* GTMUnitTestDevLog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMUnitTestDevLog.m; sourceTree = "<group>"; };
+ 8B7DCBF00DFF1A610017E983 /* GTMUnitTestDevLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMUnitTestDevLog.h; sourceTree = "<group>"; };
+ 8B7DCE180DFF39850017E983 /* GTMSenTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMSenTestCase.m; sourceTree = "<group>"; };
8BC046B80DAE8C4B00C2D1CA /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = /System/Library/Frameworks/ApplicationServices.framework; sourceTree = "<absolute>"; };
8BC04D140DB0061300C2D1CA /* RunMacOSUnitTests.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = RunMacOSUnitTests.sh; sourceTree = "<group>"; };
+ 8BE2836B0DED0F130035B3F8 /* GTMFourCharCode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMFourCharCode.m; sourceTree = "<group>"; };
+ 8BE2836C0DED0F130035B3F8 /* GTMFourCharCodeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMFourCharCodeTest.m; sourceTree = "<group>"; };
+ 8BE2836D0DED0F130035B3F8 /* GTMFourCharCode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMFourCharCode.h; sourceTree = "<group>"; };
8BE869720DBE89C100749827 /* GTMNSBezierPath+RoundRectTest.x86_64.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "GTMNSBezierPath+RoundRectTest.x86_64.tiff"; sourceTree = "<group>"; };
8BEEA90A0DA7446300894774 /* GTMUnitTestingImage.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = GTMUnitTestingImage.tiff; sourceTree = "<group>"; };
8BEEA90B0DA7446300894774 /* GTMUnitTestingWindow.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = GTMUnitTestingWindow.tiff; sourceTree = "<group>"; };
@@ -273,7 +333,7 @@
F428FF010D48E55E00382ED1 /* GTMNSBezierPath+CGPathTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSBezierPath+CGPathTest.m"; sourceTree = "<group>"; };
F42E08210D19987200D5DDE0 /* UnitTest - Foundation.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "UnitTest - Foundation.octest"; sourceTree = BUILT_PRODUCTS_DIR; };
F42E086D0D199A5B00D5DDE0 /* GoogleToolboxForMac.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GoogleToolboxForMac.framework; sourceTree = BUILT_PRODUCTS_DIR; };
- F42E086E0D199A5B00D5DDE0 /* GTM-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = "GTM-Info.plist"; sourceTree = "<group>"; };
+ F42E086E0D199A5B00D5DDE0 /* GTM-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GTM-Info.plist"; sourceTree = "<group>"; };
F42E089B0D199B1800D5DDE0 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; };
F42E09AD0D19A62F00D5DDE0 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = "<absolute>"; };
F43122190DD4E3B800F45252 /* GTMStackTrace.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = GTMStackTrace.c; sourceTree = "<group>"; };
@@ -288,7 +348,6 @@
F435E27E0DC7B0630069CDE8 /* GTMProgressMonitorInputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMProgressMonitorInputStream.m; sourceTree = "<group>"; };
F435E3930DC8CAAF0069CDE8 /* GTMHTTPFetcherTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMHTTPFetcherTest.m; sourceTree = "<group>"; };
F435E3B90DC8D1720069CDE8 /* GTMHTTPFetcherTestPage.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = GTMHTTPFetcherTestPage.html; sourceTree = "<group>"; };
- F435E3BA0DC8D1720069CDE8 /* GTMHTTPFetcherTestServer */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = GTMHTTPFetcherTestServer; sourceTree = "<group>"; };
F437F55A0D50BC0A00F5C3A4 /* GTMRegex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMRegex.h; sourceTree = "<group>"; };
F437F55B0D50BC0A00F5C3A4 /* GTMRegex.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMRegex.m; sourceTree = "<group>"; };
F437F55C0D50BC0A00F5C3A4 /* GTMRegexTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMRegexTest.m; sourceTree = "<group>"; };
@@ -308,6 +367,7 @@
F43E4E5F0D4E5EC90041161F /* GTMNSData+zlib.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSData+zlib.m"; sourceTree = "<group>"; };
F43E4E600D4E5EC90041161F /* GTMNSData+zlibTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSData+zlibTest.m"; sourceTree = "<group>"; };
F43E4F6C0D4E60C50041161F /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = /usr/lib/libz.dylib; sourceTree = "<absolute>"; };
+ F440EDB70DFECC4B0003E81F /* BuildingAndUsing.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = BuildingAndUsing.txt; sourceTree = "<group>"; };
F47A79850D746EE9002302AB /* GTMScriptRunner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMScriptRunner.h; sourceTree = "<group>"; };
F47A79860D746EE9002302AB /* GTMScriptRunner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMScriptRunner.m; sourceTree = "<group>"; };
F47A79870D746EE9002302AB /* GTMScriptRunnerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMScriptRunnerTest.m; sourceTree = "<group>"; };
@@ -327,7 +387,7 @@
F48FE2460D197F9A009257D2 /* SharedLibrary.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = SharedLibrary.xcconfig; sourceTree = "<group>"; };
F48FE2470D197F9A009257D2 /* StaticLibrary.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = StaticLibrary.xcconfig; sourceTree = "<group>"; };
F48FE2640D198C1E009257D2 /* UnitTest - AppKit.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "UnitTest - AppKit.octest"; sourceTree = BUILT_PRODUCTS_DIR; };
- F48FE26E0D198CAD009257D2 /* UnitTest-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = "UnitTest-Info.plist"; sourceTree = "<group>"; };
+ F48FE26E0D198CAD009257D2 /* UnitTest-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "UnitTest-Info.plist"; sourceTree = "<group>"; };
F48FE27C0D198D0E009257D2 /* GTMDelegatingTableColumn.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMDelegatingTableColumn.h; sourceTree = "<group>"; };
F48FE27D0D198D0E009257D2 /* GTMDelegatingTableColumn.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMDelegatingTableColumn.m; sourceTree = "<group>"; };
F48FE27E0D198D0E009257D2 /* GTMGeometryUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMGeometryUtils.h; sourceTree = "<group>"; };
@@ -347,6 +407,11 @@
F48FE29C0D198D36009257D2 /* GTMNSObject+UnitTesting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSObject+UnitTesting.m"; sourceTree = "<group>"; };
F48FE29F0D198D36009257D2 /* GTMSenTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMSenTestCase.h; sourceTree = "<group>"; };
F48FE2E10D198E4C009257D2 /* GTMSystemVersionTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMSystemVersionTest.m; sourceTree = "<group>"; };
+ F4BC1C860DDDD45D00108B7D /* GTMHTTPServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMHTTPServer.h; sourceTree = "<group>"; };
+ F4BC1C870DDDD45D00108B7D /* GTMHTTPServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMHTTPServer.m; sourceTree = "<group>"; };
+ F4BC1E8C0DE1FC4A00108B7D /* GTMHTTPServerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMHTTPServerTest.m; sourceTree = "<group>"; };
+ F4BC22CF0DE4C39000108B7D /* GTMTestHTTPServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMTestHTTPServer.h; sourceTree = "<group>"; };
+ F4BC22D00DE4C39000108B7D /* GTMTestHTTPServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMTestHTTPServer.m; sourceTree = "<group>"; };
F4C978090D5B79C7001C29A6 /* ReleaseNotes.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ReleaseNotes.txt; sourceTree = "<group>"; };
F4CA854E0DAFAAB600B4AB10 /* xcconfigs-readme.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "xcconfigs-readme.txt"; sourceTree = "<group>"; };
F4CA864A0DB3ACB200B4AB10 /* DebugLeopardOrLater.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = DebugLeopardOrLater.xcconfig; sourceTree = "<group>"; };
@@ -366,6 +431,7 @@
8B45A0B80DA46A2F001148C5 /* SenTestingKit.framework in Frameworks */,
8B45A03A0DA46A2A001148C5 /* Foundation.framework in Frameworks */,
8B45A19A0DA46AAA001148C5 /* QuartzCore.framework in Frameworks */,
+ 8B7DCBA50DFF0EFF0017E983 /* GoogleToolboxForMac.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -385,6 +451,7 @@
F42E08800D199AB500D5DDE0 /* GoogleToolboxForMac.framework in Frameworks */,
F42E089D0D199B1800D5DDE0 /* SenTestingKit.framework in Frameworks */,
8BC046B90DAE8C4B00C2D1CA /* ApplicationServices.framework in Frameworks */,
+ 8B33455E0DBF8844009FD32C /* Carbon.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -428,6 +495,7 @@
isa = PBXGroup;
children = (
F4C978090D5B79C7001C29A6 /* ReleaseNotes.txt */,
+ F440EDB70DFECC4B0003E81F /* BuildingAndUsing.txt */,
8B1A16050D90344B00CA1E8E /* GTMDefines.h */,
F48FE26F0D198CBA009257D2 /* AppKit */,
F48FE2720D198CCE009257D2 /* Foundation */,
@@ -502,7 +570,6 @@
isa = PBXGroup;
children = (
F435E3B90DC8D1720069CDE8 /* GTMHTTPFetcherTestPage.html */,
- F435E3BA0DC8D1720069CDE8 /* GTMHTTPFetcherTestServer */,
);
path = TestData;
sourceTree = "<group>";
@@ -557,6 +624,16 @@
F48FE2720D198CCE009257D2 /* Foundation */ = {
isa = PBXGroup;
children = (
+ 8B3344170DBF7A36009FD32C /* GTMNSAppleScript+HandlerTest.m */,
+ 8B3344180DBF7A36009FD32C /* GTMNSAppleScript+Handler.m */,
+ 8B3344190DBF7A36009FD32C /* GTMNSAppleScript+Handler.h */,
+ 8B3344200DBF7A36009FD32C /* GTMNSAppleEvent+HandlerTest.applescript */,
+ 8B33441A0DBF7A36009FD32C /* GTMNSAppleEventDescriptor+HandlerTest.m */,
+ 8B33441B0DBF7A36009FD32C /* GTMNSAppleEventDescriptor+Handler.m */,
+ 8B33441C0DBF7A36009FD32C /* GTMNSAppleEventDescriptor+Handler.h */,
+ 8B33441D0DBF7A36009FD32C /* GTMNSAppleEventDescriptor+FoundationTest.m */,
+ 8B33441E0DBF7A36009FD32C /* GTMNSAppleEventDescriptor+Foundation.m */,
+ 8B33441F0DBF7A36009FD32C /* GTMNSAppleEventDescriptor+Foundation.h */,
F41D25880DBD21A300774EEB /* GTMBase64.h */,
F41D25890DBD21A300774EEB /* GTMBase64.m */,
F41D258A0DBD21A300774EEB /* GTMBase64Test.m */,
@@ -570,6 +647,9 @@
F435E0870DC63F6D0069CDE8 /* GTMHTTPFetcher.h */,
F435E0880DC63F6D0069CDE8 /* GTMHTTPFetcher.m */,
F435E3930DC8CAAF0069CDE8 /* GTMHTTPFetcherTest.m */,
+ F4BC1C860DDDD45D00108B7D /* GTMHTTPServer.h */,
+ F4BC1C870DDDD45D00108B7D /* GTMHTTPServer.m */,
+ F4BC1E8C0DE1FC4A00108B7D /* GTMHTTPServerTest.m */,
33C374360DD8D44800E97817 /* GTMNSDictionary+URLArguments.h */,
33C374370DD8D44800E97817 /* GTMNSDictionary+URLArguments.m */,
33C3745E0DD8D85B00E97817 /* GTMNSDictionary+URLArgumentsTest.m */,
@@ -582,6 +662,9 @@
F48FE28E0D198D24009257D2 /* GTMNSString+HTML.h */,
F48FE28F0D198D24009257D2 /* GTMNSString+HTML.m */,
F48FE2900D198D24009257D2 /* GTMNSString+HTMLTest.m */,
+ 8BE2836B0DED0F130035B3F8 /* GTMFourCharCode.m */,
+ 8BE2836C0DED0F130035B3F8 /* GTMFourCharCodeTest.m */,
+ 8BE2836D0DED0F130035B3F8 /* GTMFourCharCode.h */,
33C372A40DD8A88500E97817 /* GTMNSString+URLArguments.h */,
33C372A50DD8A88500E97817 /* GTMNSString+URLArguments.m */,
33C372AE0DD8A8D700E97817 /* GTMNSString+URLArgumentsTest.m */,
@@ -617,6 +700,10 @@
F48FE2770D198CEA009257D2 /* UnitTesting */ = {
isa = PBXGroup;
children = (
+ 8B7DCBEC0DFF1A4F0017E983 /* GTMUnitTestDevLog.m */,
+ 8B7DCBF00DFF1A610017E983 /* GTMUnitTestDevLog.h */,
+ F4BC22CF0DE4C39000108B7D /* GTMTestHTTPServer.h */,
+ F4BC22D00DE4C39000108B7D /* GTMTestHTTPServer.m */,
8BC04D140DB0061300C2D1CA /* RunMacOSUnitTests.sh */,
8B55479A0DB3B7A50014CC1C /* GTMAppKit+UnitTesting.h */,
8B55479B0DB3B7A50014CC1C /* GTMAppKit+UnitTesting.m */,
@@ -629,6 +716,7 @@
8B45A2670DA498A0001148C5 /* GTMUnitTestingUtilities.h */,
8B45A2680DA498A0001148C5 /* GTMUnitTestingUtilities.m */,
F48FE29F0D198D36009257D2 /* GTMSenTestCase.h */,
+ 8B7DCE180DFF39850017E983 /* GTMSenTestCase.m */,
8B45A2E00DA51ABC001148C5 /* GTMUnitTestingTest.h */,
8B45A2E10DA51ABC001148C5 /* GTMUnitTestingTest.m */,
8B7AD4AD0DABBFEE00B84F4A /* GTMUnitTestingBindingTest.m */,
@@ -667,6 +755,7 @@
F4FF22760D9D47FB003880AC /* DebugUtils */ = {
isa = PBXGroup;
children = (
+ 8B7DCBE10DFF18720017E983 /* GTMDevLog.m */,
F4FF22770D9D4835003880AC /* GTMDebugSelectorValidation.h */,
8B6F31EF0DA347720052CA40 /* GTMMethodCheck.m */,
8B6F31F40DA3489B0052CA40 /* GTMMethodCheck.h */,
@@ -712,11 +801,47 @@
F431221D0DD4E3B800F45252 /* GTMStackTrace.h in Headers */,
33C372A60DD8A88500E97817 /* GTMNSString+URLArguments.h in Headers */,
33C374380DD8D44800E97817 /* GTMNSDictionary+URLArguments.h in Headers */,
+ F4BC1C880DDDD45D00108B7D /* GTMHTTPServer.h in Headers */,
+ 8BE281B00DEC7E930035B3F8 /* GTMNSAppleScript+Handler.h in Headers */,
+ 8BE281B10DEC7E930035B3F8 /* GTMNSAppleEventDescriptor+Handler.h in Headers */,
+ 8BE281B20DEC7E930035B3F8 /* GTMNSAppleEventDescriptor+Foundation.h in Headers */,
+ 8BE283730DED13AB0035B3F8 /* GTMFourCharCode.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
+/* Begin PBXLegacyTarget section */
+ F41A6EE00E02DB4F00788A6C /* Build GTM All SDKs */ = {
+ isa = PBXLegacyTarget;
+ buildArgumentsString = "GTM \"$(BuildAllSDKs)\" \"$(DEVELOPER_BIN_DIR)\" \"$(ACTION)\"";
+ buildConfigurationList = F41A6EED0E02DB6800788A6C /* Build configuration list for PBXLegacyTarget "Build GTM All SDKs" */;
+ buildPhases = (
+ );
+ buildToolPath = BuildScripts/BuildAllSDKs.sh;
+ buildWorkingDirectory = "$(SRCROOT)";
+ dependencies = (
+ );
+ name = "Build GTM All SDKs";
+ passBuildSettingsInEnvironment = 1;
+ productName = "Build GTM All SDKs";
+ };
+ F41A6EF80E02DCFC00788A6C /* All UnitTests All SDKs */ = {
+ isa = PBXLegacyTarget;
+ buildArgumentsString = "\"All UnitTests\" \"$(BuildAllSDKs)\" \"$(DEVELOPER_BIN_DIR)\" \"$(ACTION)\"";
+ buildConfigurationList = F41A6F070E02DD1500788A6C /* Build configuration list for PBXLegacyTarget "All UnitTests All SDKs" */;
+ buildPhases = (
+ );
+ buildToolPath = BuildScripts/BuildAllSDKs.sh;
+ buildWorkingDirectory = "$(SRCROOT)";
+ dependencies = (
+ );
+ name = "All UnitTests All SDKs";
+ passBuildSettingsInEnvironment = 1;
+ productName = "All UnitTests All SDKs";
+ };
+/* End PBXLegacyTarget section */
+
/* Begin PBXNativeTarget section */
8B45A0270DA4696C001148C5 /* UnitTest - UnitTesting */ = {
isa = PBXNativeTarget;
@@ -731,6 +856,7 @@
);
dependencies = (
8B45A2D40DA51A0E001148C5 /* PBXTargetDependency */,
+ 8B7DCBA40DFF0EFB0017E983 /* PBXTargetDependency */,
);
name = "UnitTest - UnitTesting";
productName = "UnitTest - UnitTesting";
@@ -758,6 +884,7 @@
isa = PBXNativeTarget;
buildConfigurationList = F42E081E0D19987200D5DDE0 /* Build configuration list for PBXNativeTarget "UnitTest - Foundation" */;
buildPhases = (
+ 8B3345CC0DBF8A95009FD32C /* AppleScript */,
F42E08140D19987200D5DDE0 /* Resources */,
F42E08160D19987200D5DDE0 /* Sources */,
F42E081C0D19987200D5DDE0 /* Frameworks */,
@@ -767,7 +894,6 @@
);
dependencies = (
F42E08790D199AA600D5DDE0 /* PBXTargetDependency */,
- F41D25520DB9068700774EEB /* PBXTargetDependency */,
);
name = "UnitTest - Foundation";
productName = "UnitTest - AppKit";
@@ -818,7 +944,7 @@
0867D690FE84028FC02AAC07 /* Project object */ = {
isa = PBXProject;
attributes = {
- BuildIndependentTargetsInParallel = NO;
+ BuildIndependentTargetsInParallel = YES;
};
buildConfigurationList = 1DEB918108733D990010E9CD /* Build configuration list for PBXProject "GTM" */;
compatibilityVersion = "Xcode 3.0";
@@ -834,6 +960,8 @@
F42E08110D19987200D5DDE0 /* UnitTest - Foundation */,
8B45A0270DA4696C001148C5 /* UnitTest - UnitTesting */,
8B45A2890DA49B99001148C5 /* UIUnitTestingHarness */,
+ F41A6EE00E02DB4F00788A6C /* Build GTM All SDKs */,
+ F41A6EF80E02DCFC00788A6C /* All UnitTests All SDKs */,
);
};
/* End PBXProject section */
@@ -866,7 +994,6 @@
buildActionMask = 2147483647;
files = (
F435E3BB0DC8D1980069CDE8 /* GTMHTTPFetcherTestPage.html in Resources */,
- F435E3BC0DC8D1980069CDE8 /* GTMHTTPFetcherTestServer in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -947,8 +1074,10 @@
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 */,
+ 8B7DCBC30DFF0F7F0017E983 /* GTMMethodCheck.m in Sources */,
+ 8B7DCBEF0DFF1A4F0017E983 /* GTMUnitTestDevLog.m in Sources */,
+ 8B7DCE1B0DFF39850017E983 /* GTMSenTestCase.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -958,6 +1087,9 @@
files = (
8B45A2AC0DA49C47001148C5 /* main.m in Sources */,
8B45A2D00DA51A01001148C5 /* GTMUnitTestingUtilities.m in Sources */,
+ 8B7DCBC40DFF0F800017E983 /* GTMMethodCheck.m in Sources */,
+ 8B7DCEF10E002C210017E983 /* GTMDevLog.m in Sources */,
+ 8B2C21B70E00885600B5ECB1 /* GTMObjC2Runtime.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -977,12 +1109,21 @@
8B6F32080DA34A1B0052CA40 /* GTMObjC2RuntimeTest.m in Sources */,
8B6F32160DA34C830052CA40 /* GTMMethodCheckTest.m in Sources */,
8BC045C20DAE899100C2D1CA /* GTMGeometryUtilsTest.m in Sources */,
- 8BC04CDE0DB004A000C2D1CA /* GTMMethodCheck.m in Sources */,
F41D258F0DBD21B900774EEB /* GTMBase64Test.m in Sources */,
- F435E3940DC8CAAF0069CDE8 /* GTMHTTPFetcherTest.m in Sources */,
F431221F0DD4E3C900F45252 /* GTMStackTraceTest.m in Sources */,
33C372B40DD8A93000E97817 /* GTMNSString+URLArgumentsTest.m in Sources */,
33C3745F0DD8D85B00E97817 /* GTMNSDictionary+URLArgumentsTest.m in Sources */,
+ F4BC1E8D0DE1FC4A00108B7D /* GTMHTTPServerTest.m in Sources */,
+ F4BC22D10DE4C39000108B7D /* GTMTestHTTPServer.m in Sources */,
+ 8B3344210DBF7A36009FD32C /* GTMNSAppleScript+HandlerTest.m in Sources */,
+ 8B3344230DBF7A36009FD32C /* GTMNSAppleEventDescriptor+HandlerTest.m in Sources */,
+ 8B3344250DBF7A36009FD32C /* GTMNSAppleEventDescriptor+FoundationTest.m in Sources */,
+ 8BE2836F0DED0F130035B3F8 /* GTMFourCharCodeTest.m in Sources */,
+ 8B7DCBC20DFF0F7F0017E983 /* GTMMethodCheck.m in Sources */,
+ 8B7DCBEE0DFF1A4F0017E983 /* GTMUnitTestDevLog.m in Sources */,
+ 8B7DCE1A0DFF39850017E983 /* GTMSenTestCase.m in Sources */,
+ 8B7DCE6D0DFF459C0017E983 /* GTMHTTPFetcherTest.m in Sources */,
+ 8B2C21B50E00883F00B5ECB1 /* GTMObjC2Runtime.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1008,13 +1149,19 @@
F41390900D75F63C00F72B31 /* GTMNSFileManager+Path.m in Sources */,
8B2A9B200D8270DA00599386 /* GTMNSWorkspace+ScreenSaver.m in Sources */,
8B45A21E0DA46E34001148C5 /* GTMObjC2Runtime.m in Sources */,
- 8BC04CD70DB003CD00C2D1CA /* GTMMethodCheck.m in Sources */,
F41D258C0DBD21A300774EEB /* GTMBase64.m in Sources */,
F435E08A0DC63F6D0069CDE8 /* GTMHTTPFetcher.m in Sources */,
F435E2800DC7B0630069CDE8 /* GTMProgressMonitorInputStream.m in Sources */,
F431221C0DD4E3B800F45252 /* GTMStackTrace.c in Sources */,
33C372A70DD8A88500E97817 /* GTMNSString+URLArguments.m in Sources */,
33C374390DD8D44800E97817 /* GTMNSDictionary+URLArguments.m in Sources */,
+ F4BC1C890DDDD45D00108B7D /* GTMHTTPServer.m in Sources */,
+ 8B7DCB9B0DFF0E850017E983 /* GTMFourCharCode.m in Sources */,
+ 8B7DCBBD0DFF0F5D0017E983 /* GTMMethodCheck.m in Sources */,
+ 8B7DCBD20DFF16070017E983 /* GTMNSAppleScript+Handler.m in Sources */,
+ 8B7DCBD30DFF16070017E983 /* GTMNSAppleEventDescriptor+Handler.m in Sources */,
+ 8B7DCBD40DFF16070017E983 /* GTMNSAppleEventDescriptor+Foundation.m in Sources */,
+ 8B7DCBE20DFF18720017E983 /* GTMDevLog.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1029,8 +1176,11 @@
F47F1C1B0D490BD200925B8F /* GTMNSBezierPath+ShadingTest.m in Sources */,
F43E447F0D4918BC0041161F /* GTMLinearRGBShadingTest.m in Sources */,
8B2A9B240D8270DA00599386 /* GTMNSWorkspace+ScreenSaverTest.m in Sources */,
- 8BC04CDD0DB004A000C2D1CA /* GTMMethodCheck.m in Sources */,
8B5547B90DB3BB220014CC1C /* GTMAppKit+UnitTesting.m in Sources */,
+ 8B7DCBC10DFF0F7F0017E983 /* GTMMethodCheck.m in Sources */,
+ 8B7DCBED0DFF1A4F0017E983 /* GTMUnitTestDevLog.m in Sources */,
+ 8B7DCE190DFF39850017E983 /* GTMSenTestCase.m in Sources */,
+ 8B2C21B60E00884000B5ECB1 /* GTMObjC2Runtime.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1047,15 +1197,15 @@
target = 8B45A2890DA49B99001148C5 /* UIUnitTestingHarness */;
targetProxy = 8B45A2D30DA51A0E001148C5 /* PBXContainerItemProxy */;
};
- F41D254F0DB9067C00774EEB /* PBXTargetDependency */ = {
+ 8B7DCBA40DFF0EFB0017E983 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
- target = 8B45A0270DA4696C001148C5 /* UnitTest - UnitTesting */;
- targetProxy = F41D254E0DB9067C00774EEB /* PBXContainerItemProxy */;
+ target = F42E086C0D199A5B00D5DDE0 /* GTM */;
+ targetProxy = 8B7DCBA30DFF0EFB0017E983 /* PBXContainerItemProxy */;
};
- F41D25520DB9068700774EEB /* PBXTargetDependency */ = {
+ F41D254F0DB9067C00774EEB /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 8B45A0270DA4696C001148C5 /* UnitTest - UnitTesting */;
- targetProxy = F41D25510DB9068700774EEB /* PBXContainerItemProxy */;
+ targetProxy = F41D254E0DB9067C00774EEB /* PBXContainerItemProxy */;
};
F42E08770D199A9B00D5DDE0 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
@@ -1175,6 +1325,90 @@
};
name = "TigerOrLater-Release";
};
+ F41A6EE10E02DB4F00788A6C /* TigerOrLater-Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "Build GTM All SDKs";
+ };
+ name = "TigerOrLater-Debug";
+ };
+ F41A6EE20E02DB4F00788A6C /* TigerOrLater-Debug-gcov */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "Build GTM All SDKs";
+ };
+ name = "TigerOrLater-Debug-gcov";
+ };
+ F41A6EE30E02DB4F00788A6C /* TigerOrLater-Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "Build GTM All SDKs";
+ };
+ name = "TigerOrLater-Release";
+ };
+ F41A6EE40E02DB4F00788A6C /* LeopardOrLater-Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "Build GTM All SDKs";
+ };
+ name = "LeopardOrLater-Debug";
+ };
+ F41A6EE50E02DB4F00788A6C /* LeopardOrLater-Debug-gcov */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "Build GTM All SDKs";
+ };
+ name = "LeopardOrLater-Debug-gcov";
+ };
+ F41A6EE60E02DB4F00788A6C /* LeopardOrLater-Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "Build GTM All SDKs";
+ };
+ name = "LeopardOrLater-Release";
+ };
+ F41A6EF90E02DCFC00788A6C /* TigerOrLater-Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "All UnitTests All SDKs";
+ };
+ name = "TigerOrLater-Debug";
+ };
+ F41A6EFA0E02DCFC00788A6C /* TigerOrLater-Debug-gcov */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "All UnitTests All SDKs";
+ };
+ name = "TigerOrLater-Debug-gcov";
+ };
+ F41A6EFB0E02DCFC00788A6C /* TigerOrLater-Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "All UnitTests All SDKs";
+ };
+ name = "TigerOrLater-Release";
+ };
+ F41A6EFC0E02DCFC00788A6C /* LeopardOrLater-Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "All UnitTests All SDKs";
+ };
+ name = "LeopardOrLater-Debug";
+ };
+ F41A6EFD0E02DCFC00788A6C /* LeopardOrLater-Debug-gcov */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "All UnitTests All SDKs";
+ };
+ name = "LeopardOrLater-Debug-gcov";
+ };
+ F41A6EFE0E02DCFC00788A6C /* LeopardOrLater-Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "All UnitTests All SDKs";
+ };
+ name = "LeopardOrLater-Release";
+ };
F42E081F0D19987200D5DDE0 /* TigerOrLater-Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = F48FE2420D197F9A009257D2 /* DebugUnittest.xcconfig */;
@@ -1183,6 +1417,7 @@
"$(value)",
"$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)",
);
+ GTM_NO_DEBUG_FRAMEWORKS = YES;
INFOPLIST_FILE = "UnitTest-Info.plist";
PRODUCT_NAME = "UnitTest - Foundation";
};
@@ -1196,6 +1431,7 @@
"$(value)",
"$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)",
);
+ GTM_NO_DEBUG_FRAMEWORKS = YES;
INFOPLIST_FILE = "UnitTest-Info.plist";
PRODUCT_NAME = "UnitTest - Foundation";
};
@@ -1298,6 +1534,7 @@
"$(value)",
"$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)",
);
+ GTM_NO_DEBUG_FRAMEWORKS = YES;
INFOPLIST_FILE = "UnitTest-Info.plist";
PRODUCT_NAME = "UnitTest - Foundation";
};
@@ -1379,6 +1616,7 @@
"$(value)",
"$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)",
);
+ GTM_NO_DEBUG_FRAMEWORKS = YES;
INFOPLIST_FILE = "UnitTest-Info.plist";
PRODUCT_NAME = "UnitTest - Foundation";
};
@@ -1459,6 +1697,7 @@
"$(value)",
"$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)",
);
+ GTM_NO_DEBUG_FRAMEWORKS = YES;
INFOPLIST_FILE = "UnitTest-Info.plist";
PRODUCT_NAME = "UnitTest - Foundation";
};
@@ -1536,6 +1775,7 @@
"$(value)",
"$(DEVELOPER_FRAMEWORKS_DIR_QUOTED)",
);
+ GTM_NO_DEBUG_FRAMEWORKS = YES;
INFOPLIST_FILE = "UnitTest-Info.plist";
PRODUCT_NAME = "UnitTest - Foundation";
};
@@ -1605,6 +1845,32 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = "TigerOrLater-Release";
};
+ F41A6EED0E02DB6800788A6C /* Build configuration list for PBXLegacyTarget "Build GTM All SDKs" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F41A6EE10E02DB4F00788A6C /* TigerOrLater-Debug */,
+ F41A6EE20E02DB4F00788A6C /* TigerOrLater-Debug-gcov */,
+ F41A6EE30E02DB4F00788A6C /* TigerOrLater-Release */,
+ F41A6EE40E02DB4F00788A6C /* LeopardOrLater-Debug */,
+ F41A6EE50E02DB4F00788A6C /* LeopardOrLater-Debug-gcov */,
+ F41A6EE60E02DB4F00788A6C /* LeopardOrLater-Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = "TigerOrLater-Release";
+ };
+ F41A6F070E02DD1500788A6C /* Build configuration list for PBXLegacyTarget "All UnitTests All SDKs" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F41A6EF90E02DCFC00788A6C /* TigerOrLater-Debug */,
+ F41A6EFA0E02DCFC00788A6C /* TigerOrLater-Debug-gcov */,
+ F41A6EFB0E02DCFC00788A6C /* TigerOrLater-Release */,
+ F41A6EFC0E02DCFC00788A6C /* LeopardOrLater-Debug */,
+ F41A6EFD0E02DCFC00788A6C /* LeopardOrLater-Debug-gcov */,
+ F41A6EFE0E02DCFC00788A6C /* LeopardOrLater-Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = "TigerOrLater-Release";
+ };
F42E081E0D19987200D5DDE0 /* Build configuration list for PBXNativeTarget "UnitTest - Foundation" */ = {
isa = XCConfigurationList;
buildConfigurations = (
diff --git a/GTMDefines.h b/GTMDefines.h
index 8bc1385..32ae642 100644
--- a/GTMDefines.h
+++ b/GTMDefines.h
@@ -64,6 +64,14 @@
#define _GTMDevLog(...) do { } while (0)
#endif
+#endif // _GTMDevLog
+
+// Declared here so that it can easily be used for logging tracking if
+// necessary. See GTMUnitTestDevLog.h for details.
+@class NSString;
+extern void _GTMUnittestDevLog(NSString *format, ...);
+
+#ifndef _GTMDevAssert
// 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)
@@ -81,7 +89,27 @@
#define _GTMDevAssert(condition, ...) do { } while (0)
#endif // !defined(NS_BLOCK_ASSERTIONS)
-#endif // _GTMDevLog
+#endif // _GTMDevAssert
+
+// _GTMCompileAssert
+// _GTMCompileAssert is an assert that is meant to fire at compile time if you
+// want to check things at compile instead of runtime. For example if you
+// want to check that a wchar is 4 bytes instead of 2 you would use
+// _GTMCompileAssert(sizeof(wchar_t) == 4, wchar_t_is_4_bytes_on_OS_X)
+// Note that the second "arg" is not in quotes, and must be a valid processor
+// symbol in it's own right (no spaces, punctuation etc).
+
+// Wrapping this in an #ifndef allows external groups to define their own
+// compile time assert scheme.
+#ifndef _GTMCompileAssert
+// We got this technique from here:
+// http://unixjunkie.blogspot.com/2007/10/better-compile-time-asserts_29.html
+
+#define _GTMCompileAssertSymbolInner(line, msg) _GTMCOMPILEASSERT ## line ## __ ## msg
+#define _GTMCompileAssertSymbol(line, msg) _GTMCompileAssertSymbolInner(line, msg)
+#define _GTMCompileAssert(test, msg) \
+ typedef char _GTMCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ]
+#endif // _GTMCompileAssert
// ============================================================================
diff --git a/GTM_Prefix.pch b/GTM_Prefix.pch
index c745642..6740ffb 100644
--- a/GTM_Prefix.pch
+++ b/GTM_Prefix.pch
@@ -14,6 +14,23 @@
// the License.
//
+// We don't require a prefix to use any GTM code, so this prefix is just to
+// speed up the basic compiles of *all* of the GTM projects/targets.
+
+// We don't want to drag GTMDefines into the prefix so we make sure each file
+// that directly needs it includes it, so we just use the iphone test directly
+// instead of our GTM_IPHONE_SDK symbol.
#ifdef __OBJC__
+ #include <TargetConditionals.h>
+ #if TARGET_OS_IPHONE // iPhone SDK
+ #import <Foundation/Foundation.h>
+ #import <UIKit/UIKit.h>
+ #else
#import <Cocoa/Cocoa.h>
+ #endif
+ // This turns on unit test logging so that we can track unittests if we are
+ // doing them. See GTMUnitTestDevLog.h for details.
+ #define _GTMDevLog _GTMUnittestDevLog
#endif
+
+
diff --git a/GTMiPhone-Info.plist b/GTMiPhone-Info.plist
index cd3c096..6898224 100644
--- a/GTMiPhone-Info.plist
+++ b/GTMiPhone-Info.plist
@@ -8,10 +8,8 @@
<string>${PRODUCT_NAME}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
- <key>CFBundleIconFile</key>
- <string></string>
<key>CFBundleIdentifier</key>
- <string>com.yourcompany.${PRODUCT_NAME:identifier}</string>
+ <string>com.google.GTMiPhone</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
diff --git a/GTMiPhone.xcodeproj/project.pbxproj b/GTMiPhone.xcodeproj/project.pbxproj
index 0baf1b8..b0f39ed 100644
--- a/GTMiPhone.xcodeproj/project.pbxproj
+++ b/GTMiPhone.xcodeproj/project.pbxproj
@@ -24,9 +24,12 @@
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 */; };
+ 67A7820C0E00927400EBF506 /* GTMIPhoneUnitTestDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 67A7820B0E00927400EBF506 /* GTMIPhoneUnitTestDelegate.m */; };
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 */; };
+ 8B7DCEAA0DFF4C760017E983 /* GTMDevLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B7DCEA90DFF4C760017E983 /* GTMDevLog.m */; };
+ 8B7DCEAD0DFF4CA60017E983 /* GTMUnitTestDevLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B7DCEAC0DFF4CA60017E983 /* GTMUnitTestDevLog.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 */; };
@@ -64,7 +67,7 @@
isa = PBXContainerItemProxy;
containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */;
proxyType = 1;
- remoteGlobalIDString = 1D6058900D05DD3D006BFB54 /* GTMiPhoneUnitTesting */;
+ remoteGlobalIDString = 1D6058900D05DD3D006BFB54;
remoteInfo = GTMiPhoneUnitTesting;
};
/* End PBXContainerItemProxy section */
@@ -74,12 +77,17 @@
1D3623EB0D0F72F000981E51 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
1D6058910D05DD3D006BFB54 /* GTMiPhoneTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GTMiPhoneTest.app; sourceTree = BUILT_PRODUCTS_DIR; };
1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
- 32CA4F630368D1EE00C91783 /* GTMiPhone_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMiPhone_Prefix.pch; sourceTree = "<group>"; };
+ 32CA4F630368D1EE00C91783 /* GTM_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTM_Prefix.pch; sourceTree = "<group>"; };
+ 67A7820A0E00927400EBF506 /* GTMIPhoneUnitTestDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMIPhoneUnitTestDelegate.h; sourceTree = "<group>"; };
+ 67A7820B0E00927400EBF506 /* GTMIPhoneUnitTestDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMIPhoneUnitTestDelegate.m; sourceTree = "<group>"; };
8B308AF40DAD070C00183556 /* RunIPhoneUnitTest.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = RunIPhoneUnitTest.sh; path = UnitTesting/RunIPhoneUnitTest.sh; sourceTree = "<group>"; };
8B308BCD0DAD0B8400183556 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
8B5547C70DB3BBF20014CC1C /* GTMUIKit+UnitTesting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMUIKit+UnitTesting.m"; sourceTree = "<group>"; };
8B5547C80DB3BBF20014CC1C /* GTMUIKit+UnitTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMUIKit+UnitTesting.h"; sourceTree = "<group>"; };
8B5547C90DB3BBF20014CC1C /* GTMUIKit+UnitTestingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMUIKit+UnitTestingTest.m"; sourceTree = "<group>"; };
+ 8B7DCEA90DFF4C760017E983 /* GTMDevLog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMDevLog.m; sourceTree = "<group>"; };
+ 8B7DCEAB0DFF4CA60017E983 /* GTMUnitTestDevLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMUnitTestDevLog.h; sourceTree = "<group>"; };
+ 8B7DCEAC0DFF4CA60017E983 /* GTMUnitTestDevLog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMUnitTestDevLog.m; sourceTree = "<group>"; };
8BC047750DAE926E00C2D1CA /* GTMDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMDefines.h; sourceTree = "<group>"; };
8BC047770DAE928A00C2D1CA /* GTMCalculatedRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMCalculatedRange.h; sourceTree = "<group>"; };
8BC047780DAE928A00C2D1CA /* GTMCalculatedRange.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMCalculatedRange.m; sourceTree = "<group>"; };
@@ -168,7 +176,7 @@
8BC0479A0DAE928A00C2D1CA /* DebugUtils */,
8BC0479F0DAE928A00C2D1CA /* UnitTesting */,
8BC049840DAEC59100C2D1CA /* XcodeConfig */,
- 32CA4F630368D1EE00C91783 /* GTMiPhone_Prefix.pch */,
+ 32CA4F630368D1EE00C91783 /* GTM_Prefix.pch */,
8B308AF40DAD070C00183556 /* RunIPhoneUnitTest.sh */,
29B97323FDCFA39411CA2CEA /* Frameworks */,
19C28FACFE9D520D11CA2CBB /* Products */,
@@ -232,6 +240,7 @@
isa = PBXGroup;
children = (
8BC0479B0DAE928A00C2D1CA /* GTMDebugSelectorValidation.h */,
+ 8B7DCEA90DFF4C760017E983 /* GTMDevLog.m */,
8BC0479C0DAE928A00C2D1CA /* GTMMethodCheck.h */,
8BC0479D0DAE928A00C2D1CA /* GTMMethodCheck.m */,
8BC0479E0DAE928A00C2D1CA /* GTMMethodCheckTest.m */,
@@ -247,12 +256,16 @@
8B5547C70DB3BBF20014CC1C /* GTMUIKit+UnitTesting.m */,
8B5547C80DB3BBF20014CC1C /* GTMUIKit+UnitTesting.h */,
8B5547C90DB3BBF20014CC1C /* GTMUIKit+UnitTestingTest.m */,
+ 67A7820A0E00927400EBF506 /* GTMIPhoneUnitTestDelegate.h */,
+ 67A7820B0E00927400EBF506 /* GTMIPhoneUnitTestDelegate.m */,
8BC047DD0DAE928A00C2D1CA /* GTMIPhoneUnitTestMain.m */,
8BC047EC0DAE928A00C2D1CA /* GTMNSObject+UnitTesting.h */,
8BC047ED0DAE928A00C2D1CA /* GTMNSObject+UnitTesting.m */,
8BC047F60DAE928A00C2D1CA /* GTMSenTestCase.h */,
8BC047F70DAE928A00C2D1CA /* GTMSenTestCase.m */,
8BC048010DAE928A00C2D1CA /* GTMUIViewUnitTestingTest.png */,
+ 8B7DCEAB0DFF4CA60017E983 /* GTMUnitTestDevLog.h */,
+ 8B7DCEAC0DFF4CA60017E983 /* GTMUnitTestDevLog.m */,
8BC0480E0DAE928A00C2D1CA /* RunIPhoneUnitTest.sh */,
F435E49F0DC8F5290069CDE8 /* TestData */,
);
@@ -394,6 +407,9 @@
F439ADEC0DBD3C0000BE9B91 /* GTMBase64Test.m in Sources */,
F439ADF00DBD3C4000BE9B91 /* GTMGeometryUtils.m in Sources */,
F439ADF10DBD3C4000BE9B91 /* GTMGeometryUtilsTest.m in Sources */,
+ 8B7DCEAA0DFF4C760017E983 /* GTMDevLog.m in Sources */,
+ 8B7DCEAD0DFF4CA60017E983 /* GTMUnitTestDevLog.m in Sources */,
+ 67A7820C0E00927400EBF506 /* GTMIPhoneUnitTestDelegate.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -429,7 +445,7 @@
baseConfigurationReference = 8BC049850DAEC59100C2D1CA /* DebugiPhone.xcconfig */;
buildSettings = {
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
- GCC_PREFIX_HEADER = GTMiPhone_Prefix.pch;
+ GCC_PREFIX_HEADER = GTM_Prefix.pch;
};
name = "Debug-gcov";
};
@@ -446,7 +462,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 8BC049850DAEC59100C2D1CA /* DebugiPhone.xcconfig */;
buildSettings = {
- GCC_PREFIX_HEADER = GTMiPhone_Prefix.pch;
+ GCC_PREFIX_HEADER = GTM_Prefix.pch;
};
name = Debug;
};
@@ -454,7 +470,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 8BC049890DAEC59100C2D1CA /* ReleaseiPhone.xcconfig */;
buildSettings = {
- GCC_PREFIX_HEADER = GTMiPhone_Prefix.pch;
+ GCC_PREFIX_HEADER = GTM_Prefix.pch;
};
name = Release;
};
diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt
index e6e565d..942f12d 100644
--- a/ReleaseNotes.txt
+++ b/ReleaseNotes.txt
@@ -101,11 +101,30 @@ Changes since 1.0.0
- GTMDelegatingTableColumn get an overhaul to match the 10.5 sdk so it's closer
to a dropin for previous sdks.
+- Added a lot of functionality to NSAppleEventDescriptor and NSAppleScript
+ allowing you to easily call labeled and positional handlers in an AppleScript,
+ get/set properties and get NSAppleEventDescriptors for most basic datatypes.
+
+- Added GTMFourCharCode for wrapping FourCharCodes in an ObjC object. Mainly for
+ use by the NSAppleEventDescriptor code, and also useful for storing them
+ in ObjC collection classes.
+
- Added GTMStackTrace.
- Added NSString+URLArguments and NSDictionary+URLArguments
+- Added GTMHTTPServer as a simple server but mainly for use in unittesting.
+
+- Added _GTMCompileAssert for doing compile time assertions to GTMDefines.h
+- Added GTMUnitTestDevLog and GTMTestCase for logging and tracking logs while
+ running unittests to verify what is being logged is what you expect. All
+ unittests should now inherit from GTMTestCase instead of SenTestCase to take
+ advantage of the new log tracking. See GTMUnitTestDevLog.h for details.
+
+- Extracted GTMIPhoneUnitTestDelegate from GTMIPhoneUnitTestMain.m to its own
+ file. Tests can now be run from another application.
+
Release 1.0.0
14-January-2008
diff --git a/UnitTesting/GTMAppKit+UnitTesting.h b/UnitTesting/GTMAppKit+UnitTesting.h
index 5db9ebb..016da7e 100644
--- a/UnitTesting/GTMAppKit+UnitTesting.h
+++ b/UnitTesting/GTMAppKit+UnitTesting.h
@@ -52,7 +52,7 @@
@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
+// See the description of the -gtm_pathForImageNamed method
// to understand how |a3| is found and written out.
// See the description of the GTMUnitTestView for a better idea
// how the view works.
@@ -70,15 +70,14 @@
// ...: A variable number of arguments to the format string. Can be absent.
//
-
-#define GTMAssertDrawingEqualToFile(a1, a2, a3, a4, description, ...) \
+#define GTMAssertDrawingEqualToImageNamed(a1, a2, a3, a4, description, ...) \
do { \
- id<GTMUnitTestViewDrawer> a1Object = (a1); \
+ id<GTMUnitTestViewDrawer> a1Drawer = (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]; \
+ GTMUnitTestView *view = [[[GTMUnitTestView alloc] initWithFrame:frame drawer:a1Drawer contextInfo:a4ContextInfo] autorelease]; \
GTMAssertObjectImageEqualToImageNamed(view, a3String, STComposeString(description, ##__VA_ARGS__)); \
} while(0)
diff --git a/UnitTesting/GTMIPhoneUnitTestDelegate.h b/UnitTesting/GTMIPhoneUnitTestDelegate.h
new file mode 100644
index 0000000..21ba84c
--- /dev/null
+++ b/UnitTesting/GTMIPhoneUnitTestDelegate.h
@@ -0,0 +1,29 @@
+//
+// GTMIPhoneUnitTestDelegate.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.
+//
+
+// Application delegate that runs all test methods in registered classes
+// extending SenTestCase. The application is terminated afterwards.
+// You can also run the tests directly from your application by invoking
+// gtm_runTests and clean up, restore data, etc. before the application
+// terminates.
+@interface GTMIPhoneUnitTestDelegate : NSObject
+// Runs through all the registered classes and runs test methods on any
+// that are subclasses of SenTestCase. Prints results and run time to
+// the default output.
+- (void)runTests;
+@end
diff --git a/UnitTesting/GTMIPhoneUnitTestDelegate.m b/UnitTesting/GTMIPhoneUnitTestDelegate.m
new file mode 100644
index 0000000..28d2fe0
--- /dev/null
+++ b/UnitTesting/GTMIPhoneUnitTestDelegate.m
@@ -0,0 +1,160 @@
+//
+// GTMIPhoneUnitTestDelegate.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 "GTMIPhoneUnitTestDelegate.h"
+
+#import "GTMDefines.h"
+#if !GTM_IPHONE_SDK
+#error GTMIPhoneUnitTestDelegate for iPhone only
+#endif
+#import <objc/runtime.h>
+#import <stdio.h>
+#import <UIKit/UIKit.h>
+#import "GTMSenTestCase.h"
+
+// Used for sorting methods below
+static int MethodSort(const void *a, const void *b) {
+ const char *nameA = sel_getName(method_getName(*(Method*)a));
+ const char *nameB = sel_getName(method_getName(*(Method*)b));
+ return strcmp(nameA, nameB);
+}
+
+@interface UIApplication (iPhoneUnitTestAdditions)
+// "Private" method that we need
+- (void)terminate;
+@end
+
+@implementation GTMIPhoneUnitTestDelegate
+
+// Return YES if class is subclass (1 or more generations) of SenTestCase
+- (BOOL)isTestFixture:(Class)aClass {
+ BOOL iscase = NO;
+ Class testCaseClass = [SenTestCase class];
+ Class superclass;
+ for (superclass = aClass;
+ !iscase && superclass;
+ superclass = class_getSuperclass(superclass)) {
+ iscase = superclass == testCaseClass ? YES : NO;
+ }
+ return iscase;
+}
+
+// Run through all the registered classes and run test methods on any
+// that are subclasses of SenTestCase. Terminate the application upon
+// test completion.
+- (void)applicationDidFinishLaunching:(UIApplication *)application {
+ [self runTests];
+ // Using private call to end our tests
+ [[UIApplication sharedApplication] terminate];
+}
+
+// Run through all the registered classes and run test methods on any
+// that are subclasses of SenTestCase. Print results and run time to
+// the default output.
+- (void)runTests {
+ 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];
+ fputs([suiteStartString UTF8String], stderr);
+ fflush(stderr);
+ for (int 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;
+ fputs([fixtureStartString UTF8String], stderr);
+ 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 (size_t 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;
+ BOOL failed = NO;
+ NSDate *caseStartDate = [NSDate date];
+ @try {
+ [testcase performTest:sel];
+ } @catch (NSException *exception) {
+ failed = YES;
+ }
+ if (failed) {
+ fixtureFailures += 1;
+ } else {
+ fixtureSuccesses += 1;
+ }
+ NSTimeInterval caseEndTime = [[NSDate date] timeIntervalSinceDate:caseStartDate];
+ NSString *caseEndString = [NSString stringWithFormat:@"Test Case '-[%@ %s]' %s (%0.3f seconds).\n",
+ fixtureName, name,
+ failed ? "failed" : "passed", caseEndTime];
+ fputs([caseEndString UTF8String], stderr);
+ 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 (%0.3f) seconds\n",
+ fixtureName, fixtureEndDate, fixtureTotal,
+ fixtureFailures, fixtureFailures,
+ fixtureEndTime, fixtureEndTime];
+
+ fputs([fixtureEndString UTF8String], stderr);
+ 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 (%0.3f) seconds\n",
+ suiteName, suiteEndDate, suiteTotal,
+ suiteFailures, suiteFailures,
+ suiteEndTime, suiteEndTime];
+ fputs([suiteEndString UTF8String], stderr);
+ fflush(stderr);
+}
+
+@end
diff --git a/UnitTesting/GTMIPhoneUnitTestMain.m b/UnitTesting/GTMIPhoneUnitTestMain.m
index 981c48b..2a0a70d 100644
--- a/UnitTesting/GTMIPhoneUnitTestMain.m
+++ b/UnitTesting/GTMIPhoneUnitTestMain.m
@@ -20,166 +20,11 @@
#if !GTM_IPHONE_SDK
#error GTMIPhoneUnitTestMain for iPhone only
#endif
-#import <objc/runtime.h>
-#import <stdio.h>
#import <UIKit/UIKit.h>
-#import "GTMSenTestCase.h"
-
-// Used for sorting methods below
-static int MethodSort(const void *a, const void *b) {
- const char *nameA = sel_getName(method_getName(*(Method*)a));
- const char *nameB = sel_getName(method_getName(*(Method*)b));
- return strcmp(nameA, nameB);
-}
-
-@interface UIApplication (iPhoneUnitTestAdditions)
-// "Private" method that we need
-- (void)terminate;
-@end
-
-@interface GTMIPhoneUnitTestDelegate : NSObject
-@end
-
-@implementation GTMIPhoneUnitTestDelegate
-
-// Return YES if class is subclass (1 or more generations) of SenTestCase
-- (BOOL)isTestFixture:(Class)aClass {
- BOOL iscase = NO;
- Class testCaseClass = [SenTestCase class];
- Class superclass;
- for (superclass = aClass;
- !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];
- @try {
- [testcase performSelector:sel];
- } @catch (NSException *exception) {
- failed = YES;
- [self printError:[exception reason]];
- }
- [testcase tearDown];
- } @catch (NSException *exception) {
- failed = YES;
- [self printError:[exception reason]];
- }
- [pool release];
- } @catch (NSException *exception) {
- failed = YES;
- [self printError:[exception reason]];
- }
- if (failed) {
- fixtureFailures += 1;
- } else {
- fixtureSuccesses += 1;
- }
- 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
+// Creates an application that runs all tests from classes extending
+// SenTestCase, outputs results and test run time, and terminates right
+// afterwards.
int main(int argc, char *argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, @"GTMIPhoneUnitTestDelegate");
diff --git a/UnitTesting/GTMNSObject+UnitTesting.h b/UnitTesting/GTMNSObject+UnitTesting.h
index 7253fd4..68fab04 100644
--- a/UnitTesting/GTMNSObject+UnitTesting.h
+++ b/UnitTesting/GTMNSObject+UnitTesting.h
@@ -203,7 +203,7 @@ do { \
// 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;
+- (CFStringRef)gtm_imageUTI;
// Return the extension to be used for saving unittest images
//
diff --git a/UnitTesting/GTMNSObject+UnitTesting.m b/UnitTesting/GTMNSObject+UnitTesting.m
index 34aeb58..dd36cb5 100644
--- a/UnitTesting/GTMNSObject+UnitTesting.m
+++ b/UnitTesting/GTMNSObject+UnitTesting.m
@@ -353,7 +353,7 @@ static inline BOOL almostEqual(unsigned char a, unsigned char b) {
length:(unsigned)lenv
forKey:(NSString *)key {
[self checkForKey:key];
- [dictionary_ setObject:[NSData dataWithBytes:(uint8_t*)bytesp
+ [dictionary_ setObject:[NSData dataWithBytes:bytesp
length:lenv]
forKey:key];
}
@@ -502,7 +502,7 @@ static NSString *gGTMUnitTestSaveToDirectory = nil;
#endif // GTM_IPHONE_SDK
extensions[1] = @"";
- int i,j;
+ size_t i, j;
// Note that we are searching for the most exact match first.
for (i = 0;
!thePath && i < sizeof(extensions) / sizeof(*extensions);
@@ -525,9 +525,9 @@ static NSString *gGTMUnitTestSaveToDirectory = nil;
- (NSString *)gtm_saveToPathForFileNamed:(NSString*)name
extension:(NSString*)extension {
- char const *system;
+ char const *systemArchitecture;
#if GTM_IPHONE_SDK
- system = "iPhone";
+ systemArchitecture = "iPhone";
#else
// In reading arch(3) you'd thing this would work:
//
@@ -540,15 +540,15 @@ static NSString *gGTMUnitTestSaveToDirectory = nil;
// but on 64bit it returns the same things as on 32bit, so...
#if __POWERPC__
#if __LP64__
- system = "ppc64";
+ systemArchitecture = "ppc64";
#else // !__LP64__
- system = "ppc";
+ systemArchitecture = "ppc";
#endif // __LP64__
#else // !__POWERPC__
#if __LP64__
- system = "x86_64";
+ systemArchitecture = "x86_64";
#else // !__LP64__
- system = "i386";
+ systemArchitecture = "i386";
#endif // __LP64__
#endif // !__POWERPC__
@@ -557,7 +557,7 @@ static NSString *gGTMUnitTestSaveToDirectory = nil;
[GTMSystemVersion getMajor:&major minor:&minor bugFix:&bugFix];
NSString *fullName = [NSString stringWithFormat:@"%@.%s.%d.%d.%d",
- name, system, major, minor, bugFix];
+ name, systemArchitecture, major, minor, bugFix];
NSString *basePath = [[self class] gtm_getUnitTestSaveToDirectory];
return [[basePath stringByAppendingPathComponent:fullName]
@@ -622,13 +622,13 @@ static NSString *gGTMUnitTestSaveToDirectory = nil;
return YES;
}
-- (NSString *)gtm_imageUTI {
+- (CFStringRef)gtm_imageUTI {
#if GTM_IPHONE_SDK
- return (NSString*)kUTTypePNG;
+ return kUTTypePNG;
#else
// Currently can't use PNG on Leopard. (10.5.2)
// Radar:5844618 PNG importer/exporter in ImageIO is lossy
- return (NSString*)kUTTypeTIFF;
+ return kUTTypeTIFF;
#endif
}
@@ -637,18 +637,18 @@ static NSString *gGTMUnitTestSaveToDirectory = nil;
// Returns
// An extension (e.g. "png")
- (NSString*)gtm_imageExtension {
- NSString *uti = [self gtm_imageUTI];
+ CFStringRef uti = [self gtm_imageUTI];
#if GTM_IPHONE_SDK
- if ([uti isEqualToString:(NSString*)kUTTypePNG]) {
+ if (CFEqual(uti, kUTTypePNG)) {
return @"png";
- } else if ([uti isEqualToString:(NSString*)kUTTypeJPEG]) {
+ } else if (CFEqual(uti, kUTTypeJPEG)) {
return @"jpg";
} else {
_GTMDevAssert(NO, @"Illegal UTI for iPhone");
}
return nil;
#else
- CFStringRef extension = UTTypeCopyPreferredTagWithClass((CFStringRef)uti,
+ CFStringRef extension = UTTypeCopyPreferredTagWithClass(uti,
kUTTagClassFilenameExtension);
_GTMDevAssert(extension, @"No extension for uti: %@", uti);
@@ -666,10 +666,10 @@ static NSString *gGTMUnitTestSaveToDirectory = nil;
#if GTM_IPHONE_SDK
// iPhone support
UIImage *uiImage = [UIImage imageWithCGImage:image];
- NSString *uti = [self gtm_imageUTI];
- if ([uti isEqualToString:(NSString*)kUTTypePNG]) {
+ CFStringRef uti = [self gtm_imageUTI];
+ if (CFEqual(uti, kUTTypePNG)) {
data = UIImagePNGRepresentation(uiImage);
- } else if ([uti isEqualToString:(NSString*)kUTTypeJPEG]) {
+ } else if (CFEqual(uti, kUTTypeJPEG)) {
data = UIImageJPEGRepresentation(uiImage, 1.0f);
} else {
_GTMDevAssert(NO, @"Illegal UTI for iPhone");
@@ -677,17 +677,17 @@ static NSString *gGTMUnitTestSaveToDirectory = nil;
#else
data = [NSMutableData data];
CGImageDestinationRef dest = CGImageDestinationCreateWithData((CFMutableDataRef)data,
- (CFStringRef)[self gtm_imageUTI],
+ [self gtm_imageUTI],
1,
NULL);
// LZW Compression for TIFF
NSDictionary *tiffDict = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:NSTIFFCompressionLZW]
- forKey:(NSString*)kCGImagePropertyTIFFCompression];
+ forKey:(const NSString*)kCGImagePropertyTIFFCompression];
NSDictionary *destProps = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat:1.0f],
- (NSString*)kCGImageDestinationLossyCompressionQuality,
+ (const NSString*)kCGImageDestinationLossyCompressionQuality,
tiffDict,
- (NSString*)kCGImagePropertyTIFFDictionary,
+ (const NSString*)kCGImagePropertyTIFFDictionary,
nil];
CGImageDestinationAddImage(dest, image, (CFDictionaryRef)destProps);
CGImageDestinationFinalize(dest);
diff --git a/UnitTesting/GTMSenTestCase.h b/UnitTesting/GTMSenTestCase.h
index 758f6d4..5f7856c 100644
--- a/UnitTesting/GTMSenTestCase.h
+++ b/UnitTesting/GTMSenTestCase.h
@@ -130,7 +130,7 @@ do { \
#define STAssertNotNULL(a1, description, ...) \
do { \
@try {\
- char* a1value = (char*)(a1); \
+ const void* a1value = (a1); \
if (a1value == NULL) { \
NSString *_expression = [NSString stringWithFormat:@"(%s) != NULL", #a1]; \
if (description) { \
@@ -159,7 +159,7 @@ do { \
#define STAssertNULL(a1, description, ...) \
do { \
@try {\
- char* a1value = (char*)(a1); \
+ const void* a1value = (a1); \
if (a1value != NULL) { \
NSString *_expression = [NSString stringWithFormat:@"(%s) == NULL", #a1]; \
if (description) { \
@@ -983,12 +983,22 @@ do { \
- (void)failWithException:(NSException*)exception;
@end
-@interface SenTestCase : NSObject
-- (void) setUp;
-- (void) tearDown;
+@interface SenTestCase : NSObject {
+ SEL currentSelector_;
+}
+
+- (void)setUp;
+- (void)invokeTest;
+- (void)tearDown;
+- (void)performTest:(SEL)sel;
@end
CF_EXPORT NSString * const SenTestFailureException;
#endif // GTM_IPHONE_SDK
+// All unittest cases in GTM should inherit from GTMTestCase. It makes sure
+// to set up our logging system correctly to verify logging calls.
+// See GTMUnitTestDevLog.h for details
+@interface GTMTestCase : SenTestCase
+@end
diff --git a/UnitTesting/GTMSenTestCase.m b/UnitTesting/GTMSenTestCase.m
index 9b6cf17..f8d7ff3 100644
--- a/UnitTesting/GTMSenTestCase.m
+++ b/UnitTesting/GTMSenTestCase.m
@@ -129,12 +129,83 @@ NSString *STComposeString(NSString *formatString, ...) {
NSString * const SenTestFailureException = @"SenTestFailureException";
+@interface SenTestCase (SenTestCasePrivate)
+// our method of logging errors
+- (void)printError:(NSString *)error;
+@end
+
@implementation SenTestCase
-- (void) setUp {
+- (void)setUp {
}
-- (void) tearDown {
+- (void)performTest:(SEL)sel {
+ currentSelector_ = sel;
+ @try {
+ [self invokeTest];
+ } @catch (NSException *exception) {
+ [self printError:[exception reason]];
+ [exception raise];
+ }
+}
+
+- (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);
+}
+
+- (void)invokeTest {
+ NSException *e = nil;
+ @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 {
+ [self setUp];
+ @try {
+ [self performSelector:currentSelector_];
+ } @catch (NSException *exception) {
+ e = [exception retain];
+ [self printError:[exception reason]];
+ }
+ [self tearDown];
+ } @catch (NSException *exception) {
+ e = [exception retain];
+ [self printError:[exception reason]];
+ }
+ [pool release];
+ } @catch (NSException *exception) {
+ e = [exception retain];
+ [self printError:[exception reason]];
+ }
+ if (e) {
+ [e autorelease];
+ [e raise];
+ }
+}
+
+- (void)tearDown {
}
@end
#endif
+
+@implementation GTMTestCase : SenTestCase
+- (void) invokeTest {
+ Class devLogClass = NSClassFromString(@"GTMUnitTestDevLog");
+ if (devLogClass) {
+ [devLogClass performSelector:@selector(enableTracking)];
+ [devLogClass performSelector:@selector(verifyNoMoreLogsExpected)];
+
+ }
+ [super invokeTest];
+ if (devLogClass) {
+ [devLogClass performSelector:@selector(verifyNoMoreLogsExpected)];
+ [devLogClass performSelector:@selector(disableTracking)];
+ }
+}
+@end
diff --git a/UnitTesting/GTMTestHTTPServer.h b/UnitTesting/GTMTestHTTPServer.h
new file mode 100644
index 0000000..0128718
--- /dev/null
+++ b/UnitTesting/GTMTestHTTPServer.h
@@ -0,0 +1,39 @@
+//
+// GTMTestHTTPServer.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 <Foundation/Foundation.h>
+
+@class GTMHTTPServer;
+
+// This is a HTTP Server that can respond to certain requests that look like
+// Google service logins. It takes extra url arguments to tell it what to
+// return for testing the code using it. See GTMHTTPFetcherTest for an example
+// of its usage.
+@interface GTMTestHTTPServer : NSObject {
+ NSString *docRoot_;
+ GTMHTTPServer *server_;
+}
+
+// Any url that isn't a specific server request (login, etc.), will be fetched
+// off |docRoot| (to allow canned repsonses).
+- (id)initWithDocRoot:(NSString *)docRoot;
+
+// fetch the port the server is running on
+- (uint16_t)port;
+
+@end
diff --git a/UnitTesting/GTMTestHTTPServer.m b/UnitTesting/GTMTestHTTPServer.m
new file mode 100644
index 0000000..7744b5e
--- /dev/null
+++ b/UnitTesting/GTMTestHTTPServer.m
@@ -0,0 +1,166 @@
+//
+// GTMTestHTTPServer.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 "GTMTestHTTPServer.h"
+#import "GTMHTTPServer.h"
+#import "GTMRegex.h"
+
+static NSArray *GetSubPatternsOfFirstStringMatchedByPattern(NSString *stringToSearch,
+ NSString *pattern) {
+ GTMRegex *regex = [GTMRegex regexWithPattern:pattern];
+ NSString *firstMatch = [regex firstSubStringMatchedInString:stringToSearch];
+ NSArray *subPatterns = [regex subPatternsOfString:firstMatch];
+ return subPatterns;
+}
+
+@implementation GTMTestHTTPServer
+
+- (id)initWithDocRoot:(NSString *)docRoot {
+ self = [super init];
+ if (self) {
+ docRoot_ = [docRoot copy];
+ server_ = [[GTMHTTPServer alloc] initWithDelegate:self];
+ NSError *error = nil;
+ if ((docRoot == nil) || (![server_ start:&error])) {
+ _GTMDevLog(@"Failed to start up the webserver (docRoot='%@', error=%@)",
+ docRoot_, error);
+ [self release];
+ return nil;
+ }
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [docRoot_ release];
+ [server_ release];
+ [super dealloc];
+}
+
+- (uint16_t)port {
+ return [server_ port];
+}
+
+- (GTMHTTPResponseMessage *)httpServer:(GTMHTTPServer *)server
+ handleRequest:(GTMHTTPRequestMessage *)request {
+ _GTMDevAssert(server == server_, @"how'd we get a different server?!");
+ UInt32 resultStatus = 0;
+ NSData *data = nil;
+ // clients should treat dates as opaque, generally
+ NSString *modifiedDate = @"thursday";
+
+ NSString *postString = @"";
+ NSData *postData = [request body];
+ if ([postData length] > 0) {
+ postString = [[[NSString alloc] initWithData:postData
+ encoding:NSUTF8StringEncoding] autorelease];
+ }
+
+ NSDictionary *allHeaders = [request allHeaderFieldValues];
+ NSString *ifModifiedSince = [allHeaders objectForKey:@"If-Modified-Since"];
+ NSString *authorization = [allHeaders objectForKey:@"Authorization"];
+ NSString *path = [[request URL] absoluteString];
+
+ if ([path hasSuffix:@".auth"]) {
+ if (![authorization isEqualToString:@"GoogleLogin auth=GoodAuthToken"]) {
+ GTMHTTPResponseMessage *response =
+ [GTMHTTPResponseMessage emptyResponseWithCode:401];
+ return response;
+ } else {
+ path = [path substringToIndex:[path length] - 5];
+ }
+ }
+
+ NSString *overrideHeader = [allHeaders objectForKey:@"X-HTTP-Method-Override"];
+ NSString *httpCommand = [request method];
+ if ([httpCommand isEqualToString:@"POST"] &&
+ [overrideHeader length] > 1) {
+ httpCommand = overrideHeader;
+ }
+ NSArray *searchResult = nil;
+ if ([path hasSuffix:@"/accounts/ClientLogin"]) {
+ // it's a sign-in attempt; it's good unless the password is "bad" or
+ // "captcha"
+
+ // use regular expression to find the password
+ NSString *password = @"";
+ searchResult = GetSubPatternsOfFirstStringMatchedByPattern(path, @"Passwd=([^&\n]*)");
+ if ([searchResult count] == 2) {
+ password = [searchResult objectAtIndex:1];
+ }
+
+ if ([password isEqualToString:@"bad"]) {
+ resultStatus = 403;
+ } else if ([password isEqualToString:@"captcha"]) {
+ NSString *loginToken = @"";
+ NSString *loginCaptcha = @"";
+
+ searchResult = GetSubPatternsOfFirstStringMatchedByPattern(postString, @"logintoken=([^&\n]*)");
+ if ([searchResult count] == 2) {
+ loginToken = [searchResult objectAtIndex:1];
+ }
+
+ searchResult = GetSubPatternsOfFirstStringMatchedByPattern(postString, @"logincaptcha=([^&\n]*)");
+ if ([searchResult count] == 2) {
+ loginCaptcha = [searchResult objectAtIndex:1];
+ }
+
+ if ([loginToken isEqualToString:@"CapToken"] &&
+ [loginCaptcha isEqualToString:@"good"]) {
+ resultStatus = 200;
+ } else {
+ // incorrect captcha token or answer provided
+ resultStatus = 403;
+ }
+ } else {
+ // valid username/password
+ resultStatus = 200;
+ }
+ } else if ([httpCommand isEqualToString:@"DELETE"]) {
+ // it's an object delete; read and return empty data
+ resultStatus = 200;
+ } else {
+ // queries that have something like "?status=456" should fail with the
+ // status code
+ searchResult = GetSubPatternsOfFirstStringMatchedByPattern(path, @"status=([0-9]+)");
+ if ([searchResult count] == 2) {
+ resultStatus = [[searchResult objectAtIndex:1] intValue];
+ } else if ([ifModifiedSince isEqualToString:modifiedDate]) {
+ resultStatus = 304;
+ } else {
+ NSString *docPath = [docRoot_ stringByAppendingPathComponent:path];
+ data = [NSData dataWithContentsOfFile:docPath];
+ if (data) {
+ resultStatus = 200;
+ } else {
+ resultStatus = 404;
+ }
+ }
+ }
+
+ GTMHTTPResponseMessage *response =
+ [GTMHTTPResponseMessage responseWithBody:data
+ contentType:@"text/plain"
+ statusCode:resultStatus];
+ [response setValue:modifiedDate forHeaderField:@"Last-Modified"];
+ [response setValue:[NSString stringWithFormat:@"TestCookie=%@", [path lastPathComponent]]
+ forHeaderField:@"Set-Cookie"];
+ return response;
+}
+
+@end
diff --git a/UnitTesting/GTMUIKit+UnitTesting.h b/UnitTesting/GTMUIKit+UnitTesting.h
index 3c231cf..578ee4d 100644
--- a/UnitTesting/GTMUIKit+UnitTesting.h
+++ b/UnitTesting/GTMUIKit+UnitTesting.h
@@ -50,12 +50,12 @@
#define GTMAssertDrawingEqualToFile(a1, a2, a3, a4, description, ...) \
do { \
- id<GTMUnitTestViewDrawer> a1Object = (a1); \
+ id<GTMUnitTestViewDrawer> a1Drawer = (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]; \
+ GTMUnitTestView *view = [[[GTMUnitTestView alloc] initWithFrame:frame drawer:a1Drawer contextInfo:a4ContextInfo] autorelease]; \
GTMAssertObjectImageEqualToImageNamed(view, a3String, STComposeString(description, ##__VA_ARGS__)); \
} while(0)
diff --git a/UnitTesting/GTMUnitTestDevLog.h b/UnitTesting/GTMUnitTestDevLog.h
new file mode 100644
index 0000000..55ec44c
--- /dev/null
+++ b/UnitTesting/GTMUnitTestDevLog.h
@@ -0,0 +1,70 @@
+//
+// GTMUnitTestDevLog.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 "GTMDefines.h"
+#import <Foundation/Foundation.h>
+
+// GTMUnitTestDevLog tracks what messages are logged to verify that you only
+// log what you expect to log during the running of unittests. This allows you
+// to log with impunity from your actual core implementations and still be able
+// to find unexpected logs in your output when running unittests.
+// In your unittests you tell GTMUnitTestDevLog what messages you expect your
+// test to spit out, and it will cause any that don't match to appear as errors
+// in your unittest run output. You can match on exact strings or standard
+// regexps.
+
+@interface GTMUnitTestDevLog : NSObject
+// Log a message
++ (void)log:(NSString*)format, ...;
++ (void)log:(NSString*)format args:(va_list)args;
+
+// Turn tracking on/off
++ (void)enableTracking;
++ (void)disableTracking;
++ (BOOL)isTrackingEnabled;
+
+// Note that you are expecting a string that has an exact match. No need to
+// escape any pattern characters.
++ (void)expectString:(NSString *)format, ...;
+
+// Note that you are expecting a pattern. Pattern characters that you want
+// exact matches on must be escaped. See [GTMRegex escapedPatternForString].
+// Patterns match across newlines (kGTMRegexOptionSupressNewlineSupport) making
+// it easier to match output from the descriptions of NS collection types such
+// as NSArray and NSDictionary.
++ (void)expectPattern:(NSString *)format, ...;
+
+// Note that you are expecting exactly 'n' strings
++ (void)expect:(NSUInteger)n casesOfString:(NSString *)format, ...;
+
+// Note that you are expecting exactly 'n' patterns
++ (void)expect:(NSUInteger)n casesOfPattern:(NSString*)format, ...;
++ (void)expect:(NSUInteger)n casesOfPattern:(NSString*)format args:(va_list)args;
+
+// Call when you want to verify that you have matched all the logs you expect
+// to match. If your unittests inherit from GTMTestcase (like they should) you
+// will get this called for free.
++ (void)verifyNoMoreLogsExpected;
+
+// Resets the expected logs so that you don't have anything expected.
+// In general should not be needed, unless you have a variable logging case
+// of some sort.
++ (void)resetExpectedLogs;
+@end
+
+
diff --git a/UnitTesting/GTMUnitTestDevLog.m b/UnitTesting/GTMUnitTestDevLog.m
new file mode 100644
index 0000000..30ab13b
--- /dev/null
+++ b/UnitTesting/GTMUnitTestDevLog.m
@@ -0,0 +1,154 @@
+//
+// GTMUnitTestDevLog.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 "GTMUnitTestDevLog.h"
+#import "GTMRegex.h"
+#import "GTMSenTestCase.h"
+
+@implementation GTMUnitTestDevLog
+// If unittests are ever being run on separate threads, this may need to be
+// made a thread local variable.
+static BOOL gTrackingEnabled = NO;
+
++ (NSMutableArray *)patterns {
+ static NSMutableArray *patterns = nil;
+ if (!patterns) {
+ patterns = [[NSMutableArray array] retain];
+ }
+ return patterns;
+}
+
++ (BOOL)isTrackingEnabled {
+ return gTrackingEnabled;
+}
+
++ (void)enableTracking {
+ gTrackingEnabled = YES;
+}
+
++ (void)disableTracking {
+ gTrackingEnabled = NO;
+}
+
++ (void)log:(NSString*)format, ... {
+ va_list argList;
+ va_start(argList, format);
+ [self log:format args:argList];
+ va_end(argList);
+}
+
++ (void)log:(NSString*)format args:(va_list)args {
+ if ([self isTrackingEnabled]) {
+ NSString *logString = [[[NSString alloc] initWithFormat:format
+ arguments:args] autorelease];
+ @synchronized(self) {
+ NSMutableArray *patterns = [self patterns];
+ BOOL logError = [patterns count] == 0 ? YES : NO;
+ GTMRegex *regex = nil;
+ if (!logError) {
+ regex = [[[patterns objectAtIndex:0] retain] autorelease];
+ logError = [regex matchesString:logString] ? NO : YES;
+ [patterns removeObjectAtIndex:0];
+ }
+ if (logError) {
+ if (regex) {
+ [NSException raise:SenTestFailureException
+ format:@"Unexpected log: %@\nExpected: %@",
+ logString, regex];
+ } else {
+ [NSException raise:SenTestFailureException
+ format:@"Unexpected log: %@", logString];
+ }
+ } else {
+ NSLog(@"Expected Log: %@", logString);
+ }
+ }
+ } else {
+ NSLogv(format, args);
+ }
+}
+
++ (void)expectString:(NSString *)format, ... {
+ va_list argList;
+ va_start(argList, format);
+ NSString *string = [[[NSString alloc] initWithFormat:format
+ arguments:argList] autorelease];
+ va_end(argList);
+ NSString *pattern = [GTMRegex escapedPatternForString:string];
+ [self expect:1 casesOfPattern:pattern];
+
+}
+
++ (void)expectPattern:(NSString *)format, ... {
+ va_list argList;
+ va_start(argList, format);
+ [self expect:1 casesOfPattern:format args:argList];
+ va_end(argList);
+}
+
++ (void)expect:(NSUInteger)n casesOfString:(NSString *)format, ... {
+ va_list argList;
+ va_start(argList, format);
+ NSString *string = [[[NSString alloc] initWithFormat:format
+ arguments:argList] autorelease];
+ va_end(argList);
+ NSString *pattern = [GTMRegex escapedPatternForString:string];
+ [self expect:n casesOfPattern:pattern];
+}
+
++ (void)expect:(NSUInteger)n casesOfPattern:(NSString*)format, ... {
+ va_list argList;
+ va_start(argList, format);
+ [self expect:n casesOfPattern:format args:argList];
+ va_end(argList);
+}
+
++ (void)expect:(NSUInteger)n
+casesOfPattern:(NSString*)format
+ args:(va_list)args {
+ NSString *pattern = [[[NSString alloc] initWithFormat:format
+ arguments:args] autorelease];
+ GTMRegex *regex = [GTMRegex regexWithPattern:pattern
+ options:kGTMRegexOptionSupressNewlineSupport];
+ @synchronized(self) {
+ NSMutableArray *patterns = [self patterns];
+ for (NSUInteger i = 0; i < n; ++i) {
+ [patterns addObject:regex];
+ }
+ }
+}
+
++ (void)verifyNoMoreLogsExpected {
+ @synchronized(self) {
+ NSMutableArray *patterns = [self patterns];
+ if ([patterns count] > 0) {
+ NSMutableArray *patternsCopy = [[patterns copy] autorelease];
+ [self resetExpectedLogs];
+ [NSException raise:SenTestFailureException
+ format:@"Logs still expected %@", patternsCopy];
+ }
+ }
+}
+
++ (void)resetExpectedLogs {
+ @synchronized(self) {
+ NSMutableArray *patterns = [self patterns];
+ [patterns removeAllObjects];
+ }
+}
+@end
diff --git a/UnitTesting/GTMUnitTestingBindingTest.m b/UnitTesting/GTMUnitTestingBindingTest.m
index d27e8e7..78e48b1 100644
--- a/UnitTesting/GTMUnitTestingBindingTest.m
+++ b/UnitTesting/GTMUnitTestingBindingTest.m
@@ -16,11 +16,11 @@
// the License.
//
-#import <SenTestingKit/SenTestingKit.h>
+#import "GTMSenTestCase.h"
#import "GTMUnitTestingTest.h"
#import "GTMNSObject+BindingUnitTesting.h"
-@interface GTMUnitTestingBindingTest : SenTestCase {
+@interface GTMUnitTestingBindingTest : GTMTestCase {
int expectedFailureCount_;
}
@end
diff --git a/UnitTesting/GTMUnitTestingTest.m b/UnitTesting/GTMUnitTestingTest.m
index cc28a1a..5d2a9f3 100644
--- a/UnitTesting/GTMUnitTestingTest.m
+++ b/UnitTesting/GTMUnitTestingTest.m
@@ -23,7 +23,7 @@
NSString *const kGTMWindowNibName = @"GTMUnitTestingTest";
NSString *const kGTMWindowSaveFileName = @"GTMUnitTestingWindow";
-@interface GTMUnitTestingTest : SenTestCase {
+@interface GTMUnitTestingTest : GTMTestCase {
int expectedFailureCount_;
}
@end
@@ -84,11 +84,11 @@ NSString *const kGTMWindowSaveFileName = @"GTMUnitTestingWindow";
- (void)testViewUnitTesting {
GTMUnitTestingView *unitTestingView = [[GTMUnitTestingView alloc] init];
- GTMAssertDrawingEqualToFile(unitTestingView,
- NSMakeSize(200,200),
- @"GTMUnitTestingView",
- NSApp,
- @"Testing view drawing");
+ GTMAssertDrawingEqualToImageNamed(unitTestingView,
+ NSMakeSize(200,200),
+ @"GTMUnitTestingView",
+ NSApp,
+ @"Testing view drawing");
STAssertTrue([unitTestingView hadGoodContext], @"bad context?");
[unitTestingView release];
}
@@ -216,7 +216,7 @@ NSString *const kGTMWindowSaveFileName = @"GTMUnitTestingWindow";
[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"];
+ [inCoder encodeBytes:(const uint8_t*)"BytesTest" length:9 forKey:@"BytesTest"];
didEncode_ = YES;
}
diff --git a/UnitTesting/GTMUnitTestingUtilities.h b/UnitTesting/GTMUnitTestingUtilities.h
index 17ec766..eca0825 100644
--- a/UnitTesting/GTMUnitTestingUtilities.h
+++ b/UnitTesting/GTMUnitTestingUtilities.h
@@ -16,6 +16,7 @@
// the License.
//
+#import <Foundation/Foundation.h>
#import <objc/objc.h>
// Collection of utilities for unit testing
diff --git a/UnitTesting/GTMUnitTestingUtilities.m b/UnitTesting/GTMUnitTestingUtilities.m
index a37b7dd..a20023f 100644
--- a/UnitTesting/GTMUnitTestingUtilities.m
+++ b/UnitTesting/GTMUnitTestingUtilities.m
@@ -126,7 +126,7 @@ static void RestoreColorProfile(void) {
}
}
-void SetColorProfileToGenericRGB() {
+void SetColorProfileToGenericRGB(void) {
NSColorSpace *genericSpace = [NSColorSpace genericRGBColorSpace];
CMProfileRef genericProfile = (CMProfileRef)[genericSpace colorSyncProfile];
CMProfileRef previousProfile;
diff --git a/XcodeConfig/Project/DebugLeopardOrLater.xcconfig b/XcodeConfig/Project/DebugLeopardOrLater.xcconfig
index 7bc3257..6aebf12 100644
--- a/XcodeConfig/Project/DebugLeopardOrLater.xcconfig
+++ b/XcodeConfig/Project/DebugLeopardOrLater.xcconfig
@@ -23,7 +23,7 @@
// the License.
//
-// Pull in our general Google settings
+// Pull in the general settings
#include "../subconfig/General.xcconfig"
// Leopard or later
diff --git a/XcodeConfig/Project/DebugTigerOrLater.xcconfig b/XcodeConfig/Project/DebugTigerOrLater.xcconfig
index 76d2e27..725ea49 100644
--- a/XcodeConfig/Project/DebugTigerOrLater.xcconfig
+++ b/XcodeConfig/Project/DebugTigerOrLater.xcconfig
@@ -23,7 +23,7 @@
// the License.
//
-// Pull in our general Google settings
+// Pull in the general settings
#include "../subconfig/General.xcconfig"
// Tiger or later
diff --git a/XcodeConfig/Project/DebugiPhone.xcconfig b/XcodeConfig/Project/DebugiPhone.xcconfig
index 45c5ba5..1ba99b4 100644
--- a/XcodeConfig/Project/DebugiPhone.xcconfig
+++ b/XcodeConfig/Project/DebugiPhone.xcconfig
@@ -23,7 +23,7 @@
// and individual Xcode target, there are other configuration files for that
// purpose.
-// Pull in our general Google settings
+// Pull in the general settings
#include "../subconfig/General.xcconfig"
// iPhone settings
diff --git a/XcodeConfig/Project/ReleaseLeopardOrLater.xcconfig b/XcodeConfig/Project/ReleaseLeopardOrLater.xcconfig
index 52f0383..0188d10 100644
--- a/XcodeConfig/Project/ReleaseLeopardOrLater.xcconfig
+++ b/XcodeConfig/Project/ReleaseLeopardOrLater.xcconfig
@@ -23,7 +23,7 @@
// the License.
//
-// Pull in our general Google settings
+// Pull in the general settings
#include "../subconfig/General.xcconfig"
// Leopard or later
diff --git a/XcodeConfig/Project/ReleaseTigerOrLater.xcconfig b/XcodeConfig/Project/ReleaseTigerOrLater.xcconfig
index d14e739..a024c4b 100644
--- a/XcodeConfig/Project/ReleaseTigerOrLater.xcconfig
+++ b/XcodeConfig/Project/ReleaseTigerOrLater.xcconfig
@@ -23,7 +23,7 @@
// the License.
//
-// Pull in our general Google settings
+// Pull in the general settings
#include "../subconfig/General.xcconfig"
// Tiger or later
diff --git a/XcodeConfig/Project/ReleaseiPhone.xcconfig b/XcodeConfig/Project/ReleaseiPhone.xcconfig
index e61d1b6..d6c0127 100644
--- a/XcodeConfig/Project/ReleaseiPhone.xcconfig
+++ b/XcodeConfig/Project/ReleaseiPhone.xcconfig
@@ -23,7 +23,7 @@
// and individual Xcode target, there are other configuration files for that
// purpose.
-// Pull in our general Google settings
+// Pull in the general settings
#include "../subconfig/General.xcconfig"
// iPhone Settings.
diff --git a/XcodeConfig/xcconfigs-readme.txt b/XcodeConfig/xcconfigs-readme.txt
index 23fde4b..5a44bb0 100644
--- a/XcodeConfig/xcconfigs-readme.txt
+++ b/XcodeConfig/xcconfigs-readme.txt
@@ -1,12 +1,32 @@
+Xcode Config is sorta a black art, any time you have a set of rules, you
+quickly hit a few exceptions.
-Configs w/in Project folder are configs expected to be set at the project level.
+The main goal of using these is as follow:
-Configs w/in the Target folder are configs expected to be set on the per target
-level.
+Edit your Project level build settings by removing as much as possible, and
+then set the per Configuration settings to one of the project xcode config
+files w/in the Project subfolder here. This will batch setup the project to
+build Debug/Release w/ a specific SDK.
-Configs w/in subconfig are not meant to be used directly; they are referenced by
-the other configs.
+If you are building a Shared Library, Loadable Bundle (Framework) or UnitTest
+you will need to apply a further Xcode Config file at the target level. You do
+this again by clearing most of the settings on the target, and just setting the
+build config for that target to be the match from the Target subfolder here.
+To see an example of this, look at CoverStory
+(http://code.google.com/p/coverstory) or Vidnik
+(http://code.google.com/p/vidnik).
-Remember: when using the configs at any given layer, make sure you set them for
-each build configuration.
+
+The common exception...If you need to have a few targets build w/ different
+SDKs, then you hit the most common of the exceptions. For these, you'd need
+the top level config not to set some things, the simplest way to do this seems
+to be to remove as many of the settings from the project file, and make new
+wrapper xcconfig files that inclue both the project level and target level
+setting and set them on the targets (yes, this is like the MetroWerks days
+where you can quickly explode in a what seems like N^2 (or worse) number of
+config files. With a little luck, future versions of Xcode might have some
+support to make mixing SDKs easier.
+
+Remember: When using the configs at any given layer, make sure you set them for
+each build configuration you need (not just the active one).