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 <XCTest/XCTest.h> 20 21NSTimeInterval const kWaitTime = 30; 22int const kNumIterations = 1; 23 24@interface GrpcIosTestUITests : XCTestCase 25@end 26 27@implementation GrpcIosTestUITests { 28 XCUIApplication *testApp; 29 XCUIApplication *settingsApp; 30} 31 32- (void)setUp { 33 self.continueAfterFailure = NO; 34 [[[XCUIApplication alloc] init] launch]; 35 testApp = [[XCUIApplication alloc] initWithBundleIdentifier:@"io.grpc.GrpcIosTest"]; 36 [testApp activate]; 37 // Reset RPC counter 38 [self pressButton:@"Reset counter"]; 39 40 settingsApp = [[XCUIApplication alloc] initWithBundleIdentifier:@"com.apple.Preferences"]; 41 [settingsApp activate]; 42 [NSThread sleepForTimeInterval:1]; 43 // Go back to the first page of Settings. 44 XCUIElement *backButton = settingsApp.navigationBars.buttons.firstMatch; 45 while (backButton.exists && backButton.isHittable) { 46 NSLog(@"Tapping back button"); 47 [backButton tap]; 48 } 49 XCTAssert([settingsApp.navigationBars[@"Settings"] waitForExistenceWithTimeout:kWaitTime]); 50 NSLog(@"Turning off airplane mode"); 51 // Turn off airplane mode 52 [self setAirplaneMode:NO]; 53 54 // Turn on wifi 55 NSLog(@"Turning on wifi"); 56 [self setWifi:YES]; 57} 58 59- (void)tearDown { 60} 61 62- (void)doUnaryCall { 63 [testApp activate]; 64 [self pressButton:@"Unary call"]; 65} 66 67- (void)do10UnaryCalls { 68 [testApp activate]; 69 [self pressButton:@"10 Unary calls"]; 70} 71 72- (void)pressButton:(NSString *)name { 73 // Wait for button to be visible 74 while (![testApp.buttons[name] exists] || ![testApp.buttons[name] isHittable]) { 75 [NSThread sleepForTimeInterval:1]; 76 } 77 // Wait until all events in run loop have been processed 78 while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, true) == kCFRunLoopRunHandledSource); 79 80 NSLog(@"Pressing button: %@", name); 81 [testApp.buttons[name] tap]; 82} 83 84- (void)expectCallSuccess { 85 XCTAssert([testApp.staticTexts[@"Call done"] waitForExistenceWithTimeout:kWaitTime]); 86} 87 88- (void)expectCallFailed { 89 XCTAssert([testApp.staticTexts[@"Call failed"] waitForExistenceWithTimeout:kWaitTime]); 90} 91 92- (void)expectCallSuccessOrFailed { 93 NSDate *startTime = [NSDate date]; 94 while (![testApp.staticTexts[@"Call done"] exists] && 95 ![testApp.staticTexts[@"Call failed"] exists]) { 96 XCTAssertLessThan([[NSDate date] timeIntervalSinceDate:startTime], kWaitTime); 97 [NSThread sleepForTimeInterval:1]; 98 } 99} 100 101- (void)setAirplaneMode:(BOOL)to { 102 [settingsApp activate]; 103 XCUIElement *mySwitch = settingsApp.tables.element.cells.switches[@"Airplane Mode"]; 104 BOOL from = [(NSString *)mySwitch.value boolValue]; 105 NSLog(@"Setting airplane from: %d to: %d", from, to); 106 if (from != to) { 107 [mySwitch tap]; 108 // wait for network change to finish 109 [NSThread sleepForTimeInterval:5]; 110 } 111 XCTAssert([(NSString *)mySwitch.value boolValue] == to); 112} 113- (void)setWifi:(BOOL)to { 114 [settingsApp activate]; 115 [settingsApp.tables.element.cells.staticTexts[@"Wi-Fi"] tap]; 116 XCUIElement *wifiSwitch = settingsApp.tables.cells.switches[@"Wi-Fi"]; 117 BOOL from = [(NSString *)wifiSwitch.value boolValue]; 118 NSLog(@"Setting wifi from: %d to: %d", from, to); 119 if (from != to) { 120 [wifiSwitch tap]; 121 // wait for wifi networks to be detected 122 [NSThread sleepForTimeInterval:10]; 123 } 124 // Go back to the first page of Settings. 125 XCUIElement *backButton = settingsApp.navigationBars.buttons.firstMatch; 126 [backButton tap]; 127} 128 129- (int)getRandomNumberBetween:(int)min max:(int)max { 130 return min + arc4random_uniform((max - min + 1)); 131} 132 133- (void)testBackgroundBeforeCall { 134 NSLog(@"%s", __func__); 135 // Open test app 136 [testApp activate]; 137 // Send test app to background 138 [XCUIDevice.sharedDevice pressButton:XCUIDeviceButtonHome]; 139 140 // Wait a bit 141 int sleepTime = [self getRandomNumberBetween:5 max:10]; 142 NSLog(@"Sleeping for %d seconds", sleepTime); 143 [NSThread sleepForTimeInterval:sleepTime]; 144 145 // Bring test app to foreground and make a unary call. Call should succeed 146 [self doUnaryCall]; 147 [self expectCallSuccess]; 148} 149 150- (void)testBackgroundDuringStreamingCall { 151 NSLog(@"%s", __func__); 152 // Open test app and start a streaming call 153 [testApp activate]; 154 [self pressButton:@"Start streaming call"]; 155 156 // Send test app to background 157 [XCUIDevice.sharedDevice pressButton:XCUIDeviceButtonHome]; 158 159 // Wait a bit 160 int sleepTime = [self getRandomNumberBetween:5 max:10]; 161 NSLog(@"Sleeping for %d seconds", sleepTime); 162 [NSThread sleepForTimeInterval:sleepTime]; 163 164 // Bring test app to foreground and make a streaming call. Call should succeed. 165 [testApp activate]; 166 [self pressButton:@"Send Message"]; 167 [self pressButton:@"Stop streaming call"]; 168 [self expectCallSuccess]; 169} 170 171- (void)testCallAfterNetworkFlap { 172 NSLog(@"%s", __func__); 173 // Open test app and make a unary call. Channel to server should be open after this. 174 [self doUnaryCall]; 175 [self expectCallSuccess]; 176 177 // Toggle airplane mode on and off 178 [self setAirplaneMode:YES]; 179 [self setAirplaneMode:NO]; 180 181 // Bring test app to foreground and make a unary call. The call should succeed 182 [self doUnaryCall]; 183 [self expectCallSuccess]; 184} 185 186- (void)testCallWhileNetworkDown { 187 NSLog(@"%s", __func__); 188 // Open test app and make a unary call. Channel to server should be open after this. 189 [self doUnaryCall]; 190 [self expectCallSuccess]; 191 192 // Turn on airplane mode 193 [self setAirplaneMode:YES]; 194 // Turn off wifi 195 [self setWifi:NO]; 196 197 // Unary call should fail 198 [self doUnaryCall]; 199 [self expectCallFailed]; 200 201 // Turn off airplane mode 202 [self setAirplaneMode:NO]; 203 // Turn on wifi 204 [self setWifi:YES]; 205 206 // Unary call should succeed 207 [self doUnaryCall]; 208 [self expectCallSuccess]; 209} 210 211- (void)testSwitchApp { 212 NSLog(@"%s", __func__); 213 // Open test app and make a unary call. Channel to server should be open after this. 214 [self doUnaryCall]; 215 [self expectCallSuccess]; 216 217 // Send test app to background 218 [XCUIDevice.sharedDevice pressButton:XCUIDeviceButtonHome]; 219 220 // Open stocks app 221 XCUIApplication *stocksApp = 222 [[XCUIApplication alloc] initWithBundleIdentifier:@"com.apple.stocks"]; 223 [stocksApp activate]; 224 // Ensure that stocks app is running in the foreground 225 XCTAssert([stocksApp waitForState:XCUIApplicationStateRunningForeground timeout:5]); 226 // Wait a bit 227 int sleepTime = [self getRandomNumberBetween:5 max:10]; 228 NSLog(@"Sleeping for %d seconds", sleepTime); 229 [NSThread sleepForTimeInterval:sleepTime]; 230 [stocksApp terminate]; 231 232 // Make another unary call 233 [self doUnaryCall]; 234 [self expectCallSuccess]; 235} 236 237- (void)testNetworkFlapDuringStreamingCall { 238 NSLog(@"%s", __func__); 239 // Open test app and make a unary call. Channel to server should be open after this. 240 [self doUnaryCall]; 241 [self expectCallSuccess]; 242 // Start streaming call and send a message 243 [self pressButton:@"Start streaming call"]; 244 [self pressButton:@"Send Message"]; 245 246 // Toggle network on and off 247 [self setAirplaneMode:YES]; 248 [self setWifi:NO]; 249 [self setAirplaneMode:NO]; 250 [self setWifi:YES]; 251 252 [testApp activate]; 253 [self pressButton:@"Stop streaming call"]; 254 // The call will fail if the stream gets a read error, else the call will succeed. 255 [self expectCallSuccessOrFailed]; 256 257 // Make another unary call, it should succeed 258 [self doUnaryCall]; 259 [self expectCallSuccess]; 260} 261 262- (void)testConcurrentCalls { 263 NSLog(@"%s", __func__); 264 265 // Press button to start 10 unary calls 266 [self do10UnaryCalls]; 267 268 // Toggle airplane mode on and off 269 [self setAirplaneMode:YES]; 270 [self setAirplaneMode:NO]; 271 272 // 10 calls should have completed 273 [testApp activate]; 274 XCTAssert([testApp.staticTexts[@"Calls completed: 10"] waitForExistenceWithTimeout:kWaitTime]); 275} 276 277- (void)invokeTest { 278 for (int i = 0; i < kNumIterations; i++) { 279 [super invokeTest]; 280 } 281} 282 283- (void)testUnaryCallTurnOffWifi { 284 NSLog(@"%s", __func__); 285 // Open test app and make a unary call. Channel to server should be open after this. 286 [self doUnaryCall]; 287 [self expectCallSuccess]; 288 289 // Turn off wifi 290 [self setWifi:NO]; 291 292 // Phone should switch to cellular connection, call should succeed 293 [self doUnaryCall]; 294 [self expectCallSuccess]; 295 296 // Turn on wifi 297 [self setWifi:YES]; 298 299 // Call should succeed after turning wifi back on 300 [self doUnaryCall]; 301 [self expectCallSuccess]; 302} 303 304- (void)testStreamingCallTurnOffWifi { 305 NSLog(@"%s", __func__); 306 // Open test app and make a unary call. Channel to server should be open after this. 307 [self doUnaryCall]; 308 [self expectCallSuccess]; 309 310 // Start streaming call and send a message 311 [self pressButton:@"Start streaming call"]; 312 [self pressButton:@"Send Message"]; 313 314 // Turn off wifi 315 [self setWifi:NO]; 316 317 // Phone should switch to cellular connection, this results in the call failing 318 [testApp activate]; 319 [self pressButton:@"Stop streaming call"]; 320 [self expectCallFailed]; 321 322 // Turn on wifi 323 [self setWifi:YES]; 324 325 // Call should succeed after turning wifi back on 326 [self doUnaryCall]; 327 [self expectCallSuccess]; 328} 329 330@end 331