blob: 2a169800a0971ef67fa1fd40d2a966768390ce29 [file] [log] [blame]
Jorge Canizalesb3be2292015-05-31 19:11:20 -07001/*
2 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02003 * Copyright 2015 gRPC authors.
Jorge Canizalesb3be2292015-05-31 19:11:20 -07004 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02005 * 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
Jorge Canizalesb3be2292015-05-31 19:11:20 -07008 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02009 * http://www.apache.org/licenses/LICENSE-2.0
Jorge Canizalesb3be2292015-05-31 19:11:20 -070010 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +020011 * 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.
Jorge Canizalesb3be2292015-05-31 19:11:20 -070016 *
17 */
18
19#import <UIKit/UIKit.h>
20#import <XCTest/XCTest.h>
Muxi Yan3305e5c2017-10-11 18:31:43 -070021#import <grpc/grpc.h>
Jorge Canizalesb3be2292015-05-31 19:11:20 -070022
Makarand Dharmapurikar1c2890d2016-02-24 14:35:46 -080023#import <GRPCClient/GRPCCall+ChannelArg.h>
Jorge Canizales631dc002015-08-06 23:08:41 -070024#import <GRPCClient/GRPCCall+OAuth2.h>
Muxi Yan8e6aec52017-02-10 09:40:18 -080025#import <GRPCClient/GRPCCall+Tests.h>
Muxi Yanc92d90a2018-04-11 18:10:02 -070026#import <GRPCClient/GRPCCall.h>
Muxi Yan8e6aec52017-02-10 09:40:18 -080027#import <GRPCClient/internal_testing/GRPCCall+InternalTests.h>
Jorge Canizalesbe808e32015-07-04 14:37:58 -070028#import <ProtoRPC/ProtoMethod.h>
Jorge Canizalesb3be2292015-05-31 19:11:20 -070029#import <RemoteTest/Messages.pbobjc.h>
Muxi Yanc92d90a2018-04-11 18:10:02 -070030#import <RxLibrary/GRXBufferedPipe.h>
Jorge Canizalesc42a38e2015-06-21 14:44:25 -070031#import <RxLibrary/GRXWriteable.h>
32#import <RxLibrary/GRXWriter+Immediate.h>
Makarand Dharmapurikara0414ee2016-02-23 16:58:46 -080033
Muxi Yan109bb092018-04-04 15:16:30 -070034#include <netinet/in.h>
35
Muxi Yan3305e5c2017-10-11 18:31:43 -070036#import "version.h"
37
Muxi Yanb1341a32016-12-05 16:38:02 -080038#define TEST_TIMEOUT 16
39
Muxi Yanc92d90a2018-04-11 18:10:02 -070040static NSString *const kHostAddress = @"localhost:5050";
41static NSString *const kPackage = @"grpc.testing";
42static NSString *const kService = @"TestService";
43static NSString *const kRemoteSSLHost = @"grpc-test.sandbox.googleapis.com";
Makarand Dharmapurikara0414ee2016-02-23 16:58:46 -080044
Yuchen Zengdbe2b9e2016-06-15 20:23:04 -070045static GRPCProtoMethod *kInexistentMethod;
46static GRPCProtoMethod *kEmptyCallMethod;
47static GRPCProtoMethod *kUnaryCallMethod;
Muxi Yan2795eae2017-09-16 09:59:20 -070048static GRPCProtoMethod *kFullDuplexCallMethod;
Jorge Canizalesd666fd02015-06-12 18:59:11 -070049
Jorge Canizales2beb88c2015-10-28 15:13:40 -070050/** Observer class for testing that responseMetadata is KVO-compliant */
murgatroid9981b4fcb2015-08-24 10:56:44 -070051@interface PassthroughObserver : NSObject
Muxi Yanc92d90a2018-04-11 18:10:02 -070052- (instancetype)initWithCallback:(void (^)(NSString *, id, NSDictionary *))callback
Jorge Canizales2beb88c2015-10-28 15:13:40 -070053 NS_DESIGNATED_INITIALIZER;
murgatroid9981b4fcb2015-08-24 10:56:44 -070054
Muxi Yanc92d90a2018-04-11 18:10:02 -070055- (void)observeValueForKeyPath:(NSString *)keyPath
56 ofObject:(id)object
57 change:(NSDictionary *)change
murgatroid9981b4fcb2015-08-24 10:56:44 -070058 context:(void *)context;
59@end
60
61@implementation PassthroughObserver {
Muxi Yanc92d90a2018-04-11 18:10:02 -070062 void (^_callback)(NSString *, id, NSDictionary *);
murgatroid9981b4fcb2015-08-24 10:56:44 -070063}
64
Jorge Canizales2beb88c2015-10-28 15:13:40 -070065- (instancetype)init {
66 return [self initWithCallback:nil];
67}
68
murgatroid9981b4fcb2015-08-24 10:56:44 -070069- (instancetype)initWithCallback:(void (^)(NSString *, id, NSDictionary *))callback {
Jorge Canizales2beb88c2015-10-28 15:13:40 -070070 if (!callback) {
71 return nil;
72 }
73 if ((self = [super init])) {
murgatroid9981b4fcb2015-08-24 10:56:44 -070074 _callback = callback;
75 }
76 return self;
murgatroid9981b4fcb2015-08-24 10:56:44 -070077}
78
Jorge Canizales2beb88c2015-10-28 15:13:40 -070079- (void)observeValueForKeyPath:(NSString *)keyPath
80 ofObject:(id)object
81 change:(NSDictionary *)change
82 context:(void *)context {
murgatroid9981b4fcb2015-08-24 10:56:44 -070083 _callback(keyPath, object, change);
murgatroid992e49a352015-08-24 14:40:45 -070084 [object removeObserver:self forKeyPath:keyPath];
murgatroid9981b4fcb2015-08-24 10:56:44 -070085}
86
87@end
88
Muxi Yanc92d90a2018-04-11 18:10:02 -070089#pragma mark Tests
Jorge Canizales2beb88c2015-10-28 15:13:40 -070090
91/**
92 * A few tests similar to InteropTests, but which use the generic gRPC client (GRPCCall) rather than
93 * a generated proto library on top of it. Its RPCs are sent to a local cleartext server.
94 *
95 * TODO(jcanizales): Run them also against a local SSL server and against a remote server.
96 */
Jorge Canizalesb3be2292015-05-31 19:11:20 -070097@interface GRPCClientTests : XCTestCase
98@end
99
100@implementation GRPCClientTests
101
Muxi Yan4b947d32017-11-30 09:36:29 -0800102+ (void)setUp {
103 NSLog(@"GRPCClientTests Started");
104}
105
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700106- (void)setUp {
Makarand Dharmapurikarc2b34902016-02-26 16:38:17 -0800107 // Add a custom user agent prefix that will be used in test
108 [GRPCCall setUserAgentPrefix:@"Foo" forHost:kHostAddress];
Jorge Canizalesb2bd0672015-08-01 23:19:11 -0700109 // Register test server as non-SSL.
110 [GRPCCall useInsecureConnectionsForHost:kHostAddress];
111
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700112 // This method isn't implemented by the remote server.
Muxi Yanc92d90a2018-04-11 18:10:02 -0700113 kInexistentMethod =
114 [[GRPCProtoMethod alloc] initWithPackage:kPackage service:kService method:@"Inexistent"];
115 kEmptyCallMethod =
116 [[GRPCProtoMethod alloc] initWithPackage:kPackage service:kService method:@"EmptyCall"];
117 kUnaryCallMethod =
118 [[GRPCProtoMethod alloc] initWithPackage:kPackage service:kService method:@"UnaryCall"];
119 kFullDuplexCallMethod =
120 [[GRPCProtoMethod alloc] initWithPackage:kPackage service:kService method:@"FullDuplexCall"];
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700121}
122
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700123- (void)testConnectionToRemoteServer {
124 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Server reachable."];
125
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700126 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
Jorge Canizales5260f532015-07-04 14:47:41 -0700127 path:kInexistentMethod.HTTPPath
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700128 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700129
Muxi Yanc92d90a2018-04-11 18:10:02 -0700130 id<GRXWriteable> responsesWriteable =
131 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
132 XCTFail(@"Received unexpected response: %@", value);
133 }
134 completionHandler:^(NSError *errorOrNil) {
135 XCTAssertNotNil(errorOrNil, @"Finished without error!");
136 XCTAssertEqual(errorOrNil.code, 12, @"Finished with unexpected error: %@", errorOrNil);
137 [expectation fulfill];
138 }];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700139
140 [call startWithWriteable:responsesWriteable];
141
Muxi Yanb1341a32016-12-05 16:38:02 -0800142 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700143}
144
145- (void)testEmptyRPC {
Muxi Yanc92d90a2018-04-11 18:10:02 -0700146 __weak XCTestExpectation *response =
147 [self expectationWithDescription:@"Empty response received."];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700148 __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
149
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700150 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
Jorge Canizales5260f532015-07-04 14:47:41 -0700151 path:kEmptyCallMethod.HTTPPath
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700152 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700153
Muxi Yanc92d90a2018-04-11 18:10:02 -0700154 id<GRXWriteable> responsesWriteable =
155 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
156 XCTAssertNotNil(value, @"nil value received as response.");
157 XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value);
158 [response fulfill];
159 }
160 completionHandler:^(NSError *errorOrNil) {
161 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
162 [completion fulfill];
163 }];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700164
165 [call startWithWriteable:responsesWriteable];
166
Muxi Yanb1341a32016-12-05 16:38:02 -0800167 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700168}
169
170- (void)testSimpleProtoRPC {
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700171 __weak XCTestExpectation *response = [self expectationWithDescription:@"Expected response."];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700172 __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
173
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700174 RMTSimpleRequest *request = [RMTSimpleRequest message];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700175 request.responseSize = 100;
176 request.fillUsername = YES;
177 request.fillOauthScope = YES;
Jorge Canizalesa8c5d962015-07-16 18:55:31 -0700178 GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700179
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700180 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
Jorge Canizales5260f532015-07-04 14:47:41 -0700181 path:kUnaryCallMethod.HTTPPath
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700182 requestsWriter:requestsWriter];
183
Muxi Yanc92d90a2018-04-11 18:10:02 -0700184 id<GRXWriteable> responsesWriteable =
185 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
186 XCTAssertNotNil(value, @"nil value received as response.");
187 XCTAssertGreaterThan(value.length, 0, @"Empty response received.");
188 RMTSimpleResponse *responseProto = [RMTSimpleResponse parseFromData:value error:NULL];
189 // We expect empty strings, not nil:
190 XCTAssertNotNil(responseProto.username, @"Response's username is nil.");
191 XCTAssertNotNil(responseProto.oauthScope, @"Response's OAuth scope is nil.");
192 [response fulfill];
193 }
194 completionHandler:^(NSError *errorOrNil) {
195 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
196 [completion fulfill];
197 }];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700198
199 [call startWithWriteable:responsesWriteable];
200
Muxi Yanb1341a32016-12-05 16:38:02 -0800201 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700202}
203
Jorge Canizalesd7981252015-06-12 19:15:18 -0700204- (void)testMetadata {
205 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"RPC unauthorized."];
206
207 RMTSimpleRequest *request = [RMTSimpleRequest message];
208 request.fillUsername = YES;
209 request.fillOauthScope = YES;
Jorge Canizalesa8c5d962015-07-16 18:55:31 -0700210 GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]];
Jorge Canizalesd7981252015-06-12 19:15:18 -0700211
Test User9656eca2016-02-18 14:47:22 -0800212 GRPCCall *call = [[GRPCCall alloc] initWithHost:kRemoteSSLHost
Jorge Canizales5260f532015-07-04 14:47:41 -0700213 path:kUnaryCallMethod.HTTPPath
Jorge Canizalesd7981252015-06-12 19:15:18 -0700214 requestsWriter:requestsWriter];
215
Jorge Canizales721b7a32015-08-07 10:11:16 -0700216 call.oauth2AccessToken = @"bogusToken";
Jorge Canizalesd7981252015-06-12 19:15:18 -0700217
Muxi Yanc92d90a2018-04-11 18:10:02 -0700218 id<GRXWriteable> responsesWriteable =
219 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
220 XCTFail(@"Received unexpected response: %@", value);
221 }
222 completionHandler:^(NSError *errorOrNil) {
223 XCTAssertNotNil(errorOrNil, @"Finished without error!");
224 XCTAssertEqual(errorOrNil.code, 16, @"Finished with unexpected error: %@", errorOrNil);
225 XCTAssertEqualObjects(call.responseHeaders, errorOrNil.userInfo[kGRPCHeadersKey],
226 @"Headers in the NSError object and call object differ.");
227 XCTAssertEqualObjects(call.responseTrailers, errorOrNil.userInfo[kGRPCTrailersKey],
228 @"Trailers in the NSError object and call object differ.");
229 NSString *challengeHeader = call.oauth2ChallengeHeader;
230 XCTAssertGreaterThan(challengeHeader.length, 0, @"No challenge in response headers %@",
231 call.responseHeaders);
232 [expectation fulfill];
233 }];
Jorge Canizalesd7981252015-06-12 19:15:18 -0700234
235 [call startWithWriteable:responsesWriteable];
236
Muxi Yanb1341a32016-12-05 16:38:02 -0800237 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
Jorge Canizalesd7981252015-06-12 19:15:18 -0700238}
239
murgatroid9981b4fcb2015-08-24 10:56:44 -0700240- (void)testResponseMetadataKVO {
Muxi Yanc92d90a2018-04-11 18:10:02 -0700241 __weak XCTestExpectation *response =
242 [self expectationWithDescription:@"Empty response received."];
murgatroid9981b4fcb2015-08-24 10:56:44 -0700243 __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
244 __weak XCTestExpectation *metadata = [self expectationWithDescription:@"Metadata changed."];
Muxi Yanc92d90a2018-04-11 18:10:02 -0700245
murgatroid9981b4fcb2015-08-24 10:56:44 -0700246 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
247 path:kEmptyCallMethod.HTTPPath
248 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
Muxi Yanc92d90a2018-04-11 18:10:02 -0700249
250 PassthroughObserver *observer = [[PassthroughObserver alloc]
251 initWithCallback:^(NSString *keypath, id object, NSDictionary *change) {
252 if ([keypath isEqual:@"responseHeaders"]) {
253 [metadata fulfill];
254 }
255 }];
256
murgatroid9981b4fcb2015-08-24 10:56:44 -0700257 [call addObserver:observer forKeyPath:@"responseHeaders" options:0 context:NULL];
Muxi Yanc92d90a2018-04-11 18:10:02 -0700258
259 id<GRXWriteable> responsesWriteable =
260 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
261 XCTAssertNotNil(value, @"nil value received as response.");
262 XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value);
263 [response fulfill];
264 }
265 completionHandler:^(NSError *errorOrNil) {
266 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
267 [completion fulfill];
268 }];
269
murgatroid9981b4fcb2015-08-24 10:56:44 -0700270 [call startWithWriteable:responsesWriteable];
Muxi Yanc92d90a2018-04-11 18:10:02 -0700271
Muxi Yanb1341a32016-12-05 16:38:02 -0800272 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
murgatroid9981b4fcb2015-08-24 10:56:44 -0700273}
274
Makarand Dharmapurikarc2b34902016-02-26 16:38:17 -0800275- (void)testUserAgentPrefix {
Muxi Yanc92d90a2018-04-11 18:10:02 -0700276 __weak XCTestExpectation *response =
277 [self expectationWithDescription:@"Empty response received."];
Makarand Dharmapurikarc2b34902016-02-26 16:38:17 -0800278 __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
279
280 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
281 path:kEmptyCallMethod.HTTPPath
282 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
283 // Setting this special key in the header will cause the interop server to echo back the
284 // user-agent value, which we confirm.
285 call.requestHeaders[@"x-grpc-test-echo-useragent"] = @"";
286
Muxi Yanc92d90a2018-04-11 18:10:02 -0700287 id<GRXWriteable> responsesWriteable =
288 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
289 XCTAssertNotNil(value, @"nil value received as response.");
290 XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value);
Muxi Yan3305e5c2017-10-11 18:31:43 -0700291
Muxi Yanc92d90a2018-04-11 18:10:02 -0700292 NSString *userAgent = call.responseHeaders[@"x-grpc-test-echo-useragent"];
293 NSError *error = nil;
Muxi Yan3305e5c2017-10-11 18:31:43 -0700294
Muxi Yanc92d90a2018-04-11 18:10:02 -0700295 // Test the regex is correct
296 NSString *expectedUserAgent = @"Foo grpc-objc/";
297 expectedUserAgent = [expectedUserAgent stringByAppendingString:GRPC_OBJC_VERSION_STRING];
298 expectedUserAgent = [expectedUserAgent stringByAppendingString:@" grpc-c/"];
299 expectedUserAgent = [expectedUserAgent stringByAppendingString:GRPC_C_VERSION_STRING];
300 expectedUserAgent = [expectedUserAgent stringByAppendingString:@" (ios; chttp2; "];
301 expectedUserAgent = [expectedUserAgent
302 stringByAppendingString:[NSString stringWithUTF8String:grpc_g_stands_for()]];
303 expectedUserAgent = [expectedUserAgent stringByAppendingString:@")"];
304 XCTAssertEqualObjects(userAgent, expectedUserAgent);
Muxi Yan3305e5c2017-10-11 18:31:43 -0700305
Muxi Yanc92d90a2018-04-11 18:10:02 -0700306 // Change in format of user-agent field in a direction that does not match the regex will
307 // likely cause problem for certain gRPC users. For details, refer to internal doc
308 // https://goo.gl/c2diBc
309 NSRegularExpression *regex = [NSRegularExpression
310 regularExpressionWithPattern:@" grpc-[a-zA-Z0-9]+(-[a-zA-Z0-9]+)?/[^ ,]+( \\([^)]*\\))?"
311 options:0
312 error:&error];
313 NSString *customUserAgent =
314 [regex stringByReplacingMatchesInString:userAgent
315 options:0
316 range:NSMakeRange(0, [userAgent length])
317 withTemplate:@""];
318 XCTAssertEqualObjects(customUserAgent, @"Foo");
Muxi Yan3305e5c2017-10-11 18:31:43 -0700319
Muxi Yanc92d90a2018-04-11 18:10:02 -0700320 [response fulfill];
321 }
322 completionHandler:^(NSError *errorOrNil) {
323 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
324 [completion fulfill];
325 }];
Makarand Dharmapurikarc2b34902016-02-26 16:38:17 -0800326
327 [call startWithWriteable:responsesWriteable];
328
Muxi Yanb1341a32016-12-05 16:38:02 -0800329 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
Makarand Dharmapurikarc2b34902016-02-26 16:38:17 -0800330}
331
Makarand Dharmapurikar716c7c52017-05-09 13:04:41 -0700332- (void)testTrailers {
Muxi Yanc92d90a2018-04-11 18:10:02 -0700333 __weak XCTestExpectation *response =
334 [self expectationWithDescription:@"Empty response received."];
Makarand Dharmapurikar716c7c52017-05-09 13:04:41 -0700335 __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
336
337 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
338 path:kEmptyCallMethod.HTTPPath
339 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
340 // Setting this special key in the header will cause the interop server to echo back the
341 // trailer data.
Muxi Yanc92d90a2018-04-11 18:10:02 -0700342 const unsigned char raw_bytes[] = {1, 2, 3, 4};
Makarand Dharmapurikar716c7c52017-05-09 13:04:41 -0700343 NSData *trailer_data = [NSData dataWithBytes:raw_bytes length:sizeof(raw_bytes)];
344 call.requestHeaders[@"x-grpc-test-echo-trailing-bin"] = trailer_data;
345
Muxi Yanc92d90a2018-04-11 18:10:02 -0700346 id<GRXWriteable> responsesWriteable =
347 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
348 XCTAssertNotNil(value, @"nil value received as response.");
349 XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value);
350 [response fulfill];
351 }
352 completionHandler:^(NSError *errorOrNil) {
353 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
354 XCTAssertEqualObjects((NSData *)call.responseTrailers[@"x-grpc-test-echo-trailing-bin"],
355 trailer_data, @"Did not receive expected trailer");
356 [completion fulfill];
357 }];
Makarand Dharmapurikar716c7c52017-05-09 13:04:41 -0700358
359 [call startWithWriteable:responsesWriteable];
360 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
361}
362
Makarand Dharmapurikar63f2b5b2016-02-24 13:54:48 -0800363// TODO(makarandd): Move to a different file that contains only unit tests
Test User9656eca2016-02-18 14:47:22 -0800364- (void)testExceptions {
Test User9656eca2016-02-18 14:47:22 -0800365 // Try to set parameters to nil for GRPCCall. This should cause an exception
366 @try {
Muxi Yanc92d90a2018-04-11 18:10:02 -0700367 (void)[[GRPCCall alloc] initWithHost:nil path:nil requestsWriter:nil];
Test User9656eca2016-02-18 14:47:22 -0800368 XCTFail(@"Did not receive an exception when parameters are nil");
Muxi Yanc92d90a2018-04-11 18:10:02 -0700369 } @catch (NSException *theException) {
Test User9656eca2016-02-18 14:47:22 -0800370 NSLog(@"Received exception as expected: %@", theException.name);
371 }
372
Test User9656eca2016-02-18 14:47:22 -0800373 // Set state to Finished by force
374 GRXWriter *requestsWriter = [GRXWriter emptyWriter];
375 [requestsWriter finishWithError:nil];
376 @try {
Yuchen Zengd5fd7dd2016-06-21 11:13:23 -0700377 (void)[[GRPCCall alloc] initWithHost:kHostAddress
378 path:kUnaryCallMethod.HTTPPath
379 requestsWriter:requestsWriter];
Test User9656eca2016-02-18 14:47:22 -0800380 XCTFail(@"Did not receive an exception when GRXWriter has incorrect state.");
Muxi Yanc92d90a2018-04-11 18:10:02 -0700381 } @catch (NSException *theException) {
Test User9656eca2016-02-18 14:47:22 -0800382 NSLog(@"Received exception as expected: %@", theException.name);
383 }
Test User9656eca2016-02-18 14:47:22 -0800384}
385
Muxi Yan1bd5c772016-09-30 15:13:58 -0700386- (void)testIdempotentProtoRPC {
387 __weak XCTestExpectation *response = [self expectationWithDescription:@"Expected response."];
388 __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
389
390 RMTSimpleRequest *request = [RMTSimpleRequest message];
391 request.responseSize = 100;
392 request.fillUsername = YES;
393 request.fillOauthScope = YES;
394 GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]];
395
396 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
397 path:kUnaryCallMethod.HTTPPath
398 requestsWriter:requestsWriter];
Muxi Yanc92d90a2018-04-11 18:10:02 -0700399 [GRPCCall setCallSafety:GRPCCallSafetyIdempotentRequest
400 host:kHostAddress
401 path:kUnaryCallMethod.HTTPPath];
Muxi Yan1bd5c772016-09-30 15:13:58 -0700402
Muxi Yanc92d90a2018-04-11 18:10:02 -0700403 id<GRXWriteable> responsesWriteable =
404 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
405 XCTAssertNotNil(value, @"nil value received as response.");
406 XCTAssertGreaterThan(value.length, 0, @"Empty response received.");
407 RMTSimpleResponse *responseProto = [RMTSimpleResponse parseFromData:value error:NULL];
408 // We expect empty strings, not nil:
409 XCTAssertNotNil(responseProto.username, @"Response's username is nil.");
410 XCTAssertNotNil(responseProto.oauthScope, @"Response's OAuth scope is nil.");
411 [response fulfill];
412 }
413 completionHandler:^(NSError *errorOrNil) {
414 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
415 [completion fulfill];
416 }];
Muxi Yan1bd5c772016-09-30 15:13:58 -0700417
418 [call startWithWriteable:responsesWriteable];
419
Muxi Yanb1341a32016-12-05 16:38:02 -0800420 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
Muxi Yan1bd5c772016-09-30 15:13:58 -0700421}
422
Muxi Yanb965ff62017-04-19 14:27:47 -0700423- (void)testAlternateDispatchQueue {
424 const int32_t kPayloadSize = 100;
425 RMTSimpleRequest *request = [RMTSimpleRequest message];
426 request.responseSize = kPayloadSize;
427
Muxi Yanc92d90a2018-04-11 18:10:02 -0700428 __weak XCTestExpectation *expectation1 =
429 [self expectationWithDescription:@"AlternateDispatchQueue1"];
Muxi Yanb965ff62017-04-19 14:27:47 -0700430
431 // Use default (main) dispatch queue
Muxi Yanc92d90a2018-04-11 18:10:02 -0700432 NSString *main_queue_label =
433 [NSString stringWithUTF8String:dispatch_queue_get_label(dispatch_get_main_queue())];
Muxi Yanb965ff62017-04-19 14:27:47 -0700434
435 GRXWriter *requestsWriter1 = [GRXWriter writerWithValue:[request data]];
436
437 GRPCCall *call1 = [[GRPCCall alloc] initWithHost:kHostAddress
Muxi Yanfd002632017-04-20 16:16:49 -0700438 path:kUnaryCallMethod.HTTPPath
439 requestsWriter:requestsWriter1];
Muxi Yanb965ff62017-04-19 14:27:47 -0700440
Muxi Yanc92d90a2018-04-11 18:10:02 -0700441 id<GRXWriteable> responsesWriteable1 =
442 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
443 NSString *label =
444 [NSString stringWithUTF8String:dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)];
445 XCTAssert([label isEqualToString:main_queue_label]);
Muxi Yanb965ff62017-04-19 14:27:47 -0700446
Muxi Yanc92d90a2018-04-11 18:10:02 -0700447 [expectation1 fulfill];
448 }
449 completionHandler:^(NSError *errorOrNil){
450 }];
Muxi Yanb965ff62017-04-19 14:27:47 -0700451
452 [call1 startWithWriteable:responsesWriteable1];
453
454 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
455
456 // Use a custom queue
Muxi Yanc92d90a2018-04-11 18:10:02 -0700457 __weak XCTestExpectation *expectation2 =
458 [self expectationWithDescription:@"AlternateDispatchQueue2"];
Muxi Yanb965ff62017-04-19 14:27:47 -0700459
460 NSString *queue_label = @"test.queue1";
461 dispatch_queue_t queue = dispatch_queue_create([queue_label UTF8String], DISPATCH_QUEUE_SERIAL);
462
463 GRXWriter *requestsWriter2 = [GRXWriter writerWithValue:[request data]];
464
465 GRPCCall *call2 = [[GRPCCall alloc] initWithHost:kHostAddress
466 path:kUnaryCallMethod.HTTPPath
467 requestsWriter:requestsWriter2];
468
469 [call2 setResponseDispatchQueue:queue];
470
Muxi Yanc92d90a2018-04-11 18:10:02 -0700471 id<GRXWriteable> responsesWriteable2 =
472 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
473 NSString *label =
474 [NSString stringWithUTF8String:dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)];
475 XCTAssert([label isEqualToString:queue_label]);
Muxi Yanb965ff62017-04-19 14:27:47 -0700476
Muxi Yanc92d90a2018-04-11 18:10:02 -0700477 [expectation2 fulfill];
478 }
479 completionHandler:^(NSError *errorOrNil){
480 }];
Muxi Yanb965ff62017-04-19 14:27:47 -0700481
482 [call2 startWithWriteable:responsesWriteable2];
483
484 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
485}
486
Muxi Yanc7c8e3c2017-08-30 15:36:47 -0700487- (void)testTimeout {
Muxi Yan2795eae2017-09-16 09:59:20 -0700488 __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
Muxi Yanf282c8f2017-08-30 14:40:15 -0700489
Muxi Yan2795eae2017-09-16 09:59:20 -0700490 GRXBufferedPipe *pipe = [GRXBufferedPipe pipe];
Muxi Yanf282c8f2017-08-30 14:40:15 -0700491 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
Muxi Yan2795eae2017-09-16 09:59:20 -0700492 path:kFullDuplexCallMethod.HTTPPath
493 requestsWriter:pipe];
Muxi Yanf282c8f2017-08-30 14:40:15 -0700494
Muxi Yanc92d90a2018-04-11 18:10:02 -0700495 id<GRXWriteable> responsesWriteable =
496 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
497 XCTAssert(0, @"Failure: response received; Expect: no response received.");
498 }
499 completionHandler:^(NSError *errorOrNil) {
500 XCTAssertNotNil(errorOrNil,
501 @"Failure: no error received; Expect: receive deadline exceeded.");
502 XCTAssertEqual(errorOrNil.code, GRPCErrorCodeDeadlineExceeded);
503 [completion fulfill];
504 }];
Muxi Yanf282c8f2017-08-30 14:40:15 -0700505
Muxi Yanf6e61bf2017-09-11 15:48:51 -0700506 call.timeout = 0.001;
Muxi Yanf282c8f2017-08-30 14:40:15 -0700507 [call startWithWriteable:responsesWriteable];
508
509 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
510}
511
Muxi Yan109bb092018-04-04 15:16:30 -0700512- (int)findFreePort {
513 struct sockaddr_in addr;
514 unsigned int addr_len = sizeof(addr);
515 memset(&addr, 0, sizeof(addr));
516 addr.sin_family = AF_INET;
517 int fd = socket(AF_INET, SOCK_STREAM, 0);
Muxi Yanc92d90a2018-04-11 18:10:02 -0700518 XCTAssertEqual(bind(fd, (struct sockaddr *)&addr, sizeof(addr)), 0);
519 XCTAssertEqual(getsockname(fd, (struct sockaddr *)&addr, &addr_len), 0);
Muxi Yan109bb092018-04-04 15:16:30 -0700520 XCTAssertEqual(addr_len, sizeof(addr));
521 close(fd);
522 return addr.sin_port;
523}
524
525- (void)testErrorCode {
526 int port = [self findFreePort];
Muxi Yanc92d90a2018-04-11 18:10:02 -0700527 NSString *const kDummyAddress = [NSString stringWithFormat:@"localhost:%d", port];
Muxi Yane5a39492018-06-11 14:26:46 -0700528 __weak XCTestExpectation *completion =
529 [self expectationWithDescription:@"Received correct error code."];
Muxi Yan109bb092018-04-04 15:16:30 -0700530
531 GRPCCall *call = [[GRPCCall alloc] initWithHost:kDummyAddress
532 path:kEmptyCallMethod.HTTPPath
533 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
534
Muxi Yanc92d90a2018-04-11 18:10:02 -0700535 id<GRXWriteable> responsesWriteable =
536 [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
537 // Should not reach here
538 XCTAssert(NO);
539 }
540 completionHandler:^(NSError *errorOrNil) {
541 XCTAssertNotNil(errorOrNil, @"Finished with no error");
542 XCTAssertEqual(errorOrNil.code, GRPC_STATUS_UNAVAILABLE);
543 [completion fulfill];
544 }];
Muxi Yan109bb092018-04-04 15:16:30 -0700545
546 [call startWithWriteable:responsesWriteable];
547
548 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
549}
550
Muxi Yan1176dc32018-06-21 11:52:59 -0700551- (void)testTimeoutBackoffWithTimeout:(double)timeout Backoff:(double)backoff {
552 const double maxConnectTime = timeout > backoff ? timeout : backoff;
553 const double kMargin = 0.1;
554
555 __weak XCTestExpectation *completion = [self expectationWithDescription:@"Timeout in a second."];
556 NSString *const kDummyAddress = [NSString stringWithFormat:@"8.8.8.8:1"];
557 GRPCCall *call = [[GRPCCall alloc] initWithHost:kDummyAddress
558 path:@""
559 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
560 [GRPCCall setMinConnectTimeout:timeout * 1000
561 initialBackoff:backoff * 1000
562 maxBackoff:0
563 forHost:kDummyAddress];
564 NSDate *startTime = [NSDate date];
565 id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(id value) {
566 XCTAssert(NO, @"Received message. Should not reach here");
567 }
568 completionHandler:^(NSError *errorOrNil) {
569 XCTAssertNotNil(errorOrNil, @"Finished with no error");
570 // The call must fail before maxConnectTime. However there is no lower bound on the time
571 // taken for connection. A shorter time happens when connection is actively refused
572 // by 8.8.8.8:1 before maxConnectTime elapsed.
573 XCTAssertLessThan([[NSDate date] timeIntervalSinceDate:startTime],
574 maxConnectTime + kMargin);
575 [completion fulfill];
576 }];
577
578 [call startWithWriteable:responsesWriteable];
579
580 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
581}
582
583// The numbers of the following three tests are selected to be smaller than the default values of
584// initial backoff (1s) and min_connect_timeout (20s), so that if they fail we know the default
585// values fail to be overridden by the channel args.
586- (void)testTimeoutBackoff2 {
587 [self testTimeoutBackoffWithTimeout:0.7 Backoff:0.3];
588}
589
590- (void)testTimeoutBackoff3 {
591 [self testTimeoutBackoffWithTimeout:0.3 Backoff:0.7];
592}
593
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700594@end