• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "../version.h"
28
29// The server address is derived from preprocessor macro, which is
30// in turn derived from environment variable of the same name.
31#define NSStringize_helper(x) #x
32#define NSStringize(x) @NSStringize_helper(x)
33static NSString *const kHostAddress = NSStringize(HOST_PORT_LOCAL);
34static NSString *const kRemoteSSLHost = NSStringize(HOST_PORT_REMOTE);
35
36// Package and service name of test server
37static NSString *const kPackage = @"grpc.testing";
38static NSString *const kService = @"TestService";
39
40static GRPCProtoMethod *kInexistentMethod;
41static GRPCProtoMethod *kEmptyCallMethod;
42static GRPCProtoMethod *kUnaryCallMethod;
43static GRPCProtoMethod *kOutputStreamingCallMethod;
44static GRPCProtoMethod *kFullDuplexCallMethod;
45
46static const int kSimpleDataLength = 100;
47
48static const NSTimeInterval kTestTimeout = 8;
49static const NSTimeInterval kInvertedTimeout = 2;
50
51// Reveal the _class ivar for testing access
52@interface GRPCCall2 () {
53 @public
54  GRPCCall *_call;
55}
56
57@end
58
59// Convenience class to use blocks as callbacks
60@interface ClientTestsBlockCallbacks : NSObject <GRPCResponseHandler>
61
62- (instancetype)initWithInitialMetadataCallback:(void (^)(NSDictionary *))initialMetadataCallback
63                                messageCallback:(void (^)(id))messageCallback
64                                  closeCallback:(void (^)(NSDictionary *, NSError *))closeCallback
65                              writeDataCallback:(void (^)(void))writeDataCallback;
66
67- (instancetype)initWithInitialMetadataCallback:(void (^)(NSDictionary *))initialMetadataCallback
68                                messageCallback:(void (^)(id))messageCallback
69                                  closeCallback:(void (^)(NSDictionary *, NSError *))closeCallback;
70
71@end
72
73@implementation ClientTestsBlockCallbacks {
74  void (^_initialMetadataCallback)(NSDictionary *);
75  void (^_messageCallback)(id);
76  void (^_closeCallback)(NSDictionary *, NSError *);
77  void (^_writeDataCallback)(void);
78  dispatch_queue_t _dispatchQueue;
79}
80
81- (instancetype)initWithInitialMetadataCallback:(void (^)(NSDictionary *))initialMetadataCallback
82                                messageCallback:(void (^)(id))messageCallback
83                                  closeCallback:(void (^)(NSDictionary *, NSError *))closeCallback
84                              writeDataCallback:(void (^)(void))writeDataCallback {
85  if ((self = [super init])) {
86    _initialMetadataCallback = initialMetadataCallback;
87    _messageCallback = messageCallback;
88    _closeCallback = closeCallback;
89    _writeDataCallback = writeDataCallback;
90    _dispatchQueue = dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL);
91  }
92  return self;
93}
94
95- (instancetype)initWithInitialMetadataCallback:(void (^)(NSDictionary *))initialMetadataCallback
96                                messageCallback:(void (^)(id))messageCallback
97                                  closeCallback:(void (^)(NSDictionary *, NSError *))closeCallback {
98  return [self initWithInitialMetadataCallback:initialMetadataCallback
99                               messageCallback:messageCallback
100                                 closeCallback:closeCallback
101                             writeDataCallback:nil];
102}
103
104- (void)didReceiveInitialMetadata:(NSDictionary *)initialMetadata {
105  if (self->_initialMetadataCallback) {
106    self->_initialMetadataCallback(initialMetadata);
107  }
108}
109
110- (void)didReceiveRawMessage:(GPBMessage *)message {
111  if (self->_messageCallback) {
112    self->_messageCallback(message);
113  }
114}
115
116- (void)didCloseWithTrailingMetadata:(NSDictionary *)trailingMetadata error:(NSError *)error {
117  if (self->_closeCallback) {
118    self->_closeCallback(trailingMetadata, error);
119  }
120}
121
122- (void)didWriteData {
123  if (self->_writeDataCallback) {
124    self->_writeDataCallback();
125  }
126}
127
128- (dispatch_queue_t)dispatchQueue {
129  return _dispatchQueue;
130}
131
132@end
133
134@interface CallAPIv2Tests : XCTestCase <GRPCAuthorizationProtocol>
135
136@end
137
138@implementation CallAPIv2Tests
139
140- (void)setUp {
141  // This method isn't implemented by the remote server.
142  kInexistentMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage
143                                                       service:kService
144                                                        method:@"Inexistent"];
145  kEmptyCallMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage
146                                                      service:kService
147                                                       method:@"EmptyCall"];
148  kUnaryCallMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage
149                                                      service:kService
150                                                       method:@"UnaryCall"];
151  kOutputStreamingCallMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage
152                                                                service:kService
153                                                                 method:@"StreamingOutputCall"];
154  kFullDuplexCallMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage
155                                                           service:kService
156                                                            method:@"FullDuplexCall"];
157}
158
159- (void)testMetadata {
160  __weak XCTestExpectation *expectation = [self expectationWithDescription:@"RPC unauthorized."];
161
162  RMTSimpleRequest *request = [RMTSimpleRequest message];
163  request.fillUsername = YES;
164  request.fillOauthScope = YES;
165
166  GRPCRequestOptions *callRequest =
167      [[GRPCRequestOptions alloc] initWithHost:(NSString *)kRemoteSSLHost
168                                          path:kUnaryCallMethod.HTTPPath
169                                        safety:GRPCCallSafetyDefault];
170  __block NSDictionary *init_md;
171  __block NSDictionary *trailing_md;
172  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
173  options.oauth2AccessToken = @"bogusToken";
174  GRPCCall2 *call = [[GRPCCall2 alloc]
175      initWithRequestOptions:callRequest
176             responseHandler:[[ClientTestsBlockCallbacks alloc]
177                                 initWithInitialMetadataCallback:^(NSDictionary *initialMetadata) {
178                                   init_md = initialMetadata;
179                                 }
180                                 messageCallback:^(id message) {
181                                   XCTFail(@"Received unexpected response.");
182                                 }
183                                 closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
184                                   trailing_md = trailingMetadata;
185                                   if (error) {
186                                     XCTAssertEqual(error.code, 16,
187                                                    @"Finished with unexpected error: %@", error);
188                                     XCTAssertEqualObjects(init_md,
189                                                           error.userInfo[kGRPCHeadersKey]);
190                                     XCTAssertEqualObjects(trailing_md,
191                                                           error.userInfo[kGRPCTrailersKey]);
192                                     NSString *challengeHeader = init_md[@"www-authenticate"];
193                                     XCTAssertGreaterThan(challengeHeader.length, 0,
194                                                          @"No challenge in response headers %@",
195                                                          init_md);
196                                     [expectation fulfill];
197                                   }
198                                 }]
199                 callOptions:options];
200
201  [call start];
202  [call writeData:[request data]];
203  [call finish];
204
205  [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
206}
207
208- (void)testUserAgentPrefix {
209  __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
210  __weak XCTestExpectation *recvInitialMd =
211      [self expectationWithDescription:@"Did not receive initial md."];
212
213  GRPCRequestOptions *request = [[GRPCRequestOptions alloc] initWithHost:kHostAddress
214                                                                    path:kEmptyCallMethod.HTTPPath
215                                                                  safety:GRPCCallSafetyDefault];
216  NSDictionary *headers =
217      [NSDictionary dictionaryWithObjectsAndKeys:@"", @"x-grpc-test-echo-useragent", nil];
218  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
219  options.transportType = GRPCTransportTypeInsecure;
220  options.userAgentPrefix = @"Foo";
221  options.initialMetadata = headers;
222  GRPCCall2 *call = [[GRPCCall2 alloc]
223      initWithRequestOptions:request
224             responseHandler:
225                 [[ClientTestsBlockCallbacks alloc]
226                     initWithInitialMetadataCallback:^(NSDictionary *initialMetadata) {
227                       NSString *userAgent = initialMetadata[@"x-grpc-test-echo-useragent"];
228                       // Test the regex is correct
229                       NSString *expectedUserAgent = @"Foo grpc-objc-cfstream/";
230                       expectedUserAgent =
231                           [expectedUserAgent stringByAppendingString:GRPC_OBJC_VERSION_STRING];
232                       expectedUserAgent = [expectedUserAgent stringByAppendingString:@" grpc-c/"];
233                       expectedUserAgent =
234                           [expectedUserAgent stringByAppendingString:GRPC_C_VERSION_STRING];
235                       expectedUserAgent = [expectedUserAgent stringByAppendingString:@" ("];
236                       expectedUserAgent =
237                           [expectedUserAgent stringByAppendingString:@GPR_PLATFORM_STRING];
238                       expectedUserAgent = [expectedUserAgent stringByAppendingString:@"; chttp2)"];
239                       XCTAssertEqualObjects(userAgent, expectedUserAgent);
240
241                       NSError *error = nil;
242                       // Change in format of user-agent field in a direction that does not match
243                       // the regex will likely cause problem for certain gRPC users. For details,
244                       // refer to internal doc https://goo.gl/c2diBc
245                       NSRegularExpression *regex = [NSRegularExpression
246                           regularExpressionWithPattern:
247                               @" grpc-[a-zA-Z0-9]+(-[a-zA-Z0-9]+)?/[^ ,]+( \\([^)]*\\))?"
248                                                options:0
249                                                  error:&error];
250
251                       NSString *customUserAgent = [regex
252                           stringByReplacingMatchesInString:userAgent
253                                                    options:0
254                                                      range:NSMakeRange(0, [userAgent length])
255                                               withTemplate:@""];
256                       XCTAssertEqualObjects(customUserAgent, @"Foo");
257                       [recvInitialMd fulfill];
258                     }
259                     messageCallback:^(id message) {
260                       XCTAssertNotNil(message);
261                       XCTAssertEqual([message length], 0, @"Non-empty response received: %@",
262                                      message);
263                     }
264                     closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
265                       if (error) {
266                         XCTFail(@"Finished with unexpected error: %@", error);
267                       } else {
268                         [completion fulfill];
269                       }
270                     }]
271                 callOptions:options];
272  [call writeData:[NSData data]];
273  [call start];
274
275  [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
276}
277
278- (void)getTokenWithHandler:(void (^)(NSString *token))handler {
279  dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
280  dispatch_sync(queue, ^{
281    handler(@"test-access-token");
282  });
283}
284
285- (void)testOAuthToken {
286  __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
287
288  GRPCRequestOptions *requestOptions =
289      [[GRPCRequestOptions alloc] initWithHost:kHostAddress
290                                          path:kEmptyCallMethod.HTTPPath
291                                        safety:GRPCCallSafetyDefault];
292  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
293  options.transportType = GRPCTransportTypeInsecure;
294  options.authTokenProvider = self;
295  __block GRPCCall2 *call = [[GRPCCall2 alloc]
296      initWithRequestOptions:requestOptions
297             responseHandler:[[ClientTestsBlockCallbacks alloc]
298                                 initWithInitialMetadataCallback:nil
299                                                 messageCallback:nil
300                                                   closeCallback:^(NSDictionary *trailingMetadata,
301                                                                   NSError *error) {
302                                                     [completion fulfill];
303                                                   }]
304                 callOptions:options];
305  [call writeData:[NSData data]];
306  [call start];
307  [call finish];
308
309  [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
310}
311
312- (void)testResponseSizeLimitExceeded {
313  __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
314
315  GRPCRequestOptions *requestOptions =
316      [[GRPCRequestOptions alloc] initWithHost:kHostAddress
317                                          path:kUnaryCallMethod.HTTPPath
318                                        safety:GRPCCallSafetyDefault];
319  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
320  options.responseSizeLimit = kSimpleDataLength;
321  options.transportType = GRPCTransportTypeInsecure;
322
323  RMTSimpleRequest *request = [RMTSimpleRequest message];
324  request.payload.body = [NSMutableData dataWithLength:options.responseSizeLimit];
325  request.responseSize = (int32_t)(options.responseSizeLimit * 2);
326
327  GRPCCall2 *call = [[GRPCCall2 alloc]
328      initWithRequestOptions:requestOptions
329             responseHandler:[[ClientTestsBlockCallbacks alloc]
330                                 initWithInitialMetadataCallback:nil
331                                                 messageCallback:nil
332                                                   closeCallback:^(NSDictionary *trailingMetadata,
333                                                                   NSError *error) {
334                                                     XCTAssertNotNil(error,
335                                                                     @"Expecting non-nil error");
336                                                     XCTAssertEqual(error.code,
337                                                                    GRPCErrorCodeResourceExhausted);
338                                                     [completion fulfill];
339                                                   }]
340                 callOptions:options];
341  [call writeData:[request data]];
342  [call start];
343  [call finish];
344
345  [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
346}
347
348- (void)testIdempotentProtoRPC {
349  __weak XCTestExpectation *response = [self expectationWithDescription:@"Expected response."];
350  __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
351
352  RMTSimpleRequest *request = [RMTSimpleRequest message];
353  request.responseSize = kSimpleDataLength;
354  request.fillUsername = YES;
355  request.fillOauthScope = YES;
356  GRPCRequestOptions *requestOptions =
357      [[GRPCRequestOptions alloc] initWithHost:kHostAddress
358                                          path:kUnaryCallMethod.HTTPPath
359                                        safety:GRPCCallSafetyIdempotentRequest];
360
361  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
362  options.transportType = GRPCTransportTypeInsecure;
363  GRPCCall2 *call = [[GRPCCall2 alloc]
364      initWithRequestOptions:requestOptions
365             responseHandler:[[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil
366                                 messageCallback:^(id message) {
367                                   NSData *data = (NSData *)message;
368                                   XCTAssertNotNil(data, @"nil value received as response.");
369                                   XCTAssertGreaterThan(data.length, 0,
370                                                        @"Empty response received.");
371                                   RMTSimpleResponse *responseProto =
372                                       [RMTSimpleResponse parseFromData:data error:NULL];
373                                   // We expect empty strings, not nil:
374                                   XCTAssertNotNil(responseProto.username,
375                                                   @"Response's username is nil.");
376                                   XCTAssertNotNil(responseProto.oauthScope,
377                                                   @"Response's OAuth scope is nil.");
378                                   [response fulfill];
379                                 }
380                                 closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
381                                   XCTAssertNil(error, @"Finished with unexpected error: %@",
382                                                error);
383                                   [completion fulfill];
384                                 }]
385                 callOptions:options];
386
387  [call start];
388  [call writeData:[request data]];
389  [call finish];
390
391  [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
392}
393
394- (void)testTimeout {
395  __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
396
397  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
398  options.timeout = 0.001;
399  options.transportType = GRPCTransportTypeInsecure;
400  GRPCRequestOptions *requestOptions =
401      [[GRPCRequestOptions alloc] initWithHost:kHostAddress
402                                          path:kFullDuplexCallMethod.HTTPPath
403                                        safety:GRPCCallSafetyDefault];
404
405  GRPCCall2 *call = [[GRPCCall2 alloc]
406      initWithRequestOptions:requestOptions
407             responseHandler:
408                 [[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil
409                     messageCallback:^(NSData *data) {
410                       XCTFail(@"Failure: response received; Expect: no response received.");
411                     }
412                     closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
413                       XCTAssertNotNil(error, @"Failure: no error received; Expect: receive "
414                                              @"deadline exceeded.");
415                       if (error.code != GRPCErrorCodeDeadlineExceeded) {
416                         NSLog(@"Unexpected error: %@", error);
417                       }
418                       XCTAssertEqual(error.code, GRPCErrorCodeDeadlineExceeded);
419                       [completion fulfill];
420                     }]
421                 callOptions:options];
422
423  [call start];
424
425  [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
426}
427
428- (void)testTimeoutBackoffWithTimeout:(double)timeout Backoff:(double)backoff {
429  const double maxConnectTime = timeout > backoff ? timeout : backoff;
430  const double kMargin = 0.1;
431
432  __weak XCTestExpectation *completion = [self expectationWithDescription:@"Timeout in a second."];
433  NSString *const kDummyAddress = [NSString stringWithFormat:@"127.0.0.1:10000"];
434  GRPCRequestOptions *requestOptions =
435      [[GRPCRequestOptions alloc] initWithHost:kDummyAddress
436                                          path:@"/dummy/path"
437                                        safety:GRPCCallSafetyDefault];
438  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
439  options.connectMinTimeout = timeout;
440  options.connectInitialBackoff = backoff;
441  options.connectMaxBackoff = 0;
442
443  NSDate *startTime = [NSDate date];
444  GRPCCall2 *call = [[GRPCCall2 alloc]
445      initWithRequestOptions:requestOptions
446             responseHandler:[[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil
447                                 messageCallback:^(NSData *data) {
448                                   XCTFail(@"Received message. Should not reach here.");
449                                 }
450                                 closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
451                                   XCTAssertNotNil(error,
452                                                   @"Finished with no error; expecting error");
453                                   XCTAssertLessThan(
454                                       [[NSDate date] timeIntervalSinceDate:startTime],
455                                       maxConnectTime + kMargin);
456                                   [completion fulfill];
457                                 }]
458                 callOptions:options];
459
460  [call start];
461
462  [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
463}
464
465- (void)testTimeoutBackoff1 {
466  [self testTimeoutBackoffWithTimeout:0.7 Backoff:0.4];
467}
468
469- (void)testTimeoutBackoff2 {
470  [self testTimeoutBackoffWithTimeout:0.3 Backoff:0.8];
471}
472
473- (void)testCompression {
474  __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
475
476  RMTSimpleRequest *request = [RMTSimpleRequest message];
477  request.expectCompressed = [RMTBoolValue message];
478  request.expectCompressed.value = YES;
479  request.responseCompressed = [RMTBoolValue message];
480  request.expectCompressed.value = YES;
481  request.responseSize = kSimpleDataLength;
482  request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength];
483  GRPCRequestOptions *requestOptions =
484      [[GRPCRequestOptions alloc] initWithHost:kHostAddress
485                                          path:kUnaryCallMethod.HTTPPath
486                                        safety:GRPCCallSafetyDefault];
487
488  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
489  options.transportType = GRPCTransportTypeInsecure;
490  options.compressionAlgorithm = GRPCCompressGzip;
491  GRPCCall2 *call = [[GRPCCall2 alloc]
492      initWithRequestOptions:requestOptions
493             responseHandler:[[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil
494                                 messageCallback:^(NSData *data) {
495                                   NSError *error;
496                                   RMTSimpleResponse *response =
497                                       [RMTSimpleResponse parseFromData:data error:&error];
498                                   XCTAssertNil(error, @"Error when parsing response: %@", error);
499                                   XCTAssertEqual(response.payload.body.length, kSimpleDataLength);
500                                 }
501                                 closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
502                                   XCTAssertNil(error, @"Received failure: %@", error);
503                                   [completion fulfill];
504                                 }]
505
506                 callOptions:options];
507
508  [call start];
509  [call writeData:[request data]];
510  [call finish];
511
512  [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
513}
514
515- (void)testFlowControlWrite {
516  __weak XCTestExpectation *expectWriteData =
517      [self expectationWithDescription:@"Reported write data"];
518
519  RMTStreamingOutputCallRequest *request = [RMTStreamingOutputCallRequest message];
520  RMTResponseParameters *parameters = [RMTResponseParameters message];
521  parameters.size = kSimpleDataLength;
522  [request.responseParametersArray addObject:parameters];
523  request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength];
524
525  GRPCRequestOptions *callRequest =
526      [[GRPCRequestOptions alloc] initWithHost:(NSString *)kHostAddress
527                                          path:kUnaryCallMethod.HTTPPath
528                                        safety:GRPCCallSafetyDefault];
529  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
530  options.transportType = GRPCTransportTypeInsecure;
531  options.flowControlEnabled = YES;
532  GRPCCall2 *call =
533      [[GRPCCall2 alloc] initWithRequestOptions:callRequest
534                                responseHandler:[[ClientTestsBlockCallbacks alloc]
535                                                    initWithInitialMetadataCallback:nil
536                                                                    messageCallback:nil
537                                                                      closeCallback:nil
538                                                                  writeDataCallback:^{
539                                                                    [expectWriteData fulfill];
540                                                                  }]
541                                    callOptions:options];
542
543  [call start];
544  [call receiveNextMessages:1];
545  [call writeData:[request data]];
546
547  // Wait for 3 seconds and make sure we do not receive the response
548  [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
549
550  [call finish];
551}
552
553- (void)testFlowControlRead {
554  __weak __block XCTestExpectation *expectBlockedMessage =
555      [self expectationWithDescription:@"Message not delivered without recvNextMessage"];
556  __weak __block XCTestExpectation *expectPassedMessage = nil;
557  __weak __block XCTestExpectation *expectBlockedClose =
558      [self expectationWithDescription:@"Call not closed with pending message"];
559  __weak __block XCTestExpectation *expectPassedClose = nil;
560  expectBlockedMessage.inverted = YES;
561  expectBlockedClose.inverted = YES;
562
563  RMTSimpleRequest *request = [RMTSimpleRequest message];
564  request.responseSize = kSimpleDataLength;
565  request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength];
566
567  GRPCRequestOptions *callRequest =
568      [[GRPCRequestOptions alloc] initWithHost:(NSString *)kHostAddress
569                                          path:kUnaryCallMethod.HTTPPath
570                                        safety:GRPCCallSafetyDefault];
571  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
572  options.transportType = GRPCTransportTypeInsecure;
573  options.flowControlEnabled = YES;
574  __block int unblocked = NO;
575  GRPCCall2 *call = [[GRPCCall2 alloc]
576      initWithRequestOptions:callRequest
577             responseHandler:[[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil
578                                 messageCallback:^(NSData *message) {
579                                   if (!unblocked) {
580                                     [expectBlockedMessage fulfill];
581                                   } else {
582                                     [expectPassedMessage fulfill];
583                                   }
584                                 }
585                                 closeCallback:^(NSDictionary *trailers, NSError *error) {
586                                   if (!unblocked) {
587                                     [expectBlockedClose fulfill];
588                                   } else {
589                                     [expectPassedClose fulfill];
590                                   }
591                                 }]
592                 callOptions:options];
593
594  [call start];
595  [call writeData:[request data]];
596  [call finish];
597
598  // Wait to make sure we do not receive the response
599  [self waitForExpectationsWithTimeout:kInvertedTimeout handler:nil];
600
601  expectPassedMessage =
602      [self expectationWithDescription:@"Message delivered with receiveNextMessage"];
603  expectPassedClose = [self expectationWithDescription:@"Close delivered after receiveNextMessage"];
604
605  unblocked = YES;
606  [call receiveNextMessages:1];
607
608  [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
609}
610
611- (void)testFlowControlMultipleMessages {
612  __weak XCTestExpectation *expectPassedMessage =
613      [self expectationWithDescription:@"two messages delivered with receiveNextMessage"];
614  expectPassedMessage.expectedFulfillmentCount = 2;
615  __weak XCTestExpectation *expectBlockedMessage =
616      [self expectationWithDescription:@"Message 3 not delivered"];
617  expectBlockedMessage.inverted = YES;
618  __weak XCTestExpectation *expectWriteTwice =
619      [self expectationWithDescription:@"Write 2 messages done"];
620  expectWriteTwice.expectedFulfillmentCount = 2;
621
622  RMTStreamingOutputCallRequest *request = [RMTStreamingOutputCallRequest message];
623  RMTResponseParameters *parameters = [RMTResponseParameters message];
624  parameters.size = kSimpleDataLength;
625  [request.responseParametersArray addObject:parameters];
626  request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength];
627
628  GRPCRequestOptions *callRequest =
629      [[GRPCRequestOptions alloc] initWithHost:(NSString *)kHostAddress
630                                          path:kFullDuplexCallMethod.HTTPPath
631                                        safety:GRPCCallSafetyDefault];
632  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
633  options.transportType = GRPCTransportTypeInsecure;
634  options.flowControlEnabled = YES;
635  __block NSUInteger messageId = 0;
636  __block GRPCCall2 *call = [[GRPCCall2 alloc]
637      initWithRequestOptions:callRequest
638             responseHandler:[[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil
639                                 messageCallback:^(NSData *message) {
640                                   if (messageId <= 1) {
641                                     [expectPassedMessage fulfill];
642                                   } else {
643                                     [expectBlockedMessage fulfill];
644                                   }
645                                   messageId++;
646                                 }
647                                 closeCallback:nil
648                                 writeDataCallback:^{
649                                   [expectWriteTwice fulfill];
650                                 }]
651                 callOptions:options];
652
653  [call receiveNextMessages:2];
654  [call start];
655  [call writeData:[request data]];
656  [call writeData:[request data]];
657
658  [self waitForExpectationsWithTimeout:kInvertedTimeout handler:nil];
659}
660
661- (void)testFlowControlReadReadyBeforeStart {
662  __weak XCTestExpectation *expectPassedMessage =
663      [self expectationWithDescription:@"Message delivered with receiveNextMessage"];
664  __weak XCTestExpectation *expectPassedClose =
665      [self expectationWithDescription:@"Close delivered with receiveNextMessage"];
666
667  RMTSimpleRequest *request = [RMTSimpleRequest message];
668  request.responseSize = kSimpleDataLength;
669  request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength];
670
671  GRPCRequestOptions *callRequest =
672      [[GRPCRequestOptions alloc] initWithHost:(NSString *)kHostAddress
673                                          path:kUnaryCallMethod.HTTPPath
674                                        safety:GRPCCallSafetyDefault];
675  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
676  options.transportType = GRPCTransportTypeInsecure;
677  options.flowControlEnabled = YES;
678  __block BOOL closed = NO;
679  GRPCCall2 *call = [[GRPCCall2 alloc]
680      initWithRequestOptions:callRequest
681             responseHandler:[[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil
682                                 messageCallback:^(NSData *message) {
683                                   [expectPassedMessage fulfill];
684                                   XCTAssertFalse(closed);
685                                 }
686                                 closeCallback:^(NSDictionary *ttrailers, NSError *error) {
687                                   closed = YES;
688                                   [expectPassedClose fulfill];
689                                 }]
690                 callOptions:options];
691
692  [call receiveNextMessages:1];
693  [call start];
694  [call writeData:[request data]];
695  [call finish];
696
697  [self waitForExpectationsWithTimeout:kInvertedTimeout handler:nil];
698}
699
700- (void)testFlowControlReadReadyAfterStart {
701  __weak XCTestExpectation *expectPassedMessage =
702      [self expectationWithDescription:@"Message delivered with receiveNextMessage"];
703  __weak XCTestExpectation *expectPassedClose =
704      [self expectationWithDescription:@"Close delivered with receiveNextMessage"];
705
706  RMTStreamingOutputCallRequest *request = [RMTStreamingOutputCallRequest message];
707  RMTResponseParameters *parameters = [RMTResponseParameters message];
708  parameters.size = kSimpleDataLength;
709  [request.responseParametersArray addObject:parameters];
710  request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength];
711
712  GRPCRequestOptions *callRequest =
713      [[GRPCRequestOptions alloc] initWithHost:(NSString *)kHostAddress
714                                          path:kUnaryCallMethod.HTTPPath
715                                        safety:GRPCCallSafetyDefault];
716  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
717  options.transportType = GRPCTransportTypeInsecure;
718  options.flowControlEnabled = YES;
719  __block BOOL closed = NO;
720  GRPCCall2 *call = [[GRPCCall2 alloc]
721      initWithRequestOptions:callRequest
722             responseHandler:[[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil
723                                 messageCallback:^(NSData *message) {
724                                   [expectPassedMessage fulfill];
725                                   XCTAssertFalse(closed);
726                                 }
727                                 closeCallback:^(NSDictionary *trailers, NSError *error) {
728                                   closed = YES;
729                                   [expectPassedClose fulfill];
730                                 }]
731                 callOptions:options];
732
733  [call start];
734  [call receiveNextMessages:1];
735  [call writeData:[request data]];
736  [call finish];
737
738  [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
739}
740
741- (void)testFlowControlReadNonBlockingFailure {
742  __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
743
744  GRPCRequestOptions *requestOptions =
745      [[GRPCRequestOptions alloc] initWithHost:kHostAddress
746                                          path:kUnaryCallMethod.HTTPPath
747                                        safety:GRPCCallSafetyDefault];
748  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
749  options.flowControlEnabled = YES;
750  options.transportType = GRPCTransportTypeInsecure;
751
752  RMTSimpleRequest *request = [RMTSimpleRequest message];
753  request.payload.body = [NSMutableData dataWithLength:options.responseSizeLimit];
754
755  RMTEchoStatus *status = [RMTEchoStatus message];
756  status.code = 2;
757  status.message = @"test";
758  request.responseStatus = status;
759
760  GRPCCall2 *call = [[GRPCCall2 alloc]
761      initWithRequestOptions:requestOptions
762             responseHandler:[[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil
763                                 messageCallback:^(NSData *data) {
764                                   XCTFail(@"Received unexpected message");
765                                 }
766                                 closeCallback:^(NSDictionary *trailingMetadata, NSError *error) {
767                                   XCTAssertNotNil(error, @"Expecting non-nil error");
768                                   XCTAssertEqual(error.code, 2);
769                                   [completion fulfill];
770                                 }]
771                 callOptions:options];
772  [call writeData:[request data]];
773  [call start];
774  [call finish];
775
776  [self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
777}
778
779@end
780