/******************************************************************************* * Copyright Technophobia Ltd 2012 * * This file is part of the Substeps Eclipse Plugin. * * The Substeps Eclipse Plugin is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License v1.0. * * The Substeps Eclipse Plugin is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * Eclipse Public License for more details. * * You should have received a copy of the Eclipse Public License * along with the Substeps Eclipse Plugin. If not, see <http://www.eclipse.org/legal/epl-v10.html>. ******************************************************************************/ package com.technophobia.substeps.junit.ui; import org.eclipse.core.runtime.Assert; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.ToolBarManager; import org.eclipse.jface.util.IOpenEventListener; import org.eclipse.jface.util.OpenStrategy; import org.eclipse.swt.SWT; import org.eclipse.swt.dnd.Clipboard; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.ToolBar; import com.technophobia.eclipse.ui.NotifyingUiUpdater; import com.technophobia.eclipse.ui.Refreshable; import com.technophobia.eclipse.ui.Resettable; import com.technophobia.substeps.junit.action.CompareResultsAction; import com.technophobia.substeps.junit.action.EnableStackFilterAction; import com.technophobia.substeps.junit.action.OpenEditorAtLineAction; import com.technophobia.substeps.junit.action.SubstepsCopyAction; import com.technophobia.substeps.model.structure.SubstepsTestElement; import com.technophobia.substeps.preferences.PreferencesConstants; import com.technophobia.substeps.supplier.Supplier; public class FailureTrace implements IMenuListener, Refreshable, Resettable { private static final int MAX_LABEL_LENGTH = 256; static final String FRAME_PREFIX = "at "; //$NON-NLS-1$ private final Table table; private String inputTrace; private final Clipboard clipboard; private SubstepsTestElement failure; private final CompareResultsAction compareAction; private final FailureTableDisplay failureTableDisplay; private final Composite parent; private final Supplier<SubstepsRunSession> substepsRunSessionSupplier; private final NotifyingUiUpdater<String> infoMessageNotifier; public FailureTrace(final Composite parent, final Clipboard clipboard, final ToolBar toolBar, final SubstepsIconProvider iconProvider, final NotifyingUiUpdater<String> infoMessageUpdater, final Supplier<SubstepsRunSession> substepsRunSessionSupplier) { Assert.isNotNull(clipboard); this.infoMessageNotifier = infoMessageUpdater; this.substepsRunSessionSupplier = substepsRunSessionSupplier; // fill the failure trace viewer toolbar final ToolBarManager failureToolBarmanager = new ToolBarManager(toolBar); failureToolBarmanager.add(new EnableStackFilterAction(this, iconProvider)); compareAction = new CompareResultsAction(parent.getShell(), failedTestSupplier(), iconProvider); compareAction.setEnabled(false); failureToolBarmanager.add(compareAction); failureToolBarmanager.update(true); this.table = new Table(parent, SWT.SINGLE | SWT.V_SCROLL | SWT.H_SCROLL); this.clipboard = clipboard; this.parent = parent; final OpenStrategy handler = new OpenStrategy(table); handler.addOpenListener(new IOpenEventListener() { @Override public void handleOpen(final SelectionEvent e) { if (table.getSelectionIndex() == 0 && failure.isComparisonFailure()) { compareAction.run(); } if (table.getSelection().length != 0) { final Action a = createOpenEditorAction(getSelectedText()); if (a != null) a.run(); } } }); initMenu(); failureTableDisplay = new FailureTableDisplay(table, iconProvider); } private void initMenu() { final MenuManager menuMgr = new MenuManager(); menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(this); final Menu menu = menuMgr.createContextMenu(table); table.setMenu(menu); } @Override public void menuAboutToShow(final IMenuManager manager) { if (table.getSelectionCount() > 0) { final Action a = createOpenEditorAction(getSelectedText()); if (a != null) manager.add(a); manager.add(new SubstepsCopyAction(parent.getShell(), this, clipboard)); } // fix for bug 68058 if (failure != null && failure.isComparisonFailure()) manager.add(compareAction); } public String getTrace() { return inputTrace; } private String getSelectedText() { return table.getSelection()[0].getText(); } private Action createOpenEditorAction(final String traceLine) { try { String testName = traceLine; testName = testName.substring(testName.indexOf(FRAME_PREFIX)); testName = testName.substring(FRAME_PREFIX.length(), testName.lastIndexOf('(')).trim(); testName = testName.substring(0, testName.lastIndexOf('.')); final int innerSeparatorIndex = testName.indexOf('$'); if (innerSeparatorIndex != -1) testName = testName.substring(0, innerSeparatorIndex); String lineNumber = traceLine; lineNumber = lineNumber.substring(lineNumber.indexOf(':') + 1, lineNumber.lastIndexOf(')')); final int line = Integer.valueOf(lineNumber).intValue(); return new OpenEditorAtLineAction(substepsRunSessionSupplier, infoMessageNotifier, getShell(), testName, line); } catch (final NumberFormatException e) { // No-op } catch (final IndexOutOfBoundsException e) { // No-op } return null; } /** * Returns the composite used to present the trace * * @return The composite */ Composite getComposite() { return table; } /** * Refresh the table from the trace. */ @Override public void refresh() { updateTable(inputTrace); } @Override public void reset() { clear(); } /** * Shows a TestFailure * * @param test * the failed test */ public void showFailure(final SubstepsTestElement test) { this.failure = test; String trace = ""; //$NON-NLS-1$ updateEnablement(test); if (test != null) trace = test.getTrace(); if (inputTrace == trace) return; inputTrace = trace; updateTable(trace); } public void updateEnablement(final SubstepsTestElement test) { final boolean enableCompare = test != null && test.isComparisonFailure(); compareAction.setEnabled(enableCompare); if (enableCompare) { compareAction.updateOpenDialog(test); } } private void updateTable(final String trace) { if (trace == null || trace.trim().equals("")) { //$NON-NLS-1$ clear(); return; } table.setRedraw(false); table.removeAll(); new TextualTrace(trace.trim(), getFilterPatterns()).display(failureTableDisplay, MAX_LABEL_LENGTH); table.setRedraw(true); } private String[] getFilterPatterns() { if (PreferencesConstants.getFilterStack()) return PreferencesConstants.getFilterPatterns(); return new String[0]; } /** * Shows other information than a stack trace. * * @param text * the informational message to be shown */ public void setInformation(final String text) { clear(); final TableItem tableItem = failureTableDisplay.newTableItem(); tableItem.setText(text); } /** * Clears the non-stack trace info */ public void clear() { table.removeAll(); inputTrace = null; } public Supplier<SubstepsTestElement> failedTestSupplier() { return new Supplier<SubstepsTestElement>() { @Override public SubstepsTestElement get() { return failure; } }; } public Shell getShell() { return table.getShell(); } public TraceDisplay getFailureTableDisplay() { return failureTableDisplay; } }