On Fri, 4 Jul 2025 06:59:24 GMT, Lukasz Kostyra <lkost...@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 two additional 
> commits since the last revision:
> 
>  - Parameterize StageFocusTest
>    
>    This is to check if Stage showing works also for Stages other than the
>    primary Stage provided by Application.start()
>  - Review fixes; revert using VisualTestBase

I did a couple of extra changes in addition to the regular fixes:
- `extend VisualTestBase` was removed, it was indeed the cause of the test 
always passing. The "Windows forbids granting focus" scenario seems to only 
happen when we try and show the Stage provided by `Application.start()`
- Tests are now parameterized and check both the primaryStage delivered by 
`Application.start()` as well as a separately created Stage. Figured it might 
be worth testing as well, since it seems to behave slightly differently and can 
affect other test.

I did notice this morning that on a fresh system start, the test _can_ pass 
even with `--daemon` gradle flag. This happens when it is the first invocation 
of Gradle that run - then the used Gradle daemon instance is a child of current 
Terminal process (which is a foreground process) which makes Windows treat it 
as a foreground process as well and grant focus to its children. The most 
reliable way I found of reproducing this and getting the test to fail on my 
Windows 11 box (also has to be done after calling `gradle --stop`):
- Run one build using `gradle --daemon`
- Close the Terminal window - that will cut the daemon's association with the 
Terminal and make Windows treat it as a background process
- Re-open the terminal and re-run the tests using `gradle --daemon`

For me the test then fails when checking the primary Stage delivered by 
`Application.start()` at the keystroke part of the test.

-------------

PR Comment: https://git.openjdk.org/jfx/pull/1804#issuecomment-3034768336

Reply via email to