diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/JUnitLaunchConfigDelegate.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/JUnitLaunchConfigDelegate.java | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/JUnitLaunchConfigDelegate.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/JUnitLaunchConfigDelegate.java new file mode 100644 index 000000000..fdb1b305a --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/JUnitLaunchConfigDelegate.java @@ -0,0 +1,156 @@ +/* + * 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.internal.launch; + +import com.android.SdkConstants; +import com.android.ide.eclipse.adt.AdtConstants; +import com.android.ide.eclipse.adt.AdtPlugin; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.Platform; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.jdt.junit.launcher.JUnitLaunchConfigurationDelegate; +import org.osgi.framework.Bundle; + +import java.io.IOException; +import java.net.URL; + +/** + * <p> + * For Android projects, android.jar gets added to the launch configuration of + * JUnit tests as a bootstrap entry. This breaks JUnit tests as android.jar + * contains a skeleton version of JUnit classes and the JVM will stop with an error similar + * to: <blockquote> Error occurred during initialization of VM + * java/lang/NoClassDefFoundError: java/lang/ref/FinalReference </blockquote> + * <p> + * At compile time, Eclipse does not know that there is no valid junit.jar in + * the classpath since it can find a correct reference to all the necessary + * org.junit.* classes in the android.jar so it does not prompt the user to add + * the JUnit3 or JUnit4 jar. + * <p> + * This delegates removes the android.jar from the bootstrap path and if + * necessary also puts back the junit.jar in the user classpath. + * <p> + * This delegate will be present for both Java and Android projects (delegates + * setting instead of only the current project) but the behavior for Java + * projects should be neutral since: + * <ol> + * <li>Java tests can only compile (and then run) when a valid junit.jar is + * present + * <li>There is no android.jar in Java projects + * </ol> + */ +public class JUnitLaunchConfigDelegate extends JUnitLaunchConfigurationDelegate { + + private static final String JUNIT_JAR = "junit.jar"; //$NON-NLS-1$ + + @Override + public String[][] getBootpathExt(ILaunchConfiguration configuration) throws CoreException { + String[][] bootpath = super.getBootpathExt(configuration); + return fixBootpathExt(bootpath); + } + + @Override + public String[] getClasspath(ILaunchConfiguration configuration) throws CoreException { + String[] classpath = super.getClasspath(configuration); + return fixClasspath(classpath, getJavaProjectName(configuration)); + } + + /** + * Removes the android.jar from the bootstrap path if present. + * + * @param bootpath Array of Arrays of bootstrap class paths + * @return a new modified (if applicable) bootpath + */ + public static String[][] fixBootpathExt(String[][] bootpath) { + for (int i = 0; i < bootpath.length; i++) { + if (bootpath[i] != null && bootpath[i].length > 0) { + // we assume that the android.jar can only be present in the + // bootstrap path of android tests + if (bootpath[i][0].endsWith(SdkConstants.FN_FRAMEWORK_LIBRARY)) { + bootpath[i] = null; + } + } + } + return bootpath; + } + + /** + * Add the junit.jar to the user classpath; since Eclipse was relying on + * android.jar to provide the appropriate org.junit classes, it does not + * know it actually needs the junit.jar. + * + * @param classpath Array containing classpath + * @param projectName The name of the project (for logging purposes) + * + * @return a new modified (if applicable) classpath + */ + public static String[] fixClasspath(String[] classpath, String projectName) { + // search for junit.jar; if any are found return immediately + for (int i = 0; i < classpath.length; i++) { + if (classpath[i].endsWith(JUNIT_JAR)) { + return classpath; + } + } + + // This delegate being called without a junit.jar present is only + // possible for Android projects. In a non-Android project, the test + // would not compile and would be unable to run. + try { + // junit4 is backward compatible with junit3 and they uses the + // same junit.jar from bundle org.junit: + // When a project has mixed JUnit3 and JUnit4 tests, if JUnit3 jar + // is added first it is then replaced by the JUnit4 jar when user is + // prompted to fix the JUnit4 test failure + String jarLocation = getJunitJarLocation(); + // we extend the classpath by one element and append junit.jar + String[] newClasspath = new String[classpath.length + 1]; + System.arraycopy(classpath, 0, newClasspath, 0, classpath.length); + newClasspath[newClasspath.length - 1] = jarLocation; + classpath = newClasspath; + } catch (IOException e) { + // This should not happen as we depend on the org.junit + // plugin explicitly; the error is logged here so that the user can + // trace back the cause when the test fails to run + AdtPlugin.log(e, "Could not find a valid junit.jar"); + AdtPlugin.printErrorToConsole(projectName, + "Could not find a valid junit.jar"); + // Return the classpath as-is (with no junit.jar) anyway because we + // will let the actual launch config fails. + } + + return classpath; + } + + /** + * Returns the path of the junit jar in the highest version bundle. + * + * (This is public only so that the test can call it) + * + * @return the path as a string + * @throws IOException + */ + public static String getJunitJarLocation() throws IOException { + Bundle bundle = Platform.getBundle("org.junit"); //$NON-NLS-1$ + if (bundle == null) { + throw new IOException("Cannot find org.junit bundle"); + } + URL jarUrl = bundle.getEntry(AdtConstants.WS_SEP + JUNIT_JAR); + return FileLocator.resolve(jarUrl).getFile(); + } +} |