/* * Copyright (C) 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #import #import #import @interface XCUIApplication () -(instancetype)initPrivateWithPath:(NSString *)path bundleID:(NSString *)bundleID; @end @interface MobileMiniBrowserUITests : XCTestCase @end @implementation MobileMiniBrowserUITests { XCUIApplication *app; NSPredicate *exists; } - (void)setUp { [super setUp]; self.continueAfterFailure = NO; exists = [NSPredicate predicateWithFormat:@"exists == 1"]; app = [[XCUIApplication alloc] init]; [app launch]; } - (void)tearDown { [super tearDown]; } - (void)waitToTapButtonNamed:(NSString *)name forApp:(XCUIApplication *)targetApp { XCUIElement *button = targetApp.buttons[name]; [self expectationForPredicate:exists evaluatedWithObject:button handler:nil]; [self waitForExpectationsWithTimeout:5.0 handler:nil]; [button tap]; } - (void)loadURL:(NSString *)url { XCUIElement *urlField = app.textFields[@"URL Field"]; [self expectationForPredicate:exists evaluatedWithObject:urlField handler:nil]; [self waitForExpectationsWithTimeout:5.0 handler:nil]; [urlField tap]; XCUIElement *clearButton = urlField.buttons[@"Clear text"]; if (clearButton.exists) [clearButton tap]; [urlField typeText:url]; [app.buttons[@"Go To URL"] tap]; } - (void)requireMinFPS:(NSTimeInterval)minFPS sampleDurationSeconds:(NSTimeInterval)sampleSeconds message:(NSString *)message { NSTimeInterval startTime = [NSDate timeIntervalSinceReferenceDate]; uint32_t initialFrameCount = CARenderServerGetFrameCounter(0); usleep(sampleSeconds*1000000); uint32_t finalFrameCount = CARenderServerGetFrameCounter(0); NSTimeInterval endTime = [NSDate timeIntervalSinceReferenceDate]; NSTimeInterval framesPerSecond = (finalFrameCount - initialFrameCount) / (endTime - startTime); XCTAssert(framesPerSecond > 30, @"framesPerSecond < %f: %@", framesPerSecond, message); } - (void)ensureFullscreenControls { if (!app.buttons[@"Done"].exists) { XCUIElement* window = [app.windows allElementsBoundByIndex][0]; XCUICoordinate* center = [window coordinateWithNormalizedOffset:CGVectorMake(0.5, 0.5)]; [center tap]; } } - (NSTimeInterval)timeForTimeString:(NSString *)time { NSArray *components = [time componentsSeparatedByString:@":"]; if (components.count == 3) return [components[0] doubleValue]*60*60 + [components[1] doubleValue]*60 + [components[2] doubleValue]; return [components[0] doubleValue]*60 + [components[1] doubleValue]; } - (void)testBasicVideoPlayback { [self loadURL:@"bundle:/looping.html"]; [self waitToTapButtonNamed:@"Start Playback" forApp:app]; UIUserInterfaceIdiom idiom = UI_USER_INTERFACE_IDIOM(); if (idiom == UIUserInterfaceIdiomPhone) { [self waitToTapButtonNamed:@"PauseButton" forApp:app]; [self waitToTapButtonNamed:@"Done" forApp:app]; } else if (idiom == UIUserInterfaceIdiomPad) [self waitToTapButtonNamed:@"Pause" forApp:app]; } - (void)testBasicVideoFullscreen { [self loadURL:@"bundle:/looping.html"]; [self waitToTapButtonNamed:@"Start Playback" forApp:app]; UIUserInterfaceIdiom idiom = UI_USER_INTERFACE_IDIOM(); if (idiom == UIUserInterfaceIdiomPad) [self waitToTapButtonNamed:@"Display Full Screen" forApp:app]; [self waitToTapButtonNamed:@"PauseButton" forApp:app]; [self waitToTapButtonNamed:@"Done" forApp:app]; } - (void)testVideoFullscreenAndRotationAnimation { XCUIDevice *device = [XCUIDevice sharedDevice]; device.orientation = UIDeviceOrientationPortrait; [self loadURL:@"bundle:/looping.html"]; [self waitToTapButtonNamed:@"Start Playback" forApp:app]; UIUserInterfaceIdiom idiom = UI_USER_INTERFACE_IDIOM(); if (idiom == UIUserInterfaceIdiomPad) [self waitToTapButtonNamed:@"Display Full Screen" forApp:app]; [self requireMinFPS:30 sampleDurationSeconds:1 message:@"Framerate during enter fullscreen animation."]; device.orientation = UIDeviceOrientationLandscapeLeft; [self requireMinFPS:30 sampleDurationSeconds:1 message:@"Framerate during rotation animation."]; [self waitToTapButtonNamed:@"Done" forApp:app]; [self requireMinFPS:30 sampleDurationSeconds:1 message:@"Framerate during exit fullscreen animation."]; } - (void)testVideoFullscreenControlCenter { [self loadURL:@"bundle:/looping.html"]; [self waitToTapButtonNamed:@"Start Playback" forApp:app]; UIUserInterfaceIdiom idiom = UI_USER_INTERFACE_IDIOM(); if (idiom == UIUserInterfaceIdiomPad) [self waitToTapButtonNamed:@"Display Full Screen" forApp:app]; XCUIElement* window = [app.windows allElementsBoundByIndex][0]; XCUICoordinate* top = [window coordinateWithNormalizedOffset:CGVectorMake(0.5, 0.5)]; XCUICoordinate* bottom = [window coordinateWithNormalizedOffset:CGVectorMake(0.5, 1)]; [bottom pressForDuration:0 thenDragToCoordinate:top]; [bottom pressForDuration:0 thenDragToCoordinate:top]; XCTAssert(app.buttons[@"PlayButton"].exists, @"Control center should have interrupted"); XCUIApplication* springboard = [[XCUIApplication alloc] initPrivateWithPath:nil bundleID:@"com.apple.springboard"]; XCUICoordinate* right = [window coordinateWithNormalizedOffset:CGVectorMake(1, 0.9)]; XCUICoordinate* left = [window coordinateWithNormalizedOffset:CGVectorMake(0, 0.9)]; if (springboard.switches[@"Continue"].exists) [springboard.switches[@"Continue"] tap]; if (springboard.switches[@"Airplane Mode"].exists) [right pressForDuration:0 thenDragToCoordinate:left]; XCTAssert(springboard.buttons[@"Play"].exists, @"Control center should have interrupted"); NSString* appElapsedString = [app.staticTexts elementBoundByIndex:0].label; NSTimeInterval appElapsed = [self timeForTimeString:appElapsedString]; NSString* controlCenterTrackString = springboard.otherElements[@"Track position"].value; NSArray* components = [controlCenterTrackString componentsSeparatedByString:@" of "]; NSTimeInterval controlCenterElapsed = [self timeForTimeString:components[0]]; XCTAssert(appElapsed == round(controlCenterElapsed), "Elapsed times match"); } // rdar://problem/27685077 - (void)testLoopingFullscreenLockup { [self loadURL:@"bundle:/looping2s.html"]; XCUIElement* window = [app.windows allElementsBoundByIndex][0]; XCUICoordinate* gifLoc = [window coordinateWithNormalizedOffset:CGVectorMake(0.5, 0.75)]; [gifLoc pressForDuration:0]; [self waitToTapButtonNamed:@"Start Playback" forApp:app]; [self waitToTapButtonNamed:@"ExitFullScreenButton" forApp:app]; [self waitToTapButtonNamed:@"ExitFullScreenButton" forApp:app]; [self waitToTapButtonNamed:@"ExitFullScreenButton" forApp:app]; [self waitToTapButtonNamed:@"ExitFullScreenButton" forApp:app]; [self waitToTapButtonNamed:@"ExitFullScreenButton" forApp:app]; } @end