| /* |
| * 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(); |
| } |
| } |