Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/UIUpdater.java')
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/UIUpdater.java517
1 files changed, 517 insertions, 0 deletions
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/UIUpdater.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/UIUpdater.java
new file mode 100644
index 00000000000..a6738e0d7e8
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/UIUpdater.java
@@ -0,0 +1,517 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Anton Gorenkov
+ * 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:
+ * Anton Gorenkov - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.testsrunner.internal.ui.view;
+
+import java.text.MessageFormat;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.cdt.testsrunner.internal.model.ITestingSessionsManagerListener;
+import org.eclipse.cdt.testsrunner.internal.model.TestingSessionsManager;
+import org.eclipse.cdt.testsrunner.model.ITestItem;
+import org.eclipse.cdt.testsrunner.model.ITestingSession;
+import org.eclipse.cdt.testsrunner.model.ITestingSessionListener;
+import org.eclipse.cdt.testsrunner.model.ITestCase;
+import org.eclipse.cdt.testsrunner.model.ITestSuite;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.progress.UIJob;
+
+/**
+ * Tracks and collects the changes in active testing session and updates the UI
+ * periodically. It allows to significantly improve the UI performance.
+ */
+public class UIUpdater {
+
+ /** Access to the results showing view. */
+ private ResultsView resultsView;
+
+ /** Access to the tests hierarchy showing widget. */
+ private TestsHierarchyViewer testsHierarchyViewer;
+
+ /** Access to the statistics showing widget. */
+ private ProgressCountPanel progressCountPanel;
+
+ /** Listener for the changes in active testing session. */
+ private ITestingSessionListener sessionListener;
+
+ /**
+ * Specifies whether tests hierarchy scrolling should be done during the
+ * testing process.
+ */
+ private boolean autoScroll = true;
+
+ /** Access to the testing sessions manager. */
+ private TestingSessionsManager sessionsManager;
+
+ /** Listener to handle active testing session change. */
+ private TestingSessionsManagerListener sessionsManagerListener;
+
+ /** Reference to the active testing session. */
+ ITestingSession testingSession;
+
+ /** Storage for the UI changes that should be done on update. */
+ UIChangesCache uiChangesCache = new UIChangesCache();
+
+ /** A job that makes an UI update periodically. */
+ UpdateUIJob updateUIJob = null;
+
+ /** Time interval over which the UI should be updated. */
+ private static final int REFRESH_INTERVAL = 200;
+
+
+ /**
+ * Storage for the UI changes that should be done on update.
+ */
+ private class UIChangesCache {
+
+ /**
+ * Specifies whether Progress Counter Panel should be updated during the
+ * next UI update.
+ */
+ private boolean needProgressCountPanelUpdate;
+
+ /**
+ * Specifies whether view actions should be updated during the next UI
+ * update.
+ */
+ private boolean needActionsUpdate;
+
+ /**
+ * A test item which path should be shown as a view caption during the
+ * next UI update.
+ */
+ private ITestItem testItemForNewViewCaption;
+
+ /**
+ * Set of tree objects on which <code>refresh()</code> should be called
+ * during the next UI update.
+ */
+ private Set<Object> treeItemsToRefresh = new HashSet<Object>();
+
+ /**
+ * Set of tree objects on which <code>update()</code> should be called
+ * during the next UI update.
+ */
+ private Set<Object> treeItemsToUpdate = new HashSet<Object>();
+
+ /** Tree object that should be revealed during the next UI update. */
+ private Object treeItemToReveal;
+
+ /** Map of tree objects that should be expanded or collapsed to their new states. */
+ private Map<Object, Boolean> treeItemsToExpand = new LinkedHashMap<Object, Boolean>();
+
+
+ UIChangesCache() {
+ resetChanges();
+ }
+
+ /**
+ * Schedules the Progress Counter Panel update during the next UI update.
+ */
+ public void scheduleProgressCountPanelUpdate() {
+ synchronized (this) {
+ needProgressCountPanelUpdate = true;
+ }
+ }
+
+ /**
+ * Schedules the view actions update during the next UI update.
+ */
+ public void scheduleActionsUpdate() {
+ synchronized (this) {
+ needActionsUpdate = true;
+ }
+ }
+
+ /**
+ * Schedules the view caption update to the path to specified test item
+ * during the next UI update.
+ *
+ * @param testItem specified test item
+ */
+ public void scheduleViewCaptionChange(ITestItem testItem) {
+ synchronized (this) {
+ testItemForNewViewCaption = testItem;
+ }
+ }
+
+ /**
+ * Schedules the <code>update()</code> call for the specified tree
+ * object during the next UI update.
+ *
+ * @param item tree object to update
+ */
+ public void scheduleTreeItemUpdate(Object item) {
+ synchronized (this) {
+ treeItemsToUpdate.add(item);
+ }
+ }
+
+ /**
+ * Schedules the revealing of the specified tree object. Overrides
+ * the previously specified tree object to reveal (if any).
+ *
+ * @param item tree object to reveal
+ */
+ public void scheduleTreeItemReveal(Object item) {
+ synchronized (this) {
+ treeItemToReveal = item;
+ }
+ }
+
+ /**
+ * Schedules the expanding or collapsing of the specified tree object.
+ * Overrides the previous state for the same tree object (if any).
+ *
+ * @param item tree object to expand or collapse
+ * @param expandedState true if the node is expanded, and false if
+ * collapsed
+ */
+ public void scheduleTreeItemExpand(Object item, boolean expandedState) {
+ synchronized (this) {
+ treeItemsToExpand.put(item, expandedState);
+ }
+ }
+
+ /**
+ * Schedules the <code>refresh()</code> call for the specified tree
+ * object during the next UI update.
+ *
+ * @param item tree object to refresh
+ */
+ public void scheduleTreeItemRefresh(Object item) {
+ synchronized (this) {
+ treeItemsToRefresh.add(item);
+ }
+ }
+
+
+ /**
+ * Apply any scheduled changes to UI.
+ */
+ public void applyChanges() {
+ synchronized (this) {
+ TreeViewer treeViewer = testsHierarchyViewer.getTreeViewer();
+ // View statistics widgets update
+ if (needProgressCountPanelUpdate) {
+ progressCountPanel.updateInfoFromSession();
+ }
+ // View actions update
+ if (needActionsUpdate) {
+ resultsView.updateActionsFromSession();
+ }
+ // View caption update
+ if (testItemForNewViewCaption != null) {
+ resultsView.setCaption(
+ MessageFormat.format(
+ UIViewMessages.UIUpdater_view_caption_format,
+ testItemForNewViewCaption.getName(),
+ TestPathUtils.getTestItemPath(testItemForNewViewCaption.getParent())
+ )
+ );
+ }
+ // Tree view update
+ if (!treeItemsToRefresh.isEmpty()) {
+ for (Object item : treeItemsToRefresh) {
+ treeViewer.refresh(item, false);
+ }
+ }
+ if (!treeItemsToUpdate.isEmpty()) {
+ treeViewer.update(treeItemsToUpdate.toArray(), null);
+ }
+ if (treeItemToReveal != null) {
+ treeViewer.reveal(treeItemToReveal);
+ }
+ if (!treeItemsToExpand.isEmpty()) {
+ for (Map.Entry<Object, Boolean> entry : treeItemsToExpand.entrySet()) {
+ treeViewer.setExpandedState(entry.getKey(), entry.getValue());
+ }
+ }
+ // All changes are applied, remove them
+ resetChangesImpl();
+ }
+ }
+
+ /**
+ * Reset all the scheduled changes to UI.
+ */
+ public void resetChanges() {
+ synchronized (this) {
+ resetChangesImpl();
+ }
+ }
+
+ /**
+ * Reset all the scheduled changes to UI. Note, this method is not
+ * synchronized so it should be used carefully
+ */
+ private void resetChangesImpl() {
+ needProgressCountPanelUpdate = false;
+ needActionsUpdate = false;
+ testItemForNewViewCaption = null;
+ treeItemsToUpdate.clear();
+ treeItemToReveal = null;
+ treeItemsToExpand.clear();
+ }
+ }
+
+
+ /**
+ * A job that makes an UI update periodically.
+ */
+ private class UpdateUIJob extends UIJob {
+
+ /** Controls whether the job should be scheduled again. */
+ private boolean isRunning = true;
+
+ public UpdateUIJob() {
+ super(UIViewMessages.UIUpdater_update_ui_job);
+ setSystem(true);
+ }
+
+ @Override
+ public IStatus runInUIThread(IProgressMonitor monitor) {
+ if (!resultsView.isDisposed()) {
+ uiChangesCache.applyChanges();
+ scheduleSelf();
+ }
+ return Status.OK_STATUS;
+ }
+
+ /**
+ * Schedule self for running after time interval.
+ */
+ public void scheduleSelf() {
+ schedule(REFRESH_INTERVAL);
+ }
+
+ /**
+ * Sets the flag that prevents planning this job again.
+ */
+ public void stop() {
+ isRunning = false;
+ }
+
+ @Override
+ public boolean shouldSchedule() {
+ return isRunning;
+ }
+
+ }
+
+
+ /**
+ * Listener for the changes in active testing session.
+ */
+ private class SessionListener implements ITestingSessionListener {
+
+ /**
+ * Common implementation for test case and test suite entering.
+ *
+ * @param testItem test case or test suite
+ */
+ private void enterTestItem(ITestItem testItem) {
+ uiChangesCache.scheduleViewCaptionChange(testItem);
+ uiChangesCache.scheduleTreeItemUpdate(testItem);
+ if (autoScroll) {
+ uiChangesCache.scheduleTreeItemReveal(testItem);
+ }
+ }
+
+ @Override
+ public void enterTestSuite(ITestSuite testSuite) {
+ enterTestItem(testSuite);
+ }
+
+ @Override
+ public void exitTestSuite(ITestSuite testSuite) {
+ uiChangesCache.scheduleTreeItemUpdate(testSuite);
+ if (autoScroll) {
+ uiChangesCache.scheduleTreeItemExpand(testSuite, false);
+ }
+ }
+
+ @Override
+ public void enterTestCase(ITestCase testCase) {
+ enterTestItem(testCase);
+ }
+
+ @Override
+ public void exitTestCase(ITestCase testCase) {
+ uiChangesCache.scheduleActionsUpdate();
+ uiChangesCache.scheduleProgressCountPanelUpdate();
+ uiChangesCache.scheduleTreeItemUpdate(testCase);
+ }
+
+ @Override
+ public void childrenUpdate(ITestSuite parent) {
+ uiChangesCache.scheduleTreeItemRefresh(parent);
+ }
+
+ @Override
+ public void testingStarted() {
+ resultsView.updateActionsFromSession();
+ Display.getDefault().syncExec(new Runnable() {
+
+ @Override
+ public void run() {
+ resultsView.setCaption(testingSession.getStatusMessage());
+ progressCountPanel.updateInfoFromSession();
+ testsHierarchyViewer.getTreeViewer().refresh();
+ }
+ });
+ startUpdateUIJob();
+ }
+
+ @Override
+ public void testingFinished() {
+ stopUpdateUIJob();
+ resultsView.updateActionsFromSession();
+ Display.getDefault().syncExec(new Runnable() {
+
+ @Override
+ public void run() {
+ uiChangesCache.applyChanges();
+ resultsView.setCaption(testingSession.getStatusMessage());
+ progressCountPanel.updateInfoFromSession();
+ testsHierarchyViewer.getTreeViewer().refresh();
+ testsHierarchyViewer.getTreeViewer().collapseAll();
+ testsHierarchyViewer.getTreeViewer().expandToLevel(2);
+ }
+ });
+ }
+ }
+
+
+ /**
+ * Listener to handle active testing session change.
+ */
+ private class TestingSessionsManagerListener implements ITestingSessionsManagerListener {
+
+ @Override
+ public void sessionActivated(ITestingSession newTestingSession) {
+ if (testingSession != newTestingSession) {
+ stopUpdateUIJob();
+ uiChangesCache.resetChanges();
+
+ unsubscribeFromSessionEvent();
+ testingSession = newTestingSession;
+ subscribeToSessionEvent();
+
+ resultsView.updateActionsFromSession();
+ Display.getDefault().syncExec(new Runnable() {
+
+ @Override
+ public void run() {
+ progressCountPanel.setTestingSession(testingSession);
+ testsHierarchyViewer.setTestingSession(testingSession);
+ resultsView.setCaption(testingSession != null ? testingSession.getStatusMessage() : ""); //$NON-NLS-1$
+ }
+ });
+ if (newTestingSession != null && !newTestingSession.isFinished()) {
+ startUpdateUIJob();
+ }
+ }
+ }
+ }
+
+
+ public UIUpdater(ResultsView resultsView, TestsHierarchyViewer testsHierarchyViewer, ProgressCountPanel progressCountPanel, TestingSessionsManager sessionsManager) {
+ this.resultsView = resultsView;
+ this.testsHierarchyViewer = testsHierarchyViewer;
+ this.progressCountPanel = progressCountPanel;
+ this.sessionsManager = sessionsManager;
+ sessionListener = new SessionListener();
+ sessionsManagerListener = new TestingSessionsManagerListener();
+ sessionsManager.addListener(sessionsManagerListener);
+ }
+
+ /**
+ * Returns whether tests hierarchy scrolling should be done during the
+ * testing process.
+ *
+ * @return auto scroll state
+ */
+ public boolean getAutoScroll() {
+ return autoScroll;
+ }
+
+ /**
+ * Sets whether whether tests hierarchy scrolling should be done during the
+ * testing process.
+ *
+ * @param autoScroll new filter state
+ */
+ public void setAutoScroll(boolean autoScroll) {
+ this.autoScroll = autoScroll;
+ }
+
+ /**
+ * Disposes of the UI Updater. Make the necessary clean up.
+ */
+ public void dispose() {
+ unsubscribeFromSessionEvent();
+ sessionsManager.removeListener(sessionsManagerListener);
+ }
+
+ /**
+ * Subscribes to the events of currently set testing session.
+ */
+ private void subscribeToSessionEvent() {
+ if (testingSession != null) {
+ testingSession.getModelAccessor().addChangesListener(sessionListener);
+ }
+ }
+
+ /**
+ * Unsubscribe from the events of currently set testing session.
+ */
+ private void unsubscribeFromSessionEvent() {
+ if (testingSession != null) {
+ testingSession.getModelAccessor().removeChangesListener(sessionListener);
+ }
+ }
+
+ /**
+ * Starts the UI updating job. Stops the previously running (if any).
+ */
+ private void startUpdateUIJob() {
+ stopUpdateUIJob();
+ uiChangesCache.resetChanges();
+ updateUIJob = new UpdateUIJob();
+ updateUIJob.scheduleSelf();
+ }
+
+ /**
+ * Stops the UI updating job (if any).
+ */
+ private void stopUpdateUIJob() {
+ if (updateUIJob != null) {
+ updateUIJob.stop();
+ updateUIJob = null;
+ }
+ }
+
+ /**
+ * Fakes the testing session activation and makes all necessary steps to
+ * handle it.
+ */
+ public void reapplyActiveSession() {
+ sessionsManagerListener.sessionActivated(sessionsManager.getActiveSession());
+ }
+
+}

Back to the top