Commit 0a900ed5 authored by Adam Blank's avatar Adam Blank
Browse files

Initial commit

parents
No related merge requests found
Pipeline #28879 canceled with stage
Showing with 455 additions and 0 deletions
+455 -0
package edu.caltech.cs2.project08;
import java.time.Duration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import edu.caltech.cs2.datastructures.Graph;
import edu.caltech.cs2.helpers.Inspection;
import edu.caltech.cs2.interfaces.IGraph;
import org.hamcrest.MatcherAssert;
import org.hamcrest.collection.IsIterableContainingInAnyOrder;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
@Tag("C")
public class GraphTests {
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 MEDIUM_OP_TIMEOUT_MS = 500;
private static final int STRESS_OP_TIMEOUT_MS = 2200;
@DisplayName("Does not use or import disallowed classes")
@Test
public void testForInvalidClasses() {
List<String> graphDisallow = List.of("java\\.util\\.(?!Iterator)", "java\\.io", "java\\.lang\\.reflect");
Inspection.assertNoImportsOf(GRAPH_SOURCE, graphDisallow);
Inspection.assertNoUsageOf(GRAPH_SOURCE, graphDisallow);
}
@Test
public void emptyGraphTest() {
IGraph<String, String> g = new Graph<>();
assertTimeout(Duration.ofMillis(SIMPLE_OP_TIMEOUT_MS), () ->
assertEquals(0, g.vertices().size(), "Empty graph should have no vertices")
);
}
/**
* Ensure that we can create a small graph with some edges.
*/
@Test
public void secondCreateTest() {
IGraph<String, Integer> g = new Graph<>();
assertTimeout(Duration.ofMillis(SIMPLE_OP_TIMEOUT_MS), () -> {
assertTrue(g.addVertex("0"), "Should be able to add a vertex");
assertTrue(g.addVertex("1"), "Should be able to add a vertex");
assertTrue(g.addVertex("2"), "Should be able to add a vertex");
assertTrue(g.addVertex("3"), "Should be able to add a vertex");
assertTrue(g.addEdge("0", "1", 2), "Should be able to add an edge");
assertTrue(g.addEdge("2", "1", 4), "Should be able to add an edge");
assertTrue(g.addEdge("1", "2", 3), "Should be able to add an edge");
assertTrue(g.addEdge("1", "3", 1), "Should be able to add an edge");
assertTrue(g.addEdge("3", "1", -1), "Should be able to add an edge");
assertEquals(g.vertices().size(), 4, "Graph should have correct number of vertices");
assertEquals(2, (int) g.adjacent("0", "1"), "Edges added should all be present");
assertEquals(4, (int) g.adjacent("2", "1"), "Edges added should all be present");
assertEquals(3, (int) g.adjacent("1", "2"), "Edges added should all be present");
assertEquals(1, (int) g.adjacent("1", "3"), "Edges added should all be present");
assertEquals((int) g.adjacent("3", "1"), -1, "Edges added should all be present");
});
}
@Test
public void addIllegalEdgeTest() {
// Test adding edge where neither src, dest exists
IGraph<String, Integer> g = new Graph<>();
assertThrows(IllegalArgumentException.class, () -> g.addEdge("", "", 1));
}
@Test
public void addIllegalEdgeTest2() {
// Test adding edge where dest does not exist
IGraph<String, Integer> g = new Graph<>();
assertTrue(g.addVertex("0"));
assertThrows(IllegalArgumentException.class, () -> g.addEdge("0", "", 1));
}
@Test
public void addIllegalEdgeTest3() {
// Test adding edge where src does not exist
IGraph<String, Integer> g = new Graph<>();
assertTrue(g.addVertex("0"));
assertThrows(IllegalArgumentException.class, () -> g.addEdge("", "0", 1));
}
@Test
public void simpleAddTest() {
assertTimeout(Duration.ofMillis(SIMPLE_OP_TIMEOUT_MS), () -> {
IGraph<String, Integer> g = new Graph<>();
assertTrue(g.addVertex("1"), "Should be able to add a vertex");
assertEquals(1, g.vertices().size(), "Should have correct number of vertices");
assertTrue(g.addVertex("2"), "Should be able to add a vertex");
assertEquals(2, g.vertices().size(), "Should have correct number of vertices");
assertTrue(g.addVertex("3"), "Should be able to add a vertex");
assertEquals(3, g.vertices().size(), "Should have correct number of vertices");
assertTrue(g.addEdge("1", "2", 5), "Should be able to add new edge");
assertFalse(g.addEdge("1", "2", 5), "Should not be able to add an existing edge");
assertFalse(g.addEdge("1", "2", 3), "Should not be able to add an existing edge");
assertTrue(g.addEdge("2", "1", 5), "Should be able to add new edge");
assertFalse(g.addEdge("2", "1", 5), "Should not be able to add an existing edge");
assertFalse(g.addEdge("2", "1", 3), "Should not be able to add an existing edge");
assertTrue(g.addEdge("1", "3", 5), "Should be able to add new edge");
assertFalse(g.addEdge("1", "3", 5), "Should not be able to add an existing edge");
assertFalse(g.addEdge("1", "3", 3), "Should not be able to add an existing edge");
assertNotNull(g.adjacent("1", "2"), "Edge should exist in the graph");
assertNotNull(g.adjacent("2", "1"), "Edge should exist in the graph");
assertNotNull(g.adjacent("1", "3"), "Edge should exist in the graph");
assertNull(g.adjacent("2", "3"), "Edge should not exist in graph");
assertNull(g.adjacent("3", "1"), "Edge should not exist in graph");
});
}
@Test
public void simpleRemoveTest() {
assertTimeout(Duration.ofMillis(SIMPLE_OP_TIMEOUT_MS), () -> {
IGraph<String, Integer> g = new Graph<>();
assertTrue(g.addVertex("1"), "Should be able to add vertex");
assertTrue(g.addVertex("2"), "Should be able to add vertex");
assertTrue(g.addEdge("1", "2", 5), "Should be able to add edge");
assertEquals(5, (int) g.adjacent("1", "2"), "Added edge should be present in graph");
assertTrue(g.removeEdge("1", "2"), "Should be able to remove an edge from the graph");
assertFalse(g.removeEdge("1", "2"), "Should not be able to remove already-removed edge");
});
}
@Test
public void creationTest() {
assertTimeout(Duration.ofMillis(MEDIUM_OP_TIMEOUT_MS), () -> {
GraphMaker.graph1();
GraphMaker.graph2Test(GraphMaker.graph2(100), 100);
GraphMaker.graph3Test(GraphMaker.graph3(100), 100);
GraphMaker.graph4Test(GraphMaker.graph4(100), 100);
GraphMaker.graph5Test(GraphMaker.graph5(100), 100);
});
}
/* Make sure that they test .equals, rather than ==, to determine
* if an edge is already there. */
@Test
public void equalsTest() {
IGraph<Integer, Integer> g = new Graph<>();
// Actually prevent the JVM from caching these values.
// This should be tested in the backing DSs anyway...
Integer i1 = 1000;
Integer i2 = 1000;
assertTrue(g.addVertex(i1), "Attempting to add missing vertex returned false");
assertFalse(g.addVertex(i2), "Attempting to add present vertex returned true");
}
@Test
public void adjacentStressTest() {
assertTimeout(Duration.ofMillis(STRESS_OP_TIMEOUT_MS), () -> {
IGraph<Integer, Integer> g = GraphMaker.graph4(400);
for (int i = 0; i < 400; i++)
for (int j = 0; j < 400; j++)
if (i != j)
assertNotNull(g.adjacent(i, j), "Edge should be present in the graph");
});
}
@Test
public void testNeighbors() {
assertTimeout(Duration.ofMillis(SIMPLE_OP_TIMEOUT_MS), () -> {
IGraph<Integer, Integer> g = GraphMaker.graph3(10);
Set<Integer> vertices = new HashSet<Integer>();
for (int i = 0; i < 10; i++) {
MatcherAssert.assertThat(g.neighbors(i),
IsIterableContainingInAnyOrder.containsInAnyOrder(vertices.toArray()));
vertices.add(i);
}
});
}
@Test
public void stringEdgeTest() {
assertTimeout(Duration.ofMillis(SIMPLE_OP_TIMEOUT_MS), () -> {
IGraph<Integer, Integer> g = new Graph<>();
assertTrue(g.addVertex(1), "Should be able to add vertex");
assertTrue(g.addVertex(2), "Should be able to add vertex");
assertTrue(g.addVertex(3), "Should be able to add vertex");
assertTrue(g.addEdge(1, 2, 0), "Should be able to add edge");
assertTrue(g.addEdge(1, 3, 0), "Should be able to add edge");
assertTrue(g.addEdge(2, 3, 0), "Should be able to add edge");
assertNotNull(g.adjacent(1, 2), "Added edge should be present in graph");
assertNotNull(g.adjacent(1, 3), "Added edge should be present in graph");
assertNotNull(g.adjacent(2, 3), "Added edge should be present in graph");
assertTrue(g.removeEdge(1, 2), "Should be able to remove an edge from the graph");
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");
});
}
}
\ No newline at end of file
package edu.caltech.cs2.project08;
import java.io.*;
import java.util.*;
/**
* Parser utility to load the Marvel Comics dataset.
*/
public class MarvelParser {
/**
* Reads the Marvel Universe dataset.
* Each line of the input file contains a character name and a comic
* book the character appeared in, separated by a tab character
*
* @param filename the file that will be read
* @param characters list in which all character names will be stored;
* typically empty when the routine is called
* @param books map from titles of comic books to characters that
* appear in them; typically empty when the routine is called
* @requires file is well-formed, with each line containing exactly two
* quote-delimited tokens separated by a tab, or else starting with
* a # symbol to indicate a comment line.
* @modifies characters, books
* @effects fills characters with a list of all unique character names
* @effects fills books with a map from each comic book to all characters
* appearing in it
*/
public static void parseData(String filename, Set<String> characters,
Map<String, List<String>> books) throws Exception {
// Why does this method accept the Collections to be filled as
// parameters rather than making them a return value? To allows us to
// "return" two different Collections. If only one or neither Collection
// needs to be returned to the caller, feel free to rewrite this method
// without the parameters. Generally this is better style.
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(filename));
// Construct the collections of characters and books, one
// <character, book> pair at a time.
String inputLine;
while ((inputLine = reader.readLine()) != null) {
// Ignore comment lines.
if (inputLine.startsWith("#")) {
continue;
}
// Parse the data, stripping out quotation marks and throwing
// an exception for malformed lines.
inputLine = inputLine.replace("\"", "");
String[] tokens = inputLine.split("\t");
if (tokens.length != 2) {
throw new Exception("Line should contain exactly one tab: " + inputLine);
}
String character = tokens[0];
String book = tokens[1];
// Add the parsed data to the character and book collections.
characters.add(character);
if (!books.containsKey(book)) {
books.put(book, new ArrayList<String>());
}
books.get(book).add(character);
}
} catch (IOException e) {
System.err.println(e.toString());
e.printStackTrace(System.err);
} finally {
if (reader != null) {
reader.close();
}
}
}
}
package edu.caltech.cs2.project08;
import edu.caltech.cs2.datastructures.Graph;
import org.hamcrest.MatcherAssert;
import org.hamcrest.collection.IsIterableContainingInAnyOrder;
import org.junit.jupiter.api.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.time.Duration;
import java.util.*;
import static org.junit.jupiter.api.Assertions.*;
@Tag("C")
public class MarvelTests {
private static Graph<String, Integer> MARVEL_GRAPH;
private static final int COMMON_TIMEOUT_MS = 1500;
@BeforeAll
public static void populateMarvelGraph() {
MARVEL_GRAPH = new Graph<>();
Set<String> characters = new HashSet<>();
Map<String, List<String>> books = new HashMap<>();
try {
MarvelParser.parseData("data/marvel/marvel.tsv", characters, books);
} catch (Exception e) {
fail("Could not read input file.");
}
for (String c : characters) {
MARVEL_GRAPH.addVertex(c);
}
for (String book : books.keySet()) {
// This will add symmetric edges automatically - no need to add undirected edge
for (String c1 : books.get(book)) {
for (String c2 : books.get(book)) {
if (!c1.equals(c2)) {
Integer num = MARVEL_GRAPH.adjacent(c1, c2);
if (num == null) {
num = 0;
}
MARVEL_GRAPH.addEdge(c1, c2, num + 1);
}
}
}
}
}
@Test
@Order(0)
public void testSingletons() {
List<String> singletons = new ArrayList<>();
for (String c : MARVEL_GRAPH.vertices()) {
if (MARVEL_GRAPH.neighbors(c).isEmpty()) {
singletons.add(c);
}
}
loadAndMatch(singletons, "data/marvel/singletons_output");
}
@Test
@Order(1)
public void testPopular() {
List<String> populars = new ArrayList<>();
for (String c : MARVEL_GRAPH.vertices()) {
if (MARVEL_GRAPH.neighbors(c).size() > 20) {
populars.add(c);
}
}
loadAndMatch(populars, "data/marvel/popular_output");
}
@Test
@Order(2)
public void testNoLoops() {
for (String c : MARVEL_GRAPH.vertices()) {
assertNull(MARVEL_GRAPH.adjacent(c, c),
"There is a loop (self-edge) in the graph, when there should be none.");
}
}
// THIS TEST MUST BE RUN LAST - IT HAS SIDE EFFECTS ON THE STATIC GRAPH
@Test
@Order(3)
public void testCommon() {
Assertions.assertTimeout(Duration.ofMillis(COMMON_TIMEOUT_MS), this::runCommon);
}
public void runCommon() {
List<String> common = new ArrayList<>();
for (String c1 : MARVEL_GRAPH.vertices()) {
Set<String> toRemove = new HashSet<>();
for (String c2 : MARVEL_GRAPH.neighbors(c1)) {
Integer edge = MARVEL_GRAPH.adjacent(c1, c2);
Integer symedge = MARVEL_GRAPH.adjacent(c2, c1);
assertNotNull(edge, "An existing edge is null");
assertNotNull(symedge, "An existing edge is not symmetric in the graph");
toRemove.add(c2);
// Sort here to ignore dependency on hashing order
if (edge > 80) {
if (c1.compareTo(c2) < 0) {
common.add(c1.strip() + " --" + edge + "-- " + c2.strip());
}
else {
common.add(c2.strip() + " --" + edge + "-- " + c1.strip());
}
}
}
// Process removals separately to prevent modifying neighbors while iterating on it
for (String c2 : toRemove) {
Integer symedge = MARVEL_GRAPH.adjacent(c2, c1);
MARVEL_GRAPH.removeEdge(c1, c2);
assertNull(MARVEL_GRAPH.adjacent(c1, c2), "An edge is not null after removal");
assertEquals(symedge, MARVEL_GRAPH.adjacent(c2, c1), "An edge is null after its symmetric edge is removed");
MARVEL_GRAPH.removeEdge(c2, c1);
assertNull(MARVEL_GRAPH.adjacent(c2, c1), "An edge is not null after removal");
}
assertTrue(MARVEL_GRAPH.neighbors(c1).isEmpty(), "After removing all of a vertex's neighbors, neighbors() is non-empty");
}
loadAndMatch(common, "data/marvel/common_output");
}
private void loadAndMatch(List<String> actual, String filename) {
List<String> expected = new ArrayList<>();
Scanner fr = null;
try {
fr = new Scanner(new File(filename));
} catch (FileNotFoundException e) {
fail("Could not open test results file.");
}
while (fr.hasNextLine()) {
String l = fr.nextLine();
// is edge - sort to remove dependence on directionality for correctness
if (l.contains("--")) {
String[] spl = l.split("--");
String v1 = spl[0].strip();
String v2 = spl[2].strip();
String e = spl[1];
if (v1.compareTo(v2) < 0) {
l = v1 + " --" + e + "-- " + v2;
}
else {
l = v2 + " --" + e + "-- " + v1;
}
}
expected.add(l);
}
MatcherAssert.assertThat(actual,
IsIterableContainingInAnyOrder.containsInAnyOrder(expected.toArray()));
}
}
\ No newline at end of file
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