Java tutorial
/******************************************************************************* * Copyright (c) 2015 Christian Pontesegger 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: * Christian Pontesegger - initial API and implementation *******************************************************************************/ package org.eclipse.ease.modules.unittest.ui.views; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.ease.debugging.IScriptDebugFrame; import org.eclipse.ease.modules.platform.UIModule; import org.eclipse.ease.modules.unittest.ITestListener; import org.eclipse.ease.modules.unittest.components.Test; import org.eclipse.ease.modules.unittest.components.TestComposite; import org.eclipse.ease.modules.unittest.components.TestFile; import org.eclipse.ease.modules.unittest.components.TestResult; import org.eclipse.ease.modules.unittest.components.TestStatus; import org.eclipse.ease.modules.unittest.components.TestSuite; import org.eclipse.ease.modules.unittest.components.TestSuiteModel; import org.eclipse.ease.modules.unittest.ui.Activator; import org.eclipse.ease.modules.unittest.ui.sourceprovider.TestSuiteSource; import org.eclipse.ease.ui.console.ScriptConsole; import org.eclipse.ease.ui.tools.DecoratedLabelProvider; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.layout.TreeColumnLayout; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.resource.LocalResourceManager; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.viewers.ColumnLabelProvider; import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; import org.eclipse.jface.viewers.ColumnWeightData; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeSelection; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.TreeViewerColumn; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.jface.window.ToolTip; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.RGB; 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.Label; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.ProgressBar; import org.eclipse.ui.IMemento; import org.eclipse.ui.IViewSite; import org.eclipse.ui.PartInitException; import org.eclipse.ui.console.ConsolePlugin; import org.eclipse.ui.console.IConsole; import org.eclipse.ui.console.IConsoleListener; import org.eclipse.ui.handlers.CollapseAllHandler; import org.eclipse.ui.handlers.ExpandAllHandler; import org.eclipse.ui.handlers.IHandlerService; import org.eclipse.ui.part.ViewPart; import org.eclipse.ui.progress.UIJob; import org.eclipse.ui.texteditor.ITextEditor; public class UnitTestView extends ViewPart implements ITestListener, IConsoleListener { public static final String VIEW_ID = "org.eclipse.ease.views.unittest"; public static final String TEST_STATUS_PROPERTY = "test status"; private static class Statistics { private final Map<Object, Integer> mCounters = new HashMap<Object, Integer>(); public synchronized void updateCounter(final Object identifier, final int value) { if (mCounters.containsKey(identifier)) mCounters.put(identifier, mCounters.get(identifier) + value); else mCounters.put(identifier, value); } public void reset() { mCounters.clear(); } public synchronized int getCounter(final Object identifier) { if (mCounters.containsKey(identifier)) return mCounters.get(identifier); return 0; } } private static final String XML_CURRENT_SUITE = "currentSuite"; private static final Object STATISTICS_TESTFILES_FINISHED = "testFiles"; private static final Object STATISTICS_TEST_ERROR = "test errors"; private static final Object STATISTICS_TEST_FAILURE = "test failures"; private static final Object STATISTICS_TEST_VALID = "valid tests"; private static final Object STATISTICS_TESTFILE_COUNT = "testFile count"; private static final Object STATISTICS_TEST_FINISHED = "test count"; private ProgressBar fProgressBar; private TreeViewer fFileTreeViewer; private TreeViewer fTestTreeViewer; private SashForm sashForm; private int[] fSashWeights = new int[] { 70, 30 }; private IMemento mMemento; private CollapseAllHandler mCollapseAllHandler; private ExpandAllHandler mExpandAllHandler; private Label lblTimeLeftText; private LocalResourceManager fResourceManager; private final UpdateUI fUIUpdater = new UpdateUI(); private final Statistics fStatistics = new Statistics(); private Label lblErrorCount; private Label lblFailureCount; private SuiteRuntimeInformation fRuntimeInformation = null; private Label lblTimeLeft; private ScriptConsole fConsole = null; public UnitTestView() { } @Override public void init(final IViewSite site, final IMemento memento) throws PartInitException { mMemento = memento; super.init(site, memento); } @Override public void createPartControl(final Composite parent) { parent.setLayout(new GridLayout(1, false)); final Composite composite = new Composite(parent, SWT.NONE); composite.setLayout(new GridLayout(8, false)); composite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); fResourceManager = new LocalResourceManager(JFaceResources.getResources(), composite); final Label lblErrorIcon = new Label(composite, SWT.NONE); final GridData gdLblErrorIcon = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1); gdLblErrorIcon.horizontalIndent = 50; lblErrorIcon.setLayoutData(gdLblErrorIcon); lblErrorIcon.setImage(fResourceManager.createImage(Activator.getImageDescriptor(Activator.ICON_ERROR))); final Label lblErrors = new Label(composite, SWT.NONE); lblErrors.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); lblErrors.setAlignment(SWT.CENTER); lblErrors.setText("Errors:"); lblErrorCount = new Label(composite, SWT.NONE); final GridData gd_lblErrorCount = new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1); gd_lblErrorCount.horizontalIndent = 20; lblErrorCount.setLayoutData(gd_lblErrorCount); final Label lblFailureIcon = new Label(composite, SWT.NONE); final GridData gdLblFailureIcon = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1); gdLblFailureIcon.horizontalIndent = 50; lblFailureIcon.setLayoutData(gdLblFailureIcon); lblFailureIcon.setImage(fResourceManager.createImage(Activator.getImageDescriptor(Activator.ICON_FAILURE))); final Label lblFailures = new Label(composite, SWT.NONE); lblFailures.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); lblFailures.setAlignment(SWT.CENTER); lblFailures.setText("Failures:"); lblFailureCount = new Label(composite, SWT.NONE); final GridData gdLblFailureCount = new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1); gdLblFailureCount.horizontalIndent = 20; lblFailureCount.setLayoutData(gdLblFailureCount); lblTimeLeftText = new Label(composite, SWT.NONE); final GridData gdLabel = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1); gdLabel.horizontalIndent = 50; lblTimeLeftText.setLayoutData(gdLabel); lblTimeLeftText.setText("Time left: "); lblTimeLeftText.setVisible(false); lblTimeLeft = new Label(composite, SWT.NONE); final GridData gdLblTimeLeft = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1); gdLblTimeLeft.horizontalIndent = 20; lblTimeLeft.setLayoutData(gdLblTimeLeft); lblTimeLeft.setVisible(false); fProgressBar = new ProgressBar(parent, SWT.NONE); fProgressBar.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); sashForm = new SashForm(parent, SWT.NONE); sashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); sashForm.setOrientation(SWT.VERTICAL); fFileTreeViewer = new TreeViewer(sashForm, SWT.BORDER | SWT.MULTI); fFileTreeViewer.setContentProvider(new TestSuiteContentProvider()); fFileTreeViewer.setComparator(new ViewerComparator() { @Override public final int category(final Object element) { if (element instanceof TestFile) return 1; return 0; } }); // use a decorated label provider final LabelProvider provider = new TestSuiteLabelProvider(fResourceManager); fFileTreeViewer.setLabelProvider(new DecoratedLabelProvider(provider)); fFileTreeViewer.addDoubleClickListener(new IDoubleClickListener() { @Override public void doubleClick(final DoubleClickEvent event) { try { final Object element = ((IStructuredSelection) event.getSelection()).getFirstElement(); if (element instanceof TestFile) { final Object file = ((TestFile) element).getFile(); if (file instanceof IFile) UIModule.showEditor((IFile) ((TestFile) element).getFile()); } else if (element instanceof TestSuite) UIModule.showEditor(((TestSuite) element).getModel().getFile()); } catch (final Throwable e) { // TODO handle this exception (but for now, at least know it happened) throw new RuntimeException(e); } } }); fFileTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(final SelectionChangedEvent event) { final ITreeSelection selection = (ITreeSelection) event.getSelection(); final Object element = selection.getFirstElement(); if (element instanceof TestComposite) { // test set selected fTestTreeViewer.setInput(element); if (sashForm.getWeights()[1] == 0) sashForm.setWeights(fSashWeights); fTestTreeViewer.refresh(); } else { // test container selected, or no selection at all fTestTreeViewer.setInput(null); if (sashForm.getWeights()[1] != 0) fSashWeights = sashForm.getWeights(); sashForm.setWeights(new int[] { 100, 0 }); } } }); // create tree viewer for tests fTestTreeViewer = createTestArea(sashForm); sashForm.setWeights(new int[] { 1, 1 }); // add context menu support final MenuManager menuManager = new MenuManager(); final Menu menu = menuManager.createContextMenu(fFileTreeViewer.getTree()); fFileTreeViewer.getTree().setMenu(menu); getSite().registerContextMenu(menuManager, fFileTreeViewer); final MenuManager menuManager2 = new MenuManager(); final Menu menu2 = menuManager2.createContextMenu(fFileTreeViewer.getTree()); fTestTreeViewer.getTree().setMenu(menu2); getSite().registerContextMenu(menuManager2, fTestTreeViewer); // add collapseAll/expandAll handlers final IHandlerService handlerService = getSite().getService(IHandlerService.class); mCollapseAllHandler = new CollapseAllHandler(fFileTreeViewer); handlerService.activateHandler(CollapseAllHandler.COMMAND_ID, mCollapseAllHandler); mExpandAllHandler = new ExpandAllHandler(fFileTreeViewer); handlerService.activateHandler(ExpandAllHandler.COMMAND_ID, mExpandAllHandler); // menuManager.setRemoveAllWhenShown(true); // load last suite if (mMemento != null) { final IMemento currentSuiteNode = mMemento.getChild(XML_CURRENT_SUITE); if (currentSuiteNode != null) { final Path path = new Path(currentSuiteNode.getTextData()); final IFile suiteFile = ResourcesPlugin.getWorkspace().getRoot().getFile(path); try { loadSuite(new TestSuite(new TestSuiteModel(suiteFile))); } catch (final Exception e) { // loading failed, ignore } } } // register for console events ConsolePlugin.getDefault().getConsoleManager().addConsoleListener(UnitTestView.this); final MultiSelectionProvider selectionProvider = new MultiSelectionProvider(); selectionProvider.addSelectionProvider(fFileTreeViewer); selectionProvider.addSelectionProvider(fTestTreeViewer); getSite().setSelectionProvider(selectionProvider); } private TreeViewer createTestArea(final Composite parent) { final Composite composite = new Composite(parent, SWT.NONE); final TreeColumnLayout layout = new TreeColumnLayout(); composite.setLayout(layout); final TreeViewer viewer = new TreeViewer(composite, SWT.BORDER | SWT.FULL_SELECTION); viewer.addDoubleClickListener(new IDoubleClickListener() { @Override public void doubleClick(final DoubleClickEvent event) { final Object element = ((IStructuredSelection) event.getSelection()).getFirstElement(); if (element instanceof Test) { List<IScriptDebugFrame> trace; final List<TestResult> messages = ((Test) element).getMessages(); if ((messages != null) && (!messages.isEmpty())) trace = messages.get(0).getStackTrace(); else trace = ((Test) element).getTestLocation(); if (trace != null) { // open trace location for (final IScriptDebugFrame traceElement : trace) { final Object file = traceElement.getScript().getFile(); if (file instanceof IFile) { if (((IFile) file).exists()) { try { final int line = Math.max(traceElement.getLineNumber(), 1); final ITextEditor textEditor = (ITextEditor) UIModule .showEditor((IFile) file); final IDocument document = textEditor.getDocumentProvider() .getDocument(textEditor.getEditorInput()); try { textEditor.selectAndReveal(document.getLineOffset(line - 1), document.getLineLength(line - 1)); } catch (final BadLocationException e) { // TODO implement throw new RuntimeException(e); } } catch (final Throwable e) { // TODO handle this exception (but for now, at least know it happened) throw new RuntimeException(e); } break; } } } } else { // we do not have a trace, open test set final Object input = viewer.getInput(); if (input instanceof TestFile) { try { final Object file = ((TestFile) input).getFile(); if (file instanceof IFile) UIModule.showEditor((IFile) ((TestFile) input).getFile()); } catch (final Throwable e) { // TODO handle this exception (but for now, at least know it happened) throw new RuntimeException(e); } } } } } }); viewer.getTree().setHeaderVisible(true); viewer.getTree().setLinesVisible(true); viewer.setContentProvider(new TestFileContentProvider()); final TreeViewerColumn testColumn = new TreeViewerColumn(viewer, SWT.NONE); testColumn.getColumn().setWidth(100); testColumn.getColumn().setText("Test"); layout.setColumnData(testColumn.getColumn(), new ColumnWeightData(30, 50, true)); testColumn.setLabelProvider(new ColumnLabelProvider() { @Override public String getText(final Object element) { if (element instanceof Test) return ((Test) element).getTitle(); if (element instanceof Entry<?, ?>) // metadata return ((Entry<?, ?>) element).getKey().toString(); return ""; } @Override public Image getImage(final Object element) { if (element instanceof Entry<?, ?>) // metadata return fResourceManager.createImage(Activator.getImageDescriptor(Activator.ICON_METADATA)); TestStatus status = null; if (element instanceof Test) status = ((Test) element).getStatus(); else if (element instanceof TestResult) status = ((TestResult) element).getStatus(); if (status != null) { switch (status) { case PASS: return fResourceManager.createImage(Activator.getImageDescriptor(Activator.ICON_PASS)); case ERROR: return fResourceManager.createImage(Activator.getImageDescriptor(Activator.ICON_ERROR)); case FAILURE: return fResourceManager.createImage(Activator.getImageDescriptor(Activator.ICON_FAILURE)); case RUNNING: return fResourceManager.createImage(Activator.getImageDescriptor(Activator.ICON_RUNNING)); default: return super.getImage(element); } } return super.getImage(element); } @Override public String getToolTipText(final Object element) { if (element instanceof Test) { if ((((Test) element).getDescription() != null) && (!((Test) element).getDescription().isEmpty())) return ((Test) element).getDescription(); } return super.getToolTipText(element); } }); final TreeViewerColumn messageColumn = new TreeViewerColumn(viewer, SWT.NONE); messageColumn.getColumn().setWidth(100); messageColumn.getColumn().setText("Message"); layout.setColumnData(messageColumn.getColumn(), new ColumnWeightData(70, 50, true)); messageColumn.setLabelProvider(new ColumnLabelProvider() { @Override public String getText(final Object element) { if (element instanceof Test) { final TestResult message = ((Test) element).getSeverestMessage(); if (message != null) return message.getDescription(); return ((Test) element).getDescription(); } if (element instanceof Entry<?, ?>) // metadata return ((Entry<?, ?>) element).getValue().toString(); return super.getText(element); } }); ColumnViewerToolTipSupport.enableFor(viewer, ToolTip.NO_RECREATE); return viewer; } @Override public void setFocus() { // nothing to do } public TreeViewer getFileTreeViewer() { return fFileTreeViewer; } private class UpdateUI extends UIJob { private final List<Object> fElements = new ArrayList<Object>(); public UpdateUI() { super("Update Script Unit View"); } public void addElement(final Object element) { synchronized (fElements) { if (fElements.isEmpty()) { // we might have added the same element again, so we cannot check for size() == 1 afterwards fElements.add(element); schedule(300); } else fElements.add(element); } } @Override public IStatus runInUIThread(final IProgressMonitor monitor) { // create a local copy of elements so we can continue tests without waiting for the UI updater ArrayList<Object> localElements; synchronized (fElements) { localElements = new ArrayList<Object>(fElements); fElements.clear(); } // update tree elements for (final Object element : localElements) { TestComposite affectedComposite = null; if (element instanceof Test) affectedComposite = ((Test) element).getParent(); else if (element instanceof TestComposite) affectedComposite = (TestComposite) element; while (affectedComposite != null) { // update tree element and all its parents (no folders) fFileTreeViewer.update(affectedComposite, new String[] { TEST_STATUS_PROPERTY }); affectedComposite = affectedComposite.getParent(); } } // update table if (fTestTreeViewer.getInput() != null) { // update only if tableviewer is visible at all for (final Object element : localElements) { if (element instanceof Test) { final TestComposite testComposite = ((Test) element).getParent(); if (fTestTreeViewer.getInput().equals(testComposite)) { fTestTreeViewer.refresh(); // TODO scroll to last element // one refresh is enough for the whole table, so bail out break; } } } } // update statistics if (!fProgressBar.isDisposed()) { lblErrorCount.setText(Integer.toString(fStatistics.getCounter(STATISTICS_TEST_ERROR))); lblFailureCount.setText(Integer.toString(fStatistics.getCounter(STATISTICS_TEST_FAILURE))); lblFailureCount.getParent().layout(); fProgressBar.setSelection(fStatistics.getCounter(STATISTICS_TESTFILES_FINISHED)); lblTimeLeft.setText(getEstimatedTime()); if ((fStatistics.getCounter(STATISTICS_TEST_ERROR) > 0) || (fStatistics.getCounter(STATISTICS_TEST_FAILURE) > 0)) fProgressBar.setForeground(fResourceManager.createColor(new RGB(0xaa, 0, 0))); else if (fStatistics.getCounter(STATISTICS_TESTFILES_FINISHED) == fStatistics .getCounter(STATISTICS_TESTFILE_COUNT)) { if ((fStatistics.getCounter(STATISTICS_TEST_ERROR) == 0) && (fStatistics.getCounter(STATISTICS_TEST_FAILURE) == 0)) fProgressBar.setForeground(fResourceManager.createColor(new RGB(0, 0xaa, 0))); } } synchronized (fElements) { if (!fElements.isEmpty()) schedule(1000); } return Status.OK_STATUS; } private String getEstimatedTime() { if (getCurrentSuite().getStatus() != TestStatus.RUNNING) { lblTimeLeft.setVisible(false); lblTimeLeftText.setVisible(false); return ""; } if (fRuntimeInformation != null) { final long time = fRuntimeInformation.getEstimatedTestTime(); if (time < 0) return "calculating..."; if (time < 60000) return new SimpleDateFormat("ss 'seconds'").format(time); if (time < 3600000) return new SimpleDateFormat("mm:ss").format(time); return new SimpleDateFormat("hh:mm:ss").format(time); } return "unknown"; } } @Override public void notify(final Object testObject, final TestStatus status) { if ((testObject instanceof TestSuite) && (status == TestStatus.RUNNING)) { Display.getDefault().syncExec(new Runnable() { @Override public void run() { try { loadSuite((TestSuite) testObject); } catch (final Exception e) { // TODO handle this exception (but for now, at least know it happened) throw new RuntimeException(e); } fStatistics.reset(); fStatistics.updateCounter(STATISTICS_TESTFILE_COUNT, ((TestSuite) testObject).getActiveTestCount()); // initialize progress bar fProgressBar.setMaximum(fStatistics.getCounter(STATISTICS_TESTFILE_COUNT)); fProgressBar.setSelection(0); fProgressBar.setForeground(null); // create console if (fConsole == null) fConsole = ScriptConsole.create(testObject.toString(), null); // clear & attach to suite fConsole.clearConsole(); fConsole.activate(); ((TestSuite) testObject).setOutputStream(fConsole.getOutputStream()); ((TestSuite) testObject).setErrorStream(fConsole.getErrorStream()); ((TestSuite) testObject).setErrorStream(fConsole.getInputStream()); // update estimated runtime lblTimeLeft.setVisible(true); lblTimeLeftText.setVisible(true); } }); } else { // update statistics if ((status != TestStatus.RUNNING) && (status != TestStatus.NOT_RUN)) { // test finished if (testObject instanceof Test) { // do not track when this is a temporary tests in test count if (!((Test) testObject).isTransient()) fStatistics.updateCounter(STATISTICS_TEST_FINISHED, 1); // do not track when this is a temporary test that passed if ((!((Test) testObject).isTransient()) || (!((Test) testObject).getMessages().isEmpty())) { switch (status) { case FAILURE: fStatistics.updateCounter(STATISTICS_TEST_FAILURE, 1); break; case ERROR: fStatistics.updateCounter(STATISTICS_TEST_ERROR, 1); break; case PASS: // do not track when global test file scope is valid fStatistics.updateCounter(STATISTICS_TEST_VALID, 1); break; default: // nothing to do break; } } } else if (testObject instanceof TestFile) { fStatistics.updateCounter(STATISTICS_TESTFILES_FINISHED, 1); } } } fUIUpdater.addElement(testObject); } public StructuredViewer getTableViewer() { return fTestTreeViewer; } @Override public void saveState(final IMemento memento) { final TestSuite suite = getCurrentSuite(); if (suite != null) { final IFile file = suite.getModel().getFile(); if ((file != null) && (file.exists())) { // we finally detected the current test suite memento.createChild(XML_CURRENT_SUITE).putTextData(file.getFullPath().toString()); } } super.saveState(memento); } public TestSuite getCurrentSuite() { final Object input = fFileTreeViewer.getInput(); if (input instanceof Object[]) { if (((Object[]) input).length > 0) { final Object suite = ((Object[]) input)[0]; if (suite instanceof TestSuite) return (TestSuite) suite; } } return null; } /** * Loads a suite file and populates the treeview. Needs to be called from UIThread. * * @param suite * testSuite * @return * @throws IOException * cannot read from suite file * @throws CoreException * invalid data within suite file */ public void loadSuite(final TestSuite suite) throws IOException, CoreException { // save current suite final TestSuite currentSuite = getCurrentSuite(); if (!suite.equals(currentSuite)) { if (currentSuite != null) { Activator.getDefault().addRecentFile(currentSuite.getModel().getFile()); // save timing information if (fRuntimeInformation != null) fRuntimeInformation.save(); } fFileTreeViewer.setInput(new Object[] { suite }); fRuntimeInformation = new SuiteRuntimeInformation(suite); // update source provider final TestSuiteSource instance = TestSuiteSource.getActiveInstance(); if (instance != null) instance.setActiveSuite(suite); suite.addTestListener(this); // refresh console if (fConsole != null) { fConsole.terminate(); fConsole = null; } } fFileTreeViewer.refresh(); fFileTreeViewer.expandAll(); } @Override public void dispose() { // unregister from console events ConsolePlugin.getDefault().getConsoleManager().removeConsoleListener(this); // dispose handlers mCollapseAllHandler.dispose(); mExpandAllHandler.dispose(); // save current suite final TestSuite currentSuite = getCurrentSuite(); if (currentSuite != null) Activator.getDefault().addRecentFile(currentSuite.getModel().getFile()); // save timing information if (fRuntimeInformation != null) fRuntimeInformation.save(); super.dispose(); } @Override public void consolesAdded(final IConsole[] consoles) { // nothing to do } @Override public void consolesRemoved(final IConsole[] consoles) { for (final IConsole console : consoles) { if (console.equals(fConsole)) fConsole = null; } } }