From 96f4ade874aaf804ad5114ea21a734f3519e5ee6 Mon Sep 17 00:00:00 2001 From: John Richardson Date: Tue, 19 Jul 2016 14:23:48 +0100 Subject: [PATCH] Add googlephotos workload --- wlauto/workloads/googlephotos/__init__.py | 89 ++++ .../com.arm.wlauto.uiauto.googlephotos.jar | Bin 0 -> 15747 bytes wlauto/workloads/googlephotos/uiauto/build.sh | 39 ++ .../workloads/googlephotos/uiauto/build.xml | 92 ++++ .../googlephotos/uiauto/project.properties | 14 + .../com/arm/wlauto/uiauto/UiAutomation.java | 486 ++++++++++++++++++ 6 files changed, 720 insertions(+) create mode 100755 wlauto/workloads/googlephotos/__init__.py create mode 100644 wlauto/workloads/googlephotos/com.arm.wlauto.uiauto.googlephotos.jar create mode 100755 wlauto/workloads/googlephotos/uiauto/build.sh create mode 100644 wlauto/workloads/googlephotos/uiauto/build.xml create mode 100644 wlauto/workloads/googlephotos/uiauto/project.properties create mode 100755 wlauto/workloads/googlephotos/uiauto/src/com/arm/wlauto/uiauto/UiAutomation.java diff --git a/wlauto/workloads/googlephotos/__init__.py b/wlauto/workloads/googlephotos/__init__.py new file mode 100755 index 00000000..8dcd1783 --- /dev/null +++ b/wlauto/workloads/googlephotos/__init__.py @@ -0,0 +1,89 @@ +# Copyright 2014-2016 ARM Limited +# +# 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 os + +from wlauto import AndroidUxPerfWorkload, Parameter, File +from wlauto.exceptions import ValidationError +from wlauto.utils.types import list_of_strings +from wlauto.utils.misc import unique + + +class Googlephotos(AndroidUxPerfWorkload): + + name = 'googlephotos' + package = 'com.google.android.apps.photos' + min_apk_version = '1.21.0.123444480' + activity = 'com.google.android.apps.photos.home.HomeActivity' + view = [package + '/com.google.android.apps.consumerphotoeditor.fragments.ConsumerPhotoEditorActivity', + package + '/com.google.android.apps.photos.home.HomeActivity', + package + '/com.google.android.apps.photos.localmedia.ui.LocalPhotosActivity', + package + '/com.google.android.apps.photos.onboarding.AccountPickerActivity', + package + '/com.google.android.apps.photos.onboarding.IntroActivity'] + description = """ + A workload to perform standard productivity tasks with Google Photos. The workload carries out + various tasks, such as browsing images, performing zooms, and post-processing the image. + + Test description: + 1. Four images are copied to the device + 2. The application is started in offline access mode + 3. Gestures are performed to swipe between images and pinch zoom in and out of the selected + image + 4. The colour of a selected image is edited by selecting the colour menu, incrementing the + colour, resetting the colour and decrementing the colour using the seek bar. + 5. A crop test is performed on a selected image. UiAutomator does not allow the selection of + the crop markers so the image is tilted positively, reset and then tilted negatively to get a + similar cropping effect. + 6. A rotate test is performed on a selected image, rotating anticlockwise 90 degrees, 180 + degrees and 270 degrees. + """ + + default_test_images = [ + 'uxperf_1200x1600.png', 'uxperf_1600x1200.jpg', + 'uxperf_2448x3264.png', 'uxperf_3264x2448.jpg', + ] + + parameters = [ + Parameter('test_images', kind=list_of_strings, default=default_test_images, + constraint=lambda x: len(unique(x)) == 4, + description=""" + A list of four JPEG and/or PNG files to be pushed to the device. + Absolute file paths may be used but tilde expansion must be escaped. + """), + ] + + def __init__(self, device, **kwargs): + super(Googlephotos, self).__init__(device, **kwargs) + self.deployable_assets = self.test_images + + def validate(self): + super(Googlephotos, self).validate() + + for image in self.test_images: + if os.path.splitext(image.lower())[1] not in ['.jpg', '.jpeg', '.png']: + raise ValidationError('{} must be a JPEG or PNG file'.format(image)) + + def setup(self, context): + super(Googlephotos, self).setup(context) + self.device.broadcast_media_mounted(self.device.working_directory) + + def teardown(self, context): + super(Googlephotos, self).teardown(context) + self.device.broadcast_media_mounted(self.device.working_directory) + + def finalize(self, context): + super(Googlephotos, self).finalize(context) + self.delete_assets() + self.device.broadcast_media_mounted(self.device.working_directory) diff --git a/wlauto/workloads/googlephotos/com.arm.wlauto.uiauto.googlephotos.jar b/wlauto/workloads/googlephotos/com.arm.wlauto.uiauto.googlephotos.jar new file mode 100644 index 0000000000000000000000000000000000000000..8e54107579e6e26309f23b24cce10eff2bfa88b0 GIT binary patch literal 15747 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 iKmITC|Ei(+UH&gJvbqui;(s2(zi&T$7s5`U( literal 0 HcmV?d00001 diff --git a/wlauto/workloads/googlephotos/uiauto/build.sh b/wlauto/workloads/googlephotos/uiauto/build.sh new file mode 100755 index 00000000..fefef54e --- /dev/null +++ b/wlauto/workloads/googlephotos/uiauto/build.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +# CD into build dir if possible - allows building from any directory +script_path='.' +if `readlink -f $0 &>/dev/null`; then + script_path=`readlink -f $0 2>/dev/null` +fi +script_dir=`dirname $script_path` +cd $script_dir + +# Ensure build.xml exists before starting +if [[ ! -f build.xml ]]; then + echo 'Ant build.xml file not found! Check that you are in the right directory.' + exit 9 +fi + +# Copy base classes from wlauto dist +class_dir=bin/classes/com/arm/wlauto/uiauto +base_classes=`python -c "import os, wlauto; print os.path.join(os.path.dirname(wlauto.__file__), 'common', 'android', '*.class')"` +mkdir -p $class_dir +cp $base_classes $class_dir + +# Build and return appropriate exit code if failed +ant build +exit_code=$? +if [[ $exit_code -ne 0 ]]; then + echo "ERROR: 'ant build' exited with code $exit_code" + exit $exit_code +fi + +# If successful move JAR file to workload folder (overwrite previous) +package=com.arm.wlauto.uiauto.googlephotos.jar +rm -f ../$package +if [[ -f bin/$package ]]; then + cp bin/$package .. +else + echo 'ERROR: UiAutomator JAR could not be found!' + exit 9 +fi diff --git a/wlauto/workloads/googlephotos/uiauto/build.xml b/wlauto/workloads/googlephotos/uiauto/build.xml new file mode 100644 index 00000000..648161d0 --- /dev/null +++ b/wlauto/workloads/googlephotos/uiauto/build.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wlauto/workloads/googlephotos/uiauto/project.properties b/wlauto/workloads/googlephotos/uiauto/project.properties new file mode 100644 index 00000000..ce39f2d0 --- /dev/null +++ b/wlauto/workloads/googlephotos/uiauto/project.properties @@ -0,0 +1,14 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-18 diff --git a/wlauto/workloads/googlephotos/uiauto/src/com/arm/wlauto/uiauto/UiAutomation.java b/wlauto/workloads/googlephotos/uiauto/src/com/arm/wlauto/uiauto/UiAutomation.java new file mode 100755 index 00000000..2e62f2b9 --- /dev/null +++ b/wlauto/workloads/googlephotos/uiauto/src/com/arm/wlauto/uiauto/UiAutomation.java @@ -0,0 +1,486 @@ +/* Copyright 2014-2016 ARM Limited + * + * 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. + */ + +package com.arm.wlauto.uiauto.googlephotos; + +import android.os.Bundle; +import android.graphics.Rect; + +// Import the uiautomator libraries +import com.android.uiautomator.core.UiObject; +import com.android.uiautomator.core.UiObjectNotFoundException; +import com.android.uiautomator.core.UiSelector; +import com.android.uiautomator.core.UiScrollable; + +import com.arm.wlauto.uiauto.UxPerfUiAutomation; + +import java.util.concurrent.TimeUnit; +import java.util.LinkedHashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +public class UiAutomation extends UxPerfUiAutomation { + + public static String TAG = "uxperf_googlephotos"; + + public Bundle parameters; + public String packageName; + public String packageID; + + private int viewTimeoutSecs = 10; + private long viewTimeout = TimeUnit.SECONDS.toMillis(viewTimeoutSecs); + + public void runUiAutomation() throws Exception { + parameters = getParams(); + packageName = parameters.getString("package"); + packageID = packageName + ":id/"; + + pauseForSplashScreen(); + setScreenOrientation(ScreenOrientation.NATURAL); + dismissWelcomeView(); + closePromotionPopUp(); + selectWorkingGallery(); + gesturesTest(); + editPhotoColorTest(); + cropPhotoTest(); + rotatePhotoTest(); + unsetScreenOrientation(); + } + + public void pauseForSplashScreen() { + sleep(5); // Pause while splash screen loads + } + + public void dismissWelcomeView() throws Exception { + + // Click through the first two pages and make sure that we don't sign + // in to our google account. This ensures the same set of photographs + // are placed in the camera directory for each run. + UiObject getStartedButton = + new UiObject(new UiSelector().textContains("Get started") + .className("android.widget.Button")); + + if (getStartedButton.waitForExists(viewTimeout)) { + getStartedButton.click(); + } + + // A network connection is not required for this workload. However, + // when the Google Photos app is invoked from the multiapp workload a + // connection is required for sharing content. Handle the different UI + // pathways when dismissing welcome views here. + UiObject doNotSignInButton = + new UiObject(new UiSelector().resourceId(packageID + "dont_sign_in_button")); + + // Folder containing test images (early check required) + UiObject workingFolder = new UiObject(new UiSelector().text("wa-working")); + + if (doNotSignInButton.exists()) { + doNotSignInButton.click(); + } else { + UiObject welcomeButton = + getUiObjectByResourceId(packageID + "name", + "android.widget.TextView"); + welcomeButton.click(); + + UiObject useWithoutAccount = + getUiObjectByText("Use without an account", "android.widget.TextView"); + useWithoutAccount.clickAndWaitForNewWindow(); + + // On some devices the welcome views don't always appear so check + // for the existence of the wa-working directory before attempting + // to dismiss welcome views promoting app features + if (!workingFolder.exists()) { + sleep(1); + uiDeviceSwipeLeft(10); + sleep(1); + uiDeviceSwipeLeft(10); + sleep(1); + uiDeviceSwipeLeft(10); + sleep(1); + } + } + + UiObject nextButton = + new UiObject(new UiSelector().resourceId(packageID + "next_button") + .className("android.widget.ImageView")); + + if (nextButton.exists()) { + nextButton.clickAndWaitForNewWindow(); + } + } + + private void gesturesTest() throws Exception { + String testTag = "gestures"; + + // Perform a range of swipe tests while browsing photo gallery + LinkedHashMap testParams = new LinkedHashMap(); + testParams.put("swipe_left", new GestureTestParams(GestureType.UIDEVICE_SWIPE, Direction.LEFT, 10)); + testParams.put("pinch_out", new GestureTestParams(GestureType.PINCH, PinchType.OUT, 100, 50)); + testParams.put("pinch_in", new GestureTestParams(GestureType.PINCH, PinchType.IN, 100, 50)); + testParams.put("swipe_right", new GestureTestParams(GestureType.UIDEVICE_SWIPE, Direction.RIGHT, 10)); + + Iterator> it = testParams.entrySet().iterator(); + + // Select first photograph + selectPhoto(1); + + while (it.hasNext()) { + Map.Entry pair = it.next(); + GestureType type = pair.getValue().gestureType; + Direction dir = pair.getValue().gestureDirection; + PinchType pinch = pair.getValue().pinchType; + int steps = pair.getValue().steps; + int percent = pair.getValue().percent; + + UiObject view = new UiObject(new UiSelector().enabled(true)); + + if (!view.waitForExists(viewTimeout)) { + throw new UiObjectNotFoundException("Could not find \"photo view\"."); + } + + String runName = String.format(testTag + "_" + pair.getKey()); + ActionLogger logger = new ActionLogger(runName, parameters); + logger.start(); + + switch (type) { + case UIDEVICE_SWIPE: + uiDeviceSwipe(dir, steps); + break; + case UIOBJECT_SWIPE: + uiObjectSwipe(view, dir, steps); + break; + case PINCH: + uiObjectVertPinch(view, pinch, steps, percent); + break; + default: + break; + } + + logger.stop(); + } + + UiObject navigateUpButton = + getUiObjectByDescription("Navigate Up", "android.widget.ImageButton"); + navigateUpButton.click(); + } + + public enum Position { LEFT, RIGHT, CENTRE }; + + private class SeekBarTestParams { + + private Position seekBarPosition; + private int percent; + private int steps; + + SeekBarTestParams(final Position position, final int steps, final int percent) { + this.seekBarPosition = position; + this.steps = steps; + this.percent = percent; + } + } + + private void editPhotoColorTest() throws Exception { + String testTag = "edit_photo"; + + // Perform a range of swipe tests while browsing photo gallery + LinkedHashMap testParams = new LinkedHashMap(); + testParams.put("increment_color", new SeekBarTestParams(Position.RIGHT, 10, 20)); + testParams.put("reset_color", new SeekBarTestParams(Position.CENTRE, 0, 0)); + testParams.put("decrement_color", new SeekBarTestParams(Position.LEFT, 10, 20)); + + Iterator> it = testParams.entrySet().iterator(); + + // Select second photograph + selectPhoto(2); + UiObject editView = getUiObjectByResourceId(packageID + "edit", + "android.widget.ImageView"); + editView.click(); + + // Manage potential different spelling of UI element + UiObject editColor = new UiObject(new UiSelector().text("Color")); + UiObject editColour = new UiObject(new UiSelector().text("Colour")); + + long timeout = TimeUnit.SECONDS.toMillis(3); + + if (editColor.waitForExists(timeout)) { + editColor.click(); + } else if (editColour.waitForExists(timeout)) { + editColour.click(); + } else { + throw new UiObjectNotFoundException(String.format("Could not find \"%s\" \"%s\"", + "Color/Colour", "android.widget.RadioButton")); + } + + UiObject seekBar = getUiObjectByResourceId(packageID + "cpe_strength_seek_bar", + "android.widget.SeekBar"); + + while (it.hasNext()) { + Map.Entry pair = it.next(); + Position pos = pair.getValue().seekBarPosition; + int steps = pair.getValue().steps; + int percent = pair.getValue().percent; + + String runName = String.format(testTag + "_" + pair.getKey()); + ActionLogger logger = new ActionLogger(runName, parameters); + + sleep(1); // pause for stability before editing the colour + + logger.start(); + seekBarTest(seekBar, pos, steps); + logger.stop(); + } + + closeAndReturn(true); + } + + private void cropPhotoTest() throws Exception { + String testTag = "crop_photo"; + + // To improve travel accuracy perform the slide bar operation slowly + final int steps = 500; + + // Perform a range of swipe tests while browsing photo gallery + LinkedHashMap testParams = new LinkedHashMap(); + testParams.put("tilt_positive", Position.LEFT); + testParams.put("tilt_reset", Position.RIGHT); + testParams.put("tilt_negative", Position.RIGHT); + + Iterator> it = testParams.entrySet().iterator(); + + // Select third photograph + selectPhoto(3); + UiObject editView = getUiObjectByResourceId(packageID + "edit", + "android.widget.ImageView"); + editView.click(); + + UiObject cropTool = getUiObjectByResourceId(packageID + "cpe_crop_tool", + "android.widget.ImageView"); + cropTool.click(); + + UiObject straightenSlider = getUiObjectByResourceId(packageID + "cpe_straighten_slider"); + + while (it.hasNext()) { + Map.Entry pair = it.next(); + Position pos = pair.getValue(); + + String runName = String.format(testTag + "_" + pair.getKey()); + ActionLogger logger = new ActionLogger(runName, parameters); + + logger.start(); + slideBarTest(straightenSlider, pos, steps); + logger.stop(); + } + + closeAndReturn(true); + } + + private void rotatePhotoTest() throws Exception { + String testTag = "rotate_photo"; + + String[] subTests = {"anticlockwise_90", "anticlockwise_180", "anticlockwise_270"}; + + // Select fourth photograph + selectPhoto(4); + UiObject editView = getUiObjectByResourceId(packageID + "edit", + "android.widget.ImageView"); + editView.click(); + + UiObject cropTool = getUiObjectByResourceId(packageID + "cpe_crop_tool"); + cropTool.click(); + + UiObject rotate = getUiObjectByResourceId(packageID + "cpe_rotate_90"); + + for (String subTest : subTests) { + String runName = String.format(testTag + "_" + subTest); + ActionLogger logger = new ActionLogger(runName, parameters); + + logger.start(); + rotate.click(); + logger.stop(); + } + + closeAndReturn(true); + } + + // Helper to slide the seekbar during photo edit. + private void seekBarTest(final UiObject view, final Position pos, final int steps) throws Exception { + final int SWIPE_MARGIN_LIMIT = 5; + Rect rect = view.getVisibleBounds(); + + switch (pos) { + case LEFT: + getUiDevice().click(rect.left + SWIPE_MARGIN_LIMIT, rect.centerY()); + break; + case RIGHT: + getUiDevice().click(rect.right - SWIPE_MARGIN_LIMIT, rect.centerY()); + break; + case CENTRE: + view.click(); + break; + default: + break; + } + } + + // Helper to slide the slidebar during photo edit. + private void slideBarTest(final UiObject view, final Position pos, final int steps) throws Exception { + final int SWIPE_MARGIN_LIMIT = 5; + Rect rect = view.getBounds(); + + switch (pos) { + case LEFT: + getUiDevice().drag(rect.left + SWIPE_MARGIN_LIMIT, rect.centerY(), + rect.left + rect.width() / 4, rect.centerY(), + steps); + break; + case RIGHT: + getUiDevice().drag(rect.right - SWIPE_MARGIN_LIMIT, rect.centerY(), + rect.right - rect.width() / 4, rect.centerY(), + steps); + break; + default: + break; + } + } + + public void closePromotionPopUp() throws Exception { + UiObject promoCloseButton = + new UiObject(new UiSelector().resourceId(packageID + "promo_close_button")); + + if (promoCloseButton.exists()) { + promoCloseButton.click(); + } + } + + // Helper to click on the wa-working gallery. + public void selectWorkingGallery() throws Exception { + UiObject workdir = new UiObject(new UiSelector().text("wa-working") + .className("android.widget.TextView")); + + // If the wa-working gallery is not present wait for a short time for + // the media server to refresh its index. + boolean discovered = workdir.waitForExists(viewTimeout); + UiScrollable scrollView = new UiScrollable(new UiSelector().scrollable(true)); + + if (!discovered && scrollView.exists()) { + // First check if the wa-working directory is visible on the first + // screen and if not scroll to the bottom of the screen to look for it. + discovered = scrollView.scrollIntoView(workdir); + + // If still not discovered scroll back to the top of the screen and + // wait for a longer amount of time for the media server to refresh + // its index. + if (!discovered) { + // scrollView.scrollToBeggining() doesn't work for this + // particular scrollable view so use device method instead + for (int i = 0; i < 10; i++) { + uiDeviceSwipeUp(20); + } + discovered = workdir.waitForExists(TimeUnit.SECONDS.toMillis(60)); + + // Scroll to the bottom of the screen one last time + if (!discovered) { + discovered = scrollView.scrollIntoView(workdir); + } + } + } + + if (discovered) { + workdir.clickAndWaitForNewWindow(); + } else { + throw new UiObjectNotFoundException("Could not find \"wa-working\" folder"); + } + } + + // Helper to click on an individual photograph based on index in wa-working gallery. + public void selectPhoto(final int index) throws Exception { + UiObject photo = + new UiObject(new UiSelector().resourceId(packageID + "recycler_view") + .childSelector(new UiSelector() + .index(index))); + + // On some versions of the app a non-zero index is used for the + // photographs position while on other versions a zero index is used. + // Try both possiblities before throwing an exception. + if (photo.exists()) { + photo.click(); + } else { + photo = new UiObject(new UiSelector().resourceId(packageID + "recycler_view") + .childSelector(new UiSelector() + .index(index - 1))); + photo.click(); + } + } + + // Helper that accepts, closes and navigates back to application home screen after an edit operation. + // dontsave - True will discard the image. False will save the image + public void closeAndReturn(final boolean dontsave) throws Exception { + UiObject accept = new UiObject(new UiSelector().description("Accept")); + UiObject done = new UiObject(new UiSelector().resourceId(packageID + "cpe_save_button")); + + long timeout = TimeUnit.SECONDS.toMillis(3); + + // On some edit operations we can either confirm an edit with "Accept" or "DONE" + if (accept.waitForExists(timeout)) { + accept.click(); + } else if (done.waitForExists(timeout)) { + done.click(); + } else { + throw new UiObjectNotFoundException("Could not find \"Accept\" or \"DONE\" button."); + } + + if (dontsave) { + UiObject close = getUiObjectByDescription("Close editor", "android.widget.ImageView"); + close.click(); + + UiObject discard = getUiObjectByText("DISCARD", "android.widget.Button"); + discard.waitForExists(viewTimeout); + discard.click(); + } else { + UiObject save = getUiObjectByText("SAVE", "android.widget.TextView"); + save.waitForExists(viewTimeout); + save.click(); + } + + UiObject navigateUpButton = + new UiObject(new UiSelector().descriptionContains("Navigate Up") + .className("android.widget.ImageButton")); + navigateUpButton.waitForExists(viewTimeout); + navigateUpButton.click(); + } + + // Helper to tag an individual photograph based on the index in wa-working + // gallery. After long clicking it tags the photograph with a tick in the + // corner of the image to indicate that the photograph has been selected + public void tagPhoto(final int index) throws Exception { + UiObject photo = + new UiObject(new UiSelector().resourceId(packageID + "recycler_view") + .childSelector(new UiSelector() + .index(index))); + + // On some versions of the app a non-zero index is used for the + // photographs position while on other versions a zero index is used. + // Try both possiblities before throwing an exception. + if (photo.exists()) { + uiObjectPerformLongClick(photo, 100); + } else { + photo = new UiObject(new UiSelector().resourceId(packageID + "recycler_view") + .childSelector(new UiSelector() + .index(index - 1))); + uiObjectPerformLongClick(photo, 100); + } + } +}