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