xref: /aosp_15_r20/external/grpc-grpc/src/objective-c/tests/UnitTests/GRPCClientTests.m (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
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