diff --git a/test/src/test/java/aima/test/unit/environment/map2d/LabeledGraphTest.java b/test/src/test/java/aima/test/unit/environment/map2d/LabeledGraphTest.java
index 05306ee17f..6c186ec8d3 100644
--- a/test/src/test/java/aima/test/unit/environment/map2d/LabeledGraphTest.java
+++ b/test/src/test/java/aima/test/unit/environment/map2d/LabeledGraphTest.java
@@ -1,7 +1,7 @@
package aima.test.unit.environment.map2d;
import aima.core.environment.map2d.LabeledGraph;
-import javafx.util.Pair;
+import aima.core.util.datastructure.Pair;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
diff --git a/test/src/test/java/aima/test/unit/search/uninformed/DepthLimitedTreeSearchTest.java b/test/src/test/java/aima/test/unit/search/uninformed/DepthLimitedTreeSearchTest.java
new file mode 100644
index 0000000000..7adfc05c59
--- /dev/null
+++ b/test/src/test/java/aima/test/unit/search/uninformed/DepthLimitedTreeSearchTest.java
@@ -0,0 +1,97 @@
+package aima.test.unit.search.uninformed;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import aima.core.environment.map2d.GoAction;
+import aima.core.environment.map2d.SimplifiedRoadMapOfPartOfRomania;
+import aima.core.environment.support.ProblemFactory;
+import aima.core.environment.vacuum.VELocalState;
+import aima.core.environment.vacuum.VacuumEnvironment;
+import aima.core.search.api.Problem;
+import aima.core.search.basic.uninformed.DepthLimitedTreeSearch;
+
+public class DepthLimitedTreeSearchTest {
+
+ private List searchForActions(Problem problem, int limit) {
+ return new DepthLimitedTreeSearch(limit).apply(problem);
+ }
+
+ @Test
+ public void testStartEqualsGoalReturnsTrivialSolution() {
+ Assert.assertEquals(Arrays.asList((String) null),
+ searchForActions(ProblemFactory.getSimplifiedRoadMapOfPartOfRomaniaProblem(
+ SimplifiedRoadMapOfPartOfRomania.ARAD, SimplifiedRoadMapOfPartOfRomania.ARAD), 0));
+ }
+
+ @Test
+ public void testRomaniaAtSufficientDepth() {
+ Assert.assertEquals(
+ Arrays.asList(new GoAction(SimplifiedRoadMapOfPartOfRomania.SIBIU),
+ new GoAction(SimplifiedRoadMapOfPartOfRomania.FAGARAS),
+ new GoAction(SimplifiedRoadMapOfPartOfRomania.BUCHAREST)),
+ searchForActions(ProblemFactory.getSimplifiedRoadMapOfPartOfRomaniaProblem(
+ SimplifiedRoadMapOfPartOfRomania.ARAD, SimplifiedRoadMapOfPartOfRomania.BUCHAREST), 3));
+ }
+
+ @Test
+ public void testRomaniaAtInsufficientDepthReturnsCutoff() {
+ // Shortest Arad -> Bucharest is 3 actions, so a limit of 2 must hit cutoff.
+ Assert.assertNull(searchForActions(ProblemFactory.getSimplifiedRoadMapOfPartOfRomaniaProblem(
+ SimplifiedRoadMapOfPartOfRomania.ARAD, SimplifiedRoadMapOfPartOfRomania.BUCHAREST), 2));
+ }
+
+ @Test
+ public void testRomaniaAtZeroLimitNonGoalReturnsCutoff() {
+ Assert.assertNull(searchForActions(ProblemFactory.getSimplifiedRoadMapOfPartOfRomaniaProblem(
+ SimplifiedRoadMapOfPartOfRomania.ARAD, SimplifiedRoadMapOfPartOfRomania.BUCHAREST), 0));
+ }
+
+ @Test
+ public void testVacuumGoalAlreadyReachableInOneStep() {
+ // Single SUCK at the start state already cleans both squares — no LEFT no-op
+ // loop can come first, since SUCK is tried before RIGHT in the action order.
+ Assert.assertEquals(Arrays.asList(VacuumEnvironment.ACTION_SUCK),
+ searchForActions(ProblemFactory.getSimpleVacuumWorldProblem("A",
+ new VELocalState("A", VacuumEnvironment.Status.Dirty),
+ new VELocalState("B", VacuumEnvironment.Status.Clean)), 1));
+ }
+
+ @Test
+ public void testTreeSearchYieldsNonMinimalPathInVacuumWorld() {
+ // DLS-tree has no cycle check. With LEFT as a no-op at the leftmost position
+ // and action order [Left, Suck, Right], the first goal-reaching plan it finds
+ // at limit=5 walks the LEFT self-loop before turning right.
+ Assert.assertEquals(
+ Arrays.asList(VacuumEnvironment.ACTION_LEFT, VacuumEnvironment.ACTION_LEFT,
+ VacuumEnvironment.ACTION_LEFT, VacuumEnvironment.ACTION_RIGHT,
+ VacuumEnvironment.ACTION_SUCK),
+ searchForActions(ProblemFactory.getSimpleVacuumWorldProblem("A",
+ new VELocalState("A", VacuumEnvironment.Status.Clean),
+ new VELocalState("B", VacuumEnvironment.Status.Dirty)), 5));
+ }
+
+ @Test
+ public void testReachableBinaryTreeGoals() {
+ Assert.assertEquals(Arrays.asList((String) null),
+ searchForActions(ProblemFactory.getSimpleBinaryTreeProblem("A", "A"), 0));
+
+ Assert.assertEquals(Arrays.asList("B"),
+ searchForActions(ProblemFactory.getSimpleBinaryTreeProblem("A", "B"), 1));
+
+ Assert.assertEquals(Arrays.asList("C", "F", "L"),
+ searchForActions(ProblemFactory.getSimpleBinaryTreeProblem("A", "L"), 3));
+ }
+
+ @Test
+ public void testUnreachableBinaryTreeGoalReturnsFailure() {
+ // "B" is a child of "A" — searching from B cannot reach A in this tree.
+ // With no path even at large depth, DLS should report failure (empty list), not cutoff.
+ Assert.assertEquals(Collections.emptyList(),
+ searchForActions(ProblemFactory.getSimpleBinaryTreeProblem("B", "A"), 10));
+ }
+}