mirror of
https://github.com/ARM-software/workload-automation.git
synced 2025-01-19 04:21:17 +00:00
Merge pull request #216 from jimboatarm/googleplay_uxperf
Add UxPerfUiAutomation and googleplaybooks workload
This commit is contained in:
commit
fe259dca05
BIN
wlauto/common/android/BaseUiAutomation$1.class
Normal file
BIN
wlauto/common/android/BaseUiAutomation$1.class
Normal file
Binary file not shown.
BIN
wlauto/common/android/BaseUiAutomation$ActionLogger.class
Normal file
BIN
wlauto/common/android/BaseUiAutomation$ActionLogger.class
Normal file
Binary file not shown.
BIN
wlauto/common/android/BaseUiAutomation$Direction.class
Normal file
BIN
wlauto/common/android/BaseUiAutomation$Direction.class
Normal file
Binary file not shown.
BIN
wlauto/common/android/BaseUiAutomation$FindByCriteria.class
Normal file
BIN
wlauto/common/android/BaseUiAutomation$FindByCriteria.class
Normal file
Binary file not shown.
BIN
wlauto/common/android/BaseUiAutomation$PinchType.class
Normal file
BIN
wlauto/common/android/BaseUiAutomation$PinchType.class
Normal file
Binary file not shown.
BIN
wlauto/common/android/BaseUiAutomation$ScreenOrientation.class
Normal file
BIN
wlauto/common/android/BaseUiAutomation$ScreenOrientation.class
Normal file
Binary file not shown.
BIN
wlauto/common/android/UxPerfUiAutomation$1.class
Normal file
BIN
wlauto/common/android/UxPerfUiAutomation$1.class
Normal file
Binary file not shown.
BIN
wlauto/common/android/UxPerfUiAutomation$ActionLogger.class
Normal file
BIN
wlauto/common/android/UxPerfUiAutomation$ActionLogger.class
Normal file
Binary file not shown.
BIN
wlauto/common/android/UxPerfUiAutomation$Direction.class
Normal file
BIN
wlauto/common/android/UxPerfUiAutomation$Direction.class
Normal file
Binary file not shown.
BIN
wlauto/common/android/UxPerfUiAutomation$GestureTestParams.class
Normal file
BIN
wlauto/common/android/UxPerfUiAutomation$GestureTestParams.class
Normal file
Binary file not shown.
BIN
wlauto/common/android/UxPerfUiAutomation$GestureType.class
Normal file
BIN
wlauto/common/android/UxPerfUiAutomation$GestureType.class
Normal file
Binary file not shown.
BIN
wlauto/common/android/UxPerfUiAutomation$PinchType.class
Normal file
BIN
wlauto/common/android/UxPerfUiAutomation$PinchType.class
Normal file
Binary file not shown.
BIN
wlauto/common/android/UxPerfUiAutomation$SurfaceLogger.class
Normal file
BIN
wlauto/common/android/UxPerfUiAutomation$SurfaceLogger.class
Normal file
Binary file not shown.
BIN
wlauto/common/android/UxPerfUiAutomation$Timer.class
Normal file
BIN
wlauto/common/android/UxPerfUiAutomation$Timer.class
Normal file
Binary file not shown.
BIN
wlauto/common/android/UxPerfUiAutomation$UxPerfLogger.class
Normal file
BIN
wlauto/common/android/UxPerfUiAutomation$UxPerfLogger.class
Normal file
Binary file not shown.
BIN
wlauto/common/android/UxPerfUiAutomation.class
Normal file
BIN
wlauto/common/android/UxPerfUiAutomation.class
Normal file
Binary file not shown.
@ -24,7 +24,7 @@ from wlauto.core.resource import NO_ONE
|
||||
from wlauto.common.android.resources import ApkFile
|
||||
from wlauto.common.resources import ExtensionAsset, Executable
|
||||
from wlauto.exceptions import WorkloadError, ResourceError, ConfigError, DeviceError
|
||||
from wlauto.utils.android import ApkInfo, ANDROID_NORMAL_PERMISSIONS
|
||||
from wlauto.utils.android import ApkInfo, ANDROID_NORMAL_PERMISSIONS, UNSUPPORTED_PACKAGES
|
||||
from wlauto.utils.types import boolean
|
||||
from wlauto.utils.revent import ReventParser
|
||||
from wlauto import File
|
||||
@ -428,6 +428,11 @@ class AndroidUiAutoBenchmark(UiAutomatorWorkload, AndroidBenchmark):
|
||||
UiAutomatorWorkload.__init__(self, device, **kwargs)
|
||||
AndroidBenchmark.__init__(self, device, _call_super=False, **kwargs)
|
||||
|
||||
def initialize(self, context):
|
||||
UiAutomatorWorkload.initialize(self, context)
|
||||
AndroidBenchmark.initialize(self, context)
|
||||
self._check_unsupported_packages()
|
||||
|
||||
def init_resources(self, context):
|
||||
UiAutomatorWorkload.init_resources(self, context)
|
||||
AndroidBenchmark.init_resources(self, context)
|
||||
@ -444,6 +449,24 @@ class AndroidUiAutoBenchmark(UiAutomatorWorkload, AndroidBenchmark):
|
||||
UiAutomatorWorkload.teardown(self, context)
|
||||
AndroidBenchmark.teardown(self, context)
|
||||
|
||||
def _check_unsupported_packages(self):
|
||||
"""
|
||||
Check for any unsupported package versions and raise an
|
||||
exception if detected.
|
||||
|
||||
"""
|
||||
for package in UNSUPPORTED_PACKAGES:
|
||||
version = self.device.get_installed_package_version(package)
|
||||
if version is None:
|
||||
continue
|
||||
|
||||
if '-' in version:
|
||||
version = version.split('-')[0] # ignore abi version
|
||||
|
||||
if version in UNSUPPORTED_PACKAGES[package]:
|
||||
message = 'This workload does not support version "{}" of package "{}"'
|
||||
raise WorkloadError(message.format(version, package))
|
||||
|
||||
|
||||
class GameWorkload(ApkWorkload, ReventWorkload):
|
||||
"""
|
||||
|
8
wlauto/external/uiauto/build.sh
vendored
8
wlauto/external/uiauto/build.sh
vendored
@ -16,6 +16,12 @@
|
||||
|
||||
|
||||
|
||||
# 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
|
||||
|
||||
cp bin/classes/com/arm/wlauto/uiauto/BaseUiAutomation.class ../../common/android
|
||||
cp bin/classes/com/arm/wlauto/uiauto/*.class ../../common/android
|
||||
|
@ -84,7 +84,7 @@ public class BaseUiAutomation extends UiAutomatorTestCase {
|
||||
|
||||
public ActionLogger(String testTag, Bundle parameters) {
|
||||
this.testTag = testTag;
|
||||
this.enabled = Boolean.parseBoolean(parameters.getString("dumpsys_enabled"));
|
||||
this.enabled = Boolean.parseBoolean(parameters.getString("markers_enabled"));
|
||||
}
|
||||
|
||||
public void start() {
|
||||
|
49
wlauto/external/uiauto/src/com/arm/wlauto/uiauto/UxPerfUiAutomation.java
vendored
Normal file
49
wlauto/external/uiauto/src/com/arm/wlauto/uiauto/UxPerfUiAutomation.java
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
/* Copyright 2013-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;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class UxPerfUiAutomation extends BaseUiAutomation {
|
||||
|
||||
private Logger logger = Logger.getLogger(UxPerfUiAutomation.class.getName());
|
||||
|
||||
public enum GestureType { UIDEVICE_SWIPE, UIOBJECT_SWIPE, PINCH };
|
||||
|
||||
public static class GestureTestParams {
|
||||
public GestureType gestureType;
|
||||
public Direction gestureDirection;
|
||||
public PinchType pinchType;
|
||||
public int percent;
|
||||
public int steps;
|
||||
|
||||
public GestureTestParams(GestureType gesture, Direction direction, int steps) {
|
||||
this.gestureType = gesture;
|
||||
this.gestureDirection = direction;
|
||||
this.pinchType = PinchType.NULL;
|
||||
this.steps = steps;
|
||||
this.percent = 0;
|
||||
}
|
||||
|
||||
public GestureTestParams(GestureType gesture, PinchType pinchType, int steps, int percent) {
|
||||
this.gestureType = gesture;
|
||||
this.gestureDirection = Direction.NULL;
|
||||
this.pinchType = pinchType;
|
||||
this.steps = steps;
|
||||
this.percent = percent;
|
||||
}
|
||||
}
|
||||
}
|
@ -40,7 +40,7 @@ class UxPerfResultProcessor(ResultProcessor):
|
||||
An action represents a series of UI interactions to capture.
|
||||
|
||||
NOTE: The UX_PERF markers are turned off by default and must be enabled in
|
||||
a agenda file by setting dumpsys_enabled for the workload to true.
|
||||
a agenda file by setting ``markers_enabled`` for the workload to ``True``.
|
||||
'''
|
||||
|
||||
parameters = [
|
||||
@ -123,14 +123,7 @@ class UxPerfParser(object):
|
||||
self.logger = logging.getLogger('UxPerfParser')
|
||||
|
||||
# regex for matching logcat message format:
|
||||
# date time PID-TID/package priority/tag: message
|
||||
self.regex = re.compile(r'(?P<date>\d{2}-\d{2})\s+'
|
||||
'(?P<time>\d{2}:\d{2}:\d{2}\.\d{3})\s+'
|
||||
'(?P<PID>\d+)\s+'
|
||||
'(?P<TID>\d+)\s+'
|
||||
'(?P<priority>\w)\s+'
|
||||
'(?P<tag>\w+)\s+:\s+'
|
||||
'(?P<message>.*$)')
|
||||
self.regex = re.compile(r'UX_PERF.*?:\s*(?P<message>.*\d+$)')
|
||||
|
||||
def parse(self, log):
|
||||
'''
|
||||
@ -200,14 +193,13 @@ class UxPerfParser(object):
|
||||
Yields tuple containing action and timestamp.
|
||||
'''
|
||||
for line in lines:
|
||||
match = self.regex.match(line)
|
||||
match = self.regex.search(line)
|
||||
|
||||
if match:
|
||||
if match.group('tag') == 'UX_PERF':
|
||||
message = match.group('message')
|
||||
action_with_suffix, timestamp = message.rsplit(' ', 1)
|
||||
action, _ = action_with_suffix.rsplit('_', 1)
|
||||
yield action, timestamp
|
||||
message = match.group('message')
|
||||
action_with_suffix, timestamp = message.rsplit(' ', 1)
|
||||
action, _ = action_with_suffix.rsplit('_', 1)
|
||||
yield action, timestamp
|
||||
|
||||
def _group_timestamps(self, markers):
|
||||
'''
|
||||
|
@ -100,6 +100,16 @@ ANDROID_NORMAL_PERMISSIONS = [
|
||||
'UNINSTALL_SHORTCUT',
|
||||
]
|
||||
|
||||
# Package versions that are known to have problems with AndroidUiAutoBenchmark workloads.
|
||||
# NOTE: ABI versions are not included.
|
||||
UNSUPPORTED_PACKAGES = {
|
||||
# Google Keyboard:
|
||||
# For some versions of the Google Keyboard package device key presses are
|
||||
# not registered correctly when running UiAutomator Workloads.
|
||||
'com.google.android.inputmethod.latin': ['5.0.25.122319759']
|
||||
}
|
||||
|
||||
|
||||
# TODO: these are set to their actual values near the bottom of the file. There
|
||||
# is some HACKery involved to ensure that ANDROID_HOME does not need to be set
|
||||
# or adb added to path for root when installing as root, and the whole
|
||||
|
94
wlauto/workloads/googleplaybooks/__init__.py
Executable file
94
wlauto/workloads/googleplaybooks/__init__.py
Executable file
@ -0,0 +1,94 @@
|
||||
# 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 re
|
||||
|
||||
from wlauto import AndroidUiAutoBenchmark, Parameter
|
||||
from wlauto.exceptions import DeviceError
|
||||
|
||||
|
||||
class Googleplaybooks(AndroidUiAutoBenchmark):
|
||||
|
||||
name = 'googleplaybooks'
|
||||
package = 'com.google.android.apps.books'
|
||||
version = '3.8.15' # apk version
|
||||
activity = 'com.google.android.apps.books.app.BooksActivity'
|
||||
view = [package + '/com.google.android.apps.books.app.HomeActivity',
|
||||
package + '/com.android.vending/com.google.android.finsky.activities.MainActivity',
|
||||
package + '/com.google.android.apps.books.app.ReadingActivity',
|
||||
package + '/com.google.android.apps.books.app.TableOfContentsActivityLight']
|
||||
description = """
|
||||
A workload to perform standard productivity tasks with googleplaybooks.
|
||||
This workload performs various tasks, such as searching for a book title
|
||||
online, browsing through a book, adding and removing notes, word searching,
|
||||
and querying information about the book.
|
||||
|
||||
Test description:
|
||||
1. Open Google Play Books application
|
||||
2. Dismisses sync operation (if applicable)
|
||||
3. Searches for a book title
|
||||
4. Adds books to library if not already present
|
||||
5. Opens 'My Library' contents
|
||||
6. Opens selected book
|
||||
7. Gestures are performed to swipe between pages and pinch zoom in and out of a page
|
||||
8. Selects a specified chapter based on page number from the navigation view
|
||||
9. Selects a word in the centre of screen and adds a test note to the page
|
||||
10. Removes the test note from the page (clean up)
|
||||
11. Searches for the number of occurrences of a common word throughout the book
|
||||
12. Switches page styles from 'Day' to 'Night' to 'Sepia' and back to 'Day'
|
||||
13. Uses the 'About this book' facility on the currently selected book
|
||||
|
||||
NOTE: This workload requires a network connection (ideally, wifi) to run
|
||||
and a Google account to be setup on the device.
|
||||
"""
|
||||
|
||||
parameters = [
|
||||
Parameter('search_book_title', kind=str, mandatory=False, default="Hamlet",
|
||||
description="""
|
||||
The book title to search for within Google Play Books archive.
|
||||
The book must either be already in the account's library, or free to purchase.
|
||||
"""),
|
||||
Parameter('select_chapter_page_number', kind=int, mandatory=False, default=22,
|
||||
description="""
|
||||
The Page Number to search for within a selected book's Chapter list.
|
||||
Note: Accepts integers only.
|
||||
"""),
|
||||
Parameter('search_word', kind=str, mandatory=False, default='the',
|
||||
description="""
|
||||
The word to search for within a selected book.
|
||||
Note: Accepts single words only.
|
||||
"""),
|
||||
Parameter('markers_enabled', kind=bool, default=True,
|
||||
description="""
|
||||
If ``True``, UX_PERF action markers will be emitted to logcat during
|
||||
the test run.
|
||||
"""),
|
||||
]
|
||||
|
||||
def validate(self):
|
||||
super(Googleplaybooks, self).validate()
|
||||
self.uiauto_params['package'] = self.package
|
||||
self.uiauto_params['markers_enabled'] = self.markers_enabled
|
||||
self.uiauto_params['book_title'] = self.search_book_title.replace(" ", "_")
|
||||
self.uiauto_params['chapter_page_number'] = self.select_chapter_page_number
|
||||
self.uiauto_params['search_word'] = self.search_word
|
||||
|
||||
def initialize(self, context):
|
||||
super(Googleplaybooks, self).initialize(context)
|
||||
|
||||
# This workload relies on the internet so check that there is a working internet connection
|
||||
if not self.device.is_network_connected():
|
||||
raise DeviceError('Network is not connected for device {}'.format(self.device.name))
|
Binary file not shown.
39
wlauto/workloads/googleplaybooks/uiauto/build.sh
Executable file
39
wlauto/workloads/googleplaybooks/uiauto/build.sh
Executable file
@ -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.googleplaybooks.jar
|
||||
rm -f ../$package
|
||||
if [[ -f bin/$package ]]; then
|
||||
cp bin/$package ..
|
||||
else
|
||||
echo 'ERROR: UiAutomator JAR could not be found!'
|
||||
exit 9
|
||||
fi
|
92
wlauto/workloads/googleplaybooks/uiauto/build.xml
Normal file
92
wlauto/workloads/googleplaybooks/uiauto/build.xml
Normal file
@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project name="com.arm.wlauto.uiauto.googleplaybooks" default="help">
|
||||
|
||||
<!-- The local.properties file is created and updated by the 'android' tool.
|
||||
It contains the path to the SDK. It should *NOT* be checked into
|
||||
Version Control Systems. -->
|
||||
<property file="local.properties" />
|
||||
|
||||
<!-- The ant.properties file can be created by you. It is only edited by the
|
||||
'android' tool to add properties to it.
|
||||
This is the place to change some Ant specific build properties.
|
||||
Here are some properties you may want to change/update:
|
||||
|
||||
source.dir
|
||||
The name of the source directory. Default is 'src'.
|
||||
out.dir
|
||||
The name of the output directory. Default is 'bin'.
|
||||
|
||||
For other overridable properties, look at the beginning of the rules
|
||||
files in the SDK, at tools/ant/build.xml
|
||||
|
||||
Properties related to the SDK location or the project target should
|
||||
be updated using the 'android' tool with the 'update' action.
|
||||
|
||||
This file is an integral part of the build system for your
|
||||
application and should be checked into Version Control Systems.
|
||||
|
||||
-->
|
||||
<property file="ant.properties" />
|
||||
|
||||
<!-- if sdk.dir was not set from one of the property file, then
|
||||
get it from the ANDROID_HOME env var.
|
||||
This must be done before we load project.properties since
|
||||
the proguard config can use sdk.dir -->
|
||||
<property environment="env" />
|
||||
<condition property="sdk.dir" value="${env.ANDROID_HOME}">
|
||||
<isset property="env.ANDROID_HOME" />
|
||||
</condition>
|
||||
|
||||
<!-- The project.properties file is created and updated by the 'android'
|
||||
tool, as well as ADT.
|
||||
|
||||
This contains project specific properties such as project target, and library
|
||||
dependencies. Lower level build properties are stored in ant.properties
|
||||
(or in .classpath for Eclipse projects).
|
||||
|
||||
This file is an integral part of the build system for your
|
||||
application and should be checked into Version Control Systems. -->
|
||||
<loadproperties srcFile="project.properties" />
|
||||
|
||||
<!-- quick check on sdk.dir -->
|
||||
<fail
|
||||
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
|
||||
unless="sdk.dir"
|
||||
/>
|
||||
|
||||
<!--
|
||||
Import per project custom build rules if present at the root of the project.
|
||||
This is the place to put custom intermediary targets such as:
|
||||
-pre-build
|
||||
-pre-compile
|
||||
-post-compile (This is typically used for code obfuscation.
|
||||
Compiled code location: ${out.classes.absolute.dir}
|
||||
If this is not done in place, override ${out.dex.input.absolute.dir})
|
||||
-post-package
|
||||
-post-build
|
||||
-pre-clean
|
||||
-->
|
||||
<import file="custom_rules.xml" optional="true" />
|
||||
|
||||
<!-- Import the actual build file.
|
||||
|
||||
To customize existing targets, there are two options:
|
||||
- Customize only one target:
|
||||
- copy/paste the target into this file, *before* the
|
||||
<import> task.
|
||||
- customize it to your needs.
|
||||
- Customize the whole content of build.xml
|
||||
- copy/paste the content of the rules files (minus the top node)
|
||||
into this file, replacing the <import> task.
|
||||
- customize to your needs.
|
||||
|
||||
***********************
|
||||
****** IMPORTANT ******
|
||||
***********************
|
||||
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
|
||||
in order to avoid having your file be overridden by tools such as "android update project"
|
||||
-->
|
||||
<!-- version-tag: VERSION_TAG -->
|
||||
<import file="${sdk.dir}/tools/ant/uibuild.xml" />
|
||||
|
||||
</project>
|
14
wlauto/workloads/googleplaybooks/uiauto/project.properties
Normal file
14
wlauto/workloads/googleplaybooks/uiauto/project.properties
Normal file
@ -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
|
@ -0,0 +1,546 @@
|
||||
/* 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.googleplaybooks;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
// 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.UiWatcher;
|
||||
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_googleplaybooks";
|
||||
|
||||
public Bundle parameters;
|
||||
private int viewTimeoutSecs = 10;
|
||||
private long viewTimeout = TimeUnit.SECONDS.toMillis(viewTimeoutSecs);
|
||||
|
||||
public void runUiAutomation() throws Exception {
|
||||
this.uiAutoTimeout = TimeUnit.SECONDS.toMillis(8);
|
||||
|
||||
parameters = getParams();
|
||||
|
||||
String bookTitle = parameters.getString("book_title").replace("_", " ");
|
||||
String chapterPageNumber = parameters.getString("chapter_page_number");
|
||||
String searchWord = parameters.getString("search_word");
|
||||
String noteText = "This is a test note";
|
||||
|
||||
setScreenOrientation(ScreenOrientation.NATURAL);
|
||||
clearFirstRunDialogues();
|
||||
dismissSync();
|
||||
|
||||
searchForBook(bookTitle);
|
||||
addToLibrary();
|
||||
openMyLibrary();
|
||||
openBook(bookTitle);
|
||||
|
||||
UiWatcher pageSyncPopUpWatcher = createPopUpWatcher();
|
||||
registerWatcher("pageSyncPopUp", pageSyncPopUpWatcher);
|
||||
runWatchers();
|
||||
|
||||
gesturesTest();
|
||||
selectChapter(chapterPageNumber);
|
||||
addNote(noteText);
|
||||
removeNote();
|
||||
searchForWord(searchWord);
|
||||
switchPageStyles();
|
||||
aboutBook();
|
||||
|
||||
removeWatcher("pageSyncPop");
|
||||
pressBack();
|
||||
unsetScreenOrientation();
|
||||
}
|
||||
|
||||
// Creates a watcher for when a pop up warning appears when pages are out
|
||||
// of sync across multiple devices.
|
||||
private UiWatcher createPopUpWatcher() throws Exception {
|
||||
UiWatcher pageSyncPopUpWatcher = new UiWatcher() {
|
||||
|
||||
@Override
|
||||
public boolean checkForCondition() {
|
||||
UiObject popUpDialogue =
|
||||
new UiObject(new UiSelector().resourceId("android:id/message")
|
||||
.textStartsWith("You're on page"));
|
||||
|
||||
// Don't sync and stay on the current page
|
||||
if (popUpDialogue.exists()) {
|
||||
try {
|
||||
UiObject stayOnPage = new UiObject(new UiSelector()
|
||||
.className("android.widget.Button")
|
||||
.text("Yes"));
|
||||
stayOnPage.click();
|
||||
} catch (UiObjectNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return popUpDialogue.waitUntilGone(viewTimeout);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
return pageSyncPopUpWatcher;
|
||||
}
|
||||
|
||||
private void dismissSync() throws Exception {
|
||||
UiObject keepSyncOff =
|
||||
new UiObject(new UiSelector().textContains("Keep sync off")
|
||||
.className("android.widget.Button"));
|
||||
if (keepSyncOff.exists()) {
|
||||
keepSyncOff.click();
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no sample book in My library we are prompted to choose a
|
||||
// book the first time application is run. Try to skip the screen or
|
||||
// pick a random sample book.
|
||||
private void clearFirstRunDialogues() throws Exception {
|
||||
UiObject startButton =
|
||||
new UiObject(new UiSelector().resourceId("com.google.android.apps.books:id/start_button"));
|
||||
|
||||
// First try and skip the sample book selection
|
||||
if (startButton.exists()) {
|
||||
startButton.click();
|
||||
}
|
||||
|
||||
UiObject endButton =
|
||||
new UiObject(new UiSelector().resourceId("com.google.android.apps.books:id/end_button"));
|
||||
|
||||
// Click next button if it exists
|
||||
if (endButton.exists()) {
|
||||
endButton.click();
|
||||
|
||||
// Select a random sample book to add to My library
|
||||
sleep(1);
|
||||
tapDisplayCentre();
|
||||
sleep(1);
|
||||
|
||||
// Click done button (uses same resource-id)
|
||||
endButton.click();
|
||||
}
|
||||
}
|
||||
|
||||
// Searches for a "free" or "purchased" book title in Google play
|
||||
private void searchForBook(final String bookTitle) throws Exception {
|
||||
UiObject search =
|
||||
new UiObject(new UiSelector().resourceId("com.google.android.apps.books:id/menu_search"));
|
||||
search.click();
|
||||
|
||||
UiObject searchText = new UiObject(new UiSelector().textContains("Search")
|
||||
.className("android.widget.EditText"));
|
||||
searchText.setText(bookTitle);
|
||||
pressEnter();
|
||||
|
||||
UiObject resultList =
|
||||
new UiObject(new UiSelector().resourceId("com.android.vending:id/search_results_list"));
|
||||
|
||||
if (!resultList.waitForExists(viewTimeout)) {
|
||||
throw new UiObjectNotFoundException("Could not find \"search results list view\".");
|
||||
}
|
||||
|
||||
String desc = String.format("Book: " + bookTitle);
|
||||
|
||||
// Create a selector so that we can search for siblings of the desired
|
||||
// book that contains a "free" or "purchased" book identifier
|
||||
UiSelector bookSelector = new UiSelector().description(desc).className("android.widget.TextView");
|
||||
|
||||
UiObject freeLabel =
|
||||
new UiObject(new UiSelector().fromParent(bookSelector)
|
||||
.resourceId("com.android.vending:id/li_label")
|
||||
.description("Free"));
|
||||
|
||||
UiObject purchasedLabel =
|
||||
new UiObject(new UiSelector().fromParent(bookSelector)
|
||||
.resourceId("com.android.vending:id/li_label")
|
||||
.description("Purchased"));
|
||||
|
||||
UiScrollable searchResultsList =
|
||||
new UiScrollable(new UiSelector().resourceId("com.android.vending:id/search_results_list"));
|
||||
|
||||
int maxSwipes = 10;
|
||||
while (!freeLabel.exists() && !purchasedLabel.exists()) {
|
||||
if (maxSwipes <= 0) {
|
||||
throw new UiObjectNotFoundException("Could not find free or purchased book \"" + bookTitle + "\"");
|
||||
} else {
|
||||
searchResultsList.swipeUp(10);
|
||||
maxSwipes--;
|
||||
}
|
||||
}
|
||||
|
||||
// Click on either the first "free" or "purchased" book found that
|
||||
// matches the book title
|
||||
try {
|
||||
freeLabel.click();
|
||||
} catch (UiObjectNotFoundException e) {
|
||||
purchasedLabel.click();
|
||||
}
|
||||
}
|
||||
|
||||
private void addToLibrary() throws Exception {
|
||||
UiObject add = new UiObject(new UiSelector().textContains("ADD TO LIBRARY")
|
||||
.className("android.widget.Button"));
|
||||
if (add.exists()) {
|
||||
add.click(); // add to My Library and opens book by default
|
||||
} else {
|
||||
UiObject read = getUiObjectByText("READ", "android.widget.Button");
|
||||
read.click(); // opens book
|
||||
}
|
||||
|
||||
waitForPage();
|
||||
|
||||
UiObject navigationButton = new UiObject(new UiSelector().description("Navigate up"));
|
||||
|
||||
// Return to main app window
|
||||
pressBack();
|
||||
|
||||
// On some devices screen ordering is not preserved so check for
|
||||
// navigation button to determine current screen
|
||||
if (navigationButton.exists()) {
|
||||
pressBack();
|
||||
pressBack();
|
||||
}
|
||||
}
|
||||
|
||||
private void openMyLibrary() throws Exception {
|
||||
String testTag = "open_library";
|
||||
ActionLogger logger = new ActionLogger(testTag, parameters);
|
||||
logger.start();
|
||||
|
||||
UiObject openDrawer = getUiObjectByDescription("Show navigation drawer");
|
||||
openDrawer.click();
|
||||
|
||||
// To correctly find the UiObject we need to specify the index also here
|
||||
UiObject myLibrary =
|
||||
new UiObject(new UiSelector().className("android.widget.TextView")
|
||||
.text("My library").index(3));
|
||||
myLibrary.clickAndWaitForNewWindow(uiAutoTimeout);
|
||||
logger.stop();
|
||||
}
|
||||
|
||||
private void openBook(final String bookTitle) throws Exception {
|
||||
|
||||
UiScrollable cardsGrid =
|
||||
new UiScrollable(new UiSelector().resourceId("com.google.android.apps.books:id/cards_grid"));
|
||||
|
||||
UiSelector bookSelector = new UiSelector().text(bookTitle).className("android.widget.TextView");
|
||||
UiObject book = new UiObject(bookSelector);
|
||||
|
||||
// Check that books are sorted by time added to library. This way we
|
||||
// can assume any newly downloaded books will be visible on the first
|
||||
// screen.
|
||||
UiObject menuSort =
|
||||
getUiObjectByResourceId("com.google.android.apps.books:id/menu_sort", "android.widget.TextView");
|
||||
menuSort.click();
|
||||
|
||||
UiObject sortByRecent = getUiObjectByText("Recent", "android.widget.TextView");
|
||||
sortByRecent.click();
|
||||
|
||||
// When the book is first added to library it may not appear in
|
||||
// cardsGrid until it has been fully downloaded. Wait for fully
|
||||
// downloaded books
|
||||
UiObject downloadComplete =
|
||||
new UiObject(new UiSelector().fromParent(bookSelector).description("100% downloaded"));
|
||||
|
||||
int maxDownloadTime = 120; // seconds
|
||||
|
||||
while (!downloadComplete.exists()) {
|
||||
if (maxDownloadTime <= 0) {
|
||||
throw new UiObjectNotFoundException(
|
||||
"Exceeded maximum wait time (" + maxDownloadTime + " seconds) to download book \"" + bookTitle + "\"");
|
||||
} else {
|
||||
sleep(1);
|
||||
maxDownloadTime--;
|
||||
}
|
||||
}
|
||||
|
||||
book.click();
|
||||
waitForPage();
|
||||
}
|
||||
|
||||
private void gesturesTest() throws Exception {
|
||||
String testTag = "gestures";
|
||||
|
||||
// Perform a range of swipe tests while browsing home photoplaybooks gallery
|
||||
LinkedHashMap<String, GestureTestParams> testParams = new LinkedHashMap<String, GestureTestParams>();
|
||||
testParams.put("swipe_left", new GestureTestParams(GestureType.UIDEVICE_SWIPE, Direction.LEFT, 20));
|
||||
testParams.put("swipe_right", new GestureTestParams(GestureType.UIDEVICE_SWIPE, Direction.RIGHT, 20));
|
||||
testParams.put("pinch_out", new GestureTestParams(GestureType.PINCH, PinchType.OUT, 100, 50));
|
||||
testParams.put("pinch_in", new GestureTestParams(GestureType.PINCH, PinchType.IN, 100, 50));
|
||||
|
||||
Iterator<Entry<String, GestureTestParams>> it = testParams.entrySet().iterator();
|
||||
|
||||
while (it.hasNext()) {
|
||||
Map.Entry<String, GestureTestParams> 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;
|
||||
|
||||
String runName = String.format(testTag + "_" + pair.getKey());
|
||||
ActionLogger logger = new ActionLogger(runName, parameters);
|
||||
|
||||
UiObject pageView = waitForPage();
|
||||
|
||||
logger.start();
|
||||
|
||||
switch (type) {
|
||||
case UIDEVICE_SWIPE:
|
||||
uiDeviceSwipe(dir, steps);
|
||||
break;
|
||||
case UIOBJECT_SWIPE:
|
||||
uiObjectSwipe(pageView, dir, steps);
|
||||
break;
|
||||
case PINCH:
|
||||
uiObjectVertPinch(pageView, pinch, steps, percent);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
logger.stop();
|
||||
}
|
||||
|
||||
waitForPage();
|
||||
}
|
||||
|
||||
private UiObject searchPage(final UiObject view, final String pagenum, final Direction updown,
|
||||
final int attempts) throws Exception {
|
||||
if (attempts <= 0) {
|
||||
throw new UiObjectNotFoundException("Could not find \"page number\" after several attempts.");
|
||||
}
|
||||
|
||||
String search = String.format("page " + pagenum);
|
||||
UiObject page = new UiObject(new UiSelector().description(search)
|
||||
.className("android.widget.TextView"));
|
||||
if (!page.exists()) {
|
||||
// Scroll up by swiping down
|
||||
if (updown == Direction.UP) {
|
||||
view.swipeDown(200);
|
||||
// Default case is to scroll down (swipe up)
|
||||
} else {
|
||||
view.swipeUp(200);
|
||||
}
|
||||
searchPage(view, pagenum, updown, attempts - 1);
|
||||
}
|
||||
return page;
|
||||
}
|
||||
|
||||
private void selectChapter(final String chapterPageNumber) throws Exception {
|
||||
getDropdownMenu();
|
||||
|
||||
UiObject contents = getUiObjectByResourceId("com.google.android.apps.books:id/menu_reader_toc",
|
||||
"android.widget.TextView");
|
||||
contents.clickAndWaitForNewWindow(uiAutoTimeout);
|
||||
|
||||
UiObject toChapterView = getUiObjectByResourceId("com.google.android.apps.books:id/toc_list_view",
|
||||
"android.widget.ExpandableListView");
|
||||
|
||||
// Navigate to top of chapter view
|
||||
searchPage(toChapterView, "1", Direction.UP, 10);
|
||||
|
||||
UiObject page = searchPage(toChapterView, chapterPageNumber, Direction.DOWN, 10);
|
||||
|
||||
page.clickAndWaitForNewWindow(viewTimeout);
|
||||
|
||||
waitForPage();
|
||||
}
|
||||
|
||||
private void addNote(final String text) throws Exception {
|
||||
String testTag = "add_note";
|
||||
ActionLogger logger = new ActionLogger(testTag, parameters);
|
||||
logger.start();
|
||||
|
||||
UiObject clickable = new UiObject(new UiSelector().longClickable(true));
|
||||
uiObjectPerformLongClick(clickable, 100);
|
||||
|
||||
UiObject addNoteButton = new UiObject(
|
||||
new UiSelector().resourceId("com.google.android.apps.books:id/add_note_button"));
|
||||
addNoteButton.click();
|
||||
|
||||
UiObject noteEditText = getUiObjectByResourceId("com.google.android.apps.books:id/note_edit_text",
|
||||
"android.widget.EditText");
|
||||
noteEditText.setText(text);
|
||||
|
||||
UiObject noteMenuButton = getUiObjectByResourceId("com.google.android.apps.books:id/note_menu_button",
|
||||
"android.widget.ImageButton");
|
||||
noteMenuButton.click();
|
||||
|
||||
UiObject saveButton = getUiObjectByText("Save", "android.widget.TextView");
|
||||
saveButton.click();
|
||||
|
||||
logger.stop();
|
||||
|
||||
waitForPage();
|
||||
}
|
||||
|
||||
private void removeNote() throws Exception {
|
||||
String testTag = "remove_note";
|
||||
ActionLogger logger = new ActionLogger(testTag, parameters);
|
||||
logger.start();
|
||||
|
||||
UiObject clickable = new UiObject(new UiSelector().longClickable(true));
|
||||
uiObjectPerformLongClick(clickable, 100);
|
||||
|
||||
UiObject removeButton = new UiObject(
|
||||
new UiSelector().resourceId("com.google.android.apps.books:id/remove_highlight_button"));
|
||||
removeButton.click();
|
||||
|
||||
UiObject confirmRemove = getUiObjectByText("Remove", "android.widget.Button");
|
||||
confirmRemove.click();
|
||||
|
||||
logger.stop();
|
||||
|
||||
waitForPage();
|
||||
}
|
||||
|
||||
private void searchForWord(final String text) throws Exception {
|
||||
String testTag = "search_for_word";
|
||||
ActionLogger logger = new ActionLogger(testTag, parameters);
|
||||
|
||||
getDropdownMenu();
|
||||
logger.start();
|
||||
UiObject search = new UiObject(
|
||||
new UiSelector().resourceId("com.google.android.apps.books:id/menu_search"));
|
||||
search.click();
|
||||
|
||||
UiObject searchText = new UiObject(
|
||||
new UiSelector().resourceId("com.google.android.apps.books:id/search_src_text"));
|
||||
searchText.setText(text);
|
||||
pressEnter();
|
||||
|
||||
UiObject resultList = new UiObject(
|
||||
new UiSelector().resourceId("com.google.android.apps.books:id/search_results_list"));
|
||||
|
||||
// Allow extra time for search queries involing high freqency words
|
||||
final long searchTimeout = TimeUnit.SECONDS.toMillis(20);
|
||||
|
||||
if (!resultList.waitForExists(searchTimeout)) {
|
||||
throw new UiObjectNotFoundException("Could not find \"search results list view\".");
|
||||
}
|
||||
|
||||
UiObject searchWeb =
|
||||
new UiObject(new UiSelector().text("Search web")
|
||||
.className("android.widget.TextView"));
|
||||
|
||||
if (!searchWeb.waitForExists(searchTimeout)) {
|
||||
throw new UiObjectNotFoundException("Could not find \"Search web view\".");
|
||||
}
|
||||
|
||||
logger.stop();
|
||||
pressBack();
|
||||
}
|
||||
|
||||
private void switchPageStyles() throws Exception {
|
||||
String testTag = "switch_page_style";
|
||||
|
||||
getDropdownMenu();
|
||||
UiObject readerSettings = getUiObjectByResourceId("com.google.android.apps.books:id/menu_reader_settings",
|
||||
"android.widget.TextView");
|
||||
readerSettings.click();
|
||||
|
||||
// Check for lighting option button on newer versions
|
||||
UiObject lightingOptionsButton =
|
||||
new UiObject(new UiSelector().resourceId("com.google.android.apps.books:id/lighting_options_button"));
|
||||
|
||||
if (lightingOptionsButton.exists()) {
|
||||
lightingOptionsButton.click();
|
||||
}
|
||||
|
||||
String[] styles = {"Night", "Sepia", "Day"};
|
||||
|
||||
for (String style : styles) {
|
||||
ActionLogger logger = new ActionLogger(testTag + "_" + style, parameters);
|
||||
logger.start();
|
||||
UiObject pageStyle = new UiObject(new UiSelector().description(style));
|
||||
pageStyle.clickAndWaitForNewWindow(viewTimeout);
|
||||
logger.stop();
|
||||
}
|
||||
|
||||
sleep(2);
|
||||
tapDisplayCentre();
|
||||
waitForPage();
|
||||
}
|
||||
|
||||
private void aboutBook() throws Exception {
|
||||
String testTag = "about_book";
|
||||
ActionLogger logger = new ActionLogger(testTag, parameters);
|
||||
|
||||
getDropdownMenu();
|
||||
logger.start();
|
||||
|
||||
UiObject moreOptions = getUiObjectByDescription("More options", "android.widget.ImageView");
|
||||
moreOptions.click();
|
||||
|
||||
UiObject bookInfo = getUiObjectByText("About this book", "android.widget.TextView");
|
||||
bookInfo.clickAndWaitForNewWindow(uiAutoTimeout);
|
||||
|
||||
UiObject detailsPanel =
|
||||
new UiObject(new UiSelector().resourceId("com.android.vending:id/item_details_panel"));
|
||||
waitObject(detailsPanel, viewTimeoutSecs);
|
||||
logger.stop();
|
||||
|
||||
pressBack();
|
||||
}
|
||||
|
||||
// Helper for accessing the drop down menu
|
||||
private void getDropdownMenu() throws Exception {
|
||||
sleep(1); // Allow previous views to settle
|
||||
int height = getDisplayHeight();
|
||||
int width = getDisplayCentreWidth();
|
||||
getUiDevice().swipe(width, 20, width, height / 10, 50);
|
||||
|
||||
// selecting the drop down menu can be unreliable so check for its
|
||||
// existence and if not present try for a second time using a different
|
||||
// start point and step size
|
||||
UiObject actionBar =
|
||||
new UiObject(new UiSelector().resourceId("com.google.android.apps.books:id/action_bar"));
|
||||
|
||||
long actionBarTimeout = TimeUnit.SECONDS.toMillis(3);
|
||||
|
||||
if (!actionBar.waitForExists(actionBarTimeout)) {
|
||||
getUiDevice().swipe(width, 5, width, height / 10, 20);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper for waiting on a page between actions
|
||||
private UiObject waitForPage() throws Exception {
|
||||
UiObject activityReader =
|
||||
new UiObject(new UiSelector().resourceId("com.google.android.apps.books:id/activity_reader")
|
||||
.childSelector(new UiSelector().focusable(true)));
|
||||
|
||||
// On some devices the object in the view hierarchy is found before it
|
||||
// becomes visible on the screen. Therefore add pause instead.
|
||||
sleep(3);
|
||||
|
||||
if (!activityReader.waitForExists(viewTimeout)) {
|
||||
throw new UiObjectNotFoundException("Could not find \"activity reader view\".");
|
||||
}
|
||||
|
||||
return activityReader;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user