Added automated test for network transitions on iOS devices

pull/18268/head
Prashant Jaikumar 6 years ago
parent 6dcf6d1645
commit 57759a3525
  1. 135
      src/objective-c/manual_tests/GrpcIosTest.xcodeproj/project.pbxproj
  2. 174
      src/objective-c/manual_tests/GrpcIosTestUITests/GrpcIosTestUITests.m
  3. 22
      src/objective-c/manual_tests/GrpcIosTestUITests/Info.plist
  4. 1
      src/objective-c/manual_tests/Main.storyboard
  5. 4
      src/objective-c/manual_tests/Podfile
  6. 22
      src/objective-c/manual_tests/ViewController.m
  7. 2
      src/objective-c/manual_tests/main.m

@ -12,8 +12,19 @@
5EDA909C220DF1B00046D27A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EDA9096220DF1B00046D27A /* main.m */; };
5EDA909E220DF1B00046D27A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5EDA9098220DF1B00046D27A /* Main.storyboard */; };
5EDA909F220DF1B00046D27A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EDA9099220DF1B00046D27A /* AppDelegate.m */; };
B0C18CA7222DEF140002B502 /* GrpcIosTestUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = B0C18CA6222DEF140002B502 /* GrpcIosTestUITests.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
B0C18CA9222DEF140002B502 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 5EDA9073220DF0BC0046D27A /* Project object */;
proxyType = 1;
remoteGlobalIDString = 5EDA907A220DF0BC0046D27A;
remoteInfo = GrpcIosTest;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
1D22EC48A487B02F76135EA3 /* libPods-GrpcIosTest.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-GrpcIosTest.a"; sourceTree = BUILT_PRODUCTS_DIR; };
5EDA907B220DF0BC0046D27A /* GrpcIosTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GrpcIosTest.app; sourceTree = BUILT_PRODUCTS_DIR; };
@ -24,6 +35,9 @@
5EDA9099220DF1B00046D27A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = SOURCE_ROOT; };
7C9FAFB11727DCA50888C1B8 /* Pods-GrpcIosTest.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GrpcIosTest.debug.xcconfig"; path = "Pods/Target Support Files/Pods-GrpcIosTest/Pods-GrpcIosTest.debug.xcconfig"; sourceTree = "<group>"; };
A4E7CA72304A7B43FE8A5BC7 /* Pods-GrpcIosTest.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GrpcIosTest.release.xcconfig"; path = "Pods/Target Support Files/Pods-GrpcIosTest/Pods-GrpcIosTest.release.xcconfig"; sourceTree = "<group>"; };
B0C18CA4222DEF140002B502 /* GrpcIosTestUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GrpcIosTestUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
B0C18CA6222DEF140002B502 /* GrpcIosTestUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GrpcIosTestUITests.m; sourceTree = "<group>"; };
B0C18CA8222DEF140002B502 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -35,6 +49,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
B0C18CA1222DEF140002B502 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@ -55,6 +76,7 @@
5EDA9096220DF1B00046D27A /* main.m */,
5EDA9098220DF1B00046D27A /* Main.storyboard */,
5EDA9094220DF1B00046D27A /* ViewController.m */,
B0C18CA5222DEF140002B502 /* GrpcIosTestUITests */,
5EDA907C220DF0BC0046D27A /* Products */,
2B8131AC634883AFEC02557C /* Pods */,
E73D92116C1C328622A8C77F /* Frameworks */,
@ -65,10 +87,20 @@
isa = PBXGroup;
children = (
5EDA907B220DF0BC0046D27A /* GrpcIosTest.app */,
B0C18CA4222DEF140002B502 /* GrpcIosTestUITests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
B0C18CA5222DEF140002B502 /* GrpcIosTestUITests */ = {
isa = PBXGroup;
children = (
B0C18CA6222DEF140002B502 /* GrpcIosTestUITests.m */,
B0C18CA8222DEF140002B502 /* Info.plist */,
);
path = GrpcIosTestUITests;
sourceTree = "<group>";
};
E73D92116C1C328622A8C77F /* Frameworks */ = {
isa = PBXGroup;
children = (
@ -99,6 +131,24 @@
productReference = 5EDA907B220DF0BC0046D27A /* GrpcIosTest.app */;
productType = "com.apple.product-type.application";
};
B0C18CA3222DEF140002B502 /* GrpcIosTestUITests */ = {
isa = PBXNativeTarget;
buildConfigurationList = B0C18CAD222DEF140002B502 /* Build configuration list for PBXNativeTarget "GrpcIosTestUITests" */;
buildPhases = (
B0C18CA0222DEF140002B502 /* Sources */,
B0C18CA1222DEF140002B502 /* Frameworks */,
B0C18CA2222DEF140002B502 /* Resources */,
);
buildRules = (
);
dependencies = (
B0C18CAA222DEF140002B502 /* PBXTargetDependency */,
);
name = GrpcIosTestUITests;
productName = GrpcIosTestUITests;
productReference = B0C18CA4222DEF140002B502 /* GrpcIosTestUITests.xctest */;
productType = "com.apple.product-type.bundle.ui-testing";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@ -111,6 +161,10 @@
5EDA907A220DF0BC0046D27A = {
CreatedOnToolsVersion = 10.0;
};
B0C18CA3222DEF140002B502 = {
CreatedOnToolsVersion = 10.0;
TestTargetID = 5EDA907A220DF0BC0046D27A;
};
};
};
buildConfigurationList = 5EDA9076220DF0BC0046D27A /* Build configuration list for PBXProject "GrpcIosTest" */;
@ -127,6 +181,7 @@
projectRoot = "";
targets = (
5EDA907A220DF0BC0046D27A /* GrpcIosTest */,
B0C18CA3222DEF140002B502 /* GrpcIosTestUITests */,
);
};
/* End PBXProject section */
@ -140,6 +195,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
B0C18CA2222DEF140002B502 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
@ -200,8 +262,24 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
B0C18CA0222DEF140002B502 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B0C18CA7222DEF140002B502 /* GrpcIosTestUITests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
B0C18CAA222DEF140002B502 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 5EDA907A220DF0BC0046D27A /* GrpcIosTest */;
targetProxy = B0C18CA9222DEF140002B502 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
5EDA908F220DF0BD0046D27A /* Debug */ = {
isa = XCBuildConfiguration;
@ -321,7 +399,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Manual;
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = EQHXZ8M8AV;
INFOPLIST_FILE = Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = (
@ -330,7 +408,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = io.grpc.GrpcIosTest;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
PROVISIONING_PROFILE_SPECIFIER = "Google Development";
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
@ -341,7 +419,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Manual;
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = EQHXZ8M8AV;
INFOPLIST_FILE = Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = (
@ -350,11 +428,51 @@
);
PRODUCT_BUNDLE_IDENTIFIER = io.grpc.GrpcIosTest;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
PROVISIONING_PROFILE_SPECIFIER = "Google Development";
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
B0C18CAB222DEF140002B502 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
DEVELOPMENT_TEAM = EQHXZ8M8AV;
INFOPLIST_FILE = GrpcIosTestUITests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.google.GrpcIosTestUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "Google Development";
TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = GrpcIosTest;
};
name = Debug;
};
B0C18CAC222DEF140002B502 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
DEVELOPMENT_TEAM = EQHXZ8M8AV;
INFOPLIST_FILE = GrpcIosTestUITests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.google.GrpcIosTestUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "Google Development";
TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = GrpcIosTest;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@ -376,6 +494,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
B0C18CAD222DEF140002B502 /* Build configuration list for PBXNativeTarget "GrpcIosTestUITests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B0C18CAB222DEF140002B502 /* Debug */,
B0C18CAC222DEF140002B502 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 5EDA9073220DF0BC0046D27A /* Project object */;

@ -0,0 +1,174 @@
/*
*
* Copyright 2019 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#import <XCTest/XCTest.h>
NSTimeInterval const kWaitTime = 30;
@interface GrpcIosTestUITests : XCTestCase
@end
@implementation GrpcIosTestUITests {
XCUIApplication *testApp;
XCUIApplication *settingsApp;
}
- (void)setUp {
self.continueAfterFailure = NO;
[[[XCUIApplication alloc] init] launch];
testApp = [[XCUIApplication alloc] initWithBundleIdentifier:@"io.grpc.GrpcIosTest"];
settingsApp = [[XCUIApplication alloc] initWithBundleIdentifier:@"com.apple.Preferences"];
[settingsApp activate];
// Go back to the first page of Settings.
XCUIElement *backButton = settingsApp.navigationBars.buttons.firstMatch;
while (backButton.exists) {
[backButton tap];
}
XCTAssert([settingsApp.navigationBars[@"Settings"] waitForExistenceWithTimeout:kWaitTime]);
// Turn off airplane mode
[self setAirplaneMode:NO];
}
- (void)tearDown {
}
- (void)doUnaryCall {
[testApp activate];
[testApp.buttons[@"Unary call"] tap];
}
- (void)doStreamingCall {
[testApp activate];
[testApp.buttons[@"Start streaming call"] tap];
[testApp.buttons[@"Send Message"] tap];
[testApp.buttons[@"Stop streaming call"] tap];
}
- (void)expectCallSuccess {
XCTAssert([testApp.staticTexts[@"Call done"] waitForExistenceWithTimeout:kWaitTime]);
}
- (void)expectCallFailed {
XCTAssert([testApp.staticTexts[@"Call failed"] waitForExistenceWithTimeout:kWaitTime]);
}
- (void)setAirplaneMode:(BOOL)to {
[settingsApp activate];
XCUIElement *mySwitch = settingsApp.tables.element.cells.switches[@"Airplane Mode"];
BOOL from = [(NSString *)mySwitch.value boolValue];
if (from != to) {
[mySwitch tap];
// wait for gRPC to detect the change
sleep(10);
}
XCTAssert([(NSString *)mySwitch.value boolValue] == to);
}
- (void)testBackgroundBeforeUnaryCall {
// Open test app
[testApp activate];
// Send test app to background
[XCUIDevice.sharedDevice pressButton:XCUIDeviceButtonHome];
sleep(5);
// Bring test app to foreground and make a unary call. Call should succeed
[self doUnaryCall];
[self expectCallSuccess];
}
- (void)testBackgroundBeforeStreamingCall {
// Open test app
[testApp activate];
// Send test app to background
[XCUIDevice.sharedDevice pressButton:XCUIDeviceButtonHome];
sleep(5);
// Bring test app to foreground and make a streaming call. Call should succeed.
[self doStreamingCall];
[self expectCallSuccess];
}
- (void)testUnaryCallAfterNetworkFlap {
// Open test app and make a unary call. Channel to server should be open after this.
[self doUnaryCall];
[self expectCallSuccess];
// Toggle airplane mode on and off
[self setAirplaneMode:YES];
[self setAirplaneMode:NO];
// Bring test app to foreground and make a unary call. The call should succeed
[self doUnaryCall];
[self expectCallSuccess];
}
- (void)testStreamingCallAfterNetworkFlap {
// Open test app and make a unary call. Channel to server should be open after this.
[self doUnaryCall];
[self expectCallSuccess];
// Toggle airplane mode on and off
[self setAirplaneMode:YES];
[self setAirplaneMode:NO];
[self doStreamingCall];
[self expectCallSuccess];
}
- (void)testUnaryCallWhileNetworkDown {
// Open test app and make a unary call. Channel to server should be open after this.
[self doUnaryCall];
[self expectCallSuccess];
// Turn on airplane mode
[self setAirplaneMode:YES];
// Unary call should fail
[self doUnaryCall];
[self expectCallFailed];
// Turn off airplane mode
[self setAirplaneMode:NO];
// Unary call should succeed
[self doUnaryCall];
[self expectCallSuccess];
}
- (void)testStreamingCallWhileNetworkDown {
// Open test app and make a unary call. Channel to server should be open after this.
[self doUnaryCall];
[self expectCallSuccess];
// Turn on airplane mode
[self setAirplaneMode:YES];
// Streaming call should fail
[self doStreamingCall];
[self expectCallFailed];
// Turn off airplane mode
[self setAirplaneMode:NO];
// Unary call should succeed
[self doStreamingCall];
[self expectCallSuccess];
}
@end

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

@ -4,6 +4,7 @@
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14283.14"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>

@ -18,8 +18,8 @@ GrpcIosTest
pod 'BoringSSL-GRPC', :podspec => "#{GRPC_LOCAL_SRC}/src/objective-c", :inhibit_warnings => true
pod 'gRPC', :path => GRPC_LOCAL_SRC
pod 'gRPC-Core', :path => GRPC_LOCAL_SRC
pod 'gRPC/CFStream', :path => GRPC_LOCAL_SRC
pod 'gRPC-Core/CFStream-Implementation', :path => GRPC_LOCAL_SRC
pod 'gRPC-RxLibrary', :path => GRPC_LOCAL_SRC
pod 'gRPC-ProtoRPC', :path => GRPC_LOCAL_SRC, :inhibit_warnings => true
pod 'RemoteTest', :path => "../tests/RemoteTestClient", :inhibit_warnings => true

@ -27,7 +27,7 @@ NSString *const kRemoteHost = @"grpc-test.sandbox.googleapis.com";
const int32_t kMessageSize = 100;
@interface ViewController : UIViewController<GRPCProtoResponseHandler>
@property(strong, nonatomic) UILabel *fromLabel;
@end
@implementation ViewController {
@ -35,16 +35,25 @@ const int32_t kMessageSize = 100;
dispatch_queue_t _dispatchQueue;
GRPCStreamingProtoCall *_call;
}
- (instancetype)init {
self = [super init];
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
_dispatchQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
_fromLabel = [[UILabel alloc] initWithFrame:CGRectMake(100, 500, 200, 20)];
_fromLabel.textColor = [UIColor blueColor];
_fromLabel.backgroundColor = [UIColor whiteColor];
[self.view addSubview:_fromLabel];
}
- (IBAction)tapUnaryCall:(id)sender {
if (_service == nil) {
_service = [RMTTestService serviceWithHost:kRemoteHost];
}
self->_fromLabel.text = @"";
// Set up request proto message
RMTSimpleRequest *request = [RMTSimpleRequest message];
@ -61,6 +70,7 @@ const int32_t kMessageSize = 100;
if (_service == nil) {
_service = [RMTTestService serviceWithHost:kRemoteHost];
}
self->_fromLabel.text = @"";
// Set up request proto message
RMTStreamingOutputCallRequest *request = RMTStreamingOutputCallRequest.message;
@ -92,7 +102,6 @@ const int32_t kMessageSize = 100;
if (_call == nil) return;
[_call finish];
_call = nil;
}
@ -107,6 +116,15 @@ const int32_t kMessageSize = 100;
- (void)didCloseWithTrailingMetadata:(NSDictionary *)trailingMetadata
error:(nullable NSError *)error {
NSLog(@"Recv trailing metadata: %@, error: %@", trailingMetadata, error);
if (error == nil) {
dispatch_async(dispatch_get_main_queue(), ^{
self->_fromLabel.text = @"Call done";
});
} else {
dispatch_async(dispatch_get_main_queue(), ^{
self->_fromLabel.text = @"Call failed";
});
}
}
- (dispatch_queue_t)dispatchQueue {

@ -21,6 +21,8 @@
int main(int argc, char* argv[]) {
@autoreleasepool {
// enable CFStream API
setenv("grpc_cfstream", "1", 1);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

Loading…
Cancel
Save