Home | History | Annotate | Download | only in junit
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.eclipse.org/org/documents/epl-v10.php
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.ide.eclipse.adt.internal.launch.junit;
     18 
     19 import com.android.SdkConstants;
     20 import com.android.ddmlib.testrunner.IRemoteAndroidTestRunner.TestSize;
     21 import com.android.ide.common.xml.ManifestData;
     22 import com.android.ide.common.xml.ManifestData.Instrumentation;
     23 import com.android.ide.eclipse.adt.AdtConstants;
     24 import com.android.ide.eclipse.adt.AdtPlugin;
     25 import com.android.ide.eclipse.adt.internal.launch.AndroidLaunch;
     26 import com.android.ide.eclipse.adt.internal.launch.AndroidLaunchConfiguration;
     27 import com.android.ide.eclipse.adt.internal.launch.AndroidLaunchController;
     28 import com.android.ide.eclipse.adt.internal.launch.IAndroidLaunchAction;
     29 import com.android.ide.eclipse.adt.internal.launch.LaunchConfigDelegate;
     30 import com.android.ide.eclipse.adt.internal.launch.LaunchMessages;
     31 import com.android.ide.eclipse.adt.internal.launch.junit.runtime.AndroidJUnitLaunchInfo;
     32 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
     33 
     34 import org.eclipse.core.resources.IFile;
     35 import org.eclipse.core.resources.IProject;
     36 import org.eclipse.core.runtime.CoreException;
     37 import org.eclipse.core.runtime.IProgressMonitor;
     38 import org.eclipse.core.runtime.IStatus;
     39 import org.eclipse.core.runtime.Status;
     40 import org.eclipse.core.runtime.jobs.Job;
     41 import org.eclipse.debug.core.ILaunchConfiguration;
     42 import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
     43 import org.eclipse.jdt.core.IJavaElement;
     44 import org.eclipse.jdt.core.JavaCore;
     45 import org.eclipse.jdt.internal.junit.launcher.JUnitLaunchConfigurationConstants;
     46 import org.eclipse.jdt.internal.junit.launcher.TestKindRegistry;
     47 import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
     48 import org.eclipse.swt.widgets.Display;
     49 
     50 /**
     51  * Run configuration that can execute JUnit tests on an Android platform.
     52  * <p/>
     53  * Will deploy apps on target Android platform by reusing functionality from ADT
     54  * LaunchConfigDelegate, and then run JUnits tests by reusing functionality from JDT
     55  * JUnitLaunchConfigDelegate.
     56  */
     57 @SuppressWarnings("restriction")
     58 public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate {
     59 
     60     /** Launch config attribute that stores instrumentation runner. */
     61     static final String ATTR_INSTR_NAME = AdtPlugin.PLUGIN_ID + ".instrumentation"; //$NON-NLS-1$
     62 
     63     /** Launch config attribute that stores the test size annotation to run. */
     64     static final String ATTR_TEST_SIZE = AdtPlugin.PLUGIN_ID + ".testSize"; //$NON-NLS-1$
     65 
     66     private static final String EMPTY_STRING = ""; //$NON-NLS-1$
     67 
     68     @Override
     69     protected void doLaunch(final ILaunchConfiguration configuration, final String mode,
     70             final IProgressMonitor monitor, final IProject project,
     71             final AndroidLaunch androidLaunch, final AndroidLaunchConfiguration config,
     72             final AndroidLaunchController controller, final IFile applicationPackage,
     73             final ManifestData manifestData) {
     74 
     75         String runner = getRunner(project, configuration, manifestData);
     76         if (runner == null) {
     77             AdtPlugin.displayError(LaunchMessages.LaunchDialogTitle,
     78                     String.format(LaunchMessages.AndroidJUnitDelegate_NoRunnerMsg_s,
     79                             project.getName()));
     80             androidLaunch.stopLaunch();
     81             return;
     82         }
     83         // get the target app's package
     84         final String targetAppPackage = getTargetPackage(manifestData, runner);
     85         if (targetAppPackage == null) {
     86             AdtPlugin.displayError(LaunchMessages.LaunchDialogTitle,
     87                     String.format(LaunchMessages.AndroidJUnitDelegate_NoTargetMsg_3s,
     88                             project.getName(), runner, SdkConstants.FN_ANDROID_MANIFEST_XML));
     89             androidLaunch.stopLaunch();
     90             return;
     91         }
     92         final String testAppPackage = manifestData.getPackage();
     93         AndroidJUnitLaunchInfo junitLaunchInfo = new AndroidJUnitLaunchInfo(project,
     94                 testAppPackage, runner);
     95         junitLaunchInfo.setTestClass(getTestClass(configuration));
     96         junitLaunchInfo.setTestPackage(getTestPackage(configuration));
     97         junitLaunchInfo.setTestMethod(getTestMethod(configuration));
     98         junitLaunchInfo.setLaunch(androidLaunch);
     99         junitLaunchInfo.setTestSize(getTestSize(configuration));
    100         final IAndroidLaunchAction junitLaunch = new AndroidJUnitLaunchAction(junitLaunchInfo);
    101 
    102         // launch on a separate thread if currently on the display thread
    103         if (Display.getCurrent() != null) {
    104             Job job = new Job("Junit Launch") {     //$NON-NLS-1$
    105                 @Override
    106                 protected IStatus run(IProgressMonitor m) {
    107                     controller.launch(project, mode, applicationPackage, testAppPackage,
    108                             targetAppPackage, manifestData.getDebuggable(),
    109                             manifestData.getMinSdkVersionString(),
    110                             junitLaunch, config, androidLaunch, monitor);
    111                     return Status.OK_STATUS;
    112                 }
    113             };
    114             job.setPriority(Job.INTERACTIVE);
    115             job.schedule();
    116         } else {
    117             controller.launch(project, mode, applicationPackage, testAppPackage, targetAppPackage,
    118                     manifestData.getDebuggable(), manifestData.getMinSdkVersionString(),
    119                     junitLaunch, config, androidLaunch, monitor);
    120         }
    121     }
    122 
    123     /**
    124      * Get the target Android application's package for the given instrumentation runner, or
    125      * <code>null</code> if it could not be found.
    126      *
    127      * @param manifestParser the {@link ManifestData} for the test project
    128      * @param runner the instrumentation runner class name
    129      * @return the target package or <code>null</code>
    130      */
    131     private String getTargetPackage(ManifestData manifestParser, String runner) {
    132         for (Instrumentation instr : manifestParser.getInstrumentations()) {
    133             if (instr.getName().equals(runner)) {
    134                 return instr.getTargetPackage();
    135             }
    136         }
    137         return null;
    138     }
    139 
    140     /**
    141      * Returns the test package stored in the launch configuration, or <code>null</code> if not
    142      * specified.
    143      *
    144      * @param configuration the {@link ILaunchConfiguration} to retrieve the test package info from
    145      * @return the test package or <code>null</code>.
    146      */
    147     private String getTestPackage(ILaunchConfiguration configuration) {
    148         // try to retrieve a package name from the JUnit container attribute
    149         String containerHandle = getStringLaunchAttribute(
    150                 JUnitLaunchConfigurationConstants.ATTR_TEST_CONTAINER, configuration);
    151         if (containerHandle != null && containerHandle.length() > 0) {
    152             IJavaElement element = JavaCore.create(containerHandle);
    153             // containerHandle could be a IProject, check if its a java package
    154             if (element.getElementType() == IJavaElement.PACKAGE_FRAGMENT) {
    155                 return element.getElementName();
    156             }
    157         }
    158         return null;
    159     }
    160 
    161     /**
    162      * Returns the test class stored in the launch configuration.
    163      *
    164      * @param configuration the {@link ILaunchConfiguration} to retrieve the test class info from
    165      * @return the test class. <code>null</code> if not specified.
    166      */
    167     private String getTestClass(ILaunchConfiguration configuration) {
    168         return getStringLaunchAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME,
    169                 configuration);
    170     }
    171 
    172     /**
    173      * Returns the test method stored in the launch configuration.
    174      *
    175      * @param configuration the {@link ILaunchConfiguration} to retrieve the test method info from
    176      * @return the test method. <code>null</code> if not specified.
    177      */
    178     private String getTestMethod(ILaunchConfiguration configuration) {
    179         return getStringLaunchAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_METHOD_NAME,
    180                 configuration);
    181     }
    182 
    183     /**
    184      * Returns the test sizes to run as saved in the launch configuration.
    185      * @return {@link TestSize} if only tests of specific sizes should be run,
    186      *         null if all tests should be run
    187      */
    188     private TestSize getTestSize(ILaunchConfiguration configuration) {
    189         String testSizeAnnotation = getStringLaunchAttribute(
    190                 AndroidJUnitLaunchConfigDelegate.ATTR_TEST_SIZE,
    191                 configuration);
    192         if (AndroidJUnitLaunchConfigurationTab.SMALL_TEST_ANNOTATION.equals(
    193                     testSizeAnnotation)){
    194             return TestSize.SMALL;
    195         } else if (AndroidJUnitLaunchConfigurationTab.MEDIUM_TEST_ANNOTATION.equals(
    196                     testSizeAnnotation)) {
    197             return TestSize.MEDIUM;
    198         } else if (AndroidJUnitLaunchConfigurationTab.LARGE_TEST_ANNOTATION.equals(
    199                     testSizeAnnotation)) {
    200             return TestSize.LARGE;
    201         } else {
    202             return null;
    203         }
    204     }
    205 
    206     /**
    207      * Gets a instrumentation runner for the launch.
    208      * <p/>
    209      * If a runner is stored in the given <code>configuration</code>, will return that.
    210      * Otherwise, will try to find the first valid runner for the project.
    211      * If a runner can still not be found, will return <code>null</code>, and will log an error
    212      * to the console.
    213      *
    214      * @param project the {@link IProject} for the app
    215      * @param configuration the {@link ILaunchConfiguration} for the launch
    216      * @param manifestData the {@link ManifestData} for the project
    217      *
    218      * @return <code>null</code> if no instrumentation runner can be found, otherwise return
    219      *   the fully qualified runner name.
    220      */
    221     private String getRunner(IProject project, ILaunchConfiguration configuration,
    222             ManifestData manifestData) {
    223         try {
    224             String runner = getRunnerFromConfig(configuration);
    225             if (runner != null) {
    226                 return runner;
    227             }
    228             final InstrumentationRunnerValidator instrFinder = new InstrumentationRunnerValidator(
    229                     BaseProjectHelper.getJavaProject(project), manifestData);
    230             runner = instrFinder.getValidInstrumentationTestRunner();
    231             if (runner != null) {
    232                 AdtPlugin.printErrorToConsole(project, String.format(
    233                         LaunchMessages.AndroidJUnitDelegate_NoRunnerConfigMsg_s, runner));
    234                 return runner;
    235             }
    236             AdtPlugin.printErrorToConsole(project, String.format(
    237                     LaunchMessages.AndroidJUnitDelegate_NoRunnerConsoleMsg_4s,
    238                     project.getName(),
    239                     SdkConstants.CLASS_INSTRUMENTATION_RUNNER,
    240                     AdtConstants.LIBRARY_TEST_RUNNER,
    241                     SdkConstants.FN_ANDROID_MANIFEST_XML));
    242             return null;
    243         } catch (CoreException e) {
    244             AdtPlugin.log(e, "Error when retrieving instrumentation info"); //$NON-NLS-1$
    245         }
    246 
    247         return null;
    248     }
    249 
    250     private String getRunnerFromConfig(ILaunchConfiguration configuration) {
    251         return getStringLaunchAttribute(ATTR_INSTR_NAME, configuration);
    252     }
    253 
    254     /**
    255      * Helper method to retrieve a string attribute from the launch configuration
    256      *
    257      * @param attributeName name of the launch attribute
    258      * @param configuration the {@link ILaunchConfiguration} to retrieve the attribute from
    259      * @return the attribute's value. <code>null</code> if not found.
    260      */
    261     private String getStringLaunchAttribute(String attributeName,
    262             ILaunchConfiguration configuration) {
    263         try {
    264             String attrValue = configuration.getAttribute(attributeName, EMPTY_STRING);
    265             if (attrValue.length() < 1) {
    266                 return null;
    267             }
    268             return attrValue;
    269         } catch (CoreException e) {
    270             AdtPlugin.log(e, String.format("Error when retrieving launch info %1$s",  //$NON-NLS-1$
    271                     attributeName));
    272         }
    273         return null;
    274     }
    275 
    276     /**
    277      * Helper method to set JUnit-related attributes expected by JDT JUnit runner
    278      *
    279      * @param config the launch configuration to modify
    280      */
    281     static void setJUnitDefaults(ILaunchConfigurationWorkingCopy config) {
    282         // set the test runner to JUnit3 to placate JDT JUnit runner logic
    283         config.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_RUNNER_KIND,
    284                 TestKindRegistry.JUNIT3_TEST_KIND_ID);
    285     }
    286 }
    287