diff --git a/wlauto/workloads/applaunch/__init__.py b/wlauto/workloads/applaunch/__init__.py new file mode 100644 index 00000000..19fcac96 --- /dev/null +++ b/wlauto/workloads/applaunch/__init__.py @@ -0,0 +1,169 @@ +# Copyright 2015 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. +# +# pylint: disable=attribute-defined-outside-init +import os + +from time import sleep + +from wlauto import Workload, AndroidBenchmark, AndroidUxPerfWorkload, UiAutomatorWorkload +from wlauto import Parameter +from wlauto import ExtensionLoader +from wlauto import File +from wlauto import settings +from wlauto.exceptions import ConfigError +from wlauto.exceptions import ResourceError +from wlauto.utils.android import ApkInfo +from wlauto.utils.uxperf import UxPerfParser + +import wlauto.common.android.resources + + +class Applaunch(AndroidUxPerfWorkload): + + name = 'applaunch' + description = ''' + This workload launches and measures the launch time of applications for supporting workloads. + + Currently supported workloads are the ones that implement ``ApplaunchInterface``. For any + workload to support this workload, it should implement the ``ApplaunchInterface``. + The corresponding java file of the workload associated with the application being measured + is executed during the run. The application that needs to be + measured is passed as a parametre ``workload_name``. The parameters required for that workload + have to be passed as a dictionary which is captured by the parametre ``workload_params``. + This information can be obtained by inspecting the workload details of the specific workload. + + The workload allows to run multiple iterations of an application + launch in two modes: + + 1. Launch from background + 2. Launch from long-idle + + These modes are captured as a parameter applaunch_type. + + ``launch_from_background`` + Launches an application after the application is sent to background by + pressing Home button. + + ``launch_from_long-idle`` + Launches an application after killing an application process and + clearing all the caches. + + **Test Description:** + + - During the initialization and setup, the application being launched is launched + for the first time. The jar file of the workload of the application + is moved to device at the location ``workdir`` which further implements the methods + needed to measure the application launch time. + + - Run phase calls the UiAutomator of the applaunch which runs in two subphases. + A. Applaunch Setup Run: + During this phase, welcome screens and dialogues during the first launch + of the instrumented application are cleared. + B. Applaunch Metric Run: + During this phase, the application is launched multiple times determined by + the iteration number specified by the parametre ``applaunch_iterations``. + Each of these iterations are instrumented to capture the launch time taken + and the values are recorded as UXPERF marker values in logfile. + ''' + supported_platforms = ['android'] + + parameters = [ + Parameter('workload_name', kind=str, + description='Name of the uxperf workload to launch', + default='gmail'), + Parameter('workload_params', kind=dict, default={}, + description=""" + parameters of the uxperf workload whose application launch + time is measured + """), + Parameter('applaunch_type', kind=str, default='launch_from_background', + allowed_values=['launch_from_background', 'launch_from_long-idle'], + description=""" + Choose launch_from_long-idle for measuring launch time + from long-idle. These two types are described in the class + description. + """), + Parameter('applaunch_iterations', kind=int, default=1, + description=""" + Number of iterations of the application launch + """), + Parameter('report_results', kind=bool, default=True, + description=""" + Choose to report results of the application launch time. + """), + ] + + def __init__(self, device, **kwargs): + super(Applaunch, self).__init__(device, **kwargs) + + def init_resources(self, context): + super(Applaunch, self).init_resources(context) + loader = ExtensionLoader(packages=settings.extension_packages, paths=settings.extension_paths) + self.workload_params['markers_enabled'] = True + self.workload = loader.get_workload(self.workload_name, self.device, + **self.workload_params) + self.init_workload_resources(context) + + def init_workload_resources(self, context): + self.workload.uiauto_file = context.resolver.get(wlauto.common.android.resources.JarFile(self.workload)) + if not self.workload.uiauto_file: + raise ResourceError('No UI automation JAR file found for workload {}.'.format(self.workload.name)) + self.workload.device_uiauto_file = self.device.path.join(self.device.working_directory, os.path.basename(self.workload.uiauto_file)) + if not self.workload.uiauto_package: + self.workload.uiauto_package = os.path.splitext(os.path.basename(self.workload.uiauto_file))[0] + + def validate(self): + super(Applaunch, self).validate() + self.workload.validate() + self.pass_parameters() + + def pass_parameters(self): + self.uiauto_params['workload'] = self.workload.name + self.uiauto_params['package'] = self.workload.package + self.uiauto_params['binaries_directory'] = self.device.binaries_directory + self.uiauto_params.update(self.workload.uiauto_params) + if self.workload.activity: + self.uiauto_params['launch_activity'] = self.workload.activity + else: + self.uiauto_params['launch_activity'] = "None" + self.uiauto_params['applaunch_type'] = self.applaunch_type + self.uiauto_params['applaunch_iterations'] = self.applaunch_iterations + + def setup(self, context): + AndroidBenchmark.setup(self.workload, context) + if not self.workload.launch_main: + self.workload.launch_app() + UiAutomatorWorkload.setup(self, context) + self.workload.device.push_file(self.workload.uiauto_file, self.workload.device_uiauto_file) + + def run(self, context): + UiAutomatorWorkload.run(self, context) + + def update_result(self, context): + super(Applaunch, self).update_result(context) + if self.report_results: + parser = UxPerfParser(context, prefix='applaunch_') + logfile = os.path.join(context.output_directory, 'logcat.log') + parser.parse(logfile) + parser.add_action_timings() + + def teardown(self, context): + super(Applaunch, self).teardown(context) + AndroidBenchmark.teardown(self.workload, context) + UiAutomatorWorkload.teardown(self.workload, context) + #Workload uses Dexclass loader while loading the jar file of the instrumented workload. + #Dexclassloader unzips and generates .dex file in the .jar directory during the run. + device_uiauto_dex_file = self.workload.device_uiauto_file.replace(".jar", ".dex") + self.workload.device.delete_file(self.device.path.join(self.device.binaries_directory, device_uiauto_dex_file)) diff --git a/wlauto/workloads/applaunch/com.arm.wlauto.uiauto.applaunch.jar b/wlauto/workloads/applaunch/com.arm.wlauto.uiauto.applaunch.jar new file mode 100644 index 00000000..c10edd6f Binary files /dev/null and b/wlauto/workloads/applaunch/com.arm.wlauto.uiauto.applaunch.jar differ diff --git a/wlauto/workloads/applaunch/uiauto/build.sh b/wlauto/workloads/applaunch/uiauto/build.sh new file mode 100755 index 00000000..1c5233c5 --- /dev/null +++ b/wlauto/workloads/applaunch/uiauto/build.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +# CD into build dir if possible - allows building from any directory +script_path='.' +if `readlink -f $0 &>/dev/null`; then + script_path=`readlink -f $0 2>/dev/null` +fi +script_dir=`dirname $script_path` +cd $script_dir + +# Ensure build.xml exists before starting +if [[ ! -f build.xml ]]; then + echo 'Ant build.xml file not found! Check that you are in the right directory.' + exit 9 +fi + +# Copy base classes from wlauto dist +class_dir=bin/classes/com/arm/wlauto/uiauto +base_classes=`python -c "import os, wlauto; print os.path.join(os.path.dirname(wlauto.__file__), 'common', 'android', '*.class')"` +mkdir -p $class_dir +cp $base_classes $class_dir + +# Build and return appropriate exit code if failed +ant build +exit_code=$? +if [[ $exit_code -ne 0 ]]; then + echo "ERROR: 'ant build' exited with code $exit_code" + exit $exit_code +fi + +# If successful move JAR file to workload folder (overwrite previous) +package=com.arm.wlauto.uiauto.applaunch.jar +rm -f ../$package +if [[ -f bin/$package ]]; then + cp bin/$package .. +else + echo 'ERROR: UiAutomator JAR could not be found!' + exit 9 +fi diff --git a/wlauto/workloads/applaunch/uiauto/build.xml b/wlauto/workloads/applaunch/uiauto/build.xml new file mode 100644 index 00000000..b4e76972 --- /dev/null +++ b/wlauto/workloads/applaunch/uiauto/build.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wlauto/workloads/applaunch/uiauto/project.properties b/wlauto/workloads/applaunch/uiauto/project.properties new file mode 100644 index 00000000..ce39f2d0 --- /dev/null +++ b/wlauto/workloads/applaunch/uiauto/project.properties @@ -0,0 +1,14 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-18 diff --git a/wlauto/workloads/applaunch/uiauto/src/com/arm/wlauto/uiauto/UiAutomation.java b/wlauto/workloads/applaunch/uiauto/src/com/arm/wlauto/uiauto/UiAutomation.java new file mode 100755 index 00000000..b0e9f80f --- /dev/null +++ b/wlauto/workloads/applaunch/uiauto/src/com/arm/wlauto/uiauto/UiAutomation.java @@ -0,0 +1,220 @@ +/* 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.applaunch; + +import android.os.Bundle; +import android.util.Log; + +// 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.ApplaunchInterface; +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; + +import java.util.concurrent.TimeUnit; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.io.File; +import java.io.FileNotFoundException; +import java.util.Map.Entry; +import dalvik.system.DexClassLoader; +import java.lang.reflect.Method; + + +public class UiAutomation extends UxPerfUiAutomation { + + /** + * Uiobject that marks the end of launch of an application, which is workload + * specific and added in the workload Java file by a method called getLaunchEndObject(). + */ + public UiObject launchEndObject; + /** Timeout to wait for application launch to finish. */ + private Integer launch_timeout = 10; + public String applaunchType; + public String applaunchIterations; + public String activityName; + public ApplaunchInterface launch_workload; + + /** Uiautomator function called by the applaunch workload. */ + public void runUiAutomation() throws Exception{ + parameters = getParams(); + + // Get workload jar file parameters + String workload = parameters.getString("workload"); + String binariesDirectory = parameters.getString("binaries_directory"); + String workloadJarPath = parameters.getString("workdir"); + String workloadJarName = String.format("com.arm.wlauto.uiauto.%1s.jar",workload); + String workloadJarFile = String.format("%1s/%2s",workloadJarPath, workloadJarName); + + // Load the jar file + File jarFile = new File(workloadJarFile); + if(!jarFile.exists()) { + throw new Exception(String.format("Jar file not found: %s", workloadJarFile)); + } + DexClassLoader classloader = new DexClassLoader(jarFile.toURI().toURL().toString(), + binariesDirectory, null, ClassLoader.getSystemClassLoader()); + Class uiautomation = null; + Object uiautomation_interface = null; + String workloadClass = String.format("com.arm.wlauto.uiauto.%1s.UiAutomation",workload); + try { + uiautomation = classloader.loadClass(workloadClass); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + Log.d("Class loaded:", uiautomation.getCanonicalName()); + uiautomation_interface = uiautomation.newInstance(); + + // Create an Application Interface object from the workload + launch_workload = ((ApplaunchInterface)uiautomation_interface); + + // Get parameters for application launch + getPackageParameters(); + applaunchType = parameters.getString("applaunch_type"); + applaunchIterations = parameters.getString("applaunch_iterations"); + activityName = parameters.getString("launch_activity"); + + // Run the workload for application launch initialization + runApplaunchSetup(); + + // Run the workload for application launch measurement + for (int iteration = 0; iteration < Integer.parseInt(applaunchIterations); iteration++) { + Log.d("Applaunch iteration number: ", applaunchIterations); + sleep(20);//sleep for a while before next iteration + killBackground(); + runApplaunchIteration(iteration); + closeApplication(); + } + } + + /** + * Setup run for applaunch workload that clears the initial + * run dialogues on launching an application package. + */ + public void runApplaunchSetup() throws Exception{ + setScreenOrientation(ScreenOrientation.NATURAL); + launch_workload.setWorkloadParameters(parameters); + launch_workload.runApplicationInitialization(); + launchEndObject = launch_workload.getLaunchEndObject(); + unsetScreenOrientation(); + closeApplication(); + } + + /** + * This method performs multiple iterations of application launch and + * records the time taken for each iteration. + */ + public void runApplaunchIteration(Integer iteration_count) throws Exception{ + String testTag = "applaunch" + iteration_count; + String launchCommand = launch_workload.getLaunchCommand(); + AppLaunch applaunch = new AppLaunch(testTag, launchCommand); + applaunch.startLaunch();//Launch the application and start timer + applaunch.endLaunch();//marks the end of launch and stops timer + } + + /* + * AppLaunch class implements methods that facilitates launching applications + * from the uiautomator. It has methods that are used for one complete iteration of application + * launch instrumentation. + * ActionLogger class is instantiated within the class for measuring applaunch time. + * startLaunch(): Marks the beginning of the application launch, starts Timer + * endLaunch(): Marks the end of application, ends Timer + * launchMain(): Starts the application launch process and validates the finish of launch. + */ + private class AppLaunch { + + private String testTag; + private String launchCommand; + private ActionLogger logger; + Process launch_p; + + public AppLaunch(String testTag, String launchCommand) { + this.testTag = testTag; + this.launchCommand = launchCommand; + this.logger = new ActionLogger(testTag, parameters); + } + + // Called by launchMain() to check if app launch is successful + public void launchValidate(Process launch_p) throws Exception { + launch_p.waitFor(); + Integer exit_val = launch_p.exitValue(); + if (exit_val != 0) { + throw new Exception("Application could not be launched"); + } + } + + // Marks the end of application launch of the workload. + public void endLaunch() throws Exception{ + waitObject(launchEndObject, launch_timeout); + logger.stop(); + launch_p.destroy(); + } + + // Launches the application. + public void launchMain() throws Exception{ + launch_p = Runtime.getRuntime().exec(launchCommand); + + launchValidate(launch_p); + } + + // Beginning of application launch + public void startLaunch() throws Exception{ + logger.start(); + launchMain(); + } + } + + // Exits the application according to application launch type. + public void closeApplication() throws Exception{ + if(applaunchType.equals("launch_from_background")) { + pressHome(); + } + else if(applaunchType.equals("launch_from_long-idle")) { + killApplication(); + dropCaches(); + } + } + + // Kills the application process + public void killApplication() throws Exception{ + Process kill_p; + kill_p = Runtime.getRuntime().exec(String.format("am force-stop %s", packageName)); + kill_p.waitFor(); + kill_p.destroy(); + } + + // Kills the background processes + public void killBackground() throws Exception{ + Process kill_p; + kill_p = Runtime.getRuntime().exec("am kill-all"); + kill_p.waitFor(); + kill_p.destroy(); + } + + // Drop the caches + public void dropCaches() throws Exception{ + Process drop_cache; + drop_cache = Runtime.getRuntime().exec("su sync; su echo 3 > /proc/sys/vm/drop_caches"); + drop_cache.waitFor(); + drop_cache.destroy(); + } +}