From e189ee18dcfa5a04e96259caab3f213f4f95ca69 Mon Sep 17 00:00:00 2001 From: Brett Chabot <> Date: Tue, 31 Mar 2009 19:13:57 -0700 Subject: [PATCH] AI 143917: ADT Android JUnit: Change logic to provide an explicit project or package to run to the device InstrumentationTestRunner, instead of providing the potentially huge list of test classes. Discontinue support for running all tests in a source folder. BUG=1749513 Automated import of CL 143917 --- .../ddmlib/testrunner/RemoteAndroidTestRunner.java | 11 ++ .../testrunner/RemoteAndroidTestRunnerTest.java | 16 ++- .../META-INF/MANIFEST.MF | 3 +- .../plugins/com.android.ide.eclipse.adt/plugin.xml | 12 +- .../adt/launch/junit/AndroidJUnitLaunchAction.java | 68 +++++------ .../junit/AndroidJUnitLaunchConfigDelegate.java | 83 ++++++++++++- .../junit/AndroidJUnitLaunchConfigurationTab.java | 34 ++++-- .../launch/junit/AndroidJUnitPropertyTester.java | 130 +++++++++++++++++++++ .../junit/runtime/AndroidJUnitLaunchInfo.java | 103 +++++++++++++--- .../launch/junit/runtime/RemoteAdtTestRunner.java | 24 ++-- 10 files changed, 404 insertions(+), 80 deletions(-) create mode 100644 eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitPropertyTester.java diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java b/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java index 999542634..9dd1d1640 100644 --- a/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java +++ b/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java @@ -49,6 +49,7 @@ public class RemoteAndroidTestRunner { private static final String LOG_ARG_NAME = "log"; private static final String DEBUG_ARG_NAME = "debug"; private static final String COVERAGE_ARG_NAME = "coverage"; + private static final String PACKAGE_ARG_NAME = "package"; /** * Creates a remote Android test runner. @@ -146,6 +147,16 @@ public class RemoteAndroidTestRunner { } /** + * Sets to run all tests in specified package + * Must be called before 'run'. + * + * @param packageName fully qualified package name (eg x.y.z) + */ + public void setTestPackageName(String packageName) { + addInstrumentationArg(PACKAGE_ARG_NAME, packageName); + } + + /** * Adds a argument to include in instrumentation command. *

* Must be called before 'run'. If an argument with given name has already been provided, it's diff --git a/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java b/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java index 6a653ad05..864e219fc 100644 --- a/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java +++ b/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java @@ -17,16 +17,17 @@ package com.android.ddmlib.testrunner; import com.android.ddmlib.Client; -import com.android.ddmlib.Device.DeviceState; import com.android.ddmlib.FileListingService; import com.android.ddmlib.IDevice; import com.android.ddmlib.IShellOutputReceiver; -import com.android.ddmlib.log.LogReceiver; import com.android.ddmlib.RawImage; import com.android.ddmlib.SyncService; +import com.android.ddmlib.Device.DeviceState; +import com.android.ddmlib.log.LogReceiver; import java.io.IOException; import java.util.Map; + import junit.framework.TestCase; /** @@ -81,6 +82,17 @@ public class RemoteAndroidTestRunnerTest extends TestCase { } /** + * Test the building of the instrumentation runner command with test package set. + */ + public void testRunWithPackage() { + final String packageName = "foo.test"; + mRunner.setTestPackageName(packageName); + mRunner.run(new EmptyListener()); + assertStringsEquals(String.format("am instrument -w -r -e package %s %s/%s", packageName, + TEST_PACKAGE, TEST_RUNNER), mMockDevice.getLastShellCommand()); + } + + /** * Test the building of the instrumentation runner command with extra argument added. */ public void testRunWithAddInstrumentationArg() { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF index 4b9d3a007..8092f3a5e 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF +++ b/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF @@ -43,7 +43,8 @@ Require-Bundle: com.android.ide.eclipse.ddms, org.eclipse.jdt.junit, org.eclipse.jdt.junit.runtime, org.eclipse.ltk.core.refactoring, - org.eclipse.ltk.ui.refactoring + org.eclipse.ltk.ui.refactoring, + org.eclipse.core.expressions Eclipse-LazyStart: true Export-Package: com.android.ide.eclipse.adt;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt.build;x-friends:="com.android.ide.eclipse.tests", diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml index a75b8b915..35ceba76e 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml +++ b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml @@ -563,7 +563,7 @@ - + @@ -595,4 +595,14 @@ id="com.android.ide.eclipse.adt.refactoring.extract.string"> + + + + diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java index 747fcfe5c..9bcc63d06 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java @@ -24,7 +24,6 @@ import com.android.ide.eclipse.adt.launch.junit.runtime.RemoteAdtTestRunner; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchManager; @@ -39,18 +38,15 @@ import org.eclipse.jdt.launching.VMRunnerConfiguration; */ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { - private String mTestPackage; - private String mRunner; + private final AndroidJUnitLaunchInfo mLaunchInfo; /** * Creates a AndroidJUnitLaunchAction. * - * @param testPackage the Android application package that contains the tests to run - * @param runner the InstrumentationTestRunner that will execute the tests + * @param launchInfo the {@link AndroidJUnitLaunchInfo} for the JUnit run */ - public AndroidJUnitLaunchAction(String testPackage, String runner) { - mTestPackage = testPackage; - mRunner = runner; + public AndroidJUnitLaunchAction(AndroidJUnitLaunchInfo launchInfo) { + mLaunchInfo = launchInfo; } /** @@ -60,17 +56,21 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { * @see IAndroidLaunchAction#doLaunchAction(DelayedLaunchInfo, IDevice) */ public boolean doLaunchAction(DelayedLaunchInfo info, IDevice device) { - String msg = String.format("Launching instrumentation %s on device %s", mRunner, - device.getSerialNumber()); + String msg = String.format("Launching instrumentation %s on device %s", + mLaunchInfo.getRunner(), device.getSerialNumber()); AdtPlugin.printToConsole(info.getProject(), msg); try { - JUnitLaunchDelegate junitDelegate = new JUnitLaunchDelegate(info, device); + mLaunchInfo.setDebugMode(info.isDebugMode()); + mLaunchInfo.setDevice(info.getDevice()); + mLaunchInfo.setLaunch(info.getLaunch()); + 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(), "Failed to launch test"); @@ -82,20 +82,18 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { * {@inheritDoc} */ public String getLaunchDescription() { - return String.format("%s JUnit launch", mRunner); + return String.format("%s JUnit launch", mLaunchInfo.getRunner()); } /** * Extends the JDT JUnit launch delegate to allow for JUnit UI reuse. */ - private class JUnitLaunchDelegate extends JUnitLaunchConfigurationDelegate { + private static class JUnitLaunchDelegate extends JUnitLaunchConfigurationDelegate { - private IDevice mDevice; - private DelayedLaunchInfo mLaunchInfo; + private AndroidJUnitLaunchInfo mLaunchInfo; - public JUnitLaunchDelegate(DelayedLaunchInfo info, IDevice device) { - mLaunchInfo = info; - mDevice = device; + public JUnitLaunchDelegate(AndroidJUnitLaunchInfo launchInfo) { + mLaunchInfo = launchInfo; } /* (non-Javadoc) @@ -110,34 +108,28 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { /** * {@inheritDoc} - * @throws CoreException * @see org.eclipse.jdt.junit.launcher.JUnitLaunchConfigurationDelegate#verifyMainTypeName(org.eclipse.debug.core.ILaunchConfiguration) */ @Override - public String verifyMainTypeName(ILaunchConfiguration configuration) throws CoreException { + 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 - * @throws CoreException */ @Override - public IVMRunner getVMRunner(ILaunchConfiguration configuration, String mode) - throws CoreException { - return new VMTestRunner(new AndroidJUnitLaunchInfo(mLaunchInfo.getProject(), - mTestPackage, mRunner, mLaunchInfo.isDebugMode(), mDevice)); + public IVMRunner getVMRunner(ILaunchConfiguration configuration, String mode) { + return new VMTestRunner(mLaunchInfo); } /** * {@inheritDoc} - * @throws CoreException * @see org.eclipse.debug.core.model.LaunchConfigurationDelegate#getLaunch(org.eclipse.debug.core.ILaunchConfiguration, java.lang.String) */ @Override - public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) - throws CoreException { + public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) { return mLaunchInfo.getLaunch(); } } @@ -161,7 +153,7 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { IProgressMonitor monitor) throws CoreException { TestRunnerProcess runnerProcess = - new TestRunnerProcess(config, launch, mJUnitInfo); + new TestRunnerProcess(config, mJUnitInfo); runnerProcess.start(); launch.addProcess(runnerProcess); } @@ -173,15 +165,12 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { private static class TestRunnerProcess extends Thread implements IProcess { private final VMRunnerConfiguration mRunConfig; - private final ILaunch mLaunch; private final AndroidJUnitLaunchInfo mJUnitInfo; private RemoteAdtTestRunner mTestRunner = null; private boolean mIsTerminated = false; - TestRunnerProcess(VMRunnerConfiguration runConfig, ILaunch launch, - AndroidJUnitLaunchInfo info) { + TestRunnerProcess(VMRunnerConfiguration runConfig, AndroidJUnitLaunchInfo info) { mRunConfig = runConfig; - mLaunch = launch; mJUnitInfo = info; } @@ -194,10 +183,9 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { /** * {@inheritDoc} - * @throws DebugException * @see org.eclipse.debug.core.model.IProcess#getExitValue() */ - public int getExitValue() throws DebugException { + public int getExitValue() { return 0; } @@ -205,14 +193,14 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { * @see org.eclipse.debug.core.model.IProcess#getLabel() */ public String getLabel() { - return mLaunch.getLaunchMode(); + return mJUnitInfo.getLaunch().getLaunchMode(); } /* (non-Javadoc) * @see org.eclipse.debug.core.model.IProcess#getLaunch() */ public ILaunch getLaunch() { - return mLaunch; + return mJUnitInfo.getLaunch(); } /* (non-Javadoc) @@ -254,10 +242,9 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { /** * {@inheritDoc} - * @throws DebugException * @see org.eclipse.debug.core.model.ITerminate#terminate() */ - public void terminate() throws DebugException { + public void terminate() { if (mTestRunner != null) { mTestRunner.terminate(); } @@ -274,3 +261,4 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { } } } + diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java index fa8e4b01a..543daf06f 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java @@ -22,6 +22,7 @@ import com.android.ide.eclipse.adt.launch.AndroidLaunchConfiguration; import com.android.ide.eclipse.adt.launch.AndroidLaunchController; import com.android.ide.eclipse.adt.launch.IAndroidLaunchAction; import com.android.ide.eclipse.adt.launch.LaunchConfigDelegate; +import com.android.ide.eclipse.adt.launch.junit.runtime.AndroidJUnitLaunchInfo; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.project.AndroidManifestParser; import com.android.ide.eclipse.common.project.BaseProjectHelper; @@ -32,8 +33,11 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.internal.junit.launcher.JUnitLaunchConfigurationConstants; import org.eclipse.jdt.internal.junit.launcher.TestKindRegistry; +import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; /** * Run configuration that can execute JUnit tests on an Android platform. @@ -47,6 +51,7 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate { /** Launch config attribute that stores instrumentation runner. */ static final String ATTR_INSTR_NAME = AdtPlugin.PLUGIN_ID + ".instrumentation"; //$NON-NLS-1$ + private static final String EMPTY_STRING = ""; //$NON-NLS-1$ @Override @@ -55,7 +60,7 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate { AndroidLaunchConfiguration config, AndroidLaunchController controller, IFile applicationPackage, AndroidManifestParser manifestParser) { - String testPackage = manifestParser.getPackage(); + String appPackage = manifestParser.getPackage(); String runner = getRunner(project, configuration, manifestParser); if (runner == null) { AdtPlugin.displayError("Android Launch", @@ -63,8 +68,13 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate { androidLaunch.stopLaunch(); return; } + AndroidJUnitLaunchInfo junitLaunchInfo = new AndroidJUnitLaunchInfo(project, appPackage, + runner); + junitLaunchInfo.setTestClass(getTestClass(configuration)); + junitLaunchInfo.setTestPackage(getTestPackage(configuration)); + junitLaunchInfo.setTestMethod(getTestMethod(configuration)); - IAndroidLaunchAction junitLaunch = new AndroidJUnitLaunchAction(testPackage, runner); + IAndroidLaunchAction junitLaunch = new AndroidJUnitLaunchAction(junitLaunchInfo); controller.launch(project, mode, applicationPackage, manifestParser.getPackage(), manifestParser.getDebuggable(), manifestParser.getApiLevelRequirement(), @@ -72,6 +82,49 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate { } /** + * Returns the test package stored in the launch configuration, or null if not + * specified. + * + * @param configuration the {@link ILaunchConfiguration} to retrieve the test package info from + * @return the test package or null. + */ + private String getTestPackage(ILaunchConfiguration configuration) { + // try to retrieve a package name from the JUnit container attribute + String containerHandle = getStringLaunchAttribute( + JUnitLaunchConfigurationConstants.ATTR_TEST_CONTAINER, configuration); + if (containerHandle != null && containerHandle.length() > 0) { + IJavaElement element = JavaCore.create(containerHandle); + // containerHandle could be a IProject, check if its a java package + if (element.getElementType() == IJavaElement.PACKAGE_FRAGMENT) { + return element.getElementName(); + } + } + return null; + } + + /** + * Returns the test class stored in the launch configuration. + * + * @param configuration the {@link ILaunchConfiguration} to retrieve the test class info from + * @return the test class. null if not specified. + */ + private String getTestClass(ILaunchConfiguration configuration) { + return getStringLaunchAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, + configuration); + } + + /** + * Returns the test method stored in the launch configuration. + * + * @param configuration the {@link ILaunchConfiguration} to retrieve the test method info from + * @return the test method. null if not specified. + */ + private String getTestMethod(ILaunchConfiguration configuration) { + return getStringLaunchAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_METHOD_NAME, + configuration); + } + + /** * Gets a instrumentation runner for the launch. *

* If a runner is stored in the given configuration, will return that. @@ -114,11 +167,29 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate { } private String getRunnerFromConfig(ILaunchConfiguration configuration) throws CoreException { - String runner = configuration.getAttribute(ATTR_INSTR_NAME, EMPTY_STRING); - if (runner.length() < 1) { - return null; + return getStringLaunchAttribute(ATTR_INSTR_NAME, configuration); + } + + /** + * Helper method to retrieve a string attribute from the launch configuration + * + * @param attributeName name of the launch attribute + * @param configuration the {@link ILaunchConfiguration} to retrieve the attribute from + * @return the attribute's value. null if not found. + */ + private String getStringLaunchAttribute(String attributeName, + ILaunchConfiguration configuration) { + try { + String attrValue = configuration.getAttribute(attributeName, EMPTY_STRING); + if (attrValue.length() < 1) { + return null; + } + return attrValue; + } catch (CoreException e) { + AdtPlugin.log(e, String.format("Error when retrieving launch info %1$s", //$NON-NLS-1$ + attributeName)); } - return runner; + return null; } /** diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java index eb5748269..584d45eb1 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java @@ -18,6 +18,7 @@ package com.android.ide.eclipse.adt.launch.junit; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.launch.MainLaunchConfigTab; import com.android.ide.eclipse.common.AndroidConstants; +import com.android.ide.eclipse.common.project.BaseProjectHelper; import com.android.ide.eclipse.common.project.ProjectChooserHelper; import org.eclipse.core.resources.IProject; @@ -241,7 +242,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat private void createTestContainerSelectionGroup(Composite comp) { mTestContainerRadioButton = new Button(comp, SWT.RADIO); mTestContainerRadioButton.setText( - JUnitMessages.JUnitLaunchConfigurationTab_label_containerTest); + "Run all tests in the selected project, or package"); GridData gd = new GridData(); gd.horizontalSpan = 3; mTestContainerRadioButton.setLayoutData(gd); @@ -249,12 +250,12 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat public void widgetSelected(SelectionEvent e) { if (mTestContainerRadioButton.getSelection()) { testModeChanged(); - } + } } public void widgetDefaultSelected(SelectionEvent e) { } }); - + mContainerText = new Text(comp, SWT.SINGLE | SWT.BORDER | SWT.READ_ONLY); gd = new GridData(GridData.FILL_HORIZONTAL); gd.horizontalIndent = 25; @@ -265,7 +266,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat updateLaunchConfigurationDialog(); } }); - + mContainerSearchButton = new Button(comp, SWT.PUSH); mContainerSearchButton.setText(JUnitMessages.JUnitLaunchConfigurationTab_label_search); mContainerSearchButton.addSelectionListener(new SelectionAdapter() { @@ -821,7 +822,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat @SuppressWarnings("unchecked") private IJavaElement chooseContainer(IJavaElement initElement) { - Class[] acceptedClasses = new Class[] { IPackageFragmentRoot.class, IJavaProject.class, + Class[] acceptedClasses = new Class[] { IJavaProject.class, IPackageFragment.class }; TypedElementSelectionValidator validator = new TypedElementSelectionValidator( acceptedClasses, false) { @@ -839,7 +840,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat if (element instanceof IPackageFragmentRoot && ((IPackageFragmentRoot) element).isArchive()) { return false; - } + } try { if (element instanceof IPackageFragment && !((IPackageFragment) element).hasChildren()) { @@ -852,7 +853,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat } }; - StandardJavaElementContentProvider provider = new StandardJavaElementContentProvider(); + AndroidJavaElementContentProvider provider = new AndroidJavaElementContentProvider(); ILabelProvider labelProvider = new JavaElementLabelProvider( JavaElementLabelProvider.SHOW_DEFAULT); ElementTreeSelectionDialog dialog = new ElementTreeSelectionDialog(getShell(), @@ -974,4 +975,23 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat mInstrumentations = null; mInstrumentationCombo.removeAll(); } + + /** + * Overrides the {@link StandardJavaElementContentProvider} to only display Android projects + */ + private static class AndroidJavaElementContentProvider + extends StandardJavaElementContentProvider { + + /** + * Override parent to return only Android projects if at the root. Otherwise, use parent + * functionality. + */ + @Override + public Object[] getChildren(Object element) { + if (element instanceof IJavaModel) { + return BaseProjectHelper.getAndroidProjects((IJavaModel) element); + } + return super.getChildren(element); + } + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitPropertyTester.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitPropertyTester.java new file mode 100644 index 000000000..eadafee77 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitPropertyTester.java @@ -0,0 +1,130 @@ +/* + * 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.launch.junit; + +import org.eclipse.core.expressions.PropertyTester; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.jdt.core.IClassFile; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IMember; +import org.eclipse.jdt.core.IPackageFragment; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.internal.junit.util.TestSearchEngine; + +/** + * A {@link PropertyTester} that checks if selected elements can be run as Android + * JUnit tests. + *

+ * Based on org.eclipse.jdt.internal.junit.JUnitPropertyTester. The only substantial difference in + * this implementation is source folders cannot be run as Android JUnit. + */ +@SuppressWarnings("restriction") +public class AndroidJUnitPropertyTester extends PropertyTester { + private static final String PROPERTY_IS_TEST = "isTest"; //$NON-NLS-1$ + + private static final String PROPERTY_CAN_LAUNCH_AS_JUNIT_TEST = "canLaunchAsJUnit"; //$NON-NLS-1$ + + /* (non-Javadoc) + * @see org.eclipse.jdt.internal.corext.refactoring.participants.properties.IPropertyEvaluator#test(java.lang.Object, java.lang.String, java.lang.String) + */ + public boolean test(Object receiver, String property, Object[] args, Object expectedValue) { + if (!(receiver instanceof IAdaptable)) { + final String elementName = (receiver == null ? "null" : //$NON-NLS-1$ + receiver.getClass().getName()); + throw new IllegalArgumentException( + String.format("Element must be of type IAdaptable, is %s", //$NON-NLS-1$ + elementName)); + } + + IJavaElement element; + if (receiver instanceof IJavaElement) { + element = (IJavaElement) receiver; + } else if (receiver instanceof IResource) { + element = JavaCore.create((IResource) receiver); + if (element == null) { + return false; + } + } else { // is IAdaptable + element= (IJavaElement) ((IAdaptable) receiver).getAdapter(IJavaElement.class); + if (element == null) { + IResource resource = (IResource) ((IAdaptable) receiver).getAdapter( + IResource.class); + element = JavaCore.create(resource); + if (element == null) { + return false; + } + } + } + if (PROPERTY_IS_TEST.equals(property)) { + return isJUnitTest(element); + } else if (PROPERTY_CAN_LAUNCH_AS_JUNIT_TEST.equals(property)) { + return canLaunchAsJUnitTest(element); + } + throw new IllegalArgumentException( + String.format("Unknown test property '%s'", property)); //$NON-NLS-1$ + } + + private boolean canLaunchAsJUnitTest(IJavaElement element) { + try { + switch (element.getElementType()) { + case IJavaElement.JAVA_PROJECT: + return true; // can run, let JDT detect if there are tests + case IJavaElement.PACKAGE_FRAGMENT_ROOT: + return false; // not supported by Android test runner + case IJavaElement.PACKAGE_FRAGMENT: + return ((IPackageFragment) element).hasChildren(); + case IJavaElement.COMPILATION_UNIT: + case IJavaElement.CLASS_FILE: + case IJavaElement.TYPE: + case IJavaElement.METHOD: + return isJUnitTest(element); + default: + return false; + } + } catch (JavaModelException e) { + return false; + } + } + + /** + * Return whether the target resource is a JUnit test. + */ + private boolean isJUnitTest(IJavaElement element) { + try { + IType testType = null; + if (element instanceof ICompilationUnit) { + testType = (((ICompilationUnit) element)).findPrimaryType(); + } else if (element instanceof IClassFile) { + testType = (((IClassFile) element)).getType(); + } else if (element instanceof IType) { + testType = (IType) element; + } else if (element instanceof IMember) { + testType = ((IMember) element).getDeclaringType(); + } + if (testType != null && testType.exists()) { + return TestSearchEngine.isTestOrTestSuite(testType); + } + } catch (CoreException e) { + // ignore, return false + } + return false; + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/AndroidJUnitLaunchInfo.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/AndroidJUnitLaunchInfo.java index 89cad97ae..8ac80cab5 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/AndroidJUnitLaunchInfo.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/AndroidJUnitLaunchInfo.java @@ -15,35 +15,38 @@ */ package com.android.ide.eclipse.adt.launch.junit.runtime; -import org.eclipse.core.resources.IProject; - import com.android.ddmlib.IDevice; +import org.eclipse.core.resources.IProject; +import org.eclipse.debug.core.ILaunch; + /** * Contains info about Android JUnit launch */ public class AndroidJUnitLaunchInfo { private final IProject mProject; - private final String mTestPackage; + private final String mAppPackage; private final String mRunner; - private final boolean mDebugMode; - private final IDevice mDevice; - - public AndroidJUnitLaunchInfo(IProject project, String testPackage, String runner, - boolean debugMode, IDevice device) { + + private boolean mDebugMode = false; + private IDevice mDevice = null; + private String mTestPackage = null; + private String mTestClass = null; + private String mTestMethod = null; + private ILaunch mLaunch = null; + + public AndroidJUnitLaunchInfo(IProject project, String appPackage, String runner) { mProject = project; - mTestPackage = testPackage; + mAppPackage = appPackage; mRunner = runner; - mDebugMode = debugMode; - mDevice = device; } - + public IProject getProject() { return mProject; } - public String getTestPackage() { - return mTestPackage; + public String getAppPackage() { + return mAppPackage; } public String getRunner() { @@ -53,8 +56,80 @@ public class AndroidJUnitLaunchInfo { public boolean isDebugMode() { return mDebugMode; } + + public void setDebugMode(boolean debugMode) { + mDebugMode = debugMode; + } public IDevice getDevice() { return mDevice; } + + public void setDevice(IDevice device) { + mDevice = device; + } + + /** + * Specify to run all tests within given package. + * + * @param testPackage fully qualified java package + */ + public void setTestPackage(String testPackage) { + mTestPackage = testPackage; + } + + /** + * Return the package of tests to run. + * + * @return fully qualified java package. null if not specified. + */ + public String getTestPackage() { + return mTestPackage; + } + + /** + * Sets the test class to run. + * + * @param testClass fully qualfied test class to run + * Expected format: x.y.x.testclass + */ + public void setTestClass(String testClass) { + mTestClass = testClass; + } + + /** + * Returns the test class to run. + * + * @return fully qualfied test class to run. + * null if not specified. + */ + public String getTestClass() { + return mTestClass; + } + + /** + * Sets the test method to run. testClass must also be set. + * + * @param testMethod test method to run + */ + public void setTestMethod(String testMethod) { + mTestMethod = testMethod; + } + + /** + * Returns the test method to run. + * + * @return test method to run. null if not specified. + */ + public String getTestMethod() { + return mTestMethod; + } + + public ILaunch getLaunch() { + return mLaunch; + } + + public void setLaunch(ILaunch launch) { + mLaunch = launch; + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteAdtTestRunner.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteAdtTestRunner.java index 0a6a3daee..962d76133 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteAdtTestRunner.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteAdtTestRunner.java @@ -69,8 +69,9 @@ public class RemoteAdtTestRunner extends RemoteTestRunner { * executing the tests, and send it back to JDT JUnit. The second is the actual test execution, * whose results will be communicated back in real-time to JDT JUnit. * - * @param testClassNames array of fully qualified test class names to execute. Cannot be empty. - * @param testName test to execute. If null, will be ignored. + * @param testClassNames ignored - the AndroidJUnitLaunchInfo will be used to determine which + * tests to run. + * @param testName ignored * @param execution used to report test progress */ @Override @@ -78,16 +79,21 @@ public class RemoteAdtTestRunner extends RemoteTestRunner { // hold onto this execution reference so it can be used to report test progress mExecution = execution; - RemoteAndroidTestRunner runner = new RemoteAndroidTestRunner(mLaunchInfo.getTestPackage(), + RemoteAndroidTestRunner runner = new RemoteAndroidTestRunner(mLaunchInfo.getAppPackage(), mLaunchInfo.getRunner(), mLaunchInfo.getDevice()); - if (testClassNames != null && testClassNames.length > 0) { - if (testName != null) { - runner.setMethodName(testClassNames[0], testName); - } else { - runner.setClassNames(testClassNames); - } + if (mLaunchInfo.getTestClass() != null) { + if (mLaunchInfo.getTestMethod() != null) { + runner.setMethodName(mLaunchInfo.getTestClass(), mLaunchInfo.getTestMethod()); + } else { + runner.setClassName(mLaunchInfo.getTestClass()); + } } + + if (mLaunchInfo.getTestPackage() != null) { + runner.setTestPackageName(mLaunchInfo.getTestPackage()); + } + // set log only to first collect test case info, so Eclipse has correct test case count/ // tree info runner.setLogOnly(true); -- 2.11.0