3 * Copyright 2015 gRPC authors.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
19 #import <XCTest/XCTest.h>
21 #import <grpc/support/port_platform.h>
23 #import <GRPCClient/GRPCCall+ChannelArg.h>
24 #import <GRPCClient/GRPCCall+OAuth2.h>
25 #import <GRPCClient/GRPCCall+Tests.h>
26 #import <GRPCClient/GRPCCall.h>
27 #import <GRPCClient/internal_testing/GRPCCall+InternalTests.h>
28 #import <ProtoRPC/ProtoMethod.h>
29 #import <RemoteTest/Messages.pbobjc.h>
30 #import <RxLibrary/GRXBufferedPipe.h>
31 #import <RxLibrary/GRXWriteable.h>
32 #import <RxLibrary/GRXWriter+Immediate.h>
34 #include <netinet/in.h>
36 #import "../version.h"
38 #define TEST_TIMEOUT 16
40 static NSString *const kHostAddress = @"localhost:5050";
41 static NSString *const kPackage = @"grpc.testing";
42 static NSString *const kService = @"TestService";
43 static NSString *const kRemoteSSLHost = @"grpc-test.sandbox.googleapis.com";
45 static GRPCProtoMethod *kInexistentMethod;
46 static GRPCProtoMethod *kEmptyCallMethod;
47 static GRPCProtoMethod *kUnaryCallMethod;
48 static GRPCProtoMethod *kFullDuplexCallMethod;
50 /** Observer class for testing that responseMetadata is KVO-compliant */
51 @interface PassthroughObserver : NSObject
52 - (instancetype)initWithCallback:(void (^)(NSString *, id, NSDictionary *))callback
53 NS_DESIGNATED_INITIALIZER;
55 - (void)observeValueForKeyPath:(NSString *)keyPath
57 change:(NSDictionary *)change
58 context:(void *)context;
61 @implementation PassthroughObserver {
62 void (^_callback)(NSString *, id, NSDictionary *);
65 - (instancetype)init {
66 return [self initWithCallback:nil];
69 - (instancetype)initWithCallback:(void (^)(NSString *, id, NSDictionary *))callback {
73 if ((self = [super init])) {
79 - (void)observeValueForKeyPath:(NSString *)keyPath
81 change:(NSDictionary *)change
82 context:(void *)context {
83 _callback(keyPath, object, change);
84 [object removeObserver:self forKeyPath:keyPath];
92 * A few tests similar to InteropTests, but which use the generic gRPC client (GRPCCall) rather than
93 * a generated proto library on top of it. Its RPCs are sent to a local cleartext server.
95 * TODO(jcanizales): Run them also against a local SSL server and against a remote server.
97 @interface GRPCClientTests : XCTestCase
100 @implementation GRPCClientTests
103 NSLog(@"GRPCClientTests Started");
107 // Add a custom user agent prefix that will be used in test
108 [GRPCCall setUserAgentPrefix:@"Foo" forHost:kHostAddress];
109 // Register test server as non-SSL.
110 [GRPCCall useInsecureConnectionsForHost:kHostAddress];
112 // This method isn't implemented by the remote server.
114 [[GRPCProtoMethod alloc] initWithPackage:kPackage service:kService method:@"Inexistent"];
116 [[GRPCProtoMethod alloc] initWithPackage:kPackage service:kService method:@"EmptyCall"];
118 [[GRPCProtoMethod alloc] initWithPackage:kPackage service:kService method:@"UnaryCall"];
119 kFullDuplexCallMethod =
120 [[GRPCProtoMethod alloc] initWithPackage:kPackage service:kService method:@"FullDuplexCall"];
123 - (void)testConnectionToRemoteServer {
124 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Server reachable."];
126 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
127 path:kInexistentMethod.HTTPPath
128 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
130 id<GRXWriteable> responsesWriteable =
131 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
132 XCTFail(@"Received unexpected response: %@", value);
134 completionHandler:^(NSError *errorOrNil) {
135 XCTAssertNotNil(errorOrNil, @"Finished without error!");
136 XCTAssertEqual(errorOrNil.code, 12, @"Finished with unexpected error: %@", errorOrNil);
137 [expectation fulfill];
140 [call startWithWriteable:responsesWriteable];
142 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
145 - (void)testEmptyRPC {
146 __weak XCTestExpectation *response =
147 [self expectationWithDescription:@"Empty response received."];
148 __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
150 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
151 path:kEmptyCallMethod.HTTPPath
152 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
154 id<GRXWriteable> responsesWriteable =
155 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
156 XCTAssertNotNil(value, @"nil value received as response.");
157 XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value);
160 completionHandler:^(NSError *errorOrNil) {
161 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
162 [completion fulfill];
165 [call startWithWriteable:responsesWriteable];
167 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
170 - (void)testSimpleProtoRPC {
171 __weak XCTestExpectation *response = [self expectationWithDescription:@"Expected response."];
172 __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
174 RMTSimpleRequest *request = [RMTSimpleRequest message];
175 request.responseSize = 100;
176 request.fillUsername = YES;
177 request.fillOauthScope = YES;
178 GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]];
180 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
181 path:kUnaryCallMethod.HTTPPath
182 requestsWriter:requestsWriter];
184 id<GRXWriteable> responsesWriteable =
185 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
186 XCTAssertNotNil(value, @"nil value received as response.");
187 XCTAssertGreaterThan(value.length, 0, @"Empty response received.");
188 RMTSimpleResponse *responseProto = [RMTSimpleResponse parseFromData:value error:NULL];
189 // We expect empty strings, not nil:
190 XCTAssertNotNil(responseProto.username, @"Response's username is nil.");
191 XCTAssertNotNil(responseProto.oauthScope, @"Response's OAuth scope is nil.");
194 completionHandler:^(NSError *errorOrNil) {
195 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
196 [completion fulfill];
199 [call startWithWriteable:responsesWriteable];
201 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
204 - (void)testMetadata {
205 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"RPC unauthorized."];
207 RMTSimpleRequest *request = [RMTSimpleRequest message];
208 request.fillUsername = YES;
209 request.fillOauthScope = YES;
210 GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]];
212 GRPCCall *call = [[GRPCCall alloc] initWithHost:kRemoteSSLHost
213 path:kUnaryCallMethod.HTTPPath
214 requestsWriter:requestsWriter];
216 call.oauth2AccessToken = @"bogusToken";
218 id<GRXWriteable> responsesWriteable =
219 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
220 XCTFail(@"Received unexpected response: %@", value);
222 completionHandler:^(NSError *errorOrNil) {
223 XCTAssertNotNil(errorOrNil, @"Finished without error!");
224 XCTAssertEqual(errorOrNil.code, 16, @"Finished with unexpected error: %@", errorOrNil);
225 XCTAssertEqualObjects(call.responseHeaders, errorOrNil.userInfo[kGRPCHeadersKey],
226 @"Headers in the NSError object and call object differ.");
227 XCTAssertEqualObjects(call.responseTrailers, errorOrNil.userInfo[kGRPCTrailersKey],
228 @"Trailers in the NSError object and call object differ.");
229 NSString *challengeHeader = call.oauth2ChallengeHeader;
230 XCTAssertGreaterThan(challengeHeader.length, 0, @"No challenge in response headers %@",
231 call.responseHeaders);
232 [expectation fulfill];
235 [call startWithWriteable:responsesWriteable];
237 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
240 - (void)testResponseMetadataKVO {
241 __weak XCTestExpectation *response =
242 [self expectationWithDescription:@"Empty response received."];
243 __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
244 __weak XCTestExpectation *metadata = [self expectationWithDescription:@"Metadata changed."];
246 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
247 path:kEmptyCallMethod.HTTPPath
248 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
250 PassthroughObserver *observer = [[PassthroughObserver alloc]
251 initWithCallback:^(NSString *keypath, id object, NSDictionary *change) {
252 if ([keypath isEqual:@"responseHeaders"]) {
257 [call addObserver:observer forKeyPath:@"responseHeaders" options:0 context:NULL];
259 id<GRXWriteable> responsesWriteable =
260 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
261 XCTAssertNotNil(value, @"nil value received as response.");
262 XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value);
265 completionHandler:^(NSError *errorOrNil) {
266 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
267 [completion fulfill];
270 [call startWithWriteable:responsesWriteable];
272 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
275 - (void)testUserAgentPrefix {
276 __weak XCTestExpectation *response =
277 [self expectationWithDescription:@"Empty response received."];
278 __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
280 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
281 path:kEmptyCallMethod.HTTPPath
282 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
283 // Setting this special key in the header will cause the interop server to echo back the
284 // user-agent value, which we confirm.
285 call.requestHeaders[@"x-grpc-test-echo-useragent"] = @"";
287 id<GRXWriteable> responsesWriteable =
288 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
289 XCTAssertNotNil(value, @"nil value received as response.");
290 XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value);
292 NSString *userAgent = call.responseHeaders[@"x-grpc-test-echo-useragent"];
293 NSError *error = nil;
295 // Test the regex is correct
296 NSString *expectedUserAgent = @"Foo grpc-objc/";
297 expectedUserAgent = [expectedUserAgent stringByAppendingString:GRPC_OBJC_VERSION_STRING];
298 expectedUserAgent = [expectedUserAgent stringByAppendingString:@" grpc-c/"];
299 expectedUserAgent = [expectedUserAgent stringByAppendingString:GRPC_C_VERSION_STRING];
300 expectedUserAgent = [expectedUserAgent stringByAppendingString:@" ("];
301 expectedUserAgent = [expectedUserAgent stringByAppendingString:@GPR_PLATFORM_STRING];
302 expectedUserAgent = [expectedUserAgent stringByAppendingString:@"; chttp2; "];
303 expectedUserAgent = [expectedUserAgent
304 stringByAppendingString:[NSString stringWithUTF8String:grpc_g_stands_for()]];
305 expectedUserAgent = [expectedUserAgent stringByAppendingString:@")"];
306 XCTAssertEqualObjects(userAgent, expectedUserAgent);
308 // Change in format of user-agent field in a direction that does not match the regex will
309 // likely cause problem for certain gRPC users. For details, refer to internal doc
310 // https://goo.gl/c2diBc
311 NSRegularExpression *regex = [NSRegularExpression
312 regularExpressionWithPattern:@" grpc-[a-zA-Z0-9]+(-[a-zA-Z0-9]+)?/[^ ,]+( \\([^)]*\\))?"
315 NSString *customUserAgent =
316 [regex stringByReplacingMatchesInString:userAgent
318 range:NSMakeRange(0, [userAgent length])
320 XCTAssertEqualObjects(customUserAgent, @"Foo");
324 completionHandler:^(NSError *errorOrNil) {
325 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
326 [completion fulfill];
329 [call startWithWriteable:responsesWriteable];
331 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
334 - (void)testTrailers {
335 __weak XCTestExpectation *response =
336 [self expectationWithDescription:@"Empty response received."];
337 __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
339 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
340 path:kEmptyCallMethod.HTTPPath
341 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
342 // Setting this special key in the header will cause the interop server to echo back the
344 const unsigned char raw_bytes[] = {1, 2, 3, 4};
345 NSData *trailer_data = [NSData dataWithBytes:raw_bytes length:sizeof(raw_bytes)];
346 call.requestHeaders[@"x-grpc-test-echo-trailing-bin"] = trailer_data;
348 id<GRXWriteable> responsesWriteable =
349 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
350 XCTAssertNotNil(value, @"nil value received as response.");
351 XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value);
354 completionHandler:^(NSError *errorOrNil) {
355 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
356 XCTAssertEqualObjects((NSData *)call.responseTrailers[@"x-grpc-test-echo-trailing-bin"],
357 trailer_data, @"Did not receive expected trailer");
358 [completion fulfill];
361 [call startWithWriteable:responsesWriteable];
362 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
365 // TODO(makarandd): Move to a different file that contains only unit tests
366 - (void)testExceptions {
367 GRXWriter *writer = [GRXWriter writerWithValue:[NSData data]];
368 // Try to set parameters to nil for GRPCCall. This should cause an exception
370 (void)[[GRPCCall alloc] initWithHost:nil path:nil requestsWriter:writer];
371 XCTFail(@"Did not receive an exception when parameters are nil");
372 } @catch (NSException *theException) {
373 NSLog(@"Received exception as expected: %@", theException.name);
376 // Set state to Finished by force
377 GRXWriter *requestsWriter = [GRXWriter emptyWriter];
378 [requestsWriter finishWithError:nil];
380 (void)[[GRPCCall alloc] initWithHost:kHostAddress
381 path:kUnaryCallMethod.HTTPPath
382 requestsWriter:requestsWriter];
383 XCTFail(@"Did not receive an exception when GRXWriter has incorrect state.");
384 } @catch (NSException *theException) {
385 NSLog(@"Received exception as expected: %@", theException.name);
389 - (void)testIdempotentProtoRPC {
390 __weak XCTestExpectation *response = [self expectationWithDescription:@"Expected response."];
391 __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
393 RMTSimpleRequest *request = [RMTSimpleRequest message];
394 request.responseSize = 100;
395 request.fillUsername = YES;
396 request.fillOauthScope = YES;
397 GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]];
399 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
400 path:kUnaryCallMethod.HTTPPath
401 requestsWriter:requestsWriter];
402 [GRPCCall setCallSafety:GRPCCallSafetyIdempotentRequest
404 path:kUnaryCallMethod.HTTPPath];
406 id<GRXWriteable> responsesWriteable =
407 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
408 XCTAssertNotNil(value, @"nil value received as response.");
409 XCTAssertGreaterThan(value.length, 0, @"Empty response received.");
410 RMTSimpleResponse *responseProto = [RMTSimpleResponse parseFromData:value error:NULL];
411 // We expect empty strings, not nil:
412 XCTAssertNotNil(responseProto.username, @"Response's username is nil.");
413 XCTAssertNotNil(responseProto.oauthScope, @"Response's OAuth scope is nil.");
416 completionHandler:^(NSError *errorOrNil) {
417 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
418 [completion fulfill];
421 [call startWithWriteable:responsesWriteable];
423 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
426 - (void)testAlternateDispatchQueue {
427 const int32_t kPayloadSize = 100;
428 RMTSimpleRequest *request = [RMTSimpleRequest message];
429 request.responseSize = kPayloadSize;
431 __weak XCTestExpectation *expectation1 =
432 [self expectationWithDescription:@"AlternateDispatchQueue1"];
434 // Use default (main) dispatch queue
435 NSString *main_queue_label =
436 [NSString stringWithUTF8String:dispatch_queue_get_label(dispatch_get_main_queue())];
438 GRXWriter *requestsWriter1 = [GRXWriter writerWithValue:[request data]];
440 GRPCCall *call1 = [[GRPCCall alloc] initWithHost:kHostAddress
441 path:kUnaryCallMethod.HTTPPath
442 requestsWriter:requestsWriter1];
444 id<GRXWriteable> responsesWriteable1 =
445 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
447 [NSString stringWithUTF8String:dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)];
448 XCTAssert([label isEqualToString:main_queue_label]);
450 [expectation1 fulfill];
452 completionHandler:^(NSError *errorOrNil){
455 [call1 startWithWriteable:responsesWriteable1];
457 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
459 // Use a custom queue
460 __weak XCTestExpectation *expectation2 =
461 [self expectationWithDescription:@"AlternateDispatchQueue2"];
463 NSString *queue_label = @"test.queue1";
464 dispatch_queue_t queue = dispatch_queue_create([queue_label UTF8String], DISPATCH_QUEUE_SERIAL);
466 GRXWriter *requestsWriter2 = [GRXWriter writerWithValue:[request data]];
468 GRPCCall *call2 = [[GRPCCall alloc] initWithHost:kHostAddress
469 path:kUnaryCallMethod.HTTPPath
470 requestsWriter:requestsWriter2];
472 [call2 setResponseDispatchQueue:queue];
474 id<GRXWriteable> responsesWriteable2 =
475 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
477 [NSString stringWithUTF8String:dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)];
478 XCTAssert([label isEqualToString:queue_label]);
480 [expectation2 fulfill];
482 completionHandler:^(NSError *errorOrNil){
485 [call2 startWithWriteable:responsesWriteable2];
487 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
490 - (void)testTimeout {
491 __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
493 GRXBufferedPipe *pipe = [GRXBufferedPipe pipe];
494 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
495 path:kFullDuplexCallMethod.HTTPPath
496 requestsWriter:pipe];
498 id<GRXWriteable> responsesWriteable =
499 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
500 XCTAssert(0, @"Failure: response received; Expect: no response received.");
502 completionHandler:^(NSError *errorOrNil) {
503 XCTAssertNotNil(errorOrNil,
504 @"Failure: no error received; Expect: receive deadline exceeded.");
505 XCTAssertEqual(errorOrNil.code, GRPCErrorCodeDeadlineExceeded);
506 [completion fulfill];
509 call.timeout = 0.001;
510 [call startWithWriteable:responsesWriteable];
512 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
515 - (int)findFreePort {
516 struct sockaddr_in addr;
517 unsigned int addr_len = sizeof(addr);
518 memset(&addr, 0, sizeof(addr));
519 addr.sin_family = AF_INET;
520 int fd = socket(AF_INET, SOCK_STREAM, 0);
521 XCTAssertEqual(bind(fd, (struct sockaddr *)&addr, sizeof(addr)), 0);
522 XCTAssertEqual(getsockname(fd, (struct sockaddr *)&addr, &addr_len), 0);
523 XCTAssertEqual(addr_len, sizeof(addr));
525 return addr.sin_port;
528 - (void)testErrorCode {
529 int port = [self findFreePort];
530 NSString *const kDummyAddress = [NSString stringWithFormat:@"localhost:%d", port];
531 __weak XCTestExpectation *completion =
532 [self expectationWithDescription:@"Received correct error code."];
534 GRPCCall *call = [[GRPCCall alloc] initWithHost:kDummyAddress
535 path:kEmptyCallMethod.HTTPPath
536 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
538 id<GRXWriteable> responsesWriteable =
539 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
540 // Should not reach here
543 completionHandler:^(NSError *errorOrNil) {
544 XCTAssertNotNil(errorOrNil, @"Finished with no error");
545 XCTAssertEqual(errorOrNil.code, GRPC_STATUS_UNAVAILABLE);
546 [completion fulfill];
549 [call startWithWriteable:responsesWriteable];
551 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
554 - (void)testTimeoutBackoffWithTimeout:(double)timeout Backoff:(double)backoff {
555 const double maxConnectTime = timeout > backoff ? timeout : backoff;
556 const double kMargin = 0.1;
558 __weak XCTestExpectation *completion = [self expectationWithDescription:@"Timeout in a second."];
559 NSString *const kDummyAddress = [NSString stringWithFormat:@"8.8.8.8:1"];
560 [GRPCCall useInsecureConnectionsForHost:kDummyAddress];
561 [GRPCCall setMinConnectTimeout:timeout * 1000
562 initialBackoff:backoff * 1000
564 forHost:kDummyAddress];
565 GRPCCall *call = [[GRPCCall alloc] initWithHost:kDummyAddress
567 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
568 NSDate *startTime = [NSDate date];
569 id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(id value) {
570 XCTAssert(NO, @"Received message. Should not reach here");
572 completionHandler:^(NSError *errorOrNil) {
573 XCTAssertNotNil(errorOrNil, @"Finished with no error");
574 // The call must fail before maxConnectTime. However there is no lower bound on the time
575 // taken for connection. A shorter time happens when connection is actively refused
576 // by 8.8.8.8:1 before maxConnectTime elapsed.
577 XCTAssertLessThan([[NSDate date] timeIntervalSinceDate:startTime],
578 maxConnectTime + kMargin);
579 [completion fulfill];
582 [call startWithWriteable:responsesWriteable];
584 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
587 // The numbers of the following three tests are selected to be smaller than the default values of
588 // initial backoff (1s) and min_connect_timeout (20s), so that if they fail we know the default
589 // values fail to be overridden by the channel args.
590 - (void)testTimeoutBackoff1 {
591 [self testTimeoutBackoffWithTimeout:0.7 Backoff:0.3];
594 - (void)testTimeoutBackoff2 {
595 [self testTimeoutBackoffWithTimeout:0.3 Backoff:0.7];
598 - (void)testErrorDebugInformation {
599 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"RPC unauthorized."];
601 RMTSimpleRequest *request = [RMTSimpleRequest message];
602 request.fillUsername = YES;
603 request.fillOauthScope = YES;
604 GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]];
606 GRPCCall *call = [[GRPCCall alloc] initWithHost:kRemoteSSLHost
607 path:kUnaryCallMethod.HTTPPath
608 requestsWriter:requestsWriter];
610 call.oauth2AccessToken = @"bogusToken";
612 id<GRXWriteable> responsesWriteable =
613 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
614 XCTFail(@"Received unexpected response: %@", value);
616 completionHandler:^(NSError *errorOrNil) {
617 XCTAssertNotNil(errorOrNil, @"Finished without error!");
618 NSDictionary *userInfo = errorOrNil.userInfo;
619 NSString *debugInformation = userInfo[NSDebugDescriptionErrorKey];
620 XCTAssertNotNil(debugInformation);
621 XCTAssertNotEqual([debugInformation length], 0);
622 NSString *challengeHeader = call.oauth2ChallengeHeader;
623 XCTAssertGreaterThan(challengeHeader.length, 0, @"No challenge in response headers %@",
624 call.responseHeaders);
625 [expectation fulfill];
628 [call startWithWriteable:responsesWriteable];
630 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];