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

Commit d86b382

Browse files
Add comprehensive tests for GraphAsciiRenderer
Cover empty/single/multi-node graphs, Unicode and ASCII mode rendering, degree annotations, legend display/hide, highlight by name and degree threshold, configuration validation (width/height/iterations bounds), adjacency list output, degree histogram, file export, edge type distribution in legend, minimal iterations, and large graph stability. 28 test cases covering all public methods and configuration options.
1 parent df36e83 commit d86b382

1 file changed

Lines changed: 347 additions & 0 deletions

File tree

Lines changed: 347 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,347 @@
1+
package gvisual;
2+
3+
import edu.uci.ics.jung.graph.Graph;
4+
import edu.uci.ics.jung.graph.UndirectedSparseGraph;
5+
import org.junit.Before;
6+
import org.junit.Test;
7+
8+
import java.io.File;
9+
import java.io.IOException;
10+
import java.nio.file.Files;
11+
12+
import static org.junit.Assert.*;
13+
14+
/**
15+
* Unit tests for {@link GraphAsciiRenderer}.
16+
* Covers: empty graphs, single-node, multi-node rendering, Unicode mode,
17+
* degree display, adjacency list, degree histogram, highlight features,
18+
* configuration validation, and file export.
19+
*/
20+
public class GraphAsciiRendererTest {
21+
22+
private Graph<String, edge> emptyGraph;
23+
private Graph<String, edge> singleNodeGraph;
24+
private Graph<String, edge> triangleGraph;
25+
private Graph<String, edge> starGraph;
26+
27+
@Before
28+
public void setUp() {
29+
emptyGraph = new UndirectedSparseGraph<>();
30+
31+
singleNodeGraph = new UndirectedSparseGraph<>();
32+
singleNodeGraph.addVertex("A");
33+
34+
// Triangle: A-B-C-A
35+
triangleGraph = new UndirectedSparseGraph<>();
36+
triangleGraph.addVertex("A");
37+
triangleGraph.addVertex("B");
38+
triangleGraph.addVertex("C");
39+
edge e1 = new edge("f", "A", "B");
40+
e1.setWeight(1.0f);
41+
edge e2 = new edge("c", "B", "C");
42+
e2.setWeight(2.0f);
43+
edge e3 = new edge("f", "C", "A");
44+
e3.setWeight(1.5f);
45+
triangleGraph.addEdge(e1, "A", "B");
46+
triangleGraph.addEdge(e2, "B", "C");
47+
triangleGraph.addEdge(e3, "C", "A");
48+
49+
// Star: center connected to 4 leaves
50+
starGraph = new UndirectedSparseGraph<>();
51+
starGraph.addVertex("Center");
52+
for (int i = 1; i <= 4; i++) {
53+
String leaf = "Leaf" + i;
54+
starGraph.addVertex(leaf);
55+
edge e = new edge("f", "Center", leaf);
56+
e.setWeight(1.0f);
57+
starGraph.addEdge(e, "Center", leaf);
58+
}
59+
}
60+
61+
// --- Constructor validation ---
62+
63+
@Test(expected = IllegalArgumentException.class)
64+
public void constructorRejectsNullGraph() {
65+
new GraphAsciiRenderer(null);
66+
}
67+
68+
// --- Empty graph ---
69+
70+
@Test
71+
public void renderEmptyGraphReturnsPlaceholder() {
72+
GraphAsciiRenderer renderer = new GraphAsciiRenderer(emptyGraph);
73+
assertEquals("(empty graph)", renderer.render());
74+
}
75+
76+
@Test
77+
public void renderEmptyGraphHistogramReturnsPlaceholder() {
78+
GraphAsciiRenderer renderer = new GraphAsciiRenderer(emptyGraph);
79+
assertEquals("(empty graph)", renderer.renderDegreeHistogram());
80+
}
81+
82+
// --- Single node ---
83+
84+
@Test
85+
public void renderSingleNodeContainsLabel() {
86+
GraphAsciiRenderer renderer = new GraphAsciiRenderer(singleNodeGraph);
87+
renderer.setWidth(40);
88+
renderer.setHeight(15);
89+
String output = renderer.render();
90+
assertTrue("Should contain vertex label 'A'", output.contains("A"));
91+
assertTrue("Should contain node count", output.contains("1 nodes"));
92+
assertTrue("Should contain edge count", output.contains("0 edges"));
93+
}
94+
95+
// --- Triangle graph basic render ---
96+
97+
@Test
98+
public void renderTriangleContainsAllVertices() {
99+
GraphAsciiRenderer renderer = new GraphAsciiRenderer(triangleGraph);
100+
renderer.setWidth(60);
101+
renderer.setHeight(20);
102+
String output = renderer.render();
103+
assertTrue(output.contains("A"));
104+
assertTrue(output.contains("B"));
105+
assertTrue(output.contains("C"));
106+
assertTrue(output.contains("3 nodes"));
107+
assertTrue(output.contains("3 edges"));
108+
}
109+
110+
// --- Unicode mode ---
111+
112+
@Test
113+
public void unicodeModeUsesUnicodeBorders() {
114+
GraphAsciiRenderer renderer = new GraphAsciiRenderer(triangleGraph);
115+
renderer.setWidth(60);
116+
renderer.setHeight(20);
117+
renderer.setUnicode(true);
118+
String output = renderer.render();
119+
assertTrue("Unicode mode should use ═ borders", output.contains("═"));
120+
assertTrue("Unicode mode should use ║ borders", output.contains("║"));
121+
}
122+
123+
@Test
124+
public void asciiModeUsesAsciiBorders() {
125+
GraphAsciiRenderer renderer = new GraphAsciiRenderer(triangleGraph);
126+
renderer.setWidth(60);
127+
renderer.setHeight(20);
128+
renderer.setUnicode(false);
129+
String output = renderer.render();
130+
assertTrue("ASCII mode should use = borders", output.contains("="));
131+
assertTrue("ASCII mode should use | borders", output.contains("|"));
132+
}
133+
134+
// --- Show degree ---
135+
136+
@Test
137+
public void showDegreeIncludesDegreeAnnotations() {
138+
GraphAsciiRenderer renderer = new GraphAsciiRenderer(triangleGraph);
139+
renderer.setWidth(80);
140+
renderer.setHeight(25);
141+
renderer.setShowDegree(true);
142+
String output = renderer.render();
143+
// Each node in triangle has degree 2
144+
assertTrue("Should show degree annotation (2)", output.contains("(2)"));
145+
}
146+
147+
// --- Legend ---
148+
149+
@Test
150+
public void legendShowsDegreeStats() {
151+
GraphAsciiRenderer renderer = new GraphAsciiRenderer(starGraph);
152+
renderer.setShowLegend(true);
153+
String output = renderer.render();
154+
assertTrue("Legend should show min degree", output.contains("min="));
155+
assertTrue("Legend should show max degree", output.contains("max="));
156+
assertTrue("Legend should show avg degree", output.contains("avg="));
157+
}
158+
159+
@Test
160+
public void legendCanBeDisabled() {
161+
GraphAsciiRenderer renderer = new GraphAsciiRenderer(triangleGraph);
162+
renderer.setShowLegend(false);
163+
String output = renderer.render();
164+
assertFalse("No legend expected", output.contains("Graph:"));
165+
assertFalse("No degree stats expected", output.contains("Degree:"));
166+
}
167+
168+
// --- Highlight ---
169+
170+
@Test
171+
public void highlightNodeByName() {
172+
GraphAsciiRenderer renderer = new GraphAsciiRenderer(triangleGraph);
173+
renderer.setWidth(60);
174+
renderer.setHeight(20);
175+
renderer.addHighlightNode("A");
176+
String output = renderer.render();
177+
// With highlight nodes, legend should mention highlighted
178+
assertTrue(output.contains("highlighted"));
179+
}
180+
181+
@Test
182+
public void highlightByDegreeThreshold() {
183+
GraphAsciiRenderer renderer = new GraphAsciiRenderer(starGraph);
184+
renderer.setWidth(80);
185+
renderer.setHeight(25);
186+
renderer.setHighlightDegreeThreshold(3);
187+
String output = renderer.render();
188+
// Center has degree 4, should be highlighted
189+
assertTrue(output.contains("highlighted"));
190+
}
191+
192+
// --- Configuration validation ---
193+
194+
@Test(expected = IllegalArgumentException.class)
195+
public void setWidthRejectsTooSmall() {
196+
GraphAsciiRenderer renderer = new GraphAsciiRenderer(triangleGraph);
197+
renderer.setWidth(19);
198+
}
199+
200+
@Test(expected = IllegalArgumentException.class)
201+
public void setHeightRejectsTooSmall() {
202+
GraphAsciiRenderer renderer = new GraphAsciiRenderer(triangleGraph);
203+
renderer.setHeight(9);
204+
}
205+
206+
@Test(expected = IllegalArgumentException.class)
207+
public void setLayoutIterationsRejectsZero() {
208+
GraphAsciiRenderer renderer = new GraphAsciiRenderer(triangleGraph);
209+
renderer.setLayoutIterations(0);
210+
}
211+
212+
@Test
213+
public void setWidthAcceptsBoundary() {
214+
GraphAsciiRenderer renderer = new GraphAsciiRenderer(triangleGraph);
215+
renderer.setWidth(20);
216+
renderer.setHeight(10);
217+
// Should not throw, and render should work
218+
String output = renderer.render();
219+
assertNotNull(output);
220+
}
221+
222+
// --- Adjacency list ---
223+
224+
@Test
225+
public void adjacencyListContainsAllVertices() {
226+
GraphAsciiRenderer renderer = new GraphAsciiRenderer(triangleGraph);
227+
String output = renderer.renderAdjacencyList();
228+
assertTrue(output.contains("A"));
229+
assertTrue(output.contains("B"));
230+
assertTrue(output.contains("C"));
231+
assertTrue(output.contains("3 nodes"));
232+
assertTrue(output.contains("3 edges"));
233+
}
234+
235+
@Test
236+
public void adjacencyListShowsDegreeWhenEnabled() {
237+
GraphAsciiRenderer renderer = new GraphAsciiRenderer(triangleGraph);
238+
renderer.setShowDegree(true);
239+
String output = renderer.renderAdjacencyList();
240+
assertTrue("Should show degree [2]", output.contains("[2]"));
241+
}
242+
243+
@Test
244+
public void adjacencyListUnicodeMode() {
245+
GraphAsciiRenderer renderer = new GraphAsciiRenderer(triangleGraph);
246+
renderer.setUnicode(true);
247+
String output = renderer.renderAdjacencyList();
248+
assertTrue("Unicode mode uses ●", output.contains("●"));
249+
assertTrue("Unicode mode uses ├──", output.contains("├──"));
250+
}
251+
252+
// --- Degree histogram ---
253+
254+
@Test
255+
public void degreeHistogramTriangle() {
256+
GraphAsciiRenderer renderer = new GraphAsciiRenderer(triangleGraph);
257+
String output = renderer.renderDegreeHistogram();
258+
assertTrue(output.contains("Degree Distribution"));
259+
// All 3 nodes have degree 2, so should show degree 2 with count 3
260+
assertTrue(output.contains("3"));
261+
}
262+
263+
@Test
264+
public void degreeHistogramStarShowsMultipleDegrees() {
265+
GraphAsciiRenderer renderer = new GraphAsciiRenderer(starGraph);
266+
String output = renderer.renderDegreeHistogram();
267+
// Center has degree 4, leaves have degree 1
268+
assertTrue("Should show degree 1 count", output.contains("4")); // 4 leaves
269+
assertTrue("Should show degree 4 count", output.contains("1")); // 1 center
270+
}
271+
272+
@Test
273+
public void degreeHistogramUnicodeUsesBlockChars() {
274+
GraphAsciiRenderer renderer = new GraphAsciiRenderer(triangleGraph);
275+
renderer.setUnicode(true);
276+
String output = renderer.renderDegreeHistogram();
277+
assertTrue("Unicode histogram uses █", output.contains("█"));
278+
assertTrue("Unicode histogram uses ═", output.contains("═"));
279+
}
280+
281+
// --- File export ---
282+
283+
@Test
284+
public void exportToFileCreatesReadableFile() throws IOException {
285+
GraphAsciiRenderer renderer = new GraphAsciiRenderer(triangleGraph);
286+
renderer.setWidth(60);
287+
renderer.setHeight(20);
288+
File tempFile = File.createTempFile("graph-ascii-test", ".txt");
289+
tempFile.deleteOnExit();
290+
try {
291+
renderer.exportToFile(tempFile.getAbsolutePath());
292+
String content = new String(Files.readAllBytes(tempFile.toPath()), "UTF-8");
293+
assertTrue(content.contains("A"));
294+
assertTrue(content.contains("3 nodes"));
295+
assertTrue(content.length() > 100);
296+
} finally {
297+
tempFile.delete();
298+
}
299+
}
300+
301+
// --- Edge type distribution in legend ---
302+
303+
@Test
304+
public void legendShowsEdgeTypeDistribution() {
305+
GraphAsciiRenderer renderer = new GraphAsciiRenderer(triangleGraph);
306+
renderer.setShowLegend(true);
307+
String output = renderer.render();
308+
// Triangle has f and c edge types
309+
assertTrue("Legend should show edge types", output.contains("Edge types:"));
310+
assertTrue("Should show 'f' type", output.contains("f="));
311+
assertTrue("Should show 'c' type", output.contains("c="));
312+
}
313+
314+
// --- Layout iterations ---
315+
316+
@Test
317+
public void minimalLayoutIterationsStillRenders() {
318+
GraphAsciiRenderer renderer = new GraphAsciiRenderer(triangleGraph);
319+
renderer.setLayoutIterations(1);
320+
renderer.setWidth(40);
321+
renderer.setHeight(15);
322+
String output = renderer.render();
323+
assertNotNull(output);
324+
assertTrue(output.contains("A"));
325+
}
326+
327+
// --- Large graph doesn't crash ---
328+
329+
@Test
330+
public void largeGraphRendersWithoutError() {
331+
Graph<String, edge> large = new UndirectedSparseGraph<>();
332+
for (int i = 0; i < 50; i++) {
333+
large.addVertex("N" + i);
334+
}
335+
for (int i = 0; i < 49; i++) {
336+
edge e = new edge("f", "N" + i, "N" + (i + 1));
337+
e.setWeight(1.0f);
338+
large.addEdge(e, "N" + i, "N" + (i + 1));
339+
}
340+
GraphAsciiRenderer renderer = new GraphAsciiRenderer(large);
341+
renderer.setLayoutIterations(10); // fast
342+
String output = renderer.render();
343+
assertNotNull(output);
344+
assertTrue(output.contains("50 nodes"));
345+
assertTrue(output.contains("49 edges"));
346+
}
347+
}

0 commit comments

Comments
 (0)