Jorge Canizales | b3be229 | 2015-05-31 19:11:20 -0700 | [diff] [blame] | 1 | /* |
| 2 | * |
Jan Tattermusch | 7897ae9 | 2017-06-07 22:57:36 +0200 | [diff] [blame] | 3 | * Copyright 2015 gRPC authors. |
Jorge Canizales | b3be229 | 2015-05-31 19:11:20 -0700 | [diff] [blame] | 4 | * |
Jan Tattermusch | 7897ae9 | 2017-06-07 22:57:36 +0200 | [diff] [blame] | 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 |
Jorge Canizales | b3be229 | 2015-05-31 19:11:20 -0700 | [diff] [blame] | 8 | * |
Jan Tattermusch | 7897ae9 | 2017-06-07 22:57:36 +0200 | [diff] [blame] | 9 | * http://www.apache.org/licenses/LICENSE-2.0 |
Jorge Canizales | b3be229 | 2015-05-31 19:11:20 -0700 | [diff] [blame] | 10 | * |
Jan Tattermusch | 7897ae9 | 2017-06-07 22:57:36 +0200 | [diff] [blame] | 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. |
Jorge Canizales | b3be229 | 2015-05-31 19:11:20 -0700 | [diff] [blame] | 16 | * |
| 17 | */ |
| 18 | |
| 19 | #import <UIKit/UIKit.h> |
| 20 | #import <XCTest/XCTest.h> |
Muxi Yan | 3305e5c | 2017-10-11 18:31:43 -0700 | [diff] [blame] | 21 | #import <grpc/grpc.h> |
Jorge Canizales | b3be229 | 2015-05-31 19:11:20 -0700 | [diff] [blame] | 22 | |
Makarand Dharmapurikar | 1c2890d | 2016-02-24 14:35:46 -0800 | [diff] [blame] | 23 | #import <GRPCClient/GRPCCall+ChannelArg.h> |
Jorge Canizales | 631dc00 | 2015-08-06 23:08:41 -0700 | [diff] [blame] | 24 | #import <GRPCClient/GRPCCall+OAuth2.h> |
Muxi Yan | 8e6aec5 | 2017-02-10 09:40:18 -0800 | [diff] [blame] | 25 | #import <GRPCClient/GRPCCall+Tests.h> |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 26 | #import <GRPCClient/GRPCCall.h> |
Muxi Yan | 8e6aec5 | 2017-02-10 09:40:18 -0800 | [diff] [blame] | 27 | #import <GRPCClient/internal_testing/GRPCCall+InternalTests.h> |
Jorge Canizales | be808e3 | 2015-07-04 14:37:58 -0700 | [diff] [blame] | 28 | #import <ProtoRPC/ProtoMethod.h> |
Jorge Canizales | b3be229 | 2015-05-31 19:11:20 -0700 | [diff] [blame] | 29 | #import <RemoteTest/Messages.pbobjc.h> |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 30 | #import <RxLibrary/GRXBufferedPipe.h> |
Jorge Canizales | c42a38e | 2015-06-21 14:44:25 -0700 | [diff] [blame] | 31 | #import <RxLibrary/GRXWriteable.h> |
| 32 | #import <RxLibrary/GRXWriter+Immediate.h> |
Makarand Dharmapurikar | a0414ee | 2016-02-23 16:58:46 -0800 | [diff] [blame] | 33 | |
Muxi Yan | 109bb09 | 2018-04-04 15:16:30 -0700 | [diff] [blame] | 34 | #include <netinet/in.h> |
| 35 | |
Muxi Yan | 3305e5c | 2017-10-11 18:31:43 -0700 | [diff] [blame] | 36 | #import "version.h" |
| 37 | |
Muxi Yan | b1341a3 | 2016-12-05 16:38:02 -0800 | [diff] [blame] | 38 | #define TEST_TIMEOUT 16 |
| 39 | |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 40 | static NSString *const kHostAddress = @"localhost:5050"; |
| 41 | static NSString *const kPackage = @"grpc.testing"; |
| 42 | static NSString *const kService = @"TestService"; |
| 43 | static NSString *const kRemoteSSLHost = @"grpc-test.sandbox.googleapis.com"; |
Makarand Dharmapurikar | a0414ee | 2016-02-23 16:58:46 -0800 | [diff] [blame] | 44 | |
Yuchen Zeng | dbe2b9e | 2016-06-15 20:23:04 -0700 | [diff] [blame] | 45 | static GRPCProtoMethod *kInexistentMethod; |
| 46 | static GRPCProtoMethod *kEmptyCallMethod; |
| 47 | static GRPCProtoMethod *kUnaryCallMethod; |
Muxi Yan | 2795eae | 2017-09-16 09:59:20 -0700 | [diff] [blame] | 48 | static GRPCProtoMethod *kFullDuplexCallMethod; |
Jorge Canizales | d666fd0 | 2015-06-12 18:59:11 -0700 | [diff] [blame] | 49 | |
Jorge Canizales | 2beb88c | 2015-10-28 15:13:40 -0700 | [diff] [blame] | 50 | /** Observer class for testing that responseMetadata is KVO-compliant */ |
murgatroid99 | 81b4fcb | 2015-08-24 10:56:44 -0700 | [diff] [blame] | 51 | @interface PassthroughObserver : NSObject |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 52 | - (instancetype)initWithCallback:(void (^)(NSString *, id, NSDictionary *))callback |
Jorge Canizales | 2beb88c | 2015-10-28 15:13:40 -0700 | [diff] [blame] | 53 | NS_DESIGNATED_INITIALIZER; |
murgatroid99 | 81b4fcb | 2015-08-24 10:56:44 -0700 | [diff] [blame] | 54 | |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 55 | - (void)observeValueForKeyPath:(NSString *)keyPath |
| 56 | ofObject:(id)object |
| 57 | change:(NSDictionary *)change |
murgatroid99 | 81b4fcb | 2015-08-24 10:56:44 -0700 | [diff] [blame] | 58 | context:(void *)context; |
| 59 | @end |
| 60 | |
| 61 | @implementation PassthroughObserver { |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 62 | void (^_callback)(NSString *, id, NSDictionary *); |
murgatroid99 | 81b4fcb | 2015-08-24 10:56:44 -0700 | [diff] [blame] | 63 | } |
| 64 | |
Jorge Canizales | 2beb88c | 2015-10-28 15:13:40 -0700 | [diff] [blame] | 65 | - (instancetype)init { |
| 66 | return [self initWithCallback:nil]; |
| 67 | } |
| 68 | |
murgatroid99 | 81b4fcb | 2015-08-24 10:56:44 -0700 | [diff] [blame] | 69 | - (instancetype)initWithCallback:(void (^)(NSString *, id, NSDictionary *))callback { |
Jorge Canizales | 2beb88c | 2015-10-28 15:13:40 -0700 | [diff] [blame] | 70 | if (!callback) { |
| 71 | return nil; |
| 72 | } |
| 73 | if ((self = [super init])) { |
murgatroid99 | 81b4fcb | 2015-08-24 10:56:44 -0700 | [diff] [blame] | 74 | _callback = callback; |
| 75 | } |
| 76 | return self; |
murgatroid99 | 81b4fcb | 2015-08-24 10:56:44 -0700 | [diff] [blame] | 77 | } |
| 78 | |
Jorge Canizales | 2beb88c | 2015-10-28 15:13:40 -0700 | [diff] [blame] | 79 | - (void)observeValueForKeyPath:(NSString *)keyPath |
| 80 | ofObject:(id)object |
| 81 | change:(NSDictionary *)change |
| 82 | context:(void *)context { |
murgatroid99 | 81b4fcb | 2015-08-24 10:56:44 -0700 | [diff] [blame] | 83 | _callback(keyPath, object, change); |
murgatroid99 | 2e49a35 | 2015-08-24 14:40:45 -0700 | [diff] [blame] | 84 | [object removeObserver:self forKeyPath:keyPath]; |
murgatroid99 | 81b4fcb | 2015-08-24 10:56:44 -0700 | [diff] [blame] | 85 | } |
| 86 | |
| 87 | @end |
| 88 | |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 89 | #pragma mark Tests |
Jorge Canizales | 2beb88c | 2015-10-28 15:13:40 -0700 | [diff] [blame] | 90 | |
| 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 Canizales | b3be229 | 2015-05-31 19:11:20 -0700 | [diff] [blame] | 97 | @interface GRPCClientTests : XCTestCase |
| 98 | @end |
| 99 | |
| 100 | @implementation GRPCClientTests |
| 101 | |
Muxi Yan | 4b947d3 | 2017-11-30 09:36:29 -0800 | [diff] [blame] | 102 | + (void)setUp { |
| 103 | NSLog(@"GRPCClientTests Started"); |
| 104 | } |
| 105 | |
Jorge Canizales | d666fd0 | 2015-06-12 18:59:11 -0700 | [diff] [blame] | 106 | - (void)setUp { |
Makarand Dharmapurikar | c2b3490 | 2016-02-26 16:38:17 -0800 | [diff] [blame] | 107 | // Add a custom user agent prefix that will be used in test |
| 108 | [GRPCCall setUserAgentPrefix:@"Foo" forHost:kHostAddress]; |
Jorge Canizales | b2bd067 | 2015-08-01 23:19:11 -0700 | [diff] [blame] | 109 | // Register test server as non-SSL. |
| 110 | [GRPCCall useInsecureConnectionsForHost:kHostAddress]; |
| 111 | |
Jorge Canizales | d666fd0 | 2015-06-12 18:59:11 -0700 | [diff] [blame] | 112 | // This method isn't implemented by the remote server. |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 113 | 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 Canizales | d666fd0 | 2015-06-12 18:59:11 -0700 | [diff] [blame] | 121 | } |
| 122 | |
Jorge Canizales | b3be229 | 2015-05-31 19:11:20 -0700 | [diff] [blame] | 123 | - (void)testConnectionToRemoteServer { |
| 124 | __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Server reachable."]; |
| 125 | |
Jorge Canizales | d666fd0 | 2015-06-12 18:59:11 -0700 | [diff] [blame] | 126 | GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress |
Jorge Canizales | 5260f53 | 2015-07-04 14:47:41 -0700 | [diff] [blame] | 127 | path:kInexistentMethod.HTTPPath |
Jorge Canizales | d666fd0 | 2015-06-12 18:59:11 -0700 | [diff] [blame] | 128 | requestsWriter:[GRXWriter writerWithValue:[NSData data]]]; |
Jorge Canizales | b3be229 | 2015-05-31 19:11:20 -0700 | [diff] [blame] | 129 | |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 130 | 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 Canizales | b3be229 | 2015-05-31 19:11:20 -0700 | [diff] [blame] | 139 | |
| 140 | [call startWithWriteable:responsesWriteable]; |
| 141 | |
Muxi Yan | b1341a3 | 2016-12-05 16:38:02 -0800 | [diff] [blame] | 142 | [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; |
Jorge Canizales | b3be229 | 2015-05-31 19:11:20 -0700 | [diff] [blame] | 143 | } |
| 144 | |
| 145 | - (void)testEmptyRPC { |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 146 | __weak XCTestExpectation *response = |
| 147 | [self expectationWithDescription:@"Empty response received."]; |
Jorge Canizales | b3be229 | 2015-05-31 19:11:20 -0700 | [diff] [blame] | 148 | __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."]; |
| 149 | |
Jorge Canizales | d666fd0 | 2015-06-12 18:59:11 -0700 | [diff] [blame] | 150 | GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress |
Jorge Canizales | 5260f53 | 2015-07-04 14:47:41 -0700 | [diff] [blame] | 151 | path:kEmptyCallMethod.HTTPPath |
Jorge Canizales | d666fd0 | 2015-06-12 18:59:11 -0700 | [diff] [blame] | 152 | requestsWriter:[GRXWriter writerWithValue:[NSData data]]]; |
Jorge Canizales | b3be229 | 2015-05-31 19:11:20 -0700 | [diff] [blame] | 153 | |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 154 | 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 Canizales | b3be229 | 2015-05-31 19:11:20 -0700 | [diff] [blame] | 164 | |
| 165 | [call startWithWriteable:responsesWriteable]; |
| 166 | |
Muxi Yan | b1341a3 | 2016-12-05 16:38:02 -0800 | [diff] [blame] | 167 | [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; |
Jorge Canizales | b3be229 | 2015-05-31 19:11:20 -0700 | [diff] [blame] | 168 | } |
| 169 | |
| 170 | - (void)testSimpleProtoRPC { |
Jorge Canizales | d666fd0 | 2015-06-12 18:59:11 -0700 | [diff] [blame] | 171 | __weak XCTestExpectation *response = [self expectationWithDescription:@"Expected response."]; |
Jorge Canizales | b3be229 | 2015-05-31 19:11:20 -0700 | [diff] [blame] | 172 | __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."]; |
| 173 | |
Jorge Canizales | d666fd0 | 2015-06-12 18:59:11 -0700 | [diff] [blame] | 174 | RMTSimpleRequest *request = [RMTSimpleRequest message]; |
Jorge Canizales | b3be229 | 2015-05-31 19:11:20 -0700 | [diff] [blame] | 175 | request.responseSize = 100; |
| 176 | request.fillUsername = YES; |
| 177 | request.fillOauthScope = YES; |
Jorge Canizales | a8c5d96 | 2015-07-16 18:55:31 -0700 | [diff] [blame] | 178 | GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]]; |
Jorge Canizales | b3be229 | 2015-05-31 19:11:20 -0700 | [diff] [blame] | 179 | |
Jorge Canizales | d666fd0 | 2015-06-12 18:59:11 -0700 | [diff] [blame] | 180 | GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress |
Jorge Canizales | 5260f53 | 2015-07-04 14:47:41 -0700 | [diff] [blame] | 181 | path:kUnaryCallMethod.HTTPPath |
Jorge Canizales | b3be229 | 2015-05-31 19:11:20 -0700 | [diff] [blame] | 182 | requestsWriter:requestsWriter]; |
| 183 | |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 184 | 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 Canizales | b3be229 | 2015-05-31 19:11:20 -0700 | [diff] [blame] | 198 | |
| 199 | [call startWithWriteable:responsesWriteable]; |
| 200 | |
Muxi Yan | b1341a3 | 2016-12-05 16:38:02 -0800 | [diff] [blame] | 201 | [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; |
Jorge Canizales | b3be229 | 2015-05-31 19:11:20 -0700 | [diff] [blame] | 202 | } |
| 203 | |
Jorge Canizales | d798125 | 2015-06-12 19:15:18 -0700 | [diff] [blame] | 204 | - (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 Canizales | a8c5d96 | 2015-07-16 18:55:31 -0700 | [diff] [blame] | 210 | GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]]; |
Jorge Canizales | d798125 | 2015-06-12 19:15:18 -0700 | [diff] [blame] | 211 | |
Test User | 9656eca | 2016-02-18 14:47:22 -0800 | [diff] [blame] | 212 | GRPCCall *call = [[GRPCCall alloc] initWithHost:kRemoteSSLHost |
Jorge Canizales | 5260f53 | 2015-07-04 14:47:41 -0700 | [diff] [blame] | 213 | path:kUnaryCallMethod.HTTPPath |
Jorge Canizales | d798125 | 2015-06-12 19:15:18 -0700 | [diff] [blame] | 214 | requestsWriter:requestsWriter]; |
| 215 | |
Jorge Canizales | 721b7a3 | 2015-08-07 10:11:16 -0700 | [diff] [blame] | 216 | call.oauth2AccessToken = @"bogusToken"; |
Jorge Canizales | d798125 | 2015-06-12 19:15:18 -0700 | [diff] [blame] | 217 | |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 218 | 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 Canizales | d798125 | 2015-06-12 19:15:18 -0700 | [diff] [blame] | 234 | |
| 235 | [call startWithWriteable:responsesWriteable]; |
| 236 | |
Muxi Yan | b1341a3 | 2016-12-05 16:38:02 -0800 | [diff] [blame] | 237 | [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; |
Jorge Canizales | d798125 | 2015-06-12 19:15:18 -0700 | [diff] [blame] | 238 | } |
| 239 | |
murgatroid99 | 81b4fcb | 2015-08-24 10:56:44 -0700 | [diff] [blame] | 240 | - (void)testResponseMetadataKVO { |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 241 | __weak XCTestExpectation *response = |
| 242 | [self expectationWithDescription:@"Empty response received."]; |
murgatroid99 | 81b4fcb | 2015-08-24 10:56:44 -0700 | [diff] [blame] | 243 | __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."]; |
| 244 | __weak XCTestExpectation *metadata = [self expectationWithDescription:@"Metadata changed."]; |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 245 | |
murgatroid99 | 81b4fcb | 2015-08-24 10:56:44 -0700 | [diff] [blame] | 246 | GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress |
| 247 | path:kEmptyCallMethod.HTTPPath |
| 248 | requestsWriter:[GRXWriter writerWithValue:[NSData data]]]; |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 249 | |
| 250 | PassthroughObserver *observer = [[PassthroughObserver alloc] |
| 251 | initWithCallback:^(NSString *keypath, id object, NSDictionary *change) { |
| 252 | if ([keypath isEqual:@"responseHeaders"]) { |
| 253 | [metadata fulfill]; |
| 254 | } |
| 255 | }]; |
| 256 | |
murgatroid99 | 81b4fcb | 2015-08-24 10:56:44 -0700 | [diff] [blame] | 257 | [call addObserver:observer forKeyPath:@"responseHeaders" options:0 context:NULL]; |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 258 | |
| 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 | |
murgatroid99 | 81b4fcb | 2015-08-24 10:56:44 -0700 | [diff] [blame] | 270 | [call startWithWriteable:responsesWriteable]; |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 271 | |
Muxi Yan | b1341a3 | 2016-12-05 16:38:02 -0800 | [diff] [blame] | 272 | [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; |
murgatroid99 | 81b4fcb | 2015-08-24 10:56:44 -0700 | [diff] [blame] | 273 | } |
| 274 | |
Makarand Dharmapurikar | c2b3490 | 2016-02-26 16:38:17 -0800 | [diff] [blame] | 275 | - (void)testUserAgentPrefix { |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 276 | __weak XCTestExpectation *response = |
| 277 | [self expectationWithDescription:@"Empty response received."]; |
Makarand Dharmapurikar | c2b3490 | 2016-02-26 16:38:17 -0800 | [diff] [blame] | 278 | __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 Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 287 | 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 Yan | 3305e5c | 2017-10-11 18:31:43 -0700 | [diff] [blame] | 291 | |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 292 | NSString *userAgent = call.responseHeaders[@"x-grpc-test-echo-useragent"]; |
| 293 | NSError *error = nil; |
Muxi Yan | 3305e5c | 2017-10-11 18:31:43 -0700 | [diff] [blame] | 294 | |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 295 | // 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 Yan | 3305e5c | 2017-10-11 18:31:43 -0700 | [diff] [blame] | 305 | |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 306 | // 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 Yan | 3305e5c | 2017-10-11 18:31:43 -0700 | [diff] [blame] | 319 | |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 320 | [response fulfill]; |
| 321 | } |
| 322 | completionHandler:^(NSError *errorOrNil) { |
| 323 | XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil); |
| 324 | [completion fulfill]; |
| 325 | }]; |
Makarand Dharmapurikar | c2b3490 | 2016-02-26 16:38:17 -0800 | [diff] [blame] | 326 | |
| 327 | [call startWithWriteable:responsesWriteable]; |
| 328 | |
Muxi Yan | b1341a3 | 2016-12-05 16:38:02 -0800 | [diff] [blame] | 329 | [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; |
Makarand Dharmapurikar | c2b3490 | 2016-02-26 16:38:17 -0800 | [diff] [blame] | 330 | } |
| 331 | |
Makarand Dharmapurikar | 716c7c5 | 2017-05-09 13:04:41 -0700 | [diff] [blame] | 332 | - (void)testTrailers { |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 333 | __weak XCTestExpectation *response = |
| 334 | [self expectationWithDescription:@"Empty response received."]; |
Makarand Dharmapurikar | 716c7c5 | 2017-05-09 13:04:41 -0700 | [diff] [blame] | 335 | __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 Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 342 | const unsigned char raw_bytes[] = {1, 2, 3, 4}; |
Makarand Dharmapurikar | 716c7c5 | 2017-05-09 13:04:41 -0700 | [diff] [blame] | 343 | NSData *trailer_data = [NSData dataWithBytes:raw_bytes length:sizeof(raw_bytes)]; |
| 344 | call.requestHeaders[@"x-grpc-test-echo-trailing-bin"] = trailer_data; |
| 345 | |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 346 | 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 Dharmapurikar | 716c7c5 | 2017-05-09 13:04:41 -0700 | [diff] [blame] | 358 | |
| 359 | [call startWithWriteable:responsesWriteable]; |
| 360 | [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; |
| 361 | } |
| 362 | |
Makarand Dharmapurikar | 63f2b5b | 2016-02-24 13:54:48 -0800 | [diff] [blame] | 363 | // TODO(makarandd): Move to a different file that contains only unit tests |
Test User | 9656eca | 2016-02-18 14:47:22 -0800 | [diff] [blame] | 364 | - (void)testExceptions { |
Test User | 9656eca | 2016-02-18 14:47:22 -0800 | [diff] [blame] | 365 | // Try to set parameters to nil for GRPCCall. This should cause an exception |
| 366 | @try { |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 367 | (void)[[GRPCCall alloc] initWithHost:nil path:nil requestsWriter:nil]; |
Test User | 9656eca | 2016-02-18 14:47:22 -0800 | [diff] [blame] | 368 | XCTFail(@"Did not receive an exception when parameters are nil"); |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 369 | } @catch (NSException *theException) { |
Test User | 9656eca | 2016-02-18 14:47:22 -0800 | [diff] [blame] | 370 | NSLog(@"Received exception as expected: %@", theException.name); |
| 371 | } |
| 372 | |
Test User | 9656eca | 2016-02-18 14:47:22 -0800 | [diff] [blame] | 373 | // Set state to Finished by force |
| 374 | GRXWriter *requestsWriter = [GRXWriter emptyWriter]; |
| 375 | [requestsWriter finishWithError:nil]; |
| 376 | @try { |
Yuchen Zeng | d5fd7dd | 2016-06-21 11:13:23 -0700 | [diff] [blame] | 377 | (void)[[GRPCCall alloc] initWithHost:kHostAddress |
| 378 | path:kUnaryCallMethod.HTTPPath |
| 379 | requestsWriter:requestsWriter]; |
Test User | 9656eca | 2016-02-18 14:47:22 -0800 | [diff] [blame] | 380 | XCTFail(@"Did not receive an exception when GRXWriter has incorrect state."); |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 381 | } @catch (NSException *theException) { |
Test User | 9656eca | 2016-02-18 14:47:22 -0800 | [diff] [blame] | 382 | NSLog(@"Received exception as expected: %@", theException.name); |
| 383 | } |
Test User | 9656eca | 2016-02-18 14:47:22 -0800 | [diff] [blame] | 384 | } |
| 385 | |
Muxi Yan | 1bd5c77 | 2016-09-30 15:13:58 -0700 | [diff] [blame] | 386 | - (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 Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 399 | [GRPCCall setCallSafety:GRPCCallSafetyIdempotentRequest |
| 400 | host:kHostAddress |
| 401 | path:kUnaryCallMethod.HTTPPath]; |
Muxi Yan | 1bd5c77 | 2016-09-30 15:13:58 -0700 | [diff] [blame] | 402 | |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 403 | 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 Yan | 1bd5c77 | 2016-09-30 15:13:58 -0700 | [diff] [blame] | 417 | |
| 418 | [call startWithWriteable:responsesWriteable]; |
| 419 | |
Muxi Yan | b1341a3 | 2016-12-05 16:38:02 -0800 | [diff] [blame] | 420 | [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; |
Muxi Yan | 1bd5c77 | 2016-09-30 15:13:58 -0700 | [diff] [blame] | 421 | } |
| 422 | |
Muxi Yan | b965ff6 | 2017-04-19 14:27:47 -0700 | [diff] [blame] | 423 | - (void)testAlternateDispatchQueue { |
| 424 | const int32_t kPayloadSize = 100; |
| 425 | RMTSimpleRequest *request = [RMTSimpleRequest message]; |
| 426 | request.responseSize = kPayloadSize; |
| 427 | |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 428 | __weak XCTestExpectation *expectation1 = |
| 429 | [self expectationWithDescription:@"AlternateDispatchQueue1"]; |
Muxi Yan | b965ff6 | 2017-04-19 14:27:47 -0700 | [diff] [blame] | 430 | |
| 431 | // Use default (main) dispatch queue |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 432 | NSString *main_queue_label = |
| 433 | [NSString stringWithUTF8String:dispatch_queue_get_label(dispatch_get_main_queue())]; |
Muxi Yan | b965ff6 | 2017-04-19 14:27:47 -0700 | [diff] [blame] | 434 | |
| 435 | GRXWriter *requestsWriter1 = [GRXWriter writerWithValue:[request data]]; |
| 436 | |
| 437 | GRPCCall *call1 = [[GRPCCall alloc] initWithHost:kHostAddress |
Muxi Yan | fd00263 | 2017-04-20 16:16:49 -0700 | [diff] [blame] | 438 | path:kUnaryCallMethod.HTTPPath |
| 439 | requestsWriter:requestsWriter1]; |
Muxi Yan | b965ff6 | 2017-04-19 14:27:47 -0700 | [diff] [blame] | 440 | |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 441 | 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 Yan | b965ff6 | 2017-04-19 14:27:47 -0700 | [diff] [blame] | 446 | |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 447 | [expectation1 fulfill]; |
| 448 | } |
| 449 | completionHandler:^(NSError *errorOrNil){ |
| 450 | }]; |
Muxi Yan | b965ff6 | 2017-04-19 14:27:47 -0700 | [diff] [blame] | 451 | |
| 452 | [call1 startWithWriteable:responsesWriteable1]; |
| 453 | |
| 454 | [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; |
| 455 | |
| 456 | // Use a custom queue |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 457 | __weak XCTestExpectation *expectation2 = |
| 458 | [self expectationWithDescription:@"AlternateDispatchQueue2"]; |
Muxi Yan | b965ff6 | 2017-04-19 14:27:47 -0700 | [diff] [blame] | 459 | |
| 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 Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 471 | 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 Yan | b965ff6 | 2017-04-19 14:27:47 -0700 | [diff] [blame] | 476 | |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 477 | [expectation2 fulfill]; |
| 478 | } |
| 479 | completionHandler:^(NSError *errorOrNil){ |
| 480 | }]; |
Muxi Yan | b965ff6 | 2017-04-19 14:27:47 -0700 | [diff] [blame] | 481 | |
| 482 | [call2 startWithWriteable:responsesWriteable2]; |
| 483 | |
| 484 | [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; |
| 485 | } |
| 486 | |
Muxi Yan | c7c8e3c | 2017-08-30 15:36:47 -0700 | [diff] [blame] | 487 | - (void)testTimeout { |
Muxi Yan | 2795eae | 2017-09-16 09:59:20 -0700 | [diff] [blame] | 488 | __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."]; |
Muxi Yan | f282c8f | 2017-08-30 14:40:15 -0700 | [diff] [blame] | 489 | |
Muxi Yan | 2795eae | 2017-09-16 09:59:20 -0700 | [diff] [blame] | 490 | GRXBufferedPipe *pipe = [GRXBufferedPipe pipe]; |
Muxi Yan | f282c8f | 2017-08-30 14:40:15 -0700 | [diff] [blame] | 491 | GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress |
Muxi Yan | 2795eae | 2017-09-16 09:59:20 -0700 | [diff] [blame] | 492 | path:kFullDuplexCallMethod.HTTPPath |
| 493 | requestsWriter:pipe]; |
Muxi Yan | f282c8f | 2017-08-30 14:40:15 -0700 | [diff] [blame] | 494 | |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 495 | 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 Yan | f282c8f | 2017-08-30 14:40:15 -0700 | [diff] [blame] | 505 | |
Muxi Yan | f6e61bf | 2017-09-11 15:48:51 -0700 | [diff] [blame] | 506 | call.timeout = 0.001; |
Muxi Yan | f282c8f | 2017-08-30 14:40:15 -0700 | [diff] [blame] | 507 | [call startWithWriteable:responsesWriteable]; |
| 508 | |
| 509 | [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; |
| 510 | } |
| 511 | |
Muxi Yan | 109bb09 | 2018-04-04 15:16:30 -0700 | [diff] [blame] | 512 | - (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 Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 518 | XCTAssertEqual(bind(fd, (struct sockaddr *)&addr, sizeof(addr)), 0); |
| 519 | XCTAssertEqual(getsockname(fd, (struct sockaddr *)&addr, &addr_len), 0); |
Muxi Yan | 109bb09 | 2018-04-04 15:16:30 -0700 | [diff] [blame] | 520 | XCTAssertEqual(addr_len, sizeof(addr)); |
| 521 | close(fd); |
| 522 | return addr.sin_port; |
| 523 | } |
| 524 | |
| 525 | - (void)testErrorCode { |
| 526 | int port = [self findFreePort]; |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 527 | NSString *const kDummyAddress = [NSString stringWithFormat:@"localhost:%d", port]; |
Muxi Yan | e5a3949 | 2018-06-11 14:26:46 -0700 | [diff] [blame] | 528 | __weak XCTestExpectation *completion = |
| 529 | [self expectationWithDescription:@"Received correct error code."]; |
Muxi Yan | 109bb09 | 2018-04-04 15:16:30 -0700 | [diff] [blame] | 530 | |
| 531 | GRPCCall *call = [[GRPCCall alloc] initWithHost:kDummyAddress |
| 532 | path:kEmptyCallMethod.HTTPPath |
| 533 | requestsWriter:[GRXWriter writerWithValue:[NSData data]]]; |
| 534 | |
Muxi Yan | c92d90a | 2018-04-11 18:10:02 -0700 | [diff] [blame] | 535 | 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 Yan | 109bb09 | 2018-04-04 15:16:30 -0700 | [diff] [blame] | 545 | |
| 546 | [call startWithWriteable:responsesWriteable]; |
| 547 | |
| 548 | [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; |
| 549 | } |
| 550 | |
Muxi Yan | 1176dc3 | 2018-06-21 11:52:59 -0700 | [diff] [blame] | 551 | - (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 Canizales | b3be229 | 2015-05-31 19:11:20 -0700 | [diff] [blame] | 594 | @end |