Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #193 from stephanenicolas/master

Add support to automatically take screenshots in case of test failure / error for UIAutomator.
  • Loading branch information...
commit b7bf732a80ca76cd468f61972939a412c8ac4a17 2 parents 4aa3a8f + b329a09
@mosabua mosabua authored
View
5 pom.xml
@@ -252,6 +252,11 @@
<artifactId>android-screenshot-paparazzo</artifactId>
<version>1.8</version>
</dependency>
+ <dependency>
+ <groupId>com.github.rtyley</groupId>
+ <artifactId>android-screenshot-celebrity</artifactId>
+ <version>1.8</version>
+ </dependency>
</dependencies>
<build>
<pluginManagement>
View
17 src/main/java/com/jayway/maven/plugins/android/configuration/UIAutomator.java
@@ -40,6 +40,14 @@
* Mirror of {@link com.jayway.maven.plugins.android.standalonemojos.UIAutomatorMojo#createReport}
*/
private Boolean createReport;
+ /**
+ * Mirror of {@link com.jayway.maven.plugins.android.standalonemojos.UIAutomatorMojo#takeScreenshotOnFailure}
+ */
+ private Boolean takeScreenshotOnFailure;
+ /**
+ * Mirror of {@link com.jayway.maven.plugins.android.standalonemojos.UIAutomatorMojo#screenshotsPathOnDevice}
+ */
+ private String screenshotsPathOnDevice;
public Boolean isSkip()
{
@@ -81,4 +89,13 @@ public Boolean isCreateReport()
return createReport;
}
+ public Boolean isTakeScreenshotOnFailure()
+ {
+ return takeScreenshotOnFailure;
+ }
+
+ public String getScreenshotsPathOnDevice()
+ {
+ return screenshotsPathOnDevice;
+ }
}
View
123 src/main/java/com/jayway/maven/plugins/android/standalonemojos/UIAutomatorMojo.java
@@ -51,6 +51,7 @@
import com.android.ddmlib.AdbCommandRejectedException;
import com.android.ddmlib.IDevice;
+import com.android.ddmlib.IShellOutputReceiver;
import com.android.ddmlib.ShellCommandUnresponsiveException;
import com.android.ddmlib.TimeoutException;
import com.android.ddmlib.testrunner.ITestRunListener;
@@ -75,7 +76,7 @@
* The tests are executed via ui automator. A surefire compatible test report can be generated and its location will be
* logged during build. <br />
* <br />
- * To use this goal, you will need to place the uiautomator.jar file (part of the Android SDK > 16) on a nexus
+ * To use this goal, you will need to place the uiautomator.jar file (part of the Android SDK >= 16) on a nexus
* repository. <br />
* <br />
* A typical usage of this goal can be found at <a
@@ -144,6 +145,8 @@
* &lt;testClassOrMethod&gt;com.bar.CalculatorTest#testCalculatorApp&lt;/testClassOrMethod&gt;
* &lt;/testClassOrMethods&gt;
* &lt;createReport&gt;true&lt;/createReport&gt;
+ * &lt;takeScreenshotOnFailure&gt;true&lt;/takeScreenshotOnFailure&gt;
+ * &lt;screenshotsPathOnDevice&gt;/sdcard/uiautomator-screenshots/&lt;/screenshotsPathOnDevice&gt;
* &lt;/uiautomator&gt;
* </pre>
*
@@ -266,6 +269,30 @@
@PullParameter( defaultValue = "false" )
private Boolean parsedCreateReport;
+ /**
+ * 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 expression="${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 expression="${android.uiautomator.screenshotsPathOnDevice}"
+ *
+ */
+ private String uiautomatorScreenshotsPathOnDevice;
+
+ @PullParameter( required = false, defaultValue = "/sdcard/uiautomator-screenshots/" )
+ private String parsedScreenshotsPathOnDevice;
+
@Override
public void execute() throws MojoExecutionException, MojoFailureException
{
@@ -278,16 +305,34 @@ public void execute() throws MojoExecutionException, MojoFailureException
}
}
+ /**
+ * 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
{
@@ -331,7 +376,7 @@ public void doWithDevice( final IDevice device ) throws MojoExecutionException,
throw new MojoFailureException( deviceLogLinePrefix + "Test run failed to complete: "
+ testRunListener.getTestRunFailureCause() );
}
- if ( testRunListener.threwException() )
+ if ( testRunListener.threwException() && !isIgnoreTestFailures() )
{
throw new MojoFailureException( deviceLogLinePrefix + testRunListener.getExceptionMessages() );
}
@@ -402,6 +447,8 @@ protected static String buildSpaceSeparatedString( String[] lines )
*/
public class AndroidTestRunListener implements ITestRunListener
{
+ private static final String SCREENSHOT_SUFFIX = "_screenshot.png";
+
/**
* the indent used in the log to group items that belong together visually *
*/
@@ -477,6 +524,14 @@ protected static String buildSpaceSeparatedString( String[] lines )
private boolean threwException = false;
private final StringBuilder exceptionMessages = new StringBuilder();
+ /**
+ * Create a new test run listener.
+ *
+ * @param project
+ * the test project.
+ * @param device
+ * the device on which test is executed.
+ */
public AndroidTestRunListener( MavenProject project, IDevice device )
{
this.project = project;
@@ -485,9 +540,15 @@ public AndroidTestRunListener( MavenProject project, IDevice device )
}
@Override
- public void testRunStarted( String runName, int testCount )
+ public void testRunStarted( String runName, int tCount )
{
- this.testCount = testCount;
+ if ( parsedTakeScreenshotOnFailure )
+ {
+ executeOnAdbShell( "rm -f " + parsedScreenshotsPathOnDevice + "/*screenshot.png" );
+ executeOnAdbShell( "mkdir " + parsedScreenshotsPathOnDevice );
+ }
+
+ this.testCount = tCount;
getLog().info( deviceLogLinePrefix + INDENT + "Run started: " + runName + ", " + testCount + " tests:" );
if ( parsedCreateReport )
@@ -548,7 +609,6 @@ public void testRunStarted( String runName, int testCount )
exceptionMessages.append( e.getMessage() );
}
}
-
}
@Override
@@ -577,6 +637,15 @@ public void testStarted( TestIdentifier testIdentifier )
@Override
public void testFailed( TestFailure status, TestIdentifier testIdentifier, String trace )
{
+ if ( parsedTakeScreenshotOnFailure )
+ {
+ String suffix = status == ERROR ? "_error" : "_failure";
+ String filepath = testIdentifier.getTestName() + suffix + SCREENSHOT_SUFFIX;
+
+ executeOnAdbShell( "screencap -p " + parsedScreenshotsPathOnDevice + "/" + filepath );
+ getLog().info( deviceLogLinePrefix + INDENT + INDENT + filepath + " saved." );
+ }
+
if ( status == ERROR )
{
++testErrorCount;
@@ -613,6 +682,47 @@ public void testFailed( TestFailure status, TestIdentifier testIdentifier, Strin
}
}
+ private void executeOnAdbShell( String command )
+ {
+ try
+ {
+ device.executeShellCommand( command, new IShellOutputReceiver()
+ {
+ @Override
+ public boolean isCancelled()
+ {
+ return false;
+ }
+
+ @Override
+ public void flush()
+ {
+ }
+
+ @Override
+ public void addOutput( byte[] data, int offset, int length )
+ {
+ }
+ } );
+ }
+ catch ( TimeoutException e )
+ {
+ getLog().error( e );
+ }
+ catch ( AdbCommandRejectedException e )
+ {
+ getLog().error( e );
+ }
+ catch ( ShellCommandUnresponsiveException e )
+ {
+ getLog().error( e );
+ }
+ catch ( IOException e )
+ {
+ getLog().error( e );
+ }
+ }
+
@Override
public void testEnded( TestIdentifier testIdentifier, Map< String, String > testMetrics )
{
@@ -830,6 +940,9 @@ public boolean testRunFailed()
return testRunFailureCause != null;
}
+ /**
+ * @return the cause of test failure if any.
+ */
public String getTestRunFailureCause()
{
return testRunFailureCause;
View
12 src/test/java/com/jayway/maven/plugins/android/standalonemojos/UIAutomatorMojoTest.java
@@ -85,6 +85,8 @@ public void testDefaultUnskippedUIAutomatorConfig() throws Exception
Boolean automatorDebug = Whitebox.getInternalState( mojo, "parsedDebug" );
String automatorJarFile = Whitebox.getInternalState( mojo, "parsedJarFile" );
String[] automatorTestClassOrMethods = Whitebox.getInternalState( mojo, "parsedTestClassOrMethods" );
+ Boolean automatorTakeScreenshotOnFailure = Whitebox.getInternalState( mojo, "parsedTakeScreenshotOnFailure" );
+ String automatorScreenshotsPathOnDevice = Whitebox.getInternalState( mojo, "parsedScreenshotsPathOnDevice" );
assertFalse( "UIAutomator skip parameter should be false", automatorSkip );
assertFalse( "UIAutomator debug parameter should be false", automatorDebug );
@@ -93,6 +95,10 @@ public void testDefaultUnskippedUIAutomatorConfig() throws Exception
assertEquals( "UIAutomator jarFile parameter should be equal to artifact name", expectedJarFile,
automatorJarFile );
assertNull( "UIAutomator testClassOrMethods parameter should be null", automatorTestClassOrMethods );
+ assertFalse( "UIAutomator takeScreenshotOnFailure parameter should be equal false",
+ automatorTakeScreenshotOnFailure );
+ assertEquals( "UIAutomator screenshotsPath on device be equal /sdcard/uiautomator-screenshots/",
+ "/sdcard/uiautomator-screenshots/", automatorScreenshotsPathOnDevice );
}
/**
@@ -127,6 +133,8 @@ public void testCustomUIAutomatorConfig() throws Exception
Boolean automatorDebug = Whitebox.getInternalState( mojo, "parsedDebug" );
String automatorJarFile = Whitebox.getInternalState( mojo, "parsedJarFile" );
String[] automatorTestClassOrMethods = Whitebox.getInternalState( mojo, "parsedTestClassOrMethods" );
+ Boolean automatorTakeScreenshotOnFailure = Whitebox.getInternalState( mojo, "parsedTakeScreenshotOnFailure" );
+ String automatorScreenshotsPathOnDevice = Whitebox.getInternalState( mojo, "parsedScreenshotsPathOnDevice" );
assertFalse( "UIAutomator skip parameter should be false", automatorSkip );
assertFalse( "UIAutomator debug parameter should be false", automatorDebug );
@@ -140,6 +148,10 @@ public void testCustomUIAutomatorConfig() throws Exception
{ "a", "b#c" };
assertTrue( "UIAutomator testClassOrMethods parameter should be equal [a,b#c]",
Arrays.equals( expectedTestClassOrMethods, automatorTestClassOrMethods ) );
+ assertTrue( "UIAutomator takeScreenshotOnFailure parameter should be equal true",
+ automatorTakeScreenshotOnFailure );
+ assertEquals( "UIAutomator screenshotsPath on device be equal /mnt/sdcard/screenshots/",
+ "/mnt/sdcard/screenshots/", automatorScreenshotsPathOnDevice );
}
}
View
2  src/test/resources/ui-automator-config-project2/plugin-config.xml
@@ -18,6 +18,8 @@
<testClassOrMethod>b#c</testClassOrMethod>
</testClassOrMethods>
<createReport>true</createReport>
+ <takeScreenshotOnFailure>true</takeScreenshotOnFailure>
+ <screenshotsPathOnDevice>/mnt/sdcard/screenshots/</screenshotsPathOnDevice>
</uiautomator>
</configuration>
</plugin>
View
30 src/test/resources/ui-automator-config-project3/plugin-config.xml
@@ -1,30 +0,0 @@
-<?xml version="1.0"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>com.jayway.maven.plugins.android.tests</groupId>
- <artifactId>ui-autmator-config-project3</artifactId>
- <version>15.4.3.1011</version>
-
- <properties>
- <maven.
- </properties>
- <build>
- <plugins>
- <plugin>
- <artifactId>android-maven-plugin</artifactId>
- <!-- no ui-automator config -->
- <configuration>
- <uiautomator>
- <skip>false</skip>
- <testClassOrMethods>
- <testClassOrMethod>a</testClassOrMethod>
- <testClassOrMethod>b#c</testClassOrMethod>
- </testClassOrMethods>
- <createReport>true</createReport>
- </uiautomator>
- </configuration>
- </plugin>
- </plugins>
- </build>
-</project>
Please sign in to comment.
Something went wrong with that request. Please try again.