diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/graphics/ImageDataTestHelper.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/graphics/ImageDataTestHelper.java index 1174db882f8..98265d833cc 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/graphics/ImageDataTestHelper.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/graphics/ImageDataTestHelper.java @@ -18,6 +18,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.Comparator; import java.util.Random; import org.eclipse.swt.graphics.Image; @@ -212,6 +213,28 @@ public static BlitTestInfo blit(BlitTestInfo srcInfo, int dstInfo_depth, int dst return new BlitTestInfo(dstInfo_depth, dstInfo_scale, dstInfo_byteOrder, dstInfo_isDirect, dst); } + /** + * Compares ImageData, allows for change in things like bit depth by comparing pixel values rather + * that the raw data like {@link #assertImageDataEqual(ImageData, ImageData, ImageData)} + */ + public static Comparator imageDataComparator() { + return Comparator.comparingInt(d -> d.width) // + .thenComparing(d -> d.height) // + .thenComparing((ImageData firstData, ImageData secondData) -> { + for (int x = 0; x < firstData.width; x++) { + for (int y = 0; y < firstData.height; y++) { + if (firstData.getPixel(x, y) != secondData.getPixel(x, y)) { + return -1; + } + if (firstData.getAlpha(x, y) != secondData.getAlpha(x, y)) { + return -1; + } + } + } + return 0; + }); + } + public static void assertImageDataEqual(ImageData source, ImageData actual, ImageData expected) { if (TEST_BLIT_SHOW_IMAGES) { Image[] images = new Image[3]; diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/ClipboardBase.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/ClipboardBase.java new file mode 100644 index 00000000000..815f090065f --- /dev/null +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/ClipboardBase.java @@ -0,0 +1,188 @@ +/******************************************************************************* + * Copyright (c) 2025 Kichwa Coders Canada, Inc. + * + * 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 + *******************************************************************************/ +package org.eclipse.swt.tests.junit; + +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assumptions.assumeFalse; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.layout.RowLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.RepeatedTest; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +/** + * Base class for tests that test clipboard and transfer types + */ +public class ClipboardBase { + + /** + * See {@link #openAndFocusShell(boolean)} - some tests require user to actually + * interact with the shell. + * + * Default to skipping tests requiring "real" activation on GHA and Jenkins. + * + * true: skip tests false: don't skip tests + * null: unknown whether to skip tests yet + */ + private static Boolean skipTestsRequiringButtonPress = (Boolean.parseBoolean(System.getenv("GITHUB_ACTIONS")) + || System.getenv("JOB_NAME") != null) ? true : null; + private static int uniqueId = 1; + protected Display display; + protected Shell shell; + protected Clipboard clipboard; + protected RemoteClipboard remote; + + /** + * Return the set of clipboards that are supported on this platform, this method + * is referenced in the {@link MethodSource} annotations on the + * {@link ParameterizedTest}s within this class. + */ + public static List supportedClipboardIds() { + if (SwtTestUtil.isGTK) { + return List.of(DND.CLIPBOARD, DND.SELECTION_CLIPBOARD); + } + return List.of(DND.CLIPBOARD); + } + + @BeforeEach + public void setUp() { + display = Display.getCurrent(); + if (display == null) { + display = Display.getDefault(); + } + clipboard = new Clipboard(display); + } + + /** + * Note: Wayland backend does not allow access to system clipboard from + * non-focussed windows. So we have to create/open and focus a window here so + * that clipboard operations work. + * + * Additionally, if we want to provide data to the clipboard, we require user + * interaction on the created shell. Therefore if forSetContents is true the + * tester needs to press a button for test to work. + * + * If there is no user interaction (button not pressed) then the test is + * skipped, rather than failed, and subsequent tests requiring user interaction + * as skipped too. See {@link #skipTestsRequiringButtonPress} + */ + protected void openAndFocusShell(boolean forSetContents) throws InterruptedException { + assertNull(shell); + shell = new Shell(display); + + boolean requireUserPress = forSetContents && SwtTestUtil.isWayland(); + if (requireUserPress) { + assumeFalse(skipTestsRequiringButtonPress != null && skipTestsRequiringButtonPress, + "Skipping tests that require user input"); + + AtomicBoolean pressed = new AtomicBoolean(false); + shell.setLayout(new RowLayout(SWT.VERTICAL)); + Button button = new Button(shell, SWT.PUSH); + button.setText("Press me!"); + button.addListener(SWT.Selection, (e) -> pressed.set(true)); + button.setSize(200, 50); + Label label = new Label(shell, SWT.NONE); + label.setText(""" + Press the button to tell Wayland that you really want this window to have access to clipboard. + This is needed on Wayland because only really focussed programs are allowed to write to the + global keyboard. + + If you don't press this button soon, the test will be skipped and you won't be asked again. + """); + Label timeleft = new Label(shell, SWT.NONE); + timeleft.setText("Time left to press button: XXXXXXXXXXXXXXXXXXXX seconds"); + + SwtTestUtil.openShell(shell); + + // If we know there is a tester pressing the buttons, allow them + // a little grace on the timeout. If we don't know if there is a + // tester around, skip tests fairly quickly and don't + // ask again. + int timeout = skipTestsRequiringButtonPress == null ? 1500 : 10000; + long startTime = System.nanoTime(); + SwtTestUtil.processEvents(timeout, () -> { + long nowTime = System.nanoTime(); + long timeLeft = nowTime - startTime; + long timeLeftMs = timeout - (timeLeft / 1_000_000); + double timeLeftS = timeLeftMs / 1_000.0d; + timeleft.setText("Time left to press button: " + timeLeftS + " seconds"); + return pressed.get(); + }); + boolean userPressedButton = pressed.get(); + if (userPressedButton) { + skipTestsRequiringButtonPress = false; + } else { + skipTestsRequiringButtonPress = true; + assumeTrue(false, "Skipping tests that require user input"); + } + } else { + SwtTestUtil.openShell(shell); + } + + } + + /** + * Note: Wayland backend does not allow access to system clipboard from + * non-focussed windows. So we have to open and focus remote here so that + * clipboard operations work. + */ + protected void openAndFocusRemote() throws Exception { + assertNull(remote); + remote = new RemoteClipboard(); + remote.start(); + + /* + * If/when OpenJDK Project Wakefield gets merged then we may need to wait for + * button pressed on the swing app just like the SWT app. This may also be + * needed if Wayland implementations get more restrictive on X apps too. + */ + // remote.waitForButtonPress(); + } + + @AfterEach + public void tearDown() throws Exception { + try { + if (remote != null) { + remote.stop(); + } + } finally { + if (clipboard != null) { + clipboard.dispose(); + } + if (shell != null) { + shell.dispose(); + } + SwtTestUtil.processEvents(); + } + } + + /** + * Make sure to always copy/paste unique strings - this ensures that tests run + * under {@link RepeatedTest}s don't false pass because of clipboard value on + * previous iteration. + */ + protected static String getUniqueTestString() { + return "Hello World " + uniqueId++; + } +} \ No newline at end of file diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_dnd_Clipboard.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_dnd_Clipboard.java index a2b23726144..04ef387f372 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_dnd_Clipboard.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_dnd_Clipboard.java @@ -14,31 +14,19 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assumptions.assumeFalse; import static org.junit.jupiter.api.Assumptions.assumeTrue; import java.util.Arrays; -import java.util.List; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicBoolean; -import org.eclipse.swt.SWT; -import org.eclipse.swt.dnd.Clipboard; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.RTFTransfer; import org.eclipse.swt.dnd.TextTransfer; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.dnd.TransferData; -import org.eclipse.swt.layout.RowLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Shell; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; @@ -54,162 +42,19 @@ */ @Tag("clipboard") @TestMethodOrder(OrderAnnotation.class) // run tests needing button presses first -public class Test_org_eclipse_swt_dnd_Clipboard { +public class Test_org_eclipse_swt_dnd_Clipboard extends ClipboardBase { - /** - * See {@link #openAndFocusShell(boolean)} - some tests require user to actually - * interact with the shell. - * - * Default to skipping tests requiring "real" activation on GHA and Jenkins. - * - * true: skip tests false: don't skip tests - * null: unknown whether to skip tests yet - */ - private static Boolean skipTestsRequiringButtonPress = (Boolean.parseBoolean(System.getenv("GITHUB_ACTIONS")) - || System.getenv("JOB_NAME") != null) ? true : null; - private static int uniqueId = 1; - private Display display; - private Shell shell; - private Clipboard clipboard; private TextTransfer textTransfer; private RTFTransfer rtfTransfer; - private RemoteClipboard remote; + @Override @BeforeEach public void setUp() { - display = Display.getCurrent(); - if (display == null) { - display = Display.getDefault(); - } - clipboard = new Clipboard(display); + super.setUp(); textTransfer = TextTransfer.getInstance(); rtfTransfer = RTFTransfer.getInstance(); } - /** - * Note: Wayland backend does not allow access to system clipboard from - * non-focussed windows. So we have to create/open and focus a window here so - * that clipboard operations work. - * - * Additionally, if we want to provide data to the clipboard, we require user - * interaction on the created shell. Therefore if forSetContents is true the - * tester needs to press a button for test to work. - * - * If there is no user interaction (button not pressed) then the test is - * skipped, rather than failed, and subsequent tests requiring user interaction - * as skipped too. See {@link #skipTestsRequiringButtonPress} - */ - private void openAndFocusShell(boolean forSetContents) throws InterruptedException { - assertNull(shell); - shell = new Shell(display); - - boolean requireUserPress = forSetContents && SwtTestUtil.isWayland(); - if (requireUserPress) { - assumeFalse(skipTestsRequiringButtonPress != null && skipTestsRequiringButtonPress, - "Skipping tests that require user input"); - - AtomicBoolean pressed = new AtomicBoolean(false); - shell.setLayout(new RowLayout(SWT.VERTICAL)); - Button button = new Button(shell, SWT.PUSH); - button.setText("Press me!"); - button.addListener(SWT.Selection, (e) -> pressed.set(true)); - button.setSize(200, 50); - Label label = new Label(shell, SWT.NONE); - label.setText(""" - Press the button to tell Wayland that you really want this window to have access to clipboard. - This is needed on Wayland because only really focussed programs are allowed to write to the - global keyboard. - - If you don't press this button soon, the test will be skipped and you won't be asked again. - """); - Label timeleft = new Label(shell, SWT.NONE); - timeleft.setText("Time left to press button: XXXXXXXXXXXXXXXXXXXX seconds"); - - SwtTestUtil.openShell(shell); - - // If we know there is a tester pressing the buttons, allow them - // a little grace on the timeout. If we don't know if there is a - // tester around, skip tests fairly quickly and don't - // ask again. - int timeout = skipTestsRequiringButtonPress == null ? 1500 : 10000; - long startTime = System.nanoTime(); - SwtTestUtil.processEvents(timeout, () -> { - long nowTime = System.nanoTime(); - long timeLeft = nowTime - startTime; - long timeLeftMs = timeout - (timeLeft / 1_000_000); - double timeLeftS = timeLeftMs / 1_000.0d; - timeleft.setText("Time left to press button: " + timeLeftS + " seconds"); - return pressed.get(); - }); - boolean userPressedButton = pressed.get(); - if (userPressedButton) { - skipTestsRequiringButtonPress = false; - } else { - skipTestsRequiringButtonPress = true; - assumeTrue(false, "Skipping tests that require user input"); - } - } else { - SwtTestUtil.openShell(shell); - } - - } - - /** - * Note: Wayland backend does not allow access to system clipboard from - * non-focussed windows. So we have to open and focus remote here so that - * clipboard operations work. - */ - private void openAndFocusRemote() throws Exception { - assertNull(remote); - remote = new RemoteClipboard(); - remote.start(); - - /* - * If/when OpenJDK Project Wakefield gets merged then we may need to wait for - * button pressed on the swing app just like the SWT app. This may also be - * needed if Wayland implementations get more restrictive on X apps too. - */ - // remote.waitForButtonPress(); - } - - @AfterEach - public void tearDown() throws Exception { - try { - if (remote != null) { - remote.stop(); - } - } finally { - if (clipboard != null) { - clipboard.dispose(); - } - if (shell != null) { - shell.dispose(); - } - SwtTestUtil.processEvents(); - } - } - - /** - * Make sure to always copy/paste unique strings - this ensures that tests run - * under {@link RepeatedTest}s don't false pass because of clipboard value on - * previous iteration. - */ - private String getUniqueTestString() { - return "Hello World " + uniqueId++; - } - - /** - * Return the set of clipboards that are supported on this platform, this method - * is referenced in the {@link MethodSource} annotations on the - * {@link ParameterizedTest}s within this class. - */ - public static List supportedClipboardIds() { - if (SwtTestUtil.isGTK) { - return List.of(DND.CLIPBOARD, DND.SELECTION_CLIPBOARD); - } - return List.of(DND.CLIPBOARD); - } - /** * Test that the remote application clipboard works */ diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_Image.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_Image.java index 78bb27b4ed2..6326d0d9c8d 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_Image.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_Image.java @@ -15,6 +15,7 @@ package org.eclipse.swt.tests.junit; +import static org.eclipse.swt.tests.graphics.ImageDataTestHelper.imageDataComparator; import static org.eclipse.swt.tests.junit.SwtTestUtil.assertSWTProblem; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -31,7 +32,6 @@ import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Comparator; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; @@ -1158,23 +1158,5 @@ public void test_gcOnImageGcDrawer_imageDataAtNonDeviceZoom() { } } -private Comparator imageDataComparator() { - return Comparator.comparingInt(d -> d.width) // - .thenComparing(d -> d.height) // - .thenComparing((ImageData firstData, ImageData secondData) -> { - for (int x = 0; x < firstData.width; x++) { - for (int y = 0; y < firstData.height; y++) { - if (firstData.getPixel(x, y) != secondData.getPixel(x, y)) { - return -1; - } - if (firstData.getAlpha(x, y) != secondData.getAlpha(x, y)) { - return -1; - } - } - } - return 0; - }); -} - }