Commit 6a618684 authored by Archie Shahidullah's avatar Archie Shahidullah :expressionless: Committed by Ethan Ordentlich
Browse files

Review Tests for Project 8

parent 61c3de6a
Showing with 395 additions and 168 deletions
+395 -168
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="A Tests" type="JUnit" factoryName="JUnit">
<module name="project08-beavermaps" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="tags" />
<option name="PARAMETERS" value="" />
<tag value="A" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>
\ No newline at end of file
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="B Tests" type="JUnit" factoryName="JUnit">
<module name="project08-beavermaps" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="tags" />
<option name="PARAMETERS" value="" />
<tag value="B" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>
\ No newline at end of file
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="BeaverMapsGraph Tests (C)" type="JUnit" factoryName="JUnit">
<module name="project08-beavermaps" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="tags" />
<tag value="C &amp; Beavermap" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>
\ No newline at end of file
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="C Tests" type="JUnit" factoryName="JUnit">
<module name="project08-beavermaps" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="tags" />
<option name="PARAMETERS" value="" />
<tag value="C" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>
\ No newline at end of file
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Graph Tests (C)" type="JUnit" factoryName="JUnit">
<module name="project08-beavermaps" />
<option name="PACKAGE_NAME" value="edu.caltech.cs2.project08" />
<option name="MAIN_CLASS_NAME" value="edu.caltech.cs2.project08.GraphTests" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="class" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>
\ No newline at end of file
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Marvel Tests (C)" type="JUnit" factoryName="JUnit">
<module name="project08-beavermaps" />
<option name="PACKAGE_NAME" value="edu.caltech.cs2.project08" />
<option name="MAIN_CLASS_NAME" value="edu.caltech.cs2.project08.MarvelTests" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="class" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>
\ No newline at end of file
...@@ -6,14 +6,11 @@ import edu.caltech.cs2.interfaces.IDeque; ...@@ -6,14 +6,11 @@ import edu.caltech.cs2.interfaces.IDeque;
import edu.caltech.cs2.interfaces.IDictionary; import edu.caltech.cs2.interfaces.IDictionary;
import edu.caltech.cs2.interfaces.ISet; import edu.caltech.cs2.interfaces.ISet;
import java.io.File;
import java.io.FileReader; import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
public class BeaverMapsGraph extends Graph<Long, Double> { public class BeaverMapsGraph extends Graph<Long, Double> {
private static JsonParser JSON_PARSER = new JsonParser();
private IDictionary<Long, Location> ids; private IDictionary<Long, Location> ids;
private ISet<Location> buildings; private ISet<Location> buildings;
...@@ -78,8 +75,7 @@ public class BeaverMapsGraph extends Graph<Long, Double> { ...@@ -78,8 +75,7 @@ public class BeaverMapsGraph extends Graph<Long, Double> {
} }
/** /**
* Returns a set of locations which are no more than threshold feet * Returns a set of locations which are reachable along a path that goes no further than `threshold` feet from start
* away from start.
* @param start the location to search around * @param start the location to search around
* @param threshold the number of feet in the search radius * @param threshold the number of feet in the search radius
* @return * @return
...@@ -117,12 +113,8 @@ public class BeaverMapsGraph extends Graph<Long, Double> { ...@@ -117,12 +113,8 @@ public class BeaverMapsGraph extends Graph<Long, Double> {
* @return the JSON data from filename * @return the JSON data from filename
*/ */
private static JsonElement fromFile(String filename) { private static JsonElement fromFile(String filename) {
try { try (FileReader reader = new FileReader(filename)) {
return JSON_PARSER.parse( return JsonParser.parseReader(reader);
new FileReader(
new File(filename)
)
);
} catch (IOException e) { } catch (IOException e) {
return null; return null;
} }
......
package edu.caltech.cs2.datastructures; package edu.caltech.cs2.datastructures;
import edu.caltech.cs2.interfaces.*; import edu.caltech.cs2.interfaces.IDictionary;
import edu.caltech.cs2.interfaces.IGraph;
import edu.caltech.cs2.interfaces.ISet;
public class Graph<V, E> extends IGraph<V, E> { public class Graph<V, E> implements IGraph<V, E> {
@Override @Override
public boolean addVertex(V vertex) { public boolean addVertex(V vertex) {
......
...@@ -23,8 +23,8 @@ public interface IDeque<E> extends ICollection<E> { ...@@ -23,8 +23,8 @@ public interface IDeque<E> extends ICollection<E> {
* @param e Element to add * @param e Element to add
*/ */
@Override @Override
default public void add(E e){ default public void add(E e) {
this.addFront(e); this.addBack(e);
} }
/** /**
......
package edu.caltech.cs2.interfaces; package edu.caltech.cs2.interfaces;
import edu.caltech.cs2.datastructures.LinkedDeque;
public interface IDictionary<K, V> extends Iterable<K> { public interface IDictionary<K, V> extends Iterable<K> {
public static class Entry<K, V> { public static class Entry<K, V> {
......
package edu.caltech.cs2.interfaces; package edu.caltech.cs2.interfaces;
public abstract class IGraph<V, E> { public interface IGraph<V, E> {
/** /**
* Add a vertex to the graph. * Add a vertex to the graph.
* @param vertex The vertex to add * @param vertex The vertex to add
* @return true if vertex was not present already. * @return true if vertex was not present already.
*/ */
public abstract boolean addVertex(V vertex); public boolean addVertex(V vertex);
/** /**
* Adds edge e to the graph. * Adds edge e to the graph.
* *
* @param e The edge to add. * @param e The edge to add.
* @throws IllegalArgumentException * @throws IllegalArgumentException
* If e is not a valid edge (eg. refers to vertices not in the graph). * If src and dest are not valid vertices (eg. refers to vertices not in the graph).
* @return true If e was not already present; false if it was (in which case the graph is still updated). * @return true if an edge from src to dest did not exist previously.
* false if an edge from src to dest did exist previously, in which case the edge value is updated
*/ */
public abstract boolean addEdge(V src, V dest, E e); public boolean addEdge(V src, V dest, E e);
/** /**
* Adds edge e to the graph in both directionns. * Adds edge e to the graph in both directionns.
* *
* @param e The edge to add. * @param e The edge to add.
* @throws IllegalArgumentException * @throws IllegalArgumentException
* If e is not a valid edge (eg. refers to vertices not in the graph). * If v1 and v2 are not valid vertices (eg. refers to vertices not in the graph).
* @return true If e was not already present in either direction; false if it was (in which case the graph is still updated). * @return true if an edge between src and dest did not exist in either direction.
* false if an edge between src and dest existed in either direction, in which case the edge value is
* updated.
*/ */
public abstract boolean addUndirectedEdge(V src, V dest, E e); public boolean addUndirectedEdge(V v1, V v2, E e);
/** /**
* Remove an edge from src to dest from the graph. * Remove an edge from src to dest from the graph.
* *
* @throws IllegalArgumentException if src or dest is not in the graph. * @throws IllegalArgumentException if src or dest is not in the graph.
* @return true if an edge from src to dest edge was present. * @return true if an edge from src to dest was present.
*/ */
public abstract boolean removeEdge(V src, V dest); public boolean removeEdge(V src, V dest);
/** /**
* Returns the set of vertices in the graph. * Returns the set of vertices in the graph.
* @return The set of all vertices in the graph. * @return The set of all vertices in the graph.
*/ */
public abstract ISet<V> vertices(); public ISet<V> vertices();
/** /**
* Tests if vertices i and j are adjacent, returning the edge between * Tests if vertices i and j are adjacent, returning the edge between
...@@ -50,7 +53,7 @@ public abstract class IGraph<V, E> { ...@@ -50,7 +53,7 @@ public abstract class IGraph<V, E> {
* @return The edge from i to j if it exists in the graph; * @return The edge from i to j if it exists in the graph;
* null otherwise. * null otherwise.
*/ */
public abstract E adjacent(V i, V j); public E adjacent(V i, V j);
/** /**
* Return the neighbours of a given vertex when this graph is treated as * Return the neighbours of a given vertex when this graph is treated as
...@@ -60,5 +63,5 @@ public abstract class IGraph<V, E> { ...@@ -60,5 +63,5 @@ public abstract class IGraph<V, E> {
* @throws IllegalArgumentException if vertex is not in the graph. * @throws IllegalArgumentException if vertex is not in the graph.
* @return The set of neighbors of vertex. * @return The set of neighbors of vertex.
*/ */
public abstract ISet<V> neighbors(V vertex); public ISet<V> neighbors(V vertex);
} }
\ No newline at end of file
...@@ -5,6 +5,7 @@ import com.sun.net.httpserver.HttpExchange; ...@@ -5,6 +5,7 @@ import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer; import com.sun.net.httpserver.HttpServer;
import edu.caltech.cs2.datastructures.BeaverMapsGraph; import edu.caltech.cs2.datastructures.BeaverMapsGraph;
import edu.caltech.cs2.datastructures.LinkedDeque;
import edu.caltech.cs2.datastructures.Location; import edu.caltech.cs2.datastructures.Location;
import edu.caltech.cs2.interfaces.IDeque; import edu.caltech.cs2.interfaces.IDeque;
import edu.caltech.cs2.interfaces.ISet; import edu.caltech.cs2.interfaces.ISet;
......
package edu.caltech.cs2.project08; package edu.caltech.cs2.project08;
import edu.caltech.cs2.datastructures.LinkedDeque;
import edu.caltech.cs2.datastructures.Location; import edu.caltech.cs2.datastructures.Location;
import edu.caltech.cs2.datastructures.TrieMap;
import edu.caltech.cs2.interfaces.IDeque; import edu.caltech.cs2.interfaces.IDeque;
import edu.caltech.cs2.interfaces.ISet; import edu.caltech.cs2.interfaces.ISet;
import edu.caltech.cs2.interfaces.ITrieMap; import edu.caltech.cs2.interfaces.ITrieMap;
......
...@@ -18,11 +18,11 @@ import java.util.List; ...@@ -18,11 +18,11 @@ import java.util.List;
import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.fail;
public class Inspection { public class Inspection {
private static String getUsageOf(List<String> regexps, List<? extends Node> codeObjects) { private static String[] getUsageOf(List<String> regexps, List<? extends Node> codeObjects) {
for (Node d : codeObjects) { for (Node d : codeObjects) {
for (String regex : regexps) { for (String regex : regexps) {
if (d.toString().replaceAll("\\R", "").matches(".*" + regex + ".*")) { if (d.toString().replaceAll("\\R", "").matches(".*" + regex + ".*")) {
return regex; return new String[]{d.toString().replaceAll("\\R", ""), regex};
} }
} }
} }
...@@ -32,9 +32,25 @@ public class Inspection { ...@@ -32,9 +32,25 @@ public class Inspection {
public static void assertNoImportsOf(String filePath, List<String> regexps) { public static void assertNoImportsOf(String filePath, List<String> regexps) {
try { try {
CompilationUnit cu = JavaParser.parse(new File(filePath)); CompilationUnit cu = JavaParser.parse(new File(filePath));
String usage = getUsageOf(regexps, cu.getImports()); String[] usage = getUsageOf(regexps, cu.getImports());
if (usage != null) { if (usage != null) {
fail("You may not import " + usage + " in " + Paths.get(filePath).getFileName() + "."); fail(usage[0] + " is a banned import under " + usage[1] + " in " + Paths.get(filePath).getFileName()
+ ".");
}
} catch (FileNotFoundException e) {
fail("Missing Java file: " + Paths.get(filePath).getFileName());
}
}
public static void assertNoImportsOfExcept(String filePath, String bannedImport, List<String> allowedClasses) {
try {
CompilationUnit cu = JavaParser.parse(new File(filePath));
String bannedRegex = bannedImport + "\\.(?!" + String.join("|", allowedClasses) + ")";
String[] usage = getUsageOf(List.of(bannedRegex), cu.getImports());
if (usage != null) {
fail(usage[0] + " is a banned import under " + bannedImport +
" and is not an allowed import " +
allowedClasses + " in " + Paths.get(filePath).getFileName() + ".");
} }
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
fail("Missing Java file: " + Paths.get(filePath).getFileName()); fail("Missing Java file: " + Paths.get(filePath).getFileName());
...@@ -66,16 +82,16 @@ public class Inspection { ...@@ -66,16 +82,16 @@ public class Inspection {
List<ConstructorDeclaration> constructors = new ArrayList<>(); List<ConstructorDeclaration> constructors = new ArrayList<>();
CONSTRUCTOR_COLLECTOR.visit(cu, constructors); CONSTRUCTOR_COLLECTOR.visit(cu, constructors);
String usage = getUsageOf(regexps, constructors); String[] usage = getUsageOf(regexps, constructors);
if (usage != null) { if (usage != null) {
fail("You may not use " + usage + " in " + Paths.get(filePath).getFileName() + "."); fail("You may not use " + usage[1] + " in " + Paths.get(filePath).getFileName() + ".");
} }
List<MethodDeclaration> methods = new ArrayList<>(); List<MethodDeclaration> methods = new ArrayList<>();
METHOD_COLLECTOR.visit(cu, methods); METHOD_COLLECTOR.visit(cu, methods);
usage = getUsageOf(regexps, methods); usage = getUsageOf(regexps, methods);
if (usage != null) { if (usage != null) {
fail("You may not use " + usage + " in " + Paths.get(filePath).getFileName() + "."); fail("You may not use " + usage[1] + " in " + Paths.get(filePath).getFileName() + ".");
} }
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
fail("Missing Java file: " + Paths.get(filePath).getFileName()); fail("Missing Java file: " + Paths.get(filePath).getFileName());
...@@ -93,8 +109,7 @@ public class Inspection { ...@@ -93,8 +109,7 @@ public class Inspection {
List<Statement> statements = body.getStatements(); List<Statement> statements = body.getStatements();
if (statements.size() != 1) { if (statements.size() != 1) {
nonThisConstructors++; nonThisConstructors++;
} } else if (!statements.get(0).toString().startsWith("this(")) {
else if (!statements.get(0).toString().startsWith("this(")) {
nonThisConstructors++; nonThisConstructors++;
} }
......
...@@ -9,7 +9,6 @@ import edu.caltech.cs2.datastructures.Location; ...@@ -9,7 +9,6 @@ import edu.caltech.cs2.datastructures.Location;
import edu.caltech.cs2.helpers.Inspection; import edu.caltech.cs2.helpers.Inspection;
import edu.caltech.cs2.helpers.Reflection; import edu.caltech.cs2.helpers.Reflection;
import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileReader; import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
...@@ -22,7 +21,7 @@ import edu.caltech.cs2.interfaces.ISet; ...@@ -22,7 +21,7 @@ import edu.caltech.cs2.interfaces.ISet;
import org.hamcrest.MatcherAssert; import org.hamcrest.MatcherAssert;
import org.hamcrest.collection.IsIterableContainingInAnyOrder; import org.hamcrest.collection.IsIterableContainingInAnyOrder;
import org.hamcrest.collection.IsIterableContainingInOrder; import org.hamcrest.collection.IsIterableContainingInOrder;
import org.hamcrest.core.IsCollectionContaining; import org.hamcrest.core.IsIterableContaining;
import org.junit.jupiter.api.*; import org.junit.jupiter.api.*;
import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.TestMethodOrder;
...@@ -34,33 +33,65 @@ import static org.junit.jupiter.api.Assertions.*; ...@@ -34,33 +33,65 @@ import static org.junit.jupiter.api.Assertions.*;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class) @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class BeavermapTests { public class BeavermapTests {
private static String BEAVERMAP_GRAPH_SOURCE = "src/edu/caltech/cs2/datastructures/BeaverMapsGraph.java"; private static String BEAVERMAP_GRAPH_SOURCE = "src/edu/caltech/cs2/datastructures/BeaverMapsGraph.java";
private static JsonParser JSON_PARSER = new JsonParser();
private static JsonElement fromFile(String filename) { private static JsonElement fromFile(String filename) {
try { try (FileReader reader = new FileReader(filename)) {
return JSON_PARSER.parse( return JsonParser.parseReader(reader);
new FileReader( }
new File(filename) catch (IOException e) {
)
);
} catch (IOException e) {
return null; return null;
} }
} }
@Tag("C") @Tag("C")
@Tag("Beavermap")
@DisplayName("BeaverMapsGraph implements required public methods")
@Test
@Order(0)
public void testMethodsBeaverMapsGraph() {
SortedSet<String> expected = new TreeSet<>(List.of(
"getLocationByName", "getBuildings", "getClosestBuilding", "dfs", "dijkstra", "addVertex"
));
SortedSet<String> actual = new TreeSet<>(
Stream.of(BeaverMapsGraph.class.getDeclaredMethods())
.filter(Reflection.hasModifier("public"))
.map(x -> x.getName())
.collect(Collectors.toList()));
MatcherAssert.assertThat(new ArrayList<>(actual),
IsIterableContaining.hasItems((expected.toArray())));
}
@Tag("C")
@Tag("Beavermap")
@Order(1)
@DisplayName("Does not use or import disallowed classes") @DisplayName("Does not use or import disallowed classes")
@Test @Test
public void testForInvalidClasses() { public void testForInvalidClasses() {
List<String> graphDisallow = List.of("java\\.util\\.(?!Iterator)", "java\\.lang\\.reflect"); List<String> graphDisallow = List.of("java\\.lang\\.reflect");
Inspection.assertNoImportsOf(BEAVERMAP_GRAPH_SOURCE, graphDisallow); Inspection.assertNoImportsOf(BEAVERMAP_GRAPH_SOURCE, graphDisallow);
Inspection.assertNoUsageOf(BEAVERMAP_GRAPH_SOURCE, graphDisallow); Inspection.assertNoUsageOf(BEAVERMAP_GRAPH_SOURCE, graphDisallow);
} }
@Order(2)
@DisplayName("Does not use or import disallowed classes from java.util")
@Test
@Tag("C")
@Tag("Beavermap")
public void testForInvalidImportsJavaUtil() {
List<String> allowed = List.of("Iterator");
Inspection.assertNoImportsOfExcept(BEAVERMAP_GRAPH_SOURCE, "java\\.util", allowed);
List<String> bannedUsages = List.of("java\\.util\\.(?!" + String.join("|", allowed) + ")");
Inspection.assertNoUsageOf(BEAVERMAP_GRAPH_SOURCE, bannedUsages);
}
// Only use Caltech map and buildings to test for correctness // Only use Caltech map and buildings to test for correctness
@Tag("C") @Tag("C")
@Tag("Beavermap")
@Test @Test
@Order(3)
@DisplayName("Test getLocationById()")
public void testGetLocationByID() { public void testGetLocationByID() {
BeaverMapsGraph bmg = new BeaverMapsGraph( BeaverMapsGraph bmg = new BeaverMapsGraph(
"data/caltech/caltech.buildings.json", "data/caltech/caltech.buildings.json",
...@@ -74,7 +105,10 @@ public class BeavermapTests { ...@@ -74,7 +105,10 @@ public class BeavermapTests {
} }
@Tag("C") @Tag("C")
@Tag("Beavermap")
@Test @Test
@Order(4)
@DisplayName("Test getLocationByName()")
public void testGetLocationByName() { public void testGetLocationByName() {
BeaverMapsGraph bmg = new BeaverMapsGraph( BeaverMapsGraph bmg = new BeaverMapsGraph(
"data/caltech/caltech.buildings.json", "data/caltech/caltech.buildings.json",
...@@ -90,12 +124,14 @@ public class BeavermapTests { ...@@ -90,12 +124,14 @@ public class BeavermapTests {
} }
@Tag("C") @Tag("C")
@Tag("Beavermap")
@DisplayName("Test getBuildings()") @DisplayName("Test getBuildings()")
@ParameterizedTest(name = "Test getBuildings() on {0}") @ParameterizedTest(name = "Test getBuildings() on {0}")
@CsvSource({ @CsvSource({
"caltech/caltech.buildings.json, caltech/caltech.waypoints.json, caltech/caltech.roads.json", "caltech/caltech.buildings.json, caltech/caltech.waypoints.json, caltech/caltech.roads.json",
"pasadena/pasadena.buildings.json, pasadena/pasadena.waypoints.json, pasadena/pasadena.roads.json", "pasadena/pasadena.buildings.json, pasadena/pasadena.waypoints.json, pasadena/pasadena.roads.json",
}) })
@Order(5)
public void testGetBuildings(String buildingsFile, String waypointsFile, String roadsFile) { public void testGetBuildings(String buildingsFile, String waypointsFile, String roadsFile) {
BeaverMapsGraph bmg = new BeaverMapsGraph( BeaverMapsGraph bmg = new BeaverMapsGraph(
"data/" + buildingsFile, "data/" + waypointsFile, "data/" + roadsFile); "data/" + buildingsFile, "data/" + waypointsFile, "data/" + roadsFile);
...@@ -110,12 +146,14 @@ public class BeavermapTests { ...@@ -110,12 +146,14 @@ public class BeavermapTests {
} }
@Tag("C") @Tag("C")
@Tag("Beavermap")
@DisplayName("Test getClosestBuilding()") @DisplayName("Test getClosestBuilding()")
@ParameterizedTest(name = "Test getClosestBuilding() on {0}") @ParameterizedTest(name = "Test getClosestBuilding() on {0}")
@CsvSource({ @CsvSource({
"caltech/caltech.buildings.json, caltech/caltech.waypoints.json, caltech/caltech.roads.json, caltech/caltech.closest_trace.json", "caltech/caltech.buildings.json, caltech/caltech.waypoints.json, caltech/caltech.roads.json, caltech/caltech.closest_trace.json",
"pasadena/pasadena.buildings.json, pasadena/pasadena.waypoints.json, pasadena/pasadena.roads.json, pasadena/pasadena.closest_trace.json", "pasadena/pasadena.buildings.json, pasadena/pasadena.waypoints.json, pasadena/pasadena.roads.json, pasadena/pasadena.closest_trace.json",
}) })
@Order(6)
public void testGetClosestLocation(String buildingsFile, String waypointsFile, String roadsFile, String traceFile) { public void testGetClosestLocation(String buildingsFile, String waypointsFile, String roadsFile, String traceFile) {
BeaverMapsGraph bmg = new BeaverMapsGraph( BeaverMapsGraph bmg = new BeaverMapsGraph(
"data/" + buildingsFile, "data/" + waypointsFile, "data/" + roadsFile); "data/" + buildingsFile, "data/" + waypointsFile, "data/" + roadsFile);
...@@ -131,29 +169,34 @@ public class BeavermapTests { ...@@ -131,29 +169,34 @@ public class BeavermapTests {
} }
@Tag("C") @Tag("C")
@DisplayName("BeaverMapsGraph implements required public methods") @Tag("Beavermap")
@DisplayName("Test addVertex() updates BeaverMapsGraph and underlying Graph properly")
@Test @Test
public void testMethodsBeaverMapsGraph() { @Order(7)
SortedSet<String> expected = new TreeSet<>(List.of( public void testAddVertexBeaverMapsGraph() {
"getLocationByName", "getBuildings", "getClosestBuilding", "dfs", "dijkstra", "addVertex" BeaverMapsGraph bmg = new BeaverMapsGraph(
)); "data/caltech/caltech.buildings.json",
SortedSet<String> actual = new TreeSet<>( "data/caltech/caltech.waypoints.json",
Stream.of(BeaverMapsGraph.class.getDeclaredMethods()) "data/caltech/caltech.roads.json");
.filter(Reflection.hasModifier("public")) JsonElement bs = fromFile("data/pasadena/pasadena.buildings.json");
.map(x -> x.getName()) for (JsonElement b : bs.getAsJsonArray()) {
.collect(Collectors.toList())); Location loc = new Location(b.getAsJsonObject());
MatcherAssert.assertThat(new ArrayList<>(actual), bmg.addVertex(loc);
IsCollectionContaining.hasItems((expected.toArray()))); assertNotNull(bmg.getLocationByID(loc.id), "Location id " + loc.id + " not found by id");
// Test that the vertex was actually added to the graph
bmg.neighbors(loc.id);
}
} }
// Note: Pasadena map is WAY TOO LARGE to test all edges, don't try // Note: Pasadena map is WAY TOO LARGE to test all edges, don't try
@Tag("C") @Tag("C")
@DisplayName("Tests nodes and edges in map for filename") @Tag("Beavermap")
@DisplayName("Completely check all nodes and edges in BeaverMapsGraph loaded from files")
@ParameterizedTest(name = "Test nodes in file {0}") @ParameterizedTest(name = "Test nodes in file {0}")
@CsvSource({ @CsvSource({
"caltech/caltech.buildings.json, caltech/caltech.waypoints.json, caltech/caltech.roads.json, caltech/caltech.neighbors_trace.json" "caltech/caltech.buildings.json, caltech/caltech.waypoints.json, caltech/caltech.roads.json, caltech/caltech.neighbors_trace.json"
}) })
@Order(8)
public void testNodesEdgesInMap(String bFile, String wFile, String roadsFile, String traceFile) { public void testNodesEdgesInMap(String bFile, String wFile, String roadsFile, String traceFile) {
BeaverMapsGraph bmg = new BeaverMapsGraph("data/" + bFile, "data/" + wFile, "data/" + roadsFile); BeaverMapsGraph bmg = new BeaverMapsGraph("data/" + bFile, "data/" + wFile, "data/" + roadsFile);
...@@ -181,6 +224,7 @@ public class BeavermapTests { ...@@ -181,6 +224,7 @@ public class BeavermapTests {
} }
// Use this instead of MatcherAssert to provide better errors (though I doubt they'll be needed) // Use this instead of MatcherAssert to provide better errors (though I doubt they'll be needed)
// Should use Truth instead... but not this year.
if (missingNeighbors.size() > 0) { if (missingNeighbors.size() > 0) {
fail(locID + " missing neighbors " + missingNeighbors); fail(locID + " missing neighbors " + missingNeighbors);
} else if (actualNeighbors.size() != 0) { } else if (actualNeighbors.size() != 0) {
...@@ -198,6 +242,7 @@ public class BeavermapTests { ...@@ -198,6 +242,7 @@ public class BeavermapTests {
"caltech/caltech.buildings.json, caltech/caltech.waypoints.json, caltech/caltech.roads.json, caltech/caltech.radius_trace.json", "caltech/caltech.buildings.json, caltech/caltech.waypoints.json, caltech/caltech.roads.json, caltech/caltech.radius_trace.json",
"pasadena/pasadena.buildings.json, pasadena/pasadena.waypoints.json, pasadena/pasadena.roads.json, pasadena/pasadena.radius_trace.json", "pasadena/pasadena.buildings.json, pasadena/pasadena.waypoints.json, pasadena/pasadena.roads.json, pasadena/pasadena.radius_trace.json",
}) })
@Order(9)
public void testDFSRadius(String buildingsFile, String waypointsFile, String roadsFile, String traceFile) { public void testDFSRadius(String buildingsFile, String waypointsFile, String roadsFile, String traceFile) {
BeaverMapsGraph bmg = new BeaverMapsGraph( BeaverMapsGraph bmg = new BeaverMapsGraph(
...@@ -226,6 +271,34 @@ public class BeavermapTests { ...@@ -226,6 +271,34 @@ public class BeavermapTests {
} }
} }
@Tag("A")
@DisplayName("Test buildings are ignored in dijkstra path")
@Test
@Order(10)
public void testDijkstraIgnoreBuildings() {
BeaverMapsGraph bmg = new BeaverMapsGraph(
"data/caltech/caltech.buildings.json",
"data/caltech/caltech.waypoints.json",
"data/caltech/caltech.roads.json");
JsonElement s = fromFile("data/caltech/caltech.paths_trace.json");
for (JsonElement b : s.getAsJsonArray()) {
JsonObject curr = b.getAsJsonObject();
Location start = bmg.getLocationByID(curr.get("start").getAsLong());
Location target = bmg.getLocationByID(curr.get("target").getAsLong());
IDeque<Location> actualPath = bmg.dijkstra(start, target);
if (actualPath == null) {
continue;
}
for (Location loc : actualPath){
if (loc.id == start.id || loc.id == target.id) {
continue;
}
ISet<Location> buildings = Reflection.getFieldValue(BeaverMapsGraph.class, "buildings", bmg);
assertFalse(buildings.contains(loc), "Location " + loc.id + " in path is a building");
}
}
}
@Tag("A") @Tag("A")
@DisplayName("Test Dijkstra") @DisplayName("Test Dijkstra")
...@@ -234,6 +307,7 @@ public class BeavermapTests { ...@@ -234,6 +307,7 @@ public class BeavermapTests {
"caltech/caltech.buildings.json, caltech/caltech.waypoints.json, caltech/caltech.roads.json, caltech/caltech.paths_trace.json", "caltech/caltech.buildings.json, caltech/caltech.waypoints.json, caltech/caltech.roads.json, caltech/caltech.paths_trace.json",
"pasadena/pasadena.buildings.json, pasadena/pasadena.waypoints.json, pasadena/pasadena.roads.json, pasadena/pasadena.paths_trace.json", "pasadena/pasadena.buildings.json, pasadena/pasadena.waypoints.json, pasadena/pasadena.roads.json, pasadena/pasadena.paths_trace.json",
}) })
@Order(11)
public void testDijkstraBeaverMap(String buildingsFile, String waypointsFile, String roadsFile, String traceFile) throws FileNotFoundException { public void testDijkstraBeaverMap(String buildingsFile, String waypointsFile, String roadsFile, String traceFile) throws FileNotFoundException {
BeaverMapsGraph bmg = new BeaverMapsGraph( BeaverMapsGraph bmg = new BeaverMapsGraph(
"data/" + buildingsFile, "data/" + waypointsFile, "data/" + roadsFile); "data/" + buildingsFile, "data/" + waypointsFile, "data/" + roadsFile);
...@@ -252,7 +326,12 @@ public class BeavermapTests { ...@@ -252,7 +326,12 @@ public class BeavermapTests {
IDeque<Location> actualPath = bmg.dijkstra(start, target); IDeque<Location> actualPath = bmg.dijkstra(start, target);
List<Long> actualPathIDs = new ArrayList<>(); List<Long> actualPathIDs = new ArrayList<>();
if (actualPath != null) {
if (expectedPathIDs.size() == 0) {
assertNull(actualPath, "Path does not exist from " + start.id + " to " + target.id + " but a path was found");
}
else {
assertNotNull(actualPath, "Path exists from " + start.id + " to " + target.id + " but a path was not found");
for (Location l : actualPath) { for (Location l : actualPath) {
actualPathIDs.add(l.id); actualPathIDs.add(l.id);
} }
...@@ -260,9 +339,6 @@ public class BeavermapTests { ...@@ -260,9 +339,6 @@ public class BeavermapTests {
MatcherAssert.assertThat(actualPathIDs, MatcherAssert.assertThat(actualPathIDs,
IsIterableContainingInOrder.contains(expectedPathIDs.toArray())); IsIterableContainingInOrder.contains(expectedPathIDs.toArray()));
} }
else if (expectedPathIDs.size() != 0) {
fail("Found path from " + start.id + " to " + target.id + " where there is none.");
}
} }
} }
......
...@@ -7,10 +7,7 @@ import edu.caltech.cs2.helpers.Reflection; ...@@ -7,10 +7,7 @@ import edu.caltech.cs2.helpers.Reflection;
import edu.caltech.cs2.interfaces.IDeque; import edu.caltech.cs2.interfaces.IDeque;
import edu.caltech.cs2.interfaces.IGraph; import edu.caltech.cs2.interfaces.IGraph;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.*;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.CsvSource;
...@@ -25,13 +22,14 @@ import java.util.Scanner; ...@@ -25,13 +22,14 @@ import java.util.Scanner;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
@Tag("A") @Tag("A")
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class DijkstraTest { public class DijkstraTest {
@Order(0) @Order(0)
@DisplayName("Loop path should be singleton") @DisplayName("Loop path should be singleton")
@Test @Test
public void dijkstraShortTripTest() { public void dijkstraShortTripTest() {
IDeque<Location> res = GraphMaker.transformToLocations(GraphMaker.graph4(10)).dijkstra(new Location(1), new Location(1)); IDeque<Location> res = GraphMaker.transformToLocations(GraphMaker.completeGraph(10)).dijkstra(new Location(1), new Location(1));
assertEquals(1, res.size(), "Path from a node to itself should only include the node once"); assertEquals(1, res.size(), "Path from a node to itself should only include the node once");
assertEquals(res.peek(), new Location(1), "Path from a node to itself should only include the node once"); assertEquals(res.peek(), new Location(1), "Path from a node to itself should only include the node once");
} }
...@@ -40,27 +38,27 @@ public class DijkstraTest { ...@@ -40,27 +38,27 @@ public class DijkstraTest {
@DisplayName("Disconnected graph should not have a path") @DisplayName("Disconnected graph should not have a path")
@Test @Test
public void dijkstraDisconnectedTest() { public void dijkstraDisconnectedTest() {
IDeque<Location> res = GraphMaker.transformToLocations(GraphMaker.graph5(10)).dijkstra(new Location(1), new Location(9)); IDeque<Location> res = GraphMaker.transformToLocations(GraphMaker.disjointCompleteGraphs(10)).dijkstra(new Location(1), new Location(9));
assertNull(res, "Disconnected graph should give null path"); assertNull(res, "Disconnected graph should give null path");
} }
@Order(2) @Order(2)
@ParameterizedTest @ParameterizedTest(name = "Graph: {0}, start: {1}, end: {2}, trace file: {3}")
@DisplayName("Tests correctness of Dijkstra implementation") @DisplayName("Tests correctness of Dijkstra implementation")
@CsvSource({ @CsvSource({
"graph1, 1, 3, simple_1_3", "simpleGraph, 1, 3, simple_1_3",
"graph2, 0, 1, line_0_1", "linearGraph, 0, 1, line_0_1",
"graph2, 0, 5, line_0_5", "linearGraph, 0, 5, line_0_5",
"graph2, 0, 20, line_0_20", "linearGraph, 0, 20, line_0_20",
"graph2, 0, 99, line_0_99", "linearGraph, 0, 99, line_0_99",
"graph2, 1, 0, line_1_0", "linearGraph, 1, 0, line_1_0",
"graph3, 0, 1, graph3_0_1", "tournamentGraph, 0, 1, graph3_0_1",
"graph3, 99, 0, graph3_99_0" "tournamentGraph, 99, 0, graph3_99_0"
}) })
public void dijkstraTestGraph(String graphName, int start, int end, String traceFile) public void dijkstraTestGraph(String graphName, int start, int end, String traceFile)
throws IllegalAccessException, InvocationTargetException, FileNotFoundException { throws IllegalAccessException, InvocationTargetException, FileNotFoundException {
BeaverMapsGraph bmg; BeaverMapsGraph bmg;
if (graphName.equals("graph1")) { if (graphName.equals("simpleGraph")) {
Method graphGen = Reflection.getMethod(GraphMaker.class, graphName); Method graphGen = Reflection.getMethod(GraphMaker.class, graphName);
bmg = GraphMaker.transformToLocations( bmg = GraphMaker.transformToLocations(
(IGraph<Integer, Integer>) graphGen.invoke(null)); (IGraph<Integer, Integer>) graphGen.invoke(null));
...@@ -77,10 +75,12 @@ public class DijkstraTest { ...@@ -77,10 +75,12 @@ public class DijkstraTest {
Scanner s = new Scanner(new File("data/dijkstra/" + traceFile)); Scanner s = new Scanner(new File("data/dijkstra/" + traceFile));
if (res == null) { String line = s.nextLine();
assertEquals(s.nextLine(), "null", "Path exists but was not found"); if (line.equals("null")) {
assertNull(res, "Path does not exist from " + start + " to " + end + " but a path was found");
} }
else { else {
assertNotNull(res, "Path exists from " + start + " to " + end + " but a path was not found");
for (Location l : res) { for (Location l : res) {
if (prev != null) { if (prev != null) {
pathLen += bmg.adjacent(prev.id, l.id); pathLen += bmg.adjacent(prev.id, l.id);
...@@ -88,7 +88,7 @@ public class DijkstraTest { ...@@ -88,7 +88,7 @@ public class DijkstraTest {
prev = l; prev = l;
} }
double expectedLen = s.nextDouble(); double expectedLen = Double.parseDouble(line);
assertEquals(expectedLen, pathLen, "Path lengths are not equivalent"); assertEquals(expectedLen, pathLen, "Path lengths are not equivalent");
} }
...@@ -97,7 +97,7 @@ public class DijkstraTest { ...@@ -97,7 +97,7 @@ public class DijkstraTest {
@Order(3) @Order(3)
@DisplayName("Tests Dijkstra on random graph and paths") @DisplayName("Tests Dijkstra on random graph and paths")
@Test @Test
public void dijkstraStressTest() throws FileNotFoundException{ public void dijkstraStressTest() throws FileNotFoundException {
final int num_tests = 1000; final int num_tests = 1000;
IGraph<Integer, Integer> refg = new Graph<Integer, Integer>(); IGraph<Integer, Integer> refg = new Graph<Integer, Integer>();
...@@ -119,6 +119,7 @@ public class DijkstraTest { ...@@ -119,6 +119,7 @@ public class DijkstraTest {
two = (two + 1) % num_vertices; two = (two + 1) % num_vertices;
int dice = r.nextInt(5); int dice = r.nextInt(5);
// TODO: y
if (dice <= 4) { if (dice <= 4) {
refg.addEdge(one, two, r.nextInt(100)); refg.addEdge(one, two, r.nextInt(100));
} else if (dice <= 5) { } else if (dice <= 5) {
...@@ -131,10 +132,12 @@ public class DijkstraTest { ...@@ -131,10 +132,12 @@ public class DijkstraTest {
BeaverMapsGraph bmg = GraphMaker.transformToLocations(refg); BeaverMapsGraph bmg = GraphMaker.transformToLocations(refg);
IDeque<Location> res = bmg.dijkstra(new Location(startvertex), new Location(endvertex)); IDeque<Location> res = bmg.dijkstra(new Location(startvertex), new Location(endvertex));
if (res == null) { String line = s.nextLine();
assertEquals(s.nextLine(), "null", "Path exists but was not found"); if (line.equals("null")) {
assertNull(res, "Path does not exist from " + startvertex + " to " + endvertex + " but a path was found");
} }
else { else {
assertNotNull(res, "Path exists from " + startvertex + " to " + endvertex + " but a path was not found");
double pathLen = 0; double pathLen = 0;
Location prev = null; Location prev = null;
for (Location l : res) { for (Location l : res) {
...@@ -144,9 +147,8 @@ public class DijkstraTest { ...@@ -144,9 +147,8 @@ public class DijkstraTest {
prev = l; prev = l;
} }
double expectedLen = s.nextDouble(); double expectedLen = Double.parseDouble(line);
s.nextLine(); assertEquals(expectedLen, pathLen, "Path is not the shortest path by length");
assertEquals(expectedLen, pathLen, "Path lengths are not equivalent");
} }
} }
} }
......
...@@ -23,8 +23,11 @@ public class GraphMaker { ...@@ -23,8 +23,11 @@ public class GraphMaker {
return ng; return ng;
} }
// A very simple graph. /**
public static IGraph<Integer, Integer> graph1() { * Generate a simple graph
* @return graph
*/
public static IGraph<Integer, Integer> simpleGraph() {
IGraph<Integer, Integer> g = new Graph<>(); IGraph<Integer, Integer> g = new Graph<>();
g.addVertex(1); g.addVertex(1);
g.addVertex(2); g.addVertex(2);
...@@ -35,8 +38,12 @@ public class GraphMaker { ...@@ -35,8 +38,12 @@ public class GraphMaker {
return g; return g;
} }
// A linear graph on n vertices. /**
public static IGraph<Integer, Integer> graph2(int n) { * Generate a directed linear graph
* @param n - number of vertices in the linear graph
* @return graph, with edges labelled with the source vertex
*/
public static IGraph<Integer, Integer> linearGraph(int n) {
IGraph<Integer, Integer> g = new Graph<>(); IGraph<Integer, Integer> g = new Graph<>();
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
assertTrue(g.addVertex(i), "Adding a new vertex should return true"); assertTrue(g.addVertex(i), "Adding a new vertex should return true");
...@@ -56,7 +63,7 @@ public class GraphMaker { ...@@ -56,7 +63,7 @@ public class GraphMaker {
} }
// Verify that a graph we're given looks reasonably like graph 2 // Verify that a graph we're given looks reasonably like graph 2
public static void graph2Test(IGraph<Integer, Integer> g, int n) { public static void verifyLinearGraph(IGraph<Integer, Integer> g, int n) {
vertexTest(g, n); vertexTest(g, n);
for (int i = 0; i < n - 1; i++) { for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - 1; j++) { for (int j = 0; j < n - 1; j++) {
...@@ -69,8 +76,12 @@ public class GraphMaker { ...@@ -69,8 +76,12 @@ public class GraphMaker {
} }
} }
// A tournament on n vertices; has an edge from i to j iff j<i /**
public static IGraph<Integer, Integer> graph3(int n) { * Construct a tournament graph on n vertices; has an edge from i to j iff j<i
* @param n - number of vertices
* @return graph
*/
public static IGraph<Integer, Integer> tournamentGraph(int n) {
IGraph<Integer, Integer> g = new Graph<>(); IGraph<Integer, Integer> g = new Graph<>();
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
assertTrue(g.addVertex(i), "Adding a new vertex should return true"); assertTrue(g.addVertex(i), "Adding a new vertex should return true");
...@@ -84,7 +95,7 @@ public class GraphMaker { ...@@ -84,7 +95,7 @@ public class GraphMaker {
} }
// Verify that a graph we're given looks reasonably like graph 3 // Verify that a graph we're given looks reasonably like graph 3
public static void graph3Test(IGraph<Integer, Integer> g, int n) { public static void verifyTournamentGraph(IGraph<Integer, Integer> g, int n) {
vertexTest(g, n); vertexTest(g, n);
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) { for (int j = 0; j < n; j++) {
...@@ -99,8 +110,12 @@ public class GraphMaker { ...@@ -99,8 +110,12 @@ public class GraphMaker {
} }
} }
// A complete graph on n vertices /**
public static IGraph<Integer, Integer> graph4(int n) { * Construct a complete graph on n vertices
* @param n - number of vertices
* @return graph
*/
public static IGraph<Integer, Integer> completeGraph(int n) {
IGraph<Integer, Integer> g = new Graph<>(); IGraph<Integer, Integer> g = new Graph<>();
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
assertTrue(g.addVertex(i), "Adding a new vertex should return true"); assertTrue(g.addVertex(i), "Adding a new vertex should return true");
...@@ -117,7 +132,7 @@ public class GraphMaker { ...@@ -117,7 +132,7 @@ public class GraphMaker {
} }
// Verify that a graph we're given looks reasonably like graph 4 // Verify that a graph we're given looks reasonably like graph 4
public static void graph4Test(IGraph<Integer, Integer> g, int n) { public static void verifyCompleteGraph(IGraph<Integer, Integer> g, int n) {
vertexTest(g, n); vertexTest(g, n);
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) { for (int j = 0; j < n; j++) {
...@@ -133,8 +148,12 @@ public class GraphMaker { ...@@ -133,8 +148,12 @@ public class GraphMaker {
} }
} }
// Two disjoint complete graphs. /**
public static IGraph<Integer, Integer> graph5(int n) { * Construct a graph that consists of 2 disjoint complete graphs, each with n vertices
* @param n - number of vertices in each complete component
* @return graph
*/
public static IGraph<Integer, Integer> disjointCompleteGraphs(int n) {
IGraph<Integer, Integer> g = new Graph<>(); IGraph<Integer, Integer> g = new Graph<>();
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
assertTrue(g.addVertex(i), "Adding a new vertex should return true"); assertTrue(g.addVertex(i), "Adding a new vertex should return true");
...@@ -162,7 +181,7 @@ public class GraphMaker { ...@@ -162,7 +181,7 @@ public class GraphMaker {
} }
// Verify that a graph we're given looks reasonably like graph 5 // Verify that a graph we're given looks reasonably like graph 5
public static void graph5Test(IGraph<Integer, Integer> g, int n) { public static void verifyDisjointCompleteGraphs(IGraph<Integer, Integer> g, int n) {
vertexTest(g, n); vertexTest(g, n);
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) { for (int j = 0; j < n; j++) {
...@@ -176,22 +195,4 @@ public class GraphMaker { ...@@ -176,22 +195,4 @@ public class GraphMaker {
} }
} }
} }
/* A graph on which Dijkstra's should fail on a search from
* vertex 0 to vertex 2. Optimal path is 0->3->2 (length 1), but Dijkstra's
* should give us 0->1->2 (length 2).
*/
public static IGraph<Integer, Integer> dijkstraFail() {
IGraph<Integer, Integer> g = new Graph<>();
g.addVertex(0);
g.addVertex(1);
g.addVertex(2);
g.addVertex(3);
g.addEdge(0, 1, 1);
g.addEdge(1, 2, 1);
g.addEdge(0, 3, 5);
g.addEdge(3, 2, -4);
return g;
}
} }
\ No newline at end of file
...@@ -10,27 +10,40 @@ import edu.caltech.cs2.helpers.Inspection; ...@@ -10,27 +10,40 @@ import edu.caltech.cs2.helpers.Inspection;
import edu.caltech.cs2.interfaces.IGraph; import edu.caltech.cs2.interfaces.IGraph;
import org.hamcrest.MatcherAssert; import org.hamcrest.MatcherAssert;
import org.hamcrest.collection.IsIterableContainingInAnyOrder; import org.hamcrest.collection.IsIterableContainingInAnyOrder;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.*;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
@Tag("C") @Tag("C")
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class GraphTests { public class GraphTests {
private static final String GRAPH_SOURCE = "src/edu/caltech/cs2/datastructures/Graph.java"; private static final String GRAPH_SOURCE = "src/edu/caltech/cs2/datastructures/Graph.java";
private static final int SIMPLE_OP_TIMEOUT_MS = 300; private static final int SIMPLE_OP_TIMEOUT_MS = 300;
private static final int MEDIUM_OP_TIMEOUT_MS = 500; private static final int MEDIUM_OP_TIMEOUT_MS = 500;
private static final int STRESS_OP_TIMEOUT_MS = 2200; private static final int STRESS_OP_TIMEOUT_MS = 2200;
@Order(0)
@DisplayName("Does not use or import disallowed classes") @DisplayName("Does not use or import disallowed classes")
@Test @Test
public void testForInvalidClasses() { public void testForInvalidClasses() {
List<String> graphDisallow = List.of("java\\.util\\.(?!Iterator)", "java\\.io", "java\\.lang\\.reflect"); List<String> graphDisallow = List.of("java\\.io", "java\\.lang\\.reflect");
Inspection.assertNoImportsOf(GRAPH_SOURCE, graphDisallow); Inspection.assertNoImportsOf(GRAPH_SOURCE, graphDisallow);
Inspection.assertNoUsageOf(GRAPH_SOURCE, graphDisallow); Inspection.assertNoUsageOf(GRAPH_SOURCE, graphDisallow);
} }
@Order(1)
@DisplayName("Does not use or import disallowed classes from java.util")
@Test
public void testForInvalidImportsJavaUtil() {
List<String> allowed = List.of("Iterator");
Inspection.assertNoImportsOfExcept(GRAPH_SOURCE, "java\\.util", allowed);
List<String> bannedUsages = List.of("java\\.util\\.(?!" + String.join("|", allowed) + ")");
Inspection.assertNoUsageOf(GRAPH_SOURCE, bannedUsages);
}
@Order(2)
@DisplayName("Test empty graph")
@Test @Test
public void emptyGraphTest() { public void emptyGraphTest() {
IGraph<String, String> g = new Graph<>(); IGraph<String, String> g = new Graph<>();
...@@ -39,9 +52,24 @@ public class GraphTests { ...@@ -39,9 +52,24 @@ public class GraphTests {
); );
} }
@Test
@Order(3)
@DisplayName("Test creating various graphs")
public void creationTest() {
assertTimeout(Duration.ofMillis(MEDIUM_OP_TIMEOUT_MS), () -> {
GraphMaker.simpleGraph();
GraphMaker.verifyLinearGraph(GraphMaker.linearGraph(100), 100);
GraphMaker.verifyTournamentGraph(GraphMaker.tournamentGraph(100), 100);
GraphMaker.verifyCompleteGraph(GraphMaker.completeGraph(100), 100);
GraphMaker.verifyDisjointCompleteGraphs(GraphMaker.disjointCompleteGraphs(100), 100);
});
}
/** /**
* Ensure that we can create a small graph with some edges. * Ensure that we can create a small graph with some edges.
*/ */
@Order(3)
@DisplayName("Test creating a small directed graph")
@Test @Test
public void secondCreateTest() { public void secondCreateTest() {
IGraph<String, Integer> g = new Graph<>(); IGraph<String, Integer> g = new Graph<>();
...@@ -69,7 +97,9 @@ public class GraphTests { ...@@ -69,7 +97,9 @@ public class GraphTests {
}); });
} }
@Order(4)
@Test @Test
@DisplayName("Adding an edge with both endpoints missing should fail")
public void addIllegalEdgeTest() { public void addIllegalEdgeTest() {
// Test adding edge where neither src, dest exists // Test adding edge where neither src, dest exists
IGraph<String, Integer> g = new Graph<>(); IGraph<String, Integer> g = new Graph<>();
...@@ -77,6 +107,8 @@ public class GraphTests { ...@@ -77,6 +107,8 @@ public class GraphTests {
} }
@Test @Test
@Order(4)
@DisplayName("Adding an edge with dest missing should fail")
public void addIllegalEdgeTest2() { public void addIllegalEdgeTest2() {
// Test adding edge where dest does not exist // Test adding edge where dest does not exist
IGraph<String, Integer> g = new Graph<>(); IGraph<String, Integer> g = new Graph<>();
...@@ -85,6 +117,8 @@ public class GraphTests { ...@@ -85,6 +117,8 @@ public class GraphTests {
} }
@Test @Test
@Order(4)
@DisplayName("Adding an edge with src missing should fail")
public void addIllegalEdgeTest3() { public void addIllegalEdgeTest3() {
// Test adding edge where src does not exist // Test adding edge where src does not exist
IGraph<String, Integer> g = new Graph<>(); IGraph<String, Integer> g = new Graph<>();
...@@ -93,6 +127,8 @@ public class GraphTests { ...@@ -93,6 +127,8 @@ public class GraphTests {
} }
@Test @Test
@Order(5)
@DisplayName("Test building a simple graph with edges and failures to add edges")
public void simpleAddTest() { public void simpleAddTest() {
assertTimeout(Duration.ofMillis(SIMPLE_OP_TIMEOUT_MS), () -> { assertTimeout(Duration.ofMillis(SIMPLE_OP_TIMEOUT_MS), () -> {
IGraph<String, Integer> g = new Graph<>(); IGraph<String, Integer> g = new Graph<>();
...@@ -125,6 +161,8 @@ public class GraphTests { ...@@ -125,6 +161,8 @@ public class GraphTests {
} }
@Test @Test
@Order(6)
@DisplayName("Test removing edges from the graph")
public void simpleRemoveTest() { public void simpleRemoveTest() {
assertTimeout(Duration.ofMillis(SIMPLE_OP_TIMEOUT_MS), () -> { assertTimeout(Duration.ofMillis(SIMPLE_OP_TIMEOUT_MS), () -> {
IGraph<String, Integer> g = new Graph<>(); IGraph<String, Integer> g = new Graph<>();
...@@ -139,33 +177,36 @@ public class GraphTests { ...@@ -139,33 +177,36 @@ public class GraphTests {
} }
@Test @Test
public void creationTest() { @Order(7)
assertTimeout(Duration.ofMillis(MEDIUM_OP_TIMEOUT_MS), () -> { @DisplayName("Test removing edges from the graph again")
GraphMaker.graph1(); public void stringEdgeTest() {
GraphMaker.graph2Test(GraphMaker.graph2(100), 100); assertTimeout(Duration.ofMillis(SIMPLE_OP_TIMEOUT_MS), () -> {
GraphMaker.graph3Test(GraphMaker.graph3(100), 100); IGraph<Integer, Integer> g = new Graph<>();
GraphMaker.graph4Test(GraphMaker.graph4(100), 100);
GraphMaker.graph5Test(GraphMaker.graph5(100), 100);
});
}
/* Make sure that they test .equals, rather than ==, to determine assertTrue(g.addVertex(1), "Should be able to add vertex");
* if an edge is already there. */ assertTrue(g.addVertex(2), "Should be able to add vertex");
@Test assertTrue(g.addVertex(3), "Should be able to add vertex");
public void equalsTest() { assertTrue(g.addEdge(1, 2, 0), "Should be able to add edge");
IGraph<Integer, Integer> g = new Graph<>(); assertTrue(g.addEdge(1, 3, 0), "Should be able to add edge");
// Actually prevent the JVM from caching these values. assertTrue(g.addEdge(2, 3, 0), "Should be able to add edge");
// This should be tested in the backing DSs anyway... assertNotNull(g.adjacent(1, 2), "Added edge should be present in graph");
Integer i1 = 1000; assertNotNull(g.adjacent(1, 3), "Added edge should be present in graph");
Integer i2 = 1000; assertNotNull(g.adjacent(2, 3), "Added edge should be present in graph");
assertTrue(g.addVertex(i1), "Attempting to add missing vertex returned false"); assertTrue(g.removeEdge(1, 2), "Should be able to remove an edge from the graph");
assertFalse(g.addVertex(i2), "Attempting to add present vertex returned true"); assertFalse(g.removeEdge(1, 2), "Should not be able to remove already-removed edge");
assertTrue(g.removeEdge(1, 3), "Should be able to remove an edge from the graph");
assertFalse(g.removeEdge(1, 3), "Should not be able to remove already-removed edge");
assertTrue(g.removeEdge(2, 3), "Should be able to remove an edge from the graph");
assertFalse(g.removeEdge(2, 3), "Should not be able to remove already-removed edge");
});
} }
@Test @Test
@Order(8)
@DisplayName("Test that all edges in a complete graph are present")
public void adjacentStressTest() { public void adjacentStressTest() {
assertTimeout(Duration.ofMillis(STRESS_OP_TIMEOUT_MS), () -> { assertTimeout(Duration.ofMillis(STRESS_OP_TIMEOUT_MS), () -> {
IGraph<Integer, Integer> g = GraphMaker.graph4(400); IGraph<Integer, Integer> g = GraphMaker.completeGraph(400);
for (int i = 0; i < 400; i++) for (int i = 0; i < 400; i++)
for (int j = 0; j < 400; j++) for (int j = 0; j < 400; j++)
if (i != j) if (i != j)
...@@ -174,39 +215,49 @@ public class GraphTests { ...@@ -174,39 +215,49 @@ public class GraphTests {
} }
@Test @Test
@Order(9)
@DisplayName("Test that directed edges in a tournament graph are correct")
public void testNeighbors() { public void testNeighbors() {
assertTimeout(Duration.ofMillis(SIMPLE_OP_TIMEOUT_MS), () -> { assertTimeout(Duration.ofMillis(SIMPLE_OP_TIMEOUT_MS), () -> {
IGraph<Integer, Integer> g = GraphMaker.graph3(10); IGraph<Integer, Integer> g = GraphMaker.tournamentGraph(10);
Set<Integer> vertices = new HashSet<Integer>(); Set<Integer> vertices = new HashSet<Integer>();
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
MatcherAssert.assertThat(g.neighbors(i), MatcherAssert.assertThat("neighbors", g.neighbors(i),
IsIterableContainingInAnyOrder.containsInAnyOrder(vertices.toArray())); IsIterableContainingInAnyOrder.containsInAnyOrder(vertices.toArray()));
// In a tournament graph, vertices have edges to smaller ones
vertices.add(i); vertices.add(i);
} }
}); });
} }
@Test @Test
public void stringEdgeTest() { @Order(10)
@DisplayName("Test adding undirected edges")
public void undirectedEdgeTest() {
assertTimeout(Duration.ofMillis(SIMPLE_OP_TIMEOUT_MS), () -> { assertTimeout(Duration.ofMillis(SIMPLE_OP_TIMEOUT_MS), () -> {
IGraph<Integer, Integer> g = new Graph<>(); IGraph<String, Integer> g = new Graph<>();
assertTrue(g.addVertex(1), "Should be able to add vertex"); assertTrue(g.addVertex("1"), "Should be able to add a vertex");
assertTrue(g.addVertex(2), "Should be able to add vertex"); assertEquals(1, g.vertices().size(), "Should have correct number of vertices");
assertTrue(g.addVertex(3), "Should be able to add vertex"); assertTrue(g.addVertex("2"), "Should be able to add a vertex");
assertTrue(g.addEdge(1, 2, 0), "Should be able to add edge"); assertEquals(2, g.vertices().size(), "Should have correct number of vertices");
assertTrue(g.addEdge(1, 3, 0), "Should be able to add edge"); assertTrue(g.addVertex("3"), "Should be able to add a vertex");
assertTrue(g.addEdge(2, 3, 0), "Should be able to add edge"); assertEquals(3, g.vertices().size(), "Should have correct number of vertices");
assertNotNull(g.adjacent(1, 2), "Added edge should be present in graph");
assertNotNull(g.adjacent(1, 3), "Added edge should be present in graph"); assertTrue(g.addUndirectedEdge("1", "2", 1), "Adding undirected edge when neither direction exists should return true");
assertNotNull(g.adjacent(2, 3), "Added edge should be present in graph"); assertFalse(g.addEdge("1", "2", 1), "Should not be able to add directed edge");
assertTrue(g.removeEdge(1, 2), "Should be able to remove an edge from the graph"); assertFalse(g.addEdge("2", "1", 1), "Should not be able to add directed edge");
assertFalse(g.removeEdge(1, 2), "Should not be able to remove already-removed edge");
assertTrue(g.removeEdge(1, 3), "Should be able to remove an edge from the graph"); assertTrue(g.addEdge("1", "3", 1), "Should be able to add new edge");
assertFalse(g.removeEdge(1, 3), "Should not be able to remove already-removed edge"); assertFalse(g.addUndirectedEdge("3", "1", 2), "Adding an undirected edge when one direction exists should return false");
assertTrue(g.removeEdge(2, 3), "Should be able to remove an edge from the graph"); assertEquals(g.adjacent("3", "1"), 2, "Adding an undirected edge when one direction exists should update existing edges");
assertFalse(g.removeEdge(2, 3), "Should not be able to remove already-removed edge"); assertEquals(g.adjacent("1", "3"), 2, "Adding an undirected edge when one direction exists should update existing edges");
assertTrue(g.addEdge("2", "3", 1), "Should be able to add new directed edge");
assertFalse(g.addUndirectedEdge("2", "3", 2), "Adding an undirected edge when one direction exists should return false");
assertEquals(g.adjacent("3", "2"), 2, "Adding an undirected edge when one direction exists should update existing edges");
assertEquals(g.adjacent("2", "3"), 2, "Adding an undirected edge when one direction exists should update existing edges");
}); });
} }
} }
\ No newline at end of file
...@@ -13,6 +13,7 @@ import java.util.*; ...@@ -13,6 +13,7 @@ import java.util.*;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
@Tag("C") @Tag("C")
@DisplayName("Test graph of shared appearances in Marvel comic books (MarvelTests)")
public class MarvelTests { public class MarvelTests {
private static Graph<String, Integer> MARVEL_GRAPH; private static Graph<String, Integer> MARVEL_GRAPH;
private static final int COMMON_TIMEOUT_MS = 1500; private static final int COMMON_TIMEOUT_MS = 1500;
...@@ -52,6 +53,7 @@ public class MarvelTests { ...@@ -52,6 +53,7 @@ public class MarvelTests {
@Test @Test
@Order(0) @Order(0)
@DisplayName("Test that characters with no co-appearances do not have neighbors - neighbor isEmpty")
public void testSingletons() { public void testSingletons() {
List<String> singletons = new ArrayList<>(); List<String> singletons = new ArrayList<>();
for (String c : MARVEL_GRAPH.vertices()) { for (String c : MARVEL_GRAPH.vertices()) {
...@@ -64,6 +66,7 @@ public class MarvelTests { ...@@ -64,6 +66,7 @@ public class MarvelTests {
@Test @Test
@Order(1) @Order(1)
@DisplayName("Look for characters that appear with many other characters - neighbor size")
public void testPopular() { public void testPopular() {
List<String> populars = new ArrayList<>(); List<String> populars = new ArrayList<>();
for (String c : MARVEL_GRAPH.vertices()) { for (String c : MARVEL_GRAPH.vertices()) {
...@@ -76,6 +79,7 @@ public class MarvelTests { ...@@ -76,6 +79,7 @@ public class MarvelTests {
@Test @Test
@Order(2) @Order(2)
@DisplayName("Test that there are no loops (self-edges) in the graph")
public void testNoLoops() { public void testNoLoops() {
for (String c : MARVEL_GRAPH.vertices()) { for (String c : MARVEL_GRAPH.vertices()) {
assertNull(MARVEL_GRAPH.adjacent(c, c), assertNull(MARVEL_GRAPH.adjacent(c, c),
...@@ -87,6 +91,7 @@ public class MarvelTests { ...@@ -87,6 +91,7 @@ public class MarvelTests {
// THIS TEST MUST BE RUN LAST - IT HAS SIDE EFFECTS ON THE STATIC GRAPH // THIS TEST MUST BE RUN LAST - IT HAS SIDE EFFECTS ON THE STATIC GRAPH
@Test @Test
@Order(3) @Order(3)
@DisplayName("Look for co-appearances that appear in many issues together - edge weights")
public void testCommon() { public void testCommon() {
Assertions.assertTimeout(Duration.ofMillis(COMMON_TIMEOUT_MS), this::runCommon); Assertions.assertTimeout(Duration.ofMillis(COMMON_TIMEOUT_MS), this::runCommon);
} }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment