RFR: 8348423: [TestBug] stress test Nodes initialization from a background thread

Kevin Rushforth kcr at openjdk.org
Fri Jan 31 19:44:00 UTC 2025


On Wed, 29 Jan 2025 23:54:44 GMT, Andy Goryachev <angorya at openjdk.org> wrote:

> Created a test that validates various Nodes can be initialized in a background thread.

I like the overall structure of this test. I left several mostly "fit and finish" type comments.

I will test it as part of finishing the review. What testing have you done? I recommend the following CI headful test runs:

1. A run using this branch "as is" to verify no failures on our headful systems
2. A run using a branch with all of the `@Disabled` tests enabled to see how well the test catches the bugs.

tests/system/src/test/java/test/robot/javafx/scene/NodeInitializationBackgroundThreadTest.java line 136:

> 134:  * Tests Node initialization from a background thread, per
> 135:  *
> 136:  * https://openjfx.io/javadoc/23/javafx.graphics/javafx/scene/Node.html

I prefer not to put external links to specific versions of the docs. I would just say "per the Node docs" or similar.

tests/system/src/test/java/test/robot/javafx/scene/NodeInitializationBackgroundThreadTest.java line 146:

> 144:  * - WebView
> 145:  *
> 146:  * The test creates a visible node of a given type, and at the same time, starts a number of background threads

Suggestion:  "creates a visible node" -->  "creates a visible node on the JavaFX application thread"

tests/system/src/test/java/test/robot/javafx/scene/NodeInitializationBackgroundThreadTest.java line 272:

> 270:             return c;
> 271:         }, (c) -> {
> 272:             c.show(); // does not fail here, unlike DatePicker?

Interesting. I still think we should disallow it as I mentioned in the `DatePicker` case.

tests/system/src/test/java/test/robot/javafx/scene/NodeInitializationBackgroundThreadTest.java line 305:

> 303:             return c;
> 304:         }, (c) -> {
> 305:             c.show(); // fails here

As mentioned in the JBS issue, I think we should consider filing an issue to update the spec of `ComboBoxPopupControl::show` to require calling it on the JavaFX app thread (throwing an exception if called off thread). Either way, we should ensure that the skins themselves never call show unless the control is showing.

tests/system/src/test/java/test/robot/javafx/scene/NodeInitializationBackgroundThreadTest.java line 377:

> 375:             c.setPopupSide(Side.RIGHT);
> 376:             accessControl(c);
> 377:             c.show();

This is another case of a `show` method that we should consider changing to require access only on the FX app thread.

tests/system/src/test/java/test/robot/javafx/scene/NodeInitializationBackgroundThreadTest.java line 745:

> 743:                     @Override
> 744:                     public void run() {
> 745:                         try {

Suggestion: use a lambda rather than an inner class here .

tests/system/src/test/java/test/robot/javafx/scene/NodeInitializationBackgroundThreadTest.java line 755:

> 753:                                     inFx(() -> {
> 754:                                         operation.accept(visibleNode);
> 755:                                     });

Does this need to be done for each thread? I can see why it might make sense to do that, as it preserves the relative frequency of operations on the FX app thread versus the frequency off thread regardless of how many threads you have. It would risk flooding the FX event queue if the number of background threads were huge, but you limit it based on the number of physical HW threads, so this seems OK.

tests/system/src/test/java/test/robot/javafx/scene/NodeInitializationBackgroundThreadTest.java line 779:

> 777: 
> 778:     private static boolean nextBoolean() {
> 779:         return new Random().nextBoolean();

It is a best practice when using Random to share a single instance and not allocate a new one each time. It is more performant and more random.

tests/system/src/test/java/test/robot/testharness/RobotTestBase.java line 42:

> 40: 
> 41: /**
> 42:  * Similar to VisualTestBase, but more convenient.

If you are going to compare it to VisualTestBase, you need a more nuanced statement than just "more convenient" which could mislead someone into thinking that this is a replacement. It isn't. VisualTestBase is specifically intended for screen capture tests, so creates stages as `UNDECORATED` and `alwaysOnTop`, and provides pixel capture and comparison utilities. I recommend describing this class on its own terms.

tests/system/src/test/java/test/robot/testharness/RobotTestBase.java line 65:

> 63:             stage.setScene(scene);
> 64:             stage.setWidth(STAGE_WIDTH);
> 65:             stage.setHeight(STAGE_HEIGHT);

Typically, best practice is to set the width / height of the Scene, rather than the Stage, especially for decorated stages, although there are some times you might want to set the stage. It might not matter for what you are using it for.

tests/system/src/test/java/test/robot/testharness/RobotTestBase.java line 81:

> 79:     @AfterAll
> 80:     public static void teardownOnce() {
> 81:         Util.shutdown();

Should you pass "stage" here, or do you ensure that the stage is always closed some other way?

tests/system/src/test/java/test/robot/testharness/RobotTestBase.java line 173:

> 171:      * @param r the code to execute
> 172:      */
> 173:     public void inFx(Runnable r) {

Minor: Maybe call this method "runAndWait" instead? That would make it clearer what it does.

tests/system/src/test/java/test/robot/testharness/RobotTestBase.java line 176:

> 174:         Util.runAndWait(() -> {
> 175:             r.run();
> 176:         });

Minor: This can be reduced to `Util.runAndWait(r)`

tests/system/src/test/java/test/robot/testharness/RobotTestBase.java line 183:

> 181:      * @param milliseconds the number of milliseconds to sleep
> 182:      */
> 183:     protected void sleep(int milliseconds) {

Maybe use `long` rather than `int` to match `Thread.sleep` and `Util.sleep`? It probably doesn't matter in practice.

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

PR Review: https://git.openjdk.org/jfx/pull/1690#pullrequestreview-2587514918
PR Review Comment: https://git.openjdk.org/jfx/pull/1690#discussion_r1937765444
PR Review Comment: https://git.openjdk.org/jfx/pull/1690#discussion_r1937767206
PR Review Comment: https://git.openjdk.org/jfx/pull/1690#discussion_r1937799226
PR Review Comment: https://git.openjdk.org/jfx/pull/1690#discussion_r1937798147
PR Review Comment: https://git.openjdk.org/jfx/pull/1690#discussion_r1937801455
PR Review Comment: https://git.openjdk.org/jfx/pull/1690#discussion_r1937772645
PR Review Comment: https://git.openjdk.org/jfx/pull/1690#discussion_r1937776420
PR Review Comment: https://git.openjdk.org/jfx/pull/1690#discussion_r1937848601
PR Review Comment: https://git.openjdk.org/jfx/pull/1690#discussion_r1937750555
PR Review Comment: https://git.openjdk.org/jfx/pull/1690#discussion_r1937753823
PR Review Comment: https://git.openjdk.org/jfx/pull/1690#discussion_r1937755797
PR Review Comment: https://git.openjdk.org/jfx/pull/1690#discussion_r1937758040
PR Review Comment: https://git.openjdk.org/jfx/pull/1690#discussion_r1937762531
PR Review Comment: https://git.openjdk.org/jfx/pull/1690#discussion_r1937759121


More information about the openjfx-dev mailing list