diff --git a/debug/org.eclipse.debug.ui.tests/.classpath b/debug/org.eclipse.debug.ui.tests/.classpath new file mode 100644 index 00000000000..675a5e2962b --- /dev/null +++ b/debug/org.eclipse.debug.ui.tests/.classpath @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/debug/org.eclipse.debug.ui.tests/.project b/debug/org.eclipse.debug.ui.tests/.project new file mode 100644 index 00000000000..367ebb15e96 --- /dev/null +++ b/debug/org.eclipse.debug.ui.tests/.project @@ -0,0 +1,28 @@ + + + org.eclipse.debug.ui.tests + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/debug/org.eclipse.debug.ui.tests/.settings/org.eclipse.core.resources.prefs b/debug/org.eclipse.debug.ui.tests/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..99f26c0203a --- /dev/null +++ b/debug/org.eclipse.debug.ui.tests/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/debug/org.eclipse.debug.ui.tests/.settings/org.eclipse.jdt.core.prefs b/debug/org.eclipse.debug.ui.tests/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..62ef3488cc0 --- /dev/null +++ b/debug/org.eclipse.debug.ui.tests/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,9 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 +org.eclipse.jdt.core.compiler.compliance=17 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=17 diff --git a/debug/org.eclipse.debug.ui.tests/META-INF/MANIFEST.MF b/debug/org.eclipse.debug.ui.tests/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..4cc224fcb4f --- /dev/null +++ b/debug/org.eclipse.debug.ui.tests/META-INF/MANIFEST.MF @@ -0,0 +1,18 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %Bundle-Name +Bundle-SymbolicName: org.eclipse.debug.ui.tests;singleton:=true +Bundle-Vendor: %Bundle-Vendor +Bundle-Version: 1.0.123.qualifier +Import-Package: org.junit.jupiter.api, + org.junit.jupiter.api.function;version="5.10.1", + org.junit.platform.suite.api, + org.opentest4j +Require-Bundle: org.eclipse.jface;bundle-version="[3.32.0,4.0.0)", + org.eclipse.debug.core;bundle-version="3.21.200", + org.eclipse.debug.ui;bundle-version="3.18.200", + org.eclipse.core.runtime;bundle-version="3.30.0", + org.junit, + org.eclipse.jdt.launching +Automatic-Module-Name: org.eclipse.debug.ui.tests +Bundle-RequiredExecutionEnvironment: JavaSE-17 diff --git a/debug/org.eclipse.debug.ui.tests/OSGI-INF/l10n/bundle.properties b/debug/org.eclipse.debug.ui.tests/OSGI-INF/l10n/bundle.properties new file mode 100644 index 00000000000..285c7edcfe0 --- /dev/null +++ b/debug/org.eclipse.debug.ui.tests/OSGI-INF/l10n/bundle.properties @@ -0,0 +1,2 @@ +Bundle-Vendor = Eclipse.org +Bundle-Name =Debug UI Test Plugin \ No newline at end of file diff --git a/debug/org.eclipse.debug.ui.tests/build.properties b/debug/org.eclipse.debug.ui.tests/build.properties new file mode 100644 index 00000000000..e9863e281ea --- /dev/null +++ b/debug/org.eclipse.debug.ui.tests/build.properties @@ -0,0 +1,5 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.xml diff --git a/debug/org.eclipse.debug.ui.tests/plugin.xml b/debug/org.eclipse.debug.ui.tests/plugin.xml new file mode 100644 index 00000000000..c031d50f982 --- /dev/null +++ b/debug/org.eclipse.debug.ui.tests/plugin.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + diff --git a/debug/org.eclipse.debug.ui.tests/src/org/eclipse/debug/internal/ui/tests/launchConfigurations/LaunchConfigurationTabGroupViewerTest.java b/debug/org.eclipse.debug.ui.tests/src/org/eclipse/debug/internal/ui/tests/launchConfigurations/LaunchConfigurationTabGroupViewerTest.java new file mode 100644 index 00000000000..18406b02f82 --- /dev/null +++ b/debug/org.eclipse.debug.ui.tests/src/org/eclipse/debug/internal/ui/tests/launchConfigurations/LaunchConfigurationTabGroupViewerTest.java @@ -0,0 +1,180 @@ +package org.eclipse.debug.internal.ui.tests.launchConfigurations; + +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; + +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.debug.ui.tests.tabs.SpyTab; +import org.eclipse.swt.widgets.Display; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.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; + + @BeforeEach + private void createDialog() throws CoreException { + fLaunchConfigurationType = getLaunchManager().getLaunchConfigurationType(LAUNCH_CONFIG_TYPE_ID); + ILaunchConfigurationTabGroup tabGroup = getLaunchConfigurationTabGroup(fLaunchConfigurationType); + + fLaunchConfigurationsDialog = (LaunchConfigurationsDialog) createLaunchConfigurationDialog(); + tabGroup.createTabs(fLaunchConfigurationsDialog, ILaunchManager.RUN_MODE); + } + + @Test + void tesAllTabsAreInitializedByDefault() throws CoreException { + // Create a launch configuration with a unique name + ThrowingRunnable createAndSelect1LaunchConfig = () -> { + fLaunchConfigurationsDialog.getTabViewer().setInput(createLaunchConfigurationInstance()); + }; + + final ILaunchConfigurationTab[] tabs = runOnDialog(createAndSelect1LaunchConfig); + Assertions.assertAll(() -> { + for (int i = 0; i < tabs.length; i++) { + assertThat("Tab " + i + " was not initialized", ((SpyTab) tabs[i]).isInitialized()); + } + }); + } + + @Test + void testFirstTabIsActivatedByDefault() throws CoreException { + // 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 + void testOtherTabInOtherConfigIsActivated() throws CoreException { + 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 + void testOtherTabIsActivated() throws CoreException { + 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(); + } + + /** + * Returns the standard java launch tab group + * + * @return the standard java launch tab group + * @throws CoreException + * + */ + protected ILaunchConfigurationTabGroup getLaunchConfigurationTabGroup( + ILaunchConfigurationType launchConfigurationType) throws CoreException { + return LaunchConfigurationPresentationManager.getDefault().getTabGroup(launchConfigurationType, + LAUNCH_CONFIG_MODE); + } + + /** + * Returns an instance of the launch configuration dialog on the the specified + * launch mode + * + * @return an new instance of IlaunchConfigurationDialog + * + */ + protected ILaunchConfigurationDialog createLaunchConfigurationDialog() { + return new LaunchConfigurationsDialog(null, DebugUIPlugin.getDefault().getLaunchConfigurationManager() + .getLaunchGroup(IDebugUIConstants.ID_DEBUG_LAUNCH_GROUP)); + } + + /** + * Returns the launch manager + * + * @return launch manager + */ + protected ILaunchManager getLaunchManager() { + return DebugPlugin.getDefault().getLaunchManager(); + } +} diff --git a/debug/org.eclipse.debug.ui.tests/src/org/eclipse/debug/ui/tests/AllTests.java b/debug/org.eclipse.debug.ui.tests/src/org/eclipse/debug/ui/tests/AllTests.java new file mode 100644 index 00000000000..f86fd0985fb --- /dev/null +++ b/debug/org.eclipse.debug.ui.tests/src/org/eclipse/debug/ui/tests/AllTests.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * 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.ui.tests; + +import org.eclipse.debug.internal.ui.tests.launchConfigurations.LaunchConfigurationTabGroupViewerTest; +import org.junit.platform.suite.api.SelectClasses; +import org.junit.platform.suite.api.Suite; + +@Suite +@SelectClasses({ LaunchConfigurationTabGroupViewerTest.class}) +public class AllTests { + +} diff --git a/debug/org.eclipse.debug.ui.tests/src/org/eclipse/debug/ui/tests/tabs/SpyTab.java b/debug/org.eclipse.debug.ui.tests/src/org/eclipse/debug/ui/tests/tabs/SpyTab.java new file mode 100644 index 00000000000..4412dd7e49d --- /dev/null +++ b/debug/org.eclipse.debug.ui.tests/src/org/eclipse/debug/ui/tests/tabs/SpyTab.java @@ -0,0 +1,64 @@ +package org.eclipse.debug.ui.tests.tabs; + +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.debug.ui.AbstractLaunchConfigurationTab; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; + +/** + * 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 3 are necessary because I need 3 tabs in the launch config and using + // always the same kind of tab produces incorrect results + public static class SpyTabA extends SpyTab { + } + + public static class SpyTabB extends SpyTab { + } + + public static class SpyTabC extends SpyTab { + } +} diff --git a/debug/org.eclipse.debug.ui.tests/src/org/eclipse/debug/ui/tests/tabs/SpyTabGroup.java b/debug/org.eclipse.debug.ui.tests/src/org/eclipse/debug/ui/tests/tabs/SpyTabGroup.java new file mode 100644 index 00000000000..a28d238523a --- /dev/null +++ b/debug/org.eclipse.debug.ui.tests/src/org/eclipse/debug/ui/tests/tabs/SpyTabGroup.java @@ -0,0 +1,17 @@ +package org.eclipse.debug.ui.tests.tabs; + +import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup; +import org.eclipse.debug.ui.ILaunchConfigurationDialog; +import org.eclipse.debug.ui.ILaunchConfigurationTab; +import org.eclipse.debug.ui.tests.tabs.SpyTab.SpyTabA; +import org.eclipse.debug.ui.tests.tabs.SpyTab.SpyTabB; +import org.eclipse.debug.ui.tests.tabs.SpyTab.SpyTabC;; + +public class SpyTabGroup extends AbstractLaunchConfigurationTabGroup { + + @Override + public void createTabs(ILaunchConfigurationDialog dialog, String mode) { + setTabs(new ILaunchConfigurationTab[] { new SpyTabA(), new SpyTabB(), new SpyTabC() }); + } + +} \ No newline at end of file diff --git a/debug/org.eclipse.debug.ui.tests/test.xml b/debug/org.eclipse.debug.ui.tests/test.xml new file mode 100644 index 00000000000..bee73e61f4d --- /dev/null +++ b/debug/org.eclipse.debug.ui.tests/test.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/debug/org.eclipse.debug.ui/META-INF/MANIFEST.MF b/debug/org.eclipse.debug.ui/META-INF/MANIFEST.MF index 35261b4ac26..248d1bd2a89 100644 --- a/debug/org.eclipse.debug.ui/META-INF/MANIFEST.MF +++ b/debug/org.eclipse.debug.ui/META-INF/MANIFEST.MF @@ -13,7 +13,8 @@ Export-Package: org.eclipse.debug.internal.ui; org.eclipse.debug.tests, org.eclipse.debug.examples.ui, org.eclipse.debug.examples.mixedmode, - org.eclipse.debug.ui.launchview", + org.eclipse.debug.ui.launchview, + org.eclipse.debug.ui.tests", org.eclipse.debug.internal.ui.actions;x-internal:=true, org.eclipse.debug.internal.ui.actions.breakpointGroups;x-internal:=true, org.eclipse.debug.internal.ui.actions.breakpointSortBy;x-internal:=true, @@ -35,7 +36,7 @@ Export-Package: org.eclipse.debug.internal.ui; org.eclipse.debug.internal.ui.hover;x-internal:=true, org.eclipse.debug.internal.ui.importexport.breakpoints;x-internal:=true, org.eclipse.debug.internal.ui.importexport.launchconfigurations;x-internal:=true, - org.eclipse.debug.internal.ui.launchConfigurations;x-friends:="org.eclipse.debug.tests", + org.eclipse.debug.internal.ui.launchConfigurations;x-friends:="org.eclipse.debug.tests,org.eclipse.debug.ui.tests", org.eclipse.debug.internal.ui.memory;x-internal:=true, org.eclipse.debug.internal.ui.memory.provisional;x-internal:=true, org.eclipse.debug.internal.ui.model.elements;x-friends:="org.eclipse.debug.examples.ui,org.eclipse.jdt.debug.ui,org.eclipse.wst.jsdt.debug.ui", 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..c3f8c32f4cc 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 oldCurrentTabIndex = fCurrentTabIndex; + fCurrentTabIndex = fTabFolder.getSelectionIndex(); + ILaunchConfigurationTab[] tabs = getTabs(); - if (fCurrentTabIndex == fTabFolder.getSelectionIndex() || tabs == null || tabs.length == 0 || fCurrentTabIndex > (tabs.length - 1)) { + if (oldCurrentTabIndex == fCurrentTabIndex || tabs == null || tabs.length == 0 + || oldCurrentTabIndex > (tabs.length - 1)) { return; } - if (fCurrentTabIndex != -1) { - ILaunchConfigurationTab tab = tabs[fCurrentTabIndex]; - ILaunchConfigurationWorkingCopy wc = getWorkingCopy(); - if (wc != null) { - tab.deactivated(wc); - getActiveTab().activated(wc); - } + + deactivateTab(oldCurrentTabIndex); + + activateCurrentlyActiveTab(); + } + + private void deactivateTab(int tabIndex) { + ILaunchConfigurationWorkingCopy wc = getWorkingCopy(); + + if (tabIndex < 0 || wc == null) { + return; } - fCurrentTabIndex = fTabFolder.getSelectionIndex(); + + getTabs()[tabIndex].deactivated(wc); + } + + private void activateCurrentlyActiveTab() { + ILaunchConfigurationWorkingCopy wc = getWorkingCopy(); + ILaunchConfigurationTab activeTab = getActiveTab(); + + if (wc == null || activeTab == null) { + return; + } + + activeTab.activated(wc); } /** diff --git a/debug/pom.xml b/debug/pom.xml index e0e30f92cdd..a9ef91c731e 100644 --- a/debug/pom.xml +++ b/debug/pom.xml @@ -34,6 +34,7 @@ org.eclipse.debug.ui org.eclipse.debug.ui.launchview org.eclipse.debug.ui.launchview.tests + org.eclipse.debug.ui.tests org.eclipse.ui.console org.eclipse.ui.externaltools org.eclipse.unittest.ui