All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.simpligility.maven.plugins.android.standalonemojos.UIAutomatorMojo Maven / Gradle / Ivy

There is a newer version: 4.6.0
Show newest version
/*
 * Copyright (C) 2009 Jayway AB
 * Copyright (C) 2007-2008 JVending Masa
 *
 * 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.simpligility.maven.plugins.android.standalonemojos;

import java.io.File;
import java.io.IOException;

import org.apache.commons.lang3.StringUtils;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;

import com.android.ddmlib.AdbCommandRejectedException;
import com.android.ddmlib.IDevice;
import com.android.ddmlib.ShellCommandUnresponsiveException;
import com.android.ddmlib.TimeoutException;
import com.android.ddmlib.testrunner.UIAutomatorRemoteAndroidTestRunner;
import com.simpligility.maven.plugins.android.AbstractAndroidMojo;
import com.simpligility.maven.plugins.android.AndroidTestRunListener;
import com.simpligility.maven.plugins.android.DeviceCallback;
import com.simpligility.maven.plugins.android.ScreenshotServiceWrapper;
import com.simpligility.maven.plugins.android.common.DeviceHelper;
import com.simpligility.maven.plugins.android.config.ConfigHandler;
import com.simpligility.maven.plugins.android.config.ConfigPojo;
import com.simpligility.maven.plugins.android.config.PullParameter;
import com.simpligility.maven.plugins.android.configuration.UIAutomator;

/**
 * Can execute tests using ui uiautomator.
* Implements parsing parameters from pom or command line arguments and sets useful defaults as well. This goal is meant * to execute a special java project dedicated to UI testing via UIAutomator. It will build the jar of the * project, dex it and send it to dalvik cache of a rooted device or to an emulator. If you use a rooted device, refer * to this thread on stack over flow.
*
* The tests are executed via ui automator. A surefire compatible test report can be generated and its location will be * logged during build.
*
* To use this goal, you will need to place the uiautomator.jar file (part of the Android SDK >= 16) on a nexus * repository.
*
* A typical usage of this goal can be found at Quality tools for Android project. * * @see Android UI testing doc * @see UI Automator manual page * @author Stéphane Nicolas */ @SuppressWarnings( "unused" ) @Mojo( name = "uiautomator", requiresProject = false ) public class UIAutomatorMojo extends AbstractAndroidMojo { /** * -Dmaven.test.skip is commonly used with Maven to skip tests. We honor it. */ @Parameter( property = "maven.test.skip", defaultValue = "false", readonly = true ) private boolean mavenTestSkip; /** * -DskipTests is commonly used with Maven to skip tests. We honor it too. */ @Parameter( property = "skipTests", defaultValue = "false", readonly = true ) private boolean mavenSkipTests; /** * -Dmaven.test.failure.ignore is commonly used with Maven to prevent failure of build when (some) tests fail. We * honor it too. Builds will still fail if tests can't complete or throw an exception. */ @Parameter( property = "maven.test.failure.ignore", defaultValue = "false", readonly = true ) private boolean mavenTestFailureIgnore; /** * -Dmaven.test.failure.ignore is commonly used with Maven to prevent failure of build when (some) tests fail. We * honor it too. Builds will still fail if tests can't complete or throw an exception. */ @Parameter( property = "testFailureIgnore", defaultValue = "false", readonly = true ) private boolean mavenIgnoreTestFailure; /** * The configuration for the ui automator goal. As soon as a lint goal is invoked the command will be executed * unless the skip parameter is set. A minimal configuration that will run lint and produce a XML report in * ${project.build.directory}/lint/lint-results.xml is * *
     * <uiautomator>
     *   <skip>false</skip>
     * </uiautomator>
     * 
* * Full configuration can use these parameters. * *
     * <uiautomator>
     *   <skip>false</skip>
     *   <testClassOrMethods>
     *     <testClassOrMethod>com.foo.SampleTest</testClassOrMethod>
     *     <testClassOrMethod>com.bar.CalculatorTest#testCalculatorApp</testClassOrMethod>
     *   </testClassOrMethods>
     *   <createReport>true</createReport>
     *   <takeScreenshotOnFailure>true</takeScreenshotOnFailure>
     *   <screenshotsPathOnDevice>/sdcard/uiautomator-screenshots/</screenshotsPathOnDevice>
     *   <propertiesKeyPrefix>UIA</propertiesKeyPrefix>
     * </uiautomator>
     * 
* * * Alternatively to the plugin configuration values can also be configured as properties on the command line as * android.lint.* or in pom or settings file as properties like lint*. */ @Parameter @ConfigPojo private UIAutomator uiautomator; /** * Enables or disables uiautomator test goal. If true it will be skipped; if false, it * will be run. */ @Parameter( property = "android.uiautomator.skip" ) private Boolean uiautomatorSkip; @PullParameter( defaultValue = "true" ) private Boolean parsedSkip; /** * Jar file that will be run during ui uiautomator tests. */ @Parameter( property = "android.uiautomator.jarFile" ) private String uiautomatorJarFile; @PullParameter( defaultValueGetterMethod = "getJarFile" ) private String parsedJarFile; /** * Test class or methods to execute during uiautomator tests. Each class or method must be fully qualified with the * package name, in one of these formats: *
    *
  • package_name.class_name *
  • package_name.class_name#method_name *
*/ @Parameter( property = "android.uiautomator.testClassOrMethod" ) private String[] uiautomatorTestClassOrMethods; @PullParameter( required = false, defaultValueGetterMethod = "getTestClassOrMethods" ) private String[] parsedTestClassOrMethods; /** * Decides whether to run the test to completion on the device even if its parent process is terminated (for * example, if the device is disconnected). */ @Parameter( property = "android.uiautomator.noHup" ) private Boolean uiautomatorNoHup; @PullParameter( defaultValue = "false" ) private Boolean parsedNoHup; /** * Decides whether to wait for debugger to connect before starting. */ @Parameter( property = "android.uiautomator.debug" ) private Boolean uiautomatorDebug = false; @PullParameter( defaultValue = "false" ) private Boolean parsedDebug; /** * Decides whether to use a dump file or not. */ @Parameter( property = "android.uiautomator.useDump" ) private Boolean uiautomatorUseDump; @PullParameter( defaultValue = "false" ) private Boolean parsedUseDump; /** * Generate an XML file with a dump of the current UI hierarchy. If a filepath is not specified, by default, the * generated dump file is stored on the device in this location /storage/sdcard0/window_dump.xml. */ @Parameter( property = "android.uiautomator.dumpFilePath" ) private String uiautomatorDumpFilePath; @PullParameter( required = false, defaultValue = "/storage/sdcard0/window_dump.xml" ) private String parsedDumpFilePath; /** * Create a junit xml format compatible output file containing the test results for each device the instrumentation * tests run on.
*
* The files are stored in target/surefire-reports and named TEST-deviceid.xml. The deviceid for an emulator is * deviceSerialNumber_avdName_manufacturer_model. The serial number is commonly emulator-5554 for the first emulator * started with numbers increasing. avdName is as defined in the SDK tool. The manufacturer is typically "unknown" * and the model is typically "sdk".
* The deviceid for an actual devices is deviceSerialNumber_manufacturer_model.
*
* The file contains system properties from the system running the Android Maven Plugin (JVM) and device properties * from the device/emulator the tests are running on.
*
* The file contains a single TestSuite for all tests and a TestCase for each test method. Errors and failures are * logged in the file and the system log with full stack traces and other details available. */ @Parameter( property = "android.uiautomator.createReport" ) private Boolean uiautomatorCreateReport; @PullParameter( defaultValue = "false" ) private Boolean parsedCreateReport; /** * Adds a suffix to the report name. For example if parameter reportSuffix is "-mySpecialReport", * the name of the report will be TEST-deviceid-mySpecialReport.xml * * Defaults to null. Hence, in the default case, the name of the report will be TEST-deviceid.xml. */ @Parameter( property = "android.uiautomator.reportSuffix" ) private String uiautomatorReportSuffix; @PullParameter( required = false, defaultValueGetterMethod = "getReportSuffix" ) private String parsedReportSuffix; /** * Decides whether or not to take screenshots when tests execution results in failure or error. Screenshots use the * utiliy screencap that is usually available within emulator/devices with SDK >= 16. */ @Parameter( property = "android.uiautomator.takeScreenshotOnFailure" ) private Boolean uiautomatorTakeScreenshotOnFailure; @PullParameter( defaultValue = "false" ) private Boolean parsedTakeScreenshotOnFailure; /** * Location of the screenshots on device. This value is only taken into account if takeScreenshotOnFailure = true. * If a filepath is not specified, by default, the screenshots will be located at /sdcard/uiautomator-screenshots/. */ @Parameter( property = "android.uiautomator.screenshotsPathOnDevice" ) private String uiautomatorScreenshotsPathOnDevice; @PullParameter( required = false, defaultValue = "/sdcard/uiautomator-screenshots/" ) private String parsedScreenshotsPathOnDevice; /** *

Specifies a prefix for custom user properties that will be sent * through to UIAutomator with the "-e key value" parameter.

* *

If any user property is needed in a test case, this is the way to send it through. * User credentials for example.

* *

If no prefix value is specified no user property will be sent.

* *

Usage example:

*

<propertiesKeyPrefix>UIA</propertiesKeyPrefix>

*

And run it with:

*

> mvn <goal> "-DUIAkey=value"

*

would become "-e key value" as it would be runned from adb

*/ @Parameter( property = "android.uiautomator.propertiesKeyPrefix" ) private String uiautomatorPropertiesKeyPrefix; @PullParameter( required = false, defaultValueGetterMethod = "getPropertiesKeyPrefix" ) private String parsedPropertiesKeyPrefix; @Override public void execute() throws MojoExecutionException, MojoFailureException { ConfigHandler configHandler = new ConfigHandler( this, this.session, this.execution ); configHandler.parseConfiguration(); if ( isEnableIntegrationTest() ) { playTests(); } } /** * Whether or not tests are enabled. * * @return a boolean indicating whether or not tests are enabled. */ protected boolean isEnableIntegrationTest() { return !parsedSkip && !mavenTestSkip && !mavenSkipTests; } /** * Whether or not test failures should be ignored. * * @return a boolean indicating whether or not test failures should be ignored. */ protected boolean isIgnoreTestFailures() { return mavenIgnoreTestFailure || mavenTestFailureIgnore; } /** * Actually plays tests. * * @throws MojoExecutionException * if at least a test threw an exception and isIgnoreTestFailures is false.. * @throws MojoFailureException * if at least a test failed and isIgnoreTestFailures is false. */ protected void playTests() throws MojoExecutionException, MojoFailureException { getLog().debug( "Parsed values for Android UI UIAutomator invocation: " ); getLog().debug( "jarFile:" + parsedJarFile ); String testClassOrMethodString = buildSpaceSeparatedString( parsedTestClassOrMethods ); getLog().debug( "testClassOrMethod:" + testClassOrMethodString ); getLog().debug( "createReport:" + parsedCreateReport ); DeviceCallback instrumentationTestExecutor = new DeviceCallback() { @Override public void doWithDevice( final IDevice device ) throws MojoExecutionException, MojoFailureException { String deviceLogLinePrefix = DeviceHelper.getDeviceLogLinePrefix( device ); UIAutomatorRemoteAndroidTestRunner automatorRemoteAndroidTestRunner // = new UIAutomatorRemoteAndroidTestRunner( parsedJarFile, device ); automatorRemoteAndroidTestRunner.setRunName( "ui uiautomator tests" ); automatorRemoteAndroidTestRunner.setDebug( uiautomatorDebug ); automatorRemoteAndroidTestRunner.setTestClassOrMethods( parsedTestClassOrMethods ); automatorRemoteAndroidTestRunner.setNoHup( parsedNoHup ); automatorRemoteAndroidTestRunner.setUserProperties( session.getUserProperties(), parsedPropertiesKeyPrefix ); if ( parsedUseDump ) { automatorRemoteAndroidTestRunner.setDumpFilePath( parsedDumpFilePath ); } getLog().info( deviceLogLinePrefix + "Running ui uiautomator tests in" + parsedJarFile ); try { AndroidTestRunListener testRunListener = new AndroidTestRunListener( project, device, getLog(), parsedCreateReport, parsedTakeScreenshotOnFailure, parsedScreenshotsPathOnDevice, parsedReportSuffix, targetDirectory ); automatorRemoteAndroidTestRunner.run( testRunListener ); if ( testRunListener.hasFailuresOrErrors() && !isIgnoreTestFailures() ) { throw new MojoFailureException( deviceLogLinePrefix + "Tests failed on device." ); } if ( testRunListener.testRunFailed() ) { throw new MojoFailureException( deviceLogLinePrefix + "Test run failed to complete: " + testRunListener.getTestRunFailureCause() ); } if ( testRunListener.threwException() && !isIgnoreTestFailures() ) { throw new MojoFailureException( deviceLogLinePrefix + testRunListener.getExceptionMessages() ); } } catch ( TimeoutException e ) { throw new MojoExecutionException( deviceLogLinePrefix + "timeout", e ); } catch ( AdbCommandRejectedException e ) { throw new MojoExecutionException( deviceLogLinePrefix + "adb command rejected", e ); } catch ( ShellCommandUnresponsiveException e ) { throw new MojoExecutionException( deviceLogLinePrefix + "shell command " + "unresponsive", e ); } catch ( IOException e ) { throw new MojoExecutionException( deviceLogLinePrefix + "IO problem", e ); } } }; instrumentationTestExecutor = new ScreenshotServiceWrapper( instrumentationTestExecutor, project, getLog() ); doWithDevices( instrumentationTestExecutor ); } private String getJarFile() { if ( parsedJarFile == null ) { File jarFilePath = new File( targetDirectory + File.separator + finalName + ".jar" ); return jarFilePath.getName(); } return parsedJarFile; } private String[] getTestClassOrMethods() { // null if not overriden by configuration return parsedTestClassOrMethods; } private String getReportSuffix() { return parsedReportSuffix; } private String getPropertiesKeyPrefix() { return parsedPropertiesKeyPrefix; } /** * Helper method to build a comma separated string from a list. Blank strings are filtered out * * @param lines * A list of strings * @return Comma separated String from given list */ protected static String buildSpaceSeparatedString( String[] lines ) { if ( lines == null || lines.length == 0 ) { return null; } return StringUtils.join( lines, " " ); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy