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