java-topology/tests/support/GraphFactory.java
russell@unturf.com 0a580b313d undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
2026-03-26 17:11:57 -04:00

105 lines
3.6 KiB
Java

package support;
import java.util.ArrayList;
import java.util.List;
/**
* Factory methods for constructing test graph topologies.
*
* Each method returns a list of nodes ready to pass to GraphUtils.tarjan().
* Topologies are chosen to stress the O(V²) defect in different ways.
*/
public class GraphFactory {
/**
* Path graph with a single back edge forming one large SCC:
* N0 → N1 → N2 → … → N(v-1) → N0
*
* Worst case for stack.contains(): when the back edge N(v-1)→N0 is
* followed, the stack holds all V nodes. contains() must scan the
* entire stack before finding N0 at the bottom.
*
* Total comparisons (defective): V*(V+1)/2
* Total comparisons (fixed): V (1 per edge)
*/
public static List<AbstractTestNode> pathWithBackEdge(int v) {
List<AbstractTestNode> nodes = new ArrayList<>();
for (int i = 0; i < v; i++) {
nodes.add(new AbstractTestNode("N" + i));
}
// forward edges
for (int i = 0; i < v - 1; i++) {
nodes.get(i).addDep(nodes.get(i + 1));
}
// single back edge
nodes.get(v - 1).addDep(nodes.get(0));
return nodes;
}
/**
* Linear path (no cycles — no back edges):
* N0 → N1 → N2 → … → N(v-1)
*
* Every node is its own SCC. The stack at each step holds only nodes
* whose SCC has not yet been closed. stack.contains() still fires for
* each edge, scanning an average of V/2 entries.
*
* Total comparisons (defective): O(V²) in the worst path pattern
* Total comparisons (fixed): O(E) = O(V)
*/
public static List<AbstractTestNode> linearPath(int v) {
List<AbstractTestNode> nodes = new ArrayList<>();
for (int i = 0; i < v; i++) {
nodes.add(new AbstractTestNode("N" + i));
}
for (int i = 0; i < v - 1; i++) {
nodes.get(i).addDep(nodes.get(i + 1));
}
return nodes;
}
/**
* k independent cycles, each of length cycleLen:
* [N0→N1→…→N(len-1)→N0], [N(len)→…], …
*
* Produces k SCCs of size cycleLen. Good for verifying correctness
* of SCC counting and node grouping.
*/
public static List<AbstractTestNode> independentCycles(int k, int cycleLen) {
List<AbstractTestNode> all = new ArrayList<>();
for (int c = 0; c < k; c++) {
List<AbstractTestNode> cycle = new ArrayList<>();
for (int i = 0; i < cycleLen; i++) {
cycle.add(new AbstractTestNode("C" + c + "_N" + i));
}
for (int i = 0; i < cycleLen - 1; i++) {
cycle.get(i).addDep(cycle.get(i + 1));
}
cycle.get(cycleLen - 1).addDep(cycle.get(0));
all.addAll(cycle);
}
return all;
}
/**
* Diamond graph with a cross edge:
* N0 → N1, N0 → N2, N1 → N3, N2 → N3
* Plus an optional back edge N3 → N0.
*
* Used to verify that SCC detection handles multiple paths correctly.
*/
public static List<AbstractTestNode> diamond(boolean withBackEdge) {
AbstractTestNode n0 = new AbstractTestNode("N0");
AbstractTestNode n1 = new AbstractTestNode("N1");
AbstractTestNode n2 = new AbstractTestNode("N2");
AbstractTestNode n3 = new AbstractTestNode("N3");
n0.addDep(n1);
n0.addDep(n2);
n1.addDep(n3);
n2.addDep(n3);
if (withBackEdge) {
n3.addDep(n0);
}
return List.of(n0, n1, n2, n3);
}
}