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 "InteropTests.h" 20 21#include <grpc/status.h> 22 23#import <Cronet/Cronet.h> 24#import <GRPCClient/GRPCCall+ChannelArg.h> 25#import <GRPCClient/GRPCCall+Cronet.h> 26#import <GRPCClient/GRPCCall+Tests.h> 27#import <GRPCClient/internal_testing/GRPCCall+InternalTests.h> 28#import <ProtoRPC/ProtoRPC.h> 29#import <RemoteTest/Messages.pbobjc.h> 30#import <RemoteTest/Test.pbobjc.h> 31#import <RemoteTest/Test.pbrpc.h> 32#import <RxLibrary/GRXBufferedPipe.h> 33#import <RxLibrary/GRXWriter+Immediate.h> 34#import <grpc/grpc.h> 35#import <grpc/support/log.h> 36 37#define TEST_TIMEOUT 32 38 39extern const char *kCFStreamVarName; 40 41// Convenience constructors for the generated proto messages: 42 43@interface RMTStreamingOutputCallRequest (Constructors) 44+ (instancetype)messageWithPayloadSize:(NSNumber *)payloadSize 45 requestedResponseSize:(NSNumber *)responseSize; 46@end 47 48@implementation RMTStreamingOutputCallRequest (Constructors) 49+ (instancetype)messageWithPayloadSize:(NSNumber *)payloadSize 50 requestedResponseSize:(NSNumber *)responseSize { 51 RMTStreamingOutputCallRequest *request = [self message]; 52 RMTResponseParameters *parameters = [RMTResponseParameters message]; 53 parameters.size = responseSize.intValue; 54 [request.responseParametersArray addObject:parameters]; 55 request.payload.body = [NSMutableData dataWithLength:payloadSize.unsignedIntegerValue]; 56 return request; 57} 58@end 59 60@interface RMTStreamingOutputCallResponse (Constructors) 61+ (instancetype)messageWithPayloadSize:(NSNumber *)payloadSize; 62@end 63 64@implementation RMTStreamingOutputCallResponse (Constructors) 65+ (instancetype)messageWithPayloadSize:(NSNumber *)payloadSize { 66 RMTStreamingOutputCallResponse *response = [self message]; 67 response.payload.type = RMTPayloadType_Compressable; 68 response.payload.body = [NSMutableData dataWithLength:payloadSize.unsignedIntegerValue]; 69 return response; 70} 71@end 72 73BOOL isRemoteInteropTest(NSString *host) { 74 return [host isEqualToString:@"grpc-test.sandbox.googleapis.com"]; 75} 76 77#pragma mark Tests 78 79@implementation InteropTests { 80 RMTTestService *_service; 81} 82 83+ (NSString *)host { 84 return nil; 85} 86 87// This number indicates how many bytes of overhead does Protocol Buffers encoding add onto the 88// message. The number varies as different message.proto is used on different servers. The actual 89// number for each interop server is overridden in corresponding derived test classes. 90- (int32_t)encodingOverhead { 91 return 0; 92} 93 94+ (void)setUp { 95 NSLog(@"InteropTest Started, class: %@", [[self class] description]); 96#ifdef GRPC_COMPILE_WITH_CRONET 97 // Cronet setup 98 [Cronet setHttp2Enabled:YES]; 99 [Cronet start]; 100 [GRPCCall useCronetWithEngine:[Cronet getGlobalEngine]]; 101#endif 102#ifdef GRPC_CFSTREAM 103 setenv(kCFStreamVarName, "1", 1); 104#endif 105} 106 107- (void)setUp { 108 self.continueAfterFailure = NO; 109 110 [GRPCCall resetHostSettings]; 111 112 _service = self.class.host ? [RMTTestService serviceWithHost:self.class.host] : nil; 113} 114 115- (void)testEmptyUnaryRPC { 116 XCTAssertNotNil(self.class.host); 117 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"EmptyUnary"]; 118 119 GPBEmpty *request = [GPBEmpty message]; 120 121 [_service emptyCallWithRequest:request 122 handler:^(GPBEmpty *response, NSError *error) { 123 XCTAssertNil(error, @"Finished with unexpected error: %@", error); 124 125 id expectedResponse = [GPBEmpty message]; 126 XCTAssertEqualObjects(response, expectedResponse); 127 128 [expectation fulfill]; 129 }]; 130 131 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 132} 133 134- (void)testLargeUnaryRPC { 135 XCTAssertNotNil(self.class.host); 136 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"LargeUnary"]; 137 138 RMTSimpleRequest *request = [RMTSimpleRequest message]; 139 request.responseType = RMTPayloadType_Compressable; 140 request.responseSize = 314159; 141 request.payload.body = [NSMutableData dataWithLength:271828]; 142 143 [_service unaryCallWithRequest:request 144 handler:^(RMTSimpleResponse *response, NSError *error) { 145 XCTAssertNil(error, @"Finished with unexpected error: %@", error); 146 147 RMTSimpleResponse *expectedResponse = [RMTSimpleResponse message]; 148 expectedResponse.payload.type = RMTPayloadType_Compressable; 149 expectedResponse.payload.body = [NSMutableData dataWithLength:314159]; 150 XCTAssertEqualObjects(response, expectedResponse); 151 152 [expectation fulfill]; 153 }]; 154 155 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 156} 157 158- (void)testPacketCoalescing { 159 XCTAssertNotNil(self.class.host); 160 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"LargeUnary"]; 161 162 RMTSimpleRequest *request = [RMTSimpleRequest message]; 163 request.responseType = RMTPayloadType_Compressable; 164 request.responseSize = 10; 165 request.payload.body = [NSMutableData dataWithLength:10]; 166 167 [GRPCCall enableOpBatchLog:YES]; 168 [_service unaryCallWithRequest:request 169 handler:^(RMTSimpleResponse *response, NSError *error) { 170 XCTAssertNil(error, @"Finished with unexpected error: %@", error); 171 172 RMTSimpleResponse *expectedResponse = [RMTSimpleResponse message]; 173 expectedResponse.payload.type = RMTPayloadType_Compressable; 174 expectedResponse.payload.body = [NSMutableData dataWithLength:10]; 175 XCTAssertEqualObjects(response, expectedResponse); 176 177 // The test is a success if there is a batch of exactly 3 ops 178 // (SEND_INITIAL_METADATA, SEND_MESSAGE, SEND_CLOSE_FROM_CLIENT). Without 179 // packet coalescing each batch of ops contains only one op. 180 NSArray *opBatches = [GRPCCall obtainAndCleanOpBatchLog]; 181 const NSInteger kExpectedOpBatchSize = 3; 182 for (NSObject *o in opBatches) { 183 if ([o isKindOfClass:[NSArray class]]) { 184 NSArray *batch = (NSArray *)o; 185 if ([batch count] == kExpectedOpBatchSize) { 186 [expectation fulfill]; 187 break; 188 } 189 } 190 } 191 }]; 192 193 [self waitForExpectationsWithTimeout:16 handler:nil]; 194 [GRPCCall enableOpBatchLog:NO]; 195} 196 197- (void)test4MBResponsesAreAccepted { 198 XCTAssertNotNil(self.class.host); 199 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"MaxResponseSize"]; 200 201 RMTSimpleRequest *request = [RMTSimpleRequest message]; 202 const int32_t kPayloadSize = 4 * 1024 * 1024 - self.encodingOverhead; // 4MB - encoding overhead 203 request.responseSize = kPayloadSize; 204 205 [_service unaryCallWithRequest:request 206 handler:^(RMTSimpleResponse *response, NSError *error) { 207 XCTAssertNil(error, @"Finished with unexpected error: %@", error); 208 XCTAssertEqual(response.payload.body.length, kPayloadSize); 209 [expectation fulfill]; 210 }]; 211 212 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 213} 214 215- (void)testResponsesOverMaxSizeFailWithActionableMessage { 216 XCTAssertNotNil(self.class.host); 217 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"ResponseOverMaxSize"]; 218 219 RMTSimpleRequest *request = [RMTSimpleRequest message]; 220 const int32_t kPayloadSize = 4 * 1024 * 1024 - self.encodingOverhead + 1; // 1B over max size 221 request.responseSize = kPayloadSize; 222 223 [_service unaryCallWithRequest:request 224 handler:^(RMTSimpleResponse *response, NSError *error) { 225 // TODO(jcanizales): Catch the error and rethrow it with an actionable 226 // message: 227 // - Use +[GRPCCall setResponseSizeLimit:forHost:] to set a higher limit. 228 // - If you're developing the server, consider using response streaming, 229 // or let clients filter 230 // responses by setting a google.protobuf.FieldMask in the request: 231 // https://github.com/google/protobuf/blob/master/src/google/protobuf/field_mask.proto 232 XCTAssertEqualObjects( 233 error.localizedDescription, 234 @"Received message larger than max (4194305 vs. 4194304)"); 235 [expectation fulfill]; 236 }]; 237 238 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 239} 240 241- (void)testResponsesOver4MBAreAcceptedIfOptedIn { 242 XCTAssertNotNil(self.class.host); 243 __weak XCTestExpectation *expectation = 244 [self expectationWithDescription:@"HigherResponseSizeLimit"]; 245 246 RMTSimpleRequest *request = [RMTSimpleRequest message]; 247 const size_t kPayloadSize = 5 * 1024 * 1024; // 5MB 248 request.responseSize = kPayloadSize; 249 250 [GRPCCall setResponseSizeLimit:6 * 1024 * 1024 forHost:self.class.host]; 251 252 [_service unaryCallWithRequest:request 253 handler:^(RMTSimpleResponse *response, NSError *error) { 254 XCTAssertNil(error, @"Finished with unexpected error: %@", error); 255 XCTAssertEqual(response.payload.body.length, kPayloadSize); 256 [expectation fulfill]; 257 }]; 258 259 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 260} 261 262- (void)testClientStreamingRPC { 263 XCTAssertNotNil(self.class.host); 264 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"ClientStreaming"]; 265 266 RMTStreamingInputCallRequest *request1 = [RMTStreamingInputCallRequest message]; 267 request1.payload.body = [NSMutableData dataWithLength:27182]; 268 269 RMTStreamingInputCallRequest *request2 = [RMTStreamingInputCallRequest message]; 270 request2.payload.body = [NSMutableData dataWithLength:8]; 271 272 RMTStreamingInputCallRequest *request3 = [RMTStreamingInputCallRequest message]; 273 request3.payload.body = [NSMutableData dataWithLength:1828]; 274 275 RMTStreamingInputCallRequest *request4 = [RMTStreamingInputCallRequest message]; 276 request4.payload.body = [NSMutableData dataWithLength:45904]; 277 278 GRXWriter *writer = [GRXWriter writerWithContainer:@[ request1, request2, request3, request4 ]]; 279 280 [_service streamingInputCallWithRequestsWriter:writer 281 handler:^(RMTStreamingInputCallResponse *response, 282 NSError *error) { 283 XCTAssertNil( 284 error, @"Finished with unexpected error: %@", error); 285 286 RMTStreamingInputCallResponse *expectedResponse = 287 [RMTStreamingInputCallResponse message]; 288 expectedResponse.aggregatedPayloadSize = 74922; 289 XCTAssertEqualObjects(response, expectedResponse); 290 291 [expectation fulfill]; 292 }]; 293 294 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 295} 296 297- (void)testServerStreamingRPC { 298 XCTAssertNotNil(self.class.host); 299 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"ServerStreaming"]; 300 301 NSArray *expectedSizes = @[ @31415, @9, @2653, @58979 ]; 302 303 RMTStreamingOutputCallRequest *request = [RMTStreamingOutputCallRequest message]; 304 for (NSNumber *size in expectedSizes) { 305 RMTResponseParameters *parameters = [RMTResponseParameters message]; 306 parameters.size = [size intValue]; 307 [request.responseParametersArray addObject:parameters]; 308 } 309 310 __block int index = 0; 311 [_service 312 streamingOutputCallWithRequest:request 313 eventHandler:^(BOOL done, RMTStreamingOutputCallResponse *response, 314 NSError *error) { 315 XCTAssertNil(error, @"Finished with unexpected error: %@", error); 316 XCTAssertTrue(done || response, 317 @"Event handler called without an event."); 318 319 if (response) { 320 XCTAssertLessThan(index, 4, @"More than 4 responses received."); 321 id expected = [RMTStreamingOutputCallResponse 322 messageWithPayloadSize:expectedSizes[index]]; 323 XCTAssertEqualObjects(response, expected); 324 index += 1; 325 } 326 327 if (done) { 328 XCTAssertEqual(index, 4, @"Received %i responses instead of 4.", index); 329 [expectation fulfill]; 330 } 331 }]; 332 333 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 334} 335 336- (void)testPingPongRPC { 337 XCTAssertNotNil(self.class.host); 338 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"PingPong"]; 339 340 NSArray *requests = @[ @27182, @8, @1828, @45904 ]; 341 NSArray *responses = @[ @31415, @9, @2653, @58979 ]; 342 343 GRXBufferedPipe *requestsBuffer = [[GRXBufferedPipe alloc] init]; 344 345 __block int index = 0; 346 347 id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:requests[index] 348 requestedResponseSize:responses[index]]; 349 [requestsBuffer writeValue:request]; 350 351 [_service fullDuplexCallWithRequestsWriter:requestsBuffer 352 eventHandler:^(BOOL done, RMTStreamingOutputCallResponse *response, 353 NSError *error) { 354 XCTAssertNil(error, @"Finished with unexpected error: %@", error); 355 XCTAssertTrue(done || response, 356 @"Event handler called without an event."); 357 358 if (response) { 359 XCTAssertLessThan(index, 4, @"More than 4 responses received."); 360 id expected = [RMTStreamingOutputCallResponse 361 messageWithPayloadSize:responses[index]]; 362 XCTAssertEqualObjects(response, expected); 363 index += 1; 364 if (index < 4) { 365 id request = [RMTStreamingOutputCallRequest 366 messageWithPayloadSize:requests[index] 367 requestedResponseSize:responses[index]]; 368 [requestsBuffer writeValue:request]; 369 } else { 370 [requestsBuffer writesFinishedWithError:nil]; 371 } 372 } 373 374 if (done) { 375 XCTAssertEqual(index, 4, @"Received %i responses instead of 4.", 376 index); 377 [expectation fulfill]; 378 } 379 }]; 380 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 381} 382 383- (void)testEmptyStreamRPC { 384 XCTAssertNotNil(self.class.host); 385 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"EmptyStream"]; 386 [_service fullDuplexCallWithRequestsWriter:[GRXWriter emptyWriter] 387 eventHandler:^(BOOL done, RMTStreamingOutputCallResponse *response, 388 NSError *error) { 389 XCTAssertNil(error, @"Finished with unexpected error: %@", error); 390 XCTAssert(done, @"Unexpected response: %@", response); 391 [expectation fulfill]; 392 }]; 393 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 394} 395 396- (void)testCancelAfterBeginRPC { 397 XCTAssertNotNil(self.class.host); 398 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"CancelAfterBegin"]; 399 400 // A buffered pipe to which we never write any value acts as a writer that just hangs. 401 GRXBufferedPipe *requestsBuffer = [[GRXBufferedPipe alloc] init]; 402 403 GRPCProtoCall *call = [_service 404 RPCToStreamingInputCallWithRequestsWriter:requestsBuffer 405 handler:^(RMTStreamingInputCallResponse *response, 406 NSError *error) { 407 XCTAssertEqual(error.code, GRPC_STATUS_CANCELLED); 408 [expectation fulfill]; 409 }]; 410 XCTAssertEqual(call.state, GRXWriterStateNotStarted); 411 412 [call start]; 413 XCTAssertEqual(call.state, GRXWriterStateStarted); 414 415 [call cancel]; 416 XCTAssertEqual(call.state, GRXWriterStateFinished); 417 418 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 419} 420 421- (void)testCancelAfterFirstResponseRPC { 422 XCTAssertNotNil(self.class.host); 423 __weak XCTestExpectation *expectation = 424 [self expectationWithDescription:@"CancelAfterFirstResponse"]; 425 426 // A buffered pipe to which we write a single value but never close 427 GRXBufferedPipe *requestsBuffer = [[GRXBufferedPipe alloc] init]; 428 429 __block BOOL receivedResponse = NO; 430 431 id request = 432 [RMTStreamingOutputCallRequest messageWithPayloadSize:@21782 requestedResponseSize:@31415]; 433 434 [requestsBuffer writeValue:request]; 435 436 __block GRPCProtoCall *call = [_service 437 RPCToFullDuplexCallWithRequestsWriter:requestsBuffer 438 eventHandler:^(BOOL done, RMTStreamingOutputCallResponse *response, 439 NSError *error) { 440 if (receivedResponse) { 441 XCTAssert(done, @"Unexpected extra response %@", response); 442 XCTAssertEqual(error.code, GRPC_STATUS_CANCELLED); 443 [expectation fulfill]; 444 } else { 445 XCTAssertNil(error, @"Finished with unexpected error: %@", 446 error); 447 XCTAssertFalse(done, @"Finished without response"); 448 XCTAssertNotNil(response); 449 receivedResponse = YES; 450 [call cancel]; 451 } 452 }]; 453 [call start]; 454 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 455} 456 457- (void)testRPCAfterClosingOpenConnections { 458 XCTAssertNotNil(self.class.host); 459 __weak XCTestExpectation *expectation = 460 [self expectationWithDescription:@"RPC after closing connection"]; 461 462 GPBEmpty *request = [GPBEmpty message]; 463 464 [_service 465 emptyCallWithRequest:request 466 handler:^(GPBEmpty *response, NSError *error) { 467 XCTAssertNil(error, @"First RPC finished with unexpected error: %@", error); 468 469#pragma clang diagnostic push 470#pragma clang diagnostic ignored "-Wdeprecated-declarations" 471 [GRPCCall closeOpenConnections]; 472#pragma clang diagnostic pop 473 474 [_service 475 emptyCallWithRequest:request 476 handler:^(GPBEmpty *response, NSError *error) { 477 XCTAssertNil( 478 error, @"Second RPC finished with unexpected error: %@", 479 error); 480 [expectation fulfill]; 481 }]; 482 }]; 483 484 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 485} 486 487- (void)testCompressedUnaryRPC { 488 // This test needs to be disabled for remote test because interop server grpc-test 489 // does not support compression. 490 if (isRemoteInteropTest(self.class.host)) { 491 return; 492 } 493 XCTAssertNotNil(self.class.host); 494 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"LargeUnary"]; 495 496 RMTSimpleRequest *request = [RMTSimpleRequest message]; 497 request.responseType = RMTPayloadType_Compressable; 498 request.responseSize = 314159; 499 request.payload.body = [NSMutableData dataWithLength:271828]; 500 request.expectCompressed.value = YES; 501 [GRPCCall setDefaultCompressMethod:GRPCCompressGzip forhost:self.class.host]; 502 503 [_service unaryCallWithRequest:request 504 handler:^(RMTSimpleResponse *response, NSError *error) { 505 XCTAssertNil(error, @"Finished with unexpected error: %@", error); 506 507 RMTSimpleResponse *expectedResponse = [RMTSimpleResponse message]; 508 expectedResponse.payload.type = RMTPayloadType_Compressable; 509 expectedResponse.payload.body = [NSMutableData dataWithLength:314159]; 510 XCTAssertEqualObjects(response, expectedResponse); 511 512 [expectation fulfill]; 513 }]; 514 515 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 516} 517 518#ifndef GRPC_COMPILE_WITH_CRONET 519- (void)testKeepalive { 520 XCTAssertNotNil(self.class.host); 521 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Keepalive"]; 522 523 [GRPCCall setKeepaliveWithInterval:1500 timeout:0 forHost:self.class.host]; 524 525 NSArray *requests = @[ @27182, @8 ]; 526 NSArray *responses = @[ @31415, @9 ]; 527 528 GRXBufferedPipe *requestsBuffer = [[GRXBufferedPipe alloc] init]; 529 530 __block int index = 0; 531 532 id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:requests[index] 533 requestedResponseSize:responses[index]]; 534 [requestsBuffer writeValue:request]; 535 536 [_service 537 fullDuplexCallWithRequestsWriter:requestsBuffer 538 eventHandler:^(BOOL done, RMTStreamingOutputCallResponse *response, 539 NSError *error) { 540 if (index == 0) { 541 XCTAssertNil(error, @"Finished with unexpected error: %@", error); 542 XCTAssertTrue(response, @"Event handler called without an event."); 543 XCTAssertFalse(done); 544 index++; 545 } else { 546 // Keepalive should kick after 1s elapsed and fails the call. 547 XCTAssertNotNil(error); 548 XCTAssertEqual(error.code, GRPC_STATUS_INTERNAL); 549 XCTAssertEqualObjects( 550 error.localizedDescription, @"keepalive watchdog timeout", 551 @"Unexpected failure that is not keepalive watchdog timeout."); 552 XCTAssertTrue(done); 553 [expectation fulfill]; 554 } 555 }]; 556 557 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 558} 559#endif 560 561@end 562