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 doLaunch(final ILaunchConfiguration configuration, final String mode, IProgressMonitor monitor, IProject project, final AndroidLaunch androidLaunch, AndroidLaunchConfiguration config, AndroidLaunchController controller, IFile applicationPackage, ManifestData manifestData)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 */ getTargetPackage(ManifestData manifestParser, String runner)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 */ getTestPackage(ILaunchConfiguration configuration)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 */ getTestClass(ILaunchConfiguration configuration)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 */ getTestMethod(ILaunchConfiguration configuration)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 */ getRunner(IProject project, ILaunchConfiguration configuration, ManifestData manifestData)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 getRunnerFromConfig(ILaunchConfiguration configuration)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 */ getStringLaunchAttribute(String attributeName, ILaunchConfiguration configuration)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 */ setJUnitDefaults(ILaunchConfigurationWorkingCopy config)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