xref: /aosp_15_r20/external/grpc-grpc/src/objective-c/tests/UnitTests/APIv2Tests.m (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1/*
2 *
3 * Copyright 2018 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 <GRPCClient/GRPCCall.h>
20#import <ProtoRPC/ProtoMethod.h>
21#import <XCTest/XCTest.h>
22#import "src/objective-c/tests/RemoteTestClient/Messages.pbobjc.h"
23
24#include <grpc/grpc.h>
25#include <grpc/support/port_platform.h>
26
27#import "../Common/GRPCBlockCallbackResponseHandler.h"
28#import "../Common/TestUtils.h"
29#import "../version.h"
30
31// Package and service name of test server
32static NSString *const kPackage = @"grpc.testing";
33static NSString *const kService = @"TestService";
34
35static GRPCProtoMethod *kInexistentMethod;
36static GRPCProtoMethod *kEmptyCallMethod;
37static GRPCProtoMethod *kUnaryCallMethod;
38static GRPCProtoMethod *kOutputStreamingCallMethod;
39static GRPCProtoMethod *kFullDuplexCallMethod;
40
41static const int kSimpleDataLength = 100;
42
43static const NSTimeInterval kTestTimeout = 8;
44static const NSTimeInterval kInvertedTimeout = 2;
45
46// Reveal the _class ivar for testing access
47@interface GRPCCall2 () {
48 @public
49  GRPCCall *_call;
50}
51
52@end
53
54@interface CallAPIv2Tests : XCTestCase <GRPCAuthorizationProtocol>
55
56@end
57
58@implementation CallAPIv2Tests
59
60+ (void)setUp {
61  GRPCPrintInteropTestServerDebugInfo();
62}
63
64- (void)setUp {
65  // This method isn't implemented by the remote server.
66  kInexistentMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage
67                                                       service:kService
68                                                        method:@"Inexistent"];
69  kEmptyCallMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage
70                                                      service:kService
71                                                       method:@"EmptyCall"];
72  kUnaryCallMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage
73                                                      service:kService
74                                                       method:@"UnaryCall"];
75  kOutputStreamingCallMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage
76                                                                service:kService
77                                                                 method:@"StreamingOutputCall"];
78  kFullDuplexCallMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage
79                                                           service:kService
80                                                            method:@"FullDuplexCall"];
81}
82
83- (void)testUserAgentPrefix {
84  __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
85  __weak XCTestExpectation *recvInitialMd =
86      [self expectationWithDescription:@"Did not receive initial md."];
87
88  GRPCRequestOptions *request =
89      [[GRPCRequestOptions alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText()
90                                          path:kEmptyCallMethod.HTTPPath
91                                        safety:GRPCCallSafetyDefault];
92  NSDictionary *headers =
93      [NSDictionary dictionaryWithObjectsAndKeys:@"", @"x-grpc-test-echo-useragent", nil];
94  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
95  options.transportType = GRPCTransportTypeInsecure;
96  options.userAgentPrefix = @"Foo";
97  options.initialMetadata = headers;
98  GRPCCall2 *call = [[GRPCCall2 alloc]
99      initWithRequestOptions:request
100             responseHandler:
101                 [[GRPCBlockCallbackResponseHandler alloc]
102                     initWithInitialMetadataCallback:^(NSDictionary *initialMetadata) {
103                       NSString *userAgent = initialMetadata[@"x-grpc-test-echo-useragent"];
104                       // Test the regex is correct
105                       NSString *expectedUserAgent = @"Foo grpc-objc-cfstream/";
106                       expectedUserAgent =
107                           [expectedUserAgent stringByAppendingString:GRPC_OBJC_VERSION_STRING];
108                       expectedUserAgent = [expectedUserAgent stringByAppendingString:@" grpc-c/"];
109                       expectedUserAgent =
110                           [expectedUserAgent stringByAppendingString:GRPC_C_VERSION_STRING];
111                       expectedUserAgent = [expectedUserAgent stringByAppendingString:@" ("];
112                       expectedUserAgent =
113                           [expectedUserAgent stringByAppendingString:@GPR_PLATFORM_STRING];
114                       expectedUserAgent = [expectedUserAgent stringByAppendingString:@"; chttp2)"];
115                       XCTAssertEqualObjects(userAgent, expectedUserAgent);
116
117                       NSError *error = nil;
118                       // Change in format of user-agent field in a direction that does not match
119                       // the regex will likely cause problem for certain gRPC users. For details,
120                       // refer to internal doc https://goo.gl/c2diBc
121                       NSRegularExpression *regex = [NSRegularExpression
122                           regularExpressionWithPattern:
123                               @" grpc-[a-zA-Z0-9]+(-[a-zA-Z0-9]+)?/[^ ,]+( \\([^)]*\\))?"
124                                                options:0
125                                                  error:&error];
126
127                       NSString *customUserAgent = [regex
128                           stringByReplacingMatchesInString:userAgent
129                                                    options:0
130                                                      range:NSMakeRange(0, [userAgent length])
131                                               withTemplate:@""];
132                       XCTAssertEqualObjects(customUserAgent, @"Foo");
133                       [recvInitialMd fulfill];
134                     }
135                     messageCallback:^(id message) {
136                       XCTAssertNotNil(message);
137                       XCTAssertEqual([message length], 0, @"Non-empty response received: %@",
138                                      message);
139                     }
140                     closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
141                       if (error) {
142                         XCTFail(@"Finished with unexpected error: %@", error);
143                       } else {
144                         [completion fulfill];
145                       }
146                     }]
147                 callOptions:options];
148  [call writeData:[NSData data]];
149  [call start];
150
151  [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
152}
153
154- (void)getTokenWithHandler:(void (^)(NSString *token))handler {
155  dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
156  dispatch_sync(queue, ^{
157    handler(@"test-access-token");
158  });
159}
160
161- (void)testOAuthToken {
162  __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
163
164  GRPCRequestOptions *requestOptions =
165      [[GRPCRequestOptions alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText()
166                                          path:kEmptyCallMethod.HTTPPath
167                                        safety:GRPCCallSafetyDefault];
168  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
169  options.transportType = GRPCTransportTypeInsecure;
170  options.authTokenProvider = self;
171  __block GRPCCall2 *call = [[GRPCCall2 alloc]
172      initWithRequestOptions:requestOptions
173             responseHandler:[[GRPCBlockCallbackResponseHandler alloc]
174                                 initWithInitialMetadataCallback:nil
175                                                 messageCallback:nil
176                                                   closeCallback:^(NSDictionary *trailingMetadata,
177                                                                   NSError *error) {
178                                                     [completion fulfill];
179                                                   }]
180                 callOptions:options];
181  [call writeData:[NSData data]];
182  [call start];
183  [call finish];
184
185  [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
186}
187
188- (void)testResponseSizeLimitExceeded {
189  __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
190
191  GRPCRequestOptions *requestOptions =
192      [[GRPCRequestOptions alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText()
193                                          path:kUnaryCallMethod.HTTPPath
194                                        safety:GRPCCallSafetyDefault];
195  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
196  options.responseSizeLimit = kSimpleDataLength;
197  options.transportType = GRPCTransportTypeInsecure;
198
199  RMTSimpleRequest *request = [RMTSimpleRequest message];
200  request.payload.body = [NSMutableData dataWithLength:options.responseSizeLimit];
201  request.responseSize = (int32_t)(options.responseSizeLimit * 2);
202
203  GRPCCall2 *call = [[GRPCCall2 alloc]
204      initWithRequestOptions:requestOptions
205             responseHandler:[[GRPCBlockCallbackResponseHandler alloc]
206                                 initWithInitialMetadataCallback:nil
207                                                 messageCallback:nil
208                                                   closeCallback:^(NSDictionary *trailingMetadata,
209                                                                   NSError *error) {
210                                                     XCTAssertNotNil(error,
211                                                                     @"Expecting non-nil error");
212                                                     XCTAssertEqual(error.code,
213                                                                    GRPCErrorCodeResourceExhausted);
214                                                     [completion fulfill];
215                                                   }]
216                 callOptions:options];
217  [call writeData:[request data]];
218  [call start];
219  [call finish];
220
221  [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
222}
223
224- (void)testTimeout {
225  __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
226
227  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
228  options.timeout = 0.001;
229  options.transportType = GRPCTransportTypeInsecure;
230  GRPCRequestOptions *requestOptions =
231      [[GRPCRequestOptions alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText()
232                                          path:kFullDuplexCallMethod.HTTPPath
233                                        safety:GRPCCallSafetyDefault];
234
235  GRPCCall2 *call = [[GRPCCall2 alloc]
236      initWithRequestOptions:requestOptions
237             responseHandler:
238                 [[GRPCBlockCallbackResponseHandler alloc] initWithInitialMetadataCallback:nil
239                     messageCallback:^(NSData *data) {
240                       XCTFail(@"Failure: response received; Expect: no response received.");
241                     }
242                     closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
243                       XCTAssertNotNil(error, @"Failure: no error received; Expect: receive "
244                                              @"deadline exceeded.");
245                       if (error.code != GRPCErrorCodeDeadlineExceeded) {
246                         NSLog(@"Unexpected error: %@", error);
247                       }
248                       XCTAssertEqual(error.code, GRPCErrorCodeDeadlineExceeded);
249                       [completion fulfill];
250                     }]
251                 callOptions:options];
252
253  [call start];
254
255  [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
256}
257
258- (void)testTimeoutBackoffWithTimeout:(double)timeout Backoff:(double)backoff {
259  const double maxConnectTime = timeout > backoff ? timeout : backoff;
260  const double kMargin = 0.1;
261
262  __weak XCTestExpectation *completion = [self expectationWithDescription:@"Timeout in a second."];
263  NSString *const kPhonyAddress = [NSString stringWithFormat:@"127.0.0.1:10000"];
264  GRPCRequestOptions *requestOptions =
265      [[GRPCRequestOptions alloc] initWithHost:kPhonyAddress
266                                          path:@"/phony/path"
267                                        safety:GRPCCallSafetyDefault];
268  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
269  options.connectMinTimeout = timeout;
270  options.connectInitialBackoff = backoff;
271  options.connectMaxBackoff = 0;
272
273  NSDate *startTime = [NSDate date];
274  GRPCCall2 *call = [[GRPCCall2 alloc]
275      initWithRequestOptions:requestOptions
276             responseHandler:[[GRPCBlockCallbackResponseHandler alloc]
277                                 initWithInitialMetadataCallback:nil
278                                 messageCallback:^(NSData *data) {
279                                   XCTFail(@"Received message. Should not reach here.");
280                                 }
281                                 closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
282                                   XCTAssertNotNil(error,
283                                                   @"Finished with no error; expecting error");
284                                   XCTAssertLessThan(
285                                       [[NSDate date] timeIntervalSinceDate:startTime],
286                                       maxConnectTime + kMargin);
287                                   [completion fulfill];
288                                 }]
289                 callOptions:options];
290
291  [call start];
292
293  [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
294}
295
296- (void)testTimeoutBackoff1 {
297  [self testTimeoutBackoffWithTimeout:0.7 Backoff:0.4];
298}
299
300- (void)testTimeoutBackoff2 {
301  [self testTimeoutBackoffWithTimeout:0.3 Backoff:0.8];
302}
303
304- (void)testCompression {
305  __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
306
307  RMTSimpleRequest *request = [RMTSimpleRequest message];
308  request.expectCompressed = [RMTBoolValue message];
309  request.expectCompressed.value = YES;
310  request.responseCompressed = [RMTBoolValue message];
311  request.expectCompressed.value = YES;
312  request.responseSize = kSimpleDataLength;
313  request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength];
314  GRPCRequestOptions *requestOptions =
315      [[GRPCRequestOptions alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText()
316                                          path:kUnaryCallMethod.HTTPPath
317                                        safety:GRPCCallSafetyDefault];
318
319  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
320  options.transportType = GRPCTransportTypeInsecure;
321  options.compressionAlgorithm = GRPCCompressGzip;
322  GRPCCall2 *call = [[GRPCCall2 alloc]
323      initWithRequestOptions:requestOptions
324             responseHandler:[[GRPCBlockCallbackResponseHandler alloc]
325                                 initWithInitialMetadataCallback:nil
326                                 messageCallback:^(NSData *data) {
327                                   NSError *error;
328                                   RMTSimpleResponse *response =
329                                       [RMTSimpleResponse parseFromData:data error:&error];
330                                   XCTAssertNil(error, @"Error when parsing response: %@", error);
331                                   XCTAssertEqual(response.payload.body.length, kSimpleDataLength);
332                                 }
333                                 closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
334                                   XCTAssertNil(error, @"Received failure: %@", error);
335                                   [completion fulfill];
336                                 }]
337
338                 callOptions:options];
339
340  [call start];
341  [call writeData:[request data]];
342  [call finish];
343
344  [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
345}
346
347- (void)testFlowControlWrite {
348  __weak XCTestExpectation *expectWriteData =
349      [self expectationWithDescription:@"Reported write data"];
350
351  RMTStreamingOutputCallRequest *request = [RMTStreamingOutputCallRequest message];
352  RMTResponseParameters *parameters = [RMTResponseParameters message];
353  parameters.size = kSimpleDataLength;
354  [request.responseParametersArray addObject:parameters];
355  request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength];
356
357  GRPCRequestOptions *callRequest =
358      [[GRPCRequestOptions alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText()
359                                          path:kUnaryCallMethod.HTTPPath
360                                        safety:GRPCCallSafetyDefault];
361  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
362  options.transportType = GRPCTransportTypeInsecure;
363  options.flowControlEnabled = YES;
364  GRPCCall2 *call =
365      [[GRPCCall2 alloc] initWithRequestOptions:callRequest
366                                responseHandler:[[GRPCBlockCallbackResponseHandler alloc]
367                                                    initWithInitialMetadataCallback:nil
368                                                                    messageCallback:nil
369                                                                      closeCallback:nil
370                                                                  writeDataCallback:^{
371                                                                    [expectWriteData fulfill];
372                                                                  }]
373                                    callOptions:options];
374
375  [call start];
376  [call receiveNextMessages:1];
377  [call writeData:[request data]];
378
379  // Wait for 3 seconds and make sure we do not receive the response
380  [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
381
382  [call finish];
383}
384
385- (void)testFlowControlRead {
386  __weak __block XCTestExpectation *expectBlockedMessage =
387      [self expectationWithDescription:@"Message not delivered without recvNextMessage"];
388  __weak __block XCTestExpectation *expectPassedMessage = nil;
389  __weak __block XCTestExpectation *expectBlockedClose =
390      [self expectationWithDescription:@"Call not closed with pending message"];
391  __weak __block XCTestExpectation *expectPassedClose = nil;
392  expectBlockedMessage.inverted = YES;
393  expectBlockedClose.inverted = YES;
394
395  RMTSimpleRequest *request = [RMTSimpleRequest message];
396  request.responseSize = kSimpleDataLength;
397  request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength];
398
399  GRPCRequestOptions *callRequest =
400      [[GRPCRequestOptions alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText()
401                                          path:kUnaryCallMethod.HTTPPath
402                                        safety:GRPCCallSafetyDefault];
403  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
404  options.transportType = GRPCTransportTypeInsecure;
405  options.flowControlEnabled = YES;
406  __block int unblocked = NO;
407  GRPCCall2 *call = [[GRPCCall2 alloc]
408      initWithRequestOptions:callRequest
409             responseHandler:[[GRPCBlockCallbackResponseHandler alloc]
410                                 initWithInitialMetadataCallback:nil
411                                 messageCallback:^(NSData *message) {
412                                   if (!unblocked) {
413                                     [expectBlockedMessage fulfill];
414                                   } else {
415                                     [expectPassedMessage fulfill];
416                                   }
417                                 }
418                                 closeCallback:^(NSDictionary *trailers, NSError *error) {
419                                   if (!unblocked) {
420                                     [expectBlockedClose fulfill];
421                                   } else {
422                                     [expectPassedClose fulfill];
423                                   }
424                                 }]
425                 callOptions:options];
426
427  [call start];
428  [call writeData:[request data]];
429  [call finish];
430
431  // Wait to make sure we do not receive the response
432  [self waitForExpectationsWithTimeout:kInvertedTimeout handler:nil];
433
434  expectPassedMessage =
435      [self expectationWithDescription:@"Message delivered with receiveNextMessage"];
436  expectPassedClose = [self expectationWithDescription:@"Close delivered after receiveNextMessage"];
437
438  unblocked = YES;
439  [call receiveNextMessages:1];
440
441  [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
442}
443
444- (void)testFlowControlMultipleMessages {
445  __weak XCTestExpectation *expectPassedMessage =
446      [self expectationWithDescription:@"two messages delivered with receiveNextMessage"];
447  expectPassedMessage.expectedFulfillmentCount = 2;
448  __weak XCTestExpectation *expectBlockedMessage =
449      [self expectationWithDescription:@"Message 3 not delivered"];
450  expectBlockedMessage.inverted = YES;
451  __weak XCTestExpectation *expectWriteTwice =
452      [self expectationWithDescription:@"Write 2 messages done"];
453  expectWriteTwice.expectedFulfillmentCount = 2;
454
455  RMTStreamingOutputCallRequest *request = [RMTStreamingOutputCallRequest message];
456  RMTResponseParameters *parameters = [RMTResponseParameters message];
457  parameters.size = kSimpleDataLength;
458  [request.responseParametersArray addObject:parameters];
459  request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength];
460
461  GRPCRequestOptions *callRequest =
462      [[GRPCRequestOptions alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText()
463                                          path:kFullDuplexCallMethod.HTTPPath
464                                        safety:GRPCCallSafetyDefault];
465  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
466  options.transportType = GRPCTransportTypeInsecure;
467  options.flowControlEnabled = YES;
468  __block NSUInteger messageId = 0;
469  __block GRPCCall2 *call =
470      [[GRPCCall2 alloc] initWithRequestOptions:callRequest
471                                responseHandler:[[GRPCBlockCallbackResponseHandler alloc]
472                                                    initWithInitialMetadataCallback:nil
473                                                    messageCallback:^(NSData *message) {
474                                                      if (messageId <= 1) {
475                                                        [expectPassedMessage fulfill];
476                                                      } else {
477                                                        [expectBlockedMessage fulfill];
478                                                      }
479                                                      messageId++;
480                                                    }
481                                                    closeCallback:nil
482                                                    writeDataCallback:^{
483                                                      [expectWriteTwice fulfill];
484                                                    }]
485                                    callOptions:options];
486
487  [call receiveNextMessages:2];
488  [call start];
489  [call writeData:[request data]];
490  [call writeData:[request data]];
491
492  [self waitForExpectationsWithTimeout:kInvertedTimeout handler:nil];
493}
494
495- (void)testFlowControlReadReadyBeforeStart {
496  __weak XCTestExpectation *expectPassedMessage =
497      [self expectationWithDescription:@"Message delivered with receiveNextMessage"];
498  __weak XCTestExpectation *expectPassedClose =
499      [self expectationWithDescription:@"Close delivered with receiveNextMessage"];
500
501  RMTSimpleRequest *request = [RMTSimpleRequest message];
502  request.responseSize = kSimpleDataLength;
503  request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength];
504
505  GRPCRequestOptions *callRequest =
506      [[GRPCRequestOptions alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText()
507                                          path:kUnaryCallMethod.HTTPPath
508                                        safety:GRPCCallSafetyDefault];
509  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
510  options.transportType = GRPCTransportTypeInsecure;
511  options.flowControlEnabled = YES;
512  __block BOOL closed = NO;
513  GRPCCall2 *call = [[GRPCCall2 alloc]
514      initWithRequestOptions:callRequest
515             responseHandler:[[GRPCBlockCallbackResponseHandler alloc]
516                                 initWithInitialMetadataCallback:nil
517                                 messageCallback:^(NSData *message) {
518                                   [expectPassedMessage fulfill];
519                                   XCTAssertFalse(closed);
520                                 }
521                                 closeCallback:^(NSDictionary *ttrailers, NSError *error) {
522                                   closed = YES;
523                                   [expectPassedClose fulfill];
524                                 }]
525                 callOptions:options];
526
527  [call receiveNextMessages:1];
528  [call start];
529  [call writeData:[request data]];
530  [call finish];
531
532  [self waitForExpectationsWithTimeout:kInvertedTimeout handler:nil];
533}
534
535- (void)testFlowControlReadReadyAfterStart {
536  __weak XCTestExpectation *expectPassedMessage =
537      [self expectationWithDescription:@"Message delivered with receiveNextMessage"];
538  __weak XCTestExpectation *expectPassedClose =
539      [self expectationWithDescription:@"Close delivered with receiveNextMessage"];
540
541  RMTStreamingOutputCallRequest *request = [RMTStreamingOutputCallRequest message];
542  RMTResponseParameters *parameters = [RMTResponseParameters message];
543  parameters.size = kSimpleDataLength;
544  [request.responseParametersArray addObject:parameters];
545  request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength];
546
547  GRPCRequestOptions *callRequest =
548      [[GRPCRequestOptions alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText()
549                                          path:kUnaryCallMethod.HTTPPath
550                                        safety:GRPCCallSafetyDefault];
551  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
552  options.transportType = GRPCTransportTypeInsecure;
553  options.flowControlEnabled = YES;
554  __block BOOL closed = NO;
555  GRPCCall2 *call = [[GRPCCall2 alloc]
556      initWithRequestOptions:callRequest
557             responseHandler:[[GRPCBlockCallbackResponseHandler alloc]
558                                 initWithInitialMetadataCallback:nil
559                                 messageCallback:^(NSData *message) {
560                                   [expectPassedMessage fulfill];
561                                   XCTAssertFalse(closed);
562                                 }
563                                 closeCallback:^(NSDictionary *trailers, NSError *error) {
564                                   closed = YES;
565                                   [expectPassedClose fulfill];
566                                 }]
567                 callOptions:options];
568
569  [call start];
570  [call receiveNextMessages:1];
571  [call writeData:[request data]];
572  [call finish];
573
574  [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
575}
576
577- (void)testFlowControlReadNonBlockingFailure {
578  __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
579
580  GRPCRequestOptions *requestOptions =
581      [[GRPCRequestOptions alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText()
582                                          path:kUnaryCallMethod.HTTPPath
583                                        safety:GRPCCallSafetyDefault];
584  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
585  options.flowControlEnabled = YES;
586  options.transportType = GRPCTransportTypeInsecure;
587
588  RMTSimpleRequest *request = [RMTSimpleRequest message];
589  request.payload.body = [NSMutableData dataWithLength:options.responseSizeLimit];
590
591  RMTEchoStatus *status = [RMTEchoStatus message];
592  status.code = 2;
593  status.message = @"test";
594  request.responseStatus = status;
595
596  GRPCCall2 *call = [[GRPCCall2 alloc]
597      initWithRequestOptions:requestOptions
598             responseHandler:[[GRPCBlockCallbackResponseHandler alloc]
599                                 initWithInitialMetadataCallback:nil
600                                 messageCallback:^(NSData *data) {
601                                   XCTFail(@"Received unexpected message");
602                                 }
603                                 closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
604                                   XCTAssertNotNil(error, @"Expecting non-nil error");
605                                   XCTAssertEqual(error.code, 2);
606                                   [completion fulfill];
607                                 }]
608                 callOptions:options];
609  [call writeData:[request data]];
610  [call start];
611  [call finish];
612
613  [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
614}
615
616@end
617