From 1b233e508c9da9dec8b32642672130cbebdfca85 Mon Sep 17 00:00:00 2001 From: Austin Shalit Date: Fri, 18 Aug 2017 00:36:59 -0700 Subject: [PATCH] AsyncProperty Tests --- .../api/properties/AsyncProperty.java | 7 +- .../api/properties/AsyncPropertyTest.java | 152 ++++++++++++++++++ 2 files changed, 154 insertions(+), 5 deletions(-) create mode 100644 api/src/test/java/edu/wpi/first/shuffleboard/api/properties/AsyncPropertyTest.java diff --git a/api/src/main/java/edu/wpi/first/shuffleboard/api/properties/AsyncProperty.java b/api/src/main/java/edu/wpi/first/shuffleboard/api/properties/AsyncProperty.java index 976bd462a..f8d45a97b 100644 --- a/api/src/main/java/edu/wpi/first/shuffleboard/api/properties/AsyncProperty.java +++ b/api/src/main/java/edu/wpi/first/shuffleboard/api/properties/AsyncProperty.java @@ -13,8 +13,7 @@ */ public class AsyncProperty extends SimpleObjectProperty { - private final Map, ChangeListener> wrappers - = new WeakHashMap<>(); + private final Map, ChangeListener> wrappers = new WeakHashMap<>(); public AsyncProperty() { super(); @@ -37,9 +36,7 @@ public void addListener(ChangeListener listener) { if (wrappers.containsKey(listener)) { return; } - wrappers.put(listener, (obs, prev, cur) -> { - AsyncUtils.runAsync(() -> listener.changed(obs, prev, cur)); - }); + wrappers.put(listener, (obs, prev, cur) -> AsyncUtils.runAsync(() -> listener.changed(obs, prev, cur))); super.addListener(wrappers.get(listener)); } diff --git a/api/src/test/java/edu/wpi/first/shuffleboard/api/properties/AsyncPropertyTest.java b/api/src/test/java/edu/wpi/first/shuffleboard/api/properties/AsyncPropertyTest.java new file mode 100644 index 000000000..b1b8247ef --- /dev/null +++ b/api/src/test/java/edu/wpi/first/shuffleboard/api/properties/AsyncPropertyTest.java @@ -0,0 +1,152 @@ +package edu.wpi.first.shuffleboard.api.properties; + +import javafx.application.Platform; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.value.ChangeListener; +import javafx.scene.Scene; +import javafx.scene.control.Label; +import javafx.stage.Stage; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.testfx.framework.junit5.ApplicationTest; +import org.testfx.util.WaitForAsyncUtils; + +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class AsyncPropertyTest extends ApplicationTest { + + @Override + public void start(Stage stage) throws Exception { + // Setup FxToolkit + } + + @Test + public void listenerAddedTwiceIsCalledTest() { + AsyncProperty asyncProperty = new AsyncProperty<>(); + CompletableFuture listenerFired = new CompletableFuture<>(); + ChangeListener listener = (observable, oldValue, newValue) -> listenerFired.complete(true); + + asyncProperty.addListener(listener); + asyncProperty.addListener(listener); + asyncProperty.set("Value"); + WaitForAsyncUtils.waitForFxEvents(); + + assertTrue(listenerFired.getNow(false)); + } + + @Test + public void removeListenerTest() { + AsyncProperty asyncProperty = new AsyncProperty<>(); + CompletableFuture listenerFired = new CompletableFuture<>(); + ChangeListener listener = (observable, oldValue, newValue) -> listenerFired.complete(true); + + asyncProperty.addListener(listener); + asyncProperty.removeListener(listener); + asyncProperty.set("Value"); + WaitForAsyncUtils.waitForFxEvents(); + + assertThrows(TimeoutException.class, () -> listenerFired.get(1, TimeUnit.SECONDS)); + } + + @Test + public void listenerIsRunOnFxThreadTest() { + AsyncProperty asyncProperty = new AsyncProperty<>(); + SimpleObjectProperty boundProperty = new SimpleObjectProperty<>(); + asyncProperty.bind(boundProperty); + + CompletableFuture listenerActionThread = new CompletableFuture<>(); + asyncProperty.addListener((observable, oldValue, newValue) + -> listenerActionThread.complete(Platform.isFxApplicationThread())); + boundProperty.set("Test"); + + assertTimeoutPreemptively(Duration.ofSeconds(3), + () -> assertTrue(listenerActionThread.get(), "Listener was not run on JavaFX Thread")); + } + + @Nested + public class SetTest extends ApplicationTest { + + private Label label; + private CompletableFuture exceptionThrown; + + @Override + public void start(Stage stage) throws Exception { + label = new Label(); + stage.setScene(new Scene(label)); + stage.show(); + + exceptionThrown = new CompletableFuture<>(); + Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> exceptionThrown.complete(throwable)); + } + + @Test + public void setRunsOnFxThreadTest() throws Throwable { + AsyncProperty property = new AsyncProperty<>(); + Platform.runLater(() -> label.textProperty().bind(property)); + WaitForAsyncUtils.waitForFxEvents(); + + property.set("Test"); + WaitForAsyncUtils.waitForFxEvents(); + + assertFalse(exceptionThrown.isDone(), exceptionThrown.getNow(new Throwable()).getMessage()); + } + } + + @Nested + public class ConstructorTest { + + @Test + public void initialValueConstructorTest() { + AsyncProperty asyncProperty = new AsyncProperty<>("Default"); + assertEquals("Default", asyncProperty.getValue()); + } + + @Test + public void beanNameConstructorBeanSetTest() { + Object bean = new Object(); + AsyncProperty asyncProperty = new AsyncProperty<>(bean, ""); + + assertEquals(bean, asyncProperty.getBean()); + } + + @Test + public void beanNameConstructorNameSetTest() { + AsyncProperty asyncProperty = new AsyncProperty<>(null, "aName"); + + assertEquals("aName", asyncProperty.getName()); + } + + @Test + public void beanNameValueConstructorBeanSetTest() { + Object bean = new Object(); + AsyncProperty asyncProperty = new AsyncProperty<>(bean, "", ""); + + assertEquals(bean, asyncProperty.getBean()); + } + + @Test + public void beanNameValueConstructorNameSetTest() { + AsyncProperty asyncProperty = new AsyncProperty<>(null, "myName", ""); + + assertEquals("myName", asyncProperty.getName()); + } + + @Test + public void beanNameValueConstructorValueSetTest() { + AsyncProperty asyncProperty = new AsyncProperty<>(null, "", "testValue"); + + assertEquals("testValue", asyncProperty.getValue()); + } + + } + +}