Adam
Adam

Reputation: 36743

JavaFX application thread exits during JUnit test

I'm testing a JavaFX application with JUnit, In most cases I use the @Rule approach from Basic JUnit test for JavaFX 8. However there are a couple of situations where this approach does not work, so I setup the JavaFX platform manually, and call Platform.runLater() where necessary.

What appears to be happening is that at some point the JavaFX application thread is disappearing, this means subsequent tests lockup as the Platform.runLater() calls never return - that's why I've added timeouts in example. I've proved this with calls to Thread.getAllStackTraces()

Code

public class JavaFxThreadJUnit {
    private static boolean setup;
    private Stage stage;

    @Before
    public void before() throws Exception {
        setupJavaFX();
        Platform.setImplicitExit(true);
        CountDownLatch latch = new CountDownLatch(1);
        Platform.runLater(() -> {
            stage = new Stage();
            stage.show();
            latch.countDown();
        });
        latch.await(5, TimeUnit.SECONDS);
    }

    @After
    public void after() throws Exception {
        CountDownLatch latch = new CountDownLatch(1);
        Platform.runLater(() -> {
            stage.hide();
            latch.countDown();
        });
        latch.await(5, TimeUnit.SECONDS);
    }

    @Test
    public void foo() throws Exception {
        // test stuff...
        System.out.println("foo test: "
                + Thread.getAllStackTraces().keySet().stream().map(Thread::getName).collect(Collectors.toList()));
    }

    @Test
    public void bar() throws Exception {
        // test stuff...
        System.out.println("bar test: "
                + Thread.getAllStackTraces().keySet().stream().map(Thread::getName).collect(Collectors.toList()));
    }

    // https://gist.github.com/andytill/3835914
    public static void setupJavaFX() throws InterruptedException {
        if (setup) {
            return;
        }

        long timeMillis = System.currentTimeMillis();

        final CountDownLatch latch = new CountDownLatch(1);

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                // initializes JavaFX environment
                new JFXPanel();

                latch.countDown();
            }
        });

        System.out.println("javafx initialising...");
        latch.await();
        System.out.println("javafx is initialised in " + (System.currentTimeMillis() - timeMillis) + "ms");
        setup = true;
    }

}

Output... JavaFX Application Thread was there, then it's gone...

javafx initialising...
javafx is initialised in 327ms
bar test: [Thread-3, ReaderThread, AWT-Shutdown, AWT-Windows, Thread-2, Finalizer, JavaFX Application Thread, Signal Dispatcher, Java2D Disposer, AWT-EventQueue-0, main, Attach Listener, Reference Handler, QuantumRenderer-0]
foo test: [Thread-3, ReaderThread, Java2D Disposer, AWT-Windows, Thread-2, main, Finalizer, Attach Listener, Reference Handler, Signal Dispatcher]

Upvotes: 1

Views: 1111

Answers (1)

Adam
Adam

Reputation: 36743

Looks like the stage closing is triggering an "implicit exit". I'd still be interested to know why this doesn't also affect tests using the @Rule approach...

Workaround:

Platform.setImplicitExit(false)

Upvotes: 1

Related Questions