/*
* ###
* Android Maven Plugin - android-maven-plugin
*
* Copyright (C) 1999 - 2012 Photon Infotech Inc.
*
* 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.
* ###
*/
/*
* Copyright (C) 2009 Jayway AB
*
* 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.photon.maven.plugins.android.standalonemojos;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import com.android.ddmlib.AdbCommandRejectedException;
import com.android.ddmlib.AndroidDebugBridge;
import com.android.ddmlib.IDevice;
import com.android.ddmlib.ShellCommandUnresponsiveException;
import com.android.ddmlib.TimeoutException;
import com.android.ddmlib.testrunner.IRemoteAndroidTestRunner;
import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
import com.photon.maven.plugins.android.AbstractInstrumentationMojo;
import com.photon.maven.plugins.android.DeviceCallback;
import com.photon.maven.plugins.android.common.DeviceHelper;
/**
* MultiDeviceExecutorMojo helps to connect to all selected android devices
* within separate threads, install the apk file on selected devices and start
* the application.
*
* @goal perftest
* @author viral_b
*/
public class RobotiumMultiDeviceExecutorMojo extends
AbstractInstrumentationMojo {
/**
* @parameter expression="${deviceList}"
*/
private String deviceList = "";
private String[] devicesListArr = null;
private ArrayList<Thread> threadObj = new ArrayList<Thread>();
private AndroidTestRunListener testRunListener;
private ArrayList<String> xmlReportNameForConnectedDevices;
public void execute() throws MojoExecutionException, MojoFailureException {
checkDeviceList();
// deployDependencies();
instrument();
joinThreads();
testRunListener.writeJunitReportToAllTestsFile(xmlReportNameForConnectedDevices);
};
/**
* Check the deviceList parameter
*/
private void checkDeviceList() {
try {
if (StringUtils.isNotBlank(deviceList)) {
if (StringUtils.indexOf(deviceList, ",") > -1) {
devicesListArr = deviceList.split(",");
} else {
devicesListArr = new String[1];
devicesListArr[0] = deviceList.trim();
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
getLog().info("checkDeviceList - Exception: " + e.getMessage());
}
}
protected void instrument() throws MojoExecutionException,
MojoFailureException {
parseConfiguration();
if (parsedInstrumentationPackage == null) {
parsedInstrumentationPackage = extractPackageNameFromAndroidManifest(androidManifestFile);
}
if (parsedInstrumentationRunner == null) {
parsedInstrumentationRunner = extractInstrumentationRunnerFromAndroidManifest(androidManifestFile);
}
// only run Tests in specific package
packagesList = buildCommaSeparatedString(parsedPackages);
packagesExists = StringUtils.isNotBlank(packagesList);
if (parsedClasses != null) {
classesExists = parsedClasses.size() > 0;
} else {
classesExists = false;
}
if (classesExists && packagesExists) {
// if both packages and classes are specified --> ERROR
throw new MojoFailureException(
"packages and classes are mutually exclusive. They cannot be specified at "
+ "the same time. Please specify either packages or classes. For details, "
+ "see http://developer.android.com/guide/developing/testing/testing_otheride.html");
}
doWithDevices(new DeviceCallback() {
public void doWithDevice(final IDevice device)
throws MojoExecutionException, MojoFailureException {
RemoteAndroidTestRunner remoteAndroidTestRunner = new RemoteAndroidTestRunner(
parsedInstrumentationPackage,
parsedInstrumentationRunner, device);
if (packagesExists) {
remoteAndroidTestRunner.setTestPackageName(packagesList);
getLog().info(
"Running tests for specified test packages: "
+ packagesList);
}
if (classesExists) {
remoteAndroidTestRunner.setClassNames(parsedClasses
.toArray(new String[parsedClasses.size()]));
getLog().info(
"Running tests for specified test classes/methods: "
+ parsedClasses);
}
remoteAndroidTestRunner.setDebug(parsedDebug);
remoteAndroidTestRunner.setCoverage(parsedCoverage);
remoteAndroidTestRunner.setLogOnly(parsedLogOnly);
if (StringUtils.isNotBlank(parsedTestSize)) {
IRemoteAndroidTestRunner.TestSize validSize = IRemoteAndroidTestRunner.TestSize
.getTestSize(parsedTestSize);
remoteAndroidTestRunner.setTestSize(validSize);
}
getLog().info(
"Running instrumentation tests in "
+ parsedInstrumentationPackage + " on "
+ device.getSerialNumber() + " (avdName="
+ device.getAvdName() + ")");
try {
testRunListener = new AndroidTestRunListener(
project, device);
remoteAndroidTestRunner.run(testRunListener);
if (testRunListener.hasFailuresOrErrors()) {
throw new MojoFailureException(
"Tests failed on device.");
}
if (testRunListener.testRunFailed()) {
throw new MojoFailureException(
"Test run failed to complete: "
+ testRunListener
.getTestRunFailureCause());
}
if (testRunListener.threwException()) {
throw new MojoFailureException(
testRunListener.getExceptionMessages());
}
} catch (TimeoutException e) {
throw new MojoExecutionException("timeout", e);
} catch (AdbCommandRejectedException e) {
throw new MojoExecutionException("adb command rejected", e);
} catch (ShellCommandUnresponsiveException e) {
throw new MojoExecutionException("shell command "
+ "unresponsive", e);
} catch (IOException e) {
throw new MojoExecutionException("IO problem", e);
}
}
});
}
protected void doWithDevices(final DeviceCallback deviceCallback)
throws MojoExecutionException, MojoFailureException {
final AndroidDebugBridge androidDebugBridge = initAndroidDebugBridge();
if (androidDebugBridge.isConnected()) {
waitForInitialDeviceList(androidDebugBridge);
List<IDevice> devices = Arrays.asList(androidDebugBridge
.getDevices());
int numberOfDevices = devices.size();
getLog().info(
"Found "
+ numberOfDevices
+ " devices connected with the Android Debug Bridge");
if (devices.size() > 0) {
getLog().info(
"android.device parameter not set, using all attached devices");
for (final IDevice idevice : devices) {
for (int i = 0; i < devicesListArr.length; i++) {
if (devicesListArr[i].equalsIgnoreCase(idevice
.getSerialNumber())) {
if (xmlReportNameForConnectedDevices == null) {
xmlReportNameForConnectedDevices = new ArrayList<String>();
}
xmlReportNameForConnectedDevices.add("TEST-"+DeviceHelper.getDescriptiveName(idevice)+".xml");
startThread(deviceCallback, idevice);
break;
}
}
}
} else {
throw new MojoExecutionException("No online devices attached.");
}
} else {
throw new MojoExecutionException(
"Android Debug Bridge is not connected.");
}
}
private void startThread(final DeviceCallback deviceCallback,
final IDevice idevice) {
try {
Thread t = new RobitiumPerformanceTestRunner(deviceCallback,
idevice);
t.setName(idevice.getSerialNumber());
t.start();
threadObj.add(t);
// getLog().info(t.getName() + ": thread started");
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
}
private void joinThreads() {
for (int i = 0; i < threadObj.size(); i++) {
try {
threadObj.get(i).join();
// getLog().info(threadObj.get(i).getName() + ": thread joined");
} catch (InterruptedException e) {
getLog().info(
threadObj.get(i).getName()
+ ": joinThreads - Exception = "
+ e.getLocalizedMessage());
}
}
}
private class RobitiumPerformanceTestRunner extends Thread {
private DeviceCallback _deviceCallback;
private IDevice _iDevice;
public RobitiumPerformanceTestRunner(DeviceCallback deviceCallback,
IDevice idevice) {
this._deviceCallback = deviceCallback;
this._iDevice = idevice;
}
public void run() {
try {
_deviceCallback.doWithDevice(_iDevice);
} catch (MojoExecutionException e) {
e.printStackTrace();
} catch (MojoFailureException e) {
e.printStackTrace();
}
}
}
}