• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 *
3 * Copyright 2019 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 "PerfTests.h"
20
21#include <grpc/status.h>
22
23#import <GRPCClient/GRPCCall+ChannelArg.h>
24#import <GRPCClient/GRPCCall+Cronet.h>
25#import <GRPCClient/GRPCCall+Interceptor.h>
26#import <GRPCClient/GRPCCall+Tests.h>
27#import <GRPCClient/GRPCInterceptor.h>
28#import <GRPCClient/internal_testing/GRPCCall+InternalTests.h>
29#import <ProtoRPC/ProtoRPC.h>
30#import <RxLibrary/GRXBufferedPipe.h>
31#import <RxLibrary/GRXWriter+Immediate.h>
32#import <grpc/grpc.h>
33#import <grpc/support/log.h>
34#import "src/objective-c/tests/RemoteTestClient/Messages.pbobjc.h"
35#import "src/objective-c/tests/RemoteTestClient/Test.pbobjc.h"
36#import "src/objective-c/tests/RemoteTestClient/Test.pbrpc.h"
37
38#import "PerfTestsBlockCallbacks.h"
39
40#define TEST_TIMEOUT 128
41
42extern const char *kCFStreamVarName;
43
44// Convenience constructors for the generated proto messages:
45
46@interface RMTStreamingOutputCallRequest (Constructors)
47+ (instancetype)messageWithPayloadSize:(NSNumber *)payloadSize
48                 requestedResponseSize:(NSNumber *)responseSize;
49@end
50
51@implementation RMTStreamingOutputCallRequest (Constructors)
52+ (instancetype)messageWithPayloadSize:(NSNumber *)payloadSize
53                 requestedResponseSize:(NSNumber *)responseSize {
54  RMTStreamingOutputCallRequest *request = [self message];
55  RMTResponseParameters *parameters = [RMTResponseParameters message];
56  parameters.size = responseSize.intValue;
57  [request.responseParametersArray addObject:parameters];
58  request.payload.body = [NSMutableData dataWithLength:payloadSize.unsignedIntegerValue];
59  return request;
60}
61@end
62
63@interface DefaultInterceptorFactory : NSObject <GRPCInterceptorFactory>
64
65- (GRPCInterceptor *)createInterceptorWithManager:(GRPCInterceptorManager *)interceptorManager;
66
67@end
68
69@implementation DefaultInterceptorFactory
70
71- (GRPCInterceptor *)createInterceptorWithManager:(GRPCInterceptorManager *)interceptorManager {
72  dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
73  return [[GRPCInterceptor alloc] initWithInterceptorManager:interceptorManager
74                                               dispatchQueue:queue];
75}
76
77@end
78
79#pragma mark Tests
80
81@implementation PerfTests {
82  RMTTestService *_service;
83}
84
85+ (XCTestSuite *)defaultTestSuite {
86  if (self == [PerfTests class]) {
87    return [XCTestSuite testSuiteWithName:@"PerfTestsEmptySuite"];
88  } else {
89    return super.defaultTestSuite;
90  }
91}
92
93+ (NSString *)host {
94  return nil;
95}
96
97// This number indicates how many bytes of overhead does Protocol Buffers encoding add onto the
98// message. The number varies as different message.proto is used on different servers. The actual
99// number for each interop server is overridden in corresponding derived test classes.
100- (int32_t)encodingOverhead {
101  return 0;
102}
103
104+ (GRPCTransportID)transport {
105  return NULL;
106}
107
108+ (NSString *)PEMRootCertificates {
109  return nil;
110}
111
112+ (NSString *)hostNameOverride {
113  return nil;
114}
115
116- (void)setUp {
117  self.continueAfterFailure = NO;
118
119  [GRPCCall resetHostSettings];
120
121#pragma clang diagnostic push
122#pragma clang diagnostic ignored "-Wdeprecated-declarations"
123  [GRPCCall closeOpenConnections];
124#pragma clang diagnostic pop
125
126  _service = [[self class] host] ? [RMTTestService serviceWithHost:[[self class] host]] : nil;
127}
128
129- (BOOL)isUsingCFStream {
130  return [NSStringFromClass([self class]) isEqualToString:@"PerfTestsCFStreamSSL"];
131}
132
133- (void)pingPongV2APIWithRequest:(RMTStreamingOutputCallRequest *)request
134                     numMessages:(int)numMessages
135                         options:(GRPCMutableCallOptions *)options {
136  __weak XCTestExpectation *expectation = [self expectationWithDescription:@"PingPong"];
137
138  __block BOOL flowControlEnabled = options.flowControlEnabled;
139  __block int index = 0;
140  __block GRPCStreamingProtoCall *call = [self->_service
141      fullDuplexCallWithResponseHandler:[[PerfTestsBlockCallbacks alloc]
142                                            initWithInitialMetadataCallback:nil
143                                            messageCallback:^(id message) {
144                                              int indexCopy;
145                                              @synchronized(self) {
146                                                indexCopy = index;
147                                                index += 1;
148                                              }
149                                              if (indexCopy < numMessages) {
150                                                [call writeMessage:request];
151                                                if (flowControlEnabled) {
152                                                  [call receiveNextMessage];
153                                                }
154                                              } else {
155                                                [call finish];
156                                              }
157                                            }
158                                            closeCallback:^(NSDictionary *trailingMetadata,
159                                                            NSError *error) {
160                                              [expectation fulfill];
161                                            }]
162                            callOptions:options];
163  [call start];
164  if (flowControlEnabled) {
165    [call receiveNextMessage];
166  }
167  [call writeMessage:request];
168  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
169}
170
171- (void)testPingPongRPCWithV2API {
172  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
173  options.transport = [[self class] transport];
174  options.PEMRootCertificates = [[self class] PEMRootCertificates];
175  options.hostNameOverride = [[self class] hostNameOverride];
176
177  id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:@1 requestedResponseSize:@1];
178
179  // warm up
180  [self pingPongV2APIWithRequest:request numMessages:1000 options:options];
181
182  [self measureBlock:^{
183    [self pingPongV2APIWithRequest:request numMessages:1000 options:options];
184  }];
185}
186
187- (void)testPingPongRPCWithFlowControl {
188  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
189  options.transport = [[self class] transport];
190  options.PEMRootCertificates = [[self class] PEMRootCertificates];
191  options.hostNameOverride = [[self class] hostNameOverride];
192  options.flowControlEnabled = YES;
193
194  id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:@1 requestedResponseSize:@1];
195
196  // warm up
197  [self pingPongV2APIWithRequest:request numMessages:1000 options:options];
198
199  [self measureBlock:^{
200    [self pingPongV2APIWithRequest:request numMessages:1000 options:options];
201  }];
202}
203
204- (void)testPingPongRPCWithInterceptor {
205  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
206  options.transport = [[self class] transport];
207  options.PEMRootCertificates = [[self class] PEMRootCertificates];
208  options.hostNameOverride = [[self class] hostNameOverride];
209  options.interceptorFactories = @[ [[DefaultInterceptorFactory alloc] init] ];
210
211  id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:@1 requestedResponseSize:@1];
212
213  // warm up
214  [self pingPongV2APIWithRequest:request numMessages:1000 options:options];
215
216  [self measureBlock:^{
217    [self pingPongV2APIWithRequest:request numMessages:1000 options:options];
218  }];
219}
220
221- (void)pingPongV1APIWithRequest:(RMTStreamingOutputCallRequest *)request
222                     numMessages:(int)numMessages {
223  __block int index = 0;
224  __weak XCTestExpectation *expectation = [self expectationWithDescription:@"PingPong"];
225  GRXBufferedPipe *requestsBuffer = [[GRXBufferedPipe alloc] init];
226
227  [requestsBuffer writeValue:request];
228
229  [_service fullDuplexCallWithRequestsWriter:requestsBuffer
230                                eventHandler:^(BOOL done, RMTStreamingOutputCallResponse *response,
231                                               NSError *error) {
232                                  if (response) {
233                                    int indexCopy;
234                                    @synchronized(self) {
235                                      index += 1;
236                                      indexCopy = index;
237                                    }
238                                    if (indexCopy < numMessages) {
239                                      [requestsBuffer writeValue:request];
240                                    } else {
241                                      [requestsBuffer writesFinishedWithError:nil];
242                                    }
243                                  }
244
245                                  if (done) {
246                                    [expectation fulfill];
247                                  }
248                                }];
249  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
250}
251
252- (void)testPingPongRPCWithV1API {
253  id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:@1 requestedResponseSize:@1];
254  [self pingPongV1APIWithRequest:request numMessages:1000];
255  [self measureBlock:^{
256    [self pingPongV1APIWithRequest:request numMessages:1000];
257  }];
258}
259
260- (void)unaryRPCsWithServices:(NSArray<RMTTestService *> *)services
261                      request:(RMTSimpleRequest *)request
262              callsPerService:(int)callsPerService
263          maxOutstandingCalls:(int)maxOutstandingCalls
264                  callOptions:(GRPCMutableCallOptions *)options {
265  __weak XCTestExpectation *expectation = [self expectationWithDescription:@"unaryRPC"];
266
267  dispatch_semaphore_t sema = dispatch_semaphore_create(maxOutstandingCalls);
268  __block int index = 0;
269
270  for (RMTTestService *service in services) {
271    for (int i = 0; i < callsPerService; ++i) {
272      GRPCUnaryProtoCall *call = [service
273          unaryCallWithMessage:request
274               responseHandler:[[PerfTestsBlockCallbacks alloc]
275                                   initWithInitialMetadataCallback:nil
276                                                   messageCallback:nil
277                                                     closeCallback:^(NSDictionary *trailingMetadata,
278                                                                     NSError *error) {
279                                                       dispatch_semaphore_signal(sema);
280                                                       @synchronized(self) {
281                                                         ++index;
282                                                         if (index ==
283                                                             callsPerService * [services count]) {
284                                                           [expectation fulfill];
285                                                         }
286                                                       }
287                                                     }]
288                   callOptions:options];
289      dispatch_time_t timeout =
290          dispatch_time(DISPATCH_TIME_NOW, (int64_t)(TEST_TIMEOUT * NSEC_PER_SEC));
291      dispatch_semaphore_wait(sema, timeout);
292      [call start];
293    }
294  }
295
296  [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
297}
298
299- (void)test1MBUnaryRPC {
300  // Workaround Apple CFStream bug
301  if ([self isUsingCFStream]) {
302    return;
303  }
304
305  RMTSimpleRequest *request = [RMTSimpleRequest message];
306  request.responseSize = 1048576;
307  request.payload.body = [NSMutableData dataWithLength:1048576];
308
309  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
310  options.transport = [[self class] transport];
311  options.PEMRootCertificates = [[self class] PEMRootCertificates];
312  options.hostNameOverride = [[self class] hostNameOverride];
313
314  // warm up
315  [self unaryRPCsWithServices:@[ self->_service ]
316                      request:request
317              callsPerService:50
318          maxOutstandingCalls:10
319                  callOptions:options];
320
321  [self measureBlock:^{
322    [self unaryRPCsWithServices:@[ self->_service ]
323                        request:request
324                callsPerService:50
325            maxOutstandingCalls:10
326                    callOptions:options];
327  }];
328}
329
330- (void)test1KBUnaryRPC {
331  RMTSimpleRequest *request = [RMTSimpleRequest message];
332  request.responseSize = 1024;
333  request.payload.body = [NSMutableData dataWithLength:1024];
334
335  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
336  options.transport = [[self class] transport];
337  options.PEMRootCertificates = [[self class] PEMRootCertificates];
338  options.hostNameOverride = [[self class] hostNameOverride];
339
340  // warm up
341  [self unaryRPCsWithServices:@[ self->_service ]
342                      request:request
343              callsPerService:1000
344          maxOutstandingCalls:100
345                  callOptions:options];
346
347  [self measureBlock:^{
348    [self unaryRPCsWithServices:@[ self->_service ]
349                        request:request
350                callsPerService:1000
351            maxOutstandingCalls:100
352                    callOptions:options];
353  }];
354}
355
356- (void)testMultipleChannels {
357  NSString *port = [[[self class] host] componentsSeparatedByString:@":"][1];
358  int kNumAddrs = 10;
359  NSMutableArray<NSString *> *addrs = [NSMutableArray arrayWithCapacity:kNumAddrs];
360  NSMutableArray<RMTTestService *> *services = [NSMutableArray arrayWithCapacity:kNumAddrs];
361  for (int i = 0; i < kNumAddrs; ++i) {
362    addrs[i] = [NSString stringWithFormat:@"127.0.0.%d", (i + 1)];
363    NSString *hostWithPort = [NSString stringWithFormat:@"%@:%@", addrs[i], port];
364    services[i] = [RMTTestService serviceWithHost:hostWithPort];
365  }
366
367  RMTSimpleRequest *request = [RMTSimpleRequest message];
368  request.responseSize = 0;
369  request.payload.body = [NSMutableData dataWithLength:0];
370
371  GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init];
372  options.transport = [[self class] transport];
373  options.PEMRootCertificates = [[self class] PEMRootCertificates];
374  options.hostNameOverride = [[self class] hostNameOverride];
375
376  // warm up
377  [self unaryRPCsWithServices:services
378                      request:request
379              callsPerService:100
380          maxOutstandingCalls:100
381                  callOptions:options];
382
383  [self measureBlock:^{
384    [self unaryRPCsWithServices:services
385                        request:request
386                callsPerService:100
387            maxOutstandingCalls:100
388                    callOptions:options];
389  }];
390}
391
392@end
393