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.ide.eclipse.adt.AdtPlugin;
     20 import com.android.ide.eclipse.adt.AdtConstants;
     21 import com.android.ide.eclipse.adt.internal.launch.AndroidLaunch;
     22 import com.android.ide.eclipse.adt.internal.launch.AndroidLaunchConfiguration;
     23 import com.android.ide.eclipse.adt.internal.launch.AndroidLaunchController;
     24 import com.android.ide.eclipse.adt.internal.launch.IAndroidLaunchAction;
     25 import com.android.ide.eclipse.adt.internal.launch.LaunchConfigDelegate;
     26 import com.android.ide.eclipse.adt.internal.launch.LaunchMessages;
     27 import com.android.ide.eclipse.adt.internal.launch.junit.runtime.AndroidJUnitLaunchInfo;
     28 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
     29 import com.android.sdklib.SdkConstants;
     30 import com.android.sdklib.xml.ManifestData;
     31 import com.android.sdklib.xml.ManifestData.Instrumentation;
     32 
     33 import org.eclipse.core.resources.IFile;
     34 import org.eclipse.core.resources.IProject;
     35 import org.eclipse.core.runtime.CoreException;
     36 import org.eclipse.core.runtime.IProgressMonitor;
     37 import org.eclipse.debug.core.ILaunchConfiguration;
     38 import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
     39 import org.eclipse.jdt.core.IJavaElement;
     40 import org.eclipse.jdt.core.JavaCore;
     41 import org.eclipse.jdt.internal.junit.launcher.JUnitLaunchConfigurationConstants;
     42 import org.eclipse.jdt.internal.junit.launcher.TestKindRegistry;
     43 import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
     44 
     45 /**
     46  * Run configuration that can execute JUnit tests on an Android platform.
     47  * <p/>
     48  * Will deploy apps on target Android platform by reusing functionality from ADT
     49  * LaunchConfigDelegate, and then run JUnits tests by reusing functionality from JDT
     50  * JUnitLaunchConfigDelegate.
     51  */
     52 @SuppressWarnings("restriction")
     53 public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate {
     54 
     55     /** Launch config attribute that stores instrumentation runner. */
     56     static final String ATTR_INSTR_NAME = AdtPlugin.PLUGIN_ID + ".instrumentation"; //$NON-NLS-1$
     57 
     58     private static final String EMPTY_STRING = ""; //$NON-NLS-1$
     59 
     60     @Override
     61     protected void doLaunch(final ILaunchConfiguration configuration, final String mode,
     62             IProgressMonitor monitor, IProject project, final AndroidLaunch androidLaunch,
     63             AndroidLaunchConfiguration config, AndroidLaunchController controller,
     64             IFile applicationPackage, ManifestData manifestData) {
     65 
     66         String runner = getRunner(project, configuration, manifestData);
     67         if (runner == null) {
     68             AdtPlugin.displayError(LaunchMessages.LaunchDialogTitle,
     69                     String.format(LaunchMessages.AndroidJUnitDelegate_NoRunnerMsg_s,
     70                             project.getName()));
     71             androidLaunch.stopLaunch();
     72             return;
     73         }
     74         // get the target app's package
     75         String targetAppPackage = getTargetPackage(manifestData, runner);
     76         if (targetAppPackage == null) {
     77             AdtPlugin.displayError(LaunchMessages.LaunchDialogTitle,
     78                     String.format(LaunchMessages.AndroidJUnitDelegate_NoTargetMsg_3s,
     79                             project.getName(), runner, SdkConstants.FN_ANDROID_MANIFEST_XML));
     80             androidLaunch.stopLaunch();
     81             return;
     82         }
     83         String testAppPackage = manifestData.getPackage();
     84         AndroidJUnitLaunchInfo junitLaunchInfo = new AndroidJUnitLaunchInfo(project,
     85                 testAppPackage, runner);
     86         junitLaunchInfo.setTestClass(getTestClass(configuration));
     87         junitLaunchInfo.setTestPackage(getTestPackage(configuration));
     88         junitLaunchInfo.setTestMethod(getTestMethod(configuration));
     89         junitLaunchInfo.setLaunch(androidLaunch);
     90         IAndroidLaunchAction junitLaunch = new AndroidJUnitLaunchAction(junitLaunchInfo);
     91 
     92         controller.launch(project, mode, applicationPackage, testAppPackage, targetAppPackage,
     93                 manifestData.getDebuggable(), manifestData.getMinSdkVersionString(),
     94                 junitLaunch, config, androidLaunch, monitor);
     95     }
     96 
     97     /**
     98      * Get the target Android application's package for the given instrumentation runner, or
     99      * <code>null</code> if it could not be found.
    100      *
    101      * @param manifestParser the {@link ManifestData} for the test project
    102      * @param runner the instrumentation runner class name
    103      * @return the target package or <code>null</code>
    104      */
    105     private String getTargetPackage(ManifestData manifestParser, String runner) {
    106         for (Instrumentation instr : manifestParser.getInstrumentations()) {
    107             if (instr.getName().equals(runner)) {
    108                 return instr.getTargetPackage();
    109             }
    110         }
    111         return null;
    112     }
    113 
    114     /**
    115      * Returns the test package stored in the launch configuration, or <code>null</code> if not
    116      * specified.
    117      *
    118      * @param configuration the {@link ILaunchConfiguration} to retrieve the test package info from
    119      * @return the test package or <code>null</code>.
    120      */
    121     private String getTestPackage(ILaunchConfiguration configuration) {
    122         // try to retrieve a package name from the JUnit container attribute
    123         String containerHandle = getStringLaunchAttribute(
    124                 JUnitLaunchConfigurationConstants.ATTR_TEST_CONTAINER, configuration);
    125         if (containerHandle != null && containerHandle.length() > 0) {
    126             IJavaElement element = JavaCore.create(containerHandle);
    127             // containerHandle could be a IProject, check if its a java package
    128             if (element.getElementType() == IJavaElement.PACKAGE_FRAGMENT) {
    129                 return element.getElementName();
    130             }
    131         }
    132         return null;
    133     }
    134 
    135     /**
    136      * Returns the test class stored in the launch configuration.
    137      *
    138      * @param configuration the {@link ILaunchConfiguration} to retrieve the test class info from
    139      * @return the test class. <code>null</code> if not specified.
    140      */
    141     private String getTestClass(ILaunchConfiguration configuration) {
    142         return getStringLaunchAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME,
    143                 configuration);
    144     }
    145 
    146     /**
    147      * Returns the test method stored in the launch configuration.
    148      *
    149      * @param configuration the {@link ILaunchConfiguration} to retrieve the test method info from
    150      * @return the test method. <code>null</code> if not specified.
    151      */
    152     private String getTestMethod(ILaunchConfiguration configuration) {
    153         return getStringLaunchAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_METHOD_NAME,
    154                 configuration);
    155     }
    156 
    157     /**
    158      * Gets a instrumentation runner for the launch.
    159      * <p/>
    160      * If a runner is stored in the given <code>configuration</code>, will return that.
    161      * Otherwise, will try to find the first valid runner for the project.
    162      * If a runner can still not be found, will return <code>null</code>, and will log an error
    163      * to the console.
    164      *
    165      * @param project the {@link IProject} for the app
    166      * @param configuration the {@link ILaunchConfiguration} for the launch
    167      * @param manifestData the {@link ManifestData} for the project
    168      *
    169      * @return <code>null</code> if no instrumentation runner can be found, otherwise return
    170      *   the fully qualified runner name.
    171      */
    172     private String getRunner(IProject project, ILaunchConfiguration configuration,
    173             ManifestData manifestData) {
    174         try {
    175             String runner = getRunnerFromConfig(configuration);
    176             if (runner != null) {
    177                 return runner;
    178             }
    179             final InstrumentationRunnerValidator instrFinder = new InstrumentationRunnerValidator(
    180                     BaseProjectHelper.getJavaProject(project), manifestData);
    181             runner = instrFinder.getValidInstrumentationTestRunner();
    182             if (runner != null) {
    183                 AdtPlugin.printErrorToConsole(project, String.format(
    184                         LaunchMessages.AndroidJUnitDelegate_NoRunnerConfigMsg_s, runner));
    185                 return runner;
    186             }
    187             AdtPlugin.printErrorToConsole(project, String.format(
    188                     LaunchMessages.AndroidJUnitDelegate_NoRunnerConsoleMsg_4s,
    189                     project.getName(),
    190                     SdkConstants.CLASS_INSTRUMENTATION_RUNNER,
    191                     AdtConstants.LIBRARY_TEST_RUNNER,
    192                     SdkConstants.FN_ANDROID_MANIFEST_XML));
    193             return null;
    194         } catch (CoreException e) {
    195             AdtPlugin.log(e, "Error when retrieving instrumentation info"); //$NON-NLS-1$
    196         }
    197 
    198         return null;
    199     }
    200 
    201     private String getRunnerFromConfig(ILaunchConfiguration configuration) throws CoreException {
    202         return getStringLaunchAttribute(ATTR_INSTR_NAME, configuration);
    203     }
    204 
    205     /**
    206      * Helper method to retrieve a string attribute from the launch configuration
    207      *
    208      * @param attributeName name of the launch attribute
    209      * @param configuration the {@link ILaunchConfiguration} to retrieve the attribute from
    210      * @return the attribute's value. <code>null</code> if not found.
    211      */
    212     private String getStringLaunchAttribute(String attributeName,
    213             ILaunchConfiguration configuration) {
    214         try {
    215             String attrValue = configuration.getAttribute(attributeName, EMPTY_STRING);
    216             if (attrValue.length() < 1) {
    217                 return null;
    218             }
    219             return attrValue;
    220         } catch (CoreException e) {
    221             AdtPlugin.log(e, String.format("Error when retrieving launch info %1$s",  //$NON-NLS-1$
    222                     attributeName));
    223         }
    224         return null;
    225     }
    226 
    227     /**
    228      * Helper method to set JUnit-related attributes expected by JDT JUnit runner
    229      *
    230      * @param config the launch configuration to modify
    231      */
    232     static void setJUnitDefaults(ILaunchConfigurationWorkingCopy config) {
    233         // set the test runner to JUnit3 to placate JDT JUnit runner logic
    234         config.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_RUNNER_KIND,
    235                 TestKindRegistry.JUNIT3_TEST_KIND_ID);
    236     }
    237 }
    238