From a9ba2f7876a4d64e49bf0b76200fdbd1ae83562f Mon Sep 17 00:00:00 2001 From: Vasili Gulevich Date: Sun, 8 Mar 2026 01:24:49 +0400 Subject: [PATCH] Test MultiPageEditorPart contracts in FormEditor #3764 --- .../META-INF/MANIFEST.MF | 4 +- .../build.properties | 3 +- .../ui/forms/editor/FormEditorMock.java | 98 ++++++++++ .../ui/forms/editor/FormEditorTest.java | 175 ++++++++++++++++++ .../eclipse/ui/tests/forms/AllFormsTests.java | 4 +- tests/org.eclipse.ui.tests.forms/plugin.xml | 14 ++ 6 files changed, 295 insertions(+), 3 deletions(-) create mode 100644 tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/forms/editor/FormEditorMock.java create mode 100644 tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/forms/editor/FormEditorTest.java create mode 100644 tests/org.eclipse.ui.tests.forms/plugin.xml diff --git a/tests/org.eclipse.ui.tests.forms/META-INF/MANIFEST.MF b/tests/org.eclipse.ui.tests.forms/META-INF/MANIFEST.MF index c46a6f017932..d9e88853a9ae 100755 --- a/tests/org.eclipse.ui.tests.forms/META-INF/MANIFEST.MF +++ b/tests/org.eclipse.ui.tests.forms/META-INF/MANIFEST.MF @@ -6,7 +6,9 @@ Bundle-Version: 3.11.100.qualifier Require-Bundle: org.eclipse.ui;bundle-version="3.208.0", org.eclipse.core.runtime, org.eclipse.test.performance, - org.eclipse.ui.forms + org.eclipse.ui.forms, + org.mockito.mockito-core;bundle-version="[5.21.0,6.0.0)", + junit-jupiter-api;bundle-version="[5.14.3,6.0.0)" Bundle-Vendor: Eclipse.org Import-Package: org.junit.jupiter.api;version="[5.14.0,6.0.0)", org.junit.jupiter.api.function;version="[5.14.0,6.0.0)", diff --git a/tests/org.eclipse.ui.tests.forms/build.properties b/tests/org.eclipse.ui.tests.forms/build.properties index e0fa9d81fe37..4fc66a2a1658 100755 --- a/tests/org.eclipse.ui.tests.forms/build.properties +++ b/tests/org.eclipse.ui.tests.forms/build.properties @@ -14,7 +14,8 @@ bin.includes = .,\ META-INF/,\ about.html,\ - test.xml + test.xml,\ + plugin.xml output.. = bin/ source.. = forms/ diff --git a/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/forms/editor/FormEditorMock.java b/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/forms/editor/FormEditorMock.java new file mode 100644 index 000000000000..26a7de7e16dc --- /dev/null +++ b/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/forms/editor/FormEditorMock.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2026 Vasili Gulevich and others + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Vasili Gulevich - initial API and implementation + *******************************************************************************/ +package org.eclipse.ui.forms.editor; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.swt.custom.CTabFolder; +import org.eclipse.swt.custom.CTabItem; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.PartInitException; + +public class FormEditorMock extends FormEditor { + public static final String ID = "org.eclipse.ui.tests.forms.FormEditorMock"; + + public interface Hook { + + default boolean isSaveAsAllowed(FormEditorMock subject) { + return false; + } + + default void addPages(FormEditorMock subject) { + FormPage page1 = new FormPage(subject, "defaultpage", "defaultpage"); + try { + subject.addPage(page1); + } catch (PartInitException e) { + throw new AssertionError(e); + } + } + + default void doSave(FormEditorMock subject) { + } + + default void createContainer(FormEditorMock subject, CTabFolder container) { + } + + default void createItem(FormEditorMock subject, CTabItem item) { + + } + } + + public static final Hook DEFAULT_HOOK = new Hook() { + }; + + public static Hook hook = DEFAULT_HOOK; + + @Override + protected CTabFolder createContainer(Composite parent) { + CTabFolder result = super.createContainer(parent); + hook.createContainer(this, result); + return result; + } + + @Override + protected CTabItem createItem(int index, Control control) { + CTabItem result = super.createItem(index, control); + hook.createItem(this, result); + return result; + } + @Override + protected void addPages() { + hook.addPages(this); + } + + @Override + public void doSave(IProgressMonitor monitor) { + hook.doSave(this); + } + + @Override + public void doSaveAs() { + hook.doSave(this); + } + + @Override + public boolean isSaveAsAllowed() { + return hook.isSaveAsAllowed(this); + } + + public CTabFolder leakContainer() { + return (CTabFolder) getContainer(); + } + + public IEditorPart[] leakPages() { + return pages.stream().toArray(IEditorPart[]::new); + } +} diff --git a/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/forms/editor/FormEditorTest.java b/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/forms/editor/FormEditorTest.java new file mode 100644 index 000000000000..279dedca96c3 --- /dev/null +++ b/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/forms/editor/FormEditorTest.java @@ -0,0 +1,175 @@ +/******************************************************************************* + * Copyright (c) 2026 Vasili Gulevich and others + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Vasili Gulevich - initial API and implementation + *******************************************************************************/ +package org.eclipse.ui.forms.editor; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CTabFolder; +import org.eclipse.swt.custom.CTabItem; +import org.eclipse.swt.widgets.Event; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.forms.editor.FormEditorMock.Hook; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class FormEditorTest { + + private static final IWorkbench WORKBENCH = PlatformUI.getWorkbench(); + + public IEditorInput input = mock(); + + @BeforeEach + public void closeEditors() { + WORKBENCH.getActiveWorkbenchWindow().getActivePage().closeAllEditors(false); + } + + @Test + public void activatePage() throws PartInitException { + FormEditorMock.hook = FormEditorMock.DEFAULT_HOOK; + FormEditorMock subject = (FormEditorMock) WORKBENCH.getActiveWorkbenchWindow().getActivePage().openEditor(input, + FormEditorMock.ID); + assertEquals("defaultpage", subject.getActivePageInstance().getTitle()); + FormPage page1 = addPage(subject, "page1"); + subject.setActivePage("page1"); + dispatch(subject); + assertSame(page1, subject.getActivePageInstance()); + } + + @Test + public void removePage() throws PartInitException { + FormEditorMock.hook = FormEditorMock.DEFAULT_HOOK; + FormEditorMock subject = (FormEditorMock) WORKBENCH.getActiveWorkbenchWindow().getActivePage().openEditor(input, + FormEditorMock.ID); + dispatch(subject); + CTabItem[] tabs = subject.leakContainer().getItems(); + assertEquals(1, tabs.length); + FormPage page1 = addPage(subject, "page1"); + assertSame(page1, subject.findPage("page1")); + tabs = subject.leakContainer().getItems(); + assertEquals(2, tabs.length); + subject.removePage(page1.getIndex()); + dispatch(subject); + assertNull(subject.findPage("page1")); + tabs = subject.leakContainer().getItems(); + assertEquals(1, tabs.length); + } + + @Test + public void disposeTab() throws PartInitException { + FormEditorMock.hook = FormEditorMock.DEFAULT_HOOK; + FormEditorMock subject = (FormEditorMock) WORKBENCH.getActiveWorkbenchWindow().getActivePage().openEditor(input, + FormEditorMock.ID); + dispatch(subject); + CTabItem[] tabs = subject.leakContainer().getItems(); + assertEquals(1, tabs.length); + FormPage page1 = addPage(subject, "page1"); + assertSame(page1, subject.findPage("page1")); + tabs = subject.leakContainer().getItems(); + assertEquals(2, tabs.length); + tabs[tabs.length - 1].dispose(); + dispatch(subject); + tabs = subject.leakContainer().getItems(); + assertEquals(1, tabs.length); + // BUG https://github.com/eclipse-platform/eclipse.platform.ui/issues/3766 + // assertNull(subject.findPage("page1")); + } + + @Test + public void maintainIntegrityOnPageClose() throws PartInitException { + FormEditorMock.hook = new Hook() { + @Override + public void createContainer(FormEditorMock subject, CTabFolder container) { + Hook.super.createContainer(subject, container); + container.setUnselectedCloseVisible(true); + } + + @Override + public void createItem(FormEditorMock subject, CTabItem item) { + Hook.super.createItem(subject, item); + item.setShowClose(true); + } + }; + FormEditorMock subject = (FormEditorMock) WORKBENCH.getActiveWorkbenchWindow().getActivePage().openEditor(input, + FormEditorMock.ID); + FormPage page1 = addPage(subject, "page1"); + CTabFolder tabFolder = subject.leakContainer(); + CTabItem tab = tabFolder.getItem(1); + assertTrue(tab.getShowClose()); + assertSame(page1, subject.findPage("page1")); + Event event = new Event(); + event.type = SWT.MouseDown; + event.button = 1; + event.x = tab.getBounds().x + tab.getBounds().width - 9; + event.y = tab.getBounds().y + tab.getBounds().height/2; + tabFolder.notifyListeners(event.type, event); + dispatch(subject); + event.type = SWT.MouseUp; + tabFolder.notifyListeners(event.type, event); + dispatch(subject); + assertEquals(1, tabFolder.getItems().length); + // BUG https://github.com/eclipse-platform/eclipse.platform.ui/issues/3766 + // assertNull(subject.findPage("page1")); + } + + @Test + public void anEditorIsActiveOnStart() throws PartInitException { + FormEditorMock.hook = FormEditorMock.DEFAULT_HOOK; + FormEditorMock subject = (FormEditorMock) WORKBENCH.getActiveWorkbenchWindow().getActivePage().openEditor(input, + FormEditorMock.ID); + dispatch(subject); + // BUG https://github.com/eclipse-platform/eclipse.platform.ui/issues/3764 + // assertNotNull(subject.getActiveEditor()); + } + + @Test + public void activateEditors() throws PartInitException { + FormEditorMock.hook = FormEditorMock.DEFAULT_HOOK; + FormEditorMock subject = (FormEditorMock) WORKBENCH.getActiveWorkbenchWindow().getActivePage().openEditor(input, + FormEditorMock.ID); + FormPage page1 = addPage(subject, "page1"); + // BUG https://github.com/eclipse-platform/eclipse.platform.ui/issues/3764 + // assertEveryTabCanBeActive(subject); + } + + private void assertEveryTabCanBeActive(FormEditorMock subject) { + dispatch(subject); + for (IEditorPart part : subject.leakPages()) { + subject.setActiveEditor(part); + dispatch(subject); + assertSame(part, subject.getActiveEditor()); + } + } + + private void dispatch(FormEditorMock subject) { + while (subject.leakContainer().getDisplay().readAndDispatch()) { + } + } + + private FormPage addPage(FormEditorMock subject, String id) throws PartInitException { + FormPage page1 = new FormPage(subject, id, id); + subject.addPage(page1); + dispatch(subject); + return page1; + } + +} diff --git a/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/AllFormsTests.java b/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/AllFormsTests.java index 1fbd271b2d8c..2df3ad4df99f 100755 --- a/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/AllFormsTests.java +++ b/tests/org.eclipse.ui.tests.forms/forms/org/eclipse/ui/tests/forms/AllFormsTests.java @@ -16,6 +16,7 @@ package org.eclipse.ui.tests.forms; +import org.eclipse.ui.forms.editor.FormEditorTest; import org.eclipse.ui.tests.forms.events.AllEventsTests; import org.eclipse.ui.tests.forms.layout.AllLayoutTests; import org.eclipse.ui.tests.forms.util.AllUtilityTests; @@ -32,7 +33,8 @@ AllEventsTests.class, // AllLayoutTests.class, // AllUtilityTests.class, // - AllWidgetsTests.class // + AllWidgetsTests.class, // + FormEditorTest.class // }) public class AllFormsTests { diff --git a/tests/org.eclipse.ui.tests.forms/plugin.xml b/tests/org.eclipse.ui.tests.forms/plugin.xml new file mode 100644 index 000000000000..2a1c9fb1c0c8 --- /dev/null +++ b/tests/org.eclipse.ui.tests.forms/plugin.xml @@ -0,0 +1,14 @@ + + + + + + + + +