Skip to content

Commit 68ef2f3

Browse files
HeikoKlareCopilot
andcommitted
Add Win32-specific tests for Cursor
CursorWin32Tests covers behaviour that is either Windows-only or directly exercises the internal implementation paths changed in the preceding cleanup commit: - testDisposedCursorReturnsZeroHandle: verifies the isDisposed() guard at the top of win32_getHandle() returns 0L after disposal. - testHandleIsCachedForSameZoomLevel: verifies the computeIfAbsent() caching - two calls with the same zoom must return the identical cached OS handle without re-creating it. - testImageDataCursorProducesDifferentHandlesForDifferentZoomLevels: verifies that ImageDataCursorHandleProvider scales the source image and produces a distinct OS cursor handle for each zoom level. - testDestroyHandlesExceptPreservesRetainedHandle: verifies that destroyHandlesExcept() leaves the retained zoom entry intact and does not mark the cursor as disposed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 462dbb3 commit 68ef2f3

File tree

1 file changed

+102
-0
lines changed

1 file changed

+102
-0
lines changed
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 Contributors to the Eclipse Foundation
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.graphics;
12+
13+
import static org.junit.jupiter.api.Assertions.*;
14+
15+
import java.util.*;
16+
17+
import org.eclipse.swt.*;
18+
import org.eclipse.swt.internal.*;
19+
import org.eclipse.swt.widgets.*;
20+
import org.junit.jupiter.api.*;
21+
import org.junit.jupiter.api.extension.*;
22+
23+
@ExtendWith(PlatformSpecificExecutionExtension.class)
24+
@ExtendWith(WithMonitorSpecificScalingExtension.class)
25+
class CursorWin32Tests {
26+
27+
private Display display;
28+
29+
@BeforeEach
30+
void setUp() {
31+
display = Display.getDefault();
32+
}
33+
34+
@Test
35+
void testDisposedCursorReturnsZeroHandle() {
36+
Cursor cursor = new Cursor(display, SWT.CURSOR_ARROW);
37+
cursor.dispose();
38+
assertEquals(0L, Cursor.win32_getHandle(cursor, 100),
39+
"A disposed cursor must return a zero handle");
40+
}
41+
42+
@Test
43+
void testHandleIsCachedForSameZoomLevel() {
44+
ImageData source = new ImageData(16, 16, 32,
45+
new PaletteData(0xFF00, 0xFF0000, 0xFF000000));
46+
source.alpha = 255;
47+
48+
Cursor cursor = new Cursor(display, source, 0, 0);
49+
try {
50+
long first = Cursor.win32_getHandle(cursor, 100);
51+
long second = Cursor.win32_getHandle(cursor, 100);
52+
assertEquals(first, second,
53+
"Repeated calls with the same zoom must return the cached handle");
54+
} finally {
55+
cursor.dispose();
56+
}
57+
}
58+
59+
@Test
60+
void testImageDataCursorProducesDifferentHandlesForDifferentZoomLevels() {
61+
// 32bpp image with uniform alpha — takes the ARGB path in setupCursorFromImageData
62+
ImageData source = new ImageData(16, 16, 32,
63+
new PaletteData(0xFF00, 0xFF0000, 0xFF000000));
64+
source.alpha = 255;
65+
66+
Cursor cursor = new Cursor(display, source, 0, 0);
67+
try {
68+
long handle100 = Cursor.win32_getHandle(cursor, 100);
69+
long handle200 = Cursor.win32_getHandle(cursor, 200);
70+
71+
assertNotEquals(0L, handle100, "Handle at 100% zoom must be non-zero");
72+
assertNotEquals(0L, handle200, "Handle at 200% zoom must be non-zero");
73+
assertNotEquals(handle100, handle200,
74+
"Different zoom levels must produce distinct OS cursor handles (different physical sizes)");
75+
} finally {
76+
cursor.dispose();
77+
}
78+
}
79+
80+
@Test
81+
void testDestroyHandlesExceptPreservesRetainedHandle() {
82+
// 32bpp ARGB source so we get a unique, non-shared OS handle per zoom level
83+
ImageData source = new ImageData(16, 16, 32,
84+
new PaletteData(0xFF00, 0xFF0000, 0xFF000000));
85+
source.alpha = 255;
86+
87+
Cursor cursor = new Cursor(display, source, 0, 0);
88+
try {
89+
long handle100 = Cursor.win32_getHandle(cursor, 100);
90+
Cursor.win32_getHandle(cursor, 200); // populate a second zoom level
91+
92+
cursor.destroyHandlesExcept(Set.of(DPIUtil.getZoomForAutoscaleProperty(100)));
93+
94+
// The cursor itself must still be alive and the retained handle unchanged
95+
assertFalse(cursor.isDisposed(), "Cursor must not be disposed after destroyHandlesExcept");
96+
assertEquals(handle100, Cursor.win32_getHandle(cursor, 100),
97+
"The handle for the retained zoom level must be unchanged after destroyHandlesExcept");
98+
} finally {
99+
cursor.dispose();
100+
}
101+
}
102+
}

0 commit comments

Comments
 (0)