import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
-import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.internal.junit.launcher.JUnitLaunchConfigurationConstants;
import org.eclipse.jdt.internal.junit.launcher.TestKindRegistry;
/**
- * Run configuration that can execute JUnit tests on an Android platform
+ * Run configuration that can execute JUnit tests on an Android platform.
* <p/>
- * Will deploy apps on target Android platform by reusing functionality from ADT
- * LaunchConfigDelegate, and then run JUnits tests by reusing functionality from JDT
+ * Will deploy apps on target Android platform by reusing functionality from ADT
+ * LaunchConfigDelegate, and then run JUnits tests by reusing functionality from JDT
* JUnitLaunchConfigDelegate.
*/
-@SuppressWarnings("restriction") //$NON-NLS-1$
+@SuppressWarnings("restriction")
public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate {
- /** Launch config attribute that stores instrumentation runner */
+ /** Launch config attribute that stores instrumentation runner. */
static final String ATTR_INSTR_NAME = AdtPlugin.PLUGIN_ID + ".instrumentation"; //$NON-NLS-1$
- static final String INSTRUMENTATION_OK = null;
private static final String EMPTY_STRING = ""; //$NON-NLS-1$
@Override
IFile applicationPackage, AndroidManifestParser manifestParser) {
String testPackage = manifestParser.getPackage();
- String runner = getRunnerFromConfig(configuration);
+ String runner = getRunner(project, configuration, manifestParser);
if (runner == null) {
AdtPlugin.displayError("Android Launch",
"An instrumention test runner is not specified!");
manifestParser.getDebuggable(), manifestParser.getApiLevelRequirement(),
junitLaunch, config, androidLaunch, monitor);
}
-
- private String getRunnerFromConfig(ILaunchConfiguration configuration) {
- String runner = EMPTY_STRING;
+
+ /**
+ * Gets a instrumentation runner for the launch.
+ * <p/>
+ * If a runner is stored in the given <code>configuration</code>, will return that.
+ * Otherwise, will try to find the first valid runner for the project.
+ * If a runner can still not be found, will return <code>null</code>.
+ *
+ * @param project the {@link IProject} for the app
+ * @param configuration the {@link ILaunchConfiguration} for the launch
+ * @param manifestParser the {@link AndroidManifestParser} for the project
+ *
+ * @return <code>null</code> if no instrumentation runner can be found, otherwise return
+ * the fully qualified runner name.
+ */
+ private String getRunner(IProject project, ILaunchConfiguration configuration,
+ AndroidManifestParser manifestParser) {
try {
- runner = configuration.getAttribute(ATTR_INSTR_NAME, EMPTY_STRING);
+ String runner = getRunnerFromConfig(configuration);
+ if (runner != null) {
+ return runner;
+ }
+ final InstrumentationRunnerValidator instrFinder = new InstrumentationRunnerValidator(
+ BaseProjectHelper.getJavaProject(project), manifestParser);
+ runner = instrFinder.getValidInstrumentationTestRunner();
+ if (runner != null) {
+ AdtPlugin.printErrorToConsole(project,
+ String.format("Warning: No instrumentation runner found for the launch, " +
+ "using %1$s", runner));
+ return runner;
+ }
+ AdtPlugin.printErrorToConsole(project,
+ String.format("ERROR: Application does not specify a %1$s instrumentation or does not declare uses-library %2$s",
+ AndroidConstants.CLASS_INSTRUMENTATION_RUNNER,
+ AndroidConstants.LIBRARY_TEST_RUNNER));
+ return null;
} catch (CoreException e) {
- AdtPlugin.log(e, "Error when retrieving instrumentation info from launch config"); //$NON-NLS-1$
+ AdtPlugin.log(e, "Error when retrieving instrumentation info"); //$NON-NLS-1$
}
+ return null;
+
+ }
+
+ private String getRunnerFromConfig(ILaunchConfiguration configuration) throws CoreException {
+ String runner = configuration.getAttribute(ATTR_INSTR_NAME, EMPTY_STRING);
if (runner.length() < 1) {
return null;
}
}
/**
- * Helper method to return the set of instrumentations for the Android project
- *
- * @param project the {@link IProject} to get instrumentations for
- * @return null if error occurred parsing instrumentations, otherwise returns array of
- * instrumentation class names
- */
- static String[] getInstrumentationsForProject(IProject project) {
- if (project != null) {
- try {
- // parse the manifest for the list of instrumentations
- AndroidManifestParser manifestParser = AndroidManifestParser.parse(
- BaseProjectHelper.getJavaProject(project), null /* errorListener */,
- true /* gatherData */, false /* markErrors */);
- if (manifestParser != null) {
- return manifestParser.getInstrumentations();
- }
- } catch (CoreException e) {
- AdtPlugin.log(e, "%s: Error parsing AndroidManifest.xml", //$NON-NLS-1$
- project.getName());
- }
- }
- return null;
- }
-
- /**
* Helper method to set JUnit-related attributes expected by JDT JUnit runner
*
* @param config the launch configuration to modify
config.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_RUNNER_KIND,
TestKindRegistry.JUNIT3_TEST_KIND_ID);
}
-
- /**
- * Helper method to determine if specified instrumentation can be used as a test runner
- *
- * @param project the {@link IJavaProject} to validate
- * @param instrumentation the instrumentation class name to validate
- * @return <code>INSTRUMENTATION_OK</code> if valid, otherwise returns error message
- */
- static String validateInstrumentationRunner(IJavaProject project, String instrumentation) {
- AndroidManifestParser manifestParser;
- try {
- manifestParser = AndroidManifestParser.parse(
- project, null /* errorListener */,
- true /* gatherData */, false /* markErrors */);
- // check if this instrumentation is the standard test runner
- if (!instrumentation.equals(AndroidConstants.CLASS_INSTRUMENTATION_RUNNER)) {
- // check if it extends the standard test runner
- String result = BaseProjectHelper.testClassForManifest(project,
- instrumentation, AndroidConstants.CLASS_INSTRUMENTATION_RUNNER, true);
- if (result != BaseProjectHelper.TEST_CLASS_OK) {
- return String.format("The instrumentation runner must be of type %s",
- AndroidConstants.CLASS_INSTRUMENTATION_RUNNER);
- }
- }
- if (!hasTestRunnerLibrary(manifestParser)) {
- return String.format("%s does not not use the %s library",
- project.getProject().getName(), AndroidConstants.LIBRARY_TEST_RUNNER);
- }
- } catch (CoreException e) {
- String err = String.format("Error parsing AndroidManifest for %s",
- project.getProject().getName());
- AdtPlugin.log(e, err);
- return err;
- }
- return INSTRUMENTATION_OK;
- }
-
- /**
- * Helper method to determine if given manifest has a <code>AndroidConstants.LIBRARY_TEST_RUNNER
- * </code> library reference
- *
- * @param manifestParser the {@link AndroidManifestParser} to search
- * @return true if test runner library found, false otherwise
- */
- static boolean hasTestRunnerLibrary(AndroidManifestParser manifestParser) {
- for (String lib : manifestParser.getUsesLibraries()) {
- if (lib.equals(AndroidConstants.LIBRARY_TEST_RUNNER)) {
- return true;
- }
- }
- return false;
- }
}
private Image mTabIcon = null;
private Combo mInstrumentationCombo;
private static final String EMPTY_STRING = ""; //$NON-NLS-1$
+ private static final String TAG = "AndroidJUnitLaunchConfigurationTab"; //$NON-NLS-1$
private String[] mInstrumentations = null;
+ private InstrumentationRunnerValidator mInstrValidator = null;
private ProjectChooserHelper mProjectChooserHelper;
/* (non-Javadoc)
break;
}
}
- }
+ }
} catch (CoreException ce) {
// ignore
}
mapResources(config);
} catch (CoreException e) {
// TODO: does the real error need to be extracted out of CoreException
- AdtPlugin.log(e, "Error occurred saving configuration");
+ AdtPlugin.log(e, "Error occurred saving configuration"); //$NON-NLS-1$
}
AndroidJUnitLaunchConfigDelegate.setJUnitDefaults(config);
public Image getImage() {
// reuse icon from the Android App Launch config tab
if (mTabIcon == null) {
- mTabIcon= AdtPlugin.getImageLoader().loadImage(MainLaunchConfigTab.LAUNCH_TAB_IMAGE,
+ mTabIcon = AdtPlugin.getImageLoader().loadImage(MainLaunchConfigTab.LAUNCH_TAB_IMAGE,
null);
}
return mTabIcon;
setErrorMessage(e.getMessage());
return;
} catch (InvocationTargetException e) {
- AdtPlugin.log(e.getTargetException(), "Error finding test types");
+ AdtPlugin.log(e.getTargetException(), "Error finding test types"); //$NON-NLS-1$
return;
} finally {
mTestRadioButton.setSelection(radioSetting[0]);
return;
}
} catch (CoreException e) {
- AdtPlugin.log(e, "validatePage failed");
+ AdtPlugin.log(e, "validatePage failed"); //$NON-NLS-1$
}
- validateInstrumentation(javaProject);
+ validateInstrumentation();
}
private void validateJavaProject(IJavaProject javaProject) {
}
}
- private void validateInstrumentation(IJavaProject javaProject) {
- if (mInstrumentations == null || mInstrumentations.length < 1) {
- setErrorMessage("Specified project has no defined instrumentations");
- return;
- }
+ private void validateInstrumentation() {
String instrumentation = getSelectedInstrumentation();
if (instrumentation == null) {
- setErrorMessage("Instrumentation not specified");
+ setErrorMessage("Instrumentation runner not specified");
return;
}
- String result = AndroidJUnitLaunchConfigDelegate.validateInstrumentationRunner(
- javaProject, instrumentation);
- if (result != AndroidJUnitLaunchConfigDelegate.INSTRUMENTATION_OK) {
+ String result = mInstrValidator.validateInstrumentationRunner(instrumentation);
+ if (result != InstrumentationRunnerValidator.INSTRUMENTATION_OK) {
setErrorMessage(result);
return;
}
/**
* Loads the UI with the instrumentations of the specified project, and stores the
- * activities in <code>mActivities</code>.
- * <p/>
- * First activity is selected by default if present.
+ * instrumentations in <code>mInstrumentations</code>.
*
* @param project the {@link IProject} to load the instrumentations from.
*/
private void loadInstrumentations(IProject project) {
- mInstrumentations = AndroidJUnitLaunchConfigDelegate.getInstrumentationsForProject(project);
+ try {
+ mInstrValidator = new InstrumentationRunnerValidator(project);
+ mInstrumentations = (mInstrValidator == null ? null :
+ mInstrValidator.getInstrumentations());
if (mInstrumentations != null) {
mInstrumentationCombo.removeAll();
for (String instrumentation : mInstrumentations) {
// config object.
return;
}
-
+ } catch (CoreException e) {
+ AdtPlugin.logAndPrintError(e, TAG, "ERROR: Failed to get instrumentations for %1$s",
+ project.getName());
+ }
// if we reach this point, either project is null, or we got an exception during
// the parsing. In either case, we empty the instrumentation list.
+ mInstrValidator = null;
mInstrumentations = null;
mInstrumentationCombo.removeAll();
}
package com.android.ide.eclipse.adt.launch.junit;
-import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.common.AndroidConstants;
-
-import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.jdt.core.IJavaElement;
protected ILaunchConfigurationWorkingCopy createLaunchConfiguration(IJavaElement element)
throws CoreException {
ILaunchConfigurationWorkingCopy config = super.createLaunchConfiguration(element);
- IProject project = element.getResource().getProject();
- String[] instrumentations =
- AndroidJUnitLaunchConfigDelegate.getInstrumentationsForProject(project);
- boolean runnerFound = false;
- if (instrumentations != null) {
- // just pick the first valid runner
- for (String instr : instrumentations) {
- if (AndroidJUnitLaunchConfigDelegate.validateInstrumentationRunner(
- element.getJavaProject(), instr) ==
- AndroidJUnitLaunchConfigDelegate.INSTRUMENTATION_OK) {
-
- config.setAttribute(AndroidJUnitLaunchConfigDelegate.ATTR_INSTR_NAME,
- instr);
- runnerFound = true;
- break;
- }
- }
- }
- if (!runnerFound) {
- // TODO: put this in a string properties
- String msg = String.format("ERROR: Application does not specify a %s instrumentation or does not declare uses-library %s",
- AndroidConstants.CLASS_INSTRUMENTATION_RUNNER,
- AndroidConstants.LIBRARY_TEST_RUNNER);
- AdtPlugin.printErrorToConsole(project, msg);
+ // just get first valid instrumentation runner
+ String instrumentation = new InstrumentationRunnerValidator(element.getJavaProject()).
+ getValidInstrumentationTestRunner();
+ if (instrumentation != null) {
+ config.setAttribute(AndroidJUnitLaunchConfigDelegate.ATTR_INSTR_NAME,
+ instrumentation);
}
- AndroidJUnitLaunchConfigDelegate.setJUnitDefaults(config);
+ // if a valid runner is not found, rely on launch delegate to log error.
+ // This method is called without explicit user action to launch Android JUnit, so avoid
+ // logging an error here.
+ AndroidJUnitLaunchConfigDelegate.setJUnitDefaults(config);
return config;
}
}
--- /dev/null
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ide.eclipse.adt.launch.junit;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.common.AndroidConstants;
+import com.android.ide.eclipse.common.project.AndroidManifestParser;
+import com.android.ide.eclipse.common.project.BaseProjectHelper;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.IJavaProject;
+
+/**
+ * Provides validation for Android instrumentation test runner
+ */
+class InstrumentationRunnerValidator {
+ private final IJavaProject mJavaProject;
+ private String[] mInstrumentations = null;
+ private boolean mHasRunnerLibrary = false;
+
+ static final String INSTRUMENTATION_OK = null;
+
+ /**
+ * Initializes the InstrumentationRunnerValidator.
+ *
+ * @param javaProject the {@link IJavaProject} for the Android project to validate
+ */
+ InstrumentationRunnerValidator(IJavaProject javaProject) {
+ mJavaProject = javaProject;
+ try {
+ AndroidManifestParser manifestParser = AndroidManifestParser.parse(javaProject,
+ null /* errorListener */, true /* gatherData */, false /* markErrors */);
+ init(manifestParser);
+ } catch (CoreException e) {
+ AdtPlugin.printErrorToConsole(javaProject.getProject(), "ERROR: Failed to parse %1$s",
+ AndroidConstants.FN_ANDROID_MANIFEST);
+ }
+ }
+
+ /**
+ * Initializes the InstrumentationRunnerValidator.
+ *
+ * @param project the {@link IProject} for the Android project to validate
+ * @throws CoreException if a fatal error occurred in initialization
+ */
+ InstrumentationRunnerValidator(IProject project) throws CoreException {
+ this(BaseProjectHelper.getJavaProject(project));
+ }
+
+ /**
+ * Initializes the InstrumentationRunnerValidator with an existing {@link AndroidManifestParser}
+ *
+ * @param javaProject the {@link IJavaProject} for the Android project to validate
+ * @param manifestParser the {@link AndroidManifestParser} for the Android project
+ */
+ InstrumentationRunnerValidator(IJavaProject javaProject, AndroidManifestParser manifestParser) {
+ mJavaProject = javaProject;
+ init(manifestParser);
+ }
+
+ private void init(AndroidManifestParser manifestParser) {
+ mInstrumentations = manifestParser.getInstrumentations();
+ mHasRunnerLibrary = hasTestRunnerLibrary(manifestParser);
+ }
+
+ /**
+ * Helper method to determine if given manifest has a <code>AndroidConstants.LIBRARY_TEST_RUNNER
+ * </code> library reference
+ *
+ * @param manifestParser the {@link AndroidManifestParser} to search
+ * @return true if test runner library found, false otherwise
+ */
+ private boolean hasTestRunnerLibrary(AndroidManifestParser manifestParser) {
+ for (String lib : manifestParser.getUsesLibraries()) {
+ if (lib.equals(AndroidConstants.LIBRARY_TEST_RUNNER)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return the set of instrumentations for the Android project.
+ *
+ * @return <code>null</code if error occurred parsing instrumentations, otherwise returns array
+ * of instrumentation class names
+ */
+ String[] getInstrumentations() {
+ return mInstrumentations;
+ }
+
+ /**
+ * Helper method to get the first instrumentation that can be used as a test runner.
+ *
+ * @return fully qualified instrumentation class name. <code>null</code> if no valid
+ * instrumentation can be found.
+ */
+ String getValidInstrumentationTestRunner() {
+ for (String instrumentation : getInstrumentations()) {
+ if (validateInstrumentationRunner(instrumentation) == INSTRUMENTATION_OK) {
+ return instrumentation;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Helper method to determine if specified instrumentation can be used as a test runner
+ *
+ * @param instrumentation the instrumentation class name to validate. Assumes this
+ * instrumentation is one of {@link #getInstrumentations()}
+ * @return <code>INSTRUMENTATION_OK</code> if valid, otherwise returns error message
+ */
+ String validateInstrumentationRunner(String instrumentation) {
+ if (!mHasRunnerLibrary) {
+ return String.format("The application does not declare uses-library %1$s",
+ AndroidConstants.LIBRARY_TEST_RUNNER);
+ }
+ // check if this instrumentation is the standard test runner
+ if (!instrumentation.equals(AndroidConstants.CLASS_INSTRUMENTATION_RUNNER)) {
+ // check if it extends the standard test runner
+ String result = BaseProjectHelper.testClassForManifest(mJavaProject,
+ instrumentation, AndroidConstants.CLASS_INSTRUMENTATION_RUNNER, true);
+ if (result != BaseProjectHelper.TEST_CLASS_OK) {
+ return String.format("The instrumentation runner must be of type %s",
+ AndroidConstants.CLASS_INSTRUMENTATION_RUNNER);
+ }
+ }
+ return INSTRUMENTATION_OK;
+ }
+}