|
|
@@ -27,7 +27,7 @@ import static org.junit.Assert.fail;
|
|
|
public class SceneGraphThreadWardenTest {
|
|
|
|
|
|
private static ExecutorService executorService;
|
|
|
-
|
|
|
+
|
|
|
@SuppressWarnings({"ReassignedVariable", "AssertWithSideEffects"})
|
|
|
@BeforeClass
|
|
|
public static void setupClass() {
|
|
|
@@ -38,12 +38,12 @@ public class SceneGraphThreadWardenTest {
|
|
|
throw new RuntimeException("WARNING: Assertions are not enabled! Tests may not work correctly.");
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
@Before
|
|
|
public void setup() {
|
|
|
executorService = newSingleThreadDaemonExecutor();
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
@After
|
|
|
public void tearDown() {
|
|
|
executorService.shutdown();
|
|
|
@@ -57,20 +57,20 @@ public class SceneGraphThreadWardenTest {
|
|
|
public void testNormalNodeMutationOnMainThread() {
|
|
|
Node rootNode = new Node("root");
|
|
|
SceneGraphThreadWarden.setup(rootNode);
|
|
|
-
|
|
|
+
|
|
|
// This should work fine since we're on the main thread
|
|
|
Node child = new Node("child");
|
|
|
rootNode.attachChild(child);
|
|
|
-
|
|
|
+
|
|
|
// Add another level of children
|
|
|
Node grandchild = new Node("grandchild");
|
|
|
child.attachChild(grandchild);
|
|
|
-
|
|
|
+
|
|
|
// Detach should also work fine
|
|
|
child.detachChild(grandchild);
|
|
|
rootNode.detachChild(child);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* Test that node mutation on nodes not connected to the root node is fine even on a non main thread.
|
|
|
* <p>
|
|
|
@@ -81,27 +81,27 @@ public class SceneGraphThreadWardenTest {
|
|
|
public void testNodeMutationOnNonConnectedNodesOnNonMainThread() throws ExecutionException, InterruptedException {
|
|
|
Node rootNode = new Node("root");
|
|
|
SceneGraphThreadWarden.setup(rootNode);
|
|
|
-
|
|
|
+
|
|
|
Future<Node> nonConnectedNodeFuture = executorService.submit(() -> {
|
|
|
// This should work fine since these nodes are not connected to the root node
|
|
|
Node parent = new Node("parent");
|
|
|
Node child = new Node("child");
|
|
|
parent.attachChild(child);
|
|
|
-
|
|
|
+
|
|
|
// Add another level of children
|
|
|
Node grandchild = new Node("grandchild");
|
|
|
child.attachChild(grandchild);
|
|
|
-
|
|
|
+
|
|
|
return parent;
|
|
|
});
|
|
|
-
|
|
|
+
|
|
|
// Get the result to ensure the task completed without exceptions
|
|
|
Node nonConnectedNode = nonConnectedNodeFuture.get();
|
|
|
-
|
|
|
+
|
|
|
// Now we can attach it to the root node on the main thread
|
|
|
rootNode.attachChild(nonConnectedNode);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* Test that adding a node to the scene graph connected to the root node in a non main thread leads to an
|
|
|
* exception.
|
|
|
@@ -110,18 +110,18 @@ public class SceneGraphThreadWardenTest {
|
|
|
public void testAddingNodeToSceneGraphOnNonMainThread() throws InterruptedException {
|
|
|
Node rootNode = new Node("root");
|
|
|
SceneGraphThreadWarden.setup(rootNode);
|
|
|
-
|
|
|
+
|
|
|
// Create a child node and attach it to the root node
|
|
|
Node child = new Node("child");
|
|
|
rootNode.attachChild(child);
|
|
|
-
|
|
|
+
|
|
|
Future<Void> illegalMutationFuture = executorService.submit(() -> {
|
|
|
// This should fail because we're trying to add a node to a node that's connected to the scene graph
|
|
|
Node grandchild = new Node("grandchild");
|
|
|
child.attachChild(grandchild);
|
|
|
return null;
|
|
|
});
|
|
|
-
|
|
|
+
|
|
|
try {
|
|
|
illegalMutationFuture.get();
|
|
|
fail("Expected an IllegalThreadSceneGraphMutation exception");
|
|
|
@@ -131,7 +131,7 @@ public class SceneGraphThreadWardenTest {
|
|
|
e.getCause() instanceof IllegalThreadSceneGraphMutation);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* Test that adding a node currently attached to a root node to a different node leads to an exception.
|
|
|
* <p>
|
|
|
@@ -144,19 +144,19 @@ public class SceneGraphThreadWardenTest {
|
|
|
public void testMovingNodeAttachedToRootOnNonMainThread() throws InterruptedException {
|
|
|
Node rootNode = new Node("root");
|
|
|
SceneGraphThreadWarden.setup(rootNode);
|
|
|
-
|
|
|
+
|
|
|
// Create two child nodes and attach them to the root node
|
|
|
Node child1 = new Node("child1");
|
|
|
Node child2 = new Node("child2");
|
|
|
|
|
|
rootNode.attachChild(child2);
|
|
|
-
|
|
|
+
|
|
|
Future<Void> illegalMutationFuture = executorService.submit(() -> {
|
|
|
// This should fail because we're trying to move a node that's connected to the root node
|
|
|
child1.attachChild(child2); // This implicitly detaches child2 from rootNode
|
|
|
return null;
|
|
|
});
|
|
|
-
|
|
|
+
|
|
|
try {
|
|
|
illegalMutationFuture.get();
|
|
|
fail("Expected an IllegalThreadSceneGraphMutation exception");
|
|
|
@@ -166,7 +166,7 @@ public class SceneGraphThreadWardenTest {
|
|
|
e.getCause() instanceof IllegalThreadSceneGraphMutation);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* Test that detaching a node releases it from thread protection.
|
|
|
*/
|
|
|
@@ -174,21 +174,94 @@ public class SceneGraphThreadWardenTest {
|
|
|
public void testDetachmentReleasesProtection() throws ExecutionException, InterruptedException {
|
|
|
Node rootNode = new Node("root");
|
|
|
SceneGraphThreadWarden.setup(rootNode);
|
|
|
-
|
|
|
+
|
|
|
// Create a child node and attach it to the root node
|
|
|
Node child = new Node("child");
|
|
|
rootNode.attachChild(child);
|
|
|
-
|
|
|
+
|
|
|
// Now detach it from the root node
|
|
|
child.removeFromParent();
|
|
|
-
|
|
|
+
|
|
|
// Now we should be able to modify it on another thread
|
|
|
Future<Void> legalMutationFuture = executorService.submit(() -> {
|
|
|
Node grandchild = new Node("grandchild");
|
|
|
child.attachChild(grandchild);
|
|
|
return null;
|
|
|
});
|
|
|
-
|
|
|
+
|
|
|
+ // This should complete without exceptions
|
|
|
+ legalMutationFuture.get();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Test that adding a child to the root node also restricts the grandchild.
|
|
|
+ * This test will add a grandchild to a child BEFORE adding the child to the root,
|
|
|
+ * then try (and fail) to make an illegal on-thread change to the grandchild.
|
|
|
+ */
|
|
|
+ @Test
|
|
|
+ public void testAddingAChildToTheRootNodeAlsoRestrictsTheGrandChild() throws InterruptedException {
|
|
|
+ Node rootNode = new Node("root");
|
|
|
+ SceneGraphThreadWarden.setup(rootNode);
|
|
|
+
|
|
|
+ // Create a child node and a grandchild node
|
|
|
+ Node child = new Node("child");
|
|
|
+ Node grandchild = new Node("grandchild");
|
|
|
+
|
|
|
+ // Attach the grandchild to the child BEFORE adding the child to the root
|
|
|
+ child.attachChild(grandchild);
|
|
|
+
|
|
|
+ // Now attach the child to the root node
|
|
|
+ rootNode.attachChild(child);
|
|
|
+
|
|
|
+ // Try to make an illegal on-thread change to the grandchild
|
|
|
+ Future<Void> illegalMutationFuture = executorService.submit(() -> {
|
|
|
+ // This should fail because the grandchild is now restricted
|
|
|
+ Node greatGrandchild = new Node("greatGrandchild");
|
|
|
+ grandchild.attachChild(greatGrandchild);
|
|
|
+ return null;
|
|
|
+ });
|
|
|
+
|
|
|
+ try {
|
|
|
+ illegalMutationFuture.get();
|
|
|
+ fail("Expected an IllegalThreadSceneGraphMutation exception");
|
|
|
+ } catch (ExecutionException e) {
|
|
|
+ // This is expected - verify it's the right exception type
|
|
|
+ assertTrue("Expected IllegalThreadSceneGraphMutation, got: " + e.getCause().getClass().getName(),
|
|
|
+ e.getCause() instanceof IllegalThreadSceneGraphMutation);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Test that removing a child from the root node also unrestricts the grandchild.
|
|
|
+ * This test will add a child with a grandchild to the root node, then remove the child
|
|
|
+ * and verify that the grandchild can be modified on a non-main thread.
|
|
|
+ */
|
|
|
+ @Test
|
|
|
+ public void testRemovingAChildFromTheRootNodeAlsoUnrestrictsTheGrandChild() throws ExecutionException, InterruptedException {
|
|
|
+ Node rootNode = new Node("root");
|
|
|
+ SceneGraphThreadWarden.setup(rootNode);
|
|
|
+
|
|
|
+ // Create a child node and a grandchild node
|
|
|
+ Node child = new Node("child");
|
|
|
+ Node grandchild = new Node("grandchild");
|
|
|
+
|
|
|
+ // Attach the grandchild to the child
|
|
|
+ child.attachChild(grandchild);
|
|
|
+
|
|
|
+ // Attach the child to the root node
|
|
|
+ rootNode.attachChild(child);
|
|
|
+
|
|
|
+ // Now remove the child from the root node
|
|
|
+ child.removeFromParent();
|
|
|
+
|
|
|
+ // Try to make a change to the grandchild on a non-main thread
|
|
|
+ Future<Void> legalMutationFuture = executorService.submit(() -> {
|
|
|
+ // This should succeed because the grandchild is no longer restricted
|
|
|
+ Node greatGrandchild = new Node("greatGrandchild");
|
|
|
+ grandchild.attachChild(greatGrandchild);
|
|
|
+ return null;
|
|
|
+ });
|
|
|
+
|
|
|
// This should complete without exceptions
|
|
|
legalMutationFuture.get();
|
|
|
}
|
|
|
@@ -201,7 +274,7 @@ public class SceneGraphThreadWardenTest {
|
|
|
private static ExecutorService newSingleThreadDaemonExecutor() {
|
|
|
return Executors.newSingleThreadExecutor(daemonThreadFactory());
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* Creates a thread factory that produces daemon threads.
|
|
|
*/
|
|
|
@@ -212,4 +285,4 @@ public class SceneGraphThreadWardenTest {
|
|
|
return t;
|
|
|
};
|
|
|
}
|
|
|
-}
|
|
|
+}
|