/******************************************************************************* * Copyright (c) 2010, 2011 Tasktop Technologies and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Tasktop Technologies - initial API and implementation * Itema AS - Corrected lazy initialisation of fields *******************************************************************************/ package org.eclipse.mylyn.internal.builds.ui.util; import java.lang.reflect.Method; import java.util.concurrent.atomic.AtomicReference; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.search.IJavaSearchConstants; import org.eclipse.jdt.core.search.SearchEngine; import org.eclipse.jdt.core.search.SearchPattern; import org.eclipse.jdt.core.search.TypeNameMatch; import org.eclipse.jdt.core.search.TypeNameMatchRequestor; import org.eclipse.jdt.internal.junit.model.JUnitModel; import org.eclipse.jdt.internal.junit.model.TestRunHandler; import org.eclipse.jdt.internal.junit.model.TestRunSession; import org.eclipse.jdt.internal.junit.ui.TestRunnerViewPart; import org.eclipse.jdt.ui.JavaUI; import org.eclipse.mylyn.builds.core.IBuild; import org.eclipse.mylyn.builds.core.IBuildPlan; import org.eclipse.mylyn.builds.core.ITestCase; import org.eclipse.mylyn.builds.core.ITestSuite; import org.eclipse.mylyn.builds.internal.core.operations.OperationChangeEvent; import org.eclipse.mylyn.builds.internal.core.operations.OperationChangeListener; import org.eclipse.mylyn.builds.internal.core.operations.RefreshOperation; import org.eclipse.mylyn.builds.internal.core.util.JUnitResultGenerator; import org.eclipse.mylyn.commons.core.ICoreRunnable; import org.eclipse.mylyn.commons.workbench.WorkbenchUtil; import org.eclipse.mylyn.internal.builds.ui.BuildsUiInternal; import org.eclipse.mylyn.internal.builds.ui.BuildsUiPlugin; import org.eclipse.ui.IViewPart; import org.eclipse.ui.statushandlers.StatusManager; import org.xml.sax.SAXException; /** * @author Steffen Pingel * @author Torkild U. Resheim */ public class TestResultManager { /** * Encapsulates JUnit dependencies to avoid ClassNotFoundException when JUnit is not available. */ static class Runner { private static volatile JUnitModel junitModel; /** * @see {@link org.eclipse.jdt.internal.junit.ui.OpenTestAction} */ static IType findType(String className, IProgressMonitor monitor) throws CoreException { final IType[] result = { null }; TypeNameMatchRequestor nameMatchRequestor = new TypeNameMatchRequestor() { @Override public void acceptTypeNameMatch(TypeNameMatch match) { result[0] = match.getType(); } }; int lastDot = className.lastIndexOf('.'); char[] packageName = lastDot >= 0 ? className.substring(0, lastDot).toCharArray() : null; char[] typeName = (lastDot >= 0 ? className.substring(lastDot + 1) : className).toCharArray(); SearchEngine engine = new SearchEngine(); engine.searchAllTypeNames(packageName, SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE, typeName, SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE, IJavaSearchConstants.TYPE, SearchEngine.createWorkspaceScope(), nameMatchRequestor, IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, monitor); return result[0]; } static JUnitModel getJUnitModel() { if (junitModel == null) { try { // Eclipse 3.6 or later Class<?> clazz; try { clazz = Class.forName("org.eclipse.jdt.internal.junit.JUnitCorePlugin"); //$NON-NLS-1$ } catch (ClassNotFoundException e) { // Eclipse 3.5 and earlier clazz = Class.forName("org.eclipse.jdt.internal.junit.ui.JUnitPlugin"); //$NON-NLS-1$ } Method method = clazz.getDeclaredMethod("getModel"); //$NON-NLS-1$ junitModel = (JUnitModel) method.invoke(null); } catch (Exception e) { NoClassDefFoundError error = new NoClassDefFoundError("Unable to locate container for JUnitModel"); //$NON-NLS-1$ error.initCause(e); throw error; } } return junitModel; } public static void openInEditor(final String className, final String testName) { final AtomicReference<IJavaElement> result = new AtomicReference<IJavaElement>(); try { WorkbenchUtil.busyCursorWhile(new ICoreRunnable() { public void run(IProgressMonitor monitor) throws CoreException { IType type = findType(className, monitor); if (type == null) { return; } result.set(type); if (testName != null) { IMethod method = type.getMethod(testName, new String[0]); if (method != null && method.exists()) { result.set(method); } } } }); if (result.get() == null) { StatusManager.getManager() .handle(new Status(IStatus.ERROR, BuildsUiPlugin.ID_PLUGIN, "Failed to locate test in workspace."), //$NON-NLS-1$ StatusManager.SHOW | StatusManager.BLOCK); return; } JavaUI.openInEditor(result.get(), true, true); } catch (OperationCanceledException e) { return; } catch (Exception e) { StatusManager.getManager() .handle(new Status(IStatus.ERROR, BuildsUiPlugin.ID_PLUGIN, "Failed to locate test in workspace.", e), //$NON-NLS-1$ StatusManager.SHOW | StatusManager.BLOCK | StatusManager.LOG); return; } } static void showInJUnitViewInternal(final IBuild build) { final TestRunSession testRunSession = new TestResultSession(build); try { WorkbenchUtil.busyCursorWhile(new ICoreRunnable() { public void run(IProgressMonitor monitor) throws CoreException { JUnitResultGenerator generator = new JUnitResultGenerator(build.getTestResult()); generator.setIncludeIgnored(jUnitSupportIgnoredTests()); TestRunHandler handler = new TestRunHandler(testRunSession); try { generator.write(handler); } catch (SAXException e) { throw new CoreException(new Status(IStatus.ERROR, BuildsUiPlugin.ID_PLUGIN, "Unexpected parsing error while preparing test results", e)); //$NON-NLS-1$ } } }); } catch (OperationCanceledException e) { return; } catch (CoreException e) { StatusManager.getManager() .handle(new Status(IStatus.ERROR, BuildsUiPlugin.ID_PLUGIN, "Unexpected error while processing test results", e), //$NON-NLS-1$ StatusManager.SHOW | StatusManager.LOG); return; } // show results in view IViewPart part = WorkbenchUtil.showViewInActiveWindow(TestRunnerViewPart.NAME); showFailuresOnly(part); getJUnitModel().addTestRunSession(testRunSession); } static void showFailuresOnly(IViewPart part) { try { boolean showFailuresOnly = BuildsUiPlugin.getDefault() .getPreferenceStore() .getBoolean(BuildsUiInternal.PREF_SHOW_TEST_FAILURES_ONLY); Method method = TestRunnerViewPart.class.getDeclaredMethod("setShowFailuresOnly", boolean.class); //$NON-NLS-1$ method.setAccessible(true); method.invoke(part, showFailuresOnly); } catch (Throwable t) { // ignore } } } public static boolean isJUnitAvailable() { return Platform.getBundle("org.eclipse.jdt.junit") != null; //$NON-NLS-1$ } static boolean jUnitSupportIgnoredTests() { // supported on Eclipse 3.6 and later return Platform.getBundle("org.eclipse.jdt.junit.core") != null; //$NON-NLS-1$ } public static void openInEditor(ITestCase testCase) { if (!isJUnitAvailable()) { return; } Runner.openInEditor(testCase.getClassName(), testCase.getLabel()); } public static void openInEditor(ITestSuite suite) { if (!isJUnitAvailable()) { return; } Runner.openInEditor(suite.getLabel(), null); } public static void showInJUnitView(final IBuild build) { Assert.isNotNull(build); if (!isJUnitAvailable()) { StatusManager.getManager().handle( new Status(IStatus.ERROR, BuildsUiPlugin.ID_PLUGIN, "JUnit is not installed."), //$NON-NLS-1$ StatusManager.SHOW | StatusManager.BLOCK); return; } if (build.getTestResult() == null) { StatusManager.getManager() .handle(new Status(IStatus.ERROR, BuildsUiPlugin.ID_PLUGIN, "The build did not produce test results."), //$NON-NLS-1$ StatusManager.SHOW | StatusManager.BLOCK); return; } // invoke separate method to avoid ClassNotFoundException when JUnit is not available Runner.showInJUnitViewInternal(build); } public static void showInJUnitView(final IBuildPlan plan) { if (plan.getLastBuild() != null) { showInJUnitView(plan.getLastBuild()); } else { RefreshOperation operation = BuildsUiInternal.getFactory().getRefreshOperation(plan); operation.addOperationChangeListener(new OperationChangeListener() { @Override public void done(OperationChangeEvent event) { event.getOperation().getService().getRealm().asyncExec(new Runnable() { public void run() { if (plan.getLastBuild() != null) { showInJUnitView(plan.getLastBuild()); } } }); } }); operation.execute(); } } }