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