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 <UIKit/UIKit.h>
20 #import <XCTest/XCTest.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>
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:@" (ios; chttp2; "];
301 expectedUserAgent = [expectedUserAgent
302 stringByAppendingString:[NSString stringWithUTF8String:grpc_g_stands_for()]];
303 expectedUserAgent = [expectedUserAgent stringByAppendingString:@")"];
304 XCTAssertEqualObjects(userAgent, expectedUserAgent);
306 // Change in format of user-agent field in a direction that does not match the regex will
307 // likely cause problem for certain gRPC users. For details, refer to internal doc
308 // https://goo.gl/c2diBc
309 NSRegularExpression *regex = [NSRegularExpression
310 regularExpressionWithPattern:@" grpc-[a-zA-Z0-9]+(-[a-zA-Z0-9]+)?/[^ ,]+( \\([^)]*\\))?"
313 NSString *customUserAgent =
314 [regex stringByReplacingMatchesInString:userAgent
316 range:NSMakeRange(0, [userAgent length])
318 XCTAssertEqualObjects(customUserAgent, @"Foo");
322 completionHandler:^(NSError *errorOrNil) {
323 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
324 [completion fulfill];
327 [call startWithWriteable:responsesWriteable];
329 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
332 - (void)testTrailers {
333 __weak XCTestExpectation *response =
334 [self expectationWithDescription:@"Empty response received."];
335 __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
337 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
338 path:kEmptyCallMethod.HTTPPath
339 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
340 // Setting this special key in the header will cause the interop server to echo back the
342 const unsigned char raw_bytes[] = {1, 2, 3, 4};
343 NSData *trailer_data = [NSData dataWithBytes:raw_bytes length:sizeof(raw_bytes)];
344 call.requestHeaders[@"x-grpc-test-echo-trailing-bin"] = trailer_data;
346 id<GRXWriteable> responsesWriteable =
347 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
348 XCTAssertNotNil(value, @"nil value received as response.");
349 XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value);
352 completionHandler:^(NSError *errorOrNil) {
353 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
354 XCTAssertEqualObjects((NSData *)call.responseTrailers[@"x-grpc-test-echo-trailing-bin"],
355 trailer_data, @"Did not receive expected trailer");
356 [completion fulfill];
359 [call startWithWriteable:responsesWriteable];
360 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
363 // TODO(makarandd): Move to a different file that contains only unit tests
364 - (void)testExceptions {
365 GRXWriter *writer = [GRXWriter writerWithValue:[NSData data]];
366 // Try to set parameters to nil for GRPCCall. This should cause an exception
368 (void)[[GRPCCall alloc] initWithHost:nil path:nil requestsWriter:writer];
369 XCTFail(@"Did not receive an exception when parameters are nil");
370 } @catch (NSException *theException) {
371 NSLog(@"Received exception as expected: %@", theException.name);
374 // Set state to Finished by force
375 GRXWriter *requestsWriter = [GRXWriter emptyWriter];
376 [requestsWriter finishWithError:nil];
378 (void)[[GRPCCall alloc] initWithHost:kHostAddress
379 path:kUnaryCallMethod.HTTPPath
380 requestsWriter:requestsWriter];
381 XCTFail(@"Did not receive an exception when GRXWriter has incorrect state.");
382 } @catch (NSException *theException) {
383 NSLog(@"Received exception as expected: %@", theException.name);
387 - (void)testIdempotentProtoRPC {
388 __weak XCTestExpectation *response = [self expectationWithDescription:@"Expected response."];
389 __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
391 RMTSimpleRequest *request = [RMTSimpleRequest message];
392 request.responseSize = 100;
393 request.fillUsername = YES;
394 request.fillOauthScope = YES;
395 GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]];
397 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
398 path:kUnaryCallMethod.HTTPPath
399 requestsWriter:requestsWriter];
400 [GRPCCall setCallSafety:GRPCCallSafetyIdempotentRequest
402 path:kUnaryCallMethod.HTTPPath];
404 id<GRXWriteable> responsesWriteable =
405 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
406 XCTAssertNotNil(value, @"nil value received as response.");
407 XCTAssertGreaterThan(value.length, 0, @"Empty response received.");
408 RMTSimpleResponse *responseProto = [RMTSimpleResponse parseFromData:value error:NULL];
409 // We expect empty strings, not nil:
410 XCTAssertNotNil(responseProto.username, @"Response's username is nil.");
411 XCTAssertNotNil(responseProto.oauthScope, @"Response's OAuth scope is nil.");
414 completionHandler:^(NSError *errorOrNil) {
415 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
416 [completion fulfill];
419 [call startWithWriteable:responsesWriteable];
421 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
424 - (void)testAlternateDispatchQueue {
425 const int32_t kPayloadSize = 100;
426 RMTSimpleRequest *request = [RMTSimpleRequest message];
427 request.responseSize = kPayloadSize;
429 __weak XCTestExpectation *expectation1 =
430 [self expectationWithDescription:@"AlternateDispatchQueue1"];
432 // Use default (main) dispatch queue
433 NSString *main_queue_label =
434 [NSString stringWithUTF8String:dispatch_queue_get_label(dispatch_get_main_queue())];
436 GRXWriter *requestsWriter1 = [GRXWriter writerWithValue:[request data]];
438 GRPCCall *call1 = [[GRPCCall alloc] initWithHost:kHostAddress
439 path:kUnaryCallMethod.HTTPPath
440 requestsWriter:requestsWriter1];
442 id<GRXWriteable> responsesWriteable1 =
443 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
445 [NSString stringWithUTF8String:dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)];
446 XCTAssert([label isEqualToString:main_queue_label]);
448 [expectation1 fulfill];
450 completionHandler:^(NSError *errorOrNil){
453 [call1 startWithWriteable:responsesWriteable1];
455 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
457 // Use a custom queue
458 __weak XCTestExpectation *expectation2 =
459 [self expectationWithDescription:@"AlternateDispatchQueue2"];
461 NSString *queue_label = @"test.queue1";
462 dispatch_queue_t queue = dispatch_queue_create([queue_label UTF8String], DISPATCH_QUEUE_SERIAL);
464 GRXWriter *requestsWriter2 = [GRXWriter writerWithValue:[request data]];
466 GRPCCall *call2 = [[GRPCCall alloc] initWithHost:kHostAddress
467 path:kUnaryCallMethod.HTTPPath
468 requestsWriter:requestsWriter2];
470 [call2 setResponseDispatchQueue:queue];
472 id<GRXWriteable> responsesWriteable2 =
473 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
475 [NSString stringWithUTF8String:dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)];
476 XCTAssert([label isEqualToString:queue_label]);
478 [expectation2 fulfill];
480 completionHandler:^(NSError *errorOrNil){
483 [call2 startWithWriteable:responsesWriteable2];
485 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
488 - (void)testTimeout {
489 __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
491 GRXBufferedPipe *pipe = [GRXBufferedPipe pipe];
492 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
493 path:kFullDuplexCallMethod.HTTPPath
494 requestsWriter:pipe];
496 id<GRXWriteable> responsesWriteable =
497 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
498 XCTAssert(0, @"Failure: response received; Expect: no response received.");
500 completionHandler:^(NSError *errorOrNil) {
501 XCTAssertNotNil(errorOrNil,
502 @"Failure: no error received; Expect: receive deadline exceeded.");
503 XCTAssertEqual(errorOrNil.code, GRPCErrorCodeDeadlineExceeded);
504 [completion fulfill];
507 call.timeout = 0.001;
508 [call startWithWriteable:responsesWriteable];
510 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
513 - (int)findFreePort {
514 struct sockaddr_in addr;
515 unsigned int addr_len = sizeof(addr);
516 memset(&addr, 0, sizeof(addr));
517 addr.sin_family = AF_INET;
518 int fd = socket(AF_INET, SOCK_STREAM, 0);
519 XCTAssertEqual(bind(fd, (struct sockaddr *)&addr, sizeof(addr)), 0);
520 XCTAssertEqual(getsockname(fd, (struct sockaddr *)&addr, &addr_len), 0);
521 XCTAssertEqual(addr_len, sizeof(addr));
523 return addr.sin_port;
526 - (void)testErrorCode {
527 int port = [self findFreePort];
528 NSString *const kDummyAddress = [NSString stringWithFormat:@"localhost:%d", port];
529 __weak XCTestExpectation *completion =
530 [self expectationWithDescription:@"Received correct error code."];
532 GRPCCall *call = [[GRPCCall alloc] initWithHost:kDummyAddress
533 path:kEmptyCallMethod.HTTPPath
534 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
536 id<GRXWriteable> responsesWriteable =
537 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
538 // Should not reach here
541 completionHandler:^(NSError *errorOrNil) {
542 XCTAssertNotNil(errorOrNil, @"Finished with no error");
543 XCTAssertEqual(errorOrNil.code, GRPC_STATUS_UNAVAILABLE);
544 [completion fulfill];
547 [call startWithWriteable:responsesWriteable];
549 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
552 - (void)testTimeoutBackoffWithTimeout:(double)timeout Backoff:(double)backoff {
553 const double maxConnectTime = timeout > backoff ? timeout : backoff;
554 const double kMargin = 0.1;
556 __weak XCTestExpectation *completion = [self expectationWithDescription:@"Timeout in a second."];
557 NSString *const kDummyAddress = [NSString stringWithFormat:@"8.8.8.8:1"];
558 [GRPCCall useInsecureConnectionsForHost:kDummyAddress];
559 [GRPCCall setMinConnectTimeout:timeout * 1000
560 initialBackoff:backoff * 1000
562 forHost:kDummyAddress];
563 GRPCCall *call = [[GRPCCall alloc] initWithHost:kDummyAddress
565 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
566 NSDate *startTime = [NSDate date];
567 id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(id value) {
568 XCTAssert(NO, @"Received message. Should not reach here");
570 completionHandler:^(NSError *errorOrNil) {
571 XCTAssertNotNil(errorOrNil, @"Finished with no error");
572 // The call must fail before maxConnectTime. However there is no lower bound on the time
573 // taken for connection. A shorter time happens when connection is actively refused
574 // by 8.8.8.8:1 before maxConnectTime elapsed.
575 XCTAssertLessThan([[NSDate date] timeIntervalSinceDate:startTime],
576 maxConnectTime + kMargin);
577 [completion fulfill];
580 [call startWithWriteable:responsesWriteable];
582 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
585 // The numbers of the following three tests are selected to be smaller than the default values of
586 // initial backoff (1s) and min_connect_timeout (20s), so that if they fail we know the default
587 // values fail to be overridden by the channel args.
588 - (void)testTimeoutBackoff1 {
589 [self testTimeoutBackoffWithTimeout:0.7 Backoff:0.3];
592 - (void)testTimeoutBackoff2 {
593 [self testTimeoutBackoffWithTimeout:0.3 Backoff:0.7];
596 - (void)testErrorDebugInformation {
597 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"RPC unauthorized."];
599 RMTSimpleRequest *request = [RMTSimpleRequest message];
600 request.fillUsername = YES;
601 request.fillOauthScope = YES;
602 GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]];
604 GRPCCall *call = [[GRPCCall alloc] initWithHost:kRemoteSSLHost
605 path:kUnaryCallMethod.HTTPPath
606 requestsWriter:requestsWriter];
608 call.oauth2AccessToken = @"bogusToken";
610 id<GRXWriteable> responsesWriteable =
611 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
612 XCTFail(@"Received unexpected response: %@", value);
614 completionHandler:^(NSError *errorOrNil) {
615 XCTAssertNotNil(errorOrNil, @"Finished without error!");
616 NSDictionary *userInfo = errorOrNil.userInfo;
617 NSString *debugInformation = userInfo[NSDebugDescriptionErrorKey];
618 XCTAssertNotNil(debugInformation);
619 XCTAssertNotEqual([debugInformation length], 0);
620 NSString *challengeHeader = call.oauth2ChallengeHeader;
621 XCTAssertGreaterThan(challengeHeader.length, 0, @"No challenge in response headers %@",
622 call.responseHeaders);
623 [expectation fulfill];
626 [call startWithWriteable:responsesWriteable];
628 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];