1*cc02d7e2SAndroid Build Coastguard Worker/* 2*cc02d7e2SAndroid Build Coastguard Worker * 3*cc02d7e2SAndroid Build Coastguard Worker * Copyright 2015 gRPC authors. 4*cc02d7e2SAndroid Build Coastguard Worker * 5*cc02d7e2SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 6*cc02d7e2SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 7*cc02d7e2SAndroid Build Coastguard Worker * You may obtain a copy of the License at 8*cc02d7e2SAndroid Build Coastguard Worker * 9*cc02d7e2SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 10*cc02d7e2SAndroid Build Coastguard Worker * 11*cc02d7e2SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 12*cc02d7e2SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 13*cc02d7e2SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*cc02d7e2SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 15*cc02d7e2SAndroid Build Coastguard Worker * limitations under the License. 16*cc02d7e2SAndroid Build Coastguard Worker * 17*cc02d7e2SAndroid Build Coastguard Worker */ 18*cc02d7e2SAndroid Build Coastguard Worker 19*cc02d7e2SAndroid Build Coastguard Worker#import <XCTest/XCTest.h> 20*cc02d7e2SAndroid Build Coastguard Worker#import <grpc/grpc.h> 21*cc02d7e2SAndroid Build Coastguard Worker#import <grpc/support/port_platform.h> 22*cc02d7e2SAndroid Build Coastguard Worker 23*cc02d7e2SAndroid Build Coastguard Worker#import <GRPCClient/GRPCCall+ChannelArg.h> 24*cc02d7e2SAndroid Build Coastguard Worker#import <GRPCClient/GRPCCall+OAuth2.h> 25*cc02d7e2SAndroid Build Coastguard Worker#import <GRPCClient/GRPCCall+Tests.h> 26*cc02d7e2SAndroid Build Coastguard Worker#import <GRPCClient/GRPCCall.h> 27*cc02d7e2SAndroid Build Coastguard Worker#import <GRPCClient/internal_testing/GRPCCall+InternalTests.h> 28*cc02d7e2SAndroid Build Coastguard Worker#import <ProtoRPC/ProtoMethod.h> 29*cc02d7e2SAndroid Build Coastguard Worker#import <RxLibrary/GRXBufferedPipe.h> 30*cc02d7e2SAndroid Build Coastguard Worker#import <RxLibrary/GRXWriteable.h> 31*cc02d7e2SAndroid Build Coastguard Worker#import <RxLibrary/GRXWriter+Immediate.h> 32*cc02d7e2SAndroid Build Coastguard Worker#import "src/objective-c/tests/RemoteTestClient/Messages.pbobjc.h" 33*cc02d7e2SAndroid Build Coastguard Worker 34*cc02d7e2SAndroid Build Coastguard Worker#include <netinet/in.h> 35*cc02d7e2SAndroid Build Coastguard Worker 36*cc02d7e2SAndroid Build Coastguard Worker#import "../Common/TestUtils.h" 37*cc02d7e2SAndroid Build Coastguard Worker#import "../version.h" 38*cc02d7e2SAndroid Build Coastguard Worker 39*cc02d7e2SAndroid Build Coastguard Worker#define TEST_TIMEOUT 16 40*cc02d7e2SAndroid Build Coastguard Worker 41*cc02d7e2SAndroid Build Coastguard Worker// The server address is derived from preprocessor macro, which is 42*cc02d7e2SAndroid Build Coastguard Worker// in turn derived from environment variable of the same name. 43*cc02d7e2SAndroid Build Coastguard Worker#define NSStringize_helper(x) #x 44*cc02d7e2SAndroid Build Coastguard Worker#define NSStringize(x) @NSStringize_helper(x) 45*cc02d7e2SAndroid Build Coastguard Workerstatic NSString *const kPackage = @"grpc.testing"; 46*cc02d7e2SAndroid Build Coastguard Workerstatic NSString *const kService = @"TestService"; 47*cc02d7e2SAndroid Build Coastguard Worker 48*cc02d7e2SAndroid Build Coastguard Workerstatic GRPCProtoMethod *kInexistentMethod; 49*cc02d7e2SAndroid Build Coastguard Workerstatic GRPCProtoMethod *kEmptyCallMethod; 50*cc02d7e2SAndroid Build Coastguard Workerstatic GRPCProtoMethod *kUnaryCallMethod; 51*cc02d7e2SAndroid Build Coastguard Workerstatic GRPCProtoMethod *kFullDuplexCallMethod; 52*cc02d7e2SAndroid Build Coastguard Worker 53*cc02d7e2SAndroid Build Coastguard Worker/** Observer class for testing that responseMetadata is KVO-compliant */ 54*cc02d7e2SAndroid Build Coastguard Worker@interface PassthroughObserver : NSObject 55*cc02d7e2SAndroid Build Coastguard Worker- (instancetype)initWithCallback:(void (^)(NSString *, id, NSDictionary *))callback 56*cc02d7e2SAndroid Build Coastguard Worker NS_DESIGNATED_INITIALIZER; 57*cc02d7e2SAndroid Build Coastguard Worker 58*cc02d7e2SAndroid Build Coastguard Worker- (void)observeValueForKeyPath:(NSString *)keyPath 59*cc02d7e2SAndroid Build Coastguard Worker ofObject:(id)object 60*cc02d7e2SAndroid Build Coastguard Worker change:(NSDictionary *)change 61*cc02d7e2SAndroid Build Coastguard Worker context:(void *)context; 62*cc02d7e2SAndroid Build Coastguard Worker@end 63*cc02d7e2SAndroid Build Coastguard Worker 64*cc02d7e2SAndroid Build Coastguard Worker@implementation PassthroughObserver { 65*cc02d7e2SAndroid Build Coastguard Worker void (^_callback)(NSString *, id, NSDictionary *); 66*cc02d7e2SAndroid Build Coastguard Worker} 67*cc02d7e2SAndroid Build Coastguard Worker 68*cc02d7e2SAndroid Build Coastguard Worker- (instancetype)init { 69*cc02d7e2SAndroid Build Coastguard Worker return [self initWithCallback:nil]; 70*cc02d7e2SAndroid Build Coastguard Worker} 71*cc02d7e2SAndroid Build Coastguard Worker 72*cc02d7e2SAndroid Build Coastguard Worker- (instancetype)initWithCallback:(void (^)(NSString *, id, NSDictionary *))callback { 73*cc02d7e2SAndroid Build Coastguard Worker if (!callback) { 74*cc02d7e2SAndroid Build Coastguard Worker return nil; 75*cc02d7e2SAndroid Build Coastguard Worker } 76*cc02d7e2SAndroid Build Coastguard Worker if ((self = [super init])) { 77*cc02d7e2SAndroid Build Coastguard Worker _callback = callback; 78*cc02d7e2SAndroid Build Coastguard Worker } 79*cc02d7e2SAndroid Build Coastguard Worker return self; 80*cc02d7e2SAndroid Build Coastguard Worker} 81*cc02d7e2SAndroid Build Coastguard Worker 82*cc02d7e2SAndroid Build Coastguard Worker- (void)observeValueForKeyPath:(NSString *)keyPath 83*cc02d7e2SAndroid Build Coastguard Worker ofObject:(id)object 84*cc02d7e2SAndroid Build Coastguard Worker change:(NSDictionary *)change 85*cc02d7e2SAndroid Build Coastguard Worker context:(void *)context { 86*cc02d7e2SAndroid Build Coastguard Worker _callback(keyPath, object, change); 87*cc02d7e2SAndroid Build Coastguard Worker [object removeObserver:self forKeyPath:keyPath]; 88*cc02d7e2SAndroid Build Coastguard Worker} 89*cc02d7e2SAndroid Build Coastguard Worker 90*cc02d7e2SAndroid Build Coastguard Worker@end 91*cc02d7e2SAndroid Build Coastguard Worker 92*cc02d7e2SAndroid Build Coastguard Worker#pragma mark Tests 93*cc02d7e2SAndroid Build Coastguard Worker 94*cc02d7e2SAndroid Build Coastguard Worker/** 95*cc02d7e2SAndroid Build Coastguard Worker * A few tests similar to InteropTests, but which use the generic gRPC client (GRPCCall) rather than 96*cc02d7e2SAndroid Build Coastguard Worker * a generated proto library on top of it. Its RPCs are sent to a local cleartext server. 97*cc02d7e2SAndroid Build Coastguard Worker * 98*cc02d7e2SAndroid Build Coastguard Worker * TODO(jcanizales): Run them also against a local SSL server and against a remote server. 99*cc02d7e2SAndroid Build Coastguard Worker */ 100*cc02d7e2SAndroid Build Coastguard Worker@interface GRPCClientTests : XCTestCase 101*cc02d7e2SAndroid Build Coastguard Worker@end 102*cc02d7e2SAndroid Build Coastguard Worker 103*cc02d7e2SAndroid Build Coastguard Worker@implementation GRPCClientTests 104*cc02d7e2SAndroid Build Coastguard Worker 105*cc02d7e2SAndroid Build Coastguard Worker+ (void)setUp { 106*cc02d7e2SAndroid Build Coastguard Worker NSLog(@"GRPCClientTests Started"); 107*cc02d7e2SAndroid Build Coastguard Worker} 108*cc02d7e2SAndroid Build Coastguard Worker 109*cc02d7e2SAndroid Build Coastguard Worker- (void)setUp { 110*cc02d7e2SAndroid Build Coastguard Worker // Add a custom user agent prefix and suffix that will be used in test 111*cc02d7e2SAndroid Build Coastguard Worker [GRPCCall setUserAgentPrefix:@"Foo" forHost:GRPCGetLocalInteropTestServerAddressPlainText()]; 112*cc02d7e2SAndroid Build Coastguard Worker [GRPCCall setUserAgentSuffix:@"Suffix" forHost:GRPCGetLocalInteropTestServerAddressPlainText()]; 113*cc02d7e2SAndroid Build Coastguard Worker // Register test server as non-SSL. 114*cc02d7e2SAndroid Build Coastguard Worker [GRPCCall useInsecureConnectionsForHost:GRPCGetLocalInteropTestServerAddressPlainText()]; 115*cc02d7e2SAndroid Build Coastguard Worker 116*cc02d7e2SAndroid Build Coastguard Worker // This method isn't implemented by the remote server. 117*cc02d7e2SAndroid Build Coastguard Worker kInexistentMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage 118*cc02d7e2SAndroid Build Coastguard Worker service:kService 119*cc02d7e2SAndroid Build Coastguard Worker method:@"Inexistent"]; 120*cc02d7e2SAndroid Build Coastguard Worker kEmptyCallMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage 121*cc02d7e2SAndroid Build Coastguard Worker service:kService 122*cc02d7e2SAndroid Build Coastguard Worker method:@"EmptyCall"]; 123*cc02d7e2SAndroid Build Coastguard Worker kUnaryCallMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage 124*cc02d7e2SAndroid Build Coastguard Worker service:kService 125*cc02d7e2SAndroid Build Coastguard Worker method:@"UnaryCall"]; 126*cc02d7e2SAndroid Build Coastguard Worker kFullDuplexCallMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage 127*cc02d7e2SAndroid Build Coastguard Worker service:kService 128*cc02d7e2SAndroid Build Coastguard Worker method:@"FullDuplexCall"]; 129*cc02d7e2SAndroid Build Coastguard Worker} 130*cc02d7e2SAndroid Build Coastguard Worker 131*cc02d7e2SAndroid Build Coastguard Worker- (void)testConnectionToRemoteServer { 132*cc02d7e2SAndroid Build Coastguard Worker __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Server reachable."]; 133*cc02d7e2SAndroid Build Coastguard Worker 134*cc02d7e2SAndroid Build Coastguard Worker GRPCCall *call = [[GRPCCall alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText() 135*cc02d7e2SAndroid Build Coastguard Worker path:kInexistentMethod.HTTPPath 136*cc02d7e2SAndroid Build Coastguard Worker requestsWriter:[GRXWriter writerWithValue:[NSData data]]]; 137*cc02d7e2SAndroid Build Coastguard Worker 138*cc02d7e2SAndroid Build Coastguard Worker id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] 139*cc02d7e2SAndroid Build Coastguard Worker initWithValueHandler:^(NSData *value) { 140*cc02d7e2SAndroid Build Coastguard Worker XCTFail(@"Received unexpected response: %@", value); 141*cc02d7e2SAndroid Build Coastguard Worker } 142*cc02d7e2SAndroid Build Coastguard Worker completionHandler:^(NSError *errorOrNil) { 143*cc02d7e2SAndroid Build Coastguard Worker XCTAssertNotNil(errorOrNil, @"Finished without error!"); 144*cc02d7e2SAndroid Build Coastguard Worker XCTAssertEqual(errorOrNil.code, 12, @"Finished with unexpected error: %@", errorOrNil); 145*cc02d7e2SAndroid Build Coastguard Worker [expectation fulfill]; 146*cc02d7e2SAndroid Build Coastguard Worker }]; 147*cc02d7e2SAndroid Build Coastguard Worker 148*cc02d7e2SAndroid Build Coastguard Worker [call startWithWriteable:responsesWriteable]; 149*cc02d7e2SAndroid Build Coastguard Worker 150*cc02d7e2SAndroid Build Coastguard Worker [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 151*cc02d7e2SAndroid Build Coastguard Worker} 152*cc02d7e2SAndroid Build Coastguard Worker 153*cc02d7e2SAndroid Build Coastguard Worker- (void)testEmptyRPC { 154*cc02d7e2SAndroid Build Coastguard Worker __weak XCTestExpectation *response = 155*cc02d7e2SAndroid Build Coastguard Worker [self expectationWithDescription:@"Empty response received."]; 156*cc02d7e2SAndroid Build Coastguard Worker __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."]; 157*cc02d7e2SAndroid Build Coastguard Worker 158*cc02d7e2SAndroid Build Coastguard Worker GRPCCall *call = [[GRPCCall alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText() 159*cc02d7e2SAndroid Build Coastguard Worker path:kEmptyCallMethod.HTTPPath 160*cc02d7e2SAndroid Build Coastguard Worker requestsWriter:[GRXWriter writerWithValue:[NSData data]]]; 161*cc02d7e2SAndroid Build Coastguard Worker 162*cc02d7e2SAndroid Build Coastguard Worker id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] 163*cc02d7e2SAndroid Build Coastguard Worker initWithValueHandler:^(NSData *value) { 164*cc02d7e2SAndroid Build Coastguard Worker XCTAssertNotNil(value, @"nil value received as response."); 165*cc02d7e2SAndroid Build Coastguard Worker XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value); 166*cc02d7e2SAndroid Build Coastguard Worker [response fulfill]; 167*cc02d7e2SAndroid Build Coastguard Worker } 168*cc02d7e2SAndroid Build Coastguard Worker completionHandler:^(NSError *errorOrNil) { 169*cc02d7e2SAndroid Build Coastguard Worker XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil); 170*cc02d7e2SAndroid Build Coastguard Worker [completion fulfill]; 171*cc02d7e2SAndroid Build Coastguard Worker }]; 172*cc02d7e2SAndroid Build Coastguard Worker 173*cc02d7e2SAndroid Build Coastguard Worker [call startWithWriteable:responsesWriteable]; 174*cc02d7e2SAndroid Build Coastguard Worker 175*cc02d7e2SAndroid Build Coastguard Worker [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 176*cc02d7e2SAndroid Build Coastguard Worker} 177*cc02d7e2SAndroid Build Coastguard Worker 178*cc02d7e2SAndroid Build Coastguard Worker- (void)testSimpleProtoRPC { 179*cc02d7e2SAndroid Build Coastguard Worker __weak XCTestExpectation *response = [self expectationWithDescription:@"Expected response."]; 180*cc02d7e2SAndroid Build Coastguard Worker __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."]; 181*cc02d7e2SAndroid Build Coastguard Worker 182*cc02d7e2SAndroid Build Coastguard Worker RMTSimpleRequest *request = [RMTSimpleRequest message]; 183*cc02d7e2SAndroid Build Coastguard Worker request.responseSize = 100; 184*cc02d7e2SAndroid Build Coastguard Worker request.fillUsername = YES; 185*cc02d7e2SAndroid Build Coastguard Worker request.fillOauthScope = YES; 186*cc02d7e2SAndroid Build Coastguard Worker GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]]; 187*cc02d7e2SAndroid Build Coastguard Worker 188*cc02d7e2SAndroid Build Coastguard Worker GRPCCall *call = [[GRPCCall alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText() 189*cc02d7e2SAndroid Build Coastguard Worker path:kUnaryCallMethod.HTTPPath 190*cc02d7e2SAndroid Build Coastguard Worker requestsWriter:requestsWriter]; 191*cc02d7e2SAndroid Build Coastguard Worker 192*cc02d7e2SAndroid Build Coastguard Worker id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] 193*cc02d7e2SAndroid Build Coastguard Worker initWithValueHandler:^(NSData *value) { 194*cc02d7e2SAndroid Build Coastguard Worker XCTAssertNotNil(value, @"nil value received as response."); 195*cc02d7e2SAndroid Build Coastguard Worker XCTAssertGreaterThan(value.length, 0, @"Empty response received."); 196*cc02d7e2SAndroid Build Coastguard Worker RMTSimpleResponse *responseProto = [RMTSimpleResponse parseFromData:value error:NULL]; 197*cc02d7e2SAndroid Build Coastguard Worker // We expect empty strings, not nil: 198*cc02d7e2SAndroid Build Coastguard Worker XCTAssertNotNil(responseProto.username, @"Response's username is nil."); 199*cc02d7e2SAndroid Build Coastguard Worker XCTAssertNotNil(responseProto.oauthScope, @"Response's OAuth scope is nil."); 200*cc02d7e2SAndroid Build Coastguard Worker [response fulfill]; 201*cc02d7e2SAndroid Build Coastguard Worker } 202*cc02d7e2SAndroid Build Coastguard Worker completionHandler:^(NSError *errorOrNil) { 203*cc02d7e2SAndroid Build Coastguard Worker XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil); 204*cc02d7e2SAndroid Build Coastguard Worker [completion fulfill]; 205*cc02d7e2SAndroid Build Coastguard Worker }]; 206*cc02d7e2SAndroid Build Coastguard Worker 207*cc02d7e2SAndroid Build Coastguard Worker [call startWithWriteable:responsesWriteable]; 208*cc02d7e2SAndroid Build Coastguard Worker 209*cc02d7e2SAndroid Build Coastguard Worker [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 210*cc02d7e2SAndroid Build Coastguard Worker} 211*cc02d7e2SAndroid Build Coastguard Worker 212*cc02d7e2SAndroid Build Coastguard Worker- (void)testResponseMetadataKVO { 213*cc02d7e2SAndroid Build Coastguard Worker __weak XCTestExpectation *response = 214*cc02d7e2SAndroid Build Coastguard Worker [self expectationWithDescription:@"Empty response received."]; 215*cc02d7e2SAndroid Build Coastguard Worker __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."]; 216*cc02d7e2SAndroid Build Coastguard Worker __weak XCTestExpectation *metadata = [self expectationWithDescription:@"Metadata changed."]; 217*cc02d7e2SAndroid Build Coastguard Worker 218*cc02d7e2SAndroid Build Coastguard Worker GRPCCall *call = [[GRPCCall alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText() 219*cc02d7e2SAndroid Build Coastguard Worker path:kEmptyCallMethod.HTTPPath 220*cc02d7e2SAndroid Build Coastguard Worker requestsWriter:[GRXWriter writerWithValue:[NSData data]]]; 221*cc02d7e2SAndroid Build Coastguard Worker 222*cc02d7e2SAndroid Build Coastguard Worker PassthroughObserver *observer = [[PassthroughObserver alloc] 223*cc02d7e2SAndroid Build Coastguard Worker initWithCallback:^(NSString *keypath, id object, NSDictionary *change) { 224*cc02d7e2SAndroid Build Coastguard Worker if ([keypath isEqual:@"responseHeaders"]) { 225*cc02d7e2SAndroid Build Coastguard Worker [metadata fulfill]; 226*cc02d7e2SAndroid Build Coastguard Worker } 227*cc02d7e2SAndroid Build Coastguard Worker }]; 228*cc02d7e2SAndroid Build Coastguard Worker 229*cc02d7e2SAndroid Build Coastguard Worker [call addObserver:observer forKeyPath:@"responseHeaders" options:0 context:NULL]; 230*cc02d7e2SAndroid Build Coastguard Worker 231*cc02d7e2SAndroid Build Coastguard Worker id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] 232*cc02d7e2SAndroid Build Coastguard Worker initWithValueHandler:^(NSData *value) { 233*cc02d7e2SAndroid Build Coastguard Worker XCTAssertNotNil(value, @"nil value received as response."); 234*cc02d7e2SAndroid Build Coastguard Worker XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value); 235*cc02d7e2SAndroid Build Coastguard Worker [response fulfill]; 236*cc02d7e2SAndroid Build Coastguard Worker } 237*cc02d7e2SAndroid Build Coastguard Worker completionHandler:^(NSError *errorOrNil) { 238*cc02d7e2SAndroid Build Coastguard Worker XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil); 239*cc02d7e2SAndroid Build Coastguard Worker [completion fulfill]; 240*cc02d7e2SAndroid Build Coastguard Worker }]; 241*cc02d7e2SAndroid Build Coastguard Worker 242*cc02d7e2SAndroid Build Coastguard Worker [call startWithWriteable:responsesWriteable]; 243*cc02d7e2SAndroid Build Coastguard Worker 244*cc02d7e2SAndroid Build Coastguard Worker [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 245*cc02d7e2SAndroid Build Coastguard Worker} 246*cc02d7e2SAndroid Build Coastguard Worker 247*cc02d7e2SAndroid Build Coastguard Worker- (void)testUserAgentPrefixAndSuffix { 248*cc02d7e2SAndroid Build Coastguard Worker __weak XCTestExpectation *response = 249*cc02d7e2SAndroid Build Coastguard Worker [self expectationWithDescription:@"Empty response received."]; 250*cc02d7e2SAndroid Build Coastguard Worker __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."]; 251*cc02d7e2SAndroid Build Coastguard Worker 252*cc02d7e2SAndroid Build Coastguard Worker GRPCCall *call = [[GRPCCall alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText() 253*cc02d7e2SAndroid Build Coastguard Worker path:kEmptyCallMethod.HTTPPath 254*cc02d7e2SAndroid Build Coastguard Worker requestsWriter:[GRXWriter writerWithValue:[NSData data]]]; 255*cc02d7e2SAndroid Build Coastguard Worker // Setting this special key in the header will cause the interop server to echo back the 256*cc02d7e2SAndroid Build Coastguard Worker // user-agent value, which we confirm. 257*cc02d7e2SAndroid Build Coastguard Worker call.requestHeaders[@"x-grpc-test-echo-useragent"] = @""; 258*cc02d7e2SAndroid Build Coastguard Worker 259*cc02d7e2SAndroid Build Coastguard Worker id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] 260*cc02d7e2SAndroid Build Coastguard Worker initWithValueHandler:^(NSData *value) { 261*cc02d7e2SAndroid Build Coastguard Worker XCTAssertNotNil(value, @"nil value received as response."); 262*cc02d7e2SAndroid Build Coastguard Worker XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value); 263*cc02d7e2SAndroid Build Coastguard Worker 264*cc02d7e2SAndroid Build Coastguard Worker NSString *userAgent = call.responseHeaders[@"x-grpc-test-echo-useragent"]; 265*cc02d7e2SAndroid Build Coastguard Worker NSError *error = nil; 266*cc02d7e2SAndroid Build Coastguard Worker 267*cc02d7e2SAndroid Build Coastguard Worker // Test the regex is correct 268*cc02d7e2SAndroid Build Coastguard Worker NSString *expectedUserAgent = @"Foo grpc-objc-cfstream/"; 269*cc02d7e2SAndroid Build Coastguard Worker expectedUserAgent = [expectedUserAgent stringByAppendingString:GRPC_OBJC_VERSION_STRING]; 270*cc02d7e2SAndroid Build Coastguard Worker expectedUserAgent = [expectedUserAgent stringByAppendingString:@" Suffix"]; 271*cc02d7e2SAndroid Build Coastguard Worker expectedUserAgent = [expectedUserAgent stringByAppendingString:@" grpc-c/"]; 272*cc02d7e2SAndroid Build Coastguard Worker expectedUserAgent = [expectedUserAgent stringByAppendingString:GRPC_C_VERSION_STRING]; 273*cc02d7e2SAndroid Build Coastguard Worker expectedUserAgent = [expectedUserAgent stringByAppendingString:@" ("]; 274*cc02d7e2SAndroid Build Coastguard Worker expectedUserAgent = [expectedUserAgent stringByAppendingString:@GPR_PLATFORM_STRING]; 275*cc02d7e2SAndroid Build Coastguard Worker expectedUserAgent = [expectedUserAgent stringByAppendingString:@"; chttp2)"]; 276*cc02d7e2SAndroid Build Coastguard Worker XCTAssertEqualObjects(userAgent, expectedUserAgent); 277*cc02d7e2SAndroid Build Coastguard Worker 278*cc02d7e2SAndroid Build Coastguard Worker // Change in format of user-agent field in a direction that does not match the regex will 279*cc02d7e2SAndroid Build Coastguard Worker // likely cause problem for certain gRPC users. For details, refer to internal doc 280*cc02d7e2SAndroid Build Coastguard Worker // https://goo.gl/c2diBc 281*cc02d7e2SAndroid Build Coastguard Worker NSRegularExpression *regex = [NSRegularExpression 282*cc02d7e2SAndroid Build Coastguard Worker regularExpressionWithPattern:@" grpc-[a-zA-Z0-9]+(-[a-zA-Z0-9]+)?/[^ ,]+( \\([^)]*\\))?" 283*cc02d7e2SAndroid Build Coastguard Worker options:0 284*cc02d7e2SAndroid Build Coastguard Worker error:&error]; 285*cc02d7e2SAndroid Build Coastguard Worker NSString *customUserAgent = 286*cc02d7e2SAndroid Build Coastguard Worker [regex stringByReplacingMatchesInString:userAgent 287*cc02d7e2SAndroid Build Coastguard Worker options:0 288*cc02d7e2SAndroid Build Coastguard Worker range:NSMakeRange(0, [userAgent length]) 289*cc02d7e2SAndroid Build Coastguard Worker withTemplate:@""]; 290*cc02d7e2SAndroid Build Coastguard Worker 291*cc02d7e2SAndroid Build Coastguard Worker NSArray *userAgentArray = [customUserAgent componentsSeparatedByString:@" "]; 292*cc02d7e2SAndroid Build Coastguard Worker XCTAssertEqual([userAgentArray count], 2); 293*cc02d7e2SAndroid Build Coastguard Worker XCTAssertEqualObjects([userAgentArray objectAtIndex:0], @"Foo"); 294*cc02d7e2SAndroid Build Coastguard Worker XCTAssertEqualObjects([userAgentArray objectAtIndex:1], @"Suffix"); 295*cc02d7e2SAndroid Build Coastguard Worker [response fulfill]; 296*cc02d7e2SAndroid Build Coastguard Worker } 297*cc02d7e2SAndroid Build Coastguard Worker completionHandler:^(NSError *errorOrNil) { 298*cc02d7e2SAndroid Build Coastguard Worker XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil); 299*cc02d7e2SAndroid Build Coastguard Worker [completion fulfill]; 300*cc02d7e2SAndroid Build Coastguard Worker }]; 301*cc02d7e2SAndroid Build Coastguard Worker 302*cc02d7e2SAndroid Build Coastguard Worker [call startWithWriteable:responsesWriteable]; 303*cc02d7e2SAndroid Build Coastguard Worker 304*cc02d7e2SAndroid Build Coastguard Worker [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 305*cc02d7e2SAndroid Build Coastguard Worker} 306*cc02d7e2SAndroid Build Coastguard Worker 307*cc02d7e2SAndroid Build Coastguard Worker- (void)testTrailers { 308*cc02d7e2SAndroid Build Coastguard Worker __weak XCTestExpectation *response = 309*cc02d7e2SAndroid Build Coastguard Worker [self expectationWithDescription:@"Empty response received."]; 310*cc02d7e2SAndroid Build Coastguard Worker __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."]; 311*cc02d7e2SAndroid Build Coastguard Worker 312*cc02d7e2SAndroid Build Coastguard Worker GRPCCall *call = [[GRPCCall alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText() 313*cc02d7e2SAndroid Build Coastguard Worker path:kEmptyCallMethod.HTTPPath 314*cc02d7e2SAndroid Build Coastguard Worker requestsWriter:[GRXWriter writerWithValue:[NSData data]]]; 315*cc02d7e2SAndroid Build Coastguard Worker // Setting this special key in the header will cause the interop server to echo back the 316*cc02d7e2SAndroid Build Coastguard Worker // trailer data. 317*cc02d7e2SAndroid Build Coastguard Worker const unsigned char raw_bytes[] = {1, 2, 3, 4}; 318*cc02d7e2SAndroid Build Coastguard Worker NSData *trailer_data = [NSData dataWithBytes:raw_bytes length:sizeof(raw_bytes)]; 319*cc02d7e2SAndroid Build Coastguard Worker call.requestHeaders[@"x-grpc-test-echo-trailing-bin"] = trailer_data; 320*cc02d7e2SAndroid Build Coastguard Worker 321*cc02d7e2SAndroid Build Coastguard Worker id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] 322*cc02d7e2SAndroid Build Coastguard Worker initWithValueHandler:^(NSData *value) { 323*cc02d7e2SAndroid Build Coastguard Worker XCTAssertNotNil(value, @"nil value received as response."); 324*cc02d7e2SAndroid Build Coastguard Worker XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value); 325*cc02d7e2SAndroid Build Coastguard Worker [response fulfill]; 326*cc02d7e2SAndroid Build Coastguard Worker } 327*cc02d7e2SAndroid Build Coastguard Worker completionHandler:^(NSError *errorOrNil) { 328*cc02d7e2SAndroid Build Coastguard Worker XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil); 329*cc02d7e2SAndroid Build Coastguard Worker XCTAssertEqualObjects((NSData *)call.responseTrailers[@"x-grpc-test-echo-trailing-bin"], 330*cc02d7e2SAndroid Build Coastguard Worker trailer_data, @"Did not receive expected trailer"); 331*cc02d7e2SAndroid Build Coastguard Worker [completion fulfill]; 332*cc02d7e2SAndroid Build Coastguard Worker }]; 333*cc02d7e2SAndroid Build Coastguard Worker 334*cc02d7e2SAndroid Build Coastguard Worker [call startWithWriteable:responsesWriteable]; 335*cc02d7e2SAndroid Build Coastguard Worker [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 336*cc02d7e2SAndroid Build Coastguard Worker} 337*cc02d7e2SAndroid Build Coastguard Worker 338*cc02d7e2SAndroid Build Coastguard Worker// TODO(makarandd): Move to a different file that contains only unit tests 339*cc02d7e2SAndroid Build Coastguard Worker- (void)testExceptions { 340*cc02d7e2SAndroid Build Coastguard Worker GRXWriter *writer = [GRXWriter writerWithValue:[NSData data]]; 341*cc02d7e2SAndroid Build Coastguard Worker // Try to set parameters to nil for GRPCCall. This should cause an exception 342*cc02d7e2SAndroid Build Coastguard Worker @try { 343*cc02d7e2SAndroid Build Coastguard Worker (void)[[GRPCCall alloc] initWithHost:nil path:nil requestsWriter:writer]; 344*cc02d7e2SAndroid Build Coastguard Worker XCTFail(@"Did not receive an exception when parameters are nil"); 345*cc02d7e2SAndroid Build Coastguard Worker } @catch (NSException *theException) { 346*cc02d7e2SAndroid Build Coastguard Worker NSLog(@"Received exception as expected: %@", theException.name); 347*cc02d7e2SAndroid Build Coastguard Worker } 348*cc02d7e2SAndroid Build Coastguard Worker 349*cc02d7e2SAndroid Build Coastguard Worker // Set state to Finished by force 350*cc02d7e2SAndroid Build Coastguard Worker GRXWriter *requestsWriter = [GRXWriter emptyWriter]; 351*cc02d7e2SAndroid Build Coastguard Worker [requestsWriter finishWithError:nil]; 352*cc02d7e2SAndroid Build Coastguard Worker @try { 353*cc02d7e2SAndroid Build Coastguard Worker (void)[[GRPCCall alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText() 354*cc02d7e2SAndroid Build Coastguard Worker path:kUnaryCallMethod.HTTPPath 355*cc02d7e2SAndroid Build Coastguard Worker requestsWriter:requestsWriter]; 356*cc02d7e2SAndroid Build Coastguard Worker XCTFail(@"Did not receive an exception when GRXWriter has incorrect state."); 357*cc02d7e2SAndroid Build Coastguard Worker } @catch (NSException *theException) { 358*cc02d7e2SAndroid Build Coastguard Worker NSLog(@"Received exception as expected: %@", theException.name); 359*cc02d7e2SAndroid Build Coastguard Worker } 360*cc02d7e2SAndroid Build Coastguard Worker} 361*cc02d7e2SAndroid Build Coastguard Worker 362*cc02d7e2SAndroid Build Coastguard Worker- (void)testAlternateDispatchQueue { 363*cc02d7e2SAndroid Build Coastguard Worker const int32_t kPayloadSize = 100; 364*cc02d7e2SAndroid Build Coastguard Worker RMTSimpleRequest *request = [RMTSimpleRequest message]; 365*cc02d7e2SAndroid Build Coastguard Worker request.responseSize = kPayloadSize; 366*cc02d7e2SAndroid Build Coastguard Worker 367*cc02d7e2SAndroid Build Coastguard Worker __weak XCTestExpectation *expectation1 = 368*cc02d7e2SAndroid Build Coastguard Worker [self expectationWithDescription:@"AlternateDispatchQueue1"]; 369*cc02d7e2SAndroid Build Coastguard Worker 370*cc02d7e2SAndroid Build Coastguard Worker // Use default (main) dispatch queue 371*cc02d7e2SAndroid Build Coastguard Worker NSString *main_queue_label = 372*cc02d7e2SAndroid Build Coastguard Worker [NSString stringWithUTF8String:dispatch_queue_get_label(dispatch_get_main_queue())]; 373*cc02d7e2SAndroid Build Coastguard Worker 374*cc02d7e2SAndroid Build Coastguard Worker GRXWriter *requestsWriter1 = [GRXWriter writerWithValue:[request data]]; 375*cc02d7e2SAndroid Build Coastguard Worker 376*cc02d7e2SAndroid Build Coastguard Worker GRPCCall *call1 = [[GRPCCall alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText() 377*cc02d7e2SAndroid Build Coastguard Worker path:kUnaryCallMethod.HTTPPath 378*cc02d7e2SAndroid Build Coastguard Worker requestsWriter:requestsWriter1]; 379*cc02d7e2SAndroid Build Coastguard Worker 380*cc02d7e2SAndroid Build Coastguard Worker id<GRXWriteable> responsesWriteable1 = [[GRXWriteable alloc] 381*cc02d7e2SAndroid Build Coastguard Worker initWithValueHandler:^(NSData *value) { 382*cc02d7e2SAndroid Build Coastguard Worker NSString *label = 383*cc02d7e2SAndroid Build Coastguard Worker [NSString stringWithUTF8String:dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)]; 384*cc02d7e2SAndroid Build Coastguard Worker XCTAssert([label isEqualToString:main_queue_label]); 385*cc02d7e2SAndroid Build Coastguard Worker 386*cc02d7e2SAndroid Build Coastguard Worker [expectation1 fulfill]; 387*cc02d7e2SAndroid Build Coastguard Worker } 388*cc02d7e2SAndroid Build Coastguard Worker completionHandler:^(NSError *errorOrNil){ 389*cc02d7e2SAndroid Build Coastguard Worker }]; 390*cc02d7e2SAndroid Build Coastguard Worker 391*cc02d7e2SAndroid Build Coastguard Worker [call1 startWithWriteable:responsesWriteable1]; 392*cc02d7e2SAndroid Build Coastguard Worker 393*cc02d7e2SAndroid Build Coastguard Worker [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 394*cc02d7e2SAndroid Build Coastguard Worker 395*cc02d7e2SAndroid Build Coastguard Worker // Use a custom queue 396*cc02d7e2SAndroid Build Coastguard Worker __weak XCTestExpectation *expectation2 = 397*cc02d7e2SAndroid Build Coastguard Worker [self expectationWithDescription:@"AlternateDispatchQueue2"]; 398*cc02d7e2SAndroid Build Coastguard Worker 399*cc02d7e2SAndroid Build Coastguard Worker NSString *queue_label = @"test.queue1"; 400*cc02d7e2SAndroid Build Coastguard Worker dispatch_queue_t queue = dispatch_queue_create([queue_label UTF8String], DISPATCH_QUEUE_SERIAL); 401*cc02d7e2SAndroid Build Coastguard Worker 402*cc02d7e2SAndroid Build Coastguard Worker GRXWriter *requestsWriter2 = [GRXWriter writerWithValue:[request data]]; 403*cc02d7e2SAndroid Build Coastguard Worker 404*cc02d7e2SAndroid Build Coastguard Worker GRPCCall *call2 = [[GRPCCall alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText() 405*cc02d7e2SAndroid Build Coastguard Worker path:kUnaryCallMethod.HTTPPath 406*cc02d7e2SAndroid Build Coastguard Worker requestsWriter:requestsWriter2]; 407*cc02d7e2SAndroid Build Coastguard Worker 408*cc02d7e2SAndroid Build Coastguard Worker [call2 setResponseDispatchQueue:queue]; 409*cc02d7e2SAndroid Build Coastguard Worker 410*cc02d7e2SAndroid Build Coastguard Worker id<GRXWriteable> responsesWriteable2 = [[GRXWriteable alloc] 411*cc02d7e2SAndroid Build Coastguard Worker initWithValueHandler:^(NSData *value) { 412*cc02d7e2SAndroid Build Coastguard Worker NSString *label = 413*cc02d7e2SAndroid Build Coastguard Worker [NSString stringWithUTF8String:dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)]; 414*cc02d7e2SAndroid Build Coastguard Worker XCTAssert([label isEqualToString:queue_label]); 415*cc02d7e2SAndroid Build Coastguard Worker 416*cc02d7e2SAndroid Build Coastguard Worker [expectation2 fulfill]; 417*cc02d7e2SAndroid Build Coastguard Worker } 418*cc02d7e2SAndroid Build Coastguard Worker completionHandler:^(NSError *errorOrNil){ 419*cc02d7e2SAndroid Build Coastguard Worker }]; 420*cc02d7e2SAndroid Build Coastguard Worker 421*cc02d7e2SAndroid Build Coastguard Worker [call2 startWithWriteable:responsesWriteable2]; 422*cc02d7e2SAndroid Build Coastguard Worker 423*cc02d7e2SAndroid Build Coastguard Worker [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 424*cc02d7e2SAndroid Build Coastguard Worker} 425*cc02d7e2SAndroid Build Coastguard Worker 426*cc02d7e2SAndroid Build Coastguard Worker- (void)testTimeout { 427*cc02d7e2SAndroid Build Coastguard Worker __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."]; 428*cc02d7e2SAndroid Build Coastguard Worker 429*cc02d7e2SAndroid Build Coastguard Worker GRXBufferedPipe *pipe = [GRXBufferedPipe pipe]; 430*cc02d7e2SAndroid Build Coastguard Worker GRPCCall *call = [[GRPCCall alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText() 431*cc02d7e2SAndroid Build Coastguard Worker path:kFullDuplexCallMethod.HTTPPath 432*cc02d7e2SAndroid Build Coastguard Worker requestsWriter:pipe]; 433*cc02d7e2SAndroid Build Coastguard Worker 434*cc02d7e2SAndroid Build Coastguard Worker id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] 435*cc02d7e2SAndroid Build Coastguard Worker initWithValueHandler:^(NSData *value) { 436*cc02d7e2SAndroid Build Coastguard Worker XCTAssert(0, @"Failure: response received; Expect: no response received."); 437*cc02d7e2SAndroid Build Coastguard Worker } 438*cc02d7e2SAndroid Build Coastguard Worker completionHandler:^(NSError *errorOrNil) { 439*cc02d7e2SAndroid Build Coastguard Worker XCTAssertNotNil(errorOrNil, 440*cc02d7e2SAndroid Build Coastguard Worker @"Failure: no error received; Expect: receive deadline exceeded."); 441*cc02d7e2SAndroid Build Coastguard Worker XCTAssertEqual(errorOrNil.code, GRPCErrorCodeDeadlineExceeded); 442*cc02d7e2SAndroid Build Coastguard Worker [completion fulfill]; 443*cc02d7e2SAndroid Build Coastguard Worker }]; 444*cc02d7e2SAndroid Build Coastguard Worker 445*cc02d7e2SAndroid Build Coastguard Worker call.timeout = 0.001; 446*cc02d7e2SAndroid Build Coastguard Worker [call startWithWriteable:responsesWriteable]; 447*cc02d7e2SAndroid Build Coastguard Worker 448*cc02d7e2SAndroid Build Coastguard Worker [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 449*cc02d7e2SAndroid Build Coastguard Worker} 450*cc02d7e2SAndroid Build Coastguard Worker 451*cc02d7e2SAndroid Build Coastguard Worker- (int)findFreePort { 452*cc02d7e2SAndroid Build Coastguard Worker struct sockaddr_in addr; 453*cc02d7e2SAndroid Build Coastguard Worker unsigned int addr_len = sizeof(addr); 454*cc02d7e2SAndroid Build Coastguard Worker memset(&addr, 0, sizeof(addr)); 455*cc02d7e2SAndroid Build Coastguard Worker addr.sin_family = AF_INET; 456*cc02d7e2SAndroid Build Coastguard Worker int fd = socket(AF_INET, SOCK_STREAM, 0); 457*cc02d7e2SAndroid Build Coastguard Worker XCTAssertEqual(bind(fd, (struct sockaddr *)&addr, sizeof(addr)), 0); 458*cc02d7e2SAndroid Build Coastguard Worker XCTAssertEqual(getsockname(fd, (struct sockaddr *)&addr, &addr_len), 0); 459*cc02d7e2SAndroid Build Coastguard Worker XCTAssertEqual(addr_len, sizeof(addr)); 460*cc02d7e2SAndroid Build Coastguard Worker close(fd); 461*cc02d7e2SAndroid Build Coastguard Worker return addr.sin_port; 462*cc02d7e2SAndroid Build Coastguard Worker} 463*cc02d7e2SAndroid Build Coastguard Worker 464*cc02d7e2SAndroid Build Coastguard Worker- (void)testErrorCode { 465*cc02d7e2SAndroid Build Coastguard Worker int port = [self findFreePort]; 466*cc02d7e2SAndroid Build Coastguard Worker NSString *const kPhonyAddress = [NSString stringWithFormat:@"localhost:%d", port]; 467*cc02d7e2SAndroid Build Coastguard Worker __weak XCTestExpectation *completion = 468*cc02d7e2SAndroid Build Coastguard Worker [self expectationWithDescription:@"Received correct error code."]; 469*cc02d7e2SAndroid Build Coastguard Worker 470*cc02d7e2SAndroid Build Coastguard Worker GRPCCall *call = [[GRPCCall alloc] initWithHost:kPhonyAddress 471*cc02d7e2SAndroid Build Coastguard Worker path:kEmptyCallMethod.HTTPPath 472*cc02d7e2SAndroid Build Coastguard Worker requestsWriter:[GRXWriter writerWithValue:[NSData data]]]; 473*cc02d7e2SAndroid Build Coastguard Worker 474*cc02d7e2SAndroid Build Coastguard Worker id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] 475*cc02d7e2SAndroid Build Coastguard Worker initWithValueHandler:^(NSData *value) { 476*cc02d7e2SAndroid Build Coastguard Worker // Should not reach here 477*cc02d7e2SAndroid Build Coastguard Worker XCTAssert(NO); 478*cc02d7e2SAndroid Build Coastguard Worker } 479*cc02d7e2SAndroid Build Coastguard Worker completionHandler:^(NSError *errorOrNil) { 480*cc02d7e2SAndroid Build Coastguard Worker XCTAssertNotNil(errorOrNil, @"Finished with no error"); 481*cc02d7e2SAndroid Build Coastguard Worker XCTAssertEqual(errorOrNil.code, GRPC_STATUS_UNAVAILABLE); 482*cc02d7e2SAndroid Build Coastguard Worker [completion fulfill]; 483*cc02d7e2SAndroid Build Coastguard Worker }]; 484*cc02d7e2SAndroid Build Coastguard Worker 485*cc02d7e2SAndroid Build Coastguard Worker [call startWithWriteable:responsesWriteable]; 486*cc02d7e2SAndroid Build Coastguard Worker 487*cc02d7e2SAndroid Build Coastguard Worker [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 488*cc02d7e2SAndroid Build Coastguard Worker} 489*cc02d7e2SAndroid Build Coastguard Worker 490*cc02d7e2SAndroid Build Coastguard Worker- (void)testTimeoutBackoffWithTimeout:(double)timeout Backoff:(double)backoff { 491*cc02d7e2SAndroid Build Coastguard Worker const double maxConnectTime = timeout > backoff ? timeout : backoff; 492*cc02d7e2SAndroid Build Coastguard Worker const double kMargin = 0.2; 493*cc02d7e2SAndroid Build Coastguard Worker 494*cc02d7e2SAndroid Build Coastguard Worker __weak XCTestExpectation *completion = [self expectationWithDescription:@"Timeout in a second."]; 495*cc02d7e2SAndroid Build Coastguard Worker NSString *const kPhonyAddress = [NSString stringWithFormat:@"8.8.8.8:1"]; 496*cc02d7e2SAndroid Build Coastguard Worker [GRPCCall useInsecureConnectionsForHost:kPhonyAddress]; 497*cc02d7e2SAndroid Build Coastguard Worker [GRPCCall setMinConnectTimeout:timeout * 1000 498*cc02d7e2SAndroid Build Coastguard Worker initialBackoff:backoff * 1000 499*cc02d7e2SAndroid Build Coastguard Worker maxBackoff:0 500*cc02d7e2SAndroid Build Coastguard Worker forHost:kPhonyAddress]; 501*cc02d7e2SAndroid Build Coastguard Worker GRPCCall *call = [[GRPCCall alloc] initWithHost:kPhonyAddress 502*cc02d7e2SAndroid Build Coastguard Worker path:@"/phonyPath" 503*cc02d7e2SAndroid Build Coastguard Worker requestsWriter:[GRXWriter writerWithValue:[NSData data]]]; 504*cc02d7e2SAndroid Build Coastguard Worker NSDate *startTime = [NSDate date]; 505*cc02d7e2SAndroid Build Coastguard Worker id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] 506*cc02d7e2SAndroid Build Coastguard Worker initWithValueHandler:^(id value) { 507*cc02d7e2SAndroid Build Coastguard Worker XCTAssert(NO, @"Received message. Should not reach here"); 508*cc02d7e2SAndroid Build Coastguard Worker } 509*cc02d7e2SAndroid Build Coastguard Worker completionHandler:^(NSError *errorOrNil) { 510*cc02d7e2SAndroid Build Coastguard Worker XCTAssertNotNil(errorOrNil, @"Finished with no error"); 511*cc02d7e2SAndroid Build Coastguard Worker // The call must fail before maxConnectTime. However there is no lower bound on the time 512*cc02d7e2SAndroid Build Coastguard Worker // taken for connection. A shorter time happens when connection is actively refused 513*cc02d7e2SAndroid Build Coastguard Worker // by 8.8.8.8:1 before maxConnectTime elapsed. 514*cc02d7e2SAndroid Build Coastguard Worker XCTAssertLessThan([[NSDate date] timeIntervalSinceDate:startTime], 515*cc02d7e2SAndroid Build Coastguard Worker maxConnectTime + kMargin); 516*cc02d7e2SAndroid Build Coastguard Worker [completion fulfill]; 517*cc02d7e2SAndroid Build Coastguard Worker }]; 518*cc02d7e2SAndroid Build Coastguard Worker 519*cc02d7e2SAndroid Build Coastguard Worker [call startWithWriteable:responsesWriteable]; 520*cc02d7e2SAndroid Build Coastguard Worker 521*cc02d7e2SAndroid Build Coastguard Worker [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 522*cc02d7e2SAndroid Build Coastguard Worker} 523*cc02d7e2SAndroid Build Coastguard Worker 524*cc02d7e2SAndroid Build Coastguard Worker// The numbers of the following three tests are selected to be smaller than the default values of 525*cc02d7e2SAndroid Build Coastguard Worker// initial backoff (1s) and min_connect_timeout (20s), so that if they fail we know the default 526*cc02d7e2SAndroid Build Coastguard Worker// values fail to be overridden by the channel args. 527*cc02d7e2SAndroid Build Coastguard Worker- (void)testTimeoutBackoff1 { 528*cc02d7e2SAndroid Build Coastguard Worker [self testTimeoutBackoffWithTimeout:0.7 Backoff:0.3]; 529*cc02d7e2SAndroid Build Coastguard Worker} 530*cc02d7e2SAndroid Build Coastguard Worker 531*cc02d7e2SAndroid Build Coastguard Worker- (void)testTimeoutBackoff2 { 532*cc02d7e2SAndroid Build Coastguard Worker [self testTimeoutBackoffWithTimeout:0.3 Backoff:0.7]; 533*cc02d7e2SAndroid Build Coastguard Worker} 534*cc02d7e2SAndroid Build Coastguard Worker 535*cc02d7e2SAndroid Build Coastguard Worker@end 536