mirror of https://github.com/grpc/grpc.git
parent
bc0ce06951
commit
17a67fdb0f
9 changed files with 870 additions and 293 deletions
@ -0,0 +1,482 @@ |
||||
/* |
||||
* |
||||
* Copyright 2018 gRPC authors. |
||||
* |
||||
* 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 <GRPCClient/GRPCCall.h> |
||||
#import <ProtoRPC/ProtoMethod.h> |
||||
#import <RemoteTest/Messages.pbobjc.h> |
||||
#import <XCTest/XCTest.h> |
||||
|
||||
#include <grpc/grpc.h> |
||||
|
||||
#import "../version.h" |
||||
|
||||
// The server address is derived from preprocessor macro, which is |
||||
// in turn derived from environment variable of the same name. |
||||
#define NSStringize_helper(x) #x |
||||
#define NSStringize(x) @NSStringize_helper(x) |
||||
static NSString *const kHostAddress = NSStringize(HOST_PORT_LOCAL); |
||||
static NSString *const kRemoteSSLHost = NSStringize(HOST_PORT_REMOTE); |
||||
|
||||
// Package and service name of test server |
||||
static NSString *const kPackage = @"grpc.testing"; |
||||
static NSString *const kService = @"TestService"; |
||||
|
||||
static GRPCProtoMethod *kInexistentMethod; |
||||
static GRPCProtoMethod *kEmptyCallMethod; |
||||
static GRPCProtoMethod *kUnaryCallMethod; |
||||
static GRPCProtoMethod *kFullDuplexCallMethod; |
||||
|
||||
static const int kSimpleDataLength = 100; |
||||
|
||||
static const NSTimeInterval kTestTimeout = 16; |
||||
|
||||
// Reveal the _class ivar for testing access |
||||
@interface GRPCCall2 () { |
||||
@public |
||||
GRPCCall *_call; |
||||
} |
||||
|
||||
@end |
||||
|
||||
// Convenience class to use blocks as callbacks |
||||
@interface ClientTestsBlockCallbacks : NSObject<GRPCResponseHandler> |
||||
|
||||
- (instancetype)initWithInitialMetadataCallback:(void (^)(NSDictionary *))initialMetadataCallback |
||||
messageCallback:(void (^)(id))messageCallback |
||||
closeCallback:(void (^)(NSDictionary *, NSError *))closeCallback; |
||||
|
||||
@end |
||||
|
||||
@implementation ClientTestsBlockCallbacks { |
||||
void (^_initialMetadataCallback)(NSDictionary *); |
||||
void (^_messageCallback)(id); |
||||
void (^_closeCallback)(NSDictionary *, NSError *); |
||||
dispatch_queue_t _dispatchQueue; |
||||
} |
||||
|
||||
- (instancetype)initWithInitialMetadataCallback:(void (^)(NSDictionary *))initialMetadataCallback |
||||
messageCallback:(void (^)(id))messageCallback |
||||
closeCallback:(void (^)(NSDictionary *, NSError *))closeCallback { |
||||
if ((self = [super init])) { |
||||
_initialMetadataCallback = initialMetadataCallback; |
||||
_messageCallback = messageCallback; |
||||
_closeCallback = closeCallback; |
||||
_dispatchQueue = dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL); |
||||
} |
||||
return self; |
||||
} |
||||
|
||||
- (void)receivedInitialMetadata:(NSDictionary *_Nullable)initialMetadata { |
||||
dispatch_async(_dispatchQueue, ^{ |
||||
if (self->_initialMetadataCallback) { |
||||
self->_initialMetadataCallback(initialMetadata); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
- (void)receivedRawMessage:(GPBMessage *_Nullable)message { |
||||
dispatch_async(_dispatchQueue, ^{ |
||||
if (self->_messageCallback) { |
||||
self->_messageCallback(message); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
- (void)closedWithTrailingMetadata:(NSDictionary *_Nullable)trailingMetadata |
||||
error:(NSError *_Nullable)error { |
||||
dispatch_async(_dispatchQueue, ^{ |
||||
if (self->_closeCallback) { |
||||
self->_closeCallback(trailingMetadata, error); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
- (dispatch_queue_t)dispatchQueue { |
||||
return _dispatchQueue; |
||||
} |
||||
|
||||
@end |
||||
|
||||
@interface CallAPIv2Tests : XCTestCase<GRPCAuthorizationProtocol> |
||||
|
||||
@end |
||||
|
||||
@implementation CallAPIv2Tests |
||||
|
||||
- (void)setUp { |
||||
// This method isn't implemented by the remote server. |
||||
kInexistentMethod = |
||||
[[GRPCProtoMethod alloc] initWithPackage:kPackage service:kService method:@"Inexistent"]; |
||||
kEmptyCallMethod = |
||||
[[GRPCProtoMethod alloc] initWithPackage:kPackage service:kService method:@"EmptyCall"]; |
||||
kUnaryCallMethod = |
||||
[[GRPCProtoMethod alloc] initWithPackage:kPackage service:kService method:@"UnaryCall"]; |
||||
kFullDuplexCallMethod = |
||||
[[GRPCProtoMethod alloc] initWithPackage:kPackage service:kService method:@"FullDuplexCall"]; |
||||
} |
||||
|
||||
- (void)testMetadata { |
||||
__weak XCTestExpectation *expectation = [self expectationWithDescription:@"RPC unauthorized."]; |
||||
|
||||
RMTSimpleRequest *request = [RMTSimpleRequest message]; |
||||
request.fillUsername = YES; |
||||
request.fillOauthScope = YES; |
||||
|
||||
GRPCRequestOptions *callRequest = |
||||
[[GRPCRequestOptions alloc] initWithHost:(NSString *)kRemoteSSLHost |
||||
path:kUnaryCallMethod.HTTPPath |
||||
safety:GRPCCallSafetyDefault]; |
||||
__block NSDictionary *init_md; |
||||
__block NSDictionary *trailing_md; |
||||
GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; |
||||
options.oauth2AccessToken = @"bogusToken"; |
||||
GRPCCall2 *call = [[GRPCCall2 alloc] |
||||
initWithRequestOptions:callRequest |
||||
responseHandler:[[ClientTestsBlockCallbacks alloc] |
||||
initWithInitialMetadataCallback:^(NSDictionary *initialMetadata) { |
||||
init_md = initialMetadata; |
||||
} |
||||
messageCallback:^(id message) { |
||||
XCTFail(@"Received unexpected response."); |
||||
} |
||||
closeCallback:^(NSDictionary *trailingMetadata, NSError *error) { |
||||
trailing_md = trailingMetadata; |
||||
if (error) { |
||||
XCTAssertEqual(error.code, 16, |
||||
@"Finished with unexpected error: %@", error); |
||||
XCTAssertEqualObjects(init_md, |
||||
error.userInfo[kGRPCHeadersKey]); |
||||
XCTAssertEqualObjects(trailing_md, |
||||
error.userInfo[kGRPCTrailersKey]); |
||||
NSString *challengeHeader = init_md[@"www-authenticate"]; |
||||
XCTAssertGreaterThan(challengeHeader.length, 0, |
||||
@"No challenge in response headers %@", |
||||
init_md); |
||||
[expectation fulfill]; |
||||
} |
||||
}] |
||||
callOptions:options]; |
||||
|
||||
[call start]; |
||||
[call writeData:[request data]]; |
||||
[call finish]; |
||||
|
||||
[self waitForExpectationsWithTimeout:kTestTimeout handler:nil]; |
||||
} |
||||
|
||||
- (void)testUserAgentPrefix { |
||||
__weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."]; |
||||
__weak XCTestExpectation *recvInitialMd = |
||||
[self expectationWithDescription:@"Did not receive initial md."]; |
||||
|
||||
GRPCRequestOptions *request = [[GRPCRequestOptions alloc] initWithHost:kHostAddress |
||||
path:kEmptyCallMethod.HTTPPath |
||||
safety:GRPCCallSafetyDefault]; |
||||
NSDictionary *headers = |
||||
[NSDictionary dictionaryWithObjectsAndKeys:@"", @"x-grpc-test-echo-useragent", nil]; |
||||
GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; |
||||
options.transportType = GRPCTransportTypeInsecure; |
||||
options.userAgentPrefix = @"Foo"; |
||||
options.initialMetadata = headers; |
||||
GRPCCall2 *call = [[GRPCCall2 alloc] |
||||
initWithRequestOptions:request |
||||
responseHandler:[[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:^( |
||||
NSDictionary *initialMetadata) { |
||||
NSString *userAgent = initialMetadata[@"x-grpc-test-echo-useragent"]; |
||||
// Test the regex is correct |
||||
NSString *expectedUserAgent = @"Foo grpc-objc/"; |
||||
expectedUserAgent = |
||||
[expectedUserAgent stringByAppendingString:GRPC_OBJC_VERSION_STRING]; |
||||
expectedUserAgent = [expectedUserAgent stringByAppendingString:@" grpc-c/"]; |
||||
expectedUserAgent = |
||||
[expectedUserAgent stringByAppendingString:GRPC_C_VERSION_STRING]; |
||||
expectedUserAgent = [expectedUserAgent stringByAppendingString:@" (ios; chttp2; "]; |
||||
expectedUserAgent = [expectedUserAgent |
||||
stringByAppendingString:[NSString stringWithUTF8String:grpc_g_stands_for()]]; |
||||
expectedUserAgent = [expectedUserAgent stringByAppendingString:@")"]; |
||||
XCTAssertEqualObjects(userAgent, expectedUserAgent); |
||||
|
||||
NSError *error = nil; |
||||
// Change in format of user-agent field in a direction that does not match |
||||
// the regex will likely cause problem for certain gRPC users. For details, |
||||
// refer to internal doc https://goo.gl/c2diBc |
||||
NSRegularExpression *regex = [NSRegularExpression |
||||
regularExpressionWithPattern: |
||||
@" grpc-[a-zA-Z0-9]+(-[a-zA-Z0-9]+)?/[^ ,]+( \\([^)]*\\))?" |
||||
options:0 |
||||
error:&error]; |
||||
|
||||
NSString *customUserAgent = |
||||
[regex stringByReplacingMatchesInString:userAgent |
||||
options:0 |
||||
range:NSMakeRange(0, [userAgent length]) |
||||
withTemplate:@""]; |
||||
XCTAssertEqualObjects(customUserAgent, @"Foo"); |
||||
[recvInitialMd fulfill]; |
||||
} |
||||
messageCallback:^(id message) { |
||||
XCTAssertNotNil(message); |
||||
XCTAssertEqual([message length], 0, |
||||
@"Non-empty response received: %@", message); |
||||
} |
||||
closeCallback:^(NSDictionary *trailingMetadata, NSError *error) { |
||||
if (error) { |
||||
XCTFail(@"Finished with unexpected error: %@", error); |
||||
} else { |
||||
[completion fulfill]; |
||||
} |
||||
}] |
||||
callOptions:options]; |
||||
[call writeData:[NSData data]]; |
||||
[call start]; |
||||
|
||||
[self waitForExpectationsWithTimeout:kTestTimeout handler:nil]; |
||||
} |
||||
|
||||
- (void)getTokenWithHandler:(void (^)(NSString *token))handler { |
||||
dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); |
||||
dispatch_sync(queue, ^{ |
||||
handler(@"test-access-token"); |
||||
}); |
||||
} |
||||
|
||||
- (void)testOAuthToken { |
||||
__weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."]; |
||||
|
||||
GRPCRequestOptions *requestOptions = |
||||
[[GRPCRequestOptions alloc] initWithHost:kHostAddress |
||||
path:kEmptyCallMethod.HTTPPath |
||||
safety:GRPCCallSafetyDefault]; |
||||
GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; |
||||
options.transportType = GRPCTransportTypeInsecure; |
||||
options.authTokenProvider = self; |
||||
__block GRPCCall2 *call = [[GRPCCall2 alloc] |
||||
initWithRequestOptions:requestOptions |
||||
responseHandler:[[ClientTestsBlockCallbacks alloc] |
||||
initWithInitialMetadataCallback:nil |
||||
messageCallback:nil |
||||
closeCallback:^(NSDictionary *trailingMetadata, |
||||
NSError *error) { |
||||
[completion fulfill]; |
||||
}] |
||||
callOptions:options]; |
||||
[call writeData:[NSData data]]; |
||||
[call start]; |
||||
|
||||
[self waitForExpectationsWithTimeout:kTestTimeout handler:nil]; |
||||
} |
||||
|
||||
- (void)testResponseSizeLimitExceeded { |
||||
__weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."]; |
||||
|
||||
GRPCRequestOptions *requestOptions = |
||||
[[GRPCRequestOptions alloc] initWithHost:kHostAddress |
||||
path:kUnaryCallMethod.HTTPPath |
||||
safety:GRPCCallSafetyDefault]; |
||||
GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; |
||||
options.responseSizeLimit = kSimpleDataLength; |
||||
options.transportType = GRPCTransportTypeInsecure; |
||||
|
||||
RMTSimpleRequest *request = [RMTSimpleRequest message]; |
||||
request.payload.body = [NSMutableData dataWithLength:options.responseSizeLimit]; |
||||
request.responseSize = (int32_t)(options.responseSizeLimit * 2); |
||||
|
||||
GRPCCall2 *call = [[GRPCCall2 alloc] |
||||
initWithRequestOptions:requestOptions |
||||
responseHandler:[[ClientTestsBlockCallbacks alloc] |
||||
initWithInitialMetadataCallback:nil |
||||
messageCallback:nil |
||||
closeCallback:^(NSDictionary *trailingMetadata, |
||||
NSError *error) { |
||||
XCTAssertNotNil(error, |
||||
@"Expecting non-nil error"); |
||||
XCTAssertEqual(error.code, |
||||
GRPCErrorCodeResourceExhausted); |
||||
[completion fulfill]; |
||||
}] |
||||
callOptions:options]; |
||||
[call writeData:[request data]]; |
||||
[call start]; |
||||
|
||||
[self waitForExpectationsWithTimeout:kTestTimeout handler:nil]; |
||||
} |
||||
|
||||
- (void)testIdempotentProtoRPC { |
||||
__weak XCTestExpectation *response = [self expectationWithDescription:@"Expected response."]; |
||||
__weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."]; |
||||
|
||||
RMTSimpleRequest *request = [RMTSimpleRequest message]; |
||||
request.responseSize = kSimpleDataLength; |
||||
request.fillUsername = YES; |
||||
request.fillOauthScope = YES; |
||||
GRPCRequestOptions *requestOptions = |
||||
[[GRPCRequestOptions alloc] initWithHost:kHostAddress |
||||
path:kUnaryCallMethod.HTTPPath |
||||
safety:GRPCCallSafetyIdempotentRequest]; |
||||
|
||||
GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; |
||||
options.transportType = GRPCTransportTypeInsecure; |
||||
GRPCCall2 *call = [[GRPCCall2 alloc] |
||||
initWithRequestOptions:requestOptions |
||||
responseHandler:[[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil |
||||
messageCallback:^(id message) { |
||||
NSData *data = (NSData *)message; |
||||
XCTAssertNotNil(data, @"nil value received as response."); |
||||
XCTAssertGreaterThan(data.length, 0, |
||||
@"Empty response received."); |
||||
RMTSimpleResponse *responseProto = |
||||
[RMTSimpleResponse parseFromData:data error:NULL]; |
||||
// We expect empty strings, not nil: |
||||
XCTAssertNotNil(responseProto.username, |
||||
@"Response's username is nil."); |
||||
XCTAssertNotNil(responseProto.oauthScope, |
||||
@"Response's OAuth scope is nil."); |
||||
[response fulfill]; |
||||
} |
||||
closeCallback:^(NSDictionary *trailingMetadata, NSError *error) { |
||||
XCTAssertNil(error, @"Finished with unexpected error: %@", |
||||
error); |
||||
[completion fulfill]; |
||||
}] |
||||
callOptions:options]; |
||||
|
||||
[call start]; |
||||
[call writeData:[request data]]; |
||||
[call finish]; |
||||
|
||||
[self waitForExpectationsWithTimeout:kTestTimeout handler:nil]; |
||||
} |
||||
|
||||
- (void)testTimeout { |
||||
__weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."]; |
||||
|
||||
GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; |
||||
options.timeout = 0.001; |
||||
GRPCRequestOptions *requestOptions = |
||||
[[GRPCRequestOptions alloc] initWithHost:kHostAddress |
||||
path:kFullDuplexCallMethod.HTTPPath |
||||
safety:GRPCCallSafetyDefault]; |
||||
|
||||
GRPCCall2 *call = [[GRPCCall2 alloc] |
||||
initWithRequestOptions:requestOptions |
||||
responseHandler: |
||||
[[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil |
||||
messageCallback:^(NSData *data) { |
||||
XCTFail(@"Failure: response received; Expect: no response received."); |
||||
} |
||||
closeCallback:^(NSDictionary *trailingMetadata, NSError *error) { |
||||
XCTAssertNotNil(error, |
||||
@"Failure: no error received; Expect: receive " |
||||
@"deadline exceeded."); |
||||
XCTAssertEqual(error.code, GRPCErrorCodeDeadlineExceeded); |
||||
[completion fulfill]; |
||||
}] |
||||
callOptions:options]; |
||||
|
||||
[call start]; |
||||
|
||||
[self waitForExpectationsWithTimeout:kTestTimeout handler:nil]; |
||||
} |
||||
|
||||
- (void)testTimeoutBackoffWithTimeout:(double)timeout Backoff:(double)backoff { |
||||
const double maxConnectTime = timeout > backoff ? timeout : backoff; |
||||
const double kMargin = 0.1; |
||||
|
||||
__weak XCTestExpectation *completion = [self expectationWithDescription:@"Timeout in a second."]; |
||||
NSString *const kDummyAddress = [NSString stringWithFormat:@"127.0.0.1:10000"]; |
||||
GRPCRequestOptions *requestOptions = |
||||
[[GRPCRequestOptions alloc] initWithHost:kDummyAddress |
||||
path:@"/dummy/path" |
||||
safety:GRPCCallSafetyDefault]; |
||||
GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; |
||||
options.connectMinTimeout = timeout; |
||||
options.connectInitialBackoff = backoff; |
||||
options.connectMaxBackoff = 0; |
||||
|
||||
NSDate *startTime = [NSDate date]; |
||||
GRPCCall2 *call = [[GRPCCall2 alloc] |
||||
initWithRequestOptions:requestOptions |
||||
responseHandler:[[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil |
||||
messageCallback:^(NSData *data) { |
||||
XCTFail(@"Received message. Should not reach here."); |
||||
} |
||||
closeCallback:^(NSDictionary *trailingMetadata, NSError *error) { |
||||
XCTAssertNotNil(error, |
||||
@"Finished with no error; expecting error"); |
||||
XCTAssertLessThan( |
||||
[[NSDate date] timeIntervalSinceDate:startTime], |
||||
maxConnectTime + kMargin); |
||||
[completion fulfill]; |
||||
}] |
||||
callOptions:options]; |
||||
|
||||
[call start]; |
||||
|
||||
[self waitForExpectationsWithTimeout:kTestTimeout handler:nil]; |
||||
} |
||||
|
||||
- (void)testTimeoutBackoff1 { |
||||
[self testTimeoutBackoffWithTimeout:0.7 Backoff:0.4]; |
||||
} |
||||
|
||||
- (void)testTimeoutBackoff2 { |
||||
[self testTimeoutBackoffWithTimeout:0.3 Backoff:0.8]; |
||||
} |
||||
|
||||
- (void)testCompression { |
||||
__weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."]; |
||||
|
||||
RMTSimpleRequest *request = [RMTSimpleRequest message]; |
||||
request.expectCompressed = [RMTBoolValue message]; |
||||
request.expectCompressed.value = YES; |
||||
request.responseCompressed = [RMTBoolValue message]; |
||||
request.expectCompressed.value = YES; |
||||
request.responseSize = kSimpleDataLength; |
||||
request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength]; |
||||
GRPCRequestOptions *requestOptions = |
||||
[[GRPCRequestOptions alloc] initWithHost:kHostAddress |
||||
path:kUnaryCallMethod.HTTPPath |
||||
safety:GRPCCallSafetyDefault]; |
||||
|
||||
GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; |
||||
options.transportType = GRPCTransportTypeInsecure; |
||||
options.compressionAlgorithm = GRPCCompressGzip; |
||||
GRPCCall2 *call = [[GRPCCall2 alloc] |
||||
initWithRequestOptions:requestOptions |
||||
responseHandler: [[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil |
||||
messageCallback:^(NSData *data) { |
||||
NSError *error; |
||||
RMTSimpleResponse *response = [RMTSimpleResponse parseFromData:data error:&error]; |
||||
XCTAssertNil(error, @"Error when parsing response: %@", error); |
||||
XCTAssertEqual(response.payload.body.length, kSimpleDataLength); |
||||
} |
||||
closeCallback:^(NSDictionary *trailingMetadata, NSError *error) { |
||||
XCTAssertNil(error, @"Received failure: %@", error); |
||||
[completion fulfill]; |
||||
}] |
||||
|
||||
callOptions:options]; |
||||
|
||||
[call start]; |
||||
[call writeData:[request data]]; |
||||
[call finish]; |
||||
|
||||
[self waitForExpectationsWithTimeout:kTestTimeout handler:nil]; |
||||
} |
||||
|
||||
@end |
@ -0,0 +1,22 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
||||
<plist version="1.0"> |
||||
<dict> |
||||
<key>CFBundleDevelopmentRegion</key> |
||||
<string>$(DEVELOPMENT_LANGUAGE)</string> |
||||
<key>CFBundleExecutable</key> |
||||
<string>$(EXECUTABLE_NAME)</string> |
||||
<key>CFBundleIdentifier</key> |
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> |
||||
<key>CFBundleInfoDictionaryVersion</key> |
||||
<string>6.0</string> |
||||
<key>CFBundleName</key> |
||||
<string>$(PRODUCT_NAME)</string> |
||||
<key>CFBundlePackageType</key> |
||||
<string>BNDL</string> |
||||
<key>CFBundleShortVersionString</key> |
||||
<string>1.0</string> |
||||
<key>CFBundleVersion</key> |
||||
<string>1</string> |
||||
</dict> |
||||
</plist> |
@ -0,0 +1,90 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<Scheme |
||||
LastUpgradeVersion = "1000" |
||||
version = "1.3"> |
||||
<BuildAction |
||||
parallelizeBuildables = "YES" |
||||
buildImplicitDependencies = "YES"> |
||||
<BuildActionEntries> |
||||
<BuildActionEntry |
||||
buildForTesting = "YES" |
||||
buildForRunning = "YES" |
||||
buildForProfiling = "NO" |
||||
buildForArchiving = "NO" |
||||
buildForAnalyzing = "NO"> |
||||
<BuildableReference |
||||
BuildableIdentifier = "primary" |
||||
BlueprintIdentifier = "5E10F5A6218CB0D1008BAB68" |
||||
BuildableName = "APIv2Tests.xctest" |
||||
BlueprintName = "APIv2Tests" |
||||
ReferencedContainer = "container:Tests.xcodeproj"> |
||||
</BuildableReference> |
||||
</BuildActionEntry> |
||||
</BuildActionEntries> |
||||
</BuildAction> |
||||
<TestAction |
||||
buildConfiguration = "Test" |
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" |
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" |
||||
shouldUseLaunchSchemeArgsEnv = "YES"> |
||||
<Testables> |
||||
<TestableReference |
||||
skipped = "NO"> |
||||
<BuildableReference |
||||
BuildableIdentifier = "primary" |
||||
BlueprintIdentifier = "5E10F5A6218CB0D1008BAB68" |
||||
BuildableName = "APIv2Tests.xctest" |
||||
BlueprintName = "APIv2Tests" |
||||
ReferencedContainer = "container:Tests.xcodeproj"> |
||||
</BuildableReference> |
||||
</TestableReference> |
||||
</Testables> |
||||
<AdditionalOptions> |
||||
</AdditionalOptions> |
||||
</TestAction> |
||||
<LaunchAction |
||||
buildConfiguration = "Test" |
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" |
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" |
||||
launchStyle = "0" |
||||
useCustomWorkingDirectory = "NO" |
||||
ignoresPersistentStateOnLaunch = "NO" |
||||
debugDocumentVersioning = "YES" |
||||
debugServiceExtension = "internal" |
||||
allowLocationSimulation = "YES"> |
||||
<MacroExpansion> |
||||
<BuildableReference |
||||
BuildableIdentifier = "primary" |
||||
BlueprintIdentifier = "5E10F5A6218CB0D1008BAB68" |
||||
BuildableName = "APIv2Tests.xctest" |
||||
BlueprintName = "APIv2Tests" |
||||
ReferencedContainer = "container:Tests.xcodeproj"> |
||||
</BuildableReference> |
||||
</MacroExpansion> |
||||
<AdditionalOptions> |
||||
</AdditionalOptions> |
||||
</LaunchAction> |
||||
<ProfileAction |
||||
buildConfiguration = "Release" |
||||
shouldUseLaunchSchemeArgsEnv = "YES" |
||||
savedToolIdentifier = "" |
||||
useCustomWorkingDirectory = "NO" |
||||
debugDocumentVersioning = "YES"> |
||||
<MacroExpansion> |
||||
<BuildableReference |
||||
BuildableIdentifier = "primary" |
||||
BlueprintIdentifier = "5E10F5A6218CB0D1008BAB68" |
||||
BuildableName = "APIv2Tests.xctest" |
||||
BlueprintName = "APIv2Tests" |
||||
ReferencedContainer = "container:Tests.xcodeproj"> |
||||
</BuildableReference> |
||||
</MacroExpansion> |
||||
</ProfileAction> |
||||
<AnalyzeAction |
||||
buildConfiguration = "Debug"> |
||||
</AnalyzeAction> |
||||
<ArchiveAction |
||||
buildConfiguration = "Release" |
||||
revealArchiveInOrganizer = "YES"> |
||||
</ArchiveAction> |
||||
</Scheme> |
Loading…
Reference in new issue