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 <RxLibrary/GRXBufferedPipe.h>
30 #import <RxLibrary/GRXWriteable.h>
31 #import <RxLibrary/GRXWriter+Immediate.h>
32 #import "src/objective-c/tests/RemoteTestClient/Messages.pbobjc.h"
34 #include <netinet/in.h>
36 #import "../version.h"
38 #define TEST_TIMEOUT 16
40 // The server address is derived from preprocessor macro, which is
41 // in turn derived from environment variable of the same name.
42 #define NSStringize_helper(x) #x
43 #define NSStringize(x) @NSStringize_helper(x)
44 static NSString *const kHostAddress = NSStringize(HOST_PORT_LOCAL);
45 static NSString *const kPackage = @"grpc.testing";
46 static NSString *const kService = @"TestService";
47 static NSString *const kRemoteSSLHost = NSStringize(HOST_PORT_REMOTE);
49 static GRPCProtoMethod *kInexistentMethod;
50 static GRPCProtoMethod *kEmptyCallMethod;
51 static GRPCProtoMethod *kUnaryCallMethod;
52 static GRPCProtoMethod *kFullDuplexCallMethod;
54 /** Observer class for testing that responseMetadata is KVO-compliant */
55 @interface PassthroughObserver : NSObject
56 - (instancetype)initWithCallback:(void (^)(NSString *, id, NSDictionary *))callback
57 NS_DESIGNATED_INITIALIZER;
59 - (void)observeValueForKeyPath:(NSString *)keyPath
61 change:(NSDictionary *)change
62 context:(void *)context;
65 @implementation PassthroughObserver {
66 void (^_callback)(NSString *, id, NSDictionary *);
69 - (instancetype)init {
70 return [self initWithCallback:nil];
73 - (instancetype)initWithCallback:(void (^)(NSString *, id, NSDictionary *))callback {
77 if ((self = [super init])) {
83 - (void)observeValueForKeyPath:(NSString *)keyPath
85 change:(NSDictionary *)change
86 context:(void *)context {
87 _callback(keyPath, object, change);
88 [object removeObserver:self forKeyPath:keyPath];
96 * A few tests similar to InteropTests, but which use the generic gRPC client (GRPCCall) rather than
97 * a generated proto library on top of it. Its RPCs are sent to a local cleartext server.
99 * TODO(jcanizales): Run them also against a local SSL server and against a remote server.
101 @interface GRPCClientTests : XCTestCase
104 @implementation GRPCClientTests
107 NSLog(@"GRPCClientTests Started");
111 // Add a custom user agent prefix that will be used in test
112 [GRPCCall setUserAgentPrefix:@"Foo" forHost:kHostAddress];
113 // Register test server as non-SSL.
114 [GRPCCall useInsecureConnectionsForHost:kHostAddress];
116 // This method isn't implemented by the remote server.
117 kInexistentMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage
119 method:@"Inexistent"];
120 kEmptyCallMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage
122 method:@"EmptyCall"];
123 kUnaryCallMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage
125 method:@"UnaryCall"];
126 kFullDuplexCallMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage
128 method:@"FullDuplexCall"];
131 - (void)testConnectionToRemoteServer {
132 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Server reachable."];
134 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
135 path:kInexistentMethod.HTTPPath
136 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
138 id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc]
139 initWithValueHandler:^(NSData *value) {
140 XCTFail(@"Received unexpected response: %@", value);
142 completionHandler:^(NSError *errorOrNil) {
143 XCTAssertNotNil(errorOrNil, @"Finished without error!");
144 XCTAssertEqual(errorOrNil.code, 12, @"Finished with unexpected error: %@", errorOrNil);
145 [expectation fulfill];
148 [call startWithWriteable:responsesWriteable];
150 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
153 - (void)testEmptyRPC {
154 __weak XCTestExpectation *response =
155 [self expectationWithDescription:@"Empty response received."];
156 __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
158 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
159 path:kEmptyCallMethod.HTTPPath
160 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
162 id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc]
163 initWithValueHandler:^(NSData *value) {
164 XCTAssertNotNil(value, @"nil value received as response.");
165 XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value);
168 completionHandler:^(NSError *errorOrNil) {
169 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
170 [completion fulfill];
173 [call startWithWriteable:responsesWriteable];
175 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
178 - (void)testSimpleProtoRPC {
179 __weak XCTestExpectation *response = [self expectationWithDescription:@"Expected response."];
180 __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
182 RMTSimpleRequest *request = [RMTSimpleRequest message];
183 request.responseSize = 100;
184 request.fillUsername = YES;
185 request.fillOauthScope = YES;
186 GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]];
188 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
189 path:kUnaryCallMethod.HTTPPath
190 requestsWriter:requestsWriter];
192 id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc]
193 initWithValueHandler:^(NSData *value) {
194 XCTAssertNotNil(value, @"nil value received as response.");
195 XCTAssertGreaterThan(value.length, 0, @"Empty response received.");
196 RMTSimpleResponse *responseProto = [RMTSimpleResponse parseFromData:value error:NULL];
197 // We expect empty strings, not nil:
198 XCTAssertNotNil(responseProto.username, @"Response's username is nil.");
199 XCTAssertNotNil(responseProto.oauthScope, @"Response's OAuth scope is nil.");
202 completionHandler:^(NSError *errorOrNil) {
203 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
204 [completion fulfill];
207 [call startWithWriteable:responsesWriteable];
209 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
212 - (void)testMetadata {
213 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"RPC unauthorized."];
215 RMTSimpleRequest *request = [RMTSimpleRequest message];
216 request.fillUsername = YES;
217 request.fillOauthScope = YES;
218 GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]];
220 GRPCCall *call = [[GRPCCall alloc] initWithHost:kRemoteSSLHost
221 path:kUnaryCallMethod.HTTPPath
222 requestsWriter:requestsWriter];
224 call.oauth2AccessToken = @"bogusToken";
226 id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc]
227 initWithValueHandler:^(NSData *value) {
228 XCTFail(@"Received unexpected response: %@", value);
230 completionHandler:^(NSError *errorOrNil) {
231 XCTAssertNotNil(errorOrNil, @"Finished without error!");
232 XCTAssertEqual(errorOrNil.code, 16, @"Finished with unexpected error: %@", errorOrNil);
233 XCTAssertEqualObjects(call.responseHeaders, errorOrNil.userInfo[kGRPCHeadersKey],
234 @"Headers in the NSError object and call object differ.");
235 XCTAssertEqualObjects(call.responseTrailers, errorOrNil.userInfo[kGRPCTrailersKey],
236 @"Trailers in the NSError object and call object differ.");
237 NSString *challengeHeader = call.oauth2ChallengeHeader;
238 XCTAssertGreaterThan(challengeHeader.length, 0, @"No challenge in response headers %@",
239 call.responseHeaders);
240 [expectation fulfill];
243 [call startWithWriteable:responsesWriteable];
245 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
248 - (void)testResponseMetadataKVO {
249 __weak XCTestExpectation *response =
250 [self expectationWithDescription:@"Empty response received."];
251 __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
252 __weak XCTestExpectation *metadata = [self expectationWithDescription:@"Metadata changed."];
254 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
255 path:kEmptyCallMethod.HTTPPath
256 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
258 PassthroughObserver *observer = [[PassthroughObserver alloc]
259 initWithCallback:^(NSString *keypath, id object, NSDictionary *change) {
260 if ([keypath isEqual:@"responseHeaders"]) {
265 [call addObserver:observer forKeyPath:@"responseHeaders" options:0 context:NULL];
267 id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc]
268 initWithValueHandler:^(NSData *value) {
269 XCTAssertNotNil(value, @"nil value received as response.");
270 XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value);
273 completionHandler:^(NSError *errorOrNil) {
274 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
275 [completion fulfill];
278 [call startWithWriteable:responsesWriteable];
280 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
283 - (void)testUserAgentPrefix {
284 __weak XCTestExpectation *response =
285 [self expectationWithDescription:@"Empty response received."];
286 __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
288 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
289 path:kEmptyCallMethod.HTTPPath
290 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
291 // Setting this special key in the header will cause the interop server to echo back the
292 // user-agent value, which we confirm.
293 call.requestHeaders[@"x-grpc-test-echo-useragent"] = @"";
295 id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc]
296 initWithValueHandler:^(NSData *value) {
297 XCTAssertNotNil(value, @"nil value received as response.");
298 XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value);
300 NSString *userAgent = call.responseHeaders[@"x-grpc-test-echo-useragent"];
301 NSError *error = nil;
303 // Test the regex is correct
304 NSString *expectedUserAgent = @"Foo grpc-objc/";
305 expectedUserAgent = [expectedUserAgent stringByAppendingString:GRPC_OBJC_VERSION_STRING];
306 expectedUserAgent = [expectedUserAgent stringByAppendingString:@" grpc-c/"];
307 expectedUserAgent = [expectedUserAgent stringByAppendingString:GRPC_C_VERSION_STRING];
308 expectedUserAgent = [expectedUserAgent stringByAppendingString:@" ("];
309 expectedUserAgent = [expectedUserAgent stringByAppendingString:@GPR_PLATFORM_STRING];
310 expectedUserAgent = [expectedUserAgent stringByAppendingString:@"; chttp2)"];
311 XCTAssertEqualObjects(userAgent, expectedUserAgent);
313 // Change in format of user-agent field in a direction that does not match the regex will
314 // likely cause problem for certain gRPC users. For details, refer to internal doc
315 // https://goo.gl/c2diBc
316 NSRegularExpression *regex = [NSRegularExpression
317 regularExpressionWithPattern:@" grpc-[a-zA-Z0-9]+(-[a-zA-Z0-9]+)?/[^ ,]+( \\([^)]*\\))?"
320 NSString *customUserAgent =
321 [regex stringByReplacingMatchesInString:userAgent
323 range:NSMakeRange(0, [userAgent length])
325 XCTAssertEqualObjects(customUserAgent, @"Foo");
329 completionHandler:^(NSError *errorOrNil) {
330 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
331 [completion fulfill];
334 [call startWithWriteable:responsesWriteable];
336 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
339 - (void)testTrailers {
340 __weak XCTestExpectation *response =
341 [self expectationWithDescription:@"Empty response received."];
342 __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
344 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
345 path:kEmptyCallMethod.HTTPPath
346 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
347 // Setting this special key in the header will cause the interop server to echo back the
349 const unsigned char raw_bytes[] = {1, 2, 3, 4};
350 NSData *trailer_data = [NSData dataWithBytes:raw_bytes length:sizeof(raw_bytes)];
351 call.requestHeaders[@"x-grpc-test-echo-trailing-bin"] = trailer_data;
353 id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc]
354 initWithValueHandler:^(NSData *value) {
355 XCTAssertNotNil(value, @"nil value received as response.");
356 XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value);
359 completionHandler:^(NSError *errorOrNil) {
360 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
361 XCTAssertEqualObjects((NSData *)call.responseTrailers[@"x-grpc-test-echo-trailing-bin"],
362 trailer_data, @"Did not receive expected trailer");
363 [completion fulfill];
366 [call startWithWriteable:responsesWriteable];
367 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
370 // TODO(makarandd): Move to a different file that contains only unit tests
371 - (void)testExceptions {
372 GRXWriter *writer = [GRXWriter writerWithValue:[NSData data]];
373 // Try to set parameters to nil for GRPCCall. This should cause an exception
375 (void)[[GRPCCall alloc] initWithHost:nil path:nil requestsWriter:writer];
376 XCTFail(@"Did not receive an exception when parameters are nil");
377 } @catch (NSException *theException) {
378 NSLog(@"Received exception as expected: %@", theException.name);
381 // Set state to Finished by force
382 GRXWriter *requestsWriter = [GRXWriter emptyWriter];
383 [requestsWriter finishWithError:nil];
385 (void)[[GRPCCall alloc] initWithHost:kHostAddress
386 path:kUnaryCallMethod.HTTPPath
387 requestsWriter:requestsWriter];
388 XCTFail(@"Did not receive an exception when GRXWriter has incorrect state.");
389 } @catch (NSException *theException) {
390 NSLog(@"Received exception as expected: %@", theException.name);
394 - (void)testIdempotentProtoRPC {
395 __weak XCTestExpectation *response = [self expectationWithDescription:@"Expected response."];
396 __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
398 RMTSimpleRequest *request = [RMTSimpleRequest message];
399 request.responseSize = 100;
400 request.fillUsername = YES;
401 request.fillOauthScope = YES;
402 GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]];
404 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
405 path:kUnaryCallMethod.HTTPPath
406 requestsWriter:requestsWriter];
407 [GRPCCall setCallSafety:GRPCCallSafetyIdempotentRequest
409 path:kUnaryCallMethod.HTTPPath];
411 id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc]
412 initWithValueHandler:^(NSData *value) {
413 XCTAssertNotNil(value, @"nil value received as response.");
414 XCTAssertGreaterThan(value.length, 0, @"Empty response received.");
415 RMTSimpleResponse *responseProto = [RMTSimpleResponse parseFromData:value error:NULL];
416 // We expect empty strings, not nil:
417 XCTAssertNotNil(responseProto.username, @"Response's username is nil.");
418 XCTAssertNotNil(responseProto.oauthScope, @"Response's OAuth scope is nil.");
421 completionHandler:^(NSError *errorOrNil) {
422 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
423 [completion fulfill];
426 [call startWithWriteable:responsesWriteable];
428 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
431 - (void)testAlternateDispatchQueue {
432 const int32_t kPayloadSize = 100;
433 RMTSimpleRequest *request = [RMTSimpleRequest message];
434 request.responseSize = kPayloadSize;
436 __weak XCTestExpectation *expectation1 =
437 [self expectationWithDescription:@"AlternateDispatchQueue1"];
439 // Use default (main) dispatch queue
440 NSString *main_queue_label =
441 [NSString stringWithUTF8String:dispatch_queue_get_label(dispatch_get_main_queue())];
443 GRXWriter *requestsWriter1 = [GRXWriter writerWithValue:[request data]];
445 GRPCCall *call1 = [[GRPCCall alloc] initWithHost:kHostAddress
446 path:kUnaryCallMethod.HTTPPath
447 requestsWriter:requestsWriter1];
449 id<GRXWriteable> responsesWriteable1 = [[GRXWriteable alloc]
450 initWithValueHandler:^(NSData *value) {
452 [NSString stringWithUTF8String:dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)];
453 XCTAssert([label isEqualToString:main_queue_label]);
455 [expectation1 fulfill];
457 completionHandler:^(NSError *errorOrNil){
460 [call1 startWithWriteable:responsesWriteable1];
462 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
464 // Use a custom queue
465 __weak XCTestExpectation *expectation2 =
466 [self expectationWithDescription:@"AlternateDispatchQueue2"];
468 NSString *queue_label = @"test.queue1";
469 dispatch_queue_t queue = dispatch_queue_create([queue_label UTF8String], DISPATCH_QUEUE_SERIAL);
471 GRXWriter *requestsWriter2 = [GRXWriter writerWithValue:[request data]];
473 GRPCCall *call2 = [[GRPCCall alloc] initWithHost:kHostAddress
474 path:kUnaryCallMethod.HTTPPath
475 requestsWriter:requestsWriter2];
477 [call2 setResponseDispatchQueue:queue];
479 id<GRXWriteable> responsesWriteable2 = [[GRXWriteable alloc]
480 initWithValueHandler:^(NSData *value) {
482 [NSString stringWithUTF8String:dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)];
483 XCTAssert([label isEqualToString:queue_label]);
485 [expectation2 fulfill];
487 completionHandler:^(NSError *errorOrNil){
490 [call2 startWithWriteable:responsesWriteable2];
492 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
495 - (void)testTimeout {
496 __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
498 GRXBufferedPipe *pipe = [GRXBufferedPipe pipe];
499 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
500 path:kFullDuplexCallMethod.HTTPPath
501 requestsWriter:pipe];
503 id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc]
504 initWithValueHandler:^(NSData *value) {
505 XCTAssert(0, @"Failure: response received; Expect: no response received.");
507 completionHandler:^(NSError *errorOrNil) {
508 XCTAssertNotNil(errorOrNil,
509 @"Failure: no error received; Expect: receive deadline exceeded.");
510 XCTAssertEqual(errorOrNil.code, GRPCErrorCodeDeadlineExceeded);
511 [completion fulfill];
514 call.timeout = 0.001;
515 [call startWithWriteable:responsesWriteable];
517 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
520 - (int)findFreePort {
521 struct sockaddr_in addr;
522 unsigned int addr_len = sizeof(addr);
523 memset(&addr, 0, sizeof(addr));
524 addr.sin_family = AF_INET;
525 int fd = socket(AF_INET, SOCK_STREAM, 0);
526 XCTAssertEqual(bind(fd, (struct sockaddr *)&addr, sizeof(addr)), 0);
527 XCTAssertEqual(getsockname(fd, (struct sockaddr *)&addr, &addr_len), 0);
528 XCTAssertEqual(addr_len, sizeof(addr));
530 return addr.sin_port;
533 - (void)testErrorCode {
534 int port = [self findFreePort];
535 NSString *const kDummyAddress = [NSString stringWithFormat:@"localhost:%d", port];
536 __weak XCTestExpectation *completion =
537 [self expectationWithDescription:@"Received correct error code."];
539 GRPCCall *call = [[GRPCCall alloc] initWithHost:kDummyAddress
540 path:kEmptyCallMethod.HTTPPath
541 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
543 id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc]
544 initWithValueHandler:^(NSData *value) {
545 // Should not reach here
548 completionHandler:^(NSError *errorOrNil) {
549 XCTAssertNotNil(errorOrNil, @"Finished with no error");
550 XCTAssertEqual(errorOrNil.code, GRPC_STATUS_UNAVAILABLE);
551 [completion fulfill];
554 [call startWithWriteable:responsesWriteable];
556 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
559 - (void)testTimeoutBackoffWithTimeout:(double)timeout Backoff:(double)backoff {
560 const double maxConnectTime = timeout > backoff ? timeout : backoff;
561 const double kMargin = 0.1;
563 __weak XCTestExpectation *completion = [self expectationWithDescription:@"Timeout in a second."];
564 NSString *const kDummyAddress = [NSString stringWithFormat:@"8.8.8.8:1"];
565 [GRPCCall useInsecureConnectionsForHost:kDummyAddress];
566 [GRPCCall setMinConnectTimeout:timeout * 1000
567 initialBackoff:backoff * 1000
569 forHost:kDummyAddress];
570 GRPCCall *call = [[GRPCCall alloc] initWithHost:kDummyAddress
572 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
573 NSDate *startTime = [NSDate date];
574 id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc]
575 initWithValueHandler:^(id value) {
576 XCTAssert(NO, @"Received message. Should not reach here");
578 completionHandler:^(NSError *errorOrNil) {
579 XCTAssertNotNil(errorOrNil, @"Finished with no error");
580 // The call must fail before maxConnectTime. However there is no lower bound on the time
581 // taken for connection. A shorter time happens when connection is actively refused
582 // by 8.8.8.8:1 before maxConnectTime elapsed.
583 XCTAssertLessThan([[NSDate date] timeIntervalSinceDate:startTime],
584 maxConnectTime + kMargin);
585 [completion fulfill];
588 [call startWithWriteable:responsesWriteable];
590 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
593 // The numbers of the following three tests are selected to be smaller than the default values of
594 // initial backoff (1s) and min_connect_timeout (20s), so that if they fail we know the default
595 // values fail to be overridden by the channel args.
596 - (void)testTimeoutBackoff1 {
597 [self testTimeoutBackoffWithTimeout:0.7 Backoff:0.3];
600 - (void)testTimeoutBackoff2 {
601 [self testTimeoutBackoffWithTimeout:0.3 Backoff:0.7];
604 - (void)testErrorDebugInformation {
605 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"RPC unauthorized."];
607 RMTSimpleRequest *request = [RMTSimpleRequest message];
608 request.fillUsername = YES;
609 request.fillOauthScope = YES;
610 GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]];
612 GRPCCall *call = [[GRPCCall alloc] initWithHost:kRemoteSSLHost
613 path:kUnaryCallMethod.HTTPPath
614 requestsWriter:requestsWriter];
616 call.oauth2AccessToken = @"bogusToken";
618 id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc]
619 initWithValueHandler:^(NSData *value) {
620 XCTFail(@"Received unexpected response: %@", value);
622 completionHandler:^(NSError *errorOrNil) {
623 XCTAssertNotNil(errorOrNil, @"Finished without error!");
624 NSDictionary *userInfo = errorOrNil.userInfo;
625 NSString *debugInformation = userInfo[NSDebugDescriptionErrorKey];
626 XCTAssertNotNil(debugInformation);
627 XCTAssertNotEqual([debugInformation length], 0);
628 NSString *challengeHeader = call.oauth2ChallengeHeader;
629 XCTAssertGreaterThan(challengeHeader.length, 0, @"No challenge in response headers %@",
630 call.responseHeaders);
631 [expectation fulfill];
634 [call startWithWriteable:responsesWriteable];
636 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];