Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnton Gorenkov2012-04-17 23:50:07 -0400
committerMarc-Andre Laperle2012-04-17 23:50:07 -0400
commitf16f0dbe483fd8fe1b2c5ab343d03201bfcccbf9 (patch)
treef4c19f2733b05d642eeb14ec90a79065640ae23e /testsrunner/org.eclipse.cdt.testsrunner/src
parent83cf5dc8303da5df560bde6ebb602b1e5cc59fb8 (diff)
downloadorg.eclipse.cdt-f16f0dbe483fd8fe1b2c5ab343d03201bfcccbf9.tar.gz
org.eclipse.cdt-f16f0dbe483fd8fe1b2c5ab343d03201bfcccbf9.tar.xz
org.eclipse.cdt-f16f0dbe483fd8fe1b2c5ab343d03201bfcccbf9.zip
Bug 210366 - CDT should have Unit Testing and Component Testing like JDT
Diffstat (limited to 'testsrunner/org.eclipse.cdt.testsrunner/src')
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/TestsRunnerPlugin.java239
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/CdiRunTestsLaunchDelegate.java26
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/DsfGdbRunTestsLaunchDelegate.java26
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/ITestsLaunchConfigurationConstants.java36
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/LauncherMessages.java26
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/LauncherMessages.properties12
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/ProcessWrapper.java115
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/TestingProcessFactory.java111
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/TestsRunnerProviderInfo.java167
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/TestsRunnerProvidersManager.java80
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/ITestingSessionsManagerListener.java30
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/ModelMessages.java28
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/ModelMessages.properties14
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestCase.java102
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestItem.java56
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestLocation.java41
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestMessage.java57
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestModelManager.java480
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestSuite.java85
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestingSession.java280
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestingSessionsManager.java215
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/launcher/CTestingTab.java200
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/launcher/EmptyConfigurationTabGroup.java29
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/launcher/UILauncherMessages.java29
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/launcher/UILauncherMessages.properties15
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/CounterPanel.java147
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/DummyUISession.java85
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/MessagesViewer.java573
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/ProgressBar.java183
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/ProgressCountPanel.java87
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/ResultsPanel.java245
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/ResultsView.java366
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/TestPathUtils.java115
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/TestsHierarchyViewer.java554
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/UIUpdater.java517
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/UIViewMessages.java36
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/UIViewMessages.properties22
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ActionsMessages.java77
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ActionsMessages.properties63
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/CopySelectedMessagesAction.java65
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/CopySelectedTestsAction.java65
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/HistoryDropDownAction.java448
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/MessageLevelFilterAction.java58
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/MessagesOrderingAction.java42
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/OpenInEditorAction.java181
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/RedebugSelectedAction.java36
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/RelaunchSelectedAction.java96
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/RerunAction.java50
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/RerunSelectedAction.java36
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ScrollLockAction.java41
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ShowFailedOnlyAction.java40
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ShowFileNameOnlyAction.java38
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ShowNextFailureAction.java40
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ShowPreviousFailureAction.java40
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ShowTestsInHierarchyAction.java40
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ShowTimeAction.java37
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/StopAction.java46
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/TestsHierarchyCollapseAllAction.java37
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/TestsHierarchyExpandAllAction.java37
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ToggleOrientationAction.java60
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/launcher/BaseTestsLaunchDelegate.java255
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/launcher/ITestsRunnerConstants.java40
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/launcher/ITestsRunnerProvider.java54
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/launcher/ITestsRunnerProviderInfo.java68
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/IModelVisitor.java33
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestCase.java31
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestItem.java95
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestLocation.java35
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestMessage.java72
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestModelAccessor.java52
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestModelUpdater.java94
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestSuite.java22
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestingSession.java135
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestingSessionListener.java66
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ModelMessages.java30
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ModelMessages.properties16
-rw-r--r--testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/TestingException.java28
77 files changed, 8228 insertions, 0 deletions
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/TestsRunnerPlugin.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/TestsRunnerPlugin.java
new file mode 100644
index 0000000000..0e5ee47fbb
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/TestsRunnerPlugin.java
@@ -0,0 +1,239 @@
+/*******************************************************************************
+ * 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;
+
+import java.net.URL;
+import java.util.HashSet;
+
+import org.eclipse.cdt.testsrunner.internal.launcher.TestsRunnerProvidersManager;
+import org.eclipse.cdt.testsrunner.internal.model.TestingSessionsManager;
+import org.eclipse.cdt.testsrunner.launcher.ITestsRunnerConstants;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunchConfigurationType;
+import org.eclipse.debug.core.ILaunchDelegate;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class TestsRunnerPlugin extends AbstractUIPlugin {
+
+ /** The plug-in ID. */
+ private static final String PLUGIN_ID = "org.eclipse.cdt.testsrunner"; //$NON-NLS-1$
+
+ /** The path to the plugin icons */
+ private static final IPath ICONS_PATH = new Path("$nl$/icons"); //$NON-NLS-1$
+
+ /** Plug-in instance. */
+ private static TestsRunnerPlugin plugin;
+
+ private TestsRunnerProvidersManager testsRunnerProvidersManager = new TestsRunnerProvidersManager();
+ private TestingSessionsManager testingSessionsManager = new TestingSessionsManager(testsRunnerProvidersManager);
+
+
+ public TestsRunnerPlugin() {
+ super();
+ plugin = this;
+ }
+
+ /**
+ * Returns the Tests Runner Plug-in instance
+ *
+ * @return the plug-in instance
+ */
+ public static TestsRunnerPlugin getDefault() {
+ return plugin;
+ }
+
+ /** Convenience method which returns the unique identifier of this plug-in. */
+ public static String getUniqueIdentifier() {
+ return PLUGIN_ID;
+ }
+
+ /**
+ * Logs the specified status with this plug-in's log.
+ *
+ * @param status status to log
+ */
+ public static void log(IStatus status) {
+ getDefault().getLog().log(status);
+ }
+
+ /**
+ * Logs an internal error with the specified message.
+ *
+ * @param message the error message to log
+ */
+ public static void logErrorMessage(String message) {
+ log(new Status(IStatus.ERROR, getUniqueIdentifier(), IStatus.ERROR, message, null));
+ }
+
+ /**
+ * Logs an internal error with the specified throwable
+ *
+ * @param e the exception to be logged
+ */
+ public static void log(Throwable e) {
+ log(new Status(IStatus.ERROR, getUniqueIdentifier(), IStatus.ERROR, e.getMessage(), e));
+ }
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+
+ setDefaultLaunchDelegates();
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ super.stop(context);
+ }
+
+ /**
+ * Access the plugin's Testing Sessions Manager instance.
+ *
+ * @return sessions manager
+ */
+ public TestingSessionsManager getTestingSessionsManager() {
+ return testingSessionsManager;
+ }
+
+ /**
+ * Access the plugin's Test Runners manager instance.
+ *
+ * @return tests runners manager
+ */
+ public TestsRunnerProvidersManager getTestsRunnerProvidersManager() {
+ return testsRunnerProvidersManager;
+ }
+
+ /**
+ * Returns the descriptor for image with <code>relativePath</code> path.
+ *
+ * @param relativePath path relative to <code>ICONS_PATH</code>
+ * @return image descriptor
+ */
+ static public ImageDescriptor getImageDescriptor(String relativePath) {
+ return getDefault().getImageDescriptorImpl(relativePath);
+ }
+
+ /**
+ * Returns the descriptor for image with <code>relativePath</code> path.
+ *
+ * @param relativePath path relative to <code>ICONS_PATH</code>
+ * @return image descriptor
+ */
+ private ImageDescriptor getImageDescriptorImpl(String relativePath) {
+ IPath path = ICONS_PATH.append(relativePath);
+ return createImageDescriptor(getDefault().getBundle(), path, true);
+ }
+
+ /**
+ * Returns the image with the specified path.
+ *
+ * @param path path
+ * @return image image
+ */
+ public static Image createAutoImage(String path) {
+ return getDefault().createAutoImageImpl(path);
+ }
+
+ /**
+ * Returns the image with the specified path.
+ *
+ * @param path path
+ * @return image image
+ */
+ private Image createAutoImageImpl(String path) {
+ Image image = getImageRegistry().get(path);
+ if (image == null) {
+ image = getImageDescriptor(path).createImage();
+ getImageRegistry().put(path, image);
+ }
+ return image;
+ }
+
+ /**
+ * Creates an image descriptor for the given path in a bundle. The path can
+ * contain variables like $NL$. If no image could be found,
+ * <code>useMissingImageDescriptor</code> decides if either the 'missing
+ * image descriptor' is returned or <code>null</code>.
+ *
+ * @param bundle a bundle
+ * @param path path in the bundle
+ * @param useMissingImageDescriptor if <code>true</code>, returns the shared
+ * image descriptor for a missing image. Otherwise, returns
+ * <code>null</code> if the image could not be found
+ * @return an {@link ImageDescriptor}, or <code>null</code> iff there's no
+ * image at the given location and <code>useMissingImageDescriptor</code> is
+ * <code>true</code>
+ */
+ private ImageDescriptor createImageDescriptor(Bundle bundle, IPath path, boolean useMissingImageDescriptor) {
+ URL url = FileLocator.find(bundle, path, null);
+ if (url != null) {
+ return ImageDescriptor.createFromURL(url);
+ }
+ if (useMissingImageDescriptor) {
+ return ImageDescriptor.getMissingImageDescriptor();
+ }
+ return null;
+ }
+
+ /**
+ * Setup the launch delegate with id <code>delegateId</code> as default for
+ * launch configuration type <code>cfgType</code> for <code>mode</code>
+ * launch mode.
+ *
+ * @param cfgType launch configuration type
+ * @param delegateId unique identifier of the launch delegate
+ * @param mode launch mode
+ */
+ private void setDefaultLaunchDelegate(ILaunchConfigurationType cfgType, String delegateId, String mode) {
+ HashSet<String> modes = new HashSet<String>();
+ modes.add(mode);
+ try {
+ if (cfgType.getPreferredDelegate(modes) == null) {
+ ILaunchDelegate[] delegates = cfgType.getDelegates(modes);
+ for (ILaunchDelegate delegate : delegates) {
+ if (delegateId.equals(delegate.getId())) {
+ cfgType.setPreferredDelegate(modes, delegate);
+ break;
+ }
+ }
+ }
+ } catch (CoreException e) {
+ log(e);
+ }
+ }
+
+ /**
+ * Sets up the default launch delegates for the Tests Runner's launch configuration type.
+ */
+ private void setDefaultLaunchDelegates() {
+ ILaunchManager launchMgr = DebugPlugin.getDefault().getLaunchManager();
+ ILaunchConfigurationType configurationType = launchMgr.getLaunchConfigurationType(ITestsRunnerConstants.LAUNCH_CONFIGURATION_TYPE_ID);
+
+ setDefaultLaunchDelegate(configurationType, ITestsRunnerConstants.PREFERRED_DEBUG_TESTS_LAUNCH_DELEGATE, ILaunchManager.DEBUG_MODE);
+ setDefaultLaunchDelegate(configurationType, ITestsRunnerConstants.PREFERRED_RUN_TESTS_LAUNCH_DELEGATE, ILaunchManager.RUN_MODE);
+ }
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/CdiRunTestsLaunchDelegate.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/CdiRunTestsLaunchDelegate.java
new file mode 100644
index 0000000000..c0bef98204
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/CdiRunTestsLaunchDelegate.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * 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.launcher;
+
+import org.eclipse.cdt.testsrunner.launcher.BaseTestsLaunchDelegate;
+
+
+/**
+ * Launch delegate implementation that redirects its queries to CDI.
+ */
+public class CdiRunTestsLaunchDelegate extends BaseTestsLaunchDelegate {
+
+ @Override
+ public String getPreferredDelegateId() {
+ return "org.eclipse.cdt.cdi.launch.localCLaunch"; //$NON-NLS-1$
+ }
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/DsfGdbRunTestsLaunchDelegate.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/DsfGdbRunTestsLaunchDelegate.java
new file mode 100644
index 0000000000..7f68f38d04
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/DsfGdbRunTestsLaunchDelegate.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * 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.launcher;
+
+import org.eclipse.cdt.testsrunner.launcher.BaseTestsLaunchDelegate;
+
+
+/**
+ * Launch delegate implementation that redirects its queries to DSF.
+ */
+public class DsfGdbRunTestsLaunchDelegate extends BaseTestsLaunchDelegate {
+
+ @Override
+ public String getPreferredDelegateId() {
+ return "org.eclipse.cdt.dsf.gdb.launch.localCLaunch"; //$NON-NLS-1$
+ }
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/ITestsLaunchConfigurationConstants.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/ITestsLaunchConfigurationConstants.java
new file mode 100644
index 0000000000..fa08017b1b
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/ITestsLaunchConfigurationConstants.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2012 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.launcher;
+
+/**
+ * Constants used for attributes in CDT Unit Testing Support launch
+ * configurations.
+ *
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface ITestsLaunchConfigurationConstants {
+
+ public static final String CDT_TESTS_LAUNCH_ID = "org.eclipse.cdt.testsrunner.launch"; //$NON-NLS-1$
+
+ /**
+ * Launch configuration attribute key. The value is a string specifying
+ * tests runner string unique identifier
+ */
+ public static final String ATTR_TESTS_RUNNER = CDT_TESTS_LAUNCH_ID + ".TESTS_RUNNER"; //$NON-NLS-1$
+
+ /**
+ * Launch configuration attribute key. The value is a string list specifying
+ * tests paths to run
+ */
+ public static final String ATTR_TESTS_FILTER = CDT_TESTS_LAUNCH_ID + ".TESTS_FILTER"; //$NON-NLS-1$
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/LauncherMessages.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/LauncherMessages.java
new file mode 100644
index 0000000000..2b66e11909
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/LauncherMessages.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * 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.launcher;
+
+import org.eclipse.osgi.util.NLS;
+
+public class LauncherMessages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.cdt.testsrunner.internal.launcher.LauncherMessages"; //$NON-NLS-1$
+ public static String BaseTestsLaunchDelegate_invalid_tests_runner;
+ public static String BaseTestsLaunchDelegate_tests_runner_load_failed;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, LauncherMessages.class);
+ }
+
+ private LauncherMessages() {
+ }
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/LauncherMessages.properties b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/LauncherMessages.properties
new file mode 100644
index 0000000000..2208efc2bc
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/LauncherMessages.properties
@@ -0,0 +1,12 @@
+###############################################################################
+# 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 implementation
+###############################################################################
+BaseTestsLaunchDelegate_invalid_tests_runner=Tests Runner is not specified or invalid
+BaseTestsLaunchDelegate_tests_runner_load_failed=Tests Runner cannot be instantiated
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/ProcessWrapper.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/ProcessWrapper.java
new file mode 100644
index 0000000000..5a99ec4bd2
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/ProcessWrapper.java
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * 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.launcher;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Wraps the underline process and prevent accessing to its output or error stream.
+ * This wrapping is necessary to prevent handling the test module output by Console
+ * because we want to handle it here.
+ *
+ */
+class ProcessWrapper extends Process {
+
+ /** The real underlying process. */
+ private Process wrappedProcess;
+
+ /** The flag shows whether input stream should be replaced with empty dummy. */
+ private boolean hideInputStream;
+
+ /** The flag shows whether error stream should be replaced with empty dummy. */
+ private boolean hideErrorStream;
+
+
+ /** Buffer for empty dummy stream. */
+ private byte buffer[] = new byte[0];
+
+ /** Empty dummy stream. */
+ private InputStream emptyInputStream = new ByteArrayInputStream(buffer);
+
+ /**
+ * The synchronization event: before it happens <code>waitFor()</code> will
+ * not be called on underlying process object. See also the comments in
+ * <code>waitFor()</code>.
+ */
+ private Object waitForSync = new Object();
+
+ /**
+ * Flag that shows that process output was not processed yet so the IO
+ * streams could not be closed yet.
+ */
+ private boolean streamsClosingIsAllowed = false;
+
+
+ /**
+ * The constructor
+ *
+ * @param wrappedProcess underlying process
+ * @param hideInputStream process input stream should be hidden
+ * @param hideErrorStream process error stream should be hidden
+ */
+ public ProcessWrapper(Process wrappedProcess, boolean hideInputStream, boolean hideErrorStream) {
+ this.wrappedProcess = wrappedProcess;
+ this.hideInputStream = hideInputStream;
+ this.hideErrorStream = hideErrorStream;
+ }
+
+ @Override
+ public void destroy() {
+ wrappedProcess.destroy();
+ }
+
+ @Override
+ public int exitValue() {
+ return wrappedProcess.exitValue();
+ }
+
+ @Override
+ public InputStream getErrorStream() {
+ return hideErrorStream ? emptyInputStream : wrappedProcess.getErrorStream();
+ }
+
+ @Override
+ public InputStream getInputStream() {
+ return hideInputStream ? emptyInputStream : wrappedProcess.getInputStream();
+ }
+
+ @Override
+ public OutputStream getOutputStream() {
+ return wrappedProcess.getOutputStream();
+ }
+
+ @Override
+ public int waitFor() throws InterruptedException {
+ // NOTE: implementation of waitFor() in Spawner will close streams after process is terminated,
+ // so we should wait with this operation until we process all stream data
+ synchronized (waitForSync) {
+ if (!streamsClosingIsAllowed) {
+ waitForSync.wait();
+ }
+ }
+ return wrappedProcess.waitFor();
+ }
+
+ /**
+ * Sets up the flag the allows IO streams closing.
+ */
+ public void allowStreamsClosing() {
+ synchronized (waitForSync) {
+ streamsClosingIsAllowed = true;
+ waitForSync.notifyAll();
+ }
+ }
+
+} \ No newline at end of file
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/TestingProcessFactory.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/TestingProcessFactory.java
new file mode 100644
index 0000000000..ce498d5bf2
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/TestingProcessFactory.java
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * 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.launcher;
+
+import java.io.InputStream;
+import java.util.Map;
+
+import org.eclipse.cdt.dsf.gdb.IGdbDebugConstants;
+import org.eclipse.cdt.dsf.gdb.launching.GDBProcess;
+import org.eclipse.cdt.dsf.gdb.launching.InferiorRuntimeProcess;
+import org.eclipse.cdt.testsrunner.internal.TestsRunnerPlugin;
+import org.eclipse.cdt.testsrunner.internal.model.TestingSession;
+import org.eclipse.cdt.testsrunner.launcher.ITestsRunnerProviderInfo;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.IProcessFactory;
+import org.eclipse.debug.core.model.IProcess;
+import org.eclipse.debug.core.model.RuntimeProcess;
+
+/**
+ * Custom testing process factory allows to handle the output stream of the
+ * testing process and prevent it from output to Console.
+ */
+public class TestingProcessFactory implements IProcessFactory {
+
+ /**
+ * Runs data processing for the testing process and close IO stream when it
+ * is done.
+ */
+ private class TestingSessionRunner implements Runnable {
+
+ private TestingSession testingSession;
+ private InputStream iStream;
+ private ProcessWrapper processWrapper;
+
+ TestingSessionRunner(TestingSession testingSession, InputStream iStream, ProcessWrapper processWrapper) {
+ this.testingSession = testingSession;
+ this.iStream = iStream;
+ this.processWrapper = processWrapper;
+ }
+
+ @Override
+ public void run() {
+ try {
+ testingSession.run(iStream);
+ }
+ finally {
+ // Streams should be closed anyway to avoid testing process hang up
+ processWrapper.allowStreamsClosing();
+ }
+ }
+ }
+
+ /**
+ * Creates a wrapper for the specified process to handle its input or error
+ * stream.
+ *
+ * @param launch launch
+ * @param process process to wrap
+ * @return wrapped process
+ * @throws CoreException
+ */
+ private Process wrapProcess(ILaunch launch, Process process) throws CoreException {
+ TestingSession testingSession = TestsRunnerPlugin.getDefault().getTestingSessionsManager().newSession(launch);
+ ITestsRunnerProviderInfo testsRunnerProvider = testingSession.getTestsRunnerProviderInfo();
+ InputStream iStream =
+ testsRunnerProvider.isOutputStreamRequired() ? process.getInputStream() :
+ testsRunnerProvider.isErrorStreamRequired() ? process.getErrorStream() : null;
+ ProcessWrapper processWrapper = new ProcessWrapper(process, testsRunnerProvider.isOutputStreamRequired(), testsRunnerProvider.isErrorStreamRequired());
+ Thread t = new Thread(new TestingSessionRunner(testingSession, iStream, processWrapper));
+ t.start();
+ return processWrapper;
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ @Override
+ public IProcess newProcess(ILaunch launch, Process process, String label, Map attributes) {
+
+ try {
+ // Mimic the behavior of DSF GDBProcessFactory.
+ if (attributes != null) {
+ Object processTypeCreationAttrValue = attributes.get(IGdbDebugConstants.PROCESS_TYPE_CREATION_ATTR);
+ if (IGdbDebugConstants.GDB_PROCESS_CREATION_VALUE.equals(processTypeCreationAttrValue)) {
+ return new GDBProcess(launch, process, label, attributes);
+ }
+
+ if (IGdbDebugConstants.INFERIOR_PROCESS_CREATION_VALUE.equals(processTypeCreationAttrValue)) {
+ return new InferiorRuntimeProcess(launch, wrapProcess(launch, process), label, attributes);
+ }
+
+ // Probably, it is CDI creating a new inferior process
+ } else {
+ return new RuntimeProcess(launch, wrapProcess(launch, process), label, attributes);
+ }
+
+ } catch (CoreException e) {
+ TestsRunnerPlugin.log(e);
+ }
+
+ return null;
+ }
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/TestsRunnerProviderInfo.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/TestsRunnerProviderInfo.java
new file mode 100644
index 0000000000..5f5927b3c9
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/TestsRunnerProviderInfo.java
@@ -0,0 +1,167 @@
+/*******************************************************************************
+ * 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.launcher;
+
+import org.eclipse.cdt.testsrunner.internal.TestsRunnerPlugin;
+import org.eclipse.cdt.testsrunner.launcher.ITestsRunnerProvider;
+import org.eclipse.cdt.testsrunner.launcher.ITestsRunnerProviderInfo;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+
+/**
+ * Provides access to the information about the Tests Runner provider plug-in
+ * (id, name, description, supported features set). Also provides the access to
+ * <code>ITestsRunnerProvider</code> interface (the main interface of Tests Runner
+ * provider plug-in).
+ */
+public class TestsRunnerProviderInfo implements ITestsRunnerProviderInfo {
+
+ private static final String TESTS_RUNNER_FEATURES_ELEMENT = "features"; //$NON-NLS-1$
+ private static final String TESTS_RUNNER_FEATURE_MULTIPLE_TEST_FILTER_ATTRIBUTE = "multipleTestFilter"; //$NON-NLS-1$
+ private static final String TESTS_RUNNER_FEATURE_TESTING_TIME_MEASUREMENT_ATTRIBUTE = "testingTimeMeasurement"; //$NON-NLS-1$
+ private static final String TESTS_RUNNER_FEATURE_DATA_STREAM_ATTRIBUTE = "dataStream"; //$NON-NLS-1$
+ private static final String TESTS_RUNNER_ID_ATTRIBUTE = "id"; //$NON-NLS-1$
+ private static final String TESTS_RUNNER_NAME_ATTRIBUTE = "name"; //$NON-NLS-1$
+ private static final String TESTS_RUNNER_CLASS_ATTRIBUTE = "class"; //$NON-NLS-1$
+ private static final String TESTS_RUNNER_DESCRIPTION_ATTRIBUTE = "description"; //$NON-NLS-1$
+ private static final String TESTS_RUNNER_ERROR_STREAM_VALUE = "error"; //$NON-NLS-1$
+
+ /** Configuration element of the Tests Runner provider plug-ins extension point. */
+ private IConfigurationElement element;
+
+ /**
+ * The constructor.
+ *
+ * @param element configuration element of the Tests Runner provider plug-ins extension point
+ */
+ public TestsRunnerProviderInfo(IConfigurationElement element) {
+ this.element = element;
+ }
+
+ @Override
+ public String getName() {
+ return element.getAttribute(TESTS_RUNNER_NAME_ATTRIBUTE);
+ }
+
+ @Override
+ public String getId() {
+ return element.getAttribute(TESTS_RUNNER_ID_ATTRIBUTE);
+ }
+
+ @Override
+ public String getDescription() {
+ String result = element.getAttribute(TESTS_RUNNER_DESCRIPTION_ATTRIBUTE);
+ return result == null ? "" : result; //$NON-NLS-1$
+ }
+
+ /**
+ * Instantiates Tests Runner provider plug-in and return its main interface.
+ *
+ * @note Instantiated Tests Runner provider plug-in objects are not cached
+ * and are instantiated as much times as
+ * <code>instantiateTestsRunnerProvider()</code> is called.
+ *
+ * @return Tests Runner provider plug-in main interface
+ */
+ public ITestsRunnerProvider instantiateTestsRunnerProvider() {
+ try {
+ Object object = element.createExecutableExtension(TESTS_RUNNER_CLASS_ATTRIBUTE);
+ if (object instanceof ITestsRunnerProvider) {
+ return (ITestsRunnerProvider)object;
+ }
+ } catch (CoreException e) {
+ TestsRunnerPlugin.log(e);
+ }
+ return null;
+ }
+
+ /**
+ * Provides an access to the 'features' node of Tests Runner provider
+ * plug-in configuration element.
+ *
+ * @return 'features' configuration element
+ */
+ private IConfigurationElement getFeatures() {
+ IConfigurationElement[] featuresElements = element.getChildren(TESTS_RUNNER_FEATURES_ELEMENT);
+ if (featuresElements.length == 1) {
+ return featuresElements[0];
+ }
+ return null;
+ }
+
+ /**
+ * Provides an access to the value of the feature with specified name.
+ *
+ * @param featureName
+ * @return feature value or null if there is no features described or there
+ * is no feature with such name
+ */
+ private String getFeatureAttributeValue(String featureName) {
+ IConfigurationElement features = getFeatures();
+ if (features != null) {
+ return features.getAttribute(featureName);
+ }
+ return null;
+ }
+
+ /**
+ * Provides an access to the boolean value of the feature with the specified
+ * name. If the feature with such name cannot be accessed or it contains
+ * invalid boolean value, the default value will be returned.
+ *
+ * @param featureName
+ * @param defaultValue
+ * @return feature value or null if there is no features described or there
+ * is no feature with such name
+ */
+ private boolean getBooleanFeatureValue(String featureName, boolean defaultValue) {
+ String attrValue = getFeatureAttributeValue(featureName);
+ if (attrValue != null) {
+ return Boolean.parseBoolean(attrValue);
+ }
+ return defaultValue;
+ }
+
+ @Override
+ public boolean isAllowedMultipleTestFilter() {
+ return getBooleanFeatureValue(TESTS_RUNNER_FEATURE_MULTIPLE_TEST_FILTER_ATTRIBUTE, false);
+ }
+
+ /**
+ * Returns whether test execution time measurement should be performed by
+ * the Tests Runner Plug-in.
+ *
+ * @note It should be used only if testing framework does not provide
+ * execution time measurement, cause measurement by Tests Runner Plug-in is
+ * not very precise (it also takes into account output processing & internal
+ * model building time).
+ *
+ * @return whether testing time measurement should be done by Tests Runner
+ * Plug-in
+ */
+ public boolean isAllowedTestingTimeMeasurement() {
+ return getBooleanFeatureValue(TESTS_RUNNER_FEATURE_TESTING_TIME_MEASUREMENT_ATTRIBUTE, false);
+ }
+
+ @Override
+ public boolean isOutputStreamRequired() {
+ return !isErrorStreamRequired();
+ }
+
+ @Override
+ public boolean isErrorStreamRequired() {
+ String attrValue = getFeatureAttributeValue(TESTS_RUNNER_FEATURE_DATA_STREAM_ATTRIBUTE);
+ if (attrValue != null) {
+ return attrValue.equals(TESTS_RUNNER_ERROR_STREAM_VALUE);
+ }
+ return false;
+ }
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/TestsRunnerProvidersManager.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/TestsRunnerProvidersManager.java
new file mode 100644
index 0000000000..39f216ab35
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/launcher/TestsRunnerProvidersManager.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * 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.launcher;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.debug.core.ILaunchConfiguration;
+
+/**
+ * Collects the data from the Tests Runner provider plug-in extension points and
+ * provides the convenient access to it.
+ */
+public class TestsRunnerProvidersManager {
+
+ /** Tests Runner Plug-ins extension point ID. */
+ private static final String TESTS_RUNNER_EXTENSION_POINT_ID = "org.eclipse.cdt.testsrunner.TestsRunner"; //$NON-NLS-1$
+
+ /** Tests Runner Plug-ins information collection. */
+ private TestsRunnerProviderInfo[] testsRunnerProviders = null;
+
+
+ /**
+ * Provides access to information about all registered Tests Runner
+ * Plug-ins.
+ *
+ * @return array of tests runner plug-ins descriptors
+ */
+ public TestsRunnerProviderInfo[] getTestsRunnersProviderInfo() {
+ if (testsRunnerProviders == null) {
+ // Initialize tests runners info
+ List<TestsRunnerProviderInfo> testsRunnerProvidersList = new ArrayList<TestsRunnerProviderInfo>();
+ for (IConfigurationElement element : Platform.getExtensionRegistry().getConfigurationElementsFor(TESTS_RUNNER_EXTENSION_POINT_ID)) {
+ testsRunnerProvidersList.add(new TestsRunnerProviderInfo(element));
+ }
+ testsRunnerProviders = testsRunnerProvidersList.toArray(new TestsRunnerProviderInfo[testsRunnerProvidersList.size()]);
+ }
+ return testsRunnerProviders;
+ }
+
+ /**
+ * Provides access to information about Tests Runner Plug-in referred in the
+ * specified launch configuration.
+ *
+ * @return tests runner plug-in descriptor
+ */
+ public TestsRunnerProviderInfo getTestsRunnerProviderInfo(ILaunchConfiguration launchConf) throws CoreException {
+ String testsRunnerId = launchConf.getAttribute(ITestsLaunchConfigurationConstants.ATTR_TESTS_RUNNER, (String)null);
+ return getTestsRunnerProviderInfo(testsRunnerId);
+ }
+
+ /**
+ * Provides access to information about Tests Runner Plug-in with the
+ * specified ID.
+ *
+ * @return tests runner plug-in descriptor
+ */
+ private TestsRunnerProviderInfo getTestsRunnerProviderInfo(String testsRunnerProviderId) {
+ if (testsRunnerProviderId != null) {
+ for (TestsRunnerProviderInfo testsRunnerProvider : getTestsRunnersProviderInfo()) {
+ if (testsRunnerProvider.getId().equals(testsRunnerProviderId)) {
+ return testsRunnerProvider;
+ }
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/ITestingSessionsManagerListener.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/ITestingSessionsManagerListener.java
new file mode 100644
index 0000000000..578ed7b2f0
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/ITestingSessionsManagerListener.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * 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.model;
+
+import org.eclipse.cdt.testsrunner.model.ITestingSession;
+
+/**
+ * Testing sessions manager listener is notified of testing sessions management.
+ *
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface ITestingSessionsManagerListener {
+
+ /**
+ * Notifies the listener that the specified testing session was activated.
+ *
+ * @param testingSession the activated testing session
+ */
+ void sessionActivated(ITestingSession testingSession);
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/ModelMessages.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/ModelMessages.java
new file mode 100644
index 0000000000..b690f1cfa3
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/ModelMessages.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * 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.model;
+
+import org.eclipse.osgi.util.NLS;
+
+public class ModelMessages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.cdt.testsrunner.internal.model.ModelMessages"; //$NON-NLS-1$
+ public static String TestingSession_finished_status;
+ public static String TestingSession_name_format;
+ public static String TestingSession_starting_status;
+ public static String TestingSession_stopped_status;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, ModelMessages.class);
+ }
+
+ private ModelMessages() {
+ }
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/ModelMessages.properties b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/ModelMessages.properties
new file mode 100644
index 0000000000..c57df6a158
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/ModelMessages.properties
@@ -0,0 +1,14 @@
+###############################################################################
+# 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 implementation
+###############################################################################
+TestingSession_name_format={0} ({1})
+TestingSession_finished_status=Finished after {0} seconds
+TestingSession_starting_status=Starting...
+TestingSession_stopped_status=Testing was stopped by user
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestCase.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestCase.java
new file mode 100644
index 0000000000..84e9db50d1
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestCase.java
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * 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.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.cdt.testsrunner.model.IModelVisitor;
+import org.eclipse.cdt.testsrunner.model.ITestCase;
+
+/**
+ * Represents the test case (test) of the test hierarchy.
+ */
+public class TestCase extends TestItem implements ITestCase {
+
+ /** Test case status (failed, passed, ...). */
+ private Status status;
+
+ /** Test case execution time. */
+ private int testingTime;
+
+ /**
+ * The messages that were generated by testing framework during test
+ * running.
+ */
+ private List<TestMessage> testMessages = new ArrayList<TestMessage>();
+
+
+ public TestCase(String name, TestSuite parent) {
+ super(name, parent);
+ reset();
+ }
+
+ @Override
+ public Status getStatus() {
+ return status;
+ }
+
+ @Override
+ public int getTestingTime() {
+ return testingTime;
+ }
+
+ @Override
+ public TestMessage[] getTestMessages() {
+ return testMessages.toArray(new TestMessage[testMessages.size()]);
+ }
+
+ @Override
+ public void visit(IModelVisitor visitor) {
+ visitor.visit(this);
+ for (TestMessage message : testMessages) {
+ message.visit(visitor);
+ }
+ visitor.leave(this);
+ }
+
+ /**
+ * Resets the test case to the default state.
+ */
+ public void reset() {
+ status = Status.Skipped;
+ testingTime = 0;
+ testMessages.clear();
+ }
+
+ /**
+ * Modifies the status of the test case.
+ *
+ * @param status new test status
+ */
+ public void setStatus(Status status) {
+ this.status = status;
+ }
+
+ /**
+ * Modifies the execution time of the test case.
+ *
+ * @param testingTime new test execution time
+ */
+ public void setTestingTime(int testingTime) {
+ this.testingTime = testingTime;
+ }
+
+ /**
+ * Adds a new test message to the test case.
+ *
+ * @param testMessage message
+ */
+ public void addTestMessage(TestMessage testMessage) {
+ testMessages.add(testMessage);
+ }
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestItem.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestItem.java
new file mode 100644
index 0000000000..55c775fda5
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestItem.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * 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.model;
+
+import org.eclipse.cdt.testsrunner.model.ITestItem;
+
+/**
+ * Common implementation for the structural item of test hierarchy (test suite
+ * or test case).
+ */
+public abstract class TestItem implements ITestItem {
+
+ /** Test item has no children by default. */
+ private static final ITestItem[] NO_CHILDREN = new ITestItem[0];
+
+ /** Test item name. */
+ private final String name;
+
+ /** Item parent test suite. May be <code>null</code> for root test suite. */
+ private TestSuite parent;
+
+
+ public TestItem(String name, TestSuite parent) {
+ this.name = name;
+ this.parent = parent;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public TestSuite getParent() {
+ return parent;
+ }
+
+ @Override
+ public boolean hasChildren() {
+ return false;
+ }
+
+ @Override
+ public ITestItem[] getChildren() {
+ return NO_CHILDREN;
+ }
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestLocation.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestLocation.java
new file mode 100644
index 0000000000..afe8bfaae5
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestLocation.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * 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.model;
+
+import org.eclipse.cdt.testsrunner.model.ITestLocation;
+
+/**
+ * Represents the location of the test object.
+ */
+public class TestLocation implements ITestLocation {
+
+ /** Stores the file name in which testing object is located. */
+ private String file;
+
+ /** Stores the line number on which testing object is located. */
+ private int line;
+
+
+ public TestLocation(String file, int line) {
+ this.file = file;
+ this.line = line;
+ }
+
+ @Override
+ public String getFile() {
+ return file;
+ }
+
+ @Override
+ public int getLine() {
+ return line;
+ }
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestMessage.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestMessage.java
new file mode 100644
index 0000000000..4a5402f48f
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestMessage.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * 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.model;
+
+import org.eclipse.cdt.testsrunner.model.IModelVisitor;
+import org.eclipse.cdt.testsrunner.model.ITestMessage;
+
+/**
+ * Represents the message that was produced during the testing process.
+ */
+public class TestMessage implements ITestMessage {
+
+ /** Test message location. */
+ private TestLocation location;
+
+ /** Test message level */
+ private Level level;
+
+ /** Test message text */
+ private String text;
+
+
+ public TestMessage(TestLocation location, Level level, String text) {
+ this.location = location;
+ this.level = level;
+ this.text = text;
+ }
+
+ @Override
+ public TestLocation getLocation() {
+ return location;
+ }
+
+ @Override
+ public Level getLevel() {
+ return level;
+ }
+
+ @Override
+ public String getText() {
+ return text;
+ }
+
+ @Override
+ public void visit(IModelVisitor visitor) {
+ visitor.visit(this);
+ visitor.leave(this);
+ }
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestModelManager.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestModelManager.java
new file mode 100644
index 0000000000..0db8d5af98
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestModelManager.java
@@ -0,0 +1,480 @@
+/*******************************************************************************
+ * 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.model;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+
+import org.eclipse.cdt.testsrunner.model.IModelVisitor;
+import org.eclipse.cdt.testsrunner.model.ITestMessage;
+import org.eclipse.cdt.testsrunner.model.ITestModelAccessor;
+import org.eclipse.cdt.testsrunner.model.ITestModelUpdater;
+import org.eclipse.cdt.testsrunner.model.ITestingSessionListener;
+import org.eclipse.cdt.testsrunner.model.ITestCase;
+import org.eclipse.cdt.testsrunner.model.ITestItem;
+import org.eclipse.cdt.testsrunner.model.ITestItem.Status;
+import org.eclipse.cdt.testsrunner.model.ITestMessage.Level;
+import org.eclipse.cdt.testsrunner.model.ITestSuite;
+
+/**
+ * Manages the testing model (creates, fill and update it) and notifies the
+ * listeners about updates.
+ */
+public class TestModelManager implements ITestModelUpdater, ITestModelAccessor {
+
+ /**
+ * Name of the root test suite.
+ *
+ * @note Root test suite is invisible (only its children are visible), so
+ * the name is not important.
+ */
+ public static final String ROOT_TEST_SUITE_NAME = "<root>"; //$NON-NLS-1$
+
+ /** Stack of the currently entered (and not existed) test suites. */
+ private Stack<TestSuite> testSuitesStack = new Stack<TestSuite>();
+
+ /**
+ * Currently running test case. There are no nested test cases, so the
+ * collection is not necessary.
+ */
+ private TestCase currentTestCase = null;
+
+ /**
+ * The mapping of test suite object to the index on which it was inserted to
+ * the parent.
+ *
+ * @note Test suites presence in this map means that test suite was visited
+ * during the testing process (not visited test suites are removed when
+ * testing is finished cause they are considered as renamed or removed).
+ * @note Test suite insert position is important for insertion algorithm.
+ */
+ private Map<TestItem, Integer> testSuitesIndex = new HashMap<TestItem, Integer>();
+
+ /** Listeners collection. */
+ private List<ITestingSessionListener> listeners = new ArrayList<ITestingSessionListener>();
+
+ /** Flag stores whether test execution time should be measured for the session. */
+ private boolean timeMeasurement = false;
+
+ /** Stores the test case start time or 0 there is no currently running test case. */
+ private long testCaseStartTime = 0;
+
+ /** Instance of the insertion algorithm for test suites. */
+ private TestSuiteInserter testSuiteInserter = new TestSuiteInserter();
+
+ /** Instance of the insertion algorithm for test cases. */
+ private TestCaseInserter testCaseInserter = new TestCaseInserter();
+
+
+ /**
+ * Builds current tests hierarchy from the other one (copies only necessary
+ * information).
+ */
+ private class HierarchyCopier implements IModelVisitor {
+
+ @Override
+ public void visit(ITestSuite testSuite) {
+ // Do not copy root test suite
+ if (testSuite.getParent() != null) {
+ enterTestSuite(testSuite.getName());
+ }
+ }
+
+ @Override
+ public void leave(ITestSuite testSuite) {
+ // Do not copy root test suite
+ if (testSuite.getParent() != null) {
+ exitTestSuite();
+ }
+ }
+
+ @Override
+ public void visit(ITestCase testCase) {
+ enterTestCase(testCase.getName());
+ setTestStatus(TestCase.Status.NotRun);
+ }
+
+ @Override
+ public void leave(ITestCase testCase) {
+ exitTestCase();
+ }
+
+ @Override
+ public void visit(ITestMessage testMessage) {}
+ @Override
+ public void leave(ITestMessage testMessage) {}
+ }
+
+
+ /**
+ * Utility class: generalization of insertion algorithm for test suites and
+ * test cases.
+ *
+ * <p>
+ * The algorithm tries to find the place where the new item should be
+ * inserted at. If the item with such name does not exist in the current top
+ * most test suite, it should be inserted at the current position. If it
+ * already exists (at the next or previous position) then it should be moved
+ * from there to the current one.
+ * </p>
+ *
+ * @param <E> test item type (test suite or test case)
+ */
+ private abstract class TestItemInserter<E extends TestItem> {
+
+ /**
+ * Check whether item has the required type (test suite for suites inserter and
+ * test case for cases one).
+ *
+ * @param item test item to check
+ * @return whether item has the required type
+ */
+ protected abstract boolean isRequiredTestItemType(TestItem item);
+
+ /**
+ * Creates a new item type with the specified name and parent (test
+ * suite for suites inserter and test case for cases one).
+ *
+ * @param name name of the new test item
+ * @param parent parent for the new test item
+ * @return new test item
+ */
+ protected abstract E createTestItem(String name, TestSuite parent);
+
+ /**
+ * Save new test item in the tracking structures (suite in stack, case
+ * in current variable). Additional operations (e.g. listeners
+ * notification about item entering) can be done too.
+ *
+ * @param item new test item
+ */
+ protected abstract void addNewTestItem(E item);
+
+
+ /**
+ * Returns the casted test item if it matches by name and type or
+ * <code>null</code> if it doesn't.
+ *
+ * @param item test item to check
+ * @param name test item name
+ * @return casted test item or null
+ */
+ @SuppressWarnings("unchecked")
+ private E checkTestItem(TestItem item, String name) {
+ return (isRequiredTestItemType(item) && item.getName().equals(name)) ? (E)item : null;
+ }
+
+ /**
+ * Returns the last insert index for the specified test suite. Returns 0
+ * if test suite was not inserted yet.
+ *
+ * @param testSuite test suite to look up
+ * @return insert index or 0
+ */
+ private int getLastInsertIndex(TestSuite testSuite) {
+ Integer intLastInsertIndex = testSuitesIndex.get(testSuite);
+ return intLastInsertIndex != null ? intLastInsertIndex : 0;
+ }
+
+ /**
+ * Notifies the listeners about children update of the specified test
+ * suite.
+ *
+ * @param suite updated test suite
+ */
+ private void notifyAboutChildrenUpdate(ITestSuite suite) {
+ for (ITestingSessionListener listener : getListenersCopy()) {
+ listener.childrenUpdate(suite);
+ }
+ }
+
+ /**
+ * Inserts the test item by the name.
+ *
+ * @param name test item name
+ */
+ public void insert(String name) {
+ TestSuite currTestSuite = testSuitesStack.peek();
+ int lastInsertIndex = getLastInsertIndex(currTestSuite);
+ List<TestItem> children = currTestSuite.getChildrenList();
+ E newTestItem = null;
+
+ // Optimization: Check whether we already pointing to the test suite with required name
+ try {
+ newTestItem = checkTestItem(children.get(lastInsertIndex), name);
+ } catch (IndexOutOfBoundsException e) {}
+ if (newTestItem != null) {
+ testSuitesIndex.put(currTestSuite, lastInsertIndex+1);
+ }
+
+ // Check whether the suite with required name was later in the hierarchy
+ if (newTestItem == null) {
+ for (int childIndex = lastInsertIndex; childIndex < children.size(); childIndex++) {
+ newTestItem = checkTestItem(children.get(childIndex), name);
+ if (newTestItem != null) {
+ testSuitesIndex.put(currTestSuite, childIndex);
+ break;
+ }
+ }
+ }
+
+ // Search in previous
+ if (newTestItem == null) {
+ for (int childIndex = 0; childIndex < lastInsertIndex; childIndex++) {
+ newTestItem = checkTestItem(children.get(childIndex), name);
+ if (newTestItem != null) {
+ children.add(lastInsertIndex, children.remove(childIndex));
+ notifyAboutChildrenUpdate(currTestSuite);
+ break;
+ }
+ }
+ }
+
+ // Add new
+ if (newTestItem == null) {
+ newTestItem = createTestItem(name, currTestSuite);
+ children.add(lastInsertIndex, newTestItem);
+ testSuitesIndex.put(currTestSuite, lastInsertIndex+1);
+ notifyAboutChildrenUpdate(currTestSuite);
+ }
+ if (!testSuitesIndex.containsKey(newTestItem)) {
+ testSuitesIndex.put(newTestItem, 0);
+ }
+ addNewTestItem(newTestItem);
+ }
+
+ }
+
+
+ /**
+ * Utility class: insertion algorithm specialization for test suites.
+ */
+ private class TestSuiteInserter extends TestItemInserter<TestSuite> {
+
+ @Override
+ protected boolean isRequiredTestItemType(TestItem item) {
+ return (item instanceof TestSuite);
+ }
+
+ @Override
+ protected TestSuite createTestItem(String name, TestSuite parent) {
+ return new TestSuite(name, parent);
+ }
+
+ @Override
+ protected void addNewTestItem(TestSuite testSuite) {
+ testSuitesStack.push(testSuite);
+
+ // Notify listeners
+ for (ITestingSessionListener listener : getListenersCopy()) {
+ listener.enterTestSuite(testSuite);
+ }
+ }
+ }
+
+
+ /**
+ * Utility class: insertion algorithm specialization for test cases.
+ */
+ private class TestCaseInserter extends TestItemInserter<TestCase> {
+
+ @Override
+ protected boolean isRequiredTestItemType(TestItem item) {
+ return (item instanceof TestCase);
+ }
+
+ @Override
+ protected TestCase createTestItem(String name, TestSuite parent) {
+ return new TestCase(name, parent);
+ }
+
+ @Override
+ protected void addNewTestItem(TestCase testCase) {
+ currentTestCase = testCase;
+ testCase.setStatus(ITestItem.Status.Skipped);
+
+ // Notify listeners
+ for (ITestingSessionListener listener : getListenersCopy()) {
+ listener.enterTestCase(testCase);
+ }
+ }
+ }
+
+
+ public TestModelManager(ITestSuite previousTestsHierarchy, boolean timeMeasurement) {
+ testSuitesStack.push(new TestSuite(ROOT_TEST_SUITE_NAME, null));
+ if (previousTestsHierarchy != null) {
+ // Copy tests hierarchy
+ this.timeMeasurement = false;
+ previousTestsHierarchy.visit(new HierarchyCopier());
+ }
+ this.timeMeasurement = timeMeasurement;
+ this.testSuitesIndex.clear();
+ }
+
+ /**
+ * Notifies the listeners that testing was started.
+ */
+ public void testingStarted() {
+ // Notify listeners
+ for (ITestingSessionListener listener : getListenersCopy()) {
+ listener.testingStarted();
+ }
+ }
+
+ /**
+ * Removes not visited test items and notifies the listeners that testing
+ * was finished.
+ */
+ public void testingFinished() {
+ // Remove all NotRun-tests and not used test suites (probably they were removed from test module)
+ getRootSuite().visit(new IModelVisitor() {
+
+ @Override
+ public void visit(ITestSuite testSuite) {
+ List<TestItem> suiteChildren = ((TestSuite)testSuite).getChildrenList();
+ for (Iterator<TestItem> it = suiteChildren.iterator(); it.hasNext();) {
+ TestItem item = it.next();
+ if ((item instanceof ITestSuite && !testSuitesIndex.containsKey(item)) ||
+ (item instanceof ITestCase && item.getStatus() == ITestItem.Status.NotRun)) {
+ it.remove();
+ }
+ }
+ }
+
+ @Override
+ public void visit(ITestMessage testMessage) {}
+ @Override
+ public void visit(ITestCase testCase) {}
+ @Override
+ public void leave(ITestSuite testSuite) {}
+ @Override
+ public void leave(ITestCase testCase) {}
+ @Override
+ public void leave(ITestMessage testMessage) {}
+ });
+ testSuitesIndex.clear();
+
+ // Notify listeners
+ for (ITestingSessionListener listener : getListenersCopy()) {
+ listener.testingFinished();
+ }
+ }
+
+ @Override
+ public void enterTestSuite(String name) {
+ testSuiteInserter.insert(name);
+ }
+
+ @Override
+ public void exitTestSuite() {
+ exitTestCase();
+ TestSuite testSuite = testSuitesStack.pop();
+ // Notify listeners
+ for (ITestingSessionListener listener : getListenersCopy()) {
+ listener.exitTestSuite(testSuite);
+ }
+ }
+
+ @Override
+ public void enterTestCase(String name) {
+ testCaseInserter.insert(name);
+ if (timeMeasurement) {
+ testCaseStartTime = System.currentTimeMillis();
+ }
+ }
+
+
+ @Override
+ public void setTestStatus(Status status) {
+ currentTestCase.setStatus(status);
+ }
+
+ @Override
+ public void setTestingTime(int testingTime) {
+ currentTestCase.setTestingTime(testingTime);
+ }
+
+ @Override
+ public void exitTestCase() {
+ if (currentTestCase != null) {
+ // Set test execution time (if time measurement is turned on)
+ if (timeMeasurement) {
+ int testingTime = (int)(System.currentTimeMillis()-testCaseStartTime);
+ currentTestCase.setTestingTime(currentTestCase.getTestingTime()+testingTime);
+ testCaseStartTime = 0;
+ }
+ TestCase testCase = currentTestCase;
+ currentTestCase = null;
+ // Notify listeners
+ for (ITestingSessionListener listener : getListenersCopy()) {
+ listener.exitTestCase(testCase);
+ }
+ }
+ }
+
+ @Override
+ public void addTestMessage(String file, int line, Level level, String text) {
+ TestLocation location = (file == null || file.isEmpty() || line <= 0) ? null : new TestLocation(file, line);
+ currentTestCase.addTestMessage(new TestMessage(location, level, text));
+ }
+
+ @Override
+ public ITestSuite currentTestSuite() {
+ return testSuitesStack.peek();
+ }
+
+
+ @Override
+ public ITestCase currentTestCase() {
+ return currentTestCase;
+ }
+
+ @Override
+ public boolean isCurrentlyRunning(ITestItem item) {
+ return (item == currentTestCase && item != null) || testSuitesStack.contains(item);
+ }
+
+ @Override
+ public TestSuite getRootSuite() {
+ return testSuitesStack.firstElement();
+ }
+
+ @Override
+ public void addChangesListener(ITestingSessionListener listener) {
+ synchronized (listeners) {
+ listeners.add(listener);
+ }
+ }
+
+ @Override
+ public void removeChangesListener(ITestingSessionListener listener) {
+ synchronized (listeners) {
+ listeners.remove(listener);
+ }
+ }
+
+ /**
+ * Copies listeners before notifying them to avoid dead-locks.
+ *
+ * @return listeners collection copy
+ */
+ private ITestingSessionListener[] getListenersCopy() {
+ synchronized (listeners) {
+ return listeners.toArray(new ITestingSessionListener[listeners.size()]);
+ }
+ }
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestSuite.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestSuite.java
new file mode 100644
index 0000000000..188f814270
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestSuite.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * 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.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.cdt.testsrunner.model.IModelVisitor;
+import org.eclipse.cdt.testsrunner.model.ITestSuite;
+
+/**
+ * Represents the test suite of the tests hierarchy.
+ */
+public class TestSuite extends TestItem implements ITestSuite {
+
+ /**
+ * Stores child test suites and test cases.
+ *
+ * @note Children order is important.
+ */
+ private List<TestItem> children = new ArrayList<TestItem>();
+
+
+ public TestSuite(String name, TestSuite parent) {
+ super(name, parent);
+ }
+
+ @Override
+ public Status getStatus() {
+ Status result = Status.NotRun;
+ for (TestItem testItem : children) {
+ Status childStatus = testItem.getStatus();
+ if (result.compareTo(childStatus) < 0) {
+ result = childStatus;
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public int getTestingTime() {
+ int result = 0;
+ for (TestItem testItem : children) {
+ result += testItem.getTestingTime();
+ }
+ return result;
+ }
+
+ @Override
+ public boolean hasChildren() {
+ return !children.isEmpty();
+ }
+
+ @Override
+ public TestItem[] getChildren() {
+ return children.toArray(new TestItem[children.size()]);
+ }
+
+ @Override
+ public void visit(IModelVisitor visitor) {
+ visitor.visit(this);
+ for (TestItem testItem : children) {
+ testItem.visit(visitor);
+ }
+ visitor.leave(this);
+ }
+
+ /**
+ * Returns list of children for the test suite.
+ *
+ * @return children list
+ */
+ public List<TestItem> getChildrenList() {
+ return children;
+ }
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestingSession.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestingSession.java
new file mode 100644
index 0000000000..f706fa302b
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestingSession.java
@@ -0,0 +1,280 @@
+/*******************************************************************************
+ * 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.model;
+
+import java.io.InputStream;
+import java.text.DateFormat;
+import java.text.MessageFormat;
+import java.util.Date;
+import java.util.EnumMap;
+import java.util.Map;
+
+import org.eclipse.cdt.testsrunner.internal.TestsRunnerPlugin;
+import org.eclipse.cdt.testsrunner.internal.launcher.TestsRunnerProviderInfo;
+import org.eclipse.cdt.testsrunner.launcher.ITestsRunnerProvider;
+import org.eclipse.cdt.testsrunner.model.IModelVisitor;
+import org.eclipse.cdt.testsrunner.model.ITestCase;
+import org.eclipse.cdt.testsrunner.model.ITestItem;
+import org.eclipse.cdt.testsrunner.model.ITestItem.Status;
+import org.eclipse.cdt.testsrunner.model.ITestMessage;
+import org.eclipse.cdt.testsrunner.model.ITestModelAccessor;
+import org.eclipse.cdt.testsrunner.model.ITestSuite;
+import org.eclipse.cdt.testsrunner.model.ITestingSession;
+import org.eclipse.cdt.testsrunner.model.ITestingSessionListener;
+import org.eclipse.cdt.testsrunner.model.TestingException;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.ILaunch;
+
+/**
+ * Stores the information about tests running.
+ */
+public class TestingSession implements ITestingSession {
+
+ /** Launch object the is connected to the tests running. */
+ private ILaunch launch;
+
+ /** Information about used Tests Runner provider plug-in. */
+ private TestsRunnerProviderInfo testsRunnerProviderInfo;
+
+ /** Main interface to Tests Runner provider plug-in. */
+ private ITestsRunnerProvider testsRunnerProvider;
+
+ /**
+ * Test Model manager that is used to fill and update the model for the
+ * session.
+ */
+ private TestModelManager modelManager;
+
+ /**
+ * Total tests counter. It is -1 by default, that means that total tests
+ * count is not available.
+ *
+ * @see getTotalCounter()
+ */
+ private int totalCounter = -1;
+
+ /** Already finished tests counter. */
+ private int currentCounter = 0;
+
+ /**
+ * Test counters map by test status. They are used to quickly provide simple
+ * statistics without model scanning.
+ *
+ */
+ private Map<ITestItem.Status, Integer> statusCounters = new EnumMap<ITestItem.Status, Integer>(ITestItem.Status.class);
+
+ /**
+ * The flag stores whether the testing session contains errors at the
+ * moment.
+ *
+ * @see hasErrors()
+ */
+ private boolean hasErrors = false;
+
+ /**
+ * The flag stores whether the testing session was stopped by user.
+ *
+ * @see wasStopped()
+ */
+ private boolean wasStopped = false;
+
+ /**
+ * The flag stores whether the testing session has been finished (with or
+ * without errors).
+ */
+ private boolean finished = false;
+
+ /** Stores current status of the testing session. */
+ private String statusMessage = ModelMessages.TestingSession_starting_status;
+
+ /** Stores the time when the testing session was created. */
+ private long startTime;
+
+
+ /**
+ * Counts the number of the test cases in tests hierarchy.
+ */
+ private class TestCasesCounter implements IModelVisitor {
+
+ public int result = 0;
+
+ @Override
+ public void visit(ITestCase testCase) {
+ ++result;
+ }
+
+ @Override
+ public void visit(ITestSuite testSuite) {}
+ @Override
+ public void visit(ITestMessage testMessage) {}
+ @Override
+ public void leave(ITestSuite testSuite) {}
+ @Override
+ public void leave(ITestCase testCase) {}
+ @Override
+ public void leave(ITestMessage testMessage) {}
+ }
+
+
+ /**
+ * The constructor.
+ *
+ * @param launch connected launch object
+ * @param testsRunnerProviderInfo the information about the tests runner
+ * @param previousSession is used to determine total tests count & for tests
+ * hierarchy reusing if it is considered as similar
+ */
+ public TestingSession(ILaunch launch, TestsRunnerProviderInfo testsRunnerProviderInfo, TestingSession previousSession) {
+ this.launch = launch;
+ this.testsRunnerProviderInfo = testsRunnerProviderInfo;
+ this.testsRunnerProvider = testsRunnerProviderInfo.instantiateTestsRunnerProvider();
+ this.startTime = System.currentTimeMillis();
+ // Calculate approximate tests count by the previous similar testing session (if available)
+ if (previousSession != null) {
+ TestCasesCounter testCasesCounter = new TestCasesCounter();
+ previousSession.getModelAccessor().getRootSuite().visit(testCasesCounter);
+ totalCounter = testCasesCounter.result;
+ }
+ ITestSuite rootTestSuite = previousSession != null ? previousSession.getModelAccessor().getRootSuite() : null;
+ this.modelManager = new TestModelManager(rootTestSuite, testsRunnerProviderInfo.isAllowedTestingTimeMeasurement());
+ this.modelManager.addChangesListener(new ITestingSessionListener() {
+
+ @Override
+ public void testingStarted() {}
+
+ @Override
+ public void testingFinished() {
+ // This is necessary if totalCounter was -1 (tests count was unknown)
+ // or if tests count was estimated not accurately
+ totalCounter = currentCounter;
+ }
+
+ @Override
+ public void exitTestSuite(ITestSuite testSuite) {}
+
+ @Override
+ public void exitTestCase(ITestCase testCase) {
+ // Update testing session info (counters, flags)
+ Status testStatus = testCase.getStatus();
+ statusCounters.put(testStatus, getCount(testStatus)+1);
+ ++currentCounter;
+ if (testStatus.isError())
+ hasErrors = true;
+ }
+
+ @Override
+ public void enterTestSuite(ITestSuite testSuite) {}
+
+ @Override
+ public void enterTestCase(ITestCase testCase) {}
+
+ @Override
+ public void childrenUpdate(ITestSuite parent) {}
+ });
+ }
+
+ /**
+ * Starts the processing of the test module output.
+ *
+ * @param inputStream test module output stream
+ */
+ public void run(InputStream inputStream) {
+ modelManager.testingStarted();
+ try {
+ testsRunnerProvider.run(modelManager, inputStream);
+ // If testing session was stopped, the status is set in stop()
+ if (!wasStopped()) {
+ double testingTime = getModelAccessor().getRootSuite().getTestingTime();
+ statusMessage = MessageFormat.format(ModelMessages.TestingSession_finished_status, testingTime/1000.0);
+ }
+ } catch (TestingException e) {
+ // If testing session was stopped, the status is set in stop()
+ if (!wasStopped()) {
+ statusMessage = e.getLocalizedMessage();
+ hasErrors = true;
+ }
+ }
+ finished = true;
+ modelManager.testingFinished();
+ }
+
+ @Override
+ public int getCurrentCounter() {
+ return currentCounter;
+ }
+
+ @Override
+ public int getTotalCounter() {
+ return totalCounter;
+ }
+
+ @Override
+ public int getCount(ITestItem.Status status) {
+ Integer counterValue = statusCounters.get(status);
+ return (counterValue == null) ? 0 : counterValue;
+ }
+
+ @Override
+ public boolean hasErrors() {
+ return hasErrors;
+ }
+
+ @Override
+ public boolean wasStopped() {
+ return wasStopped;
+ }
+
+ @Override
+ public boolean isFinished() {
+ return finished;
+ }
+
+ @Override
+ public ITestModelAccessor getModelAccessor() {
+ return modelManager;
+ }
+
+ @Override
+ public ILaunch getLaunch() {
+ return launch;
+ }
+
+ @Override
+ public TestsRunnerProviderInfo getTestsRunnerProviderInfo() {
+ return testsRunnerProviderInfo;
+ }
+
+ @Override
+ public String getStatusMessage() {
+ return statusMessage;
+ }
+
+ @Override
+ public String getName() {
+ String launchConfName = launch.getLaunchConfiguration().getName();
+ String startTimeStr = DateFormat.getDateTimeInstance().format(new Date(startTime));
+ return MessageFormat.format(ModelMessages.TestingSession_name_format, launchConfName, startTimeStr);
+ }
+
+ @Override
+ public void stop() {
+ if (!launch.isTerminated() && launch.canTerminate()) {
+ try {
+ launch.terminate();
+ wasStopped = true;
+ statusMessage = ModelMessages.TestingSession_stopped_status;
+ } catch (DebugException e) {
+ TestsRunnerPlugin.log(e);
+ }
+ }
+ }
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestingSessionsManager.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestingSessionsManager.java
new file mode 100644
index 0000000000..66bf252c6b
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/model/TestingSessionsManager.java
@@ -0,0 +1,215 @@
+/*******************************************************************************
+ * 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.model;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.cdt.testsrunner.internal.launcher.TestsRunnerProvidersManager;
+import org.eclipse.cdt.testsrunner.internal.launcher.TestsRunnerProviderInfo;
+import org.eclipse.cdt.testsrunner.model.ITestingSession;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+
+/**
+ * Manages all the testing sessions (creates, activates, stores history).
+ */
+public class TestingSessionsManager {
+
+ /** Tests Runners Plug-ins Manager. */
+ private TestsRunnerProvidersManager testsRunnersManager;
+
+ /** Testing sessions history list (the first is the newest). */
+ private LinkedList<TestingSession> sessions = new LinkedList<TestingSession>();
+
+ /** Currently active testing session. */
+ private TestingSession activeSession;
+
+ /** Listeners collection. */
+ private List<ITestingSessionsManagerListener> listeners = new ArrayList<ITestingSessionsManagerListener>();
+
+ /** The size limit of the testing sessions history. */
+ private int historySizeLimit = 10;
+
+ public TestingSessionsManager(TestsRunnerProvidersManager testsRunnersManager) {
+ this.testsRunnersManager = testsRunnersManager;
+ }
+
+ /**
+ * Tries to find the last testing session for the specified launch
+ * configuration and Tests Runner provide plug-in.
+ *
+ * <p>
+ * Usually testing frameworks do not provide the information about tests
+ * hierarchy and total tests count before the testing is finished. So we try
+ * to reuse them from one the previous testing sessions that meets the
+ * requirements:
+ * <ul>
+ * <li>it should be for the same launch configuration;
+ * <li>it should be completed (finished and not stopped);
+ * <li>it should has the same tests runner;
+ * </ul>
+ * This function tries to find a such session.
+ * </p>
+ *
+ * @param launchConfiguration required launch configuration
+ * @param testsRunnerProviderInfo required Tests Runner provide plug-in
+ * @return testing session or null if not found
+ */
+ private TestingSession findActualPreviousSession(ILaunchConfiguration launchConfiguration, TestsRunnerProviderInfo testsRunnerProviderInfo) {
+ String testsRunnerName = testsRunnerProviderInfo.getName();
+ for (TestingSession session : sessions) {
+ // Find the latest testing session that matches the next requirements:
+ // - it should be for the same launch configuration (should have the same parameters)
+ // - should be already terminated (to have complete tests hierarchy structure)
+ // - should not be stopped by user (the same as terminated)
+ // - should have the same tests runner
+ if (session != null) {
+ if (launchConfiguration.equals(session.getLaunch().getLaunchConfiguration())
+ && session.isFinished()
+ && !session.wasStopped()
+ && session.getTestsRunnerProviderInfo().getName().equals(testsRunnerName)) {
+ return session;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Creates a new testing session for the specified launch.
+ *
+ * @param launch launch
+ * @return new testing session
+ */
+ public TestingSession newSession(ILaunch launch) throws CoreException {
+ TestsRunnerProviderInfo testsRunnerProviderInfo = testsRunnersManager.getTestsRunnerProviderInfo(launch.getLaunchConfiguration());
+ TestingSession previousSession = findActualPreviousSession(launch.getLaunchConfiguration(), testsRunnerProviderInfo);
+ TestingSession newTestingSession = new TestingSession(launch, testsRunnerProviderInfo, previousSession);
+ sessions.addFirst(newTestingSession);
+ setActiveSession(newTestingSession);
+ truncateHistory();
+ return newTestingSession;
+ }
+
+ /**
+ * Returns the testing sessions history (the first is the newest).
+ *
+ * @return testing sessions list
+ */
+ public List<? extends ITestingSession> getSessions() {
+ return sessions;
+ }
+
+ /**
+ * Rewrites the testing sessions history with the specified list. Truncates
+ * the history if necessary.
+ *
+ * @return testing sessions list
+ */
+ public void setSessions(List<ITestingSession> newSessions) {
+ sessions.clear();
+ for (ITestingSession newSession : newSessions) {
+ sessions.add((TestingSession) newSession);
+ }
+ truncateHistory();
+ }
+
+ /**
+ * Returns the testing sessions history size.
+ *
+ * @return history size
+ */
+ public int getSessionsCount() {
+ return sessions.size();
+ }
+
+ /**
+ * Accesses the currently active testing session.
+ *
+ * @return testing session
+ */
+ public ITestingSession getActiveSession() {
+ return activeSession;
+ }
+
+ /**
+ * Sets the new active testing session.
+ *
+ * @param newActiveSession testing session
+ */
+ public void setActiveSession(ITestingSession newActiveSession) {
+ if (activeSession != newActiveSession) {
+ activeSession = (TestingSession) newActiveSession;
+ // Notify listeners
+ for (ITestingSessionsManagerListener listener : listeners) {
+ listener.sessionActivated(activeSession);
+ }
+ }
+ }
+
+ /**
+ * Adds the given listener to this registered listeners collection.
+ * Has no effect if an identical listener is already registered.
+ *
+ * @param listener the listener to add
+ */
+ public void addListener(ITestingSessionsManagerListener listener) {
+ listeners.add(listener);
+ }
+
+ /**
+ * Removes the given listener from registered listeners collection.
+ * Has no effect if the listener is not already registered.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeListener(ITestingSessionsManagerListener listener) {
+ listeners.remove(listener);
+ }
+
+ /**
+ * Returns the size limit of the testing sessions history.
+ *
+ * @return history size limit
+ */
+ public int getHistorySizeLimit() {
+ return historySizeLimit;
+ }
+
+ /**
+ * Sets the size limit of the testing sessions history.
+ *
+ * @param historySizeLimit new history size limit
+ */
+ public void setHistorySizeLimit(int historySizeLimit) {
+ this.historySizeLimit = historySizeLimit;
+ truncateHistory();
+ }
+
+ /**
+ * Truncates the history list if it is longer than size limit.
+ */
+ private void truncateHistory() {
+ // The most frequently this method will be used to remove one element, so removeAll() is unnecessary here
+ while (sessions.size() > historySizeLimit) {
+ sessions.removeLast();
+ }
+ // Handle the case when active testing session was removed from history due to truncation
+ if (!sessions.contains(activeSession)) {
+ ITestingSession newActiveSession = sessions.isEmpty() ? null : sessions.getFirst();
+ setActiveSession(newActiveSession);
+ }
+ }
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/launcher/CTestingTab.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/launcher/CTestingTab.java
new file mode 100644
index 0000000000..bb1d058a4e
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/launcher/CTestingTab.java
@@ -0,0 +1,200 @@
+/*******************************************************************************
+ * 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.launcher;
+
+import org.eclipse.cdt.launch.ui.CLaunchConfigurationTab;
+import org.eclipse.cdt.testsrunner.internal.TestsRunnerPlugin;
+import org.eclipse.cdt.testsrunner.internal.launcher.ITestsLaunchConfigurationConstants;
+import org.eclipse.cdt.testsrunner.internal.launcher.TestsRunnerProviderInfo;
+import org.eclipse.cdt.testsrunner.launcher.ITestsRunnerProviderInfo;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+
+
+/**
+ * A launch configuration tab that displays and edits different testing options
+ * (e.g. Tests Runner provider plug-in).
+ * <p>
+ * This class may be instantiated. This class is not intended to be subclassed.
+ * </p>
+ *
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class CTestingTab extends CLaunchConfigurationTab {
+
+ /**
+ * Tab identifier used for ordering of tabs added using the
+ * <code>org.eclipse.debug.ui.launchConfigurationTabs</code>
+ * extension point.
+ */
+ private static final String TAB_ID = "org.eclipse.cdt.testsrunner.testingTab"; //$NON-NLS-1$
+
+ private static final String TESTING_PROCESS_FACTORY_ID = "org.eclipse.cdt.testsrunner.TestingProcessFactory"; //$NON-NLS-1$
+
+ /** Shows the list of available Tests Runner provider plug-ins. */
+ private Combo testsRunnerProviderCombo;
+
+ /** Shows the description for the currently selected Tests Runner provider plug-in. */
+ private Label testsRunnerProviderDescriptionLabel;
+
+ @Override
+ public void createControl(Composite parent) {
+ Composite pageComposite = new Composite(parent, SWT.NONE);
+ GridLayout pageCompositeLayout = new GridLayout(2, false);
+ pageCompositeLayout.horizontalSpacing = 40;
+ pageComposite.setLayout(pageCompositeLayout);
+
+ // Create a tests runner selector
+ new Label(pageComposite, SWT.NONE).setText(UILauncherMessages.CTestingTab_tests_runner_label);
+ testsRunnerProviderCombo = new Combo(pageComposite, SWT.READ_ONLY | SWT.DROP_DOWN);
+ testsRunnerProviderCombo.add(UILauncherMessages.CTestingTab_tests_runner_is_not_set);
+ testsRunnerProviderCombo.setData("0", null); //$NON-NLS-1$
+
+ // Add all the tests runners
+ for (TestsRunnerProviderInfo testsRunnerProviderInfo : TestsRunnerPlugin.getDefault().getTestsRunnerProvidersManager().getTestsRunnersProviderInfo()) {
+ testsRunnerProviderCombo.setData(Integer.toString(testsRunnerProviderCombo.getItemCount()), testsRunnerProviderInfo);
+ testsRunnerProviderCombo.add(testsRunnerProviderInfo.getName());
+ }
+
+ testsRunnerProviderCombo.addModifyListener(new ModifyListener() {
+
+ @Override
+ public void modifyText(ModifyEvent e) {
+ testsRunnerProviderDescriptionLabel.setText(getCurrentTestsRunnerDescription());
+ updateLaunchConfigurationDialog();
+ }
+ });
+
+ // Create a tests runner description label
+ testsRunnerProviderDescriptionLabel = new Label(pageComposite, SWT.WRAP);
+ GridData testsRunnerProviderLabelGD = new GridData(GridData.FILL_BOTH);
+ testsRunnerProviderLabelGD.horizontalSpan = 2;
+ testsRunnerProviderLabelGD.horizontalAlignment = GridData.FILL;
+ testsRunnerProviderDescriptionLabel.setLayoutData(testsRunnerProviderLabelGD);
+
+ GridData pageCompositeGD = new GridData(GridData.FILL_BOTH);
+ pageCompositeGD.horizontalAlignment = GridData.FILL;
+ pageCompositeGD.grabExcessHorizontalSpace = true;
+ pageComposite.setLayoutData(pageCompositeGD);
+ setControl(pageComposite);
+ }
+
+ /**
+ * Returns the information for the currently selected Tests Runner provider
+ * plug-in.
+ *
+ * @return Tests Runner provide plug-in information
+ */
+ private ITestsRunnerProviderInfo getCurrentTestsRunnerProviderInfo() {
+ return getTestsRunnerProviderInfo(testsRunnerProviderCombo.getSelectionIndex());
+ }
+
+ /**
+ * Returns the information for the Tests Runner provide plug-in specified by
+ * index.
+ *
+ * @param comboIndex index in combo widget
+ * @return Tests Runner provide plug-in information
+ */
+ private ITestsRunnerProviderInfo getTestsRunnerProviderInfo(int comboIndex) {
+ return (ITestsRunnerProviderInfo)testsRunnerProviderCombo.getData(Integer.toString(comboIndex));
+ }
+
+ /**
+ * Returns the description for the currently selected Tests Runner provide
+ * plug-in.
+ *
+ * @return the description
+ */
+ private String getCurrentTestsRunnerDescription() {
+ ITestsRunnerProviderInfo testsRunnerProvider = getCurrentTestsRunnerProviderInfo();
+ if (testsRunnerProvider != null) {
+ return testsRunnerProvider.getDescription();
+ } else {
+ return UILauncherMessages.CTestingTab_no_tests_runner_label;
+ }
+ }
+
+ @Override
+ public boolean isValid(ILaunchConfiguration config) {
+ return getCurrentTestsRunnerProviderInfo() != null;
+ }
+
+ @Override
+ public void setDefaults(ILaunchConfigurationWorkingCopy config) {
+ config.setAttribute(ITestsLaunchConfigurationConstants.ATTR_TESTS_RUNNER, (String) null);
+ config.setAttribute(DebugPlugin.ATTR_PROCESS_FACTORY_ID, TESTING_PROCESS_FACTORY_ID);
+ }
+
+ @Override
+ public void initializeFrom(ILaunchConfiguration configuration) {
+ try {
+ String testsRunnerId = configuration.getAttribute(ITestsLaunchConfigurationConstants.ATTR_TESTS_RUNNER, (String) null);
+ int comboIndex = 0;
+ for (int i = 1; i < testsRunnerProviderCombo.getItemCount(); i++) {
+ if (getTestsRunnerProviderInfo(i).getId().equals(testsRunnerId)) {
+ comboIndex = i;
+ break;
+ }
+ }
+ testsRunnerProviderCombo.select(comboIndex);
+
+ } catch (CoreException e) {
+ TestsRunnerPlugin.log(e);
+ }
+ }
+
+ @Override
+ public void performApply(ILaunchConfigurationWorkingCopy configuration) {
+ ITestsRunnerProviderInfo testsRunnerProvider = getCurrentTestsRunnerProviderInfo();
+ String testsRunnerProviderId = testsRunnerProvider != null ? testsRunnerProvider.getId() : null;
+ configuration.setAttribute(ITestsLaunchConfigurationConstants.ATTR_TESTS_RUNNER, testsRunnerProviderId);
+ configuration.setAttribute(DebugPlugin.ATTR_PROCESS_FACTORY_ID, TESTING_PROCESS_FACTORY_ID);
+ }
+
+ @Override
+ public String getId() {
+ return TAB_ID;
+ }
+
+ @Override
+ public String getName() {
+ return UILauncherMessages.CTestingTab_tab_name;
+ }
+
+ @Override
+ public String getErrorMessage() {
+ String m = super.getErrorMessage();
+ if (m == null) {
+ if (getCurrentTestsRunnerProviderInfo() == null) {
+ return UILauncherMessages.CTestingTab_no_tests_runner_error;
+ }
+ }
+ return m;
+ }
+
+ @Override
+ public Image getImage() {
+ return TestsRunnerPlugin.createAutoImage("obj16/test_notrun.gif"); //$NON-NLS-1$
+ }
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/launcher/EmptyConfigurationTabGroup.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/launcher/EmptyConfigurationTabGroup.java
new file mode 100644
index 0000000000..77a9d44cd2
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/launcher/EmptyConfigurationTabGroup.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * 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.launcher;
+
+import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup;
+import org.eclipse.debug.ui.ILaunchConfigurationDialog;
+import org.eclipse.debug.ui.ILaunchConfigurationTab;
+
+
+/**
+ * Represents an empty tab group. Actual tabs are added via the
+ * <code>org.eclipse.debug.ui.launchConfigurationTabs</code> extension point.
+ */
+public class EmptyConfigurationTabGroup extends AbstractLaunchConfigurationTabGroup {
+
+ @Override
+ public void createTabs(ILaunchConfigurationDialog dialog, String mode) {
+ setTabs(new ILaunchConfigurationTab[0]);
+ }
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/launcher/UILauncherMessages.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/launcher/UILauncherMessages.java
new file mode 100644
index 0000000000..8e75b59f54
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/launcher/UILauncherMessages.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * 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.launcher;
+
+import org.eclipse.osgi.util.NLS;
+
+public class UILauncherMessages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.cdt.testsrunner.internal.ui.launcher.UILauncherMessages"; //$NON-NLS-1$
+ public static String CTestingTab_no_tests_runner_error;
+ public static String CTestingTab_no_tests_runner_label;
+ public static String CTestingTab_tab_name;
+ public static String CTestingTab_tests_runner_is_not_set;
+ public static String CTestingTab_tests_runner_label;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, UILauncherMessages.class);
+ }
+
+ private UILauncherMessages() {
+ }
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/launcher/UILauncherMessages.properties b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/launcher/UILauncherMessages.properties
new file mode 100644
index 0000000000..752755e5f7
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/launcher/UILauncherMessages.properties
@@ -0,0 +1,15 @@
+###############################################################################
+# 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 implementation
+###############################################################################
+CTestingTab_no_tests_runner_error=Tests runner is not selected
+CTestingTab_no_tests_runner_label=Select a tests runner...
+CTestingTab_tab_name=C/C++ Testing
+CTestingTab_tests_runner_is_not_set=<not set>
+CTestingTab_tests_runner_label=Tests Runner
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/CounterPanel.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/CounterPanel.java
new file mode 100644
index 0000000000..4f3f8d910e
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/CounterPanel.java
@@ -0,0 +1,147 @@
+/*******************************************************************************
+ * 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 org.eclipse.cdt.testsrunner.internal.TestsRunnerPlugin;
+import org.eclipse.cdt.testsrunner.model.ITestItem;
+import org.eclipse.cdt.testsrunner.model.ITestingSession;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import java.text.MessageFormat;
+
+
+/**
+ * Shows a simple tests count statics information (run/error/failed).
+ */
+public class CounterPanel extends Composite {
+
+ /** Testing session to show statistics for. */
+ private ITestingSession testingSession;
+
+ /** Widget showing the failed tests count. */
+ private Label failedCounterLabel;
+
+ /** Widget showing the error tests count. */
+ private Label abortedCounterLabel;
+
+ /** Widget showing the run tests count. */
+ private Label currentCounterLabel;
+
+ /**
+ * Shows whether there were skipped tests. It is used to force layout of the
+ * counter widgets after skipped tests are appeared.
+ */
+ private boolean hasSkipped;
+
+ private final Image errorIcon = TestsRunnerPlugin.createAutoImage("ovr16/failed_counter.gif"); //$NON-NLS-1$
+ private final Image failureIcon = TestsRunnerPlugin.createAutoImage("ovr16/aborted_counter.gif"); //$NON-NLS-1$
+
+
+ public CounterPanel(Composite parent, ITestingSession testingSession) {
+ super(parent, SWT.WRAP);
+ GridLayout gridLayout = new GridLayout();
+ gridLayout.numColumns = 9;
+ gridLayout.makeColumnsEqualWidth = false;
+ gridLayout.marginWidth = 0;
+ setLayout(gridLayout);
+
+ currentCounterLabel = createLabel(UIViewMessages.CounterPanel_tests_run, null);
+ abortedCounterLabel = createLabel(UIViewMessages.CounterPanel_tests_erred, errorIcon);
+ failedCounterLabel = createLabel(UIViewMessages.CounterPanel_tests_failed, failureIcon);
+ setTestingSession(testingSession);
+ }
+
+ /**
+ * Creates counter label widget.
+ *
+ * @param name widget text prefix
+ * @param image widget image or <code>null</code>
+ * @return created label
+ */
+ private Label createLabel(String name, Image image) {
+ Label label = new Label(this, SWT.NONE);
+ if (image != null) {
+ image.setBackground(label.getBackground());
+ label.setImage(image);
+ }
+ label.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING));
+
+ label = new Label(this, SWT.NONE);
+ label.setText(name);
+ label.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING));
+
+ Label value = new Label(this, SWT.READ_ONLY);
+ value.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.HORIZONTAL_ALIGN_BEGINNING));
+ return value;
+ }
+
+ /**
+ * Sets the testing session to show information about.
+ *
+ * @param testingSession testing session (null is not acceptable)
+ */
+ public void setTestingSession(ITestingSession testingSession) {
+ this.testingSession = testingSession;
+ this.hasSkipped = testingSession.getCount(ITestItem.Status.Skipped) != 0;
+ updateInfoFromSession();
+ }
+
+ /**
+ * Updates the information on the panel from the currently set testing
+ * session.
+ */
+ public void updateInfoFromSession() {
+ setFailedCounter(testingSession.getCount(ITestItem.Status.Failed));
+ setAbortedCounter(testingSession.getCount(ITestItem.Status.Aborted));
+ setCurrentCounter(testingSession.getCurrentCounter(), testingSession.getCount(ITestItem.Status.Skipped));
+ redraw();
+ }
+
+ /**
+ * Sets a new value for the failed tests counter.
+ *
+ * @param newValue new counter value
+ */
+ private void setFailedCounter(int newValue) {
+ failedCounterLabel.setText(Integer.toString(newValue));
+ }
+
+ /**
+ * Sets a new value for the error tests counter.
+ *
+ * @param newValue new counter value
+ */
+ private void setAbortedCounter(int newValue) {
+ abortedCounterLabel.setText(Integer.toString(newValue));
+ }
+
+ /**
+ * Sets a new value for the run tests counter.
+ *
+ * @param currentValue new counter value
+ * @param skippedValue skipped tests counter
+ */
+ private void setCurrentCounter(int currentValue, int skippedValue) {
+ if (!hasSkipped && skippedValue != 0) {
+ layout();
+ }
+ String runString = (skippedValue == 0)
+ ? Integer.toString(currentValue)
+ : MessageFormat.format(UIViewMessages.CounterPanel_tests_skipped, currentValue, skippedValue);
+ currentCounterLabel.setText(runString);
+ }
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/DummyUISession.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/DummyUISession.java
new file mode 100644
index 0000000000..9787a5bb5b
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/DummyUISession.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * 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 org.eclipse.cdt.testsrunner.launcher.ITestsRunnerProviderInfo;
+import org.eclipse.cdt.testsrunner.model.ITestItem;
+import org.eclipse.cdt.testsrunner.model.ITestModelAccessor;
+import org.eclipse.cdt.testsrunner.model.ITestingSession;
+import org.eclipse.debug.core.ILaunch;
+
+/**
+ * Represents a simple testing session which is used for UI when there is no
+ * "real" testing sessions to show (e.g. when there was no launched testing
+ * session or when all of them were cleared).
+ */
+public class DummyUISession implements ITestingSession {
+
+ @Override
+ public int getCurrentCounter() {
+ return 0;
+ }
+
+ @Override
+ public int getTotalCounter() {
+ return 0;
+ }
+
+ @Override
+ public int getCount(ITestItem.Status status) {
+ return 0;
+ }
+
+ @Override
+ public boolean hasErrors() {
+ return false;
+ }
+
+ @Override
+ public boolean wasStopped() {
+ return false;
+ }
+
+ @Override
+ public boolean isFinished() {
+ return false;
+ }
+
+ @Override
+ public ITestModelAccessor getModelAccessor() {
+ return null;
+ }
+
+ @Override
+ public ILaunch getLaunch() {
+ return null;
+ }
+
+ @Override
+ public ITestsRunnerProviderInfo getTestsRunnerProviderInfo() {
+ return null;
+ }
+
+ @Override
+ public String getStatusMessage() {
+ return ""; //$NON-NLS-1$
+ }
+
+ @Override
+ public String getName() {
+ return "<dummy>"; //$NON-NLS-1$
+ }
+
+ @Override
+ public void stop() {
+ }
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/MessagesViewer.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/MessagesViewer.java
new file mode 100644
index 0000000000..db0aa21e9b
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/MessagesViewer.java
@@ -0,0 +1,573 @@
+/*******************************************************************************
+ * 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.io.File;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.eclipse.cdt.testsrunner.internal.model.TestingSessionsManager;
+import org.eclipse.cdt.testsrunner.internal.ui.view.actions.CopySelectedMessagesAction;
+import org.eclipse.cdt.testsrunner.internal.ui.view.actions.OpenInEditorAction;
+import org.eclipse.cdt.testsrunner.model.IModelVisitor;
+import org.eclipse.cdt.testsrunner.model.ITestCase;
+import org.eclipse.cdt.testsrunner.model.ITestItem;
+import org.eclipse.cdt.testsrunner.model.ITestLocation;
+import org.eclipse.cdt.testsrunner.model.ITestMessage;
+import org.eclipse.cdt.testsrunner.model.ITestMessage.Level;
+import org.eclipse.cdt.testsrunner.model.ITestSuite;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.viewers.IOpenListener;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.OpenEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.actions.ActionFactory;
+
+
+/**
+ * Shows the messages for the currently selected items in tests hierarchy (test
+ * suites or test cases).
+ */
+public class MessagesViewer {
+
+ /**
+ * Enumeration of all possible message filter actions by level.
+ */
+ public enum LevelFilter {
+ Info(ISharedImages.IMG_OBJS_INFO_TSK, ITestMessage.Level.Info, ITestMessage.Level.Message),
+ Warning(ISharedImages.IMG_OBJS_WARN_TSK, ITestMessage.Level.Warning),
+ Error(ISharedImages.IMG_OBJS_ERROR_TSK, ITestMessage.Level.Error, ITestMessage.Level.FatalError, ITestMessage.Level.Exception);
+
+ private String imageId;
+ private ITestMessage.Level [] includedLevels;
+
+ LevelFilter(String imageId, ITestMessage.Level... includedLevels) {
+ this.imageId = imageId;
+ this.includedLevels = includedLevels;
+ }
+
+ /**
+ * The shared image ID corresponding to the message level filter action.
+ *
+ * @return shared image ID
+ */
+ public String getImageId() {
+ return imageId;
+ }
+
+ /**
+ * The message levels that should be shown if current message level
+ * filter action is set.
+ *
+ * @return array of message levels
+ */
+ public ITestMessage.Level [] getLevels() {
+ return includedLevels;
+ }
+
+ /**
+ * Checks whether the specified message level should be shown if current
+ * message level filter action is set.
+ *
+ * @param messageLevel message level to search
+ * @return <code>true</code> if found
+ */
+ public boolean isIncluded(ITestMessage.Level messageLevel) {
+ for (ITestMessage.Level currLevel : includedLevels) {
+ if (currLevel.equals(messageLevel)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * The content provider for the test messages viewer.
+ */
+ private class MessagesContentProvider implements IStructuredContentProvider {
+
+ /**
+ * Utility class: recursively collects all the messages of the specified
+ * test item.
+ */
+ class MessagesCollector implements IModelVisitor {
+
+ /** Collected test messages. */
+ Collection<ITestMessage> collectedTestMessages;
+
+ /**
+ * Specifies whether gathering should be done. It is used to skip
+ * the messages of the passed tests if they should not be shown.
+ */
+ boolean collect = true;
+
+ MessagesCollector(Collection<ITestMessage> testMessages) {
+ this.collectedTestMessages = testMessages;
+ }
+
+ @Override
+ public void visit(ITestMessage testMessage) {
+ if (collect) {
+ collectedTestMessages.add(testMessage);
+ }
+ }
+
+ @Override
+ public void visit(ITestCase testCase) {
+ collect = !showFailedOnly || testCase.getStatus().isError();
+ }
+
+ @Override
+ public void visit(ITestSuite testSuite) {}
+
+ @Override
+ public void leave(ITestSuite testSuite) {}
+
+ @Override
+ public void leave(ITestCase testCase) {}
+
+ @Override
+ public void leave(ITestMessage testMessage) {}
+ }
+
+ /** Test messages to show in the viewer. */
+ ITestMessage[] testMessages;
+
+ @Override
+ public void inputChanged(Viewer v, Object oldInput, Object newInput) {
+ if (newInput != null) {
+ collectMessages((ITestItem[]) newInput);
+ } else {
+ testMessages = new ITestMessage[0];
+ }
+ }
+
+ @Override
+ public void dispose() {
+ }
+
+ @Override
+ public Object[] getElements(Object parent) {
+ return testMessages;
+ }
+
+ /**
+ * Creates a messages set with a custom comparator. It is used for the
+ * ordered messages showing.
+ *
+ * @return set to store the test messages
+ */
+ private TreeSet<ITestMessage> createMessagesSet() {
+ return new TreeSet<ITestMessage>(new Comparator<ITestMessage>() {
+
+ @Override
+ public int compare(ITestMessage message1, ITestMessage message2) {
+ // Compare messages by location
+ ITestLocation location1 = message1.getLocation();
+ ITestLocation location2 = message2.getLocation();
+
+ if (location1 != null && location2 != null) {
+ // Compare by file name
+ String file1 = location1.getFile();
+ String file2 = location2.getFile();
+ int fileResult = file1.compareTo(file2);
+ if (fileResult != 0) {
+ return fileResult;
+ } else {
+ // Compare by line number
+ int line1 = location1.getLine();
+ int line2 = location2.getLine();
+ if (line1 < line2) {
+ return -1;
+
+ } else if (line1 > line2) {
+ return 1;
+ }
+ }
+
+ } else if (location1 == null && location2 != null) {
+ return -1;
+
+ } else if (location1 != null && location2 == null) {
+ return 1;
+ }
+
+ // Compare by message text
+ String text1 = message1.getText();
+ String text2 = message2.getText();
+ return text1.compareTo(text2);
+ }
+ });
+ }
+
+ /**
+ * Creates a list to store the test messages. It is used for the
+ * unordered messages showing.
+ *
+ * @return list to store the test messages
+ */
+ private ArrayList<ITestMessage> createMessagesList() {
+ return new ArrayList<ITestMessage>();
+ }
+
+ /**
+ * Creates a collection to store the test messages depending on whether
+ * ordering is required.
+ *
+ * @return collection to store the test messages
+ */
+ private Collection<ITestMessage> createMessagesCollection() {
+ return orderingMode ? createMessagesSet() : createMessagesList();
+ }
+
+ /**
+ * Run messages collecting for the specified test items.
+ *
+ * @param testItems test items array
+ */
+ private void collectMessages(ITestItem[] testItems) {
+ Collection<ITestMessage> testMessagesCollection = createMessagesCollection();
+ for (ITestItem testItem : testItems) {
+ testItem.visit(new MessagesCollector(testMessagesCollection));
+ }
+ testMessages = testMessagesCollection.toArray(new ITestMessage[testMessagesCollection.size()]);
+ }
+ }
+
+ /**
+ * The label provider for the test messages viewer.
+ */
+ private class MessagesLabelProvider extends LabelProvider implements ITableLabelProvider {
+
+ /**
+ * Returns the full (file path) or short (file name only) file path view
+ * depending on the filter set.
+ *
+ * @param location test object location
+ * @return file path
+ */
+ private String getLocationFile(ITestLocation location) {
+ String filePath = location.getFile();
+ if (showFileNameOnly) {
+ return new File(filePath).getName();
+ } else {
+ return filePath;
+ }
+ }
+
+ @Override
+ public String getColumnText(Object obj, int index) {
+ ITestMessage message = (ITestMessage)obj;
+ ITestLocation location = message.getLocation();
+ String locationString = ""; //$NON-NLS-1$
+ if (location != null) {
+ locationString = MessageFormat.format(
+ UIViewMessages.MessagesViewer_location_format,
+ new Object[] { getLocationFile(location), location.getLine() }
+ );
+ }
+ return MessageFormat.format(UIViewMessages.MessagesViewer_message_format,
+ locationString, message.getLevel(), message.getText()
+ );
+ }
+
+ @Override
+ public Image getColumnImage(Object obj, int index) {
+ return getImage(obj);
+ }
+
+ @Override
+ public Image getImage(Object obj) {
+ Level level = ((ITestMessage)obj).getLevel();
+ String imageId = ISharedImages.IMG_OBJ_ELEMENT;
+ for (LevelFilter levelFilter : LevelFilter.values()) {
+ if (levelFilter.isIncluded(level)) {
+ imageId = levelFilter.getImageId();
+ break;
+ }
+ }
+ return PlatformUI.getWorkbench().getSharedImages().getImage(imageId);
+ }
+ }
+
+ /**
+ * Filters the required test messages by level.
+ */
+ private class MessageLevelFilter extends ViewerFilter {
+
+ @Override
+ public boolean select(Viewer viewer, Object parentElement, Object element) {
+ return acceptedMessageLevels.contains(((ITestMessage)element).getLevel());
+ }
+ }
+
+
+ /** Main widget. */
+ private TableViewer tableViewer;
+
+ private IViewSite viewSite;
+
+ // Context menu actions
+ private OpenInEditorAction openInEditorAction;
+ private Action copyAction;
+
+ /** Specifies whether only messages for failed tests should be shown. */
+ private boolean showFailedOnly = false;
+
+ /**
+ * Specifies whether only file names should be shown (instead of full file
+ * paths).
+ */
+ private boolean showFileNameOnly = false;
+
+ /** The set of message level to show the messages with. */
+ private Set<ITestMessage.Level> acceptedMessageLevels = new HashSet<ITestMessage.Level>();
+
+ /** Specifies whether test messages ordering is on or off. */
+ private boolean orderingMode = false;
+
+
+ public MessagesViewer(Composite parent,
+ TestingSessionsManager sessionsManager, IWorkbench workbench,
+ IViewSite viewSite, Clipboard clipboard) {
+ this.viewSite = viewSite;
+ tableViewer = new TableViewer(parent, SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL);
+ tableViewer.setLabelProvider(new MessagesLabelProvider());
+ tableViewer.setContentProvider(new MessagesContentProvider());
+ tableViewer.addFilter(new MessageLevelFilter());
+ initContextMenu(viewSite, sessionsManager, workbench, clipboard);
+ tableViewer.addOpenListener(new IOpenListener() {
+ @Override
+ public void open(OpenEvent event) {
+ openInEditorAction.run();
+ }
+ });
+ }
+
+ /**
+ * Initializes the viewer context menu.
+ *
+ * @param viewSite view
+ * @param sessionsManager testing sessions manager
+ * @param workbench workbench
+ * @param clipboard clipboard
+ */
+ private void initContextMenu(IViewSite viewSite,
+ TestingSessionsManager sessionsManager, IWorkbench workbench,
+ Clipboard clipboard) {
+ openInEditorAction = new OpenInEditorAction(tableViewer, sessionsManager, workbench);
+ copyAction = new CopySelectedMessagesAction(tableViewer, clipboard);
+
+ MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$
+ menuMgr.addMenuListener(new IMenuListener() {
+ @Override
+ public void menuAboutToShow(IMenuManager manager) {
+ handleMenuAboutToShow(manager);
+ }
+ });
+ viewSite.registerContextMenu(menuMgr, tableViewer);
+ Menu menu = menuMgr.createContextMenu(tableViewer.getTable());
+ tableViewer.getTable().setMenu(menu);
+
+ menuMgr.add(openInEditorAction);
+ menuMgr.add(copyAction);
+ configureCopy();
+ }
+
+ /**
+ * Configures the view copy action which should be run on CTRL+C. We have to
+ * track widget focus to select the actual action because we have a few
+ * widgets that should provide copy action (at least tests hierarchy viewer
+ * and messages viewer).
+ */
+ private void configureCopy() {
+ getTableViewer().getTable().addFocusListener(new FocusListener() {
+ IAction viewCopyHandler;
+
+ @Override
+ public void focusLost(FocusEvent e) {
+ if (viewCopyHandler != null) {
+ switchTo(viewCopyHandler);
+ }
+ }
+
+ @Override
+ public void focusGained(FocusEvent e) {
+ switchTo(copyAction);
+ }
+
+ private void switchTo(IAction copyAction) {
+ IActionBars actionBars = viewSite.getActionBars();
+ viewCopyHandler = actionBars.getGlobalActionHandler(ActionFactory.COPY.getId());
+ actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(), copyAction);
+ actionBars.updateActionBars();
+ }
+ });
+ }
+
+ /**
+ * Handles the context menu showing.
+ *
+ * @param manager context menu manager
+ */
+ private void handleMenuAboutToShow(IMenuManager manager) {
+ ISelection selection = tableViewer.getSelection();
+ openInEditorAction.setEnabled(!selection.isEmpty());
+ copyAction.setEnabled(!selection.isEmpty());
+ }
+
+ /**
+ * Provides access to the main widget of the messages viewer.
+ *
+ * @return main widget of the messages viewer
+ */
+ public TableViewer getTableViewer() {
+ return tableViewer;
+ }
+
+ /**
+ * Sets the test items for which the messages should be shown.
+ *
+ * @param testItems test items array
+ */
+ public void showItemsMessages(ITestItem[] testItems) {
+ tableViewer.setInput(testItems);
+ }
+
+ /**
+ * Forces the messages recollecting. It is used after message filters
+ * change.
+ */
+ private void forceRecollectMessages() {
+ // NOTE: Set input again makes content provider to recollect messages (with filters applied)
+ tableViewer.setInput(tableViewer.getInput());
+ }
+
+ /**
+ * Returns whether the messages only for the failed tests should be shown.
+ *
+ * @return filter state
+ */
+ public boolean getShowFailedOnly() {
+ return showFailedOnly;
+ }
+
+ /**
+ * Sets whether the messages only for the failed tests should be shown.
+ *
+ * @param showFailedOnly new filter state
+ */
+ public void setShowFailedOnly(boolean showFailedOnly) {
+ if (this.showFailedOnly != showFailedOnly) {
+ this.showFailedOnly = showFailedOnly;
+ forceRecollectMessages();
+ }
+ }
+
+ /**
+ * Returns whether short or long view for file paths should be shown.
+ *
+ * @return filter state
+ */
+ public boolean getShowFileNameOnly() {
+ return showFileNameOnly;
+ }
+
+ /**
+ * Sets whether short or long view for file paths should be shown.
+ *
+ * @param showFileNameOnly new filter state
+ */
+ public void setShowFileNameOnly(boolean showFileNameOnly) {
+ if (this.showFileNameOnly != showFileNameOnly) {
+ this.showFileNameOnly = showFileNameOnly;
+ forceRecollectMessages();
+ }
+ }
+
+ /**
+ * Returns whether test messages should be ordered by location.
+ *
+ * @return messages ordering state
+ */
+ public boolean getOrderingMode() {
+ return orderingMode;
+ }
+
+ /**
+ * Sets whether test messages should be ordered by location.
+ *
+ * @param orderingMode new messages ordering state
+ */
+ public void setOrderingMode(boolean orderingMode) {
+ if (this.orderingMode != orderingMode) {
+ this.orderingMode = orderingMode;
+ forceRecollectMessages();
+ }
+ }
+
+ /**
+ * Adds the filter message level filters by the message filter action level.
+ *
+ * @param levelFilter message filter action level
+ * @param refresh specifies whether viewer should be refreshed after filter
+ * update (small optimization: avoid many updates on initialization)
+ */
+ public void addLevelFilter(LevelFilter levelFilter, boolean refresh) {
+ for (ITestMessage.Level level : levelFilter.getLevels()) {
+ acceptedMessageLevels.add(level);
+ }
+ if (refresh) {
+ tableViewer.refresh();
+ }
+ }
+
+ /**
+ * Removed the filter message level filters by the message filter action
+ * level.
+ *
+ * @param levelFilter message filter action level
+ */
+ public void removeLevelFilter(LevelFilter levelFilter) {
+ for (ITestMessage.Level level : levelFilter.getLevels()) {
+ acceptedMessageLevels.remove(level);
+ }
+ tableViewer.refresh();
+ }
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/ProgressBar.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/ProgressBar.java
new file mode 100644
index 0000000000..dc1c004420
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/ProgressBar.java
@@ -0,0 +1,183 @@
+/*******************************************************************************
+ * 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 org.eclipse.cdt.testsrunner.model.ITestingSession;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * A progress bar with a red/green indication for testing success or failure.
+ */
+public class ProgressBar extends Canvas {
+
+ /** Default bar width */
+ private static final int DEFAULT_WIDTH = 160;
+
+ /** Default bar height */
+ private static final int DEFAULT_HEIGHT = 18;
+
+ /** Testing session to show progress bar for. */
+ private ITestingSession testingSession;
+
+ /** Current bar width. */
+ private int colorBarWidth;
+
+ /** The bar color when everything is OK (no tests failed and no testing errors). */
+ private Color okColor;
+
+ /** The bar color when there are tests failed and or testing errors. */
+ private Color failureColor;
+
+ /** The bar color when the testing session was stopped by user. */
+ private Color stoppedColor;
+
+
+ public ProgressBar(Composite parent, ITestingSession testingSession) {
+ super(parent, SWT.NONE);
+
+ addControlListener(new ControlAdapter() {
+ @Override
+ public void controlResized(ControlEvent e) {
+ updateInfoFromSession();
+ }
+ });
+ addPaintListener(new PaintListener() {
+ @Override
+ public void paintControl(PaintEvent e) {
+ paint(e);
+ }
+ });
+
+ // Manage progress bar colors
+ Display display = parent.getDisplay();
+ failureColor = new Color(display, 159, 63, 63);
+ okColor = new Color(display, 95, 191, 95);
+ stoppedColor = new Color(display, 120, 120, 120);
+ addDisposeListener(new DisposeListener() {
+ @Override
+ public void widgetDisposed(DisposeEvent e) {
+ failureColor.dispose();
+ okColor.dispose();
+ stoppedColor.dispose();
+ }
+ });
+ setTestingSession(testingSession);
+ }
+
+ /**
+ * Sets the testing session to show information about.
+ *
+ * @param testingSession testing session (null is not acceptable)
+ */
+ public void setTestingSession(ITestingSession testingSession) {
+ this.testingSession = testingSession;
+ updateInfoFromSession();
+ }
+
+ /**
+ * Updates the progress from the currently set testing session.
+ */
+ public void updateInfoFromSession() {
+ recalculateColorBarWidth();
+ redraw();
+ }
+
+ /**
+ * Sets the color of the progress bar depending on the testing session.
+ *
+ * @param gc gc
+ */
+ private void setStatusColor(GC gc) {
+ if (testingSession.wasStopped())
+ gc.setBackground(stoppedColor);
+ else if (testingSession.hasErrors())
+ gc.setBackground(failureColor);
+ else
+ gc.setBackground(okColor);
+ }
+
+ /**
+ * Calculate the width of the progress rectangle in a widget.
+ *
+ * @note If total tests count is known it is used to determine width of the
+ * progress rectangle. If it isn't the width of progress rectangle is set to
+ * the half of a widget.
+ */
+ private void recalculateColorBarWidth() {
+ Rectangle r = getClientArea();
+ int newColorBarWidth;
+ if (testingSession.getTotalCounter() > 0) {
+ newColorBarWidth = testingSession.getCurrentCounter()*(r.width-2)/testingSession.getTotalCounter();
+ } else {
+ newColorBarWidth = testingSession.getCurrentCounter() > 0 ? (r.width-2)/2 : (testingSession.isFinished() ? r.width-2 : 0);
+ }
+ colorBarWidth = Math.max(0, newColorBarWidth);
+ }
+
+ /**
+ * Draws the widget border
+ */
+ private void drawBevelRect(GC gc, int x, int y, int w, int h, Color topleft, Color bottomright) {
+ gc.setForeground(topleft);
+ gc.drawLine(x, y, x+w-1, y);
+ gc.drawLine(x, y, x, y+h-1);
+
+ gc.setForeground(bottomright);
+ gc.drawLine(x+w, y, x+w, y+h);
+ gc.drawLine(x, y+h, x+w, y+h);
+ }
+
+ /**
+ * Handles paint event and redraws the widget if necessary.
+ *
+ * @param event paint event
+ */
+ private void paint(PaintEvent event) {
+ GC gc = event.gc;
+ Display disp = getDisplay();
+
+ Rectangle rect = getClientArea();
+ gc.fillRectangle(rect);
+ drawBevelRect(gc, rect.x, rect.y, rect.width-1, rect.height-1,
+ disp.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW),
+ disp.getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW));
+
+ setStatusColor(gc);
+ colorBarWidth = Math.min(rect.width-2, colorBarWidth);
+ gc.fillRectangle(1, 1, colorBarWidth, rect.height-2);
+ }
+
+ @Override
+ public Point computeSize(int wHint, int hHint, boolean changed) {
+ checkWidget();
+ Point size = new Point(DEFAULT_WIDTH, DEFAULT_HEIGHT);
+ if (wHint != SWT.DEFAULT) {
+ size.x = wHint;
+ }
+ if (hHint != SWT.DEFAULT) {
+ size.y = hHint;
+ }
+ return size;
+ }
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/ProgressCountPanel.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/ProgressCountPanel.java
new file mode 100644
index 0000000000..eb8e65cb4a
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/ProgressCountPanel.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * 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 org.eclipse.cdt.testsrunner.model.ITestingSession;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * A statistics panel that compounds counter panel and red/green progress bar.
+ * Depending on orientation it may layout them vertically or horizontally.
+ */
+public class ProgressCountPanel extends Composite {
+
+ /** Child widget: counter panel. */
+ private CounterPanel counterPanel;
+
+ /** Child widget: red/green progress bar */
+ private ProgressBar progressBar;
+
+ /**
+ * Dummy session is used when there is no "real" testing sessions to show
+ * (e.g. when there was no launched testing session or when all of them were
+ * cleared).
+ */
+ private DummyUISession dummyUISession = new DummyUISession();
+
+
+ public ProgressCountPanel(Composite parent, ResultsView.Orientation currOrientation) {
+ super(parent, SWT.NONE);
+ GridLayout layout = new GridLayout();
+ setLayout(layout);
+ setPanelOrientation(currOrientation);
+
+ counterPanel = new CounterPanel(this, dummyUISession);
+ counterPanel.setLayoutData(
+ new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL));
+ progressBar = new ProgressBar(this, dummyUISession);
+ progressBar.setLayoutData(
+ new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL));
+
+ // Data for parent (view's) layout
+ setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL));
+ }
+
+ /**
+ * Sets the testing session to show information about.
+ *
+ * @param testingSession testing session or null to set default empty
+ * session
+ */
+ public void setTestingSession(ITestingSession testingSession) {
+ ITestingSession newSession = (testingSession != null) ? testingSession : dummyUISession;
+ counterPanel.setTestingSession(newSession);
+ progressBar.setTestingSession(newSession);
+ }
+
+ /**
+ * Updates the information on the panel from the currently set testing
+ * session.
+ */
+ public void updateInfoFromSession() {
+ counterPanel.updateInfoFromSession();
+ progressBar.updateInfoFromSession();
+ }
+
+ /**
+ * Sets the widget orientation.
+ *
+ * @param orientation new widget orientation (vertical or horizontal; auto
+ * is not supported)
+ */
+ public void setPanelOrientation(ResultsView.Orientation orientation) {
+ ((GridLayout)getLayout()).numColumns = (orientation == ResultsView.Orientation.Horizontal) ? 2 : 1;
+ }
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/ResultsPanel.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/ResultsPanel.java
new file mode 100644
index 0000000000..50c8d94c8b
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/ResultsPanel.java
@@ -0,0 +1,245 @@
+/*******************************************************************************
+ * 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.util.Iterator;
+
+import org.eclipse.cdt.testsrunner.internal.model.TestingSessionsManager;
+import org.eclipse.cdt.testsrunner.internal.ui.view.MessagesViewer.LevelFilter;
+import org.eclipse.cdt.testsrunner.internal.ui.view.actions.MessageLevelFilterAction;
+import org.eclipse.cdt.testsrunner.internal.ui.view.actions.MessagesOrderingAction;
+import org.eclipse.cdt.testsrunner.model.ITestItem;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+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.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.ui.IMemento;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.IWorkbench;
+
+/**
+ * The main widget of testing results view. It compounds tests hierarchy and
+ * messages viewer. Depending on orientation it may layout them vertically or
+ * horizontally.
+ */
+public class ResultsPanel {
+
+ /** Parent for the child widgets (messages & tests hierarchy viewer). */
+ private SashForm sashForm;
+
+ /** Child widget: messages viewer. */
+ private MessagesViewer messagesViewer;
+
+ /** Child widget: tests hierarchy viewer. */
+ private TestsHierarchyViewer testsHierarchyViewer;
+
+ // Persistence tags
+ static final String TAG_WEIGHT0 = "weight0"; //$NON-NLS-1$
+ static final String TAG_WEIGHT1 = "weight1"; //$NON-NLS-1$
+ static final String TAG_MESSAGES_ORDERING_ACTION = "messagesOrderingAction"; //$NON-NLS-1$
+ static final String TAG_ERROR_FILTER_ACTION = "errorFilterAction"; //$NON-NLS-1$
+ static final String TAG_WARNING_FILTER_ACTION = "warningFilterAction"; //$NON-NLS-1$
+ static final String TAG_INFO_FILTER_ACTION = "infoFilterAction"; //$NON-NLS-1$
+
+ // Messages Viewer actions
+ Action messagesOrderingAction;
+ Action errorFilterAction;
+ Action warningFilterAction;
+ Action infoFilterAction;
+
+
+ public ResultsPanel(Composite parent, TestingSessionsManager sessionsManager, IWorkbench workbench, IViewSite site, Clipboard clipboard) {
+ sashForm = new SashForm(parent, SWT.VERTICAL);
+
+ // Configure tests hierarchy viewer
+ ViewForm top = new ViewForm(sashForm, SWT.NONE);
+ Composite empty = new Composite(top, SWT.NONE);
+ empty.setLayout(new Layout() {
+ @Override
+ protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {
+ return new Point(1, 1); // (0, 0) does not work with super-intelligent ViewForm
+ }
+ @Override
+ protected void layout(Composite composite, boolean flushCache) {}
+ });
+ top.setTopLeft(empty); // makes ViewForm draw the horizontal separator line ...
+ testsHierarchyViewer = new TestsHierarchyViewer(top, site, clipboard);
+ top.setContent(testsHierarchyViewer.getTreeViewer().getControl());
+
+ // Configure test messages viewer
+ ViewForm bottom = new ViewForm(sashForm, SWT.NONE);
+ messagesViewer = new MessagesViewer(bottom, sessionsManager, workbench, site, clipboard);
+ Composite topLeftPanel = new Composite(bottom, SWT.NONE);
+ RowLayout topLeftPanelLayout = new RowLayout(SWT.HORIZONTAL);
+ topLeftPanelLayout.spacing = 0;
+ topLeftPanelLayout.center = true;
+ topLeftPanelLayout.marginBottom = topLeftPanelLayout.marginLeft = topLeftPanelLayout.marginRight = topLeftPanelLayout.marginTop = 0;
+ topLeftPanel.setLayout(topLeftPanelLayout);
+ ToolBar leftMessagesToolBar = new ToolBar(topLeftPanel, SWT.FLAT | SWT.WRAP);
+ ToolBarManager leftMessagesToolBarManager = new ToolBarManager(leftMessagesToolBar);
+ messagesOrderingAction = new MessagesOrderingAction(messagesViewer);
+ leftMessagesToolBarManager.add(messagesOrderingAction);
+ leftMessagesToolBarManager.update(true);
+ CLabel label = new CLabel(topLeftPanel, SWT.NONE);
+ label.setText(UIViewMessages.MessagesPanel_label);
+ bottom.setTopLeft(topLeftPanel);
+ ToolBar rightMessagesToolBar = new ToolBar(bottom, SWT.FLAT | SWT.WRAP);
+ ToolBarManager rightMessagesToolBarManager = new ToolBarManager(rightMessagesToolBar);
+ errorFilterAction = new MessageLevelFilterAction(messagesViewer, LevelFilter.Error, true);
+ warningFilterAction = new MessageLevelFilterAction(messagesViewer, LevelFilter.Warning, true);
+ infoFilterAction = new MessageLevelFilterAction(messagesViewer, LevelFilter.Info, false);
+ rightMessagesToolBarManager.add(errorFilterAction);
+ rightMessagesToolBarManager.add(warningFilterAction);
+ rightMessagesToolBarManager.add(infoFilterAction);
+ rightMessagesToolBarManager.update(true);
+ bottom.setTopCenter(rightMessagesToolBar);
+ bottom.setContent(messagesViewer.getTableViewer().getControl());
+
+ sashForm.setWeights(new int[]{50, 50});
+
+ testsHierarchyViewer.getTreeViewer().addSelectionChangedListener(new ISelectionChangedListener() {
+ @Override
+ public void selectionChanged(SelectionChangedEvent event) {
+ handleTestItemSelected();
+ }
+ });
+
+ // Initialize default value
+ setShowFailedOnly(false);
+
+ // Data for parent (view's) layout
+ sashForm.setLayoutData(new GridData(GridData.FILL_BOTH));
+ }
+
+ /**
+ * Provides access to the tests hierarchy viewer.
+ *
+ * @return tests hierarchy viewer
+ */
+ public TestsHierarchyViewer getTestsHierarchyViewer() {
+ return testsHierarchyViewer;
+ }
+
+ /**
+ * Provides access to the messages viewer.
+ *
+ * @return messages viewer
+ */
+ public MessagesViewer getMessagesViewer() {
+ return messagesViewer;
+ }
+
+ /**
+ * Handles selection change in tests hierarchy viewer and updates the
+ * content of the messages viewer to show the messages for the selected
+ * items.
+ */
+ private void handleTestItemSelected() {
+ IStructuredSelection selection = (IStructuredSelection)testsHierarchyViewer.getTreeViewer().getSelection();
+ ITestItem[] testItems = new ITestItem[selection.size()];
+ int index = 0;
+ for (Iterator<?> it = selection.iterator(); it.hasNext();) {
+ testItems[index] = (ITestItem)it.next();
+ ++index;
+ }
+ messagesViewer.showItemsMessages(testItems);
+ }
+
+ /**
+ * Sets the widget orientation.
+ *
+ * @param orientation new widget orientation (vertical or horizontal; auto
+ * is not supported)
+ */
+ public void setPanelOrientation(ResultsView.Orientation orientation) {
+ sashForm.setOrientation(orientation == ResultsView.Orientation.Horizontal ? SWT.HORIZONTAL : SWT.VERTICAL);
+ }
+
+ /**
+ * Returns whether only failed tests (and messages for them) should be
+ * shown.
+ *
+ * @return filter state
+ */
+ public boolean getShowFailedOnly() {
+ return messagesViewer.getShowFailedOnly();
+ }
+
+ /**
+ * Sets whether only failed tests (and messages for them) should be shown.
+ *
+ * @param showFailedOnly new filter state
+ */
+ public void setShowFailedOnly(boolean showFailedOnly) {
+ testsHierarchyViewer.setShowFailedOnly(showFailedOnly);
+ messagesViewer.setShowFailedOnly(showFailedOnly);
+ }
+
+ /**
+ * Restores the value of the checkable action.
+ *
+ * @param memento previously saved state to restore the action value from
+ * @param key tag name that is used to restore the value
+ * @param action action to restore
+ */
+ private void restoreActionChecked(IMemento memento, String key, Action action) {
+ Boolean checked = memento.getBoolean(key);
+ if (checked != null) {
+ action.setChecked(checked);
+ action.run();
+ }
+ }
+
+ /**
+ * Restores the state of the widget.
+ *
+ * @param memento previously saved state
+ */
+ public void restoreState(IMemento memento) {
+ Integer weight0 = memento.getInteger(TAG_WEIGHT0);
+ Integer weight1 = memento.getInteger(TAG_WEIGHT1);
+ if (weight0 != null && weight1 != null) {
+ sashForm.setWeights(new int[] {weight0, weight1});
+ }
+ restoreActionChecked(memento, TAG_MESSAGES_ORDERING_ACTION, messagesOrderingAction);
+ restoreActionChecked(memento, TAG_ERROR_FILTER_ACTION, errorFilterAction);
+ restoreActionChecked(memento, TAG_WARNING_FILTER_ACTION, warningFilterAction);
+ restoreActionChecked(memento, TAG_INFO_FILTER_ACTION, infoFilterAction);
+ }
+
+ /**
+ * Saves the state of the widget.
+ *
+ * @param memento where to save the state
+ */
+ public void saveState(IMemento memento) {
+ int[] weights = sashForm.getWeights();
+ memento.putInteger(TAG_WEIGHT0, weights[0]);
+ memento.putInteger(TAG_WEIGHT1, weights[1]);
+ memento.putBoolean(TAG_MESSAGES_ORDERING_ACTION, messagesOrderingAction.isChecked());
+ memento.putBoolean(TAG_ERROR_FILTER_ACTION, errorFilterAction.isChecked());
+ memento.putBoolean(TAG_WARNING_FILTER_ACTION, warningFilterAction.isChecked());
+ memento.putBoolean(TAG_INFO_FILTER_ACTION, infoFilterAction.isChecked());
+ }
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/ResultsView.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/ResultsView.java
new file mode 100644
index 0000000000..585cf84e80
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/ResultsView.java
@@ -0,0 +1,366 @@
+/*******************************************************************************
+ * 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 org.eclipse.cdt.testsrunner.internal.TestsRunnerPlugin;
+import org.eclipse.cdt.testsrunner.internal.model.TestingSessionsManager;
+import org.eclipse.cdt.testsrunner.internal.ui.view.actions.HistoryDropDownAction;
+import org.eclipse.cdt.testsrunner.internal.ui.view.actions.RerunAction;
+import org.eclipse.cdt.testsrunner.internal.ui.view.actions.ScrollLockAction;
+import org.eclipse.cdt.testsrunner.internal.ui.view.actions.ShowFailedOnlyAction;
+import org.eclipse.cdt.testsrunner.internal.ui.view.actions.ShowFileNameOnlyAction;
+import org.eclipse.cdt.testsrunner.internal.ui.view.actions.ShowNextFailureAction;
+import org.eclipse.cdt.testsrunner.internal.ui.view.actions.ShowPreviousFailureAction;
+import org.eclipse.cdt.testsrunner.internal.ui.view.actions.ShowTestsInHierarchyAction;
+import org.eclipse.cdt.testsrunner.internal.ui.view.actions.ShowTimeAction;
+import org.eclipse.cdt.testsrunner.internal.ui.view.actions.StopAction;
+import org.eclipse.cdt.testsrunner.internal.ui.view.actions.ToggleOrientationAction;
+import org.eclipse.cdt.testsrunner.model.ITestingSession;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IMemento;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.actions.ActionFactory;
+import org.eclipse.ui.part.ViewPart;
+
+/**
+ * Represents a view part showing the testing results (count statistics,
+ * red/green bar, tests hierarchy and test messages).
+ */
+public class ResultsView extends ViewPart {
+
+ /**
+ * Represents view orientation
+ *
+ * @note <code>Auto</code> state may be not acceptable for some methods (see
+ * their comments for details).
+ */
+ public enum Orientation {
+ Horizontal,
+ Vertical,
+ Auto,
+ }
+
+ /** View parent. */
+ private Composite parent;
+
+ /** Child widget: statistics viewer. */
+ private ProgressCountPanel progressCountPanel;
+
+ /** Tests hierarchy and message viewer. */
+ private ResultsPanel resultsPanel;
+
+ /** User interface updater instance. */
+ private UIUpdater uiUpdater;
+
+ /** The reference to the testing sessions manager instance. */
+ private TestingSessionsManager sessionsManager;
+
+ /** Shows whether the results view was disposed. */
+ private boolean isDisposed = false;
+
+ // Toolbar & view menu actions
+ private Action nextAction;
+ private Action previousAction;
+ private Action rerunAction;
+ private Action stopAction;
+ private ToggleOrientationAction[] toggleOrientationActions;
+ private Action historyAction;
+ private Action showFailedOnly;
+ private Action showTestsInHierarchyAction;
+ private Action showTimeAction;
+ private Action scrollLockAction;
+ private Action showFileNameOnlyAction;
+
+ /**
+ * The current orientation preference (Horizontal, Vertical, Auto).
+ */
+ private Orientation orientation = Orientation.Auto;
+
+ /**
+ * The current view orientation (Horizontal or Vertical).
+ */
+ private Orientation currentOrientation;
+
+ /**
+ * Previously saved state. It is used to store the same state if the view
+ * was not opened.
+ */
+ private IMemento memento;
+
+ // Persistence tags
+ static final String TAG_ORIENTATION = "orientation"; //$NON-NLS-1$
+ static final String TAG_SHOW_FAILED_ONLY = "showFailedOnly"; //$NON-NLS-1$
+ static final String TAG_SHOW_TESTS_IN_HIERARCHY = "showTestsInHierarchy"; //$NON-NLS-1$
+ static final String TAG_SHOW_TIME = "showTime"; //$NON-NLS-1$
+ static final String TAG_SCROLL_LOCK = "scrollLock"; //$NON-NLS-1$
+ static final String TAG_SHOW_FILE_NAME_ONLY_ACTION = "showFileNameOnly"; //$NON-NLS-1$
+ static final String TAG_HISTORY_SIZE = "history_size"; //$NON-NLS-1$
+
+
+ @Override
+ public void createPartControl(Composite parent) {
+ sessionsManager = TestsRunnerPlugin.getDefault().getTestingSessionsManager();
+ IWorkbench workbench = TestsRunnerPlugin.getDefault().getWorkbench();
+ Clipboard clipboard = new Clipboard(parent.getDisplay());
+
+ this.parent = parent;
+ GridLayout gridLayout = new GridLayout();
+ gridLayout.marginWidth = 0;
+ gridLayout.marginHeight = 0;
+ parent.setLayout(gridLayout);
+ currentOrientation = getActualOrientation();
+
+ progressCountPanel = new ProgressCountPanel(parent, currentOrientation);
+ resultsPanel = new ResultsPanel(parent, sessionsManager, workbench, getViewSite(), clipboard);
+ uiUpdater = new UIUpdater(this, resultsPanel.getTestsHierarchyViewer(), progressCountPanel, sessionsManager);
+ configureActionsBars();
+
+ parent.addControlListener(new ControlListener() {
+ @Override
+ public void controlMoved(ControlEvent e) {
+ }
+ @Override
+ public void controlResized(ControlEvent e) {
+ computeOrientation();
+ }
+ });
+
+ restoreState(memento);
+ uiUpdater.reapplyActiveSession();
+ }
+
+ @Override
+ public void setFocus() {
+ resultsPanel.getTestsHierarchyViewer().getTreeViewer().getControl().setFocus();
+ }
+
+ /**
+ * Configures the view tool bar and menu.
+ */
+ private void configureActionsBars() {
+ IActionBars actionBars = getViewSite().getActionBars();
+
+ // Create common action
+ toggleOrientationActions = new ToggleOrientationAction[] {
+ new ToggleOrientationAction(this, Orientation.Vertical),
+ new ToggleOrientationAction(this, Orientation.Horizontal),
+ new ToggleOrientationAction(this, Orientation.Auto),
+ };
+
+ nextAction = new ShowNextFailureAction(resultsPanel.getTestsHierarchyViewer());
+ nextAction.setEnabled(false);
+ actionBars.setGlobalActionHandler(ActionFactory.NEXT.getId(), nextAction);
+
+ previousAction = new ShowPreviousFailureAction(resultsPanel.getTestsHierarchyViewer());
+ previousAction.setEnabled(false);
+ actionBars.setGlobalActionHandler(ActionFactory.PREVIOUS.getId(), previousAction);
+
+ showFailedOnly = new ShowFailedOnlyAction(resultsPanel);
+ showTestsInHierarchyAction = new ShowTestsInHierarchyAction(resultsPanel.getTestsHierarchyViewer());
+ showTimeAction = new ShowTimeAction(resultsPanel.getTestsHierarchyViewer());
+ scrollLockAction = new ScrollLockAction(uiUpdater);
+ showFileNameOnlyAction = new ShowFileNameOnlyAction(resultsPanel.getMessagesViewer());
+ rerunAction = new RerunAction(sessionsManager);
+ rerunAction.setEnabled(false);
+ stopAction = new StopAction(sessionsManager);
+ stopAction.setEnabled(false);
+
+ historyAction = new HistoryDropDownAction(sessionsManager, parent.getShell());
+
+ // Configure toolbar
+ IToolBarManager toolBar = actionBars.getToolBarManager();
+ toolBar.add(nextAction);
+ toolBar.add(previousAction);
+ toolBar.add(showFailedOnly);
+ toolBar.add(scrollLockAction);
+ toolBar.add(new Separator());
+ toolBar.add(rerunAction);
+ toolBar.add(stopAction);
+ toolBar.add(historyAction);
+
+ // Configure view menu
+ IMenuManager viewMenu = actionBars.getMenuManager();
+ viewMenu.add(showTestsInHierarchyAction);
+ viewMenu.add(showTimeAction);
+ viewMenu.add(new Separator());
+ MenuManager layoutSubMenu = new MenuManager(UIViewMessages.ResultsView_layout_menu_text);
+ for (int i = 0; i < toggleOrientationActions.length; ++i) {
+ layoutSubMenu.add(toggleOrientationActions[i]);
+ }
+ viewMenu.add(layoutSubMenu);
+ viewMenu.add(new Separator());
+ viewMenu.add(showFailedOnly);
+ viewMenu.add(showFileNameOnlyAction);
+ }
+
+ @Override
+ public void dispose() {
+ isDisposed = true;
+ if (uiUpdater != null) {
+ uiUpdater.dispose();
+ }
+ }
+
+ /**
+ * Changes the view orientation
+ *
+ * @param orientation new view orientation (Horizontal, Vertical, Auto)
+ */
+ public void setOrientation(Orientation orientation) {
+ this.orientation = orientation;
+ computeOrientation();
+ }
+
+ /**
+ * Checks whether actual orientation is changed and changes orientation of
+ * the child widgets.
+ */
+ private void computeOrientation() {
+ Orientation newActualOrientation = getActualOrientation();
+ if (newActualOrientation != currentOrientation) {
+ currentOrientation = newActualOrientation;
+ progressCountPanel.setPanelOrientation(currentOrientation);
+ resultsPanel.setPanelOrientation(currentOrientation);
+ for (int i = 0; i < toggleOrientationActions.length; ++i) {
+ toggleOrientationActions[i].setChecked(orientation == toggleOrientationActions[i].getOrientation());
+ }
+ parent.layout();
+ }
+ }
+
+ /**
+ * Recalculates actual view orientation depending on the specified by user
+ * orientation value and current view size.
+ *
+ * @param orientation by user specified orientation
+ * @return actual orientation
+ */
+ private Orientation getActualOrientation() {
+ switch (orientation) {
+ case Horizontal:
+ case Vertical:
+ return orientation;
+ case Auto:
+ Point size = parent.getSize();
+ return (size.x > size.y) ? Orientation.Horizontal : Orientation.Vertical;
+ }
+ return null;
+ }
+
+ /**
+ * Updates view actions state from the active session.
+ */
+ public void updateActionsFromSession() {
+ ITestingSession session = sessionsManager.getActiveSession();
+ boolean hasErrors = session != null && session.hasErrors();
+ previousAction.setEnabled(hasErrors);
+ nextAction.setEnabled(hasErrors);
+ rerunAction.setEnabled(session != null && session.isFinished());
+ stopAction.setEnabled(session != null && !session.isFinished());
+ }
+
+ /**
+ * Changes the view caption.
+ *
+ * @param message new view caption
+ */
+ public void setCaption(String message) {
+ setContentDescription(message);
+ }
+
+ @Override
+ public void init(IViewSite site, IMemento memento) throws PartInitException {
+ super.init(site, memento);
+ this.memento = memento;
+ }
+
+ /**
+ * Restores the value of the checkable action.
+ *
+ * @param memento previously saved state to restore the action value from
+ * @param key tag name that is used to restore the value
+ * @param action action to restore
+ */
+ private void restoreActionChecked(IMemento memento, String key, Action action) {
+ Boolean checked = memento.getBoolean(key);
+ if (checked != null) {
+ action.setChecked(checked);
+ action.run();
+ }
+ }
+
+ /**
+ * Restores the state of the view.
+ *
+ * @param memento previously saved state
+ */
+ private void restoreState(IMemento memento) {
+ if (memento != null) {
+ Integer orientationIndex = memento.getInteger(TAG_ORIENTATION);
+ if (orientationIndex != null) {
+ setOrientation(Orientation.values()[orientationIndex]);
+ }
+ resultsPanel.restoreState(memento);
+ restoreActionChecked(memento, TAG_SHOW_FAILED_ONLY, showFailedOnly);
+ restoreActionChecked(memento, TAG_SHOW_TESTS_IN_HIERARCHY, showTestsInHierarchyAction);
+ restoreActionChecked(memento, TAG_SHOW_TIME, showTimeAction);
+ restoreActionChecked(memento, TAG_SCROLL_LOCK, scrollLockAction);
+ restoreActionChecked(memento, TAG_SHOW_FILE_NAME_ONLY_ACTION, showFileNameOnlyAction);
+ Integer historySize = memento.getInteger(TAG_HISTORY_SIZE);
+ if (historySize != null) {
+ sessionsManager.setHistorySizeLimit(historySize);
+ }
+ }
+ }
+
+ @Override
+ public void saveState(IMemento memento) {
+ //Keep the old state;
+ if (parent == null) {
+ if (this.memento != null) {
+ memento.putMemento(this.memento);
+ }
+ return;
+ }
+
+ memento.putInteger(TAG_ORIENTATION, orientation.ordinal());
+ resultsPanel.saveState(memento);
+ memento.putBoolean(TAG_SHOW_FAILED_ONLY, showFailedOnly.isChecked());
+ memento.putBoolean(TAG_SHOW_TESTS_IN_HIERARCHY, showTestsInHierarchyAction.isChecked());
+ memento.putBoolean(TAG_SHOW_TIME, showTimeAction.isChecked());
+ memento.putBoolean(TAG_SCROLL_LOCK, scrollLockAction.isChecked());
+ memento.putBoolean(TAG_SHOW_FILE_NAME_ONLY_ACTION, showFileNameOnlyAction.isChecked());
+ memento.putInteger(TAG_HISTORY_SIZE, sessionsManager.getHistorySizeLimit());
+ }
+
+ /**
+ * Returns whether the view was disposed.
+ *
+ * @return true if the view was disposed
+ */
+ public boolean isDisposed() {
+ return isDisposed;
+ }
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/TestPathUtils.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/TestPathUtils.java
new file mode 100644
index 0000000000..512754435a
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/TestPathUtils.java
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * 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.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.cdt.testsrunner.model.ITestItem;
+
+/**
+ * The utilities collection to work with the test paths for items in test
+ * hierarchy.
+ */
+public class TestPathUtils {
+
+ /**
+ * The delimiter between parts of serialized test path. Should not be met in
+ * test paths names.
+ */
+ private static final String TEST_PATH_PART_DELIMITER = "\n"; //$NON-NLS-1$
+
+ /** The delimiter between parts of human readable test path. */
+ private static final String TEST_PATH_DELIMITER = "."; //$NON-NLS-1$
+
+ /**
+ * Returns the human readable path to the item in test hierarchy (e.g.
+ * MySuite.MyInnerSuite.MyTest1).
+ *
+ * @param testItem test item (test suite or test case)
+ *
+ * @return path to test item
+ */
+ public static String getTestItemPath(ITestItem testItem) {
+ StringBuilder itemPath = new StringBuilder();
+ List<ITestItem> parentItems = new ArrayList<ITestItem>();
+ while (testItem != null) {
+ parentItems.add(testItem);
+ testItem = testItem.getParent();
+ }
+ if (!parentItems.isEmpty()) {
+ for (int i = parentItems.size()-2/* exclude unnamed root test suite */; i >= 0; --i) {
+ itemPath.append(parentItems.get(i).getName());
+ if (i != 0) {
+ itemPath.append(TEST_PATH_DELIMITER);
+ }
+ }
+ }
+ return itemPath.toString();
+ }
+
+ /**
+ * Unpack the paths from the string list.
+ *
+ * @param testPaths packed test paths
+ * @return array of test paths
+ *
+ * @see packTestPaths()
+ */
+ public static String[][] unpackTestPaths(String[] testPaths) {
+ String [][] result = new String[testPaths.length][];
+ for (int i = 0; i < result.length; i++) {
+ result[i] = testPaths[i].split(TEST_PATH_PART_DELIMITER);
+ }
+ return result;
+ }
+
+ /**
+ * Pack the paths to specified test items to string list.
+ *
+ * @param testItems test items to pack
+ * @return string list
+ *
+ * @see unpackTestPaths()
+ */
+ public static String[] packTestPaths(ITestItem[] testItems) {
+ String [] result = new String[testItems.length];
+ List<String> testPath = new ArrayList<String>();
+
+ for (int itemIdx = 0; itemIdx < testItems.length; itemIdx++) {
+ // Collect test path parts (in reverse order)
+ testPath.clear();
+ ITestItem item = testItems[itemIdx];
+ while (item != null) {
+ // Exclude root test suite
+ if (item.getParent()!= null) {
+ testPath.add(item.getName());
+ }
+ item = item.getParent();
+ }
+ // Join path parts into the only string
+ StringBuilder sb = new StringBuilder();
+ boolean needDelimiter = false;
+ for (int pathPartIdx = testPath.size()-1; pathPartIdx >= 0; pathPartIdx--) {
+ if (needDelimiter) {
+ sb.append(TEST_PATH_PART_DELIMITER);
+ } else {
+ needDelimiter = true;
+ }
+ sb.append(testPath.get(pathPartIdx));
+ }
+ result[itemIdx] = sb.toString();
+ }
+ return result;
+ }
+
+}
+
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/TestsHierarchyViewer.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/TestsHierarchyViewer.java
new file mode 100644
index 0000000000..117a7b3bee
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/TestsHierarchyViewer.java
@@ -0,0 +1,554 @@
+/*******************************************************************************
+ * 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.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.cdt.internal.ui.viewsupport.ColoringLabelProvider;
+import org.eclipse.cdt.testsrunner.internal.TestsRunnerPlugin;
+import org.eclipse.cdt.testsrunner.internal.ui.view.actions.CopySelectedTestsAction;
+import org.eclipse.cdt.testsrunner.internal.ui.view.actions.RedebugSelectedAction;
+import org.eclipse.cdt.testsrunner.internal.ui.view.actions.RelaunchSelectedAction;
+import org.eclipse.cdt.testsrunner.internal.ui.view.actions.RerunSelectedAction;
+import org.eclipse.cdt.testsrunner.internal.ui.view.actions.TestsHierarchyCollapseAllAction;
+import org.eclipse.cdt.testsrunner.internal.ui.view.actions.TestsHierarchyExpandAllAction;
+import org.eclipse.cdt.testsrunner.model.IModelVisitor;
+import org.eclipse.cdt.testsrunner.model.ITestCase;
+import org.eclipse.cdt.testsrunner.model.ITestItem;
+import org.eclipse.cdt.testsrunner.model.ITestMessage;
+import org.eclipse.cdt.testsrunner.model.ITestSuite;
+import org.eclipse.cdt.testsrunner.model.ITestingSession;
+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.Separator;
+import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.StyledCellLabelProvider;
+import org.eclipse.jface.viewers.StyledString;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.actions.ActionFactory;
+
+/**
+ * Shows the tests hierarchy in a flat or hierarchical view.
+ */
+public class TestsHierarchyViewer {
+
+ /**
+ * The content provider for the tests hierarchy viewer.
+ */
+ private class TestTreeContentProvider implements ITreeContentProvider {
+
+ /**
+ * Utility class: recursively collects all the test cases of the
+ * specified test item.
+ *
+ * It is used for flat view of tests hierarchy.
+ */
+ private class TestCasesCollector implements IModelVisitor {
+
+ public List<ITestCase> testCases = new ArrayList<ITestCase>();
+
+ @Override
+ public void visit(ITestCase testCase) {
+ testCases.add(testCase);
+ }
+
+ @Override
+ public void visit(ITestMessage testMessage) {}
+ @Override
+ public void visit(ITestSuite testSuite) {}
+ @Override
+ public void leave(ITestSuite testSuite) {}
+ @Override
+ public void leave(ITestCase testCase) {}
+ @Override
+ public void leave(ITestMessage testMessage) {}
+ }
+
+ @Override
+ public Object[] getChildren(Object parentElement) {
+ return ((ITestItem) parentElement).getChildren();
+ }
+
+ @Override
+ public Object[] getElements(Object rootTestSuite) {
+ if (showTestsHierarchy) {
+ return getChildren(rootTestSuite);
+ } else {
+ TestCasesCollector testCasesCollector = new TestCasesCollector();
+ ((ITestItem)rootTestSuite).visit(testCasesCollector);
+ return testCasesCollector.testCases.toArray();
+ }
+ }
+
+ @Override
+ public Object getParent(Object object) {
+ return ((ITestItem) object).getParent();
+ }
+
+ @Override
+ public boolean hasChildren(Object object) {
+ return ((ITestItem) object).hasChildren();
+ }
+
+ @Override
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {}
+
+ @Override
+ public void dispose() {}
+ }
+
+ /**
+ * The label provider for the tests hierarchy viewer.
+ */
+ private class TestLabelProvider extends LabelProvider implements IStyledLabelProvider {
+
+ /** Images for the test cases with the different statuses. */
+ private Map<ITestItem.Status, Image> testCaseImages = new HashMap<ITestItem.Status, Image>();
+ {
+ testCaseImages.put(ITestItem.Status.NotRun, TestsRunnerPlugin.createAutoImage("obj16/test_notrun.gif")); //$NON-NLS-1$
+ testCaseImages.put(ITestItem.Status.Skipped, TestsRunnerPlugin.createAutoImage("obj16/test_skipped.gif")); //$NON-NLS-1$
+ testCaseImages.put(ITestItem.Status.Passed, TestsRunnerPlugin.createAutoImage("obj16/test_passed.gif")); //$NON-NLS-1$
+ testCaseImages.put(ITestItem.Status.Failed, TestsRunnerPlugin.createAutoImage("obj16/test_failed.gif")); //$NON-NLS-1$
+ testCaseImages.put(ITestItem.Status.Aborted, TestsRunnerPlugin.createAutoImage("obj16/test_aborted.gif")); //$NON-NLS-1$
+ }
+
+ /** Running test case image (overrides the test case status image). */
+ private Image testCaseRunImage = TestsRunnerPlugin.createAutoImage("obj16/test_run.gif"); //$NON-NLS-1$
+
+ /** Images for the test suites with the different statuses. */
+ private Map<ITestItem.Status, Image> testSuiteImages = new HashMap<ITestItem.Status, Image>();
+ {
+ // NOTE: There is no skipped-icon for test suite, but it seems it is not a problem
+ testSuiteImages.put(ITestItem.Status.NotRun, TestsRunnerPlugin.createAutoImage("obj16/tsuite_notrun.gif")); //$NON-NLS-1$
+ testSuiteImages.put(ITestItem.Status.Skipped, TestsRunnerPlugin.createAutoImage("obj16/tsuite_notrun.gif")); //$NON-NLS-1$
+ testSuiteImages.put(ITestItem.Status.Passed, TestsRunnerPlugin.createAutoImage("obj16/tsuite_passed.gif")); //$NON-NLS-1$
+ testSuiteImages.put(ITestItem.Status.Failed, TestsRunnerPlugin.createAutoImage("obj16/tsuite_failed.gif")); //$NON-NLS-1$
+ testSuiteImages.put(ITestItem.Status.Aborted, TestsRunnerPlugin.createAutoImage("obj16/tsuite_aborted.gif")); //$NON-NLS-1$
+ }
+
+ /** Running test suite image (overrides the test suite status image). */
+ private Image testSuiteRunImage = TestsRunnerPlugin.createAutoImage("obj16/tsuite_run.gif"); //$NON-NLS-1$
+
+ /** Small optimization: the last test item cache */
+ private ITestItem lastTestItemCache = null;
+
+ /** Small optimization: test path for the last test item is cache */
+ private String lastTestItemPathCache = null;
+
+
+ @Override
+ public Image getImage(Object element) {
+ Map<ITestItem.Status, Image> imagesMap = null;
+ Image runImage = null;
+ if (element instanceof ITestCase) {
+ imagesMap = testCaseImages;
+ runImage = testCaseRunImage;
+
+ } else if (element instanceof ITestSuite) {
+ imagesMap = testSuiteImages;
+ runImage = testSuiteRunImage;
+ }
+ if (imagesMap != null) {
+ ITestItem testItem = (ITestItem)element;
+ if (testingSession.getModelAccessor().isCurrentlyRunning(testItem)) {
+ return runImage;
+ }
+ return imagesMap.get(testItem.getStatus());
+ }
+
+ return null;
+ }
+
+ @Override
+ public String getText(Object element) {
+ ITestItem testItem = (ITestItem)element;
+ StringBuilder sb = new StringBuilder();
+ sb.append(testItem.getName());
+ if (!showTestsHierarchy) {
+ appendTestItemPath(sb, testItem);
+ }
+ if (showTime) {
+ sb.append(getTestingTimeString(element));
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public StyledString getStyledText(Object element) {
+ ITestItem testItem = (ITestItem)element;
+ StringBuilder labelBuf = new StringBuilder();
+ labelBuf.append(testItem.getName());
+ StyledString name = new StyledString(labelBuf.toString());
+ if (!showTestsHierarchy) {
+ appendTestItemPath(labelBuf, testItem);
+ name = StyledCellLabelProvider.styleDecoratedString(labelBuf.toString(), StyledString.QUALIFIER_STYLER, name);
+ }
+ if (showTime) {
+ String time = getTestingTimeString(element);
+ labelBuf.append(time);
+ name = StyledCellLabelProvider.styleDecoratedString(labelBuf.toString(), StyledString.COUNTER_STYLER, name);
+ }
+ return name;
+ }
+
+ /**
+ * Appends path to the parent of the specified test item. Also
+ * implements caching of the last path (cause the test item parent is
+ * often the same).
+ *
+ * @param sb string builder to append test item path to
+ * @param testItem specified test item
+ */
+ private void appendTestItemPath(StringBuilder sb, ITestItem testItem) {
+ ITestSuite testItemParent = testItem.getParent();
+ if (lastTestItemCache != testItemParent) {
+ lastTestItemCache = testItemParent;
+ lastTestItemPathCache = TestPathUtils.getTestItemPath(lastTestItemCache);
+ }
+ sb.append(MessageFormat.format(
+ UIViewMessages.TestsHierarchyViewer_test_path_format,
+ new Object[] { lastTestItemPathCache }
+ ));
+ }
+
+ /**
+ * Returns the execution time suffix for the test item.
+ *
+ * @param element test item
+ * @return execution time suffix
+ */
+ private String getTestingTimeString(Object element) {
+ return (element instanceof ITestItem)
+ ? MessageFormat.format(UIViewMessages.TestsHierarchyViewer_test_time_format, Double.toString(((ITestItem)element).getTestingTime()/1000.0))
+ : ""; //$NON-NLS-1$
+ }
+
+ }
+
+ /**
+ * Filters passed test cases and test suites.
+ */
+ private class FailedOnlyFilter extends ViewerFilter {
+
+ @Override
+ public boolean select(Viewer viewer, Object parentElement, Object element) {
+ return ((ITestItem)element).getStatus().isError();
+ }
+ }
+
+ /** Testing session to show hierarchy of. */
+ private ITestingSession testingSession;
+
+ /** Main widget. */
+ private TreeViewer treeViewer;
+
+ /** Specifies whether test items execution time should be shown in hierarchy. */
+ private boolean showTime = true;
+
+ /** Specifies whether tests hierarchy should be shown in flat or hierarchical view. */
+ private boolean showTestsHierarchy = true;
+
+ /** Failed only tree filter instance. Created on first demand. */
+ private FailedOnlyFilter failedOnlyFilter = null;
+
+ /** System clipboard access to provide copy operations. */
+ private Clipboard clipboard;
+
+ // Context menu actions
+ private Action expandAllAction;
+ private Action collapseAllAction;
+ private Action copyAction;
+ private RelaunchSelectedAction rerunAction;
+ private RelaunchSelectedAction redebugAction;
+
+
+ public TestsHierarchyViewer(Composite parent, IViewSite viewSite, Clipboard clipboard) {
+ this.clipboard = clipboard;
+ treeViewer = new TreeViewer(parent, SWT.V_SCROLL | SWT.MULTI);
+ treeViewer.setContentProvider(new TestTreeContentProvider());
+ treeViewer.setLabelProvider(new ColoringLabelProvider(new TestLabelProvider()));
+ initContextMenu(viewSite);
+ }
+
+ /**
+ * Initializes the viewer context menu.
+ *
+ * @param viewSite view
+ */
+ private void initContextMenu(IViewSite viewSite) {
+ expandAllAction = new TestsHierarchyExpandAllAction(treeViewer);
+ collapseAllAction = new TestsHierarchyCollapseAllAction(treeViewer);
+ copyAction = new CopySelectedTestsAction(treeViewer, clipboard);
+ rerunAction = new RerunSelectedAction(testingSession, treeViewer);
+ redebugAction = new RedebugSelectedAction(testingSession, treeViewer);
+
+ MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$
+ menuMgr.addMenuListener(new IMenuListener() {
+ @Override
+ public void menuAboutToShow(IMenuManager manager) {
+ handleMenuAboutToShow(manager);
+ }
+ });
+ viewSite.registerContextMenu(menuMgr, treeViewer);
+ Menu menu = menuMgr.createContextMenu(treeViewer.getTree());
+ treeViewer.getTree().setMenu(menu);
+
+ menuMgr.add(copyAction);
+ menuMgr.add(new Separator());
+ menuMgr.add(rerunAction);
+ menuMgr.add(redebugAction);
+ menuMgr.add(new Separator());
+ menuMgr.add(expandAllAction);
+ menuMgr.add(collapseAllAction);
+
+ IActionBars actionBars = viewSite.getActionBars();
+ actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(), copyAction);
+ actionBars.updateActionBars();
+ }
+
+ /**
+ * Handles the context menu showing.
+ *
+ * @param manager context menu manager
+ */
+ private void handleMenuAboutToShow(IMenuManager manager) {
+ IStructuredSelection selection = (IStructuredSelection)treeViewer.getSelection();
+ boolean isRelaunchEnabledForSelection = !selection.isEmpty() &&
+ (testingSession.getTestsRunnerProviderInfo().isAllowedMultipleTestFilter() || (selection.size() == 1));
+ rerunAction.setEnabled(isRelaunchEnabledForSelection);
+ rerunAction.setTestingSession(testingSession);
+ redebugAction.setEnabled(isRelaunchEnabledForSelection);
+ redebugAction.setTestingSession(testingSession);
+ copyAction.setEnabled(!selection.isEmpty());
+
+ boolean hasAnything = treeViewer.getInput() != null;
+ expandAllAction.setEnabled(hasAnything);
+ collapseAllAction.setEnabled(hasAnything);
+ }
+
+ /**
+ * Sets the testing session to show.
+ *
+ * @param testingSession testing session or null to set default empty
+ * session
+ */
+ public void setTestingSession(ITestingSession testingSession) {
+ this.testingSession = testingSession;
+ treeViewer.setInput(testingSession != null ? testingSession.getModelAccessor().getRootSuite() : null);
+ }
+
+ /**
+ * Provides access to the main widget of the tests hierarchy viewer.
+ *
+ * @return main widget of the tests hierarchy viewer
+ */
+ public TreeViewer getTreeViewer() {
+ return treeViewer;
+ }
+
+ /**
+ * Move the selection to the next failed test case.
+ */
+ public void showNextFailure() {
+ showFailure(true);
+ }
+
+ /**
+ * Move the selection to the previous failed test case.
+ */
+ public void showPreviousFailure() {
+ showFailure(false);
+ }
+
+ /**
+ * Common implementation for movement the selection to the next or previous
+ * failed test case.
+ *
+ * @param next true if the next failed test case should be selected and false otherwise
+ */
+ private void showFailure(boolean next) {
+ IStructuredSelection selection = (IStructuredSelection) getTreeViewer().getSelection();
+ ITestItem selected = (ITestItem) selection.getFirstElement();
+ ITestItem failedItem;
+
+ if (selected == null) {
+ ITestItem rootSuite = (ITestItem)treeViewer.getInput();
+ // For next element we should also check its children, for previous shouldn't.
+ failedItem = findFailedImpl(rootSuite, null, next, next);
+ } else {
+ // For next element we should also check its children, for previous shouldn't.
+ failedItem = findFailedImpl(selected.getParent(), selected, next, next);
+ }
+
+ if (failedItem != null)
+ getTreeViewer().setSelection(new StructuredSelection(failedItem), true);
+ }
+
+ /**
+ * Returns the next or previous failed test case relatively to the
+ * <code>currItem</code> that should be a child of <code>parentItem</code>.
+ * If the such item was not found through the children, it steps up to the
+ * parent and continues search.
+ *
+ * @param parentItem parent test item to the current one
+ * @param currItem current item search should be started from or null if
+ * there is no any
+ * @param next true if the next failed test case should be looked for and
+ * false otherwise
+ * @param checkCurrentChild specifies whether the search should be also
+ * through the children for the current item
+ * @return found item or null
+ */
+ private ITestItem findFailedImpl(ITestItem parentItem, ITestItem currItem, boolean next, boolean checkCurrentChild) {
+ ITestItem result = findFailedChild(parentItem, currItem, next, checkCurrentChild);
+ if (result != null) {
+ return result;
+ }
+ // Nothing found at this level - try to step up
+ ITestSuite grandParentItem = parentItem.getParent();
+ if (grandParentItem != null) {
+ return findFailedImpl(grandParentItem, parentItem, next, false);
+ }
+ return null;
+ }
+
+ /**
+ * Returns the next or previous failed test case relatively to the
+ * <code>currItem</code> that should be a child of <code>parentItem</code>.
+ * Note that unlike <code>findFailedImpl()</code> this method search only
+ * through the children items.
+ *
+ * @param parentItem parent test item to the current one
+ * @param currItem current item search should be started from or null if
+ * there is no any
+ * @param next true if the next failed test case should be looked for and
+ * false otherwise
+ * @param checkCurrentChild specifies whether the search should be also
+ * through the children for the current item
+ * @return found item or null
+ */
+ private ITestItem findFailedChild(ITestItem parentItem, ITestItem currItem, boolean next, boolean checkCurrentChild) {
+ ITestItem[] children = parentItem.getChildren();
+ boolean doSearch = (currItem == null);
+ int increment = next ? 1 : -1;
+ int startIndex = next ? 0 : children.length-1;
+ int endIndex = next ? children.length : -1;
+ for (int index = startIndex; index != endIndex; index += increment) {
+ ITestItem item = children[index];
+ // Check element
+ if (doSearch) {
+ if (item instanceof ITestCase && item.getStatus().isError()) {
+ return item;
+ }
+ }
+ // If children of current element should be checked we should enable search here (if necessary)
+ if (checkCurrentChild && item == currItem) {
+ doSearch = true;
+ }
+ // Search element's children
+ if (doSearch) {
+ ITestItem result = findFailedChild(item, null, next, checkCurrentChild);
+ if (result != null) {
+ return result;
+ }
+ }
+ // If children of current element should NOT be checked we should enable search here
+ if (!checkCurrentChild && item == currItem) {
+ doSearch = true;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns whether test items execution time should be shown in tests
+ * hierarchy.
+ *
+ * @return true if time should be shown and false otherwise
+ */
+ public boolean showTime() {
+ return showTime;
+ }
+
+ /**
+ * Sets whether test items execution time should be shown in tests
+ * hierarchy. Updates tests hierarchy viewer if the view is changed.
+ *
+ * @param showTime true if time is shown and false otherwise
+ */
+ public void setShowTime(boolean showTime) {
+ if (this.showTime != showTime) {
+ this.showTime = showTime;
+ getTreeViewer().refresh();
+ }
+ }
+
+ /**
+ * Sets whether only failed tests should be shown.
+ *
+ * @param showFailedOnly new filter state
+ */
+ public void setShowFailedOnly(boolean showFailedOnly) {
+ // Create filter on first demand
+ if (failedOnlyFilter == null) {
+ failedOnlyFilter = new FailedOnlyFilter();
+ }
+ if (showFailedOnly) {
+ getTreeViewer().addFilter(failedOnlyFilter);
+ } else {
+ getTreeViewer().removeFilter(failedOnlyFilter);
+ }
+ }
+
+ /**
+ * Returns whether tests hierarchy should be shown in flat or hierarchical
+ * mode.
+ *
+ * @return tests hierarchy view mode
+ */
+ public boolean showTestsHierarchy() {
+ return showTestsHierarchy;
+ }
+
+ /**
+ * Sets whether tests hierarchy should be shown in flat or hierarchical
+ * mode. Updates tests hierarchy viewer if the view is changed.
+ *
+ * @param showTestsHierarchy true if tests hierarchy is shown in
+ * hierarchical mode and false otherwise
+ */
+ public void setShowTestsHierarchy(boolean showTestsHierarchy) {
+ if (this.showTestsHierarchy != showTestsHierarchy) {
+ this.showTestsHierarchy = showTestsHierarchy;
+ getTreeViewer().refresh();
+ }
+ }
+
+}
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 0000000000..a6738e0d7e
--- /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());
+ }
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/UIViewMessages.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/UIViewMessages.java
new file mode 100644
index 0000000000..9db469e8bf
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/UIViewMessages.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * 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 org.eclipse.osgi.util.NLS;
+
+public class UIViewMessages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.cdt.testsrunner.internal.ui.view.UIViewMessages"; //$NON-NLS-1$
+ public static String CounterPanel_tests_erred;
+ public static String CounterPanel_tests_failed;
+ public static String CounterPanel_tests_run;
+ public static String CounterPanel_tests_skipped;
+ public static String MessagesPanel_label;
+ public static String MessagesViewer_location_format;
+ public static String MessagesViewer_message_format;
+ public static String ResultsView_layout_menu_text;
+ public static String TestsHierarchyViewer_test_path_format;
+ public static String TestsHierarchyViewer_test_time_format;
+ public static String UIUpdater_update_ui_job;
+ public static String UIUpdater_view_caption_format;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, UIViewMessages.class);
+ }
+
+ private UIViewMessages() {
+ }
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/UIViewMessages.properties b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/UIViewMessages.properties
new file mode 100644
index 0000000000..f586280fdf
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/UIViewMessages.properties
@@ -0,0 +1,22 @@
+###############################################################################
+# 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 implementation
+###############################################################################
+CounterPanel_tests_erred=Errors:
+CounterPanel_tests_failed=Failures:
+CounterPanel_tests_run=Runs:
+CounterPanel_tests_skipped={0} ({1} ignored)
+MessagesPanel_label=Messages
+MessagesViewer_location_format={0}({1}):
+MessagesViewer_message_format={0}{1}: {2}
+ResultsView_layout_menu_text=&Layout
+TestsHierarchyViewer_test_path_format=\ - {0}
+TestsHierarchyViewer_test_time_format=\ ({0} s)
+UIUpdater_update_ui_job=Update C/C++ Tests Runner
+UIUpdater_view_caption_format={0} - {1}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ActionsMessages.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ActionsMessages.java
new file mode 100644
index 0000000000..1f5097b1c4
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ActionsMessages.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * 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.actions;
+
+import org.eclipse.osgi.util.NLS;
+
+public class ActionsMessages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.cdt.testsrunner.internal.ui.view.actions.ActionsMessages"; //$NON-NLS-1$
+ public static String CopySelectedMessagesAction_text;
+ public static String CopySelectedMessagesAction_tooltip;
+ public static String CopySelectedTestsAction_text;
+ public static String CopySelectedTestsAction_tooltip;
+ public static String HistoryAction_dialog_button_remove;
+ public static String HistoryAction_dialog_button_remove_all;
+ public static String HistoryAction_dialog_limit_label;
+ public static String HistoryAction_dialog_limit_label_error;
+ public static String HistoryAction_dialog_list_title;
+ public static String HistoryAction_dialog_title;
+ public static String HistoryAction_history_item_clear_text;
+ public static String HistoryAction_history_item_show_text;
+ public static String HistoryAction_history_text;
+ public static String HistoryAction_history_tooltip;
+ public static String MessageLevelFilterAction_errors_text;
+ public static String MessageLevelFilterAction_errors_tooltip;
+ public static String MessageLevelFilterAction_infos_text;
+ public static String MessageLevelFilterAction_infos_tooltip;
+ public static String MessageLevelFilterAction_warnings_text;
+ public static String MessageLevelFilterAction_warnings_tooltip;
+ public static String MessagesOrderingAction_text;
+ public static String MessagesOrderingAction_tooltip;
+ public static String OpenInEditorAction_text;
+ public static String OpenInEditorAction_tooltip;
+ public static String RedebugSelectedAction_text;
+ public static String RedebugSelectedAction_tooltip;
+ public static String RerunAction_text;
+ public static String RerunAction_tooltip;
+ public static String RerunSelectedAction_text;
+ public static String RerunSelectedAction_tooltip;
+ public static String ScrollLockAction_name;
+ public static String ScrollLockAction_tooltip;
+ public static String ShowFailedOnlyAction_text;
+ public static String ShowFailedOnlyAction_tooltip;
+ public static String ShowFileNameOnlyAction_text;
+ public static String ShowFileNameOnlyAction_tooltip;
+ public static String ShowNextFailureAction_text;
+ public static String ShowNextFailureAction_tooltip;
+ public static String ShowPreviousFailureAction_text;
+ public static String ShowPreviousFailureAction_tooltip;
+ public static String ShowTestsInHierarchyAction_text;
+ public static String ShowTestsInHierarchyAction_tooltip;
+ public static String ShowTimeAction_text;
+ public static String ShowTimeAction_tooltip;
+ public static String StopAction_text;
+ public static String StopAction_tooltip;
+ public static String TestsHierarchyCollapseAllAction_text;
+ public static String TestsHierarchyCollapseAllAction_tooltip;
+ public static String TestsHierarchyExpandAllAction_text;
+ public static String TestsHierarchyExpandAllAction_tooltip;
+ public static String ToggleOrientationAction_automatic_text;
+ public static String ToggleOrientationAction_horizontal_text;
+ public static String ToggleOrientationAction_vertical_text;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, ActionsMessages.class);
+ }
+
+ private ActionsMessages() {
+ }
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ActionsMessages.properties b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ActionsMessages.properties
new file mode 100644
index 0000000000..267fc50cfe
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ActionsMessages.properties
@@ -0,0 +1,63 @@
+###############################################################################
+# 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 implementation
+###############################################################################
+CopySelectedMessagesAction_text=&Copy
+CopySelectedMessagesAction_tooltip=Copy Selected Messages To Clipboard
+CopySelectedTestsAction_text=&Copy
+CopySelectedTestsAction_tooltip=Copy Selected Test Names To Clipboard
+HistoryAction_dialog_button_remove=&Remove
+HistoryAction_dialog_button_remove_all=Remove &All
+HistoryAction_dialog_limit_label=&Maximum count of remembered test runs:
+HistoryAction_dialog_limit_label_error=Please enter a positive integer smaller than {0}.
+HistoryAction_dialog_list_title=Select a test run:
+HistoryAction_dialog_title=Test Runs
+HistoryAction_history_item_clear_text=&Clear Terminated
+HistoryAction_history_item_show_text=History...
+HistoryAction_history_text=History
+HistoryAction_history_tooltip=Test Run History...
+MessageLevelFilterAction_errors_text=&Errors
+MessageLevelFilterAction_errors_tooltip=Show error messages
+MessageLevelFilterAction_infos_text=&Infos
+MessageLevelFilterAction_infos_tooltip=Show information messages
+MessageLevelFilterAction_warnings_text=&Warnings
+MessageLevelFilterAction_warnings_tooltip=Show warning messages
+MessagesOrderingAction_text=Messages &Ordering
+MessagesOrderingAction_tooltip=Order Messages By Location, Skip Duplicates
+OpenInEditorAction_text=&Go to File
+OpenInEditorAction_tooltip=Show File Referred By the Message
+RedebugSelectedAction_text=&Debug
+RedebugSelectedAction_tooltip=Start Debug Session For Selected Tests
+RerunAction_text=Rerun
+RerunAction_tooltip=Rerun All Tests
+RerunSelectedAction_text=&Run
+RerunSelectedAction_tooltip=Rerun Selected Tests
+ScrollLockAction_name=Scroll Lock
+ScrollLockAction_tooltip=Scroll Lock
+ShowFailedOnlyAction_text=Show &Failures Only
+ShowFailedOnlyAction_tooltip=Show &Failures Only
+ShowFileNameOnlyAction_text=Show File Names Only
+ShowFileNameOnlyAction_tooltip=Show Only File Names in Message Locations
+ShowNextFailureAction_text=Next Failure
+ShowNextFailureAction_tooltip=Next Failed Test
+ShowPreviousFailureAction_text=Previous Failure
+ShowPreviousFailureAction_tooltip=Previous Failed Test
+ShowTestsInHierarchyAction_text=Show Tests in &Hierarchy
+ShowTestsInHierarchyAction_tooltip=Show Tests in Hierarchy
+ShowTimeAction_text=Show Execution &Time
+ShowTimeAction_tooltip=Show Execution Time
+StopAction_text=Stop
+StopAction_tooltip=Stop Test Run
+TestsHierarchyCollapseAllAction_text=C&ollapse All
+TestsHierarchyCollapseAllAction_tooltip=Collapse All Nodes
+TestsHierarchyExpandAllAction_text=&Expand All
+TestsHierarchyExpandAllAction_tooltip=Expand All Nodes
+ToggleOrientationAction_automatic_text=&Automatic
+ToggleOrientationAction_horizontal_text=&Horizontal
+ToggleOrientationAction_vertical_text=&Vertical
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/CopySelectedMessagesAction.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/CopySelectedMessagesAction.java
new file mode 100644
index 0000000000..bcad308fee
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/CopySelectedMessagesAction.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * 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.actions;
+
+
+import java.util.Iterator;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.ui.IWorkbenchCommandConstants;
+
+/**
+ * Copies the selected test messages to the clipboard.
+ */
+public class CopySelectedMessagesAction extends Action {
+
+ private Clipboard clipboard;
+ private TableViewer tableViewer;
+
+
+ public CopySelectedMessagesAction(TableViewer tableViewer, Clipboard clipboard) {
+ super(ActionsMessages.CopySelectedMessagesAction_text);
+ setToolTipText(ActionsMessages.CopySelectedMessagesAction_tooltip);
+ setActionDefinitionId(IWorkbenchCommandConstants.EDIT_COPY);
+ this.tableViewer = tableViewer;
+ this.clipboard = clipboard;
+ }
+
+ @Override
+ public void run() {
+ ITableLabelProvider labelProvider = (ITableLabelProvider)tableViewer.getLabelProvider();
+ IStructuredSelection selection = (IStructuredSelection)tableViewer.getSelection();
+ if (!selection.isEmpty()) {
+ StringBuilder sb = new StringBuilder();
+ boolean needEOL = false;
+ for (Iterator<?> it = selection.iterator(); it.hasNext();) {
+ Object item = it.next();
+ if (needEOL) {
+ sb.append(System.getProperty("line.separator")); //$NON-NLS-1$
+ } else {
+ needEOL = true;
+ }
+ sb.append(labelProvider.getColumnText(item, 0));
+ }
+ clipboard.setContents(
+ new String[]{ sb.toString() },
+ new Transfer[]{ TextTransfer.getInstance() });
+ }
+ }
+
+}
+
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/CopySelectedTestsAction.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/CopySelectedTestsAction.java
new file mode 100644
index 0000000000..488abcdbaf
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/CopySelectedTestsAction.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * 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.actions;
+
+
+import java.util.Iterator;
+
+import org.eclipse.cdt.testsrunner.internal.ui.view.TestPathUtils;
+import org.eclipse.cdt.testsrunner.model.ITestItem;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.ui.IWorkbenchCommandConstants;
+
+/**
+ * Copies the name of the selected test items (test suites or cases) to the
+ * clipboard.
+ */
+public class CopySelectedTestsAction extends Action {
+
+ private TreeViewer treeViewer;
+ private Clipboard clipboard;
+
+
+ public CopySelectedTestsAction(TreeViewer treeViewer, Clipboard clipboard) {
+ super(ActionsMessages.CopySelectedTestsAction_text);
+ setToolTipText(ActionsMessages.CopySelectedTestsAction_tooltip);
+ setActionDefinitionId(IWorkbenchCommandConstants.EDIT_COPY);
+ this.treeViewer = treeViewer;
+ this.clipboard = clipboard;
+ }
+
+ @Override
+ public void run() {
+ IStructuredSelection selection = (IStructuredSelection)treeViewer.getSelection();
+ if (!selection.isEmpty()) {
+ StringBuilder sb = new StringBuilder();
+ boolean needEOL = false;
+ for (Iterator<?> it = selection.iterator(); it.hasNext();) {
+ if (needEOL) {
+ sb.append(System.getProperty("line.separator")); //$NON-NLS-1$
+ } else {
+ needEOL = true;
+ }
+ sb.append(TestPathUtils.getTestItemPath((ITestItem)it.next()));
+ }
+ clipboard.setContents(
+ new String[]{ sb.toString() },
+ new Transfer[]{ TextTransfer.getInstance() });
+ }
+ }
+
+}
+
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/HistoryDropDownAction.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/HistoryDropDownAction.java
new file mode 100644
index 0000000000..788879ef15
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/HistoryDropDownAction.java
@@ -0,0 +1,448 @@
+/*******************************************************************************
+ * 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.actions;
+
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.cdt.internal.ui.dialogs.StatusInfo;
+import org.eclipse.cdt.internal.ui.wizards.dialogfields.DialogField;
+import org.eclipse.cdt.internal.ui.wizards.dialogfields.IDialogFieldListener;
+import org.eclipse.cdt.internal.ui.wizards.dialogfields.IListAdapter;
+import org.eclipse.cdt.internal.ui.wizards.dialogfields.LayoutUtil;
+import org.eclipse.cdt.internal.ui.wizards.dialogfields.ListDialogField;
+import org.eclipse.cdt.internal.ui.wizards.dialogfields.StringDialogField;
+import org.eclipse.cdt.testsrunner.internal.TestsRunnerPlugin;
+import org.eclipse.cdt.testsrunner.internal.model.TestingSessionsManager;
+import org.eclipse.cdt.testsrunner.model.ITestingSession;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IMenuCreator;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.dialogs.StatusDialog;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Shows the testing sessions history in drop down list.
+ */
+public class HistoryDropDownAction extends Action {
+
+ /**
+ * The dialog for testing sessions history management. Allows to browse,
+ * activate and remove the selected testing sessions and to set testing
+ * sessions history size limit.
+ */
+ private class HistoryListDialog extends StatusDialog {
+
+ /** Max value for the history size limit that may be set via the dialog. */
+ private static final int MAX_HISTORY_SIZE_LIMIT = 100;
+
+ /** Testing sessions history list. */
+ private ListDialogField<ITestingSession> historyList;
+
+ /** String field that allows to set testing sessions history size limit. */
+ private StringDialogField historySizeLimitField;
+
+ /** Currently set testing sessions history size limit. */
+ private int historySizeLimit;
+
+ /** The currently selected active testing session. */
+ private ITestingSession resultActiveSession;
+
+
+ /**
+ * Label provider for the dialog testing sessions list.
+ */
+ private final class TestRunLabelProvider extends LabelProvider {
+
+ @Override
+ public String getText(Object element) {
+ return ((ITestingSession)element).getName();
+ }
+
+ }
+
+
+ private HistoryListDialog(Shell shell) {
+ super(shell);
+ setHelpAvailable(false);
+ setTitle(ActionsMessages.HistoryAction_dialog_title);
+
+ createHistoryList();
+ createHistorySizeLimitField();
+ }
+
+ @Override
+ protected boolean isResizable() {
+ return true;
+ }
+
+ /**
+ * Fills the testing sessions history list.
+ */
+ private void createHistoryList() {
+ IListAdapter<ITestingSession> adapter = new IListAdapter<ITestingSession>() {
+ @Override
+ public void customButtonPressed(ListDialogField<ITestingSession> field, int index) {
+ doCustomButtonPressed(index);
+ }
+ @Override
+ public void selectionChanged(ListDialogField<ITestingSession> field) {
+ doSelectionChanged();
+ }
+
+ @Override
+ public void doubleClicked(ListDialogField<ITestingSession> field) {
+ doDoubleClicked();
+ }
+ };
+ String[] buttonLabels = new String[] { ActionsMessages.HistoryAction_dialog_button_remove, ActionsMessages.HistoryAction_dialog_button_remove_all };
+ LabelProvider labelProvider = new TestRunLabelProvider();
+ historyList = new ListDialogField<ITestingSession>(adapter, buttonLabels, labelProvider);
+ historyList.setLabelText(ActionsMessages.HistoryAction_dialog_list_title);
+
+ historyList.setElements(testingSessionsManager.getSessions());
+ Object currentEntry = testingSessionsManager.getActiveSession();
+ ISelection sel = (currentEntry != null) ? new StructuredSelection(currentEntry) : new StructuredSelection();
+ historyList.selectElements(sel);
+ }
+
+ /**
+ * Initializes history size limit field of the dialog.
+ */
+ private void createHistorySizeLimitField() {
+ historySizeLimitField = new StringDialogField();
+ historySizeLimitField.setLabelText(ActionsMessages.HistoryAction_dialog_limit_label);
+ historySizeLimitField.setDialogFieldListener(new IDialogFieldListener() {
+ @Override
+ public void dialogFieldChanged(DialogField field) {
+ String maxString = historySizeLimitField.getText();
+ boolean valid;
+ try {
+ historySizeLimit = Integer.parseInt(maxString);
+ valid = historySizeLimit > 0 && historySizeLimit < MAX_HISTORY_SIZE_LIMIT;
+ } catch (NumberFormatException e) {
+ valid = false;
+ }
+ IStatus status = valid ? StatusInfo.OK_STATUS : new StatusInfo(IStatus.ERROR,
+ MessageFormat.format(ActionsMessages.HistoryAction_dialog_limit_label_error,
+ Integer.toString(MAX_HISTORY_SIZE_LIMIT)
+ )
+ );
+ updateStatus(status);
+ }
+ });
+ historySizeLimitField.setText(Integer.toString(testingSessionsManager.getHistorySizeLimit()));
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ initializeDialogUnits(parent);
+
+ Composite composite = (Composite) super.createDialogArea(parent);
+
+ Composite inner = new Composite(composite, SWT.NONE);
+ inner.setLayoutData(new GridData(GridData.FILL_BOTH));
+ inner.setFont(composite.getFont());
+
+ LayoutUtil.doDefaultLayout(inner, new DialogField[] { historyList, new org.eclipse.cdt.internal.ui.wizards.dialogfields.Separator() }, true);
+ LayoutUtil.setHeightHint(historyList.getListControl(null), convertHeightInCharsToPixels(12));
+ LayoutUtil.setHorizontalGrabbing(historyList.getListControl(null));
+
+ Composite additionalControls = new Composite(inner, SWT.NONE);
+ additionalControls.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ LayoutUtil.doDefaultLayout(additionalControls, new DialogField[] { historySizeLimitField }, false);
+ LayoutUtil.setHorizontalGrabbing(historySizeLimitField.getTextControl(null));
+
+ applyDialogFont(composite);
+ return composite;
+ }
+
+ /**
+ * Processes dialog custom button pressing.
+ *
+ * @param index index of the button
+ */
+ private void doCustomButtonPressed(int index) {
+ switch (index) {
+ case 0: // remove
+ historyList.removeElements(historyList.getSelectedElements());
+ historyList.selectFirstElement();
+ break;
+
+ case 1: // remove all
+ historyList.removeAllElements();
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Processes double click on the item in dialog.
+ */
+ private void doDoubleClicked() {
+ okPressed();
+ }
+
+ /**
+ * Processes the selection change in the dialog
+ */
+ private void doSelectionChanged() {
+ List<ITestingSession> selected = historyList.getSelectedElements();
+ if (selected.size() >= 1) {
+ resultActiveSession = selected.get(0);
+ } else {
+ resultActiveSession = null;
+ }
+ historyList.enableButton(0, selected.size() != 0);
+ }
+
+ /**
+ * Provides access to the active session currently selected by user.
+ *
+ * @return testing session
+ */
+ public ITestingSession getResultActiveSession() {
+ return resultActiveSession;
+ }
+
+ /**
+ * Provides access to the testing sessions history list edited by user.
+ *
+ * @return list of testing sessions
+ */
+ public List<ITestingSession> getResultSessions() {
+ return historyList.getElements();
+ }
+
+ /**
+ * Provides access to value of history size limit specified by user.
+ *
+ * @return history size limit
+ */
+ public int getResultHistorySizeLimit() {
+ return historySizeLimit;
+ }
+ }
+
+
+ /**
+ * Represents a testing sessions history item.
+ */
+ private class HistoryAction extends Action {
+
+ /** Testing session of the history item. */
+ private final ITestingSession testingSession;
+
+ public HistoryAction(int testingSessionIndex, ITestingSession testingSession) {
+ super("", AS_RADIO_BUTTON); //$NON-NLS-1$
+ this.testingSession = testingSession;
+
+ // Generate action text
+ String label = testingSession.getName();
+ if (testingSessionIndex < 10) {
+ // Add the numerical accelerator
+ label = new StringBuilder().append('&').append(testingSessionIndex).append(' ').append(label).toString();
+ }
+ setText(label);
+ }
+
+ @Override
+ public void run() {
+ if (isChecked()) {
+ testingSessionsManager.setActiveSession(testingSession);
+ }
+ }
+ }
+
+
+ /**
+ * Provides access to the history list management dialog.
+ */
+ private class HistoryListAction extends Action {
+
+ public HistoryListAction() {
+ super(ActionsMessages.HistoryAction_history_item_show_text, AS_RADIO_BUTTON);
+ }
+
+ @Override
+ public void run() {
+ if (isChecked()) {
+ runHistoryDialog();
+ }
+ }
+ }
+
+
+ /**
+ * Removes the terminated testing sessions from the history list.
+ */
+ private class ClearAction extends Action {
+
+ public ClearAction() {
+ setText(ActionsMessages.HistoryAction_history_item_clear_text);
+
+ boolean enabled = false;
+ for (ITestingSession testingSession : testingSessionsManager.getSessions()) {
+ if (testingSession.isFinished()) {
+ enabled = true;
+ break;
+ }
+ }
+ setEnabled(enabled);
+ }
+
+ @Override
+ public void run() {
+ List<ITestingSession> remainingSessions = new ArrayList<ITestingSession>();
+ for (ITestingSession testingSession : testingSessionsManager.getSessions()) {
+ if (!testingSession.isFinished()) {
+ remainingSessions.add(testingSession);
+ }
+ }
+
+ ITestingSession newActiveSession = remainingSessions.isEmpty() ? null : remainingSessions.get(0);
+ testingSessionsManager.setActiveSession(newActiveSession);
+ testingSessionsManager.setSessions(remainingSessions);
+ }
+ }
+
+
+ /**
+ * Represents the testing sessions history menu factory.
+ */
+ private class HistoryMenuCreator implements IMenuCreator {
+
+ @Override
+ public Menu getMenu(Menu parent) {
+ return null;
+ }
+
+ @Override
+ public Menu getMenu(Control parent) {
+ if (menu != null) {
+ menu.dispose();
+ }
+ final MenuManager manager = new MenuManager();
+ manager.setRemoveAllWhenShown(true);
+ manager.addMenuListener(new IMenuListener() {
+ @Override
+ public void menuAboutToShow(IMenuManager manager2) {
+ boolean checkOthers = addHistoryItems(manager2);
+
+ Action others = new HistoryListAction();
+ others.setChecked(checkOthers);
+ manager2.add(others);
+ manager2.add(new Separator());
+ manager2.add(new ClearAction());
+ }
+
+ private boolean addHistoryItems(IMenuManager manager) {
+ boolean checkOthers = true;
+
+ int sessionsCount = testingSessionsManager.getSessionsCount();
+ if (sessionsCount == 0) {
+ return false;
+ }
+ int menuItemsCount = Math.min(sessionsCount, RESULTS_IN_DROP_DOWN_MENU);
+
+ ITestingSession activeSession = testingSessionsManager.getActiveSession();
+ int testingSessionIndex = 0;
+ for (ITestingSession testingSession : testingSessionsManager.getSessions()) {
+ if (testingSessionIndex >= menuItemsCount) {
+ break;
+ }
+ HistoryAction action = new HistoryAction(testingSessionIndex, testingSession);
+ boolean check = (testingSession == activeSession);
+ action.setChecked(check);
+ if (check) {
+ checkOthers = false;
+ }
+ manager.add(action);
+ testingSessionIndex++;
+ }
+
+ return checkOthers;
+ }
+ });
+
+ menu = manager.createContextMenu(parent);
+ return menu;
+ }
+
+ @Override
+ public void dispose() {
+ if (menu != null) {
+ menu.dispose();
+ menu = null;
+ }
+ }
+ }
+
+
+ /** Defines how many history items should be showed in drop down history menu. */
+ public static final int RESULTS_IN_DROP_DOWN_MENU = 10;
+
+ /** Accessor to the Testing Sessions Plug-in Manager. */
+ private TestingSessionsManager testingSessionsManager;
+
+ /** Required for the history dialog. */
+ private Shell shell;
+
+ /** Drop down menu */
+ private Menu menu;
+
+
+ public HistoryDropDownAction(TestingSessionsManager testingSessionsManager, Shell shell) {
+ super(ActionsMessages.HistoryAction_history_text);
+ setToolTipText(ActionsMessages.HistoryAction_history_tooltip);
+ setDisabledImageDescriptor(TestsRunnerPlugin.getImageDescriptor("dlcl16/history_list.gif")); //$NON-NLS-1$
+ setHoverImageDescriptor(TestsRunnerPlugin.getImageDescriptor("elcl16/history_list.gif")); //$NON-NLS-1$
+ setImageDescriptor(TestsRunnerPlugin.getImageDescriptor("elcl16/history_list.gif")); //$NON-NLS-1$
+ this.testingSessionsManager = testingSessionsManager;
+ this.shell = shell;
+ setMenuCreator(new HistoryMenuCreator());
+ }
+
+ /**
+ * Shows the testing sessions history dialog.
+ */
+ private void runHistoryDialog() {
+ HistoryListDialog dialog = new HistoryListDialog(shell);
+ if (dialog.open() == Window.OK) {
+ testingSessionsManager.setHistorySizeLimit(dialog.getResultHistorySizeLimit());
+ testingSessionsManager.setActiveSession(dialog.getResultActiveSession());
+ testingSessionsManager.setSessions(dialog.getResultSessions());
+ }
+ }
+
+ @Override
+ public void run() {
+ runHistoryDialog();
+ }
+
+}
+
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/MessageLevelFilterAction.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/MessageLevelFilterAction.java
new file mode 100644
index 0000000000..5eba442426
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/MessageLevelFilterAction.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * 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.actions;
+
+
+import org.eclipse.cdt.testsrunner.internal.ui.view.MessagesViewer;
+import org.eclipse.jface.action.Action;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * Represents a filtering action for test messages. It is used for all kind of
+ * filters (info, warnings, errors).
+ */
+public class MessageLevelFilterAction extends Action {
+
+ private MessagesViewer.LevelFilter levelFilter;
+ private MessagesViewer messagesViewer;
+
+
+ public MessageLevelFilterAction(MessagesViewer messagePanel, MessagesViewer.LevelFilter levelFilter, boolean checked) {
+ super("", AS_CHECK_BOX); //$NON-NLS-1$
+ this.levelFilter = levelFilter;
+ this.messagesViewer = messagePanel;
+ if (levelFilter == MessagesViewer.LevelFilter.Info) {
+ setText(ActionsMessages.MessageLevelFilterAction_infos_text);
+ setToolTipText(ActionsMessages.MessageLevelFilterAction_infos_tooltip);
+ } else if (levelFilter == MessagesViewer.LevelFilter.Warning) {
+ setText(ActionsMessages.MessageLevelFilterAction_warnings_text);
+ setToolTipText(ActionsMessages.MessageLevelFilterAction_warnings_tooltip);
+ } else if (levelFilter == MessagesViewer.LevelFilter.Error) {
+ setText(ActionsMessages.MessageLevelFilterAction_errors_text);
+ setToolTipText(ActionsMessages.MessageLevelFilterAction_errors_tooltip);
+ }
+ setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(levelFilter.getImageId()));
+ setChecked(checked);
+ if (checked) {
+ messagePanel.addLevelFilter(levelFilter, false);
+ }
+ }
+
+ @Override
+ public void run() {
+ if (isChecked()) {
+ messagesViewer.addLevelFilter(levelFilter, true);
+ } else {
+ messagesViewer.removeLevelFilter(levelFilter);
+ }
+ }
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/MessagesOrderingAction.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/MessagesOrderingAction.java
new file mode 100644
index 0000000000..b7e032a07b
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/MessagesOrderingAction.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * 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.actions;
+
+
+import org.eclipse.cdt.testsrunner.internal.TestsRunnerPlugin;
+import org.eclipse.cdt.testsrunner.internal.ui.view.MessagesViewer;
+import org.eclipse.jface.action.Action;
+
+/**
+ * Turns on/off the messages ordering by location.
+ */
+public class MessagesOrderingAction extends Action {
+
+ private MessagesViewer messagesViewer;
+
+
+ public MessagesOrderingAction(MessagesViewer messagesViewer) {
+ super("", AS_CHECK_BOX); //$NON-NLS-1$
+ this.messagesViewer = messagesViewer;
+ setText(ActionsMessages.MessagesOrderingAction_text);
+ setToolTipText(ActionsMessages.MessagesOrderingAction_tooltip);
+ setDisabledImageDescriptor(TestsRunnerPlugin.getImageDescriptor("dlcl16/sort.gif")); //$NON-NLS-1$
+ setHoverImageDescriptor(TestsRunnerPlugin.getImageDescriptor("elcl16/sort.gif")); //$NON-NLS-1$
+ setImageDescriptor(TestsRunnerPlugin.getImageDescriptor("elcl16/sort.gif")); //$NON-NLS-1$
+ setChecked(messagesViewer.getOrderingMode());
+ }
+
+ @Override
+ public void run() {
+ messagesViewer.setOrderingMode(isChecked());
+ }
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/OpenInEditorAction.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/OpenInEditorAction.java
new file mode 100644
index 0000000000..5438d5b38d
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/OpenInEditorAction.java
@@ -0,0 +1,181 @@
+/*******************************************************************************
+ * 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.actions;
+
+
+import java.net.URI;
+
+import org.eclipse.cdt.debug.ui.CDebugUIPlugin;
+import org.eclipse.cdt.debug.core.CDebugCorePlugin;
+import org.eclipse.cdt.testsrunner.internal.TestsRunnerPlugin;
+import org.eclipse.cdt.testsrunner.internal.model.TestingSessionsManager;
+import org.eclipse.cdt.testsrunner.model.ITestLocation;
+import org.eclipse.cdt.testsrunner.model.ITestMessage;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.filesystem.URIUtil;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.model.IPersistableSourceLocator;
+import org.eclipse.debug.core.model.ISourceLocator;
+import org.eclipse.debug.core.sourcelookup.AbstractSourceLookupDirector;
+import org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant;
+import org.eclipse.debug.core.sourcelookup.containers.LocalFileStorage;
+import org.eclipse.debug.ui.DebugUITools;
+import org.eclipse.debug.ui.IDebugModelPresentation;
+import org.eclipse.debug.ui.sourcelookup.ISourceLookupResult;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.ide.IDE;
+import org.eclipse.ui.texteditor.IDocumentProvider;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+/**
+ * Opens the editor in place where the currently selected message is pointed to.
+ */
+public class OpenInEditorAction extends Action {
+
+ private TableViewer messagesViewer;
+ private TestingSessionsManager testingSessionsManager;
+ private IWorkbench workbench;
+
+
+ public OpenInEditorAction(TableViewer tableViewer, TestingSessionsManager testingSessionsManager, IWorkbench workbench) {
+ super(ActionsMessages.OpenInEditorAction_text);
+ this.messagesViewer = tableViewer;
+ this.testingSessionsManager = testingSessionsManager;
+ this.workbench = workbench;
+ setToolTipText(ActionsMessages.OpenInEditorAction_tooltip);
+ }
+
+ @Override
+ public void run() {
+ Object selectedObject = ((IStructuredSelection)messagesViewer.getSelection()).getFirstElement();
+ if (selectedObject != null && selectedObject instanceof ITestMessage) {
+ ITestLocation messageLocation = ((ITestMessage)selectedObject).getLocation();
+ if (messageLocation != null) {
+ ILaunch launch = testingSessionsManager.getActiveSession().getLaunch();
+ lookupSource(messageLocation, launch);
+ }
+ }
+ }
+
+ // NOTE: This method is copied from Linux Tools Project (http://www.eclipse.org/linuxtools).
+ // Valgrind Support Plugin is implementing similar functionality so it is just reused.
+ // See also org.eclipse.linuxtools.valgrind.ui/src/org/eclipse/linuxtools/internal/valgrind/ui/CoreMessagesViewer.java
+ private void lookupSource(ITestLocation messageLocation, ILaunch launch) {
+ ISourceLocator locator = launch.getSourceLocator();
+ if (locator instanceof AbstractSourceLookupDirector) {
+ AbstractSourceLookupDirector director = (AbstractSourceLookupDirector) locator;
+ ISourceLookupParticipant[] participants = director.getParticipants();
+ if (participants.length == 0) {
+ // source locator likely disposed, try recreating it
+ IPersistableSourceLocator sourceLocator;
+ ILaunchConfiguration config = launch.getLaunchConfiguration();
+ if (config != null) {
+ try {
+ String id = config.getAttribute(ILaunchConfiguration.ATTR_SOURCE_LOCATOR_ID, (String) null);
+ if (id == null) {
+ sourceLocator = CDebugUIPlugin.createDefaultSourceLocator();
+ sourceLocator.initializeDefaults(config);
+ } else {
+ sourceLocator = DebugPlugin.getDefault().getLaunchManager().newSourceLocator(id);
+ String memento = config.getAttribute(ILaunchConfiguration.ATTR_SOURCE_LOCATOR_MEMENTO, (String) null);
+ if (memento == null) {
+ sourceLocator.initializeDefaults(config);
+ } else {
+ sourceLocator.initializeFromMemento(memento);
+ }
+ }
+
+ // replace old source locator
+ locator = sourceLocator;
+ launch.setSourceLocator(sourceLocator);
+ } catch (CoreException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ ISourceLookupResult result = DebugUITools.lookupSource(messageLocation.getFile(), locator);
+ try {
+ openEditorAndSelect(result, messageLocation.getLine());
+ } catch (PartInitException e) {
+ TestsRunnerPlugin.log(e);
+ } catch (BadLocationException e) {
+ TestsRunnerPlugin.log(e);
+ }
+ }
+
+ // NOTE: This method is copied from Linux Tools Project (http://www.eclipse.org/linuxtools).
+ // Valgrind Support Plugin is implementing similar functionality so it is just reused.
+ // See also org.eclipse.linuxtools.valgrind.ui/src/org/eclipse/linuxtools/internal/valgrind/ui/CoreMessagesViewer.java
+ private void openEditorAndSelect(ISourceLookupResult result, int line) throws PartInitException, BadLocationException {
+ IEditorInput input = result.getEditorInput();
+ String editorID = result.getEditorId();
+
+ if (input == null || editorID == null) {
+ // Consult the CDT DebugModelPresentation
+ Object sourceElement = result.getSourceElement();
+ if (sourceElement != null) {
+ // Resolve IResource in case we get a LocalFileStorage object
+ if (sourceElement instanceof LocalFileStorage) {
+ IPath filePath = ((LocalFileStorage) sourceElement).getFullPath();
+ URI fileURI = URIUtil.toURI(filePath);
+ IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
+ IFile[] files = root.findFilesForLocationURI(fileURI);
+ if (files.length > 0) {
+ // Take the first match
+ sourceElement = files[0];
+ }
+ }
+
+ IDebugModelPresentation pres = DebugUITools.newDebugModelPresentation(CDebugCorePlugin.getUniqueIdentifier());
+ input = pres.getEditorInput(sourceElement);
+ editorID = pres.getEditorId(input, sourceElement);
+ pres.dispose();
+ }
+ }
+ if (input != null && editorID != null) {
+ // Open the editor
+ IWorkbenchPage activePage = workbench.getActiveWorkbenchWindow().getActivePage();
+
+ IEditorPart editor = IDE.openEditor(activePage, input, editorID);
+ // Select the line
+ if (editor instanceof ITextEditor) {
+ ITextEditor textEditor = (ITextEditor) editor;
+
+ if (line > 0) {
+ IDocumentProvider provider = textEditor.getDocumentProvider();
+ IDocument document = provider.getDocument(textEditor.getEditorInput());
+
+ IRegion lineRegion = document.getLineInformation(line - 1); //zero-indexed
+ textEditor.selectAndReveal(lineRegion.getOffset(), lineRegion.getLength());
+ }
+ }
+ }
+ }
+
+}
+
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/RedebugSelectedAction.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/RedebugSelectedAction.java
new file mode 100644
index 0000000000..a6b670dbbb
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/RedebugSelectedAction.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * 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.actions;
+
+
+import org.eclipse.cdt.testsrunner.model.ITestingSession;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.jface.viewers.TreeViewer;
+
+/**
+ * Launches the new debug session for the currently selected items of test
+ * hierarchy.
+ */
+public class RedebugSelectedAction extends RelaunchSelectedAction {
+
+ public RedebugSelectedAction(ITestingSession testingSession, TreeViewer treeViewer) {
+ super(testingSession, treeViewer);
+ setText(ActionsMessages.RedebugSelectedAction_text);
+ setToolTipText(ActionsMessages.RedebugSelectedAction_tooltip);
+ }
+
+ @Override
+ protected String getLaunchMode() {
+ return ILaunchManager.DEBUG_MODE;
+ }
+
+}
+
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/RelaunchSelectedAction.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/RelaunchSelectedAction.java
new file mode 100644
index 0000000000..b40e0d6ab8
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/RelaunchSelectedAction.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * 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.actions;
+
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.cdt.testsrunner.internal.TestsRunnerPlugin;
+import org.eclipse.cdt.testsrunner.internal.launcher.ITestsLaunchConfigurationConstants;
+import org.eclipse.cdt.testsrunner.internal.ui.view.TestPathUtils;
+import org.eclipse.cdt.testsrunner.model.ITestItem;
+import org.eclipse.cdt.testsrunner.model.ITestingSession;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.ui.DebugUITools;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.TreeViewer;
+
+/**
+ * Launches the new run or debug session for the currently selected items of
+ * test hierarchy.
+ */
+public abstract class RelaunchSelectedAction extends Action {
+
+ private ITestingSession testingSession;
+ private TreeViewer treeViewer;
+
+
+ public RelaunchSelectedAction(ITestingSession testingSession, TreeViewer treeViewer) {
+ this.testingSession = testingSession;
+ this.treeViewer = treeViewer;
+ }
+
+ /**
+ * Returns the launch mode that should be use to run selected test item.
+ *
+ * @return launch mode
+ */
+ protected abstract String getLaunchMode();
+
+ @Override
+ public void run() {
+ if (testingSession != null) {
+ try {
+ ILaunch launch = testingSession.getLaunch();
+ ILaunchConfigurationWorkingCopy launchConf = launch.getLaunchConfiguration().getWorkingCopy();
+ List<String> testsFilterAttr = Arrays.asList(TestPathUtils.packTestPaths(getSelectedTestItems()));
+ launchConf.setAttribute(ITestsLaunchConfigurationConstants.ATTR_TESTS_FILTER, testsFilterAttr);
+ DebugUITools.launch(launchConf, getLaunchMode());
+ return;
+ } catch (CoreException e) {
+ TestsRunnerPlugin.log(e);
+ }
+ }
+ setEnabled(false);
+ }
+
+ /**
+ * Returns the currently selected items of test hierarchy.
+ *
+ * @return array of test items
+ */
+ private ITestItem[] getSelectedTestItems() {
+ IStructuredSelection selection = (IStructuredSelection)treeViewer.getSelection();
+ ITestItem[] result = new ITestItem[selection.size()];
+ int resultIndex = 0;
+ for (Iterator<?> it = selection.iterator(); it.hasNext();) {
+ result[resultIndex] = (ITestItem)it.next();
+ ++resultIndex;
+ }
+ return result;
+ }
+
+ /**
+ * Sets actual testing session.
+ *
+ * @param testingSession testing session
+ */
+ public void setTestingSession(ITestingSession testingSession) {
+ this.testingSession = testingSession;
+ }
+
+}
+
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/RerunAction.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/RerunAction.java
new file mode 100644
index 0000000000..54da1a7163
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/RerunAction.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * 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.actions;
+
+
+import org.eclipse.cdt.testsrunner.internal.TestsRunnerPlugin;
+import org.eclipse.cdt.testsrunner.internal.model.TestingSessionsManager;
+import org.eclipse.cdt.testsrunner.model.ITestingSession;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.ui.DebugUITools;
+import org.eclipse.jface.action.Action;
+
+/**
+ * Restarts the last testing session (it may be run or debug session).
+ */
+public class RerunAction extends Action {
+
+ private TestingSessionsManager testingSessionsManager;
+
+
+ public RerunAction(TestingSessionsManager testingSessionsManager) {
+ super(ActionsMessages.RerunAction_text);
+ setToolTipText(ActionsMessages.RerunAction_tooltip);
+ setDisabledImageDescriptor(TestsRunnerPlugin.getImageDescriptor("dlcl16/rerun.gif")); //$NON-NLS-1$
+ setHoverImageDescriptor(TestsRunnerPlugin.getImageDescriptor("elcl16/rerun.gif")); //$NON-NLS-1$
+ setImageDescriptor(TestsRunnerPlugin.getImageDescriptor("elcl16/rerun.gif")); //$NON-NLS-1$
+ this.testingSessionsManager = testingSessionsManager;
+ }
+
+ @Override
+ public void run() {
+ ITestingSession activeSession = testingSessionsManager.getActiveSession();
+ if (activeSession != null) {
+ ILaunch launch = activeSession.getLaunch();
+ DebugUITools.launch(launch.getLaunchConfiguration(), launch.getLaunchMode());
+ } else {
+ setEnabled(false);
+ }
+ }
+
+}
+
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/RerunSelectedAction.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/RerunSelectedAction.java
new file mode 100644
index 0000000000..29d57942e4
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/RerunSelectedAction.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * 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.actions;
+
+
+import org.eclipse.cdt.testsrunner.model.ITestingSession;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.jface.viewers.TreeViewer;
+
+/**
+ * Launches the new run session for the currently selected items of test
+ * hierarchy.
+ */
+public class RerunSelectedAction extends RelaunchSelectedAction {
+
+ public RerunSelectedAction(ITestingSession testingSession, TreeViewer treeViewer) {
+ super(testingSession, treeViewer);
+ setText(ActionsMessages.RerunSelectedAction_text);
+ setToolTipText(ActionsMessages.RerunSelectedAction_tooltip);
+ }
+
+ @Override
+ protected String getLaunchMode() {
+ return ILaunchManager.RUN_MODE;
+ }
+
+}
+
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ScrollLockAction.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ScrollLockAction.java
new file mode 100644
index 0000000000..841b8ec408
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ScrollLockAction.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * 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.actions;
+
+
+import org.eclipse.cdt.testsrunner.internal.TestsRunnerPlugin;
+import org.eclipse.cdt.testsrunner.internal.ui.view.UIUpdater;
+import org.eclipse.jface.action.Action;
+
+/**
+ * Toggles the auto-scroll for tests hierarchy tree.
+ */
+public class ScrollLockAction extends Action {
+
+ private UIUpdater modelSyncronizer;
+
+
+ public ScrollLockAction(UIUpdater modelSyncronizer) {
+ super(ActionsMessages.ScrollLockAction_name);
+ this.modelSyncronizer = modelSyncronizer;
+ setToolTipText(ActionsMessages.ScrollLockAction_tooltip);
+ setDisabledImageDescriptor(TestsRunnerPlugin.getImageDescriptor("dlcl16/scroll_lock.gif")); //$NON-NLS-1$
+ setHoverImageDescriptor(TestsRunnerPlugin.getImageDescriptor("elcl16/scroll_lock.gif")); //$NON-NLS-1$
+ setImageDescriptor(TestsRunnerPlugin.getImageDescriptor("elcl16/scroll_lock.gif")); //$NON-NLS-1$
+ setChecked(!this.modelSyncronizer.getAutoScroll());
+ }
+
+ @Override
+ public void run() {
+ modelSyncronizer.setAutoScroll(!isChecked());
+ }
+}
+
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ShowFailedOnlyAction.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ShowFailedOnlyAction.java
new file mode 100644
index 0000000000..13d2ee5f5c
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ShowFailedOnlyAction.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * 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.actions;
+
+
+import org.eclipse.cdt.testsrunner.internal.TestsRunnerPlugin;
+import org.eclipse.cdt.testsrunner.internal.ui.view.ResultsPanel;
+import org.eclipse.jface.action.Action;
+
+/**
+ * Toggles the filter for the passed test items.
+ */
+public class ShowFailedOnlyAction extends Action {
+
+ private ResultsPanel resultsPanel;
+
+
+ public ShowFailedOnlyAction(ResultsPanel resultsPanel) {
+ super("", AS_CHECK_BOX); //$NON-NLS-1$
+ this.resultsPanel = resultsPanel;
+ setText(ActionsMessages.ShowFailedOnlyAction_text);
+ setToolTipText(ActionsMessages.ShowFailedOnlyAction_tooltip);
+ setImageDescriptor(TestsRunnerPlugin.getImageDescriptor("obj16/show_failed_only.gif")); //$NON-NLS-1$
+ setChecked(resultsPanel.getShowFailedOnly());
+ }
+
+ @Override
+ public void run() {
+ resultsPanel.setShowFailedOnly(isChecked());
+ }
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ShowFileNameOnlyAction.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ShowFileNameOnlyAction.java
new file mode 100644
index 0000000000..a9fdf4914d
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ShowFileNameOnlyAction.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * 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.actions;
+
+
+import org.eclipse.cdt.testsrunner.internal.ui.view.MessagesViewer;
+import org.eclipse.jface.action.Action;
+
+/**
+ * Toggles the short or long view for file paths in message locations. The short
+ * view shows only the file names instead of full paths.
+ */
+public class ShowFileNameOnlyAction extends Action {
+
+ private MessagesViewer messagesViewer;
+
+
+ public ShowFileNameOnlyAction(MessagesViewer messagesViewer) {
+ super(ActionsMessages.ShowFileNameOnlyAction_text, AS_CHECK_BOX);
+ this.messagesViewer = messagesViewer;
+ setToolTipText(ActionsMessages.ShowFileNameOnlyAction_tooltip);
+ setChecked(messagesViewer.getShowFileNameOnly());
+ }
+
+ @Override
+ public void run() {
+ messagesViewer.setShowFileNameOnly(isChecked());
+ }
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ShowNextFailureAction.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ShowNextFailureAction.java
new file mode 100644
index 0000000000..e337b03fca
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ShowNextFailureAction.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * 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.actions;
+
+
+import org.eclipse.cdt.testsrunner.internal.TestsRunnerPlugin;
+import org.eclipse.cdt.testsrunner.internal.ui.view.TestsHierarchyViewer;
+import org.eclipse.jface.action.Action;
+
+/**
+ * Looks for the next failed test case in tests hierarchy (corresponding to the
+ * currently selected one).
+ */
+public class ShowNextFailureAction extends Action {
+
+ private TestsHierarchyViewer testsHierarchyViewer;
+
+ public ShowNextFailureAction(TestsHierarchyViewer testsHierarchyViewer) {
+ super(ActionsMessages.ShowNextFailureAction_text);
+ this.testsHierarchyViewer = testsHierarchyViewer;
+ setToolTipText(ActionsMessages.ShowNextFailureAction_tooltip);
+ setDisabledImageDescriptor(TestsRunnerPlugin.getImageDescriptor("dlcl16/show_next.gif")); //$NON-NLS-1$
+ setHoverImageDescriptor(TestsRunnerPlugin.getImageDescriptor("elcl16/show_next.gif")); //$NON-NLS-1$
+ setImageDescriptor(TestsRunnerPlugin.getImageDescriptor("elcl16/show_next.gif")); //$NON-NLS-1$
+ }
+
+ @Override
+ public void run() {
+ testsHierarchyViewer.showNextFailure();
+ }
+}
+
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ShowPreviousFailureAction.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ShowPreviousFailureAction.java
new file mode 100644
index 0000000000..79c43502e7
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ShowPreviousFailureAction.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * 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.actions;
+
+
+import org.eclipse.cdt.testsrunner.internal.TestsRunnerPlugin;
+import org.eclipse.cdt.testsrunner.internal.ui.view.TestsHierarchyViewer;
+import org.eclipse.jface.action.Action;
+
+/**
+ * Looks for the previous failed test case in tests hierarchy (corresponding to the
+ * currently selected one).
+ */
+public class ShowPreviousFailureAction extends Action {
+
+ private TestsHierarchyViewer testsHierarchyViewer;
+
+ public ShowPreviousFailureAction(TestsHierarchyViewer testsHierarchyViewer) {
+ super(ActionsMessages.ShowPreviousFailureAction_text);
+ this.testsHierarchyViewer = testsHierarchyViewer;
+ setToolTipText(ActionsMessages.ShowPreviousFailureAction_tooltip);
+ setDisabledImageDescriptor(TestsRunnerPlugin.getImageDescriptor("dlcl16/show_previous.gif")); //$NON-NLS-1$
+ setHoverImageDescriptor(TestsRunnerPlugin.getImageDescriptor("elcl16/show_previous.gif")); //$NON-NLS-1$
+ setImageDescriptor(TestsRunnerPlugin.getImageDescriptor("elcl16/show_previous.gif")); //$NON-NLS-1$
+ }
+
+ @Override
+ public void run() {
+ testsHierarchyViewer.showPreviousFailure();
+ }
+}
+
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ShowTestsInHierarchyAction.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ShowTestsInHierarchyAction.java
new file mode 100644
index 0000000000..ab4869cedf
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ShowTestsInHierarchyAction.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * 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.actions;
+
+
+import org.eclipse.cdt.testsrunner.internal.TestsRunnerPlugin;
+import org.eclipse.cdt.testsrunner.internal.ui.view.TestsHierarchyViewer;
+import org.eclipse.jface.action.Action;
+
+/**
+ * Specifies whether tests hierarchy should be shown in hierarchical or flat
+ * view.
+ */
+public class ShowTestsInHierarchyAction extends Action {
+
+ private TestsHierarchyViewer testsHierarchyViewer;
+
+
+ public ShowTestsInHierarchyAction(TestsHierarchyViewer testsHierarchyViewer) {
+ super(ActionsMessages.ShowTestsInHierarchyAction_text, AS_CHECK_BOX);
+ this.testsHierarchyViewer = testsHierarchyViewer;
+ setToolTipText(ActionsMessages.ShowTestsInHierarchyAction_tooltip);
+ setChecked(testsHierarchyViewer.showTestsHierarchy());
+ setImageDescriptor(TestsRunnerPlugin.getImageDescriptor("elcl16/show_tests_hierarchy.gif")); //$NON-NLS-1$
+ }
+
+ @Override
+ public void run() {
+ testsHierarchyViewer.setShowTestsHierarchy(isChecked());
+ }
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ShowTimeAction.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ShowTimeAction.java
new file mode 100644
index 0000000000..c312bdcebb
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ShowTimeAction.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * 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.actions;
+
+
+import org.eclipse.cdt.testsrunner.internal.ui.view.TestsHierarchyViewer;
+import org.eclipse.jface.action.Action;
+
+/**
+ * Toggles the test execution time showing in tests hierarchy viewer.
+ */
+public class ShowTimeAction extends Action {
+
+ private TestsHierarchyViewer testsHierarchyViewer;
+
+
+ public ShowTimeAction(TestsHierarchyViewer testsHierarchyViewer) {
+ super(ActionsMessages.ShowTimeAction_text, AS_CHECK_BOX);
+ this.testsHierarchyViewer = testsHierarchyViewer;
+ setToolTipText(ActionsMessages.ShowTimeAction_tooltip);
+ setChecked(testsHierarchyViewer.showTime());
+ }
+
+ @Override
+ public void run() {
+ testsHierarchyViewer.setShowTime(isChecked());
+ }
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/StopAction.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/StopAction.java
new file mode 100644
index 0000000000..e6905998d6
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/StopAction.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * 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.actions;
+
+
+import org.eclipse.cdt.testsrunner.internal.TestsRunnerPlugin;
+import org.eclipse.cdt.testsrunner.internal.model.TestingSessionsManager;
+import org.eclipse.cdt.testsrunner.model.ITestingSession;
+import org.eclipse.jface.action.Action;
+
+/**
+ * Stops running of the active testing session.
+ */
+public class StopAction extends Action {
+
+ private TestingSessionsManager testingSessionsManager;
+
+
+ public StopAction(TestingSessionsManager testingSessionsManager) {
+ super(ActionsMessages.StopAction_text);
+ setToolTipText(ActionsMessages.StopAction_tooltip);
+ setDisabledImageDescriptor(TestsRunnerPlugin.getImageDescriptor("dlcl16/stop.gif")); //$NON-NLS-1$
+ setHoverImageDescriptor(TestsRunnerPlugin.getImageDescriptor("elcl16/stop.gif")); //$NON-NLS-1$
+ setImageDescriptor(TestsRunnerPlugin.getImageDescriptor("elcl16/stop.gif")); //$NON-NLS-1$
+ this.testingSessionsManager = testingSessionsManager;
+ }
+
+ @Override
+ public void run() {
+ ITestingSession activeSession = testingSessionsManager.getActiveSession();
+ if (activeSession != null) {
+ activeSession.stop();
+ }
+ setEnabled(false);
+ }
+
+}
+
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/TestsHierarchyCollapseAllAction.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/TestsHierarchyCollapseAllAction.java
new file mode 100644
index 0000000000..c42a42cb1e
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/TestsHierarchyCollapseAllAction.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * 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.actions;
+
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.viewers.AbstractTreeViewer;
+
+/**
+ * Collapses the tests hierarchy tree in the viewer.
+ */
+public class TestsHierarchyCollapseAllAction extends Action {
+
+ private AbstractTreeViewer testsHierarchyTreeViewer;
+
+
+ public TestsHierarchyCollapseAllAction(AbstractTreeViewer testsHierarchyTreeViewer) {
+ setText(ActionsMessages.TestsHierarchyCollapseAllAction_text);
+ setToolTipText(ActionsMessages.TestsHierarchyCollapseAllAction_tooltip);
+ this.testsHierarchyTreeViewer = testsHierarchyTreeViewer;
+ }
+
+ @Override
+ public void run(){
+ testsHierarchyTreeViewer.collapseAll();
+ }
+
+}
+
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/TestsHierarchyExpandAllAction.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/TestsHierarchyExpandAllAction.java
new file mode 100644
index 0000000000..a48ba783ad
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/TestsHierarchyExpandAllAction.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * 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.actions;
+
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.viewers.AbstractTreeViewer;
+
+/**
+ * Expands the tests hierarchy tree in the viewer.
+ */
+public class TestsHierarchyExpandAllAction extends Action {
+
+ private AbstractTreeViewer testsHierarchyTreeViewer;
+
+
+ public TestsHierarchyExpandAllAction(AbstractTreeViewer testsHierarchyTreeViewer) {
+ setText(ActionsMessages.TestsHierarchyExpandAllAction_text);
+ setToolTipText(ActionsMessages.TestsHierarchyExpandAllAction_tooltip);
+ this.testsHierarchyTreeViewer = testsHierarchyTreeViewer;
+ }
+
+ @Override
+ public void run(){
+ testsHierarchyTreeViewer.expandAll();
+ }
+
+}
+
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ToggleOrientationAction.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ToggleOrientationAction.java
new file mode 100644
index 0000000000..1ecaafe8ed
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/internal/ui/view/actions/ToggleOrientationAction.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * 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.actions;
+
+
+import org.eclipse.cdt.testsrunner.internal.TestsRunnerPlugin;
+import org.eclipse.cdt.testsrunner.internal.ui.view.ResultsView;
+import org.eclipse.jface.action.Action;
+
+/**
+ * Toggles the orientation of the view.
+ */
+public class ToggleOrientationAction extends Action {
+
+ private ResultsView resultsView;
+ private ResultsView.Orientation orientation;
+
+
+ public ToggleOrientationAction(ResultsView resultsView, ResultsView.Orientation orientation) {
+ super("", AS_RADIO_BUTTON); //$NON-NLS-1$
+ this.resultsView = resultsView;
+ if (orientation == ResultsView.Orientation.Horizontal) {
+ setText(ActionsMessages.ToggleOrientationAction_horizontal_text);
+ setImageDescriptor(TestsRunnerPlugin.getImageDescriptor("elcl16/orientation_horizontal.gif")); //$NON-NLS-1$
+ setDisabledImageDescriptor(TestsRunnerPlugin.getImageDescriptor("dlcl16/orientation_horizontal.gif")); //$NON-NLS-1$
+ setHoverImageDescriptor(TestsRunnerPlugin.getImageDescriptor("elcl16/orientation_horizontal.gif")); //$NON-NLS-1$
+ } else if (orientation == ResultsView.Orientation.Vertical) {
+ setText(ActionsMessages.ToggleOrientationAction_vertical_text);
+ setImageDescriptor(TestsRunnerPlugin.getImageDescriptor("elcl16/orientation_vertical.gif")); //$NON-NLS-1$
+ setDisabledImageDescriptor(TestsRunnerPlugin.getImageDescriptor("dlcl16/orientation_vertical.gif")); //$NON-NLS-1$
+ setHoverImageDescriptor(TestsRunnerPlugin.getImageDescriptor("elcl16/orientation_vertical.gif")); //$NON-NLS-1$
+ } else if (orientation == ResultsView.Orientation.Auto) {
+ setText(ActionsMessages.ToggleOrientationAction_automatic_text);
+ setImageDescriptor(TestsRunnerPlugin.getImageDescriptor("elcl16/orientation_auto.gif")); //$NON-NLS-1$
+ setDisabledImageDescriptor(TestsRunnerPlugin.getImageDescriptor("dlcl16/orientation_auto.gif")); //$NON-NLS-1$
+ setHoverImageDescriptor(TestsRunnerPlugin.getImageDescriptor("elcl16/orientation_auto.gif")); //$NON-NLS-1$
+ }
+ this.orientation = orientation;
+ }
+
+ public ResultsView.Orientation getOrientation() {
+ return orientation;
+ }
+
+ @Override
+ public void run() {
+ if (isChecked()) {
+ resultsView.setOrientation(orientation);
+ }
+ }
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/launcher/BaseTestsLaunchDelegate.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/launcher/BaseTestsLaunchDelegate.java
new file mode 100644
index 0000000000..2effdce889
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/launcher/BaseTestsLaunchDelegate.java
@@ -0,0 +1,255 @@
+/*******************************************************************************
+ * 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.launcher;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
+import org.eclipse.cdt.testsrunner.internal.TestsRunnerPlugin;
+import org.eclipse.cdt.testsrunner.internal.launcher.ITestsLaunchConfigurationConstants;
+import org.eclipse.cdt.testsrunner.internal.launcher.LauncherMessages;
+import org.eclipse.cdt.testsrunner.internal.launcher.TestsRunnerProviderInfo;
+import org.eclipse.cdt.testsrunner.internal.ui.view.TestPathUtils;
+import org.eclipse.cdt.testsrunner.model.TestingException;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationType;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.core.ILaunchDelegate;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.debug.core.model.ILaunchConfigurationDelegate2;
+import org.eclipse.debug.core.model.LaunchConfigurationDelegate;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+
+/**
+ * Launch delegate implementation that redirects its queries to the preferred
+ * launch delegate, correcting the arguments attribute (to take into account
+ * auto generated test module parameters) and setting up the custom process
+ * factory (to handle testing process IO streams).
+ */
+public abstract class BaseTestsLaunchDelegate extends LaunchConfigurationDelegate {
+
+ /** Stores the changes made to the launch configuration. */
+ private Map<String, String> changesToLaunchConfiguration = new HashMap<String, String>();
+
+
+ @Override
+ public ILaunch getLaunch(ILaunchConfiguration config, String mode) throws CoreException {
+ return getPreferredDelegate(config, mode).getLaunch(config, mode);
+ }
+
+ @Override
+ public boolean buildForLaunch(ILaunchConfiguration config, String mode, IProgressMonitor monitor) throws CoreException {
+ return getPreferredDelegate(config, mode).buildForLaunch(config, mode, monitor);
+ }
+
+ @Override
+ public boolean finalLaunchCheck(ILaunchConfiguration config, String mode, IProgressMonitor monitor) throws CoreException {
+ return getPreferredDelegate(config, mode).finalLaunchCheck(config, mode, monitor);
+ }
+
+ @Override
+ public boolean preLaunchCheck(ILaunchConfiguration config, String mode, IProgressMonitor monitor) throws CoreException {
+ return getPreferredDelegate(config, mode).preLaunchCheck(config, mode, monitor);
+ }
+
+ @Override
+ public void launch(ILaunchConfiguration config, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException {
+
+ if (mode.equals(ILaunchManager.RUN_MODE) || mode.equals(ILaunchManager.DEBUG_MODE)) {
+
+ // NOTE: The modified working copy of launch configuration cannot be passed directly
+ // to the preferred delegate because in this case the LaunchHistory will not work
+ // properly (and the rerun last launched configuration action will fail). So we
+ // just modify the existing configuration and revert all the changes back after
+ // the launch is done.
+
+ try {
+ // Changes launch configuration a bit and redirect it to the preferred C/C++ Application Launch delegate
+ updatedLaunchConfiguration(config);
+ getPreferredDelegate(config, mode).launch(config, mode, launch, monitor);
+ }
+ finally {
+ revertChangedToLaunchConfiguration(config);
+ }
+ activateTestingView();
+ }
+ }
+
+ /**
+ * Revert the changes to launch configuration previously made with
+ * <code>updatedLaunchConfigurationAttribute()</code>.
+ *
+ * @param config launch configuration to revert
+ */
+ private void revertChangedToLaunchConfiguration(ILaunchConfiguration config) throws CoreException {
+ ILaunchConfigurationWorkingCopy configWC = config.getWorkingCopy();
+ for (Map.Entry<String, String> changeEntry : changesToLaunchConfiguration.entrySet()) {
+ configWC.setAttribute(changeEntry.getKey(), changeEntry.getValue());
+ }
+ configWC.doSave();
+ changesToLaunchConfiguration.clear();
+ }
+
+ /**
+ * Saves the current value of the specified attribute (to be reverted later)
+ * and update its value in launch configuration.
+ *
+ * @param config launch configuration which attribute should be updated
+ * @param attributeName attribute name
+ * @param value new value of the specified attribute
+ */
+ private void updatedLaunchConfigurationAttribute(ILaunchConfigurationWorkingCopy config, String attributeName, String value) throws CoreException {
+ changesToLaunchConfiguration.put(attributeName, config.getAttribute(attributeName, "")); //$NON-NLS-1$
+ config.setAttribute(attributeName, value);
+ }
+
+ /**
+ * Makes the necessary changes to the launch configuration before passing it
+ * to the underlying delegate. Currently, updates the program arguments with
+ * the value that was obtained from Tests Runner provider plug-in.
+ *
+ * @param config launch configuration
+ */
+ private void updatedLaunchConfiguration(ILaunchConfiguration config) throws CoreException {
+ changesToLaunchConfiguration.clear();
+ ILaunchConfigurationWorkingCopy configWC = config.getWorkingCopy();
+ setProgramArguments(configWC);
+ configWC.doSave();
+ }
+
+ /**
+ * Updates the program arguments with the value that was obtained from Tests
+ * Runner provider plug-in.
+ *
+ * @param config launch configuration
+ */
+ private void setProgramArguments(ILaunchConfigurationWorkingCopy config) throws CoreException {
+ List<?> packedTestsFilter = config.getAttribute(ITestsLaunchConfigurationConstants.ATTR_TESTS_FILTER, Collections.EMPTY_LIST);
+ String [][] testsFilter = TestPathUtils.unpackTestPaths(packedTestsFilter.toArray(new String[packedTestsFilter.size()]));
+
+ // Configure test module run parameters with a Tests Runner
+ String[] params = null;
+ try {
+ params = getTestsRunner(config).getAdditionalLaunchParameters(testsFilter);
+
+ } catch (TestingException e) {
+ throw new CoreException(
+ new Status(
+ IStatus.ERROR, TestsRunnerPlugin.getUniqueIdentifier(),
+ e.getLocalizedMessage(), null
+ )
+ );
+ }
+
+ // Rewrite ATTR_PROGRAM_ARGUMENTS attribute of launch configuration
+ if (params != null && params.length >= 1) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, "")); //$NON-NLS-1$
+ for (String param : params) {
+ sb.append(' ');
+ sb.append(param);
+ }
+ updatedLaunchConfigurationAttribute(config, ICDTLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, sb.toString());
+ }
+ }
+
+ /**
+ * Resolves Tests Runner provider plug-in interface by the value written in
+ * launch configuration.
+ *
+ * @param config launch configuration
+ */
+ private ITestsRunnerProvider getTestsRunner(ILaunchConfiguration config) throws CoreException {
+ TestsRunnerProviderInfo testsRunnerProviderInfo = TestsRunnerPlugin.getDefault().getTestsRunnerProvidersManager().getTestsRunnerProviderInfo(config);
+ if (testsRunnerProviderInfo == null) {
+ throw new CoreException(
+ new Status(
+ IStatus.ERROR, TestsRunnerPlugin.getUniqueIdentifier(),
+ LauncherMessages.BaseTestsLaunchDelegate_invalid_tests_runner, null
+ )
+ );
+ }
+ ITestsRunnerProvider testsRunnerProvider = testsRunnerProviderInfo.instantiateTestsRunnerProvider();
+ if (testsRunnerProvider == null) {
+ throw new CoreException(
+ new Status(
+ IStatus.ERROR, TestsRunnerPlugin.getUniqueIdentifier(),
+ LauncherMessages.BaseTestsLaunchDelegate_tests_runner_load_failed, null
+ )
+ );
+ }
+ return testsRunnerProvider;
+ }
+
+ /**
+ * Resolves the preferred launch delegate for the specified configuration to
+ * launch C/C++ Local Application in the specified mode. The preferred
+ * launch delegate ID is taken from <code>getPreferredDelegateId()</code>.
+ *
+ * @param config launch configuration
+ * @param mode mode
+ * @return launch delegate
+ */
+ @SuppressWarnings("unchecked")
+ private ILaunchConfigurationDelegate2 getPreferredDelegate(ILaunchConfiguration config, String mode) throws CoreException {
+ ILaunchManager launchMgr = DebugPlugin.getDefault().getLaunchManager();
+ ILaunchConfigurationType localCfg =
+ launchMgr.getLaunchConfigurationType(ICDTLaunchConfigurationConstants.ID_LAUNCH_C_APP);
+ Set<String> modes = config.getModes();
+ modes.add(mode);
+ String preferredDelegateId = getPreferredDelegateId();
+ for (ILaunchDelegate delegate : localCfg.getDelegates(modes)) {
+ if (preferredDelegateId.equals(delegate.getId())) {
+ return (ILaunchConfigurationDelegate2) delegate.getDelegate();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the launch delegate id which should be used to redirect the
+ * launch.
+ *
+ * @return launch delegate ID
+ */
+ public abstract String getPreferredDelegateId();
+
+ /**
+ * Activates the view showing testing results.
+ */
+ private void activateTestingView() {
+ Display.getDefault().syncExec(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ IWorkbenchWindow activeWindow = TestsRunnerPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow();
+ IViewPart view = activeWindow.getActivePage().showView(ITestsRunnerConstants.TESTS_RUNNER_RESULTS_VIEW_ID);
+ TestsRunnerPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage().activate(view);
+ } catch (PartInitException e) {
+ TestsRunnerPlugin.log(e);
+ }
+ }
+ });
+ }
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/launcher/ITestsRunnerConstants.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/launcher/ITestsRunnerConstants.java
new file mode 100644
index 0000000000..948e4e685f
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/launcher/ITestsRunnerConstants.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2012 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.launcher;
+
+/**
+ * Constants used for attributes in CDT Tests Runner launch configurations.
+ *
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface ITestsRunnerConstants {
+
+ /**
+ * Tests Runner launch configuration type.
+ */
+ public static final String LAUNCH_CONFIGURATION_TYPE_ID = "org.eclipse.cdt.testsrunner.launch.CTestsRunner"; //$NON-NLS-1$
+
+ /**
+ * Specifies the default launch delegate for a Tests Run session.
+ */
+ public static final String PREFERRED_RUN_TESTS_LAUNCH_DELEGATE = "org.eclipse.cdt.testsrunner.launch.runTests"; //$NON-NLS-1$
+
+ /**
+ * Specifies the default launch delegate for a Tests Debug session.
+ */
+ public static final String PREFERRED_DEBUG_TESTS_LAUNCH_DELEGATE = "org.eclipse.cdt.testsrunner.launch.dsf.runTests"; //$NON-NLS-1$
+
+ /**
+ * Specifies the ID of the Tests Runner main view for browsing results.
+ */
+ public static final String TESTS_RUNNER_RESULTS_VIEW_ID = "org.eclipse.cdt.testsrunner.resultsview"; //$NON-NLS-1$
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/launcher/ITestsRunnerProvider.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/launcher/ITestsRunnerProvider.java
new file mode 100644
index 0000000000..4f6ceb049b
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/launcher/ITestsRunnerProvider.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * 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.launcher;
+
+import java.io.InputStream;
+
+import org.eclipse.cdt.testsrunner.model.ITestModelUpdater;
+import org.eclipse.cdt.testsrunner.model.TestingException;
+
+
+/**
+ * The interface for a Tests Runner provider plug-in.
+ *
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ */
+public interface ITestsRunnerProvider {
+
+ /**
+ * Returns the list of automatically generated parameters that should be
+ * passed to test module for launching. Usually there are parameters to
+ * configure test module output format and content. If testPaths is not
+ * <code>null</code>, it contains the paths to the test suites and test
+ * cases that should be run during the testing process, so the proper
+ * filters should be generated too.
+ *
+ * @param testPaths the array of test paths
+ * @return parameters list
+ * @throws TestingException if test parameters cannot be generated
+ */
+ public String[] getAdditionalLaunchParameters(String[][] testPaths) throws TestingException;
+
+ /**
+ * Starts the processing of testing results by Tests Runner Plug-in. The
+ * input data are provided via the stream. They should be converted to the
+ * commands for the Model Updater.
+ *
+ * @param modelUpdater model updater instance
+ * @param inputStream test model output or error data stream
+ * @throws TestingException if there are errors during the testing process
+ * or in data stream
+ */
+ public void run(ITestModelUpdater modelUpdater, InputStream inputStream) throws TestingException;
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/launcher/ITestsRunnerProviderInfo.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/launcher/ITestsRunnerProviderInfo.java
new file mode 100644
index 0000000000..37272797d4
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/launcher/ITestsRunnerProviderInfo.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * 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.launcher;
+
+/**
+ * Describes the Tests Runner input provider plug-in, its requirements and
+ * features provided.
+ *
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface ITestsRunnerProviderInfo {
+
+ /**
+ * Returns the user readable name of the Tests Runner provider plug-in.
+ *
+ * @return readable name
+ */
+ public String getName();
+
+ /**
+ * Returns the unique ID of the Tests Runner provider plug-in.
+ *
+ * @return unique id
+ */
+ public String getId();
+
+ /**
+ * Returns the short description of the Tests Runner provider plug-in.
+ *
+ * @return short description
+ */
+ public String getDescription();
+
+ /**
+ * Returns whether Tests Runner provider plug-in allows to add a filter for
+ * running a few custom test cases or test suites at a time (e.g. Google
+ * Test and Qt Test allow it, but Boost.Test doesn't).
+ *
+ * @return whether multiple filter is supported
+ */
+ public boolean isAllowedMultipleTestFilter();
+
+ /**
+ * Returns whether Tests Runner provider plug-in requires to handle standard
+ * output stream of the testing process.
+ *
+ * @return whether output stream is required
+ */
+ public boolean isOutputStreamRequired();
+
+ /**
+ * Returns whether Tests Runner provider plug-in requires to handle standard
+ * error stream of the testing process.
+ *
+ * @return whether error stream is required
+ */
+ public boolean isErrorStreamRequired();
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/IModelVisitor.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/IModelVisitor.java
new file mode 100644
index 0000000000..5bf196610f
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/IModelVisitor.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * 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.model;
+
+/**
+ * Interface to the visitor through the test model.
+ *
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IModelVisitor {
+
+ public void visit(ITestSuite testSuite);
+
+ public void leave(ITestSuite testSuite);
+
+ public void visit(ITestCase testCase);
+
+ public void leave(ITestCase testCase);
+
+ public void visit(ITestMessage testMessage);
+
+ public void leave(ITestMessage testMessage);
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestCase.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestCase.java
new file mode 100644
index 0000000000..8aea7d533c
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestCase.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * 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.model;
+
+/**
+ * Interface to the test case (test) of the test hierarchy.
+ *
+ * Test cases stores the name, status, testing time and messages.
+ * Also has the reference to the parent test suite (if any).
+ *
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface ITestCase extends ITestItem {
+
+ /**
+ * Returns test messages that were produced during the test case running.
+ *
+ * @return array of test messages
+ */
+ public ITestMessage[] getTestMessages();
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestItem.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestItem.java
new file mode 100644
index 0000000000..92d3d46ab8
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestItem.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * 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.model;
+
+/**
+ * Base interface to the structural item of test hierarchy (test suite or test
+ * case).
+ *
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface ITestItem {
+
+ /**
+ * Represents status of the test item.
+ *
+ * @note Order of values is significant (cause enumeration values comparison
+ * is necessary)
+ */
+ public enum Status {
+ NotRun,
+ Skipped,
+ Passed,
+ Failed,
+ Aborted;
+
+ public boolean isError() {
+ return (this == Failed) || (this == Aborted);
+ }
+ }
+
+ /**
+ * @return name of the test item.
+ */
+ public String getName();
+
+ /**
+ * Returns status of the test item.
+ * For test case it is its own status.
+ * For test suite it is the greatest status of all its children.
+ *
+ * @return test item status
+ */
+ public Status getStatus();
+
+ /**
+ * Returns execution time (in milliseconds) of the test item.
+ * For test case it is its own execution time.
+ * For test suite it is the sum of execution time of all its children.
+ *
+ * @return item execution time (in milliseconds)
+ */
+ public int getTestingTime();
+
+ /**
+ * Returns parent of the current test item or null if not available
+ * (e.g. it is a root test suite).
+ *
+ * @return parent or null
+ */
+ public ITestSuite getParent();
+
+ /**
+ * Returns <code>true</code> if test item has children.
+ * Always returns <code>false</code> for test cases.
+ *
+ * @return true if has children
+ */
+ public boolean hasChildren();
+
+ /**
+ * Returns all the children of the test item.
+ * For test case always returns empty array.
+ * For test suite returns all child test suites and test cases.
+ *
+ * @return array of test item children
+ */
+ public ITestItem[] getChildren();
+
+ /**
+ * Visitor pattern support for the tests hierarchy.
+ *
+ * @param visitor - any object that supports visitor interface
+ */
+ public void visit(IModelVisitor visitor);
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestLocation.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestLocation.java
new file mode 100644
index 0000000000..133d15bf30
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestLocation.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * 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.model;
+
+/**
+ * Describes the location of the test object.
+ *
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface ITestLocation {
+
+ /**
+ * Returns the file name in which testing object is located.
+ *
+ * @return file name
+ */
+ public String getFile();
+
+ /**
+ * Returns the line number on which testing object is located.
+ *
+ * @return line number
+ */
+ public int getLine();
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestMessage.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestMessage.java
new file mode 100644
index 0000000000..60ba09219c
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestMessage.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * 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.model;
+
+/**
+ * Describes the message that was produced during the testing process.
+ *
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface ITestMessage {
+
+ /**
+ * The level of the test message.
+ */
+ public enum Level {
+ Info(ModelMessages.MessageLevel_info),
+ Message(ModelMessages.MessageLevel_message),
+ Warning(ModelMessages.MessageLevel_warning),
+ Error(ModelMessages.MessageLevel_error),
+ FatalError(ModelMessages.MessageLevel_fatal_error),
+ Exception(ModelMessages.MessageLevel_exception);
+
+ String stringRepr;
+
+ Level(String stringRepr) {
+ this.stringRepr = stringRepr;
+ }
+
+ @Override
+ public String toString() {
+ return stringRepr;
+ }
+ }
+
+ /**
+ * Returns the location of the test message.
+ *
+ * @return message location
+ */
+ public ITestLocation getLocation();
+
+ /**
+ * Returns the level of the test message.
+ *
+ * @return message level
+ */
+ public Level getLevel();
+
+ /**
+ * Returns the text of the test message.
+ *
+ * @return message text
+ */
+ public String getText();
+
+ /**
+ * Visitor pattern support for the tests hierarchy.
+ *
+ * @param visitor - any object that supports visitor interface
+ */
+ public void visit(IModelVisitor visitor);
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestModelAccessor.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestModelAccessor.java
new file mode 100644
index 0000000000..8a797c7423
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestModelAccessor.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * 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.model;
+
+/**
+ * Provides an access to the tests model.
+ *
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface ITestModelAccessor {
+
+ /**
+ * Checks whether the specified testing item (case or suite) is running now.
+ *
+ * @param item the interested item
+ * @return whether the item is running now
+ */
+ public boolean isCurrentlyRunning(ITestItem item);
+
+ /**
+ * Provides access to the root test suite.
+ *
+ * @return root test suite
+ */
+ public ITestSuite getRootSuite();
+
+ /**
+ * Adds the given listener to this registered listeners collection.
+ * Has no effect if an identical listener is already registered.
+ *
+ * @param listener the listener to add
+ */
+ public void addChangesListener(ITestingSessionListener listener);
+
+ /**
+ * Removes the given listener from registered listeners collection.
+ * Has no effect if the listener is not already registered.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeChangesListener(ITestingSessionListener listener);
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestModelUpdater.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestModelUpdater.java
new file mode 100644
index 0000000000..6cc419318a
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestModelUpdater.java
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * 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.model;
+
+/**
+ * The interface to easily build or update testing model.
+ * It is intended to use from the Tests Runner provider plug-in.
+ *
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface ITestModelUpdater {
+
+ /**
+ * Specifies that a new test suite has been started.
+ *
+ * @param name the name of the started test suite
+ */
+ public void enterTestSuite(String name);
+
+ /**
+ * Specifies that the last test suite has been finished.
+ * Automatically exists from the currently running test case (if any).
+ */
+ public void exitTestSuite();
+
+ /**
+ * Specifies that a new test case has been started.
+ *
+ * @param name the name of the started test case
+ */
+ public void enterTestCase(String name);
+
+ /**
+ * Sets the status of the currently running test case.
+ * The exception is thrown if no test case is running.
+ *
+ * @param status new test status
+ */
+ public void setTestStatus(ITestItem.Status status);
+
+ /**
+ * Sets the execution time of the currently running test case.
+ * If the execution time has already been set, it will be overridden.
+ * The exception is thrown if no test case is running.
+ *
+ * @param testingTime test execution time
+ */
+ public void setTestingTime(int testingTime);
+
+ /**
+ * Specifies that the currently running test case has been finished.
+ * The test execution time is set if Tests Runner provider plug-in
+ * requires time measurement.
+ */
+ public void exitTestCase();
+
+ /**
+ * Add a new testing message connected to the currently running test case.
+ *
+ * @param file message file name
+ * @param line message line number
+ * @param level message level
+ * @param text message text
+ *
+ * @note If file name is <code>null</code> or empty or if line number is
+ * negative or 0 then message location will not be set.
+ */
+ public void addTestMessage(String file, int line, ITestMessage.Level level, String text);
+
+
+ /**
+ * Access the top most currently running test suite object.
+ *
+ * @return top most test suite
+ */
+ public ITestSuite currentTestSuite();
+
+ /**
+ * Access the currently running test case object.
+ *
+ * @return top most test case
+ */
+ public ITestCase currentTestCase();
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestSuite.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestSuite.java
new file mode 100644
index 0000000000..b2db06c594
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestSuite.java
@@ -0,0 +1,22 @@
+/*******************************************************************************
+ * 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.model;
+
+/**
+ * Interface to the test suite of the tests hierarchy.
+ * Test suites group the test cases and the other test suites.
+ * They also provides group operations (e.g. status or execution time access).
+ *
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface ITestSuite extends ITestItem {
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestingSession.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestingSession.java
new file mode 100644
index 0000000000..fee3089860
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestingSession.java
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * 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.model;
+
+import org.eclipse.cdt.testsrunner.launcher.ITestsRunnerProviderInfo;
+import org.eclipse.debug.core.ILaunch;
+
+
+/**
+ * Stores the information about tests running.
+ *
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface ITestingSession {
+
+ /**
+ * Returns the count of already finished tests.
+ *
+ * @return already finished tests count
+ */
+ public int getCurrentCounter();
+
+ /**
+ * Returns the total tests count (if available).
+ *
+ * @note Usually testing frameworks do not provide the information about
+ * total tests count (at least Boost.Test, Google Test and Qt Test do not).
+ * So the total tests count from previous launch of the same test module is
+ * used instead. That is why, it is unavailable for the first run and it may
+ * be not accurate. When testing is finished the total tests count is
+ * updated to the accurate value.
+ *
+ * @return total tests count or -1 if unavailable
+ */
+ public int getTotalCounter();
+
+ /**
+ * Returns the count tests with the specified status at the moment.
+ *
+ * @note Tests count with the specified status may change if testing session
+ * is not terminated.
+ *
+ * @param status the required status
+ * @return tests count with status 'status'
+ */
+ public int getCount(ITestItem.Status status);
+
+ /**
+ * Returns whether the testing session contains errors at the moment.
+ * Testing session may contain errors if one of its tests failed or if there
+ * were some errors during tests running (e.g. test module cannot be
+ * launched or output parsing failed).
+ *
+ * @return whether the testing session contains errors
+ */
+ public boolean hasErrors();
+
+ /**
+ * Returns whether the testing session was stopped by user.
+ *
+ * @note Testing session is not considered as stopped when it was terminated
+ * by the <code>ILaunch.terminate()</code> method (e.g. when user clicks on
+ * 'Stop' button of the 'Console' or 'Debug' view). In this case it will be
+ * considered as finished (probably, with errors cause not all output was
+ * obtained from launched process). To stop testing session properly the
+ * <code>stop()</code> method should be used.
+ *
+ * @return whether the testing session was stopped
+ */
+ public boolean wasStopped();
+
+ /**
+ * Returns whether the testing session has been finished (with or without
+ * errors).
+ *
+ * @return whether the testing session has been finished
+ */
+ public boolean isFinished();
+
+ /**
+ * Returns the testing model accessor that is associated with this session.
+ *
+ * @return testing model accessor
+ */
+ public ITestModelAccessor getModelAccessor();
+
+ /**
+ * Returns the launch that is associated with this testing session.
+ *
+ * @return launch
+ */
+ public ILaunch getLaunch();
+
+ /**
+ * Returns the information about the tests runner which was used to launch
+ * this testing session.
+ *
+ * @return tests runner information
+ */
+ public ITestsRunnerProviderInfo getTestsRunnerProviderInfo();
+
+ /**
+ * Returns the current status message for the current testing session. It
+ * may contain error, some session information or simple statistics.
+ *
+ * @return text status of the testing session
+ */
+ public String getStatusMessage();
+
+ /**
+ * Returns the name of the current testing session. Usually it contains
+ * launch configuration name with some additional information.
+ *
+ * @return testing session name
+ */
+ public String getName();
+
+ /**
+ * Stops the current session with a special status setting. Does nothing if
+ * the launched process of testing session has already been terminated or
+ * cannot been terminated.
+ */
+ public void stop();
+
+}
+
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestingSessionListener.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestingSessionListener.java
new file mode 100644
index 0000000000..b75b34c8d4
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ITestingSessionListener.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * 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.model;
+
+/**
+ * Testing session listener is notified of testing process going.
+ *
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface ITestingSessionListener {
+
+ /**
+ * Notifies the listener that the new test suite running is started.
+ *
+ * @param testSuite the started test suite
+ */
+ public void enterTestSuite(ITestSuite testSuite);
+
+ /**
+ * Notifies the listener that the test suite running is finished.
+ *
+ * @param testSuite the finished test suite
+ */
+ public void exitTestSuite(ITestSuite testSuite);
+
+ /**
+ * Notifies the listener that the new test case running is started.
+ *
+ * @param testCase the started test case
+ */
+ public void enterTestCase(ITestCase testCase);
+
+ /**
+ * Notifies the listener that the test case running is finished.
+ *
+ * @param testCase the finished test case
+ */
+ public void exitTestCase(ITestCase testCase);
+
+ /**
+ * Notifies the listener that the children of the test suite were updated.
+ *
+ * @param testSuite the test suite which require children update
+ */
+ public void childrenUpdate(ITestSuite testSuite);
+
+ /**
+ * Notifies the listener that the testing process is started.
+ */
+ public void testingStarted();
+
+ /**
+ * Notifies the listener that the testing process is finished.
+ */
+ public void testingFinished();
+
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ModelMessages.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ModelMessages.java
new file mode 100644
index 0000000000..eb1f39edf3
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ModelMessages.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * 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.model;
+
+import org.eclipse.osgi.util.NLS;
+
+public class ModelMessages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.cdt.testsrunner.model.ModelMessages"; //$NON-NLS-1$
+ public static String MessageLevel_error;
+ public static String MessageLevel_exception;
+ public static String MessageLevel_fatal_error;
+ public static String MessageLevel_info;
+ public static String MessageLevel_message;
+ public static String MessageLevel_warning;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, ModelMessages.class);
+ }
+
+ private ModelMessages() {
+ }
+}
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ModelMessages.properties b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ModelMessages.properties
new file mode 100644
index 0000000000..05dfe24e33
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/ModelMessages.properties
@@ -0,0 +1,16 @@
+###############################################################################
+# 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 implementation
+###############################################################################
+MessageLevel_error=Error
+MessageLevel_exception=Exception
+MessageLevel_fatal_error=Fatal error
+MessageLevel_info=Info
+MessageLevel_message=Message
+MessageLevel_warning=Warning
diff --git a/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/TestingException.java b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/TestingException.java
new file mode 100644
index 0000000000..5e3c4716ad
--- /dev/null
+++ b/testsrunner/org.eclipse.cdt.testsrunner/src/org/eclipse/cdt/testsrunner/model/TestingException.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * 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.model;
+
+
+/**
+ * Represents a failure in the Tests Runner operations.
+ */
+public class TestingException extends Exception {
+
+ /**
+ * Constructs an exception with the given descriptive message.
+ *
+ * @param msg Description of the occurred exception.
+ */
+ public TestingException(String msg) {
+ super(msg);
+ }
+
+}

Back to the top