From 876c73b31115be5f69968a272156220014694d96 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2026 01:00:23 +0000 Subject: [PATCH] Add tests for GreedySearch core algorithm Added test cases covering state checking, non-state checking, depth limits, and unreachable goals. Provided minimal mock implementations inside the test file to satisfy compilation requirements for the standalone setup without polluting the repository with additional domain files. Co-authored-by: tsainez <13399044+tsainez@users.noreply.github.com> --- java/GreedySearchTest.java | 270 +++++++++++++++++++++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 java/GreedySearchTest.java diff --git a/java/GreedySearchTest.java b/java/GreedySearchTest.java new file mode 100644 index 0000000..bae045e --- /dev/null +++ b/java/GreedySearchTest.java @@ -0,0 +1,270 @@ +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.PriorityQueue; +import java.util.Comparator; + +public class GreedySearchTest { + + public static void main(String[] args) { + System.out.println("Running GreedySearch tests..."); + int passed = 0; + int failed = 0; + + Runnable[] tests = { + GreedySearchTest::testAlreadyAtDestination, + GreedySearchTest::testUnreachableGoal, + GreedySearchTest::testStateCheckingHappyPath, + GreedySearchTest::testNoStateCheckingHappyPath, + GreedySearchTest::testDepthLimitReached + }; + + for (Runnable test : tests) { + try { + test.run(); + passed++; + } catch (AssertionError | Exception e) { + System.err.println("Test failed!"); + e.printStackTrace(); + failed++; + } + } + + System.out.println("Tests passed: " + passed + ", Failed: " + failed); + if (failed > 0) { + System.exit(1); + } + } + + private static RoadMap buildMockGraph() { + RoadMap map = new RoadMap(); + + Location a = new Location("A", 0, 0); + Location b = new Location("B", 0, 10); + Location c = new Location("C", 10, 10); + Location d = new Location("D", 10, 0); + Location goal = new Location("Goal", 20, 20); + Location unreachable = new Location("Unreachable", -10, -10); + + a.roads = Arrays.asList(new Road(a, b, 1), new Road(a, c, 5)); + b.roads = Arrays.asList(new Road(b, c, 1), new Road(b, a, 1)); + c.roads = Arrays.asList(new Road(c, d, 2), new Road(c, goal, 2)); + d.roads = Arrays.asList(new Road(d, goal, 1)); // D -> Goal is closer + goal.roads = Arrays.asList(); // no outgoing + unreachable.roads = Arrays.asList(); // isolated + + map.addLocation(a); + map.addLocation(b); + map.addLocation(c); + map.addLocation(d); + map.addLocation(goal); + map.addLocation(unreachable); + + return map; + } + + private static void assertNotNull(Object obj) { + if (obj == null) throw new AssertionError("Expected not null"); + } + + private static void assertNull(Object obj) { + if (obj != null) throw new AssertionError("Expected null, but got " + obj); + } + + private static void assertEquals(Object expected, Object actual) { + if (expected == null && actual == null) return; + if (expected == null || !expected.equals(actual)) { + throw new AssertionError("Expected " + expected + ", but got " + actual); + } + } + + private static void testAlreadyAtDestination() { + RoadMap map = buildMockGraph(); + GreedySearch search = new GreedySearch(map, "A", "A", 10, new SearchDisplay()); + Node result = search.search(true); + assertNotNull(result); + assertEquals("A", result.loc.name); + System.out.println("testAlreadyAtDestination passed."); + } + + private static void testUnreachableGoal() { + RoadMap map = buildMockGraph(); + GreedySearch search = new GreedySearch(map, "A", "Unreachable", 10, new SearchDisplay()); + Node result = search.search(true); + assertNull(result); + System.out.println("testUnreachableGoal passed."); + } + + private static void testStateCheckingHappyPath() { + RoadMap map = buildMockGraph(); + GreedySearch search = new GreedySearch(map, "A", "Goal", 10, new SearchDisplay()); + Node result = search.search(true); + assertNotNull(result); + assertEquals("Goal", result.loc.name); + System.out.println("testStateCheckingHappyPath passed."); + } + + private static void testNoStateCheckingHappyPath() { + RoadMap map = buildMockGraph(); + GreedySearch search = new GreedySearch(map, "A", "Goal", 10, new SearchDisplay()); + Node result = search.search(false); + assertNotNull(result); + assertEquals("Goal", result.loc.name); + System.out.println("testNoStateCheckingHappyPath passed."); + } + + private static void testDepthLimitReached() { + RoadMap map = buildMockGraph(); + GreedySearch search = new GreedySearch(map, "A", "Goal", 0, new SearchDisplay()); + Node result = search.search(false); + assertNull(result); + System.out.println("testDepthLimitReached passed."); + } +} + +// --------------------------------------------------------- +// Minimal Stubs needed to test GreedySearch +// --------------------------------------------------------- + +class Location { + public String name; + public double latitude; + public double longitude; + public List roads = new ArrayList<>(); + + public Location(String name, double latitude, double longitude) { + this.name = name; + this.latitude = latitude; + this.longitude = longitude; + } +} + +class Road { + public Location fromLocation; + public Location toLocation; + public double cost; + + public Road(Location fromLocation, Location toLocation, double cost) { + this.fromLocation = fromLocation; + this.toLocation = toLocation; + this.cost = cost; + } +} + +class RoadMap { + public List locations = new ArrayList<>(); + + public Location findLocation(String name) { + for (Location loc : locations) { + if (loc.name.equals(name)) { + return loc; + } + } + return null; + } + + public void addLocation(Location loc) { + locations.add(loc); + } +} + +abstract class Heuristic { + public Location destination; + + public void setDestination(Location destination) { + this.destination = destination; + } + + public abstract double heuristicValue(Node thisNode); +} + +class SearchDisplay { +} + +enum SortBy { + h, f +} + +class SortedFrontier { + private PriorityQueue queue; + + public SortedFrontier(SortBy sortBy) { + if (sortBy == SortBy.h) { + queue = new PriorityQueue<>(Comparator.comparingDouble(n -> n.heuristicValue)); + } else { + queue = new PriorityQueue<>(Comparator.comparingDouble(n -> n.partialPathCost + n.heuristicValue)); + } + } + + public boolean isEmpty() { + return queue.isEmpty(); + } + + public Node removeTop() { + return queue.poll(); + } + + public void addSorted(Node n) { + queue.add(n); + } + + public void addSorted(List nodes) { + queue.addAll(nodes); + } + + public boolean contains(String name) { + for (Node n : queue) { + if (n.loc.name.equals(name)) return true; + } + return false; + } + + public boolean contains(Node n) { + return queue.contains(n); + } + + public void remove(Node n) { + queue.remove(n); + } + + public Node find(Node node) { + for (Node n : queue) { + if (n.loc.name.equals(node.loc.name)) return n; + } + return null; + } +} + +class Node { + public Location loc; + public double heuristicValue; + public double partialPathCost; + public int depth; + public List children = new ArrayList<>(); + + public Node(Location loc) { + this.loc = loc; + this.depth = 0; + this.partialPathCost = 0; + } + + public Node(Location loc, double partialPathCost, int depth) { + this.loc = loc; + this.partialPathCost = partialPathCost; + this.depth = depth; + } + + public boolean isDestination(String destinationLoc) { + return loc.name.equals(destinationLoc); + } + + public void expand(Heuristic val) { + if (children.isEmpty() && loc.roads != null) { + for (Road r : loc.roads) { + Node child = new Node(r.toLocation, this.partialPathCost + r.cost, this.depth + 1); + child.heuristicValue = val.heuristicValue(child); + children.add(child); + } + } + } +}