/******************************************************************************* * 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 java.lang.reflect.InvocationTargetException; import java.util.List; import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.debug.ui.IDebugUIConstants; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IStatusLineManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CLabel; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.custom.ViewForm; import org.eclipse.swt.dnd.Clipboard; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.DropTarget; import org.eclipse.swt.dnd.DropTargetAdapter; import org.eclipse.swt.dnd.DropTargetEvent; import org.eclipse.swt.dnd.TextTransfer; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.ControlListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Layout; import org.eclipse.swt.widgets.ToolBar; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IEditorActionBarContributor; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IMemento; import org.eclipse.ui.IPropertyListener; import org.eclipse.ui.IViewPart; import org.eclipse.ui.IViewSite; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchPartSite; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.actions.ActionFactory; import org.eclipse.ui.handlers.IHandlerService; import org.eclipse.ui.part.EditorActionBarContributor; import org.eclipse.ui.part.PageSwitcher; import org.eclipse.ui.part.ViewPart; import org.eclipse.ui.progress.IWorkbenchSiteProgressService; import org.eclipse.ui.progress.UIJob; import org.eclipse.ui.statushandlers.StatusManager; import com.technophobia.eclipse.transformer.Callback; import com.technophobia.eclipse.ui.Notifier; import com.technophobia.eclipse.ui.NotifyingUiUpdater; import com.technophobia.eclipse.ui.Renderer; import com.technophobia.eclipse.ui.UiUpdater; import com.technophobia.eclipse.ui.job.StoppableJob; import com.technophobia.eclipse.ui.part.DefaultVisibilityPartMonitor; import com.technophobia.eclipse.ui.part.PartMonitor; import com.technophobia.eclipse.ui.render.OneTimeRenderUpdater; import com.technophobia.eclipse.ui.view.ViewLayout; import com.technophobia.eclipse.ui.view.ViewOrientation; import com.technophobia.substeps.FeatureRunnerPlugin; import com.technophobia.substeps.junit.action.ActivateOnErrorAction; import com.technophobia.substeps.junit.action.FailuresOnlyFilterAction; import com.technophobia.substeps.junit.action.RerunFailedFirstAction; import com.technophobia.substeps.junit.action.RerunLastTestAction; import com.technophobia.substeps.junit.action.ScrollLockAction; import com.technophobia.substeps.junit.action.ShowNextFailureAction; import com.technophobia.substeps.junit.action.ShowPreviousFailureAction; import com.technophobia.substeps.junit.action.ShowTestHierarchyAction; import com.technophobia.substeps.junit.action.ShowTimeAction; import com.technophobia.substeps.junit.action.StopTestAction; import com.technophobia.substeps.junit.action.SubstepsCopyAction; import com.technophobia.substeps.junit.action.SubstepsPasteAction; import com.technophobia.substeps.junit.action.ToggleOrientationAction; import com.technophobia.substeps.junit.ui.component.FeatureViewer; import com.technophobia.substeps.junit.ui.component.ProgressBar; import com.technophobia.substeps.junit.ui.component.SubstepsCounterPanel; import com.technophobia.substeps.junit.ui.handler.SubstepsHandlerServiceManager; import com.technophobia.substeps.junit.ui.help.SubstepsHelpContextIds; import com.technophobia.substeps.junit.ui.image.ProgressImages; import com.technophobia.substeps.junit.ui.job.ProcessRunningJob; import com.technophobia.substeps.junit.ui.job.UpdateJobManager; import com.technophobia.substeps.junit.ui.job.UpdateSubstepsUIJob; import com.technophobia.substeps.junit.ui.progress.SubstepsProgressBar; import com.technophobia.substeps.junit.ui.testsession.JunitTestResultsView; import com.technophobia.substeps.junit.ui.testsession.TestResultsView; import com.technophobia.substeps.junit.ui.viewhistory.RunnerViewHistory; import com.technophobia.substeps.model.SubstepsSessionListener; import com.technophobia.substeps.model.structure.Status; import com.technophobia.substeps.model.structure.SubstepsTestElement; import com.technophobia.substeps.model.structure.SubstepsTestLeafElement; import com.technophobia.substeps.preferences.PreferencesConstants; import com.technophobia.substeps.supplier.Supplier; public class SubstepsFeatureTestRunnerViewPart extends ViewPart implements UpdateJobManager, Notifier<Runnable>, Callback, IPropertyListener { public static final String NAME = "com.technophobia.substeps.runner.SubstepsResultView"; private static final IMarker FAMILY_SUBSTEPS_FEATURE_RUN = new IMarker() { // No-op }; private static final String RERUN_LAST_COMMAND = "com.technophobia.substeps.junit.rerunLast"; //$NON-NLS-1$ private static final String RERUN_FAILED_FIRST_COMMAND = "com.technophobia.substeps.junit.rerunFailedFirst"; //$NON-NLS-1$ private static final String TAG_SCROLL = "scroll"; private static final String TAG_RATIO = "ratio"; private static final String TAG_ORIENTATION = "orientation"; private static final String TAG_FAILURES_ONLY = "failuresOnly"; private static final String TAG_LAYOUT = "layout"; private static final String TAG_SHOW_TIME = "time"; private static final int REFRESH_INTERVAL = 200; private SubstepsIconProvider iconProvider; private ProgressImages progressImages; private IMemento memento; private SashForm sashForm; private ScrollLockAction scrollLockAction; private ToggleOrientationAction[] toggleOrientationActions; private Action showTestHierarchyAction; private ViewOrientation orientation = ViewOrientation.VIEW_ORIENTATION_AUTOMATIC; private ViewOrientation currentOrientation; private ViewLayout layout = ViewLayout.HIERARCHICAL; private Action failuresOnlyFilterAction; private Action showTimeAction; private UIJob updateJob; private StoppableJob substepsIsRunningJob; private Supplier<Boolean> disposedChecker; private UiUpdater uiUpdater; private UiUpdater testCounterUpdater; private UiUpdater viewTitleUiUpdater; // private UiUpdater toolbarUiUpdater; // private UiUpdater statusMessageUpdater; private final NotifyingUiUpdater<String> infoMessageUpdater; private FailureTrace failureTrace; private Supplier<TestRunStats> testRunStatsSupplier; private SubstepsRunSessionManager sessionManager; private SubstepsRunSessionListener testRunSessionListener; private SubstepsHandlerServiceManager handlerServiceManager; private TestResultsView testResultsView; private FeatureViewer testViewer; private PartMonitor partMonitor; private com.technophobia.substeps.junit.ui.component.CounterPanel counterPanel; private Clipboard clipboard; private DirtyListener dirtyListener; private IMenuListener viewMenuListener; private boolean autoScroll = false; private boolean disposed = false; private boolean showOnErrorOnly = false; private Composite parent; private RunnerViewHistory viewHistory; private SubstepsActionManager actionManager; private Composite counterComposite; private ProgressBar progressBar; private Image originalViewImage; private Image viewImage; public SubstepsFeatureTestRunnerViewPart() { this.disposedChecker = new Supplier<Boolean>() { @Override public Boolean get() { return Boolean.valueOf(isDisposed()); } }; this.iconProvider = new SubstepsIconProvider(new ImageDescriptorImageLoader(), new ImageDescriptorLoader()); this.infoMessageUpdater = new OneTimeRenderUpdater<String>(new Renderer<String>() { @Override public void render(final String t) { setContentDescription(t); } }); } @Override public void init(final IViewSite site, final IMemento m) throws PartInitException { super.init(site, m); this.memento = m; this.partMonitor = new DefaultVisibilityPartMonitor(getSite()); this.dirtyListener = new JavaCoreDirtyListener(); this.currentOrientation = ViewOrientation.VIEW_ORIENTATION_AUTOMATIC; this.testRunStatsSupplier = testRunStatsSupplier(); final IWorkbenchSiteProgressService progressService = getProgressService(); if (progressService != null) { progressService.showBusyForFamily(FAMILY_SUBSTEPS_FEATURE_RUN); } } private SubstepsActionManager createActionManager() { final Action stopAction = new StopTestAction(runSessionSupplier(), infoMessageUpdater, iconProvider); final Action copyAction = new SubstepsCopyAction(getSite().getShell(), failureTrace, clipboard); final Action rerunFailedFirstAction = new RerunFailedFirstAction(RERUN_FAILED_FIRST_COMMAND, new FailedTestFirstTestRelauncher(runSessionSupplier(), getSite().getShell(), infoMessageUpdater), iconProvider); final Action rerunLastTestAction = new RerunLastTestAction(RERUN_LAST_COMMAND, new TestRelauncher( runSessionSupplier(), getSite().getShell(), infoMessageUpdater), iconProvider); final Action nextAction = new ShowNextFailureAction(testViewer, iconProvider); final Action prevAction = new ShowPreviousFailureAction(testViewer, iconProvider); return new SubstepsActionManager(stopAction, copyAction, rerunFailedFirstAction, rerunLastTestAction, nextAction, prevAction); } @Override public void saveState(final IMemento m) { if (sashForm == null && m != null) { // Keep the old state; m.putMemento(this.memento); return; } decorateMemento(m); } public void setAutoScroll(final boolean scroll) { autoScroll = scroll; } public boolean isAutoScroll() { return autoScroll; } @Override public synchronized void dispose() { disposed = true; if (testRunSessionListener != null) FeatureRunnerPlugin.instance().getModel().removeTestRunSessionListener(testRunSessionListener); handlerServiceManager.deactivateHandlers(); sessionManager.setActiveState(null); if (progressImages != null) progressImages.dispose(); getViewSite().getPage().removePartListener(partMonitor); disposeImages(); if (clipboard != null) clipboard.dispose(); if (viewMenuListener != null) { getViewSite().getActionBars().getMenuManager().removeMenuListener(viewMenuListener); } dirtyListener.removeDirtyListener(); } @Override public void setFocus() { if (testViewer != null) testViewer.getTestViewerControl().setFocus(); } @Override public void createPartControl(final Composite parentComposite) { this.parent = parentComposite; addResizeListener(parentComposite); this.clipboard = new Clipboard(parentComposite.getDisplay()); this.testResultsView = new JunitTestResultsView(getSite().getWorkbenchWindow()); final GridLayout gridLayout = new GridLayout(); gridLayout.marginWidth = 0; gridLayout.marginHeight = 0; parentComposite.setLayout(gridLayout); final UiUpdater tooltipUpdater = new TooltipUpdater(tooltipNotifier()); final UiUpdater statusMessageUpdater = new StatusMessageUiUpdater(getViewSite()); this.counterComposite = createProgressCountPanel(parentComposite); final SashForm sash = createSashForm(parentComposite); this.actionManager = createActionManager(); this.sessionManager = new SubstepsRunSessionManager(disposedSashFormChecker(), testViewer, tooltipUpdater, infoMessageUpdater, viewTitleUiUpdater, statusMessageUpdater, failureTrace, testResultsView, actionManager, this, testSessionListenerSupplier()); this.handlerServiceManager = new SubstepsHandlerServiceManager((IHandlerService) getSite().getWorkbenchWindow() .getService(IHandlerService.class)); this.counterComposite.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL)); sash.setLayoutData(new GridData(GridData.FILL_BOTH)); final IActionBars actionBars = getViewSite().getActionBars(); final Action copyAction = new SubstepsCopyAction(parentComposite.getShell(), failureTrace, clipboard); copyAction.setActionDefinitionId(ActionFactory.COPY.getCommandId()); actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(), copyAction); final Action pasteAction = new SubstepsPasteAction(parentComposite.getShell(), clipboard); pasteAction.setActionDefinitionId(ActionFactory.PASTE.getCommandId()); actionBars.setGlobalActionHandler(ActionFactory.PASTE.getId(), pasteAction); this.viewHistory = new RunnerViewHistory(sessionManager, parentComposite.getShell(), sessionManager, new ImageDescriptorLoader(), iconProvider, pasteAction); configureToolBar(); initPageSwitcher(); addDropAdapter(); originalViewImage = getTitleImage(); progressImages = new ProgressImages(iconProvider); PlatformUI.getWorkbench().getHelpSystem().setHelp(parentComposite, SubstepsHelpContextIds.RESULTS_VIEW); getViewSite().getPage().addPartListener(partMonitor); setFilterAndLayout(false, ViewLayout.HIERARCHICAL.value()); setShowExecutionTime(true); if (memento != null) { restoreLayoutState(memento); } memento = null; testRunSessionListener = new DefaultSubstepsRunSessionListener(displaySupplier(), siteSupplier(), infoMessageUpdater, sessionManager); FeatureRunnerPlugin.instance().getModel().addTestRunSessionListener(testRunSessionListener); // always show youngest test run in view. simulate "sessionAdded" event // to do that final List<SubstepsRunSession> testRunSessions = FeatureRunnerPlugin.instance().getModel().getTestRunSessions(); if (!testRunSessions.isEmpty()) { testRunSessionListener.sessionAdded(testRunSessions.get(0)); } this.testCounterUpdater = new TestCounterUiUpdater(testRunStatsSupplier, counterPanel, progressBar); this.viewTitleUiUpdater = new ViewTitleUiUpdater(partMonitor, sessionManager, testRunStatsSupplier, iconProvider, originalViewImage, progressImages, this); this.uiUpdater = new SubstepsFeatureUiUpdater(disposedChecker, infoMessageUpdater, testCounterUpdater, viewTitleUiUpdater, testViewer, testRunStatsSupplier, actionManager); } private void decorateMemento(final IMemento m) { m.putString(TAG_SCROLL, scrollLockAction.isChecked() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$ final int weigths[] = sashForm.getWeights(); final int ratio = (weigths[0] * 1000) / (weigths[0] + weigths[1]); m.putInteger(TAG_RATIO, ratio); m.putInteger(TAG_ORIENTATION, orientation.value()); m.putString(TAG_FAILURES_ONLY, failuresOnlyFilterAction.isChecked() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$ m.putInteger(TAG_LAYOUT, layout.value()); m.putString(TAG_SHOW_TIME, showTimeAction.isChecked() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$ } @Override public void startUpdateJobs() { postSyncProcessChanges(); if (updateJob != null) { return; } substepsIsRunningJob = new ProcessRunningJob( SubstepsFeatureMessages.SubstepsFeatureTestRunnerViewPart_wrapperJobName, FAMILY_SUBSTEPS_FEATURE_RUN, Job.getJobManager().newLock()); // acquire lock while a test run is running // the lock is released when the test run terminates // the wrapper job will wait on this lock. getProgressService().schedule((Job) substepsIsRunningJob); updateJob = new UpdateSubstepsUIJob(SubstepsFeatureMessages.SubstepsFeatureTestRunnerViewPart_jobName, REFRESH_INTERVAL, uiUpdater); updateJob.schedule(REFRESH_INTERVAL); } @Override public void stopUpdateJobs() { if (updateJob != null) { ((StoppableJob) updateJob).stop(); updateJob = null; } if (substepsIsRunningJob != null && substepsIsRunningJob != null) { substepsIsRunningJob.stop(); substepsIsRunningJob = null; } postSyncProcessChanges(); } @Override public void notify(final Runnable r) { if (!isDisposed()) getDisplay().syncExec(r); } @Override public Runnable currentValue() { return null; } @Override public void callback() { warnOfContentChange(); } @Override public void propertyChanged(final Object source, final int propId) { firePropertyChange(propId); } private IWorkbenchSiteProgressService getProgressService() { final IWorkbenchSiteProgressService siteService = (IWorkbenchSiteProgressService) getSite().getAdapter( IWorkbenchSiteProgressService.class); if (siteService != null) { return siteService; } return null; } private void restoreLayoutState(final IMemento m) { final Integer ratio = m.getInteger(TAG_RATIO); if (ratio != null) sashForm.setWeights(new int[] { ratio.intValue(), 1000 - ratio.intValue() }); final Integer newOrientation = m.getInteger(TAG_ORIENTATION); if (newOrientation != null) orientation = ViewOrientation.forValue(newOrientation.intValue()); computeOrientation(); final String scrollLock = m.getString(TAG_SCROLL); if (scrollLock != null) { scrollLockAction.setChecked(scrollLock.equals("true")); //$NON-NLS-1$ setAutoScroll(!scrollLockAction.isChecked()); } final Integer l = m.getInteger(TAG_LAYOUT); ViewLayout layoutValue = ViewLayout.HIERARCHICAL; if (layout != null && l != null) layoutValue = ViewLayout.forValue(l.intValue()); final String failuresOnly = m.getString(TAG_FAILURES_ONLY); boolean showFailuresOnly = false; if (failuresOnly != null) showFailuresOnly = failuresOnly.equals("true"); //$NON-NLS-1$ final String time = m.getString(TAG_SHOW_TIME); boolean showTime = true; if (time != null) showTime = time.equals("true"); //$NON-NLS-1$ setFilterAndLayout(showFailuresOnly, layoutValue.value()); setShowExecutionTime(showTime); } private void disposeImages() { iconProvider.dispose(); } private SashForm createSashForm(final Composite parentComposite) { sashForm = new SashForm(parentComposite, SWT.VERTICAL); final ViewForm top = new ViewForm(sashForm, SWT.NONE); this.testViewer = new FeatureViewer(top, siteSupplier(), failedTestNotifier(), testRerunner(), autoScrollNotifier(), testKindDisplayNameSupplier(), iconProvider); final Composite empty = new Composite(top, SWT.NONE); empty.setLayout(new Layout() { @Override protected Point computeSize(final Composite composite, final int wHint, final int hHint, final boolean flushCache) { return new Point(1, 1); // (0, 0) does not work with // super-intelligent ViewForm } @Override protected void layout(final Composite composite, final boolean flushCache) { // No-op } }); top.setTopLeft(empty); // makes ViewForm draw the horizontal separator // line ... top.setContent(testViewer.getTestViewerControl()); final ViewForm bottom = new ViewForm(sashForm, SWT.NONE); final CLabel label = new CLabel(bottom, SWT.NONE); label.setText(SubstepsFeatureMessages.SubstepsFeatureTestRunnerViewPart_label_failure); label.setImage(iconProvider.imageFor(SubstepsFailureTraceIcon.StackView)); bottom.setTopLeft(label); final ToolBar failureToolBar = new ToolBar(bottom, SWT.FLAT | SWT.WRAP); bottom.setTopCenter(failureToolBar); failureTrace = new FailureTrace(bottom, clipboard, failureToolBar, iconProvider, infoMessageUpdater, runSessionSupplier()); bottom.setContent(failureTrace.getComposite()); sashForm.setWeights(new int[] { 50, 50 }); return sashForm; } private void addDropAdapter() { final DropTarget dropTarget = new DropTarget(parent, DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK | DND.DROP_DEFAULT); dropTarget.setTransfer(new Transfer[] { TextTransfer.getInstance() }); class DropAdapter extends DropTargetAdapter { @Override public void dragEnter(final DropTargetEvent event) { event.detail = DND.DROP_COPY; event.feedback = DND.FEEDBACK_NONE; } @Override public void dragOver(final DropTargetEvent event) { event.detail = DND.DROP_COPY; event.feedback = DND.FEEDBACK_NONE; } @Override public void dragOperationChanged(final DropTargetEvent event) { event.detail = DND.DROP_COPY; event.feedback = DND.FEEDBACK_NONE; } @Override public void drop(final DropTargetEvent event) { if (TextTransfer.getInstance().isSupportedType(event.currentDataType)) { // final String url = (String) event.data; importTestRunSession(); } } } dropTarget.addDropListener(new DropAdapter()); } private void initPageSwitcher() { @SuppressWarnings("unused") final PageSwitcher pageSwitcher = new PageSwitcher(this) { @Override public Object[] getPages() { return viewHistory.getHistoryEntries().toArray(); } @Override public String getName(final Object page) { return viewHistory.getText((SubstepsRunSession) page); } @Override public ImageDescriptor getImageDescriptor(final Object page) { return viewHistory.getImageDescriptor(page); } @Override public void activatePage(final Object page) { viewHistory.setActiveEntry((SubstepsRunSession) page); } @Override public int getCurrentPageIndex() { return viewHistory.getHistoryEntries().indexOf(viewHistory.getCurrentEntry()); } }; } private void addResizeListener(final Composite parentComposite) { parentComposite.addControlListener(new ControlListener() { @Override public void controlMoved(final ControlEvent e) { // No-op } @Override public void controlResized(final ControlEvent e) { computeOrientation(); } }); } void computeOrientation() { if (orientation != ViewOrientation.VIEW_ORIENTATION_AUTOMATIC) { currentOrientation = orientation; setOrientation(currentOrientation.value()); } else { final Point size = parent.getSize(); if (size.x != 0 && size.y != 0) { if (size.x > size.y) setOrientation(ViewOrientation.VIEW_ORIENTATION_HORIZONTAL.value()); else setOrientation(ViewOrientation.VIEW_ORIENTATION_VERTICAL.value()); } } } private void configureToolBar() { final IActionBars actionBars = getViewSite().getActionBars(); final IToolBarManager toolBar = actionBars.getToolBarManager(); final IMenuManager viewMenu = actionBars.getMenuManager(); actionManager.setNextActionEnabled(false); actionBars.setGlobalActionHandler(ActionFactory.NEXT.getId(), actionManager.nextAction()); actionManager.setPrevActionEnabled(false); actionBars.setGlobalActionHandler(ActionFactory.PREVIOUS.getId(), actionManager.prevAction()); actionManager.setStopActionEnabled(false); handlerServiceManager.activateHandlers(RERUN_LAST_COMMAND, new AbstractHandler() { @Override public Object execute(final ExecutionEvent event) throws ExecutionException { actionManager.rerunLastTestAction().run(); return null; } @Override public boolean isEnabled() { return actionManager.rerunLastTestAction().isEnabled(); } }); handlerServiceManager.activateHandlers(RERUN_FAILED_FIRST_COMMAND, new AbstractHandler() { @Override public Object execute(final ExecutionEvent event) throws ExecutionException { actionManager.rerunFailedFirstAction().run(); return null; } @Override public boolean isEnabled() { return actionManager.rerunFailedFirstAction().isEnabled(); } }); failuresOnlyFilterAction = new FailuresOnlyFilterAction(showFailuresOnlyNotifier(), iconProvider); scrollLockAction = new ScrollLockAction(autoScrollNotifier(), iconProvider); scrollLockAction.setChecked(!autoScroll); final Notifier<ViewOrientation> viewOrientationNotifier = viewOrientationNotifier(); toggleOrientationActions = new ToggleOrientationAction[] { new ToggleOrientationAction(ViewOrientation.VIEW_ORIENTATION_VERTICAL, viewOrientationNotifier, iconProvider), new ToggleOrientationAction(ViewOrientation.VIEW_ORIENTATION_HORIZONTAL, viewOrientationNotifier, iconProvider), new ToggleOrientationAction(ViewOrientation.VIEW_ORIENTATION_AUTOMATIC, viewOrientationNotifier, iconProvider) }; showTestHierarchyAction = new ShowTestHierarchyAction(layoutModeNotifier(), iconProvider); showTimeAction = new ShowTimeAction(testViewer); toolBar.add(actionManager.nextAction()); toolBar.add(actionManager.prevAction()); toolBar.add(failuresOnlyFilterAction); toolBar.add(scrollLockAction); toolBar.add(new Separator()); toolBar.add(actionManager.rerunLastTestAction()); toolBar.add(actionManager.rerunFailedFirstAction()); toolBar.add(actionManager.stopAction()); toolBar.add(viewHistory.createHistoryDropDownAction()); viewMenu.add(showTestHierarchyAction); viewMenu.add(showTimeAction); viewMenu.add(new Separator()); final MenuManager layoutSubMenu = new MenuManager( SubstepsFeatureMessages.SubstepsFeatureTestRunnerViewPart_layout_menu); for (int i = 0; i < toggleOrientationActions.length; ++i) { layoutSubMenu.add(toggleOrientationActions[i]); } viewMenu.add(layoutSubMenu); viewMenu.add(new Separator()); viewMenu.add(failuresOnlyFilterAction); final ActivateOnErrorAction activateOnErrorAction = new ActivateOnErrorAction(showOnErrorOnly, showOnErrorOnlyNotifier()); viewMenu.add(activateOnErrorAction); viewMenuListener = new IMenuListener() { @Override public void menuAboutToShow(final IMenuManager manager) { activateOnErrorAction.update(showOnErrorOnly); } }; viewMenu.addMenuListener(viewMenuListener); actionBars.updateActionBars(); } @SuppressWarnings("unused") private IStatusLineManager getStatusLine() { // we want to show messages globally hence we // have to go through the active part final IViewSite site = getViewSite(); final IWorkbenchPage page = site.getPage(); final IWorkbenchPart activePart = page.getActivePart(); if (activePart instanceof IViewPart) { final IViewPart activeViewPart = (IViewPart) activePart; final IViewSite activeViewSite = activeViewPart.getViewSite(); return activeViewSite.getActionBars().getStatusLineManager(); } if (activePart instanceof IEditorPart) { final IEditorPart activeEditorPart = (IEditorPart) activePart; final IEditorActionBarContributor contributor = activeEditorPart.getEditorSite().getActionBarContributor(); if (contributor instanceof EditorActionBarContributor) return ((EditorActionBarContributor) contributor).getActionBars().getStatusLineManager(); } // no active part return getViewSite().getActionBars().getStatusLineManager(); } protected Composite createProgressCountPanel(final Composite parentComposite) { final Composite composite = new Composite(parentComposite, SWT.NONE); final GridLayout l = new GridLayout(); composite.setLayout(l); setCounterColumns(l); counterPanel = new SubstepsCounterPanel(composite, iconProvider); counterPanel.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL)); progressBar = new SubstepsProgressBar(composite); ((Composite) progressBar) .setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL)); return composite; } private void showFailure(final SubstepsTestElement test) { notify(new Runnable() { @Override public void run() { if (!isDisposed()) failureTrace.showFailure(test); } }); } private boolean isDisposed() { return disposed || counterPanel.isDisposed(); } private Display getDisplay() { return getViewSite().getShell().getDisplay(); } /* * @see IWorkbenchPart#getTitleImage() */ @Override public Image getTitleImage() { if (originalViewImage == null) originalViewImage = super.getTitleImage(); if (viewImage == null) return super.getTitleImage(); return viewImage; } void codeHasChanged() { dirtyListener.removeDirtyListener(); if (viewImage == iconProvider.imageFor(SubstepsTestIcon.TestRunOK)) viewImage = iconProvider.imageFor(SubstepsTestIcon.TestRunOKDirty); else if (viewImage == iconProvider.imageFor(SubstepsTestIcon.TestRunFail)) viewImage = iconProvider.imageFor(SubstepsTestIcon.TestRunFailDirty); final Runnable r = new Runnable() { @Override public void run() { if (isDisposed()) return; firePropertyChange(IWorkbenchPart.PROP_TITLE); } }; if (!isDisposed()) getDisplay().asyncExec(r); } public void rerunTest(final String testId, final String className, final String testName, final String launchMode) { final boolean buildBeforeLaunch = Platform.getPreferencesService().getBoolean(IDebugUIConstants.PLUGIN_ID, IDebugUIConstants.PREF_BUILD_BEFORE_LAUNCH, false, null); try { final boolean couldLaunch = sessionManager.get().rerunTest(testId, className, testName, launchMode, buildBeforeLaunch); if (!couldLaunch) { MessageDialog.openInformation(getSite().getShell(), SubstepsFeatureMessages.SubstepsFeatureTestRunnerViewPart_cannotrerun_title, SubstepsFeatureMessages.SubstepsFeatureTestRunnerViewPart_cannotrerurn_message); } else if (sessionManager.get().isKeptAlive()) { final SubstepsTestLeafElement testCaseElement = (SubstepsTestLeafElement) sessionManager.get() .getTestElement(testId); testCaseElement.setStatus(Status.RUNNING, null, null, null); testViewer.registerViewerUpdate(testCaseElement); postSyncProcessChanges(); } } catch (final CoreException e) { ErrorDialog.openError(getSite().getShell(), SubstepsFeatureMessages.SubstepsFeatureTestRunnerViewPart_error_cannotrerun, e.getMessage(), e.getStatus()); } } private void postSyncProcessChanges() { notify(new Runnable() { @Override public void run() { uiUpdater.doUpdate(); } }); } public void warnOfContentChange() { final IWorkbenchSiteProgressService service = getProgressService(); if (service != null) service.warnOfContentChange(); } public boolean lastLaunchIsKeptAlive() { final SubstepsRunSession subsepsRunSession = sessionManager.get(); return subsepsRunSession != null && subsepsRunSession.isKeptAlive(); } private void setOrientation(final int orientation) { if ((sashForm == null) || sashForm.isDisposed()) return; final boolean horizontal = orientation == ViewOrientation.VIEW_ORIENTATION_HORIZONTAL.value(); sashForm.setOrientation(horizontal ? SWT.HORIZONTAL : SWT.VERTICAL); for (int i = 0; i < toggleOrientationActions.length; ++i) toggleOrientationActions[i].setChecked(orientation == toggleOrientationActions[i].getOrientation()); currentOrientation = ViewOrientation.forValue(orientation); final GridLayout l = (GridLayout) counterComposite.getLayout(); setCounterColumns(l); parent.layout(); } private void setCounterColumns(final GridLayout layout) { if (currentOrientation.equals(ViewOrientation.VIEW_ORIENTATION_HORIZONTAL)) layout.numColumns = 2; else layout.numColumns = 1; } static boolean getShowOnErrorOnly() { return Platform.getPreferencesService().getBoolean(FeatureRunnerPlugin.PLUGIN_ID, PreferencesConstants.SHOW_ON_ERROR_ONLY, false, null); } static void importTestRunSession() { try { PlatformUI.getWorkbench().getProgressService().busyCursorWhile(new IRunnableWithProgress() { @Override public void run(final IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { throw new UnsupportedOperationException("Import is not currently available"); } }); } catch (final InterruptedException e) { // cancelled } catch (final InvocationTargetException e) { final CoreException ce = (CoreException) e.getCause(); StatusManager.getManager().handle(ce.getStatus(), StatusManager.SHOW | StatusManager.LOG); } } public FailureTrace getFailureTrace() { return failureTrace; } private void setLayoutMode(final int mode) { setFilterAndLayout(failuresOnlyFilterAction.isChecked(), mode); } private void setFilterAndLayout(final boolean failuresOnly, final int layoutMode) { showTestHierarchyAction.setChecked(layoutMode == ViewLayout.HIERARCHICAL.value()); layout = ViewLayout.forValue(layoutMode); failuresOnlyFilterAction.setChecked(failuresOnly); testViewer.setShowFailuresOnly(failuresOnly, layoutMode); } private void setShowExecutionTime(final boolean showTime) { testViewer.setShowTime(showTime); showTimeAction.setChecked(showTime); } SubstepsTestElement[] getAllFailures() { return sessionManager.get().getAllFailedTestElements(); } private Supplier<Display> displaySupplier() { return new Supplier<Display>() { @Override public Display get() { return getDisplay(); } }; } private Supplier<IWorkbenchPartSite> siteSupplier() { return new Supplier<IWorkbenchPartSite>() { @Override public IWorkbenchPartSite get() { return getSite(); } }; } private Notifier<Boolean> showFailuresOnlyNotifier() { return new Notifier<Boolean>() { @Override public void notify(final Boolean t) { setFilterAndLayout(t.booleanValue(), layout.value()); } @Override public Boolean currentValue() { return Boolean.valueOf(failuresOnlyFilterAction.isChecked()); } }; } private Notifier<Boolean> autoScrollNotifier() { return new Notifier<Boolean>() { @Override public void notify(final Boolean t) { setAutoScroll(t.booleanValue()); } @Override public Boolean currentValue() { return Boolean.valueOf(autoScroll); } }; } private Notifier<Boolean> showOnErrorOnlyNotifier() { return new Notifier<Boolean>() { @Override public void notify(final Boolean t) { showOnErrorOnly = t.booleanValue(); } @Override public Boolean currentValue() { return Boolean.valueOf(showOnErrorOnly); } }; } private Notifier<ViewOrientation> viewOrientationNotifier() { return new Notifier<ViewOrientation>() { @Override public void notify(final ViewOrientation t) { orientation = t; computeOrientation(); } @Override public ViewOrientation currentValue() { // TODO Auto-generated method stub return null; } }; } private Notifier<ViewLayout> layoutModeNotifier() { return new Notifier<ViewLayout>() { @Override public void notify(final ViewLayout t) { setLayoutMode(t.value()); } @Override public ViewLayout currentValue() { return null; } }; } private Notifier<SubstepsTestElement> failedTestNotifier() { return new Notifier<SubstepsTestElement>() { @Override public void notify(final SubstepsTestElement t) { showFailure(t); ((SubstepsCopyAction) actionManager.copyAction()).handleTestSelected(t); } @Override public SubstepsTestElement currentValue() { // TODO Auto-generated method stub return null; } }; } private Notifier<TestContext> testRerunner() { return new Notifier<TestContext>() { @Override public void notify(final TestContext t) { rerunTest(t.getTestId(), t.getClassName(), t.getTestName(), t.getLaunchMode()); } @Override public TestContext currentValue() { return null; } }; } private Supplier<String> testKindDisplayNameSupplier() { return new Supplier<String>() { @Override public String get() { final String testRunnerKind = sessionManager.get().getTestRunnerKind(); return testRunnerKind == null ? "" : testRunnerKind; } }; } private Notifier<String> tooltipNotifier() { return new Notifier<String>() { @Override public void notify(final String t) { setTitleToolTip(t); } @Override public String currentValue() { // TODO Auto-generated method stub return null; } }; } private Supplier<Boolean> disposedSashFormChecker() { return new Supplier<Boolean>() { @Override public Boolean get() { return Boolean.valueOf(sashForm.isDisposed()); } }; } private Supplier<SubstepsSessionListener> testSessionListenerSupplier() { return new Supplier<SubstepsSessionListener>() { @Override public SubstepsSessionListener get() { return new TestSessionListener(testViewer, SubstepsFeatureTestRunnerViewPart.this, infoMessageUpdater, uiUpdater, showOnErrorOnlyNotifier(), SubstepsFeatureTestRunnerViewPart.this, actionManager, sessionManager, testRunStatsSupplier, disposedChecker, autoScrollNotifier(), SubstepsFeatureTestRunnerViewPart.this, viewTitleUiUpdater, testResultsView, failureTrace, dirtyListener); } }; } private Supplier<TestRunStats> testRunStatsSupplier() { return new Supplier<TestRunStats>() { @Override public TestRunStats get() { return sessionManager.get(); } }; } private Supplier<SubstepsRunSession> runSessionSupplier() { return new Supplier<SubstepsRunSession>() { @Override public SubstepsRunSession get() { return sessionManager.get(); } }; } }