Skip to content

Commit 0cbf8f2

Browse files
committed
Extract a ClipboardBase class from Test_org_eclipse_swt_dnd_Clipboard
This will aid the subsequent authoring of tests for the dnd Transfer classes
1 parent 551199e commit 0cbf8f2

File tree

2 files changed

+191
-158
lines changed

2 files changed

+191
-158
lines changed
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Kichwa Coders Canada, Inc.
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*******************************************************************************/
11+
package org.eclipse.swt.tests.junit;
12+
13+
import static org.junit.jupiter.api.Assertions.assertNull;
14+
import static org.junit.jupiter.api.Assumptions.assumeFalse;
15+
import static org.junit.jupiter.api.Assumptions.assumeTrue;
16+
17+
import java.util.List;
18+
import java.util.concurrent.atomic.AtomicBoolean;
19+
20+
import org.eclipse.swt.SWT;
21+
import org.eclipse.swt.dnd.Clipboard;
22+
import org.eclipse.swt.dnd.DND;
23+
import org.eclipse.swt.layout.RowLayout;
24+
import org.eclipse.swt.widgets.Button;
25+
import org.eclipse.swt.widgets.Display;
26+
import org.eclipse.swt.widgets.Label;
27+
import org.eclipse.swt.widgets.Shell;
28+
import org.junit.jupiter.api.AfterEach;
29+
import org.junit.jupiter.api.BeforeEach;
30+
import org.junit.jupiter.api.RepeatedTest;
31+
import org.junit.jupiter.params.ParameterizedTest;
32+
import org.junit.jupiter.params.provider.MethodSource;
33+
34+
/**
35+
* Base class for tests that test clipboard and transfer types
36+
*/
37+
public class ClipboardBase {
38+
39+
/**
40+
* See {@link #openAndFocusShell(boolean)} - some tests require user to actually
41+
* interact with the shell.
42+
*
43+
* Default to skipping tests requiring "real" activation on GHA and Jenkins.
44+
*
45+
* <code>true</code>: skip tests <code>false</code>: don't skip tests
46+
* <code>null</code>: unknown whether to skip tests yet
47+
*/
48+
private static Boolean skipTestsRequiringButtonPress = (Boolean.parseBoolean(System.getenv("GITHUB_ACTIONS"))
49+
|| System.getenv("JOB_NAME") != null) ? true : null;
50+
private static int uniqueId = 1;
51+
protected Display display;
52+
protected Shell shell;
53+
protected Clipboard clipboard;
54+
protected RemoteClipboard remote;
55+
56+
/**
57+
* Return the set of clipboards that are supported on this platform, this method
58+
* is referenced in the {@link MethodSource} annotations on the
59+
* {@link ParameterizedTest}s within this class.
60+
*/
61+
public static List<Integer> supportedClipboardIds() {
62+
if (SwtTestUtil.isGTK) {
63+
return List.of(DND.CLIPBOARD, DND.SELECTION_CLIPBOARD);
64+
}
65+
return List.of(DND.CLIPBOARD);
66+
}
67+
68+
@BeforeEach
69+
public void setUp() {
70+
display = Display.getCurrent();
71+
if (display == null) {
72+
display = Display.getDefault();
73+
}
74+
clipboard = new Clipboard(display);
75+
}
76+
77+
/**
78+
* Note: Wayland backend does not allow access to system clipboard from
79+
* non-focussed windows. So we have to create/open and focus a window here so
80+
* that clipboard operations work.
81+
*
82+
* Additionally, if we want to provide data to the clipboard, we require user
83+
* interaction on the created shell. Therefore if forSetContents is true the
84+
* tester needs to press a button for test to work.
85+
*
86+
* If there is no user interaction (button not pressed) then the test is
87+
* skipped, rather than failed, and subsequent tests requiring user interaction
88+
* as skipped too. See {@link #skipTestsRequiringButtonPress}
89+
*/
90+
protected void openAndFocusShell(boolean forSetContents) throws InterruptedException {
91+
assertNull(shell);
92+
shell = new Shell(display);
93+
94+
boolean requireUserPress = forSetContents && SwtTestUtil.isWayland();
95+
if (requireUserPress) {
96+
assumeFalse(skipTestsRequiringButtonPress != null && skipTestsRequiringButtonPress,
97+
"Skipping tests that require user input");
98+
99+
AtomicBoolean pressed = new AtomicBoolean(false);
100+
shell.setLayout(new RowLayout(SWT.VERTICAL));
101+
Button button = new Button(shell, SWT.PUSH);
102+
button.setText("Press me!");
103+
button.addListener(SWT.Selection, (e) -> pressed.set(true));
104+
button.setSize(200, 50);
105+
Label label = new Label(shell, SWT.NONE);
106+
label.setText("""
107+
Press the button to tell Wayland that you really want this window to have access to clipboard.
108+
This is needed on Wayland because only really focussed programs are allowed to write to the
109+
global keyboard.
110+
111+
If you don't press this button soon, the test will be skipped and you won't be asked again.
112+
""");
113+
Label timeleft = new Label(shell, SWT.NONE);
114+
timeleft.setText("Time left to press button: XXXXXXXXXXXXXXXXXXXX seconds");
115+
116+
SwtTestUtil.openShell(shell);
117+
118+
// If we know there is a tester pressing the buttons, allow them
119+
// a little grace on the timeout. If we don't know if there is a
120+
// tester around, skip tests fairly quickly and don't
121+
// ask again.
122+
int timeout = skipTestsRequiringButtonPress == null ? 1500 : 10000;
123+
long startTime = System.nanoTime();
124+
SwtTestUtil.processEvents(timeout, () -> {
125+
long nowTime = System.nanoTime();
126+
long timeLeft = nowTime - startTime;
127+
long timeLeftMs = timeout - (timeLeft / 1_000_000);
128+
double timeLeftS = timeLeftMs / 1_000.0d;
129+
timeleft.setText("Time left to press button: " + timeLeftS + " seconds");
130+
return pressed.get();
131+
});
132+
boolean userPressedButton = pressed.get();
133+
if (userPressedButton) {
134+
skipTestsRequiringButtonPress = false;
135+
} else {
136+
skipTestsRequiringButtonPress = true;
137+
assumeTrue(false, "Skipping tests that require user input");
138+
}
139+
} else {
140+
SwtTestUtil.openShell(shell);
141+
}
142+
143+
}
144+
145+
/**
146+
* Note: Wayland backend does not allow access to system clipboard from
147+
* non-focussed windows. So we have to open and focus remote here so that
148+
* clipboard operations work.
149+
*/
150+
protected void openAndFocusRemote() throws Exception {
151+
assertNull(remote);
152+
remote = new RemoteClipboard();
153+
remote.start();
154+
155+
/*
156+
* If/when OpenJDK Project Wakefield gets merged then we may need to wait for
157+
* button pressed on the swing app just like the SWT app. This may also be
158+
* needed if Wayland implementations get more restrictive on X apps too.
159+
*/
160+
// remote.waitForButtonPress();
161+
}
162+
163+
@AfterEach
164+
public void tearDown() throws Exception {
165+
try {
166+
if (remote != null) {
167+
remote.stop();
168+
}
169+
} finally {
170+
if (clipboard != null) {
171+
clipboard.dispose();
172+
}
173+
if (shell != null) {
174+
shell.dispose();
175+
}
176+
SwtTestUtil.processEvents();
177+
}
178+
}
179+
180+
/**
181+
* Make sure to always copy/paste unique strings - this ensures that tests run
182+
* under {@link RepeatedTest}s don't false pass because of clipboard value on
183+
* previous iteration.
184+
*/
185+
protected static String getUniqueTestString() {
186+
return "Hello World " + uniqueId++;
187+
}
188+
}

tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_dnd_Clipboard.java

Lines changed: 3 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -14,31 +14,19 @@
1414
import static org.junit.jupiter.api.Assertions.assertNotNull;
1515
import static org.junit.jupiter.api.Assertions.assertNull;
1616
import static org.junit.jupiter.api.Assertions.assertTrue;
17-
import static org.junit.jupiter.api.Assumptions.assumeFalse;
1817
import static org.junit.jupiter.api.Assumptions.assumeTrue;
1918

2019
import java.util.Arrays;
21-
import java.util.List;
2220
import java.util.concurrent.CompletableFuture;
23-
import java.util.concurrent.atomic.AtomicBoolean;
2421

25-
import org.eclipse.swt.SWT;
26-
import org.eclipse.swt.dnd.Clipboard;
2722
import org.eclipse.swt.dnd.DND;
2823
import org.eclipse.swt.dnd.RTFTransfer;
2924
import org.eclipse.swt.dnd.TextTransfer;
3025
import org.eclipse.swt.dnd.Transfer;
3126
import org.eclipse.swt.dnd.TransferData;
32-
import org.eclipse.swt.layout.RowLayout;
33-
import org.eclipse.swt.widgets.Button;
34-
import org.eclipse.swt.widgets.Display;
35-
import org.eclipse.swt.widgets.Label;
36-
import org.eclipse.swt.widgets.Shell;
37-
import org.junit.jupiter.api.AfterEach;
3827
import org.junit.jupiter.api.BeforeEach;
3928
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
4029
import org.junit.jupiter.api.Order;
41-
import org.junit.jupiter.api.RepeatedTest;
4230
import org.junit.jupiter.api.Tag;
4331
import org.junit.jupiter.api.Test;
4432
import org.junit.jupiter.api.TestMethodOrder;
@@ -54,162 +42,19 @@
5442
*/
5543
@Tag("clipboard")
5644
@TestMethodOrder(OrderAnnotation.class) // run tests needing button presses first
57-
public class Test_org_eclipse_swt_dnd_Clipboard {
45+
public class Test_org_eclipse_swt_dnd_Clipboard extends ClipboardBase {
5846

59-
/**
60-
* See {@link #openAndFocusShell(boolean)} - some tests require user to actually
61-
* interact with the shell.
62-
*
63-
* Default to skipping tests requiring "real" activation on GHA and Jenkins.
64-
*
65-
* <code>true</code>: skip tests <code>false</code>: don't skip tests
66-
* <code>null</code>: unknown whether to skip tests yet
67-
*/
68-
private static Boolean skipTestsRequiringButtonPress = (Boolean.parseBoolean(System.getenv("GITHUB_ACTIONS"))
69-
|| System.getenv("JOB_NAME") != null) ? true : null;
70-
private static int uniqueId = 1;
71-
private Display display;
72-
private Shell shell;
73-
private Clipboard clipboard;
7447
private TextTransfer textTransfer;
7548
private RTFTransfer rtfTransfer;
76-
private RemoteClipboard remote;
7749

50+
@Override
7851
@BeforeEach
7952
public void setUp() {
80-
display = Display.getCurrent();
81-
if (display == null) {
82-
display = Display.getDefault();
83-
}
84-
clipboard = new Clipboard(display);
53+
super.setUp();
8554
textTransfer = TextTransfer.getInstance();
8655
rtfTransfer = RTFTransfer.getInstance();
8756
}
8857

89-
/**
90-
* Note: Wayland backend does not allow access to system clipboard from
91-
* non-focussed windows. So we have to create/open and focus a window here so
92-
* that clipboard operations work.
93-
*
94-
* Additionally, if we want to provide data to the clipboard, we require user
95-
* interaction on the created shell. Therefore if forSetContents is true the
96-
* tester needs to press a button for test to work.
97-
*
98-
* If there is no user interaction (button not pressed) then the test is
99-
* skipped, rather than failed, and subsequent tests requiring user interaction
100-
* as skipped too. See {@link #skipTestsRequiringButtonPress}
101-
*/
102-
private void openAndFocusShell(boolean forSetContents) throws InterruptedException {
103-
assertNull(shell);
104-
shell = new Shell(display);
105-
106-
boolean requireUserPress = forSetContents && SwtTestUtil.isWayland();
107-
if (requireUserPress) {
108-
assumeFalse(skipTestsRequiringButtonPress != null && skipTestsRequiringButtonPress,
109-
"Skipping tests that require user input");
110-
111-
AtomicBoolean pressed = new AtomicBoolean(false);
112-
shell.setLayout(new RowLayout(SWT.VERTICAL));
113-
Button button = new Button(shell, SWT.PUSH);
114-
button.setText("Press me!");
115-
button.addListener(SWT.Selection, (e) -> pressed.set(true));
116-
button.setSize(200, 50);
117-
Label label = new Label(shell, SWT.NONE);
118-
label.setText("""
119-
Press the button to tell Wayland that you really want this window to have access to clipboard.
120-
This is needed on Wayland because only really focussed programs are allowed to write to the
121-
global keyboard.
122-
123-
If you don't press this button soon, the test will be skipped and you won't be asked again.
124-
""");
125-
Label timeleft = new Label(shell, SWT.NONE);
126-
timeleft.setText("Time left to press button: XXXXXXXXXXXXXXXXXXXX seconds");
127-
128-
SwtTestUtil.openShell(shell);
129-
130-
// If we know there is a tester pressing the buttons, allow them
131-
// a little grace on the timeout. If we don't know if there is a
132-
// tester around, skip tests fairly quickly and don't
133-
// ask again.
134-
int timeout = skipTestsRequiringButtonPress == null ? 1500 : 10000;
135-
long startTime = System.nanoTime();
136-
SwtTestUtil.processEvents(timeout, () -> {
137-
long nowTime = System.nanoTime();
138-
long timeLeft = nowTime - startTime;
139-
long timeLeftMs = timeout - (timeLeft / 1_000_000);
140-
double timeLeftS = timeLeftMs / 1_000.0d;
141-
timeleft.setText("Time left to press button: " + timeLeftS + " seconds");
142-
return pressed.get();
143-
});
144-
boolean userPressedButton = pressed.get();
145-
if (userPressedButton) {
146-
skipTestsRequiringButtonPress = false;
147-
} else {
148-
skipTestsRequiringButtonPress = true;
149-
assumeTrue(false, "Skipping tests that require user input");
150-
}
151-
} else {
152-
SwtTestUtil.openShell(shell);
153-
}
154-
155-
}
156-
157-
/**
158-
* Note: Wayland backend does not allow access to system clipboard from
159-
* non-focussed windows. So we have to open and focus remote here so that
160-
* clipboard operations work.
161-
*/
162-
private void openAndFocusRemote() throws Exception {
163-
assertNull(remote);
164-
remote = new RemoteClipboard();
165-
remote.start();
166-
167-
/*
168-
* If/when OpenJDK Project Wakefield gets merged then we may need to wait for
169-
* button pressed on the swing app just like the SWT app. This may also be
170-
* needed if Wayland implementations get more restrictive on X apps too.
171-
*/
172-
// remote.waitForButtonPress();
173-
}
174-
175-
@AfterEach
176-
public void tearDown() throws Exception {
177-
try {
178-
if (remote != null) {
179-
remote.stop();
180-
}
181-
} finally {
182-
if (clipboard != null) {
183-
clipboard.dispose();
184-
}
185-
if (shell != null) {
186-
shell.dispose();
187-
}
188-
SwtTestUtil.processEvents();
189-
}
190-
}
191-
192-
/**
193-
* Make sure to always copy/paste unique strings - this ensures that tests run
194-
* under {@link RepeatedTest}s don't false pass because of clipboard value on
195-
* previous iteration.
196-
*/
197-
private String getUniqueTestString() {
198-
return "Hello World " + uniqueId++;
199-
}
200-
201-
/**
202-
* Return the set of clipboards that are supported on this platform, this method
203-
* is referenced in the {@link MethodSource} annotations on the
204-
* {@link ParameterizedTest}s within this class.
205-
*/
206-
public static List<Integer> supportedClipboardIds() {
207-
if (SwtTestUtil.isGTK) {
208-
return List.of(DND.CLIPBOARD, DND.SELECTION_CLIPBOARD);
209-
}
210-
return List.of(DND.CLIPBOARD);
211-
}
212-
21358
/**
21459
* Test that the remote application clipboard works
21560
*/

0 commit comments

Comments
 (0)