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); } /**