mirror of
				https://github.com/ARM-software/workload-automation.git
				synced 2025-11-04 09:02:12 +00:00 
			
		
		
		
	Merge pull request #242 from jimboatarm/upstream-slides
Add Google slides workload
This commit is contained in:
		@@ -341,7 +341,7 @@ class ApkWorkload(Workload):
 | 
			
		||||
                try:
 | 
			
		||||
                    self.device.execute("pm grant {} {}".format(self.package, permission))
 | 
			
		||||
                except DeviceError as e:
 | 
			
		||||
                    if "not a changeable permission" in e.message:
 | 
			
		||||
                    if "changeable permission" in e.message or "Unknown permission" in e.message:
 | 
			
		||||
                        self.logger.debug(e)
 | 
			
		||||
                    else:
 | 
			
		||||
                        raise e
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										143
									
								
								wlauto/workloads/googleslides/__init__.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										143
									
								
								wlauto/workloads/googleslides/__init__.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,143 @@
 | 
			
		||||
#    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 AndroidUxPerfWorkload, Parameter, File
 | 
			
		||||
from wlauto.exceptions import WorkloadError
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GoogleSlides(AndroidUxPerfWorkload):
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
    This workload should be able to run without a network connection.
 | 
			
		||||
 | 
			
		||||
    There are two main scenarios:
 | 
			
		||||
      1. create test: a presentation is created in-app and some editing done on it,
 | 
			
		||||
      2. load test: a pre-existing PowerPoint file is copied onto the device for testing.
 | 
			
		||||
 | 
			
		||||
    --- create ---
 | 
			
		||||
    Create a new file in the application and perform basic editing on it. This test also
 | 
			
		||||
    requires an image file specified by the param ``test_image`` to be copied onto the device.
 | 
			
		||||
 | 
			
		||||
    Test description:
 | 
			
		||||
    1. Start the app and skip the welcome screen. Dismiss the work offline banner if present.
 | 
			
		||||
    2. Go to the app settings page and enables PowerPoint compatibility mode. This allows
 | 
			
		||||
       PowerPoint files to be created inside Google Slides.
 | 
			
		||||
    3. Create a new PowerPoint presentation in the app (PPT compatibility mode) with a title
 | 
			
		||||
       slide and save it to device storage.
 | 
			
		||||
    4. Insert another slide and to it insert the pushed image by picking it from the gallery.
 | 
			
		||||
    5. Insert the final slide and add a shape to it. Resize and drag the shape to modify it.
 | 
			
		||||
    6. Finally, navigate back to the documents list and delete file from the list to remove
 | 
			
		||||
       it from the device.
 | 
			
		||||
 | 
			
		||||
    --- load ---
 | 
			
		||||
    Copy a PowerPoint presentation onto the device to test slide navigation. The PowerPoint
 | 
			
		||||
    file to be copied is given by ``test_file``.
 | 
			
		||||
 | 
			
		||||
    Test description:
 | 
			
		||||
    1. From the documents list (following the create test), open the specified PowerPoint
 | 
			
		||||
       by navigating into device storage and wait for it to be loaded.
 | 
			
		||||
    2. A navigation test is performed while the file is in editing mode (i.e. not slideshow).
 | 
			
		||||
       swiping forward to the next slide until ``slide_count`` swipes are performed.
 | 
			
		||||
    3. While still in editing mode, the same action is done in the reverse direction back to
 | 
			
		||||
       the first slide.
 | 
			
		||||
    4. Enter presentation mode by selecting to play the slideshow.
 | 
			
		||||
    5. Swipe forward to play the slideshow, for a maximum number of ``slide_count`` swipes.
 | 
			
		||||
    6. Finally, repeat the previous step in the reverse direction while still in presentation
 | 
			
		||||
       mode, navigating back to the first slide.
 | 
			
		||||
 | 
			
		||||
    NOTE: There are known issues with the reliability of this workload on some targets.
 | 
			
		||||
    It MAY NOT ALWAYS WORK on your device. If you do run into problems, it might help to
 | 
			
		||||
    set ``do_text_entry`` parameter to ``False``.
 | 
			
		||||
 | 
			
		||||
    '''
 | 
			
		||||
 | 
			
		||||
    package = 'com.google.android.apps.docs.editors.slides'
 | 
			
		||||
    min_apk_version = '1.6.312.08'
 | 
			
		||||
    max_apk_version = None  # works with latest (1.6.332.13) at time of publishing this
 | 
			
		||||
    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('test_image', kind=str, mandatory=True, default='uxperf_1600x1200.jpg',
 | 
			
		||||
                  description='''
 | 
			
		||||
                  An image to be copied onto the device that will be embedded in the
 | 
			
		||||
                  PowerPoint file as part of the test.
 | 
			
		||||
                  '''),
 | 
			
		||||
        Parameter('test_file', kind=str, default='uxperf_test_doc.pptx',
 | 
			
		||||
                  description='''
 | 
			
		||||
                  If specified, the workload will copy the PowerPoint file to be used for
 | 
			
		||||
                  testing onto 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.
 | 
			
		||||
                  '''),
 | 
			
		||||
        Parameter('do_text_entry', kind=bool, default=True,
 | 
			
		||||
                  description='''
 | 
			
		||||
                  If set to ``True``, will attempt to enter text in the first slide as part
 | 
			
		||||
                  of the test. Currently seems to be problematic on some devices, most
 | 
			
		||||
                  notably Samsung devices.
 | 
			
		||||
                  ''')
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    # Created file will be saved with this name
 | 
			
		||||
    new_doc_name = "WORKLOAD AUTOMATION"
 | 
			
		||||
 | 
			
		||||
    def __init__(self, device, **kwargs):
 | 
			
		||||
        super(GoogleSlides, self).__init__(device, **kwargs)
 | 
			
		||||
        self.run_timeout = 600
 | 
			
		||||
        self.deployable_assets += [self.test_image, self.test_file]
 | 
			
		||||
 | 
			
		||||
    def validate(self):
 | 
			
		||||
        super(GoogleSlides, self).validate()
 | 
			
		||||
        self.uiauto_params['workdir_name'] = self.device.path.basename(self.device.working_directory)
 | 
			
		||||
        self.uiauto_params['test_file'] = self.test_file.replace(' ', '0space0')
 | 
			
		||||
        self.uiauto_params['slide_count'] = self.slide_count
 | 
			
		||||
        self.uiauto_params['do_text_entry'] = self.do_text_entry
 | 
			
		||||
        self.uiauto_params['new_doc_name'] = self.new_doc_name.replace(' ', '0space0')
 | 
			
		||||
 | 
			
		||||
    def setup(self, context):
 | 
			
		||||
        super(GoogleSlides, self).setup(context)
 | 
			
		||||
        # Force re-index of removable storage to pick up pushed image in gallery
 | 
			
		||||
        self.device.broadcast_media_mounted(self.device.working_directory)
 | 
			
		||||
 | 
			
		||||
    def teardown(self, context):
 | 
			
		||||
        super(GoogleSlides, self).teardown(context)
 | 
			
		||||
        self.device.delete_file(self.device.path.join(self.device.working_directory, self.new_doc_name))
 | 
			
		||||
        self.device.broadcast_media_mounted(self.device.working_directory)
 | 
			
		||||
 | 
			
		||||
    def finalize(self, context):
 | 
			
		||||
        super(GoogleSlides, self).finalize(context)
 | 
			
		||||
        self.delete_assets()
 | 
			
		||||
        self.device.broadcast_media_mounted(self.device.working_directory)
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										39
									
								
								wlauto/workloads/googleslides/uiauto/build.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										39
									
								
								wlauto/workloads/googleslides/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 (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
 | 
			
		||||
							
								
								
									
										92
									
								
								wlauto/workloads/googleslides/uiauto/build.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								wlauto/workloads/googleslides/uiauto/build.xml
									
									
									
									
									
										Normal 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>
 | 
			
		||||
							
								
								
									
										14
									
								
								wlauto/workloads/googleslides/uiauto/project.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								wlauto/workloads/googleslides/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
 | 
			
		||||
							
								
								
									
										417
									
								
								wlauto/workloads/googleslides/uiauto/src/com/arm/wlauto/uiauto/UiAutomation.java
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										417
									
								
								wlauto/workloads/googleslides/uiauto/src/com/arm/wlauto/uiauto/UiAutomation.java
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,417 @@
 | 
			
		||||
/*    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.graphics.Rect;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.os.SystemClock;
 | 
			
		||||
 | 
			
		||||
// Import the uiautomator libraries
 | 
			
		||||
import com.android.uiautomator.core.Configurator;
 | 
			
		||||
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 ANDROID_WIDGET = "android.widget.";
 | 
			
		||||
    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 String CLASS_PROGRESS_BAR = ANDROID_WIDGET + "ProgressBar";
 | 
			
		||||
    public static final String CLASS_LIST_VIEW = ANDROID_WIDGET + "ListView";
 | 
			
		||||
 | 
			
		||||
    public static final int WAIT_TIMEOUT_1SEC = 1000;
 | 
			
		||||
    public static final int SLIDE_WAIT_TIME_MS = 200;
 | 
			
		||||
    public static final int DEFAULT_SWIPE_STEPS = 10;
 | 
			
		||||
 | 
			
		||||
    protected ActionLogger logger;
 | 
			
		||||
    protected String packageId;
 | 
			
		||||
    protected Bundle parameters;
 | 
			
		||||
    protected String newDocumentName;
 | 
			
		||||
    protected String pushedDocumentName;
 | 
			
		||||
    protected String workingDirectoryName;
 | 
			
		||||
    protected int slideCount;
 | 
			
		||||
    protected boolean doTextEntry;
 | 
			
		||||
 | 
			
		||||
    public void runUiAutomation() throws Exception {
 | 
			
		||||
        // Setup
 | 
			
		||||
        parameters = getParams();
 | 
			
		||||
        parseParams(parameters);
 | 
			
		||||
        setScreenOrientation(ScreenOrientation.NATURAL);
 | 
			
		||||
        changeAckTimeout(100);
 | 
			
		||||
        // UI automation begins here
 | 
			
		||||
        skipWelcomeScreen();
 | 
			
		||||
        sleep(1);
 | 
			
		||||
        dismissWorkOfflineBanner();
 | 
			
		||||
        sleep(1);
 | 
			
		||||
        enablePowerpointCompat();
 | 
			
		||||
        sleep(1);
 | 
			
		||||
        testEditNewSlidesDocument(newDocumentName);
 | 
			
		||||
        sleep(1);
 | 
			
		||||
        testSlideshowFromStorage(pushedDocumentName);
 | 
			
		||||
        // UI automation ends here
 | 
			
		||||
        unsetScreenOrientation();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void parseParams(Bundle parameters) throws Exception {
 | 
			
		||||
        pushedDocumentName = parameters.getString("test_file").replaceAll("0space0", " ");
 | 
			
		||||
        newDocumentName = parameters.getString("new_doc_name").replaceAll("0space0", " ");
 | 
			
		||||
        slideCount = Integer.parseInt(parameters.getString("slide_count"));
 | 
			
		||||
        packageId = parameters.getString("package") + ":id/";
 | 
			
		||||
        workingDirectoryName = parameters.getString("workdir_name");
 | 
			
		||||
        doTextEntry = Boolean.parseBoolean(parameters.getString("do_text_entry"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void dismissWorkOfflineBanner() throws Exception {
 | 
			
		||||
        UiObject banner = new UiObject(new UiSelector().textContains("Work offline"));
 | 
			
		||||
        if (banner.waitForExists(WAIT_TIMEOUT_1SEC)) {
 | 
			
		||||
            clickUiObject(BY_TEXT, "Got it", CLASS_BUTTON);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void enterTextInSlide(String viewName, String textToEnter) throws Exception {
 | 
			
		||||
        UiSelector container = new UiSelector().resourceId(packageId + "main_canvas");
 | 
			
		||||
        UiObject view = new UiObject(container.childSelector(new UiSelector().descriptionMatches(viewName)));
 | 
			
		||||
        view.click();
 | 
			
		||||
        getUiDevice().pressEnter();
 | 
			
		||||
        view.setText(textToEnter);
 | 
			
		||||
        tapOpenArea();
 | 
			
		||||
        // 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 insertSlide(String slideLayout) throws Exception {
 | 
			
		||||
        clickUiObject(BY_DESC, "Add slide", true);
 | 
			
		||||
        clickUiObject(BY_TEXT, slideLayout, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void insertImage() throws Exception {
 | 
			
		||||
        UiObject insertButton = new UiObject(new UiSelector().descriptionContains("Insert"));
 | 
			
		||||
        if (insertButton.exists()) {
 | 
			
		||||
            insertButton.click();
 | 
			
		||||
        } else {
 | 
			
		||||
            clickUiObject(BY_DESC, "More options");
 | 
			
		||||
            clickUiObject(BY_TEXT, "Insert");
 | 
			
		||||
        }
 | 
			
		||||
        clickUiObject(BY_TEXT, "Image", true);
 | 
			
		||||
        clickUiObject(BY_TEXT, "From photos");
 | 
			
		||||
 | 
			
		||||
        UiObject imagesFolder = new UiObject(new UiSelector().className(CLASS_TEXT_VIEW).textContains("Images"));
 | 
			
		||||
        if (!imagesFolder.waitForExists(WAIT_TIMEOUT_1SEC*10)) {
 | 
			
		||||
            clickUiObject(BY_DESC, "Show roots");
 | 
			
		||||
        }
 | 
			
		||||
        imagesFolder.click();
 | 
			
		||||
 | 
			
		||||
        UiObject folderEntry = new UiObject(new UiSelector().textContains(workingDirectoryName));
 | 
			
		||||
        UiScrollable list = new UiScrollable(new UiSelector().scrollable(true));
 | 
			
		||||
        if (!folderEntry.exists() && list.waitForExists(WAIT_TIMEOUT_1SEC)) {
 | 
			
		||||
            list.scrollIntoView(folderEntry);
 | 
			
		||||
        } else {
 | 
			
		||||
            folderEntry.waitForExists(WAIT_TIMEOUT_1SEC*10);
 | 
			
		||||
        }
 | 
			
		||||
        folderEntry.clickAndWaitForNewWindow();
 | 
			
		||||
        clickUiObject(BY_ID, "com.android.documentsui:id/date", true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void insertShape(String shapeName) throws Exception {
 | 
			
		||||
        startLogger("shape_insert");
 | 
			
		||||
        UiObject insertButton = new UiObject(new UiSelector().descriptionContains("Insert"));
 | 
			
		||||
        if (insertButton.exists()) {
 | 
			
		||||
            insertButton.click();
 | 
			
		||||
        } else {
 | 
			
		||||
            clickUiObject(BY_DESC, "More options");
 | 
			
		||||
            clickUiObject(BY_TEXT, "Insert");
 | 
			
		||||
        }
 | 
			
		||||
        clickUiObject(BY_TEXT, "Shape");
 | 
			
		||||
        clickUiObject(BY_DESC, shapeName);
 | 
			
		||||
        stopLogger("shape_insert");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void modifyShape(String shapeName) throws Exception {
 | 
			
		||||
        UiObject resizeHandle = new UiObject(new UiSelector().descriptionMatches(".*Bottom[- ]right resize.*"));
 | 
			
		||||
        Rect bounds = resizeHandle.getVisibleBounds();
 | 
			
		||||
        int newX = bounds.left - 40;
 | 
			
		||||
        int newY = bounds.bottom - 40;
 | 
			
		||||
        startLogger("shape_resize");
 | 
			
		||||
        resizeHandle.dragTo(newX, newY, 40);
 | 
			
		||||
        stopLogger("shape_resize");
 | 
			
		||||
 | 
			
		||||
        UiSelector container = new UiSelector().resourceId(packageId + "main_canvas");
 | 
			
		||||
        UiSelector shapeSelector = container.childSelector(new UiSelector().descriptionContains(shapeName));
 | 
			
		||||
        startLogger("shape_drag");
 | 
			
		||||
        new UiObject(shapeSelector).dragTo(newX, newY, 40);
 | 
			
		||||
        stopLogger("shape_drag");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void openDocument(String docName) throws Exception {
 | 
			
		||||
        clickUiObject(BY_DESC, "Open presentation");
 | 
			
		||||
        clickUiObject(BY_TEXT, "Device storage", true);
 | 
			
		||||
        clickUiObject(BY_DESC, "Navigate up");
 | 
			
		||||
        UiScrollable list = new UiScrollable(new UiSelector().className(CLASS_LIST_VIEW));
 | 
			
		||||
        list.scrollIntoView(new UiSelector().textMatches(workingDirectoryName));
 | 
			
		||||
        clickUiObject(BY_TEXT, workingDirectoryName);
 | 
			
		||||
        list.scrollIntoView(new UiSelector().textContains(docName));
 | 
			
		||||
        startLogger("document_open");
 | 
			
		||||
        clickUiObject(BY_TEXT, docName);
 | 
			
		||||
        clickUiObject(BY_TEXT, "Open", CLASS_BUTTON, true);
 | 
			
		||||
        stopLogger("document_open");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void newDocument() throws Exception {
 | 
			
		||||
        startLogger("document_new");
 | 
			
		||||
        clickUiObject(BY_DESC, "New presentation");
 | 
			
		||||
        clickUiObject(BY_TEXT, "New PowerPoint", true);
 | 
			
		||||
        stopLogger("document_new");
 | 
			
		||||
        waitForProgress(WAIT_TIMEOUT_1SEC * 30);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void saveDocument(String docName) throws Exception {
 | 
			
		||||
        UiObject saveActionButton = new UiObject(new UiSelector().resourceId(packageId + "action"));
 | 
			
		||||
        UiObject unsavedIndicator = new UiObject(new UiSelector().textContains("Not saved"));
 | 
			
		||||
        startLogger("document_save");
 | 
			
		||||
        if (saveActionButton.waitForExists(WAIT_TIMEOUT_1SEC)) {
 | 
			
		||||
            saveActionButton.click();
 | 
			
		||||
        } else if (unsavedIndicator.waitForExists(WAIT_TIMEOUT_1SEC)) {
 | 
			
		||||
            unsavedIndicator.click();
 | 
			
		||||
        }
 | 
			
		||||
        clickUiObject(BY_TEXT, "Device");
 | 
			
		||||
        UiObject save = clickUiObject(BY_TEXT, "Save", CLASS_BUTTON);
 | 
			
		||||
        if (save.waitForExists(WAIT_TIMEOUT_1SEC)) {
 | 
			
		||||
            save.click();
 | 
			
		||||
        }
 | 
			
		||||
        stopLogger("document_save");
 | 
			
		||||
 | 
			
		||||
        // 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(WAIT_TIMEOUT_1SEC)) {
 | 
			
		||||
            clickUiObject(BY_TEXT, "Overwrite");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void deleteDocument(String docName) throws Exception {
 | 
			
		||||
        String filenameRegex = String.format(".*((%s)|([Uu]ntitled presentation)).pptx.*", docName);
 | 
			
		||||
        UiObject doc = new UiObject(new UiSelector().textMatches(filenameRegex));
 | 
			
		||||
        UiObject moreActions = doc.getFromParent(new UiSelector().descriptionContains("More actions"));
 | 
			
		||||
        startLogger("document_delete");
 | 
			
		||||
        moreActions.click();
 | 
			
		||||
 | 
			
		||||
        UiObject deleteButton = new UiObject(new UiSelector().textMatches(".*([Dd]elete|[Rr]emove).*"));
 | 
			
		||||
        if (deleteButton.waitForExists(WAIT_TIMEOUT_1SEC)) {
 | 
			
		||||
            deleteButton.click();
 | 
			
		||||
        } else {
 | 
			
		||||
            // Delete button not found, try to scroll the view
 | 
			
		||||
            UiScrollable scrollable = new UiScrollable(new UiSelector().scrollable(true)
 | 
			
		||||
                    .childSelector(new UiSelector().textContains("Rename")));
 | 
			
		||||
            if (scrollable.exists()) {
 | 
			
		||||
                scrollable.scrollIntoView(deleteButton);
 | 
			
		||||
            } else {
 | 
			
		||||
                UiObject content = new UiObject(new UiSelector().resourceId(packageId + "content"));
 | 
			
		||||
                int attemptsLeft = 10; // try a maximum of 10 swipe attempts
 | 
			
		||||
                while (!deleteButton.exists() && attemptsLeft > 0) {
 | 
			
		||||
                    content.swipeUp(DEFAULT_SWIPE_STEPS);
 | 
			
		||||
                    attemptsLeft--;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            deleteButton.click();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        UiObject okButton = new UiObject(new UiSelector().className(CLASS_BUTTON).textContains("OK"));
 | 
			
		||||
        if (okButton.waitForExists(WAIT_TIMEOUT_1SEC)) {
 | 
			
		||||
            okButton.clickAndWaitForNewWindow();
 | 
			
		||||
        } else {
 | 
			
		||||
            clickUiObject(BY_TEXT, "Remove", CLASS_BUTTON, true);
 | 
			
		||||
        }
 | 
			
		||||
        stopLogger("document_delete");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    protected void skipWelcomeScreen() throws Exception {
 | 
			
		||||
        clickUiObject(BY_TEXT, "Skip", true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected void enablePowerpointCompat() throws Exception {
 | 
			
		||||
        startLogger("enable_pptmode");
 | 
			
		||||
        clickUiObject(BY_DESC, "drawer");
 | 
			
		||||
        clickUiObject(BY_TEXT, "Settings", true);
 | 
			
		||||
        clickUiObject(BY_TEXT, "Create PowerPoint");
 | 
			
		||||
        getUiDevice().pressBack();
 | 
			
		||||
        stopLogger("enable_pptmode");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected void testEditNewSlidesDocument(String docName) throws Exception {
 | 
			
		||||
        // Init
 | 
			
		||||
        newDocument();
 | 
			
		||||
 | 
			
		||||
        // Slide 1 - Text
 | 
			
		||||
        if (doTextEntry) {
 | 
			
		||||
            enterTextInSlide(".*[Tt]itle.*", docName);
 | 
			
		||||
            // Save
 | 
			
		||||
            saveDocument(docName);
 | 
			
		||||
            sleep(1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Slide 2 - Image
 | 
			
		||||
        insertSlide("Title only");
 | 
			
		||||
        insertImage();
 | 
			
		||||
        sleep(1);
 | 
			
		||||
 | 
			
		||||
        // If text wasn't entered in first slide, save prompt will appear here
 | 
			
		||||
        if (!doTextEntry) {
 | 
			
		||||
            // Save
 | 
			
		||||
            saveDocument(docName);
 | 
			
		||||
            sleep(1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Slide 3 - Shape
 | 
			
		||||
        insertSlide("Title slide");
 | 
			
		||||
        String shapeName = "Rounded rectangle";
 | 
			
		||||
        insertShape(shapeName);
 | 
			
		||||
        modifyShape(shapeName);
 | 
			
		||||
        getUiDevice().pressBack();
 | 
			
		||||
        sleep(1);
 | 
			
		||||
 | 
			
		||||
        // Tidy up
 | 
			
		||||
        getUiDevice().pressBack();
 | 
			
		||||
        dismissWorkOfflineBanner(); // if it appears on the homescreen
 | 
			
		||||
 | 
			
		||||
        // Note: Currently disabled because it fails on Samsung devices
 | 
			
		||||
        // deleteDocument(docName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected void testSlideshowFromStorage(String docName) throws Exception {
 | 
			
		||||
        // Open document
 | 
			
		||||
        openDocument(docName);
 | 
			
		||||
        waitForProgress(WAIT_TIMEOUT_1SEC*30);
 | 
			
		||||
 | 
			
		||||
        // Begin Slide show test
 | 
			
		||||
 | 
			
		||||
        // Note: Using coordinates slightly offset from the slide edges avoids accidentally
 | 
			
		||||
        // selecting any shapes or text boxes inside the slides while swiping, which may
 | 
			
		||||
        // cause the view to switch into edit mode and fail the test
 | 
			
		||||
        UiObject slideCanvas = new UiObject(new UiSelector().resourceId(packageId + "main_canvas"));
 | 
			
		||||
        Rect canvasBounds = slideCanvas.getVisibleBounds();
 | 
			
		||||
        int leftEdge = canvasBounds.left + 10;
 | 
			
		||||
        int rightEdge = canvasBounds.right - 10;
 | 
			
		||||
        int yCoordinate = canvasBounds.top + 5;
 | 
			
		||||
        int slideIndex = 0;
 | 
			
		||||
 | 
			
		||||
        // scroll forward in edit mode
 | 
			
		||||
        startLogger("slideshow_editforward");
 | 
			
		||||
        while (++slideIndex < slideCount) {
 | 
			
		||||
            uiDeviceSwipeHorizontal(rightEdge, leftEdge, yCoordinate, DEFAULT_SWIPE_STEPS);
 | 
			
		||||
            waitForProgress(WAIT_TIMEOUT_1SEC*5);
 | 
			
		||||
        }
 | 
			
		||||
        stopLogger("slideshow_editforward");
 | 
			
		||||
        sleep(1);
 | 
			
		||||
 | 
			
		||||
        // scroll backward in edit mode
 | 
			
		||||
        startLogger("slideshow_editbackward");
 | 
			
		||||
        while (--slideIndex > 0) {
 | 
			
		||||
            uiDeviceSwipeHorizontal(leftEdge, rightEdge, yCoordinate, DEFAULT_SWIPE_STEPS);
 | 
			
		||||
            waitForProgress(WAIT_TIMEOUT_1SEC*5);
 | 
			
		||||
        }
 | 
			
		||||
        stopLogger("slideshow_editbackward");
 | 
			
		||||
        sleep(1);
 | 
			
		||||
 | 
			
		||||
        // run slideshow
 | 
			
		||||
        startLogger("slideshow_run");
 | 
			
		||||
        clickUiObject(BY_DESC, "Start slideshow", true);
 | 
			
		||||
        UiObject onDevice = new UiObject(new UiSelector().textContains("this device"));
 | 
			
		||||
        if (onDevice.waitForExists(WAIT_TIMEOUT_1SEC)) {
 | 
			
		||||
            onDevice.clickAndWaitForNewWindow();
 | 
			
		||||
            waitForProgress(WAIT_TIMEOUT_1SEC*30);
 | 
			
		||||
            UiObject presentation = new UiObject(new UiSelector().descriptionContains("Presentation Viewer"));
 | 
			
		||||
            presentation.waitForExists(WAIT_TIMEOUT_1SEC*30);
 | 
			
		||||
        }
 | 
			
		||||
        stopLogger("slideshow_run");
 | 
			
		||||
        sleep(1);
 | 
			
		||||
 | 
			
		||||
        // scroll forward in slideshow mode
 | 
			
		||||
        startLogger("slideshow_playforward");
 | 
			
		||||
        while (++slideIndex < slideCount) {
 | 
			
		||||
            uiDeviceSwipeHorizontal(rightEdge, leftEdge, yCoordinate, DEFAULT_SWIPE_STEPS);
 | 
			
		||||
            waitForProgress(WAIT_TIMEOUT_1SEC*5);
 | 
			
		||||
        }
 | 
			
		||||
        stopLogger("slideshow_playforward");
 | 
			
		||||
        sleep(1);
 | 
			
		||||
 | 
			
		||||
        // scroll backward in slideshow mode
 | 
			
		||||
        startLogger("slideshow_playbackward");
 | 
			
		||||
        while (--slideIndex > 0) {
 | 
			
		||||
            uiDeviceSwipeHorizontal(leftEdge, rightEdge, yCoordinate, DEFAULT_SWIPE_STEPS);
 | 
			
		||||
            waitForProgress(WAIT_TIMEOUT_1SEC*5);
 | 
			
		||||
        }
 | 
			
		||||
        stopLogger("slideshow_playbackward");
 | 
			
		||||
        sleep(1);
 | 
			
		||||
 | 
			
		||||
        getUiDevice().pressBack();
 | 
			
		||||
        getUiDevice().pressBack();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected void startLogger(String name) throws Exception {
 | 
			
		||||
        logger = new ActionLogger(name, parameters);
 | 
			
		||||
        logger.start();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected void stopLogger(String name) throws Exception {
 | 
			
		||||
        logger.stop();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected boolean waitForProgress(int timeout) throws Exception {
 | 
			
		||||
        UiObject progress = new UiObject(new UiSelector().className(CLASS_PROGRESS_BAR));
 | 
			
		||||
        if (progress.waitForExists(WAIT_TIMEOUT_1SEC)) {
 | 
			
		||||
            return progress.waitUntilGone(timeout);
 | 
			
		||||
        } else {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private long changeAckTimeout(long newTimeout) {
 | 
			
		||||
        Configurator config = Configurator.getInstance();
 | 
			
		||||
        long oldTimeout = config.getActionAcknowledgmentTimeout();
 | 
			
		||||
        config.setActionAcknowledgmentTimeout(newTimeout);
 | 
			
		||||
        return oldTimeout;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void tapOpenArea() throws Exception {
 | 
			
		||||
        UiObject openArea = getUiObjectByResourceId(packageId + "punch_view_pager");
 | 
			
		||||
        Rect bounds = openArea.getVisibleBounds();
 | 
			
		||||
        // 10px from top of view, 10px from the right edge
 | 
			
		||||
        tapDisplay(bounds.right - 10, bounds.top + 10);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user