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