From 1fd8a14ca9e7d4d7cd176b6a6d5aaa1b8a30f2d5 Mon Sep 17 00:00:00 2001 From: John Richardson Date: Tue, 19 Jul 2016 14:23:48 +0100 Subject: [PATCH] Add googlephotos workload The googlephotos workload demonstrates the use of the ActionLogger for measuring performance metrics when used in conjunction with the appropriate instruments and result processors. --- wlauto/workloads/googlephotos/__init__.py | 131 ++++++ .../com.arm.wlauto.uiauto.googlephotos.jar | Bin 0 -> 15382 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 | 440 ++++++++++++++++++ 6 files changed, 716 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..ce733478 --- /dev/null +++ b/wlauto/workloads/googlephotos/__init__.py @@ -0,0 +1,131 @@ +# 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 +import wlauto.common.resources + +from wlauto import AndroidUiAutoBenchmark, Parameter +from wlauto.exceptions import ValidationError +from wlauto.utils.types import list_of_strings + +__version__ = '0.1.1' + + +class Googlephotos(AndroidUiAutoBenchmark): + + name = 'googlephotos' + package = 'com.google.android.apps.photos' + 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, post-processing and saving a selected + image to file. + + 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 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('dumpsys_enabled', kind=bool, default=True, + description=""" + If ``True`` turns on the action logger which outputs + timestamps to logcat for actions recorded in the workload. + """), + Parameter('test_images', kind=list_of_strings, default=default_test_images, + description=""" + A list of four image files to be pushed to the device. + Absolute file paths may be used but tilde expansion is not supported. + """), + ] + + def validate(self): + super(Googlephotos, self).validate() + self.uiauto_params['package'] = self.package + self.uiauto_params['output_dir'] = self.device.working_directory + self.uiauto_params['dumpsys_enabled'] = self.dumpsys_enabled + + self._check_image_numbers() + self._check_image_extensions() + self._check_image_duplicates() + self._info_image_used() + + def initialize(self, context): + super(Googlephotos, self).initialize(context) + for image in self.test_images: + if os.path.exists(image): + image_path = image + else: + image_path = context.resolver.get(wlauto.common.resources.File(self, image)) + self.device.push_file(image_path, self.device.working_directory, timeout=300) + + # Force a re-index of the mediaserver cache to pick up new files + self.device.execute('am broadcast -a android.intent.action.MEDIA_MOUNTED -d file:///sdcard') + + def teardown(self, context): + super(Googlephotos, self).teardown(context) + self.device.execute('am broadcast -a android.intent.action.MEDIA_MOUNTED -d file:///sdcard') + + def finalize(self, context): + super(Googlephotos, self).finalize(context) + + for entry in self.device.listdir(self.device.working_directory): + if entry in self.test_images: + self.device.delete_file(self.device.path.join(self.device.working_directory, entry)) + + self.device.execute('am broadcast -a android.intent.action.MEDIA_MOUNTED -d file:///sdcard') + + # ------------------------------------------------------------------------- + # Internal methods + # ------------------------------------------------------------------------- + + def _check_image_extensions(self): + for image in self.test_images: + if not image.endswith(('jpg', 'jpeg', 'png')): + raise ValidationError('{} must be a jpeg or png file'.format(image)) + + def _check_image_numbers(self): + if len(self.test_images) != 4: + message = "This workload requires four test images - only {} specified" + raise ValidationError(message.format(len(self.test_images))) + + def _check_image_duplicates(self): + if len(self.test_images) != len(set(self.test_images)): + raise ValidationError('Duplicate image names not allowed') + + def _info_image_used(self): + if set(self.test_images) & set(self.default_test_images): + self.logger.info('Using default test images') + else: + self.logger.warning('Using custom test images') 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..acb01ecec29b62ed4bf8eefcb133cdd4e0e3d7ad GIT binary patch literal 15382 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 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..3189e8a1 --- /dev/null +++ b/wlauto/workloads/googlephotos/uiauto/src/com/arm/wlauto/uiauto/UiAutomation.java @@ -0,0 +1,440 @@ +/* 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; + private int viewTimeoutSecs = 10; + private long viewTimeout = TimeUnit.SECONDS.toMillis(viewTimeoutSecs); + + public void runUiAutomation() throws Exception { + parameters = getParams(); + + 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")); + + waitObject(getStartedButton, viewTimeoutSecs); + 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("com.google.android.apps.photos:id/dont_sign_in_button")); + + if (doNotSignInButton.exists()) { + doNotSignInButton.click(); + } else { + UiObject welcomeButton = + getUiObjectByResourceId("com.google.android.apps.photos:id/name", + "android.widget.TextView"); + welcomeButton.click(); + + UiObject useWithoutAccount = + getUiObjectByText("Use without an account", "android.widget.TextView"); + useWithoutAccount.clickAndWaitForNewWindow(); + + // Dismiss welcome views promoting app features + sleep(1); + uiDeviceSwipeLeft(10); + sleep(1); + uiDeviceSwipeLeft(10); + sleep(1); + uiDeviceSwipeLeft(10); + sleep(1); + } + + UiObject nextButton = + new UiObject(new UiSelector().resourceId("com.google.android.apps.photos:id/next_button") + .className("android.widget.ImageView")); + + if (nextButton.exists()) { + nextButton.clickAndWaitForNewWindow(); + } + + UiObject workingFolder = new UiObject(new UiSelector().text("wa-working")); + waitObject(workingFolder, viewTimeoutSecs); + } + + 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("com.google.android.apps.photos:id/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")); + + if (editColor.exists()) { + editColor.click(); + } else if (editColour.exists()) { + editColour.click(); + } else { + throw new UiObjectNotFoundException(String.format("Could not find \"%s\" \"%s\"", + "Color/Colour", "android.widget.RadioButton")); + } + + UiObject seekBar = getUiObjectByResourceId("com.google.android.apps.photos:id/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); + + 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("com.google.android.apps.photos:id/edit", + "android.widget.ImageView"); + editView.click(); + + UiObject cropTool = getUiObjectByResourceId("com.google.android.apps.photos:id/cpe_crop_tool", + "android.widget.ImageView"); + cropTool.click(); + + UiObject straightenSlider = getUiObjectByResourceId("com.google.android.apps.photos:id/cpe_straighten_slider", + "android.view.View"); + + 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("com.google.android.apps.photos:id/edit", + "android.widget.ImageView"); + editView.click(); + + UiObject cropTool = getUiObjectByResourceId("com.google.android.apps.photos:id/cpe_crop_tool", + "android.widget.ImageView"); + cropTool.click(); + + UiObject rotate = getUiObjectByResourceId("com.google.android.apps.photos:id/cpe_rotate_90", + "android.widget.ImageView"); + + 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("com.google.android.apps.photos:id/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")); + + UiScrollable scrollView = new UiScrollable(new UiSelector().scrollable(true)); + + while (!workdir.exists()) { + scrollView.scrollForward(); + } + + workdir.clickAndWaitForNewWindow(); + } + + // 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("com.google.android.apps.photos:id/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("com.google.android.apps.photos:id/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 = getUiObjectByDescription("Accept", "android.widget.ImageView"); + accept.click(); + + 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("com.google.android.apps.photos:id/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("com.google.android.apps.photos:id/recycler_view") + .childSelector(new UiSelector() + .index(index - 1))); + uiObjectPerformLongClick(photo, 100); + } + } +}