Skip to content

Commit 5dec05e

Browse files
committed
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.
1 parent fda9a4a commit 5dec05e

File tree

1 file changed

+98
-0
lines changed

1 file changed

+98
-0
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 Vector Informatik GmbH 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+
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), "A disposed cursor must return a zero handle");
39+
}
40+
41+
@Test
42+
void testHandleIsCachedForSameZoomLevel() {
43+
ImageData source = new ImageData(16, 16, 32, new PaletteData(0xFF00, 0xFF0000, 0xFF000000));
44+
source.alpha = 255;
45+
46+
Cursor cursor = new Cursor(display, source, 0, 0);
47+
try {
48+
long first = Cursor.win32_getHandle(cursor, 100);
49+
long second = Cursor.win32_getHandle(cursor, 100);
50+
assertEquals(first, second, "Repeated calls with the same zoom must return the cached handle");
51+
} finally {
52+
cursor.dispose();
53+
}
54+
}
55+
56+
@Test
57+
void testImageDataCursorProducesDifferentHandlesForDifferentZoomLevels() {
58+
// 32bpp image with uniform alpha — takes the ARGB path in
59+
// setupCursorFromImageData
60+
ImageData source = new ImageData(16, 16, 32, new PaletteData(0xFF00, 0xFF0000, 0xFF000000));
61+
source.alpha = 255;
62+
63+
Cursor cursor = new Cursor(display, source, 0, 0);
64+
try {
65+
long handle100 = Cursor.win32_getHandle(cursor, 100);
66+
long handle200 = Cursor.win32_getHandle(cursor, 200);
67+
68+
assertNotEquals(0L, handle100, "Handle at 100% zoom must be non-zero");
69+
assertNotEquals(0L, handle200, "Handle at 200% zoom must be non-zero");
70+
assertNotEquals(handle100, handle200,
71+
"Different zoom levels must produce distinct OS cursor handles (different physical sizes)");
72+
} finally {
73+
cursor.dispose();
74+
}
75+
}
76+
77+
@Test
78+
void testDestroyHandlesExceptPreservesRetainedHandle() {
79+
// 32bpp ARGB source so we get a unique, non-shared OS handle per zoom level
80+
ImageData source = new ImageData(16, 16, 32, new PaletteData(0xFF00, 0xFF0000, 0xFF000000));
81+
source.alpha = 255;
82+
83+
Cursor cursor = new Cursor(display, source, 0, 0);
84+
try {
85+
long handle100 = Cursor.win32_getHandle(cursor, 100);
86+
Cursor.win32_getHandle(cursor, 200); // populate a second zoom level
87+
88+
cursor.destroyHandlesExcept(Set.of(DPIUtil.getZoomForAutoscaleProperty(100)));
89+
90+
// The cursor itself must still be alive and the retained handle unchanged
91+
assertFalse(cursor.isDisposed(), "Cursor must not be disposed after destroyHandlesExcept");
92+
assertEquals(handle100, Cursor.win32_getHandle(cursor, 100),
93+
"The handle for the retained zoom level must be unchanged after destroyHandlesExcept");
94+
} finally {
95+
cursor.dispose();
96+
}
97+
}
98+
}

0 commit comments

Comments
 (0)