diff --git a/debug/org.eclipse.debug.tests/META-INF/MANIFEST.MF b/debug/org.eclipse.debug.tests/META-INF/MANIFEST.MF
index 0f01cc5e065..14fa35edf24 100644
--- a/debug/org.eclipse.debug.tests/META-INF/MANIFEST.MF
+++ b/debug/org.eclipse.debug.tests/META-INF/MANIFEST.MF
@@ -29,5 +29,6 @@ Export-Package: org.eclipse.debug.tests,
org.eclipse.debug.tests.ui,
org.eclipse.debug.tests.view.memory,
org.eclipse.debug.tests.viewer.model
+Import-Package: org.assertj.core.api;version="3.24.2"
Eclipse-BundleShape: dir
Automatic-Module-Name: org.eclipse.debug.tests
diff --git a/debug/org.eclipse.debug.tests/plugin.properties b/debug/org.eclipse.debug.tests/plugin.properties
index 56ca2e3b1bb..64495cd2370 100755
--- a/debug/org.eclipse.debug.tests/plugin.properties
+++ b/debug/org.eclipse.debug.tests/plugin.properties
@@ -22,4 +22,5 @@ launchConfigurationType.name = Test Launch Type
extension.name = Debug File System
launchConfigurationType.name.0 = Cancelling Launch Type
launchConfigurationType.name.1 = Throwing Launch Type
-testBreakpoint.name = Test Breakpoint
\ No newline at end of file
+testBreakpoint.name = Test Breakpoint
+launchConfigurationType.name.2 = Example launch
\ No newline at end of file
diff --git a/debug/org.eclipse.debug.tests/plugin.xml b/debug/org.eclipse.debug.tests/plugin.xml
index 3738652a0cb..f8395ed7e27 100644
--- a/debug/org.eclipse.debug.tests/plugin.xml
+++ b/debug/org.eclipse.debug.tests/plugin.xml
@@ -163,4 +163,20 @@
priority="-1"
class="org.eclipse.debug.tests.ui.TestVariableValueEditor3"/>
+
+
+
+
+
+
+
+
+
diff --git a/debug/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java b/debug/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java
index 5e5e9ba65f6..2e91df5d59f 100644
--- a/debug/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java
+++ b/debug/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java
@@ -44,6 +44,7 @@
import org.eclipse.debug.tests.sourcelookup.SourceLookupFacilityTests;
import org.eclipse.debug.tests.statushandlers.StatusHandlerTests;
import org.eclipse.debug.tests.stepfilters.StepFiltersTests;
+import org.eclipse.debug.tests.ui.LaunchConfigurationTabGroupViewerTest;
import org.eclipse.debug.tests.ui.VariableValueEditorManagerTests;
import org.eclipse.debug.tests.view.memory.MemoryRenderingTests;
import org.eclipse.debug.tests.view.memory.TableRenderingTests;
@@ -131,6 +132,7 @@
// Launch Groups
LaunchGroupTests.class,
+ LaunchConfigurationTabGroupViewerTest.class,
// Logical structure
LogicalStructureCacheTest.class,
diff --git a/debug/org.eclipse.debug.tests/src/org/eclipse/debug/tests/ui/LaunchConfigurationTabGroupViewerTest.java b/debug/org.eclipse.debug.tests/src/org/eclipse/debug/tests/ui/LaunchConfigurationTabGroupViewerTest.java
new file mode 100644
index 00000000000..fc6ed459dfd
--- /dev/null
+++ b/debug/org.eclipse.debug.tests/src/org/eclipse/debug/tests/ui/LaunchConfigurationTabGroupViewerTest.java
@@ -0,0 +1,203 @@
+/*******************************************************************************
+ * Copyright (c) 2018, 2019 SAP SE and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * SAP SE - initial version
+ *******************************************************************************/
+
+package org.eclipse.debug.tests.ui;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunchConfigurationType;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.debug.internal.ui.DebugUIPlugin;
+import org.eclipse.debug.internal.ui.launchConfigurations.LaunchConfigurationPresentationManager;
+import org.eclipse.debug.internal.ui.launchConfigurations.LaunchConfigurationsDialog;
+import org.eclipse.debug.ui.IDebugUIConstants;
+import org.eclipse.debug.ui.ILaunchConfigurationDialog;
+import org.eclipse.debug.ui.ILaunchConfigurationTab;
+import org.eclipse.debug.ui.ILaunchConfigurationTabGroup;
+import org.eclipse.swt.widgets.Display;
+import org.junit.Before;
+import org.junit.Test;
+
+public class LaunchConfigurationTabGroupViewerTest {
+
+ private static interface ThrowingRunnable {
+ void run() throws T;
+ }
+
+ private static final String LAUNCH_CONFIG_TYPE_ID = "org.eclipse.debug.ui.tests.launchConfigurationType1";
+ private static final String LAUNCH_CONFIG_MODE = ILaunchManager.RUN_MODE;
+ private ILaunchConfigurationType fLaunchConfigurationType;
+ private LaunchConfigurationsDialog fLaunchConfigurationsDialog;
+
+ @Before
+ public void createDialog() throws CoreException {
+ fLaunchConfigurationType = getLaunchManager().getLaunchConfigurationType(LAUNCH_CONFIG_TYPE_ID);
+ ILaunchConfigurationTabGroup tabGroup = getLaunchConfigurationTabGroup(fLaunchConfigurationType);
+
+ fLaunchConfigurationsDialog = (LaunchConfigurationsDialog) createLaunchConfigurationDialog();
+ tabGroup.createTabs(fLaunchConfigurationsDialog, ILaunchManager.RUN_MODE);
+
+ ILaunchConfigurationTab[] tabs = tabGroup.getTabs();
+
+ assertThat(tabs).hasSizeGreaterThanOrEqualTo(2);
+ assertThat(tabs).allMatch(SpyTab.class::isInstance, "Use only SpyTabs in the group");
+ long typesOfTabs = Arrays.stream(tabs).map(Object::getClass).distinct().count();
+ assertThat("There are tabs of the exact same type in the group", tabs.length == typesOfTabs);
+ }
+
+ @Test
+ public void testAllTabsAreInitializedByDefault() {
+ // Create a launch configuration with a unique name
+ ThrowingRunnable createAndSelect1LaunchConfig = () -> {
+ fLaunchConfigurationsDialog.getTabViewer().setInput(createLaunchConfigurationInstance());
+ };
+
+ final ILaunchConfigurationTab[] tabs = runOnDialog(createAndSelect1LaunchConfig);
+
+ for (int i = 0; i < tabs.length; i++) {
+ assertThat("Tab " + i + " was not initialized", ((SpyTab) tabs[i]).isInitialized());
+ }
+ }
+
+ @Test
+ public void testFirstTabIsActivatedByDefault() {
+ // Create a launch configuration with a unique name
+ ThrowingRunnable createAndSelect1LaunchConfig = () -> {
+ fLaunchConfigurationsDialog.getTabViewer().setInput(createLaunchConfigurationInstance());
+ };
+
+ final ILaunchConfigurationTab[] tabs = runOnDialog(createAndSelect1LaunchConfig);
+ assertThat("The 1st tab was not activated", ((SpyTab) tabs[0]).isActivated());
+ }
+
+ @Test
+ public void testOtherTabInOtherConfigIsActivated() {
+ int secondTabIndex = 1;
+
+ ThrowingRunnable setActiveTab = () -> {
+ // Create and select launch config
+ fLaunchConfigurationsDialog.getTabViewer().setInput(createLaunchConfigurationInstance());
+
+ // Select another tab
+ fLaunchConfigurationsDialog.getTabViewer().setActiveTab(secondTabIndex);
+
+ // Create a new launch config. This one should activate the same tab
+ // by default.
+ fLaunchConfigurationsDialog.getTabViewer().setInput(createLaunchConfigurationInstance());
+ };
+
+ final ILaunchConfigurationTab[] tabs = runOnDialog(setActiveTab);
+
+ assertThat("The 1st tab of the other launch configuration shouldn't have been activated", not(((SpyTab) tabs[0]).isActivated()));
+ assertThat("The tab was not activated", ((SpyTab) tabs[secondTabIndex]).isActivated());
+ }
+
+ @Test
+ public void testOnlyDefaultTabInOtherConfigIsActivated() {
+ int overflowTabIndex = Integer.MAX_VALUE;
+
+ ThrowingRunnable setActiveTab = () -> {
+ // Create and select launch config
+ fLaunchConfigurationsDialog.getTabViewer().setInput(createLaunchConfigurationInstance());
+
+ // Select another tab
+ fLaunchConfigurationsDialog.getTabViewer().setActiveTab(overflowTabIndex);
+
+ // Create a new launch config. This one should activate the same tab
+ // by default.
+ fLaunchConfigurationsDialog.getTabViewer().setInput(createLaunchConfigurationInstance());
+ };
+
+ final ILaunchConfigurationTab[] tabs = runOnDialog(setActiveTab);
+
+ assertThat("The 1st tab of the other launch configuration should have been activated", ((SpyTab) tabs[0]).isActivated());
+
+ // All other tabs should not have been initialized
+ for (int i = 1; i < tabs.length; i++) {
+ assertThat("Tab " + i + " should not have been initialized", not(((SpyTab) tabs[i]).isInitialized()));
+ }
+ }
+
+ @Test
+ public void testOtherTabIsActivated() {
+ int secondTabIndex = 1;
+
+ ThrowingRunnable setActiveTab = () -> {
+ // Create and select launch config
+ fLaunchConfigurationsDialog.getTabViewer().setInput(createLaunchConfigurationInstance());
+
+ // Select another tab
+ fLaunchConfigurationsDialog.getTabViewer().setActiveTab(secondTabIndex);
+ };
+
+ final ILaunchConfigurationTab[] tabs = runOnDialog(setActiveTab);
+
+ assertThat("The tab was not activated", ((SpyTab) tabs[secondTabIndex]).isActivated());
+ }
+
+ private ILaunchConfigurationWorkingCopy createLaunchConfigurationInstance() throws CoreException {
+ return fLaunchConfigurationType.newInstance(null, "MyLaunchConfiguration_" + System.currentTimeMillis());
+ }
+
+ private ILaunchConfigurationTab[] runOnDialog(ThrowingRunnable runnable) {
+ AtomicReference tabsRef = new AtomicReference<>();
+ AtomicReference throwableRef = new AtomicReference<>();
+
+ Display.getCurrent().asyncExec(() -> {
+ try {
+
+ runnable.run();
+
+ // I need to store the tabs here because the tab viewer (and all
+ // its tabs) are
+ // gone as soon as the dialog is closed
+ tabsRef.set(fLaunchConfigurationsDialog.getTabs());
+
+ } catch (Throwable e) {
+ // neither calling "fail" not throwing an exception will let the
+ // test fail so I
+ // need to store this and check it outside of the runnable
+ throwableRef.set(e);
+ // DebugPlugin.log(e);
+ } finally {
+ fLaunchConfigurationsDialog.close();
+ }
+ });
+
+ fLaunchConfigurationsDialog.open();
+
+ if (throwableRef.get() != null) {
+ throw new AssertionError("An exception occurred while executing the runnable.", throwableRef.get());
+ }
+
+ return tabsRef.get();
+ }
+
+ protected ILaunchConfigurationTabGroup getLaunchConfigurationTabGroup(ILaunchConfigurationType launchConfigurationType) throws CoreException {
+ return LaunchConfigurationPresentationManager.getDefault().getTabGroup(launchConfigurationType, LAUNCH_CONFIG_MODE);
+ }
+
+ protected ILaunchConfigurationDialog createLaunchConfigurationDialog() {
+ return new LaunchConfigurationsDialog(null, DebugUIPlugin.getDefault().getLaunchConfigurationManager().getLaunchGroup(IDebugUIConstants.ID_DEBUG_LAUNCH_GROUP));
+ }
+
+ protected ILaunchManager getLaunchManager() {
+ return DebugPlugin.getDefault().getLaunchManager();
+ }
+}
diff --git a/debug/org.eclipse.debug.tests/src/org/eclipse/debug/tests/ui/SpyTab.java b/debug/org.eclipse.debug.tests/src/org/eclipse/debug/tests/ui/SpyTab.java
new file mode 100644
index 00000000000..b97e7cb49ed
--- /dev/null
+++ b/debug/org.eclipse.debug.tests/src/org/eclipse/debug/tests/ui/SpyTab.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2018, 2019 SAP SE and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * SAP SE - initial version
+ *******************************************************************************/
+
+package org.eclipse.debug.tests.ui;
+
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.ui.AbstractLaunchConfigurationTab;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * A Tab whose sole purpose is to say if it was initialized and activated
+ * properly
+ */
+public abstract class SpyTab extends AbstractLaunchConfigurationTab {
+
+ private boolean initialized;
+ private boolean activated;
+
+ @Override
+ public void createControl(Composite parent) {
+ }
+
+ @Override
+ public String getName() {
+ return getClass().getSimpleName();
+ }
+
+ @Override
+ public void initializeFrom(ILaunchConfiguration configuration) {
+ initialized = true;
+ }
+
+ @Override
+ public void activated(ILaunchConfigurationWorkingCopy workingCopy) {
+ activated = true;
+ }
+
+ @Override
+ public void performApply(ILaunchConfigurationWorkingCopy configuration) {
+ }
+
+ @Override
+ public void setDefaults(ILaunchConfigurationWorkingCopy configuration) {
+ }
+
+ public boolean isInitialized() {
+ return initialized;
+ }
+
+ public boolean isActivated() {
+ return activated;
+ }
+
+ // These are necessary because I need several tabs in the launch config and
+ // using always the same kind (class) of tab produces incorrect results
+ public static class SpyTabA extends SpyTab {
+ }
+
+ public static class SpyTabB extends SpyTab {
+ }
+}
diff --git a/debug/org.eclipse.debug.tests/src/org/eclipse/debug/tests/ui/SpyTabGroup.java b/debug/org.eclipse.debug.tests/src/org/eclipse/debug/tests/ui/SpyTabGroup.java
new file mode 100644
index 00000000000..3a623cbd8d6
--- /dev/null
+++ b/debug/org.eclipse.debug.tests/src/org/eclipse/debug/tests/ui/SpyTabGroup.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (c) 2018, 2019 SAP SE and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * SAP SE - initial version
+ *******************************************************************************/
+
+package org.eclipse.debug.tests.ui;
+
+import org.eclipse.debug.tests.ui.SpyTab.SpyTabA;
+import org.eclipse.debug.tests.ui.SpyTab.SpyTabB;
+import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup;
+import org.eclipse.debug.ui.ILaunchConfigurationDialog;
+import org.eclipse.debug.ui.ILaunchConfigurationTab;
+
+public class SpyTabGroup extends AbstractLaunchConfigurationTabGroup {
+
+ @Override
+ public void createTabs(ILaunchConfigurationDialog dialog, String mode) {
+ setTabs(new ILaunchConfigurationTab[] { new SpyTabA(), new SpyTabB() });
+ }
+
+}
\ No newline at end of file
diff --git a/debug/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/launchConfigurations/LaunchConfigurationTabGroupViewer.java b/debug/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/launchConfigurations/LaunchConfigurationTabGroupViewer.java
index 2ba0dd7e72c..50962e39650 100644
--- a/debug/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/launchConfigurations/LaunchConfigurationTabGroupViewer.java
+++ b/debug/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/launchConfigurations/LaunchConfigurationTabGroupViewer.java
@@ -22,6 +22,7 @@
import java.text.MessageFormat;
import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import org.eclipse.core.resources.IFile;
@@ -722,7 +723,9 @@ protected void inputChanged(Object input) {
if (finput instanceof ILaunchConfiguration configuration) {
boolean refreshTabs = true;
if (fWorkingCopy != null
- && fWorkingCopy.getOriginal().equals(configuration.getWorkingCopy().getOriginal())) {
+ && fWorkingCopy.getOriginal() != null //
+ && Objects.equals(fWorkingCopy.getOriginal(),
+ configuration.getWorkingCopy().getOriginal())) {
refreshTabs = false;
}
fOriginal = configuration;
@@ -1449,19 +1452,39 @@ protected void handleTabSelected() {
if (fDisposingTabs || fInitializingTabs) {
return;
}
+ int previousTabIndex = fCurrentTabIndex;
+ fCurrentTabIndex = fTabFolder.getSelectionIndex();
+
ILaunchConfigurationTab[] tabs = getTabs();
- if (fCurrentTabIndex == fTabFolder.getSelectionIndex() || tabs == null || tabs.length == 0 || fCurrentTabIndex > (tabs.length - 1)) {
+ if (previousTabIndex == fCurrentTabIndex || tabs == null || tabs.length == 0
+ || previousTabIndex > (tabs.length - 1)) {
return;
}
- if (fCurrentTabIndex != -1) {
- ILaunchConfigurationTab tab = tabs[fCurrentTabIndex];
- ILaunchConfigurationWorkingCopy wc = getWorkingCopy();
- if (wc != null) {
- tab.deactivated(wc);
- getActiveTab().activated(wc);
- }
+
+ propagateTabDeactivation(previousTabIndex);
+
+ propagateTabActivation();
+ }
+
+ private void propagateTabDeactivation(int tabIndex) {
+ ILaunchConfigurationWorkingCopy wc = getWorkingCopy();
+
+ if (tabIndex < 0 || wc == null) {
+ return;
}
- fCurrentTabIndex = fTabFolder.getSelectionIndex();
+
+ getTabs()[tabIndex].deactivated(wc);
+ }
+
+ private void propagateTabActivation() {
+ ILaunchConfigurationWorkingCopy wc = getWorkingCopy();
+ ILaunchConfigurationTab activeTab = getActiveTab();
+
+ if (wc == null || activeTab == null) {
+ return;
+ }
+
+ activeTab.activated(wc);
}
/**