Skip to content

Commit ec5992a

Browse files
committed
Add CloseTestWindowsExtension for JUnit5 based on CloseTestWindowsRule
to JUnit 5 and migrate one test to use it
1 parent edd5b27 commit ec5992a

File tree

4 files changed

+202
-8
lines changed

4 files changed

+202
-8
lines changed

tests/org.eclipse.ui.tests.harness/META-INF/MANIFEST.MF

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ Require-Bundle: org.eclipse.ui;bundle-version="3.208.0",
99
org.junit,
1010
org.eclipse.core.resources
1111
Bundle-ActivationPolicy: lazy
12+
Import-Package: org.junit.jupiter.api;version="[5.0.0,6.0.0)",
13+
org.junit.jupiter.api.extension;version="[5.0.0,6.0.0)"
1214
Export-Package: org.eclipse.ui.tests.harness,
1315
org.eclipse.ui.tests.harness.tests,
1416
org.eclipse.ui.tests.harness.util,
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2000, 2025 IBM Corporation and others.
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+
* Contributors:
12+
* IBM Corporation - initial API and implementation
13+
* Rolf Theunissen <[email protected]> - Bug 553836 (extracted from UITestCase)
14+
*******************************************************************************/
15+
16+
package org.eclipse.ui.tests.harness.util;
17+
18+
import static org.eclipse.ui.tests.harness.util.UITestUtil.processEvents;
19+
import static org.junit.jupiter.api.Assertions.assertEquals;
20+
21+
import java.util.ArrayList;
22+
import java.util.List;
23+
import java.util.Set;
24+
25+
import org.eclipse.swt.widgets.Shell;
26+
import org.eclipse.ui.IWindowListener;
27+
import org.eclipse.ui.IWorkbenchWindow;
28+
import org.eclipse.ui.PlatformUI;
29+
import org.junit.jupiter.api.extension.AfterEachCallback;
30+
import org.junit.jupiter.api.extension.BeforeEachCallback;
31+
import org.junit.jupiter.api.extension.ExtensionContext;
32+
33+
/**
34+
* JUnit 5 Extension for UI tests to clean up windows/shells:
35+
* <ul>
36+
* <li>prints the test name to the log before and after each test case
37+
* <li>closes windows opened during the test case
38+
* <li>checks for shells unintentionally leaked from the test case
39+
* </ul>
40+
*/
41+
public class CloseTestWindowsExtension implements BeforeEachCallback, AfterEachCallback {
42+
43+
private static final String TEST_NAME_KEY = "testName";
44+
private static final String TEST_WINDOWS_KEY = "testWindows";
45+
private static final String WINDOW_LISTENER_KEY = "windowListener";
46+
private static final String INITIAL_SHELLS_KEY = "initialShells";
47+
private static final String LEAK_CHECKS_DISABLED_KEY = "leakChecksDisabled";
48+
49+
@Override
50+
public void beforeEach(ExtensionContext context) throws Exception {
51+
String testName = context.getDisplayName();
52+
context.getStore(ExtensionContext.Namespace.create(getClass(), context.getUniqueId()))
53+
.put(TEST_NAME_KEY, testName);
54+
55+
List<IWorkbenchWindow> testWindows = new ArrayList<>(3);
56+
context.getStore(ExtensionContext.Namespace.create(getClass(), context.getUniqueId()))
57+
.put(TEST_WINDOWS_KEY, testWindows);
58+
59+
TestWindowListener windowListener = new TestWindowListener(testWindows);
60+
context.getStore(ExtensionContext.Namespace.create(getClass(), context.getUniqueId()))
61+
.put(WINDOW_LISTENER_KEY, windowListener);
62+
63+
addWindowListener(windowListener);
64+
storeInitialShells(context);
65+
}
66+
67+
@Override
68+
public void afterEach(ExtensionContext context) throws Exception {
69+
TestWindowListener windowListener = getWindowListener(context);
70+
removeWindowListener(windowListener);
71+
72+
processEvents();
73+
closeAllTestWindows(context);
74+
processEvents();
75+
checkForLeakedShells(context);
76+
}
77+
78+
79+
/**
80+
* Adds a window listener to the workbench to keep track of opened test windows.
81+
*/
82+
private void addWindowListener(TestWindowListener windowListener) {
83+
PlatformUI.getWorkbench().addWindowListener(windowListener);
84+
}
85+
86+
/**
87+
* Removes the listener.
88+
*/
89+
private void removeWindowListener(TestWindowListener windowListener) {
90+
if (windowListener != null) {
91+
PlatformUI.getWorkbench().removeWindowListener(windowListener);
92+
}
93+
}
94+
95+
/**
96+
* Close all test windows.
97+
*/
98+
private void closeAllTestWindows(ExtensionContext context) {
99+
@SuppressWarnings("unchecked")
100+
List<IWorkbenchWindow> testWindows = (List<IWorkbenchWindow>) context
101+
.getStore(ExtensionContext.Namespace.create(getClass(), context.getUniqueId()))
102+
.get(TEST_WINDOWS_KEY);
103+
104+
if (testWindows != null) {
105+
List<IWorkbenchWindow> testWindowsCopy = new ArrayList<>(testWindows);
106+
for (IWorkbenchWindow testWindow : testWindowsCopy) {
107+
testWindow.close();
108+
}
109+
testWindows.clear();
110+
}
111+
}
112+
113+
private static class TestWindowListener implements IWindowListener {
114+
private final List<IWorkbenchWindow> testWindows;
115+
116+
public TestWindowListener(List<IWorkbenchWindow> testWindows) {
117+
this.testWindows = testWindows;
118+
}
119+
120+
@Override
121+
public void windowActivated(IWorkbenchWindow window) {
122+
// do nothing
123+
}
124+
125+
@Override
126+
public void windowDeactivated(IWorkbenchWindow window) {
127+
// do nothing
128+
}
129+
130+
@Override
131+
public void windowClosed(IWorkbenchWindow window) {
132+
testWindows.remove(window);
133+
}
134+
135+
@Override
136+
public void windowOpened(IWorkbenchWindow window) {
137+
testWindows.add(window);
138+
}
139+
}
140+
141+
private void storeInitialShells(ExtensionContext context) {
142+
Set<Shell> initialShells = Set.of(PlatformUI.getWorkbench().getDisplay().getShells());
143+
context.getStore(ExtensionContext.Namespace.create(getClass(), context.getUniqueId()))
144+
.put(INITIAL_SHELLS_KEY, initialShells);
145+
}
146+
147+
private void checkForLeakedShells(ExtensionContext context) {
148+
@SuppressWarnings("unchecked")
149+
Set<Shell> initialShells = (Set<Shell>) context
150+
.getStore(ExtensionContext.Namespace.create(getClass(), context.getUniqueId()))
151+
.get(INITIAL_SHELLS_KEY);
152+
153+
Boolean leakChecksDisabled = (Boolean) context
154+
.getStore(ExtensionContext.Namespace.create(getClass(), context.getUniqueId()))
155+
.get(LEAK_CHECKS_DISABLED_KEY);
156+
157+
if (initialShells == null) {
158+
return;
159+
}
160+
161+
List<String> leakedModalShellTitles = new ArrayList<>();
162+
Shell[] shells = PlatformUI.getWorkbench().getDisplay().getShells();
163+
for (Shell shell : shells) {
164+
if (!shell.isDisposed() && !initialShells.contains(shell)) {
165+
leakedModalShellTitles.add(shell.getText());
166+
shell.close();
167+
}
168+
}
169+
170+
if (leakChecksDisabled == null || !leakChecksDisabled) {
171+
assertEquals(0, leakedModalShellTitles.size(),
172+
"Test leaked modal shell: [" + String.join(", ", leakedModalShellTitles) + "]");
173+
}
174+
}
175+
176+
/**
177+
* Disable leak checks for the current test.
178+
* This method should be called from test methods when needed.
179+
*/
180+
public void disableLeakChecks(ExtensionContext context) {
181+
context.getStore(ExtensionContext.Namespace.create(getClass(), context.getUniqueId()))
182+
.put(LEAK_CHECKS_DISABLED_KEY, Boolean.TRUE);
183+
}
184+
185+
186+
private TestWindowListener getWindowListener(ExtensionContext context) {
187+
return (TestWindowListener) context
188+
.getStore(ExtensionContext.Namespace.create(getClass(), context.getUniqueId()))
189+
.get(WINDOW_LISTENER_KEY);
190+
}
191+
}

tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/api/IPageLayoutTest.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@
1414
package org.eclipse.ui.tests.api;
1515

1616
import static org.eclipse.ui.tests.harness.util.UITestUtil.openTestWindow;
17-
import static org.junit.Assert.assertEquals;
17+
import static org.junit.jupiter.api.Assertions.assertEquals;
1818

19-
import org.eclipse.ui.tests.harness.util.CloseTestWindowsRule;
19+
import org.eclipse.ui.tests.harness.util.CloseTestWindowsExtension;
2020
import org.eclipse.ui.tests.harness.util.EmptyPerspective;
21-
import org.junit.Rule;
22-
import org.junit.Test;
21+
import org.junit.jupiter.api.Test;
22+
import org.junit.jupiter.api.extension.RegisterExtension;
2323

2424
/**
2525
* Test cases for the <code>IPageLayout</code> API.
@@ -28,8 +28,8 @@
2828
*/
2929
public class IPageLayoutTest {
3030

31-
@Rule
32-
public final CloseTestWindowsRule closeTestWindows = new CloseTestWindowsRule();
31+
@RegisterExtension
32+
public CloseTestWindowsExtension closeTestWindows = new CloseTestWindowsExtension();
3333

3434
@Test
3535
public void testGetDescriptor() {

tests/org.eclipse.ui.tests/META-INF/MANIFEST.MF

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,10 @@ Require-Bundle: org.eclipse.core.resources;bundle-version="3.14.0",
4747
org.eclipse.emf.ecore
4848
Import-Package: jakarta.annotation,
4949
jakarta.inject,
50-
org.osgi.service.event,
5150
org.junit.jupiter.api;version="[5.14.0,6.0.0)",
52-
org.junit.platform.suite.api;version="[1.14.0,2.0.0)"
51+
org.junit.jupiter.api.extension;version="[5.14.0,6.0.0)",
52+
org.junit.platform.suite.api;version="[1.14.0,2.0.0)",
53+
org.osgi.service.event
5354
Eclipse-AutoStart: true
5455
Export-Package: org.eclipse.ui.tests.api,
5556
org.eclipse.ui.tests.menus

0 commit comments

Comments
 (0)