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

Merge pull request #28 from jimboatarm/workload-slides

Google Slides Workload
This commit is contained in:
jimboatarm 2016-05-31 11:21:38 +01:00
commit aaab37ba3b
23 changed files with 823 additions and 4 deletions

1
.gitignore vendored
View File

@ -28,3 +28,4 @@ pmu_logger.mod.c
obj/
libs/armeabi
wlauto/workloads/*/uiauto/bin/
wlauto/external/uiauto/bin/

View File

@ -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/*.class ../../common/android

View File

@ -23,6 +23,7 @@ import java.util.concurrent.TimeoutException;
import java.util.concurrent.TimeUnit;
import android.os.Bundle;
import android.os.SystemClock;
import android.graphics.Point;
import android.graphics.Rect;
@ -37,6 +38,14 @@ public class BaseUiAutomation extends UiAutomatorTestCase {
public long waitTimeout = TimeUnit.SECONDS.toMillis(4);
public enum ScreenOrientation { RIGHT, NATURAL, LEFT };
public static final int CLICK_REPEAT_INTERVAL_MINIMUM = 5;
public static final int CLICK_REPEAT_INTERVAL_DEFAULT = 50;
/*
* Used by clickUiObject() methods in order to provide a consistent API
*/
public enum FindByCriteria { BY_ID, BY_TEXT, BY_DESC; }
public void sleep(int second) {
super.sleep(second * 1000);
}
@ -276,4 +285,82 @@ public class BaseUiAutomation extends UiAutomatorTestCase {
getUiDevice().getInstance().swipe(rect.centerX(), rect.centerY(),
rect.centerX(), rect.centerY(), steps);
}
public void uiDeviceSwipeVertical(int startY, int endY, int xCoordinate, int steps) {
getUiDevice().swipe(startY, xCoordinate, endY, xCoordinate, steps);
}
public void uiDeviceSwipeHorizontal(int startX, int endX, int yCoordinate, int steps) {
getUiDevice().swipe(startX, yCoordinate, endX, yCoordinate, steps);
}
public void repeatClickUiObject(UiObject view, int repeatCount, int intervalInMillis) throws Exception {
int repeatInterval = intervalInMillis > CLICK_REPEAT_INTERVAL_MINIMUM ? intervalInMillis : CLICK_REPEAT_INTERVAL_DEFAULT;
if (repeatCount < 1 || !view.isClickable()) {
return;
}
while (repeatCount-- > 0) {
view.click();
SystemClock.sleep(repeatInterval); // in order to register as separate click
}
}
public UiObject clickUiObject(FindByCriteria criteria, String matching) throws Exception {
return clickUiObject(criteria, matching, null, false);
}
public UiObject clickUiObject(FindByCriteria criteria, String matching, boolean wait) throws Exception {
return clickUiObject(criteria, matching, null, wait);
}
public UiObject clickUiObject(FindByCriteria criteria, String matching, String clazz) throws Exception {
return clickUiObject(criteria, matching, clazz, false);
}
public UiObject clickUiObject(FindByCriteria criteria, String matching, String clazz, boolean wait) throws Exception {
UiObject view;
switch (criteria) {
case BY_ID:
view = clazz == null ? getUiObjectByResourceId(matching) : getUiObjectByResourceId(matching, clazz);
break;
case BY_DESC:
view = clazz == null ? getUiObjectByDescription(matching) : getUiObjectByDescription(matching, clazz);
break;
case BY_TEXT:
default:
view = clazz == null ? getUiObjectByText(matching) : getUiObjectByText(matching, clazz);
break;
}
if (wait) {
view.clickAndWaitForNewWindow();
} else {
view.click();
}
return view;
}
public UiObject getUiObjectByText(String text) throws Exception {
UiObject object = new UiObject(new UiSelector().textContains(text));
if (!object.waitForExists(waitTimeout)) {
throw new UiObjectNotFoundException("Could not find view with text: " + text);
};
return object;
}
public UiObject getUiObjectByDescription(String desc) throws Exception {
UiObject object = new UiObject(new UiSelector().descriptionContains(desc));
if (!object.waitForExists(waitTimeout)) {
throw new UiObjectNotFoundException("Could not find view with description: " + desc);
};
return object;
}
public UiObject getUiObjectByResourceId(String id) throws Exception {
UiObject object = new UiObject(new UiSelector().resourceId(id));
if (!object.waitForExists(waitTimeout)) {
throw new UiObjectNotFoundException("Could not find view with resource ID: " + id);
};
return object;
}
}

View File

@ -51,7 +51,7 @@ public class UxPerfUiAutomation extends BaseUiAutomation {
public enum GestureType { UIDEVICE_SWIPE, UIOBJECT_SWIPE, PINCH };
public enum PinchType { IN, OUT, NULL };
public class Timer {
public static class Timer {
private long startTime = 0;
private long endTime = 0;
private long duration = 0;
@ -110,14 +110,20 @@ public class UxPerfUiAutomation extends BaseUiAutomation {
}
public void initDumpsysSurfaceFlinger(String appPackage) {
String packageView = getSurfaceFlingerView(appPackage);
initDumpsysSurfaceFlinger(appPackage, getSurfaceFlingerView(appPackage));
}
public void initDumpsysSurfaceFlinger(String appPackage, String packageView) {
List<String> command = Arrays.asList("dumpsys", "SurfaceFlinger", "--latency-clear",
packageView);
executeCommand(command);
}
public void exitDumpsysSurfaceFlinger(String appPackage, File filename) {
String packageView = getSurfaceFlingerView(appPackage);
exitDumpsysSurfaceFlinger(appPackage, getSurfaceFlingerView(appPackage), filename);
}
public void exitDumpsysSurfaceFlinger(String appPackage, String packageView, File filename) {
List<String> command = Arrays.asList("dumpsys", "SurfaceFlinger", "--latency", packageView);
exitDumpsys(command, filename);
}
@ -295,7 +301,7 @@ public class UxPerfUiAutomation extends BaseUiAutomation {
return results;
}
public class GestureTestParams {
public static class GestureTestParams {
public GestureType gestureType;
public Direction gestureDirection;
public PinchType pinchType;

View File

@ -0,0 +1,171 @@
# 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 os.path as path
import re
from wlauto import AndroidUiAutoBenchmark, Parameter
from wlauto.utils.types import list_of_strings
def log_method(workload, name):
workload.logger.info('===== {}() ======'.format(name))
class GoogleSlides(AndroidUiAutoBenchmark):
name = 'googleslides'
description = '''
A workload to perform standard productivity tasks with Google Slides. The workload carries
out various tasks, such as creating a new presentation, adding text, images, and shapes,
as well as basic editing and playing a slideshow.
Under normal circumstances, this workload should be able to run without a network connection.
Test description:
1. The workload is split into two main scenarios:
- A. a PowerPoint presentation is copied on to the devices to test slide navigation,
- B. a new file is created in the application and basic editing performed.
2. The application is started in offline access mode
3. For scenario A, the navigation test is performed while the file is in editing mode,
swiping forward to the next slide until the end, followed by the same action in the
reverse direction back to the first slide.
4. Afterwards, one more navigation pass through is done while in presentation mode.
5. In scenario B, a new PowerPoint presentation is created on-device, new slides added
with some text, an image from the gallery and a shape. Editing is done on the text
to change font size and resize the shape.
6. Finally, the file is saved to storage, and then deleted from the documents list and device
after a short delay.
'''
package = 'com.google.android.apps.docs.editors.slides'
activity = ''
# Views for FPS instrumentation
view = [
package + '/com.google.android.apps.docs.quickoffice.filepicker.FilePickerActivity',
package + '/com.google.android.apps.docs.editors.shared.filepicker.FilePickerActivity',
package + '/com.google.android.apps.docs.quickoffice.filepicker.LocalSaveAsActivity',
package + '/com.qo.android.quickpoint.Quickpoint',
package + '/com.google.android.apps.docs.app.DocsPreferencesActivity',
package + '/com.google.android.apps.docs.app.DocListActivity',
package + '/com.google.android.apps.docs.welcome.warmwelcome.TrackingWelcomeActivity',
package + '/com.google.android.apps.docs.app.NewMainProxyActivity',
]
parameters = [
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.
'''),
Parameter('local_file', kind=str,
description='''
If specified, the workload will push the PowerPoint file to be used for
testing on the device. Otherwise, a file will be created inside the app.
'''),
Parameter('slide_count', kind=int, default=5,
description='''
Number of slides in aforementioned local file. Determines number of
swipe actions when playing slide show.
'''),
]
instrumentation_log = '{}_instrumentation.log'.format(name)
def __init__(self, device, **kwargs):
super(GoogleSlides, self).__init__(device, **kwargs)
self.run_timeout = 300
self.output_file = path.join(self.device.working_directory, self.instrumentation_log)
self.local_dir = self.dependencies_directory
# Use Android downloads folder as it is the default folder opened by Google Slides'
# file picker, and not WA's working directory. Helps test reliability by avoiding
# having to navigate around the filesystem to locate pushed file.
self.device_dir = path.join(self.device.working_directory, '..', 'Download')
self.wa_test_file = 'wa_test_{}'.format(self.local_file) if self.local_file else None
def validate(self):
log_method(self, 'validate')
super(GoogleSlides, self).validate()
self.uiauto_params['dumpsys_enabled'] = self.dumpsys_enabled
self.uiauto_params['output_dir'] = self.device.working_directory
self.uiauto_params['results_file'] = self.output_file
if self.local_file:
self.uiauto_params['local_file'] = self.wa_test_file
self.uiauto_params['slide_count'] = self.slide_count
def initialize(self, context):
log_method(self, 'initialize')
super(GoogleSlides, self).initialize(context)
self.logger.info('local_dir={}, local_file={}'.format(self.local_dir, self.local_file))
self.logger.info('device_dir={}, wa_test_file={}'.format(self.device_dir, self.wa_test_file))
if self.local_file:
# push local PPT file
for entry in os.listdir(self.local_dir):
if entry == self.local_file:
self.device.push_file(path.join(self.local_dir, self.local_file),
path.join(self.device_dir, self.wa_test_file),
timeout=60)
def setup(self, context):
log_method(self, 'setup')
super(GoogleSlides, self).setup(context)
def run(self, context):
log_method(self, 'run')
super(GoogleSlides, self).run(context)
def update_result(self, context):
log_method(self, 'update_result')
super(GoogleSlides, self).update_result(context)
self.get_metrics(context)
def teardown(self, context):
log_method(self, 'teardown')
super(GoogleSlides, self).teardown(context)
self.pull_logs(context)
def finalize(self, context):
log_method(self, 'finalize')
super(GoogleSlides, self).finalize(context)
if self.local_file:
# delete pushed PPT file
for entry in self.device.listdir(self.device_dir):
if entry == self.wa_test_file:
self.device.delete_file(path.join(self.device_dir, entry))
def wa_filename(self, filename):
return self.file_prefix + filename
def get_metrics(self, context):
self.device.pull_file(self.output_file, context.output_directory)
metrics_file = path.join(context.output_directory, self.instrumentation_log)
with open(metrics_file, 'r') as wfh:
regex = re.compile(r'(?P<key>\w+)\s+(?P<value1>\d+)\s+(?P<value2>\d+)\s+(?P<value3>\d+)')
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 pull_logs(self, context):
wd = self.device.working_directory
for entry in self.device.listdir(wd):
if entry.startswith(self.name) and entry.endswith('.log'):
self.device.pull_file(path.join(wd, entry), context.output_directory)
self.device.delete_file(path.join(wd, 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 (remove previous)
package=com.arm.wlauto.uiauto.googleslides.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.googleslides" 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,403 @@
/* 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.googleslides;
import java.io.File;
import java.util.LinkedHashMap;
import java.util.Map;
import android.os.Bundle;
import android.os.SystemClock;
// Import the uiautomator libraries
import com.android.uiautomator.core.UiObject;
import com.android.uiautomator.core.UiObjectNotFoundException;
import com.android.uiautomator.core.UiScrollable;
import com.android.uiautomator.core.UiSelector;
import com.arm.wlauto.uiauto.UxPerfUiAutomation;
import static com.arm.wlauto.uiauto.BaseUiAutomation.FindByCriteria.BY_ID;
import static com.arm.wlauto.uiauto.BaseUiAutomation.FindByCriteria.BY_TEXT;
import static com.arm.wlauto.uiauto.BaseUiAutomation.FindByCriteria.BY_DESC;
public class UiAutomation extends UxPerfUiAutomation {
public static final String TAG = "googleslides";
public static final String PACKAGE = "com.google.android.apps.docs.editors.slides";
public static final String PACKAGE_ID = PACKAGE + ":id/";
public static final String ACTIVITY_DOCLIST = "com.google.android.apps.docs.app.DocListActivity";
public static final String ACTIVITY_SLIDES = "com.qo.android.quickpoint.Quickpoint";
public static final String ACTIVITY_SETTINGS = "com.google.android.apps.docs.app.DocsPreferencesActivity";
public static final String CLASS_TEXT_VIEW = "android.widget.TextView";
public static final String CLASS_IMAGE_VIEW = "android.widget.ImageView";
public static final String CLASS_BUTTON = "android.widget.Button";
public static final String CLASS_IMAGE_BUTTON = "android.widget.ImageButton";
public static final String CLASS_TABLE_ROW = "android.widget.TableRow";
public static final int DIALOG_WAIT_TIME_MS = 3000;
public static final int SLIDE_WAIT_TIME_MS = 200;
public static final int CLICK_REPEAT_INTERVAL_MS = 50;
public static final int DEFAULT_SWIPE_STEPS = 10;
public static final String NEW_DOC_FILENAME = "UX Perf Slides";
public static final String SLIDE_TEXT_CONTENT =
"class Workload(Extension):\n\tname = None\n\tdef init_resources(self, context):\n\t\tpass\n"
+ "\tdef validate(self):\n\t\tpass\n\tdef initialize(self, context):\n\t\tpass\n"
+ "\tdef setup(self, context):\n\t\tpass\n\tdef setup(self, context):\n\t\tpass\n"
+ "\tdef run(self, context):\n\t\tpass\n\tdef update_result(self, context):\n\t\tpass\n"
+ "\tdef teardown(self, context):\n\t\tpass\n\tdef finalize(self, context):\n\t\tpass\n";
protected Map<String, Timer> results = new LinkedHashMap<String, Timer>();
protected Timer timer = new Timer();
protected Bundle parameters;
protected boolean dumpsysEnabled;
protected String outputDir;
protected String localFile;
protected int slideCount;
protected boolean useLocalFile;
public void parseParams(Bundle parameters) throws Exception {
dumpsysEnabled = Boolean.parseBoolean(parameters.getString("dumpsys_enabled"));
outputDir = parameters.getString("output_dir");
localFile = parameters.getString("local_file");
useLocalFile = localFile != null;
if (useLocalFile) {
slideCount = Integer.parseInt(parameters.getString("slide_count"));
}
}
public void runUiAutomation() throws Exception {
parameters = getParams();
parseParams(parameters);
skipWelcomeScreen();
openAndCloseDrawer();
enablePowerpointCompat();
if (useLocalFile) {
testSlideshowFromStorage(localFile);
} else {
testEditNewSlidesDocument(NEW_DOC_FILENAME);
}
writeResultsToFile(results, parameters.getString("results_file"));
}
protected void skipWelcomeScreen() throws Exception {
timer = new Timer();
timer.start();
clickUiObject(BY_TEXT, "Skip", true);
timer.end();
results.put("skip_welcome", timer);
sleep(1);
}
protected void openAndCloseDrawer() throws Exception {
startDumpsys(ACTIVITY_DOCLIST);
timer = new Timer();
timer.start();
clickUiObject(BY_DESC, "drawer");
getUiDevice().pressBack();
timer.end();
results.put("open_drawer", timer);
endDumpsys(ACTIVITY_DOCLIST, "open_drawer");
sleep(1);
}
protected void enablePowerpointCompat() throws Exception {
startDumpsys(ACTIVITY_SETTINGS);
timer = new Timer();
timer.start();
clickUiObject(BY_DESC, "drawer");
clickUiObject(BY_TEXT, "Settings", true);
clickUiObject(BY_TEXT, "Create PowerPoint");
getUiDevice().pressBack();
timer.end();
results.put("enable_ppt_compat", timer);
endDumpsys(ACTIVITY_SETTINGS, "enable_ppt_compat");
sleep(1);
}
protected void testSlideshowFromStorage(String docName) throws Exception {
// Sometimes docs deleted in __init__.py falsely appear on the app's home
// For robustness, it's nice to remove these placeholders
// However, the test should not crash because of it, so a silent catch is used
UiObject docView = new UiObject(new UiSelector().textContains(docName));
if (docView.waitForExists(1000)) {
try {
deleteDocument(docName);
} catch (Exception e) {
// do nothing
}
}
// Open document
timer = new Timer();
timer.start();
clickUiObject(BY_DESC, "Open presentation");
clickUiObject(BY_TEXT, "Device storage", true);
timer.end();
results.put("open_file_picker", timer);
// Scroll through document list if necessary
UiScrollable list = new UiScrollable(new UiSelector().className("android.widget.ListView"));
list.scrollIntoView(new UiSelector().textContains(docName));
timer = new Timer();
timer.start();
clickUiObject(BY_TEXT, docName);
clickUiObject(BY_TEXT, "Open", CLASS_BUTTON, true);
timer.end();
results.put("open_document", timer);
sleep(5);
// Begin Slide show test
// Note: A short wait-time is introduced before transition to the next slide to simulate
// a real user's behaviour. Otherwise the test swipes through the slides too quickly.
// These waits are not measured in the per-slide timings, and introduce a systematic
// error in the overall slideshow timings.
int centerY = getUiDevice().getDisplayHeight() / 2;
int centerX = getUiDevice().getDisplayWidth() / 2;
int slideIndex = 0;
String testTag;
Timer slideTimer;
// scroll forward in edit mode
startDumpsys(ACTIVITY_SLIDES);
timer = new Timer();
timer.start();
while (++slideIndex < slideCount) {
testTag = "slides_next_" + slideIndex;
startDumpsys(ACTIVITY_SLIDES);
slideTimer = new Timer();
slideTimer.start();
uiDeviceSwipeHorizontal(centerX + centerX/2, centerX - centerX/2,
centerY, DEFAULT_SWIPE_STEPS);
slideTimer.end();
results.put(testTag, slideTimer);
endDumpsys(ACTIVITY_SLIDES, testTag);
SystemClock.sleep(SLIDE_WAIT_TIME_MS);
}
timer.end();
results.put("slides_forward", timer);
endDumpsys(ACTIVITY_SLIDES, "slides_forward");
sleep(1);
// scroll backward in edit mode
startDumpsys(ACTIVITY_SLIDES);
timer = new Timer();
timer.start();
while (--slideIndex > 0) {
testTag = "slides_previous_" + slideIndex;
startDumpsys(ACTIVITY_SLIDES);
slideTimer = new Timer();
slideTimer.start();
uiDeviceSwipeHorizontal(centerX - centerX/2, centerX + centerX/2,
centerY, DEFAULT_SWIPE_STEPS);
slideTimer.end();
results.put(testTag, slideTimer);
endDumpsys(ACTIVITY_SLIDES, testTag);
SystemClock.sleep(SLIDE_WAIT_TIME_MS);
}
timer.end();
results.put("slides_reverse", timer);
endDumpsys(ACTIVITY_SLIDES, "slides_reverse");
sleep(1);
// scroll forward in slideshow mode
timer = new Timer();
timer.start();
clickUiObject(BY_DESC, "Start slideshow", true);
timer.end();
results.put("open_slideshow", timer);
startDumpsys(ACTIVITY_SLIDES);
timer = new Timer();
timer.start();
while (++slideIndex < slideCount) {
testTag = "slideshow_next_" + slideIndex;
startDumpsys(ACTIVITY_SLIDES);
slideTimer = new Timer();
slideTimer.start();
uiDeviceSwipeHorizontal(centerX + centerX/2, centerX - centerX/2,
centerY, DEFAULT_SWIPE_STEPS);
slideTimer.end();
results.put(testTag, slideTimer);
endDumpsys(ACTIVITY_SLIDES, testTag);
SystemClock.sleep(SLIDE_WAIT_TIME_MS);
}
timer.end();
results.put("play_slideshow", timer);
endDumpsys(ACTIVITY_SLIDES, "play_slideshow");
sleep(1);
getUiDevice().pressBack();
getUiDevice().pressBack();
}
protected void testEditNewSlidesDocument(String docName) throws Exception {
startDumpsys(ACTIVITY_DOCLIST);
// create new file
timer = new Timer();
timer.start();
clickUiObject(BY_DESC, "New presentation");
clickUiObject(BY_TEXT, "New PowerPoint", true);
timer.end();
results.put("create_document", timer);
endDumpsys(ACTIVITY_DOCLIST, "create_document");
// first slide
enterTextInSlide("Title", "WORKLOAD AUTOMATION");
enterTextInSlide("Subtitle", "Measuring perfomance of different productivity apps on Android OS");
saveDocument(docName);
insertSlide("Title and Content");
enterTextInSlide("title", "Extensions - Workloads");
enterTextInSlide("Text placeholder", SLIDE_TEXT_CONTENT);
clickUiObject(BY_DESC, "Text placeholder");
clickUiObject(BY_DESC, "Format");
clickUiObject(BY_TEXT, "Droid Sans");
clickUiObject(BY_TEXT, "Droid Sans Mono");
clickUiObject(BY_ID, PACKAGE_ID + "palette_back_button");
UiObject decreaseFont = getUiObjectByDescription("Decrease text");
repeatClickUiObject(decreaseFont, 20, CLICK_REPEAT_INTERVAL_MS);
getUiDevice().pressBack();
// get image from gallery and insert
// To keep the test simple just select the most recent image regardless of what
// folder it's in. More reliable than trying to find a pushed image in the file
// picker, and fails gracefully in the rare case that no images exist.
insertSlide("Title Only");
clickUiObject(BY_DESC, "Insert");
clickUiObject(BY_TEXT, "Image", true);
clickUiObject(BY_TEXT, "Recent");
try {
UiObject image = new UiObject(new UiSelector().resourceId("com.android.documentsui:id/date").instance(2));
image.clickAndWaitForNewWindow();
} catch (UiObjectNotFoundException e) {
clickUiObject(BY_ID, "com.android.documentsui:id/date", true);
}
// last slide
insertSlide("Title Slide");
// insert "?" shape
clickUiObject(BY_DESC, "Insert");
clickUiObject(BY_TEXT, "Shape");
clickUiObject(BY_TEXT, "Buttons");
clickUiObject(BY_DESC, "actionButtonHelp");
UiObject resize = getUiObjectByDescription("Bottom-left resize");
UiObject shape = getUiObjectByDescription("actionButtonHelp");
UiObject subtitle = getUiObjectByDescription("subTitle");
resize.dragTo(subtitle, 40);
shape.dragTo(subtitle, 40);
enterTextInSlide("title", "THE END. QUESTIONS?");
sleep(1);
getUiDevice().pressBack();
deleteDocument(docName);
}
public void insertSlide(String slideLayout) throws Exception {
sleep(1); // a bit of time to see previous slide
UiObject view = getUiObjectByDescription("Insert slide");
view.clickAndWaitForNewWindow();
view = getUiObjectByText(slideLayout);
view.clickAndWaitForNewWindow();
}
public void enterTextInSlide(String viewName, String textToEnter) throws Exception {
UiObject view = getUiObjectByDescription(viewName);
view.click();
view.setText(textToEnter);
try {
clickUiObject(BY_DESC, "Done");
} catch (UiObjectNotFoundException e) {
clickUiObject(BY_ID, "android:id/action_mode_close_button");
}
// On some devices, keyboard pops up when entering text, and takes a noticeable
// amount of time (few milliseconds) to disappear after clicking Done.
// In these cases, trying to find a view immediately after entering text leads
// to an exception, so a short wait-time is added for stability.
SystemClock.sleep(SLIDE_WAIT_TIME_MS);
}
public void saveDocument(String docName) throws Exception {
timer = new Timer();
timer.start();
clickUiObject(BY_TEXT, "SAVE");
clickUiObject(BY_TEXT, "Device");
timer.end();
results.put("save_dialog_1", timer);
timer = new Timer();
timer.start();
UiObject filename = getUiObjectByResourceId(PACKAGE_ID + "file_name_edit_text");
filename.clearTextField();
filename.setText(docName);
clickUiObject(BY_TEXT, "Save", CLASS_BUTTON);
timer.end();
results.put("save_dialog_2", timer);
// Overwrite if prompted
// Should not happen under normal circumstances. But ensures test doesn't stop
// if a previous iteration failed prematurely and was unable to delete the file.
// Note that this file isn't removed during workload teardown as deleting it is
// part of the UiAutomator test case.
UiObject overwriteView = new UiObject(new UiSelector().textContains("already exists"));
if (overwriteView.waitForExists(DIALOG_WAIT_TIME_MS)) {
clickUiObject(BY_TEXT, "Overwrite");
}
sleep(1);
}
public void deleteDocument(String docName) throws Exception {
timer = new Timer();
timer.start();
UiObject doc = getUiObjectByText(docName);
doc.longClick();
clickUiObject(BY_TEXT, "Remove");
timer.end();
results.put("delete_dialog_1", timer);
timer = new Timer();
timer.start();
UiObject deleteButton;
try {
deleteButton = getUiObjectByText("Remove", CLASS_BUTTON);
} catch (UiObjectNotFoundException e) {
deleteButton = getUiObjectByText("Ok", CLASS_BUTTON);
}
deleteButton.clickAndWaitForNewWindow();
timer.end();
results.put("delete_dialog_2", timer);
sleep(1);
}
public void startDumpsys(String viewName) throws Exception {
if (!dumpsysEnabled)
return;
initDumpsysSurfaceFlinger(PACKAGE);
initDumpsysGfxInfo(PACKAGE);
}
public void endDumpsys(String viewName, String testTag) throws Exception {
if (!dumpsysEnabled)
return;
String dumpsysTag = TAG + "_" + testTag;
exitDumpsysSurfaceFlinger(PACKAGE, new File(outputDir, dumpsysTag + "_surfFlinger.log"));
exitDumpsysGfxInfo(PACKAGE, new File(outputDir, dumpsysTag + "_gfxInfo.log"));
}
}