Skip to content
This repository was archived by the owner on Jun 18, 2026. It is now read-only.

Commit a40f2f1

Browse files
test+ci: add QuadTree/EdgeType tests and harden CI build
Tests: - gvisual.QuadTreeTest: 6 tests covering Barnes-Hut quadtree build, insertion, repulsion forces, and finite-force guarantee for nearby bodies (MIN_DIST clamp). - gvisual.EdgeTypeTest: 16 tests covering enum codes, color/label metadata, fromCode lookup (case-insensitive + null/unknown), and cluster id derivation. All 22 new tests pass locally. CI workflow: - Exclude *Test.java from production javac (some test files live under src/ next to the code they exercise and depend on JUnit, which broke the prod compile). - Add -encoding UTF-8 to all javac invocations (box-drawing chars in comments break Windows default encoding). - Add new test classes to the JUnitCore runner. Drive-by fixes for pre-existing breakage: - Main.java: add missing edu.uci.ics.jung.visualization.VisualizationViewer import. - TimelineMetricsRecorder.java: fix typo'd lowercase 'edge' type references to the actual Edge class (8 references). - ShortestPathFinderTest.java: same typo fix. - GraphVoronoiPartitioner.java, PerfectGraphAnalyzer.java: add missing java.util.stream.Collectors import. - EulerianPathAnalyzer.java: switch ArrayDeque to LinkedList to match the declared map value type.
1 parent 3aa376b commit a40f2f1

9 files changed

Lines changed: 314 additions & 42 deletions

File tree

.github/workflows/ci.yml

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ jobs:
1010
build-and-test:
1111
runs-on: ubuntu-latest
1212
strategy:
13+
fail-fast: false
1314
matrix:
1415
java-version: [11, 17]
1516

@@ -34,20 +35,32 @@ jobs:
3435
- name: Compile source
3536
working-directory: Gvisual
3637
run: |
38+
set -e
3739
mkdir -p build/classes
38-
find src -name '*.java' > sources.txt
39-
javac -source 8 -target 8 \
40-
-cp "$(find lib -name '*.jar' -not -path 'lib/test/*' | tr '\n' ':')" \
40+
# Production sources only: anything under src/ that is NOT a *Test.java
41+
# (some test files are checked in under src/ alongside production code).
42+
# We still want them on the test classpath later, but they must not
43+
# be compiled with javac without junit on the path.
44+
find src -name '*.java' ! -name '*Test.java' > sources.txt
45+
MAIN_CP="$(find lib -name '*.jar' -not -path 'lib/test/*' | tr '\n' ':')"
46+
javac -encoding UTF-8 \
47+
-source 8 -target 8 \
48+
-cp "$MAIN_CP" \
4149
-d build/classes \
4250
@sources.txt
4351
4452
- name: Compile tests
4553
working-directory: Gvisual
4654
run: |
55+
set -e
4756
mkdir -p build/test/classes
48-
find test -name '*.java' > test-sources.txt
49-
javac -source 8 -target 8 \
50-
-cp "build/classes:$(find lib -name '*.jar' | tr '\n' ':')" \
57+
# All *Test.java files, wherever they live (test/ tree plus a handful
58+
# that sit under src/ next to the code they exercise).
59+
find src test -name '*Test.java' > test-sources.txt
60+
TEST_CP="build/classes:$(find lib -name '*.jar' | tr '\n' ':')"
61+
javac -encoding UTF-8 \
62+
-source 8 -target 8 \
63+
-cp "$TEST_CP" \
5164
-d build/test/classes \
5265
@test-sources.txt
5366
@@ -58,6 +71,8 @@ jobs:
5871
org.junit.runner.JUnitCore \
5972
app.UtilMethodsTest \
6073
gvisual.EdgeTest \
74+
gvisual.EdgeTypeTest \
75+
gvisual.QuadTreeTest \
6176
gvisual.GraphStatsTest \
6277
gvisual.ShortestPathFinderTest \
6378
gvisual.GraphMLExporterTest \

Gvisual/src/gvisual/EulerianPathAnalyzer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ public EulerianPathResult findEulerianPath() {
171171
// Build adjacency with Edge tracking
172172
Map<String, LinkedList<EdgeEntry>> adj = new LinkedHashMap<String, LinkedList<EdgeEntry>>();
173173
for (String v : graph.getVertices()) {
174-
adj.put(v, new ArrayDeque<EdgeEntry>());
174+
adj.put(v, new LinkedList<EdgeEntry>());
175175
}
176176

177177
Set<Edge> allEdges = new HashSet<Edge>();

Gvisual/src/gvisual/GraphVoronoiPartitioner.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import edu.uci.ics.jung.graph.Graph;
44
import java.util.*;
5+
import java.util.stream.Collectors;
56

67
/**
78
* Computes <b>Voronoi-like partitions</b> on a graph: given a set of seed

Gvisual/src/gvisual/Main.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import edu.uci.ics.jung.algorithms.layout.StaticLayout;
77
import edu.uci.ics.jung.graph.Graph;
88
import edu.uci.ics.jung.graph.UndirectedSparseGraph;
9+
import edu.uci.ics.jung.visualization.VisualizationViewer;
910
import java.awt.BorderLayout;
1011
import java.awt.Color;
1112
import java.awt.Dimension;

Gvisual/src/gvisual/PerfectGraphAnalyzer.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import edu.uci.ics.jung.graph.Graph;
44
import java.util.*;
5+
import java.util.stream.Collectors;
56

67
/**
78
* Perfect Graph Analyzer — determines whether a graph is perfect and provides

Gvisual/src/gvisual/TimelineMetricsRecorder.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -268,17 +268,17 @@ public void setPhaseThreshold(double threshold) {
268268
/**
269269
* Records metrics for the current state of the graph at this timeline step.
270270
*/
271-
public void recordStep(String label, Graph<String, edge> graph,
272-
List<edge> friendEdges, List<edge> fsEdges,
273-
List<edge> classmateEdges, List<edge> strangerEdges,
274-
List<edge> studyGEdges) {
271+
public void recordStep(String label, Graph<String, Edge> graph,
272+
List<Edge> friendEdges, List<Edge> fsEdges,
273+
List<Edge> classmateEdges, List<Edge> strangerEdges,
274+
List<Edge> studyGEdges) {
275275
if (graph == null) {
276276
throw new IllegalArgumentException("Graph must not be null");
277277
}
278278

279279
Set<String> currentNodes = new HashSet<>(graph.getVertices());
280280
Set<String> currentEdgeKeys = new HashSet<>();
281-
for (edge e : graph.getEdges()) {
281+
for (Edge e : graph.getEdges()) {
282282
currentEdgeKeys.add(edgeKey(e));
283283
}
284284

@@ -304,7 +304,7 @@ public void recordStep(String label, Graph<String, edge> graph,
304304
}
305305

306306
double totalWeight = 0;
307-
for (edge e : graph.getEdges()) {
307+
for (Edge e : graph.getEdges()) {
308308
totalWeight += e.getWeight();
309309
}
310310
double avgWt = edgeCount > 0 ? totalWeight / edgeCount : 0;
@@ -475,22 +475,22 @@ private static double computeDensity(int nodes, int edges) {
475475
return (2.0 * edges) / (nodes * (nodes - 1));
476476
}
477477

478-
private static String edgeKey(edge e) {
478+
private static String edgeKey(Edge e) {
479479
String v1 = e.getVertex1();
480480
String v2 = e.getVertex2();
481481
if (v1.compareTo(v2) > 0) { String tmp = v1; v1 = v2; v2 = tmp; }
482482
return v1 + "|" + v2 + "|" + e.getType();
483483
}
484484

485-
private static int countInGraph(Graph<String, edge> graph, List<edge> edges) {
485+
private static int countInGraph(Graph<String, Edge> graph, List<Edge> edges) {
486486
int count = 0;
487-
for (edge e : edges) {
487+
for (Edge e : edges) {
488488
if (graph.containsEdge(e)) count++;
489489
}
490490
return count;
491491
}
492492

493-
private static int countComponents(Graph<String, edge> graph) {
493+
private static int countComponents(Graph<String, Edge> graph) {
494494
Set<String> visited = new HashSet<>();
495495
int components = 0;
496496
for (String v : graph.getVertices()) {

Gvisual/src/test/gvisual/ShortestPathFinderTest.java

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,18 @@ public class ShortestPathFinderTest {
1717

1818
// ── Helpers ─────────────────────────────────────────────────────
1919

20-
private static edge addEdge(Graph<String, edge> g, String v1, String v2) {
20+
private static Edge addEdge(Graph<String, Edge> g, String v1, String v2) {
2121
g.addVertex(v1);
2222
g.addVertex(v2);
23-
edge e = new edge("f", v1, v2);
23+
Edge e = new Edge("f", v1, v2);
2424
g.addEdge(e, v1, v2);
2525
return e;
2626
}
2727

28-
private static edge addWeightedEdge(Graph<String, edge> g, String v1, String v2, float w) {
28+
private static Edge addWeightedEdge(Graph<String, Edge> g, String v1, String v2, float w) {
2929
g.addVertex(v1);
3030
g.addVertex(v2);
31-
edge e = new edge("f", v1, v2);
31+
Edge e = new Edge("f", v1, v2);
3232
e.setWeight(w);
3333
g.addEdge(e, v1, v2);
3434
return e;
@@ -45,7 +45,7 @@ public void constructor_nullGraph_throws() {
4545

4646
@Test
4747
public void byHops_sameVertex_returnsZeroHopPath() {
48-
Graph<String, edge> g = new UndirectedSparseGraph<>();
48+
Graph<String, Edge> g = new UndirectedSparseGraph<>();
4949
g.addVertex("A");
5050
ShortestPathFinder spf = new ShortestPathFinder(g);
5151

@@ -59,7 +59,7 @@ public void byHops_sameVertex_returnsZeroHopPath() {
5959

6060
@Test
6161
public void byHops_directNeighbor_returns1Hop() {
62-
Graph<String, edge> g = new UndirectedSparseGraph<>();
62+
Graph<String, Edge> g = new UndirectedSparseGraph<>();
6363
addEdge(g, "A", "B");
6464
ShortestPathFinder spf = new ShortestPathFinder(g);
6565

@@ -73,7 +73,7 @@ public void byHops_directNeighbor_returns1Hop() {
7373
public void byHops_choosesFewestHops_notLowestWeight() {
7474
// A --1-- B --1-- C (2 hops)
7575
// A --10-- C (1 hop, heavier)
76-
Graph<String, edge> g = new UndirectedSparseGraph<>();
76+
Graph<String, Edge> g = new UndirectedSparseGraph<>();
7777
addWeightedEdge(g, "A", "B", 1f);
7878
addWeightedEdge(g, "B", "C", 1f);
7979
addWeightedEdge(g, "A", "C", 10f);
@@ -87,7 +87,7 @@ public void byHops_choosesFewestHops_notLowestWeight() {
8787

8888
@Test
8989
public void byHops_disconnectedVertices_returnsNull() {
90-
Graph<String, edge> g = new UndirectedSparseGraph<>();
90+
Graph<String, Edge> g = new UndirectedSparseGraph<>();
9191
g.addVertex("A");
9292
g.addVertex("B");
9393
ShortestPathFinder spf = new ShortestPathFinder(g);
@@ -97,7 +97,7 @@ public void byHops_disconnectedVertices_returnsNull() {
9797

9898
@Test
9999
public void byHops_linearChain_returnsCorrectPath() {
100-
Graph<String, edge> g = new UndirectedSparseGraph<>();
100+
Graph<String, Edge> g = new UndirectedSparseGraph<>();
101101
addEdge(g, "A", "B");
102102
addEdge(g, "B", "C");
103103
addEdge(g, "C", "D");
@@ -112,14 +112,14 @@ public void byHops_linearChain_returnsCorrectPath() {
112112

113113
@Test(expected = IllegalArgumentException.class)
114114
public void byHops_nullSource_throws() {
115-
Graph<String, edge> g = new UndirectedSparseGraph<>();
115+
Graph<String, Edge> g = new UndirectedSparseGraph<>();
116116
g.addVertex("A");
117117
new ShortestPathFinder(g).findShortestByHops(null, "A");
118118
}
119119

120120
@Test(expected = IllegalArgumentException.class)
121121
public void byHops_vertexNotInGraph_throws() {
122-
Graph<String, edge> g = new UndirectedSparseGraph<>();
122+
Graph<String, Edge> g = new UndirectedSparseGraph<>();
123123
g.addVertex("A");
124124
new ShortestPathFinder(g).findShortestByHops("A", "Z");
125125
}
@@ -128,7 +128,7 @@ public void byHops_vertexNotInGraph_throws() {
128128

129129
@Test
130130
public void byWeight_sameVertex_returnsZeroWeight() {
131-
Graph<String, edge> g = new UndirectedSparseGraph<>();
131+
Graph<String, Edge> g = new UndirectedSparseGraph<>();
132132
g.addVertex("X");
133133
ShortestPathFinder spf = new ShortestPathFinder(g);
134134

@@ -142,7 +142,7 @@ public void byWeight_sameVertex_returnsZeroWeight() {
142142
public void byWeight_prefersLighterPath() {
143143
// A --1-- B --1-- C (weight 2, 2 hops)
144144
// A --10-- C (weight 10, 1 hop)
145-
Graph<String, edge> g = new UndirectedSparseGraph<>();
145+
Graph<String, Edge> g = new UndirectedSparseGraph<>();
146146
addWeightedEdge(g, "A", "B", 1f);
147147
addWeightedEdge(g, "B", "C", 1f);
148148
addWeightedEdge(g, "A", "C", 10f);
@@ -157,7 +157,7 @@ public void byWeight_prefersLighterPath() {
157157
@Test
158158
public void byWeight_zeroWeightEdges_treatedAsWeight1() {
159159
// Zero-weight edges should be normalized to 1.0
160-
Graph<String, edge> g = new UndirectedSparseGraph<>();
160+
Graph<String, Edge> g = new UndirectedSparseGraph<>();
161161
addEdge(g, "A", "B"); // weight 0 (default) -> treated as 1.0
162162
addEdge(g, "B", "C"); // weight 0 -> treated as 1.0
163163
ShortestPathFinder spf = new ShortestPathFinder(g);
@@ -169,7 +169,7 @@ public void byWeight_zeroWeightEdges_treatedAsWeight1() {
169169

170170
@Test
171171
public void byWeight_disconnected_returnsNull() {
172-
Graph<String, edge> g = new UndirectedSparseGraph<>();
172+
Graph<String, Edge> g = new UndirectedSparseGraph<>();
173173
g.addVertex("A");
174174
g.addVertex("B");
175175
ShortestPathFinder spf = new ShortestPathFinder(g);
@@ -179,7 +179,7 @@ public void byWeight_disconnected_returnsNull() {
179179

180180
@Test(expected = IllegalArgumentException.class)
181181
public void byWeight_negativeEdge_throws() {
182-
Graph<String, edge> g = new UndirectedSparseGraph<>();
182+
Graph<String, Edge> g = new UndirectedSparseGraph<>();
183183
addWeightedEdge(g, "A", "B", -5f);
184184
new ShortestPathFinder(g).findShortestByWeight("A", "B");
185185
}
@@ -188,7 +188,7 @@ public void byWeight_negativeEdge_throws() {
188188

189189
@Test
190190
public void reachable_isolatedVertex_returnsSelf() {
191-
Graph<String, edge> g = new UndirectedSparseGraph<>();
191+
Graph<String, Edge> g = new UndirectedSparseGraph<>();
192192
g.addVertex("A");
193193
ShortestPathFinder spf = new ShortestPathFinder(g);
194194

@@ -198,7 +198,7 @@ public void reachable_isolatedVertex_returnsSelf() {
198198

199199
@Test
200200
public void reachable_connectedComponent_returnsAll() {
201-
Graph<String, edge> g = new UndirectedSparseGraph<>();
201+
Graph<String, Edge> g = new UndirectedSparseGraph<>();
202202
addEdge(g, "A", "B");
203203
addEdge(g, "B", "C");
204204
g.addVertex("D"); // isolated
@@ -213,29 +213,29 @@ public void reachable_connectedComponent_returnsAll() {
213213

214214
@Test
215215
public void areConnected_sameVertex_returnsTrue() {
216-
Graph<String, edge> g = new UndirectedSparseGraph<>();
216+
Graph<String, Edge> g = new UndirectedSparseGraph<>();
217217
g.addVertex("A");
218218
assertTrue(new ShortestPathFinder(g).areConnected("A", "A"));
219219
}
220220

221221
@Test
222222
public void areConnected_directNeighbors_returnsTrue() {
223-
Graph<String, edge> g = new UndirectedSparseGraph<>();
223+
Graph<String, Edge> g = new UndirectedSparseGraph<>();
224224
addEdge(g, "A", "B");
225225
assertTrue(new ShortestPathFinder(g).areConnected("A", "B"));
226226
}
227227

228228
@Test
229229
public void areConnected_disconnected_returnsFalse() {
230-
Graph<String, edge> g = new UndirectedSparseGraph<>();
230+
Graph<String, Edge> g = new UndirectedSparseGraph<>();
231231
g.addVertex("A");
232232
g.addVertex("B");
233233
assertFalse(new ShortestPathFinder(g).areConnected("A", "B"));
234234
}
235235

236236
@Test
237237
public void areConnected_transitivelyConnected_returnsTrue() {
238-
Graph<String, edge> g = new UndirectedSparseGraph<>();
238+
Graph<String, Edge> g = new UndirectedSparseGraph<>();
239239
addEdge(g, "A", "B");
240240
addEdge(g, "B", "C");
241241
addEdge(g, "C", "D");
@@ -246,7 +246,7 @@ public void areConnected_transitivelyConnected_returnsTrue() {
246246

247247
@Test
248248
public void pathResult_toString_formatsCorrectly() {
249-
Graph<String, edge> g = new UndirectedSparseGraph<>();
249+
Graph<String, Edge> g = new UndirectedSparseGraph<>();
250250
addWeightedEdge(g, "A", "B", 3f);
251251
addWeightedEdge(g, "B", "C", 4f);
252252
ShortestPathFinder spf = new ShortestPathFinder(g);
@@ -264,7 +264,7 @@ public void pathResult_toString_formatsCorrectly() {
264264

265265
@Test
266266
public void byHops_directedGraph_respectsEdgeDirection() {
267-
Graph<String, edge> g = new DirectedSparseGraph<>();
267+
Graph<String, Edge> g = new DirectedSparseGraph<>();
268268
addEdge(g, "A", "B"); // A -> B only
269269
addEdge(g, "B", "C"); // B -> C only
270270
ShortestPathFinder spf = new ShortestPathFinder(g);
@@ -292,7 +292,7 @@ public void byWeight_diamondGraph_findsOptimalPath() {
292292
// 2 1
293293
// \ /
294294
// C
295-
Graph<String, edge> g = new UndirectedSparseGraph<>();
295+
Graph<String, Edge> g = new UndirectedSparseGraph<>();
296296
addWeightedEdge(g, "A", "B", 1f);
297297
addWeightedEdge(g, "A", "C", 2f);
298298
addWeightedEdge(g, "B", "D", 5f);

0 commit comments

Comments
 (0)