1
0
mirror of https://github.com/ARM-software/workload-automation.git synced 2025-03-13 22:28:36 +00:00

Google Play Books workload

A new workload for productivity tasks within Google Play Books
This commit is contained in:
John Richardson 2016-05-12 11:37:54 +01:00
parent aaab37ba3b
commit d4b2d873f0
9 changed files with 681 additions and 4 deletions

View File

@ -152,6 +152,14 @@ public class BaseUiAutomation extends UiAutomatorTestCase {
return object;
}
public void pressEnter() {
getUiDevice().getInstance().pressEnter();
}
public void pressBack() {
getUiDevice().getInstance().pressBack();
}
public int getDisplayHeight () {
return getUiDevice().getInstance().getDisplayHeight();
}
@ -176,10 +184,6 @@ public class BaseUiAutomation extends UiAutomatorTestCase {
getUiDevice().getInstance().click(x, y);
}
public void pressBack() {
getUiDevice().getInstance().pressBack();
}
public void uiDeviceSwipeUp (int steps) {
getUiDevice().getInstance().swipe(
getDisplayCentreWidth(),

View File

@ -0,0 +1,120 @@
# 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
__version__ = '0.1.0'
class Googleplaybooks(AndroidUiAutoBenchmark):
name = 'googleplaybooks'
package = 'com.google.android.apps.books'
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. Gestures are performed to swipe between pages and pinch zoom in and out of a page
5. Selects a random chapter from the navigation view
6. Selects a word in the centre of screen and adds a test note to the page
7. Removes the test note from the page (clean up)
8. Searches for the number of occurrences of a common word throughout the book
9. Uses the 'About this book' facility on the currently selected book
NOTE: This workload requires a network connection (ideally, wifi) to run.
"""
parameters = [
Parameter('search_book_title', kind=str, mandatory=False, default='Shakespeare',
description="""
The book title to search for within Google Play Books archive.
Note: spaces must be replaced with underscores in the book title.
"""),
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('dumpsys_enabled', kind=bool, default=True,
description="""
If ``True``, dumpsys captures will be carried out during the
test run. The output is piped to log files which are then
pulled from the phone.
"""),
]
instrumentation_log = ''.join([name, '_instrumentation.log'])
def __init__(self, device, **kwargs):
super(Googleplaybooks, self).__init__(device, **kwargs)
self.output_file = os.path.join(self.device.working_directory, self.instrumentation_log)
def validate(self):
super(Googleplaybooks, self).validate()
self.uiauto_params['package'] = self.package
self.uiauto_params['output_dir'] = self.device.working_directory
self.uiauto_params['output_file'] = self.output_file
self.uiauto_params['dumpsys_enabled'] = self.dumpsys_enabled
self.uiauto_params['book_title'] = self.search_book_title
self.uiauto_params['search_word'] = self.search_word
def initialize(self, context):
super(Googleplaybooks, self).initialize(context)
if not self.device.is_wifi_connected():
raise DeviceError('Wifi is not connected for device {}'.format(self.device.name))
def update_result(self, context):
super(Googleplaybooks, self).update_result(context)
self.device.pull_file(self.output_file, context.output_directory)
result_file = os.path.join(context.output_directory, self.instrumentation_log)
with open(result_file, 'r') as wfh:
pattern = r'(?P<key>\w+)\s+(?P<value1>\d+)\s+(?P<value2>\d+)\s+(?P<value3>\d+)'
regex = re.compile(pattern)
for line in wfh:
match = regex.search(line)
if match:
context.result.add_metric((match.group('key') + "_start"),
match.group('value1'), units='ms')
context.result.add_metric((match.group('key') + "_finish"),
match.group('value2'), units='ms')
context.result.add_metric((match.group('key') + "_duration"),
match.group('value3'), units='ms')
def teardown(self, context):
super(Googleplaybooks, self).teardown(context)
for entry in self.device.listdir(self.device.working_directory):
if entry.endswith(".log"):
self.device.pull_file(os.path.join(self.device.working_directory, entry),
context.output_directory)
self.device.delete_file(os.path.join(self.device.working_directory, entry))

View 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

View 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>

View 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

View File

@ -0,0 +1,408 @@
/* 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.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);
private LinkedHashMap<String, Timer> timingResults = new LinkedHashMap<String, Timer>();
public void runUiAutomation() throws Exception {
this.timeout = TimeUnit.SECONDS.toMillis(8);
parameters = getParams();
String bookTitle = parameters.getString("book_title").replace("_", " ");
String searchWord = parameters.getString("search_word");
String noteText = "This is a test note";
setScreenOrientation(ScreenOrientation.NATURAL);
clearFirstRunDialogues();
dismissSync();
openMyLibrary();
searchForBook(bookTitle);
selectBook(0); // Select the first book
gesturesTest();
selectRandomChapter();
addNote(noteText);
removeNote();
searchForWord(searchWord);
switchPageStyles();
aboutBook();
pressBack();
unsetScreenOrientation();
writeResultsToFile(timingResults, parameters.getString("output_file"));
}
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 must choose a book the first
// time application is run
private void clearFirstRunDialogues() throws Exception {
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();
}
}
private void openMyLibrary() throws Exception {
Timer result = new Timer();
result.start();
UiObject openDrawer = getUiObjectByDescription("Show navigation drawer",
"android.widget.ImageButton");
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(timeout);
result.end();
timingResults.put("open_library", result);
}
private void searchForBook(final String text) throws Exception {
Timer result = new Timer();
result.start();
UiObject search =
new UiObject(new UiSelector().resourceId("com.google.android.apps.books:id/menu_search"));
search.click();
UiObject searchText =
getUiObjectByResourceId("com.google.android.apps.books:id/search_src_text",
"android.widget.EditText");
searchText.setText(text);
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\".");
}
result.end();
timingResults.put("search_for_book", result);
pressBack();
}
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, 10));
testParams.put("swipe_right", new GestureTestParams(GestureType.UIDEVICE_SWIPE, Direction.RIGHT, 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));
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());
String gfxInfologName = String.format(runName + "_gfxInfo.log");
String surfFlingerlogName = String.format(runName + "_surfFlinger.log");
UiObject pageView = getPageView();
if (!pageView.waitForExists(viewTimeout)) {
throw new UiObjectNotFoundException("Could not find \"page view\".");
}
startDumpsysGfxInfo(parameters);
startDumpsysSurfaceFlinger(parameters);
Timer result = new Timer();
switch (type) {
case UIDEVICE_SWIPE:
result = uiDeviceSwipeTest(dir, steps);
break;
case UIOBJECT_SWIPE:
result = uiObjectSwipeTest(pageView, dir, steps);
break;
case PINCH:
result = uiObjectVertPinchTest(pageView, pinch, steps, percent);
break;
default:
break;
}
stopDumpsysSurfaceFlinger(parameters, surfFlingerlogName);
stopDumpsysGfxInfo(parameters, gfxInfologName);
timingResults.put(runName, result);
}
if (!getPageView().waitForExists(viewTimeout)) {
throw new UiObjectNotFoundException("Could not find \"page view\".");
}
}
private void selectRandomChapter() throws Exception {
String testTag = "select_random_chapter";
String gfxInfologName = String.format(testTag + "_gfxInfo.log");
String surfFlingerlogName = String.format(testTag + "_surfFlinger.log");
getDropdownMenu();
Timer result = new Timer();
result.start();
UiObject contents = getUiObjectByResourceId("com.google.android.apps.books:id/menu_reader_toc",
"android.widget.TextView");
contents.clickAndWaitForNewWindow(timeout);
UiObject toChapterView = getUiObjectByResourceId("com.google.android.apps.books:id/toc_list_view",
"android.widget.ExpandableListView");
startDumpsysGfxInfo(parameters);
startDumpsysSurfaceFlinger(parameters);
toChapterView.swipeUp(100);
tapDisplayCentre();
stopDumpsysSurfaceFlinger(parameters, surfFlingerlogName);
stopDumpsysGfxInfo(parameters, gfxInfologName);
waitForPage();
result.end();
timingResults.put(testTag, result);
}
private void addNote(final String text) throws Exception {
Timer result = new Timer();
result.start();
UiObject clickable = new UiObject(new UiSelector().longClickable(true));
uiDevicePerformLongClick(clickable, 100);
UiObject addNoteButton = getUiObjectByResourceId("com.google.android.apps.books:id/add_note_button",
"android.widget.ImageButton");
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();
waitForPage();
result.end();
timingResults.put("add_note", result);
}
private void removeNote() throws Exception {
Timer result = new Timer();
result.start();
UiObject clickable = new UiObject(new UiSelector().longClickable(true));
uiDevicePerformLongClick(clickable, 100);
UiObject removeButton = getUiObjectByResourceId("com.google.android.apps.books:id/remove_highlight_button",
"android.widget.ImageButton");
removeButton.click();
UiObject confirmRemove = getUiObjectByText("Remove", "android.widget.Button");
confirmRemove.click();
waitForPage();
result.end();
timingResults.put("remove_note", result);
}
private void searchForWord(final String text) throws Exception {
getDropdownMenu();
Timer result = new Timer();
result.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"));
if (!resultList.waitForExists(viewTimeout)) {
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(viewTimeout)) {
throw new UiObjectNotFoundException("Could not find \"Search web view\".");
}
result.end();
timingResults.put("search_for_word", result);
pressBack();
}
private void switchPageStyles() throws Exception {
String testTag = "switch_page_style";
String gfxInfologName = String.format(testTag + "_gfxInfo.log");
getDropdownMenu();
UiObject readerSettings = getUiObjectByResourceId("com.google.android.apps.books:id/menu_reader_settings",
"android.widget.TextView");
readerSettings.click();
String[] styles = {"Night", "Sepia", "Day"};
startDumpsysGfxInfo(parameters);
for (String style : styles) {
Timer result = new Timer();
result.start();
UiObject pageStyle = getUiObjectByDescription(style, "android.widget.TextView");
pageStyle.clickAndWaitForNewWindow(viewTimeout);
result.end();
timingResults.put(String.format(testTag + "_" + style), result);
}
stopDumpsysGfxInfo(parameters, gfxInfologName);
pressBack();
}
private void aboutBook() throws Exception {
getDropdownMenu();
Timer result = new Timer();
result.start();
UiObject moreOptions = getUiObjectByDescription("More options", "android.widget.ImageView");
moreOptions.click();
UiObject bookInfo = getUiObjectByText("About this book", "android.widget.TextView");
bookInfo.clickAndWaitForNewWindow(timeout);
UiObject detailsPanel =
new UiObject(new UiSelector().resourceId("com.android.vending:id/item_details_panel"));
waitObject(detailsPanel, viewTimeoutSecs);
result.end();
timingResults.put("about_book", result);
pressBack();
}
// Helper to click on an individual book based on index in My library gallery.
private void selectBook(final int index) throws Exception {
UiObject book =
new UiObject(new UiSelector().resourceId("com.google.android.apps.books:id/cards_grid")
.childSelector(new UiSelector()
.index(index + 1) // adjust for zero index
.className("android.widget.FrameLayout")
.clickable(true)));
book.clickAndWaitForNewWindow();
if (!getPageView().waitForExists(viewTimeout)) {
throw new UiObjectNotFoundException("Could not find \"page view\".");
}
}
// 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, 5, width, height / 10, 20);
}
// Helper for returning common UiObject page view
private UiObject getPageView() {
return new UiObject(new UiSelector().resourceId("com.google.android.apps.books:id/book_view")
.childSelector(new UiSelector()
.focusable(true)));
}
// Helper for waiting on a page between actions
private void waitForPage() throws Exception {
UiObject activityReader =
new UiObject(new UiSelector().resourceId("com.google.android.apps.books:id/activity_reader")
.childSelector(new UiSelector().focusable(true)));
if (!activityReader.waitForExists(viewTimeout)) {
throw new UiObjectNotFoundException("Could not find \"activity reader view\".");
}
}
}