blob: c773ab9ba9dca29b70832dfe96ebdb8d461c9940 [file] [log] [blame]
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
*
* 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.android.ide.eclipse.adt.internal.launch.junit;
import com.android.ddmlib.IDevice;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.launch.DelayedLaunchInfo;
import com.android.ide.eclipse.adt.internal.launch.IAndroidLaunchAction;
import com.android.ide.eclipse.adt.internal.launch.LaunchMessages;
import com.android.ide.eclipse.adt.internal.launch.junit.runtime.AndroidJUnitLaunchInfo;
import com.android.ide.eclipse.adt.internal.launch.junit.runtime.RemoteAdtTestRunner;
import com.google.common.base.Joiner;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.core.model.IStreamsProxy;
import org.eclipse.jdt.junit.launcher.JUnitLaunchConfigurationDelegate;
import org.eclipse.jdt.launching.IVMRunner;
import org.eclipse.jdt.launching.VMRunnerConfiguration;
import org.eclipse.swt.widgets.Display;
import java.util.Collection;
/**
* A launch action that executes a instrumentation test run on an Android device.
*/
class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
private static final Joiner JOINER = Joiner.on(',').skipNulls();
private final AndroidJUnitLaunchInfo mLaunchInfo;
/**
* Creates a AndroidJUnitLaunchAction.
*
* @param launchInfo the {@link AndroidJUnitLaunchInfo} for the JUnit run
*/
public AndroidJUnitLaunchAction(AndroidJUnitLaunchInfo launchInfo) {
mLaunchInfo = launchInfo;
}
/**
* Launch a instrumentation test run on given Android devices.
* Reuses JDT JUnit launch delegate so results can be communicated back to JDT JUnit UI.
* <p/>
* Note: Must be executed on non-UI thread.
*
* @see IAndroidLaunchAction#doLaunchActions(DelayedLaunchInfo, IDevice)
*/
@Override
public boolean doLaunchAction(DelayedLaunchInfo info, Collection<IDevice> devices) {
String msg = String.format(LaunchMessages.AndroidJUnitLaunchAction_LaunchInstr_2s,
mLaunchInfo.getRunner(), JOINER.join(devices));
AdtPlugin.printToConsole(info.getProject(), msg);
try {
mLaunchInfo.setDebugMode(info.isDebugMode());
mLaunchInfo.setDevices(devices);
JUnitLaunchDelegate junitDelegate = new JUnitLaunchDelegate(mLaunchInfo);
final String mode = info.isDebugMode() ? ILaunchManager.DEBUG_MODE :
ILaunchManager.RUN_MODE;
junitDelegate.launch(info.getLaunch().getLaunchConfiguration(), mode, info.getLaunch(),
info.getMonitor());
// TODO: need to add AMReceiver-type functionality somewhere
} catch (CoreException e) {
AdtPlugin.printErrorToConsole(info.getProject(),
LaunchMessages.AndroidJUnitLaunchAction_LaunchFail);
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
public String getLaunchDescription() {
return String.format(LaunchMessages.AndroidJUnitLaunchAction_LaunchDesc_s,
mLaunchInfo.getRunner());
}
/**
* Extends the JDT JUnit launch delegate to allow for JUnit UI reuse.
*/
private static class JUnitLaunchDelegate extends JUnitLaunchConfigurationDelegate {
private AndroidJUnitLaunchInfo mLaunchInfo;
public JUnitLaunchDelegate(AndroidJUnitLaunchInfo launchInfo) {
mLaunchInfo = launchInfo;
}
/* (non-Javadoc)
* @see org.eclipse.jdt.junit.launcher.JUnitLaunchConfigurationDelegate#launch(org.eclipse.debug.core.ILaunchConfiguration, java.lang.String, org.eclipse.debug.core.ILaunch, org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
public synchronized void launch(ILaunchConfiguration configuration, String mode,
ILaunch launch, IProgressMonitor monitor) throws CoreException {
// TODO: is progress monitor adjustment needed here?
super.launch(configuration, mode, launch, monitor);
}
/**
* {@inheritDoc}
* @see org.eclipse.jdt.junit.launcher.JUnitLaunchConfigurationDelegate#verifyMainTypeName(org.eclipse.debug.core.ILaunchConfiguration)
*/
@Override
public String verifyMainTypeName(ILaunchConfiguration configuration) {
return "com.android.ide.eclipse.adt.junit.internal.runner.RemoteAndroidTestRunner"; //$NON-NLS-1$
}
/**
* Overrides parent to return a VM Runner implementation which launches a thread, rather
* than a separate VM process
*/
@Override
public IVMRunner getVMRunner(ILaunchConfiguration configuration, String mode) {
return new VMTestRunner(mLaunchInfo);
}
/**
* {@inheritDoc}
* @see org.eclipse.debug.core.model.LaunchConfigurationDelegate#getLaunch(org.eclipse.debug.core.ILaunchConfiguration, java.lang.String)
*/
@Override
public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) {
return mLaunchInfo.getLaunch();
}
}
/**
* Provides a VM runner implementation which starts a inline implementation of a launch process
*/
private static class VMTestRunner implements IVMRunner {
private final AndroidJUnitLaunchInfo mJUnitInfo;
VMTestRunner(AndroidJUnitLaunchInfo info) {
mJUnitInfo = info;
}
/**
* {@inheritDoc}
* @throws CoreException
*/
@Override
public void run(final VMRunnerConfiguration config, ILaunch launch,
IProgressMonitor monitor) throws CoreException {
TestRunnerProcess runnerProcess =
new TestRunnerProcess(config, mJUnitInfo);
launch.addProcess(runnerProcess);
runnerProcess.run();
}
}
/**
* Launch process that executes the tests.
*/
private static class TestRunnerProcess implements IProcess {
private final VMRunnerConfiguration mRunConfig;
private final AndroidJUnitLaunchInfo mJUnitInfo;
private RemoteAdtTestRunner mTestRunner = null;
private boolean mIsTerminated = false;
TestRunnerProcess(VMRunnerConfiguration runConfig, AndroidJUnitLaunchInfo info) {
mRunConfig = runConfig;
mJUnitInfo = info;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IProcess#getAttribute(java.lang.String)
*/
@Override
public String getAttribute(String key) {
return null;
}
/**
* {@inheritDoc}
* @see org.eclipse.debug.core.model.IProcess#getExitValue()
*/
@Override
public int getExitValue() {
return 0;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IProcess#getLabel()
*/
@Override
public String getLabel() {
return mJUnitInfo.getLaunch().getLaunchMode();
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IProcess#getLaunch()
*/
@Override
public ILaunch getLaunch() {
return mJUnitInfo.getLaunch();
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IProcess#getStreamsProxy()
*/
@Override
public IStreamsProxy getStreamsProxy() {
return null;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IProcess#setAttribute(java.lang.String,
* java.lang.String)
*/
@Override
public void setAttribute(String key, String value) {
// ignore
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
*/
@Override
public Object getAdapter(Class adapter) {
return null;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.ITerminate#canTerminate()
*/
@Override
public boolean canTerminate() {
return true;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.ITerminate#isTerminated()
*/
@Override
public boolean isTerminated() {
return mIsTerminated;
}
/**
* {@inheritDoc}
* @see org.eclipse.debug.core.model.ITerminate#terminate()
*/
@Override
public void terminate() {
if (mTestRunner != null) {
mTestRunner.terminate();
}
mIsTerminated = true;
}
/**
* Launches a test runner that will communicate results back to JDT JUnit UI.
* <p/>
* Must be executed on a non-UI thread.
*/
public void run() {
if (Display.getCurrent() != null) {
AdtPlugin.log(IStatus.ERROR, "Adt test runner executed on UI thread");
AdtPlugin.printErrorToConsole(mJUnitInfo.getProject(),
"Test launch failed due to internal error: Running tests on UI thread");
terminate();
return;
}
mTestRunner = new RemoteAdtTestRunner();
mTestRunner.runTests(mRunConfig.getProgramArguments(), mJUnitInfo);
}
}
}