RFR: 8351357: Add canary system test checking if Stage receives focus on show [v5]
Andy Goryachev
angorya at openjdk.org
Wed Jul 2 21:01:50 UTC 2025
On Wed, 2 Jul 2025 12:58:30 GMT, Lukasz Kostyra <lkostyra at openjdk.org> wrote:
>> Originally this issue was supposed to resolve problems with some system tests (`MenuDoubleShortcutTest`, `TextAreaBehaviorTest` and others) failing on my Windows machine. In the process of figuring this out I found out the problem is Windows `::SetForegroundWindow()` API refusing to give focus to JFX Stage upon calling `Stage.show()`.
>>
>> The problem happened only when running system tests via Gradle, and with more investigation it turned out the culprit is actually running tests via a Gradle Daemon, which is the default behavior. According to [SetForegroundWindow API remarks](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setforegroundwindow) there is a list of conditions a process must meet to be granted a privilege of receiving focus, which is supposed to prevent focus stealing. While we do meet the required conditions, we don't meet "one of" additional conditions listed in the reference:
>> - Gradle daemon is a background process, so tests started by it do not meet `The calling process is the foreground process.` and `The calling process was started by the foreground process.` conditions
>> - We most probably run the tests from the terminal, so `There is currently no foreground window, and thus no foreground process.` condition fails - the foreground window is the Terminal itself.
>> - Each test has fresh-started JFX stage so `The calling process received the last input event.` condition cannot be met and would require either Robot workarounds or manual interaction before each test case.
>> - There is no debugger involved in the process (at least most of the time) so `Either the foreground process or the calling process is being debugged.` is also not met.
>>
>> As such, Windows refuses to grant JFX Stage focus, which fails some system tests relying on it.
>>
>> While we cannot remedy these conditions in-code (outside of hacky solutions I found with `AttachThreadInput` API which I am not a fan of) the only solution seems to be running the tests on Windows via either `gradle --no-daemon` or by setting `org.gradle.daemon=false` property somewhere in `gradle.properties`.
>>
>> In the process of debugging this problem I wrote a canary test to detect whether a Stage receives focus right after calling `show()`. I ran this test on all (accessible to me) platforms (Windows, Linux, macOS) - on both Linux and macOS the test passes regardless of whether the Gradle deamon is used or not. On my Windows machine (Win 11 24H2) it fails when testing...
>
> Lukasz Kostyra has updated the pull request incrementally with one additional commit since the last revision:
>
> Expand check for whether Stage is on top
Looks good! Left some minor suggestions.
tests/system/src/test/java/test/robot/javafx/stage/StageFocusTest.java line 67:
> 65: static final Color SCENE_COLOR = Color.LIGHTGREEN;
> 66:
> 67: private Stage theStage = null;
null is unnecessary
tests/system/src/test/java/test/robot/javafx/stage/StageFocusTest.java line 99:
> 97: Platform.runLater(() -> {
> 98: theStage.setX(STAGE_X);
> 99: theStage.setY(STAGE_Y);
setting Stage (x,y) coordinates in runLater makes the window jump.
it's probably better to move LL98-99 before runLater, to L96
with the robot disabled, the test fails with expected
org.opentest4j.AssertionFailedError: Event received latch timed out! Stage most probably did not have focus after showing. Some tests might fail because of this. If that is the case, try re-running the tests with '--no-daemon' flag in Gradle. ==> expected: <true> but was: <false>
at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:151)
tests/system/src/test/java/test/robot/javafx/stage/StageFocusTest.java line 124:
> 122: PixelReader captureReader = capture.getPixelReader();
> 123: for (int x = 0; x < STAGE_SIZE; ++x) {
> 124: for (int y = 0; y < STAGE_SIZE; ++y) {
I was thinking of testing ever 5th or 10th pixel, but this code is fast enough.
If I quickly click on some window underneath the test window, the test correctly fails with
org.opentest4j.AssertionFailedError: expected:rgba(144,238,144,255) but was:rgba(234,234,234,255)
tests/system/src/test/java/test/robot/javafx/stage/StageFocusTest.java line 137:
> 135: assertTrue(
> 136: eventReceivedLatch.await(TIMEOUT, TimeUnit.MILLISECONDS),
> 137: "Event received latch timed out! Stage most probably did not have focus after showing. Some tests might fail because of this. " +
I'd suggest to replace "Event received latch timed out!", or re-phrase completely, maybe
"The Stage did not received the key stroke generated by Robot. This might happen if the stage did not receive focus. Some tests might fail because of this.
Try re-running the tests with '--no-daemon' flag in Gradle.".
or something along these lines.
-------------
PR Review: https://git.openjdk.org/jfx/pull/1804#pullrequestreview-2980590440
PR Review Comment: https://git.openjdk.org/jfx/pull/1804#discussion_r2180956422
PR Review Comment: https://git.openjdk.org/jfx/pull/1804#discussion_r2180948401
PR Review Comment: https://git.openjdk.org/jfx/pull/1804#discussion_r2180961162
PR Review Comment: https://git.openjdk.org/jfx/pull/1804#discussion_r2180954317
More information about the openjfx-dev
mailing list