OSDN Git Service

AI 143918: am: CL 143917 ADT Android JUnit: Change logic to provide an explicit proje...
authorBrett Chabot <>
Wed, 1 Apr 2009 02:15:42 +0000 (19:15 -0700)
committerThe Android Open Source Project <initial-contribution@android.com>
Wed, 1 Apr 2009 02:15:42 +0000 (19:15 -0700)
  Original author: brettchabot
  Merged from: //branches/cupcake/...

Automated import of CL 143918

ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java
ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitPropertyTester.java [new file with mode: 0644]
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/AndroidJUnitLaunchInfo.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteAdtTestRunner.java

index 9995426..9dd1d16 100644 (file)
@@ -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.
      * <p/>
      * Must be called before 'run'. If an argument with given name has already been provided, it's
index 6a653ad..864e219 100644 (file)
 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() {
index 4b9d3a0..8092f3a 100644 (file)
@@ -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",
index a75b8b9..35ceba7 100644 (file)
                       <adapt type="org.eclipse.jdt.core.IJavaElement">
                         <test property="org.eclipse.jdt.core.isInJavaProjectWithNature" value="com.android.ide.eclipse.adt.AndroidNature"/>
                         <test property="org.eclipse.jdt.core.hasTypeOnClasspath" value="junit.framework.Test"/>
-                        <test property="org.eclipse.jdt.junit.canLaunchAsJUnit" forcePluginActivation="true"/>
+                        <test property="com.android.ide.eclipse.adt.canLaunchAsJUnit"/>
                      </adapt>
                   </iterate>
                </with>
             id="com.android.ide.eclipse.adt.refactoring.extract.string">
       </contribution>
    </extension>
+    <extension
+         point="org.eclipse.core.expressions.propertyTesters">
+      <propertyTester
+            properties="isTest,canLaunchAsJUnit"
+            namespace="com.android.ide.eclipse.adt"
+            type="org.eclipse.core.runtime.IAdaptable"
+            class="com.android.ide.eclipse.adt.launch.junit.AndroidJUnitPropertyTester"
+            id="com.android.ide.eclipse.adt.AndroidJUnitPropertyTester">
+      </propertyTester>
+   </extension>
 </plugin>
index 747fcfe..9bcc63d 100644 (file)
@@ -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 {
         }
     }
 }
+
index fa8e4b0..543daf0 100755 (executable)
@@ -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 <code>null</code> if not 
+     * specified.
+     * 
+     * @param configuration the {@link ILaunchConfiguration} to retrieve the test package info from
+     * @return the test package or <code>null</code>.
+     */
+    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. <code>null</code> 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. <code>null</code> if not specified.
+     */
+    private String getTestMethod(ILaunchConfiguration configuration) {
+        return getStringLaunchAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_METHOD_NAME,
+                configuration);
+    }
+
+    /**
      * Gets a instrumentation runner for the launch. 
      * <p/>
      * If a runner is stored in the given <code>configuration</code>, 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. <code>null</code> 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;
     }
 
     /**
index eb57482..584d45e 100644 (file)
@@ -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 (file)
index 0000000..eadafee
--- /dev/null
@@ -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.
+ * <p/>
+ * 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;
+    }
+}
index 89cad97..8ac80ca 100644 (file)
  */
 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. <code>null</code> 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.
+     *   <code>null</code> 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. <code>null</code> if not specified.
+     */
+    public String getTestMethod() {
+        return mTestMethod;
+    }
+
+    public ILaunch getLaunch() {
+        return mLaunch;
+    }
+
+    public void setLaunch(ILaunch launch) {
+        mLaunch = launch;
+    }
 }
index 0a6a3da..962d761 100755 (executable)
@@ -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);