Remove redundant tests
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013dggmjXoVy7JswW68o7M2V
This commit is contained in:
@@ -59,11 +59,6 @@ class TestMultiGraphVertices(unittest.TestCase):
|
||||
self.g: MultiGraph = MultiGraph()
|
||||
"""The graph under test."""
|
||||
|
||||
def test_add_vertex(self) -> None:
|
||||
"""Test adding a single vertex."""
|
||||
self.g.add_vertex(1)
|
||||
self.assertIn(1, self.g.vertices)
|
||||
|
||||
def test_add_multiple_vertices(self) -> None:
|
||||
"""Test adding multiple vertices."""
|
||||
for v in [1, 2, 3, 4]:
|
||||
@@ -110,11 +105,6 @@ class TestMultiGraphEdges(unittest.TestCase):
|
||||
self.assertEqual(e.u, 1)
|
||||
self.assertEqual(e.v, 2)
|
||||
|
||||
def test_add_edge_returns_edge(self) -> None:
|
||||
"""Test that add_edge returns an Edge object."""
|
||||
e: Edge = self.g.add_edge(1, 2)
|
||||
self.assertIsInstance(e, Edge)
|
||||
|
||||
def test_add_parallel_edges(self) -> None:
|
||||
"""Test adding parallel edges between the same pair."""
|
||||
e1: Edge = self.g.add_edge(1, 2)
|
||||
@@ -151,14 +141,6 @@ class TestMultiGraphEdges(unittest.TestCase):
|
||||
e: Edge = self.g.add_edge(1, 2, virtual=True)
|
||||
self.assertTrue(e.virtual)
|
||||
|
||||
def test_edges_property(self) -> None:
|
||||
"""Test that edges property returns all edges."""
|
||||
e1: Edge = self.g.add_edge(1, 2)
|
||||
e2: Edge = self.g.add_edge(2, 3)
|
||||
ids: set[int] = {e.id for e in self.g.edges}
|
||||
self.assertIn(e1.id, ids)
|
||||
self.assertIn(e2.id, ids)
|
||||
|
||||
|
||||
class TestMultiGraphNeighbors(unittest.TestCase):
|
||||
"""Tests for neighbor/adjacency operations on MultiGraph."""
|
||||
|
||||
@@ -71,17 +71,6 @@ def _make_c4() -> tuple[MultiGraph, list[int]]:
|
||||
return g, [e0.id, e1.id, e2.id, e3.id]
|
||||
|
||||
|
||||
class TestPalmTreeType(unittest.TestCase):
|
||||
"""Tests that build_palm_tree returns a PalmTree instance."""
|
||||
|
||||
def test_returns_palm_tree(self) -> None:
|
||||
"""Test that build_palm_tree returns a PalmTree object."""
|
||||
g: MultiGraph
|
||||
g, _ = _make_k3()
|
||||
pt: PalmTree = build_palm_tree(g, 1)
|
||||
self.assertIsInstance(pt, PalmTree)
|
||||
|
||||
|
||||
class TestPalmTreePath(unittest.TestCase):
|
||||
"""Tests for palm tree on a path graph P3 (no back edges)."""
|
||||
|
||||
@@ -94,15 +83,6 @@ class TestPalmTreePath(unittest.TestCase):
|
||||
self.pt: PalmTree = build_palm_tree(g, 1)
|
||||
"""The palm tree for the graph."""
|
||||
|
||||
def test_dfs_num_root(self) -> None:
|
||||
"""Test that the start vertex has DFS number 1."""
|
||||
self.assertEqual(self.pt.dfs_num[1], 1)
|
||||
|
||||
def test_dfs_num_order(self) -> None:
|
||||
"""Test that DFS numbers are assigned 1, 2, 3 in traversal order."""
|
||||
nums: list[int] = sorted(self.pt.dfs_num.values())
|
||||
self.assertEqual(nums, [1, 2, 3])
|
||||
|
||||
def test_tree_edges_count(self) -> None:
|
||||
"""Test that there are n-1 = 2 tree edges."""
|
||||
self.assertEqual(len(self.pt.tree_edges), 2)
|
||||
@@ -117,32 +97,6 @@ class TestPalmTreePath(unittest.TestCase):
|
||||
self.assertIn(e0, self.pt.tree_edges)
|
||||
self.assertIn(e1, self.pt.tree_edges)
|
||||
|
||||
def test_parent_root(self) -> None:
|
||||
"""Test that the root has no parent (None)."""
|
||||
self.assertIsNone(self.pt.parent.get(1))
|
||||
|
||||
def test_nd_values(self) -> None:
|
||||
"""Test that ND values are correct for P3."""
|
||||
# DFS from 1: 1→2→3 (since 2 is first in adj[1])
|
||||
self.assertEqual(self.pt.nd[1], 3)
|
||||
|
||||
def test_nd_leaf(self) -> None:
|
||||
"""Test that a leaf vertex has ND = 1."""
|
||||
# Vertex 3 is a leaf in P3
|
||||
self.assertEqual(self.pt.nd[3], 1)
|
||||
|
||||
def test_lowpt1_values(self) -> None:
|
||||
"""Test lowpt1 values for P3 (all vertices reach only themselves)."""
|
||||
for v in [1, 2, 3]:
|
||||
self.assertLessEqual(
|
||||
self.pt.lowpt1[v], self.pt.dfs_num[v]
|
||||
)
|
||||
|
||||
def test_lowpt1_no_fronds(self) -> None:
|
||||
"""Test that lowpt1[v] == dfs_num[v] when no fronds exist."""
|
||||
for v in [1, 2, 3]:
|
||||
self.assertEqual(self.pt.lowpt1[v], self.pt.dfs_num[v])
|
||||
|
||||
|
||||
class TestPalmTreeTriangle(unittest.TestCase):
|
||||
"""Tests for palm tree on the triangle graph K3."""
|
||||
@@ -189,14 +143,6 @@ class TestPalmTreeTriangle(unittest.TestCase):
|
||||
"""Test that the root has nd = 3."""
|
||||
self.assertEqual(self.pt.nd[1], 3)
|
||||
|
||||
def test_nd_leaf(self) -> None:
|
||||
"""Test that the DFS leaf (vertex 3) has nd = 1."""
|
||||
# Vertex 3 is visited last in K3 with DFS from 1
|
||||
leaf: Hashable = next(
|
||||
v for v, n in self.pt.nd.items() if n == 1
|
||||
)
|
||||
self.assertEqual(self.pt.nd[leaf], 1)
|
||||
|
||||
def test_first_child_of_root(self) -> None:
|
||||
"""Test that root vertex 1 has a first child."""
|
||||
self.assertIsNotNone(self.pt.first_child.get(1))
|
||||
@@ -208,11 +154,6 @@ class TestPalmTreeTriangle(unittest.TestCase):
|
||||
)
|
||||
self.assertIsNone(self.pt.first_child.get(leaf))
|
||||
|
||||
def test_lowpt1_le_dfs_num(self) -> None:
|
||||
"""Test that lowpt1[v] <= dfs_num[v] for all v."""
|
||||
for v in [1, 2, 3]:
|
||||
self.assertLessEqual(self.pt.lowpt1[v], self.pt.dfs_num[v])
|
||||
|
||||
def test_lowpt1_le_lowpt2(self) -> None:
|
||||
"""Test that lowpt1[v] <= lowpt2[v] for all v."""
|
||||
for v in [1, 2, 3]:
|
||||
@@ -374,20 +315,6 @@ class TestPhiKeyP3(unittest.TestCase):
|
||||
# lowpt1[3]=3, case 3: 3*3+2 = 11
|
||||
self.assertEqual(key, 3 * 3 + 2)
|
||||
|
||||
def test_tree_edge_case3_greater_than_case1(self) -> None:
|
||||
"""Test that case-3 phi > case-1 phi for ordering.
|
||||
|
||||
Case 1: phi = 3*lowpt1[w]
|
||||
Case 3: phi = 3*lowpt1[w]+2
|
||||
Case 3 must be strictly greater than case 1 for same lowpt1.
|
||||
"""
|
||||
e0: int = self.eids[0]
|
||||
key_v1: int = phi_key(
|
||||
v=1, eid=e0, pt=self.pt, graph=self.g,
|
||||
)
|
||||
# Case 3 value (8) > case 1 value (6) for lowpt1[2]=2
|
||||
self.assertGreater(key_v1, 3 * 2)
|
||||
|
||||
|
||||
class TestPhiKeyK3Frond(unittest.TestCase):
|
||||
"""Tests for phi_key correctness on the triangle K3.
|
||||
@@ -426,35 +353,6 @@ class TestPhiKeyK3Frond(unittest.TestCase):
|
||||
# Correct formula: 3 * dfs_num[w=1] + 1 = 3*1+1 = 4
|
||||
self.assertEqual(key, 3 * 1 + 1)
|
||||
|
||||
def test_frond_phi_different_from_v_formula(self) -> None:
|
||||
"""Test that frond phi uses w (not v) DFS number.
|
||||
|
||||
The buggy formula uses dfs_num[v] (= 3) giving 3*3+2 = 11.
|
||||
The correct formula uses dfs_num[w] (= 1) giving 3*1+1 = 4.
|
||||
These must differ.
|
||||
"""
|
||||
e2: int = self.eids[2]
|
||||
key: int = phi_key(
|
||||
v=3, eid=e2, pt=self.pt, graph=self.g,
|
||||
)
|
||||
# The buggy value would be 3*dfs_num[v=3]+2 = 3*3+2 = 11.
|
||||
# The correct value is 3*dfs_num[w=1]+1 = 4.
|
||||
self.assertNotEqual(key, 11)
|
||||
self.assertEqual(key, 4)
|
||||
|
||||
def test_frond_phi_less_than_tree_edge_case3(self) -> None:
|
||||
"""Test ordering: frond phi < tree-edge case-3 phi.
|
||||
|
||||
The frond phi (4) should be less than a case-3 tree-edge phi
|
||||
with the same lowpt1 (3*1+2=5), ensuring correct DFS order.
|
||||
"""
|
||||
e2: int = self.eids[2]
|
||||
frond_key: int = phi_key(
|
||||
v=3, eid=e2, pt=self.pt, graph=self.g,
|
||||
)
|
||||
# Frond phi = 3*1+1=4; case-3 tree-edge phi at lowpt1=1 = 3*1+2=5
|
||||
self.assertLess(frond_key, 3 * 1 + 2)
|
||||
|
||||
def test_tree_edge_case1_condition(self) -> None:
|
||||
"""Test phi_key for tree edge where lowpt2[w] < dfs_num[v].
|
||||
|
||||
|
||||
@@ -120,11 +120,6 @@ def _collect_all_nodes(root: SPQRNode) -> list[SPQRNode]:
|
||||
class TestSPQRNodeStructure(unittest.TestCase):
|
||||
"""Tests for SPQRNode structure and attributes."""
|
||||
|
||||
def test_spqrnode_has_type(self) -> None:
|
||||
"""Test that SPQRNode has a type attribute."""
|
||||
root: SPQRNode = build_spqr_tree(_make_k3())
|
||||
self.assertIsInstance(root.type, NodeType)
|
||||
|
||||
def test_spqrnode_has_skeleton(self) -> None:
|
||||
"""Test that SPQRNode has a skeleton graph."""
|
||||
root: SPQRNode = build_spqr_tree(_make_k3())
|
||||
@@ -146,12 +141,6 @@ class TestSPQRNodeStructure(unittest.TestCase):
|
||||
root: SPQRNode = build_spqr_tree(_make_k3())
|
||||
self.assertIsNone(root.parent)
|
||||
|
||||
def test_children_parent_links(self) -> None:
|
||||
"""Test that children have correct parent links."""
|
||||
root: SPQRNode = build_spqr_tree(_make_k3())
|
||||
for child in root.children:
|
||||
self.assertIs(child.parent, root)
|
||||
|
||||
|
||||
class TestSPQRK3(unittest.TestCase):
|
||||
"""Tests for the SPQR-tree of the triangle K3."""
|
||||
@@ -164,20 +153,10 @@ class TestSPQRK3(unittest.TestCase):
|
||||
_collect_all_nodes(self.root)
|
||||
"""All SPQR tree nodes."""
|
||||
|
||||
def test_returns_spqrnode(self) -> None:
|
||||
"""Test that build_spqr_tree returns an SPQRNode."""
|
||||
self.assertIsInstance(self.root, SPQRNode)
|
||||
|
||||
def test_root_is_s_node(self) -> None:
|
||||
"""Test that K3 produces an S-node (POLYGON) as root."""
|
||||
self.assertEqual(self.root.type, NodeType.S)
|
||||
|
||||
def test_node_types_are_valid(self) -> None:
|
||||
"""Test that all node types are valid NodeType values."""
|
||||
for node in self.all_nodes:
|
||||
self.assertIsInstance(node.type, NodeType)
|
||||
self.assertIn(node.type, list(NodeType))
|
||||
|
||||
|
||||
class TestSPQRK4(unittest.TestCase):
|
||||
"""Tests for the SPQR-tree of the complete graph K4."""
|
||||
@@ -194,10 +173,6 @@ class TestSPQRK4(unittest.TestCase):
|
||||
"""Test that K4 produces a single R-node."""
|
||||
self.assertEqual(self.root.type, NodeType.R)
|
||||
|
||||
def test_skeleton_has_vertices(self) -> None:
|
||||
"""Test that the R-node skeleton has vertices."""
|
||||
self.assertGreater(self.root.skeleton.num_vertices(), 0)
|
||||
|
||||
|
||||
class TestSPQRC4(unittest.TestCase):
|
||||
"""Tests for the SPQR-tree of the 4-cycle C4."""
|
||||
@@ -214,11 +189,6 @@ class TestSPQRC4(unittest.TestCase):
|
||||
"""Test that C4 produces an S-node (POLYGON) as root."""
|
||||
self.assertEqual(self.root.type, NodeType.S)
|
||||
|
||||
def test_node_types_are_valid(self) -> None:
|
||||
"""Test that all node types are valid NodeType values."""
|
||||
for node in self.all_nodes:
|
||||
self.assertIsInstance(node.type, NodeType)
|
||||
|
||||
|
||||
class TestSPQRTwoParallel(unittest.TestCase):
|
||||
"""Tests for the SPQR-tree of two parallel edges."""
|
||||
@@ -258,29 +228,10 @@ class TestSPQRThreeParallel(unittest.TestCase):
|
||||
"""Test that 3 parallel edges produce a single P-node."""
|
||||
self.assertEqual(self.root.type, NodeType.P)
|
||||
|
||||
def test_node_types_are_valid(self) -> None:
|
||||
"""Test that all node types are valid NodeType values."""
|
||||
for node in self.all_nodes:
|
||||
self.assertIsInstance(node.type, NodeType)
|
||||
|
||||
|
||||
class TestSPQRInvariants(unittest.TestCase):
|
||||
"""Tests for global SPQR-tree invariants across all graphs."""
|
||||
|
||||
def _check_parent_links(self, root: SPQRNode) -> None:
|
||||
"""Check that parent-child links are consistent.
|
||||
|
||||
:param root: The SPQR-tree root.
|
||||
:return: None
|
||||
"""
|
||||
for node in _collect_all_nodes(root):
|
||||
for child in node.children:
|
||||
self.assertIs(
|
||||
child.parent,
|
||||
node,
|
||||
f"Child {child.type} has wrong parent",
|
||||
)
|
||||
|
||||
def _check_skeleton_edges(self, root: SPQRNode) -> None:
|
||||
"""Check that each node's skeleton has at least 1 edge.
|
||||
|
||||
@@ -294,28 +245,6 @@ class TestSPQRInvariants(unittest.TestCase):
|
||||
f"Node {node.type} has empty skeleton",
|
||||
)
|
||||
|
||||
def test_k3_parent_links(self) -> None:
|
||||
"""Test parent link invariant for K3."""
|
||||
self._check_parent_links(build_spqr_tree(_make_k3()))
|
||||
|
||||
def test_c4_parent_links(self) -> None:
|
||||
"""Test parent link invariant for C4."""
|
||||
self._check_parent_links(build_spqr_tree(_make_c4()))
|
||||
|
||||
def test_k4_parent_links(self) -> None:
|
||||
"""Test parent link invariant for K4."""
|
||||
self._check_parent_links(build_spqr_tree(_make_k4()))
|
||||
|
||||
def test_two_parallel_parent_links(self) -> None:
|
||||
"""Test parent link invariant for 2 parallel edges."""
|
||||
self._check_parent_links(build_spqr_tree(_make_two_parallel()))
|
||||
|
||||
def test_three_parallel_parent_links(self) -> None:
|
||||
"""Test parent link invariant for 3 parallel edges."""
|
||||
self._check_parent_links(
|
||||
build_spqr_tree(_make_three_parallel())
|
||||
)
|
||||
|
||||
def test_k3_skeleton_edges(self) -> None:
|
||||
"""Test skeleton edge invariant for K3."""
|
||||
self._check_skeleton_edges(build_spqr_tree(_make_k3()))
|
||||
@@ -409,20 +338,6 @@ class TestSPQRDiamond(unittest.TestCase):
|
||||
_collect_all_nodes(self.root)
|
||||
"""All SPQR tree nodes."""
|
||||
|
||||
def test_at_least_two_nodes(self) -> None:
|
||||
"""Test that diamond produces at least 2 SPQR-tree nodes."""
|
||||
self.assertGreaterEqual(
|
||||
len(self.all_nodes),
|
||||
2,
|
||||
"Diamond has a separation pair, expect >=2 SPQR nodes",
|
||||
)
|
||||
|
||||
def test_node_types_are_valid(self) -> None:
|
||||
"""Test that all node types are valid NodeType values."""
|
||||
for node in self.all_nodes:
|
||||
self.assertIsInstance(node.type, NodeType)
|
||||
self.assertIn(node.type, list(NodeType))
|
||||
|
||||
def test_no_ss_adjacency(self) -> None:
|
||||
"""Test that no S-node is adjacent to another S-node."""
|
||||
_assert_no_ss_pp(self, self.root, NodeType.S)
|
||||
@@ -463,11 +378,6 @@ class TestSPQRTheta(unittest.TestCase):
|
||||
"expect P-node at root",
|
||||
)
|
||||
|
||||
def test_node_types_are_valid(self) -> None:
|
||||
"""Test that all node types are valid NodeType values."""
|
||||
for node in self.all_nodes:
|
||||
self.assertIsInstance(node.type, NodeType)
|
||||
|
||||
def test_no_ss_adjacency(self) -> None:
|
||||
"""Test that no S-node is adjacent to another S-node."""
|
||||
_assert_no_ss_pp(self, self.root, NodeType.S)
|
||||
@@ -504,10 +414,6 @@ class TestSPQRPrism(unittest.TestCase):
|
||||
)
|
||||
self.assertEqual(self.root.type, NodeType.R)
|
||||
|
||||
def test_no_children(self) -> None:
|
||||
"""Test that the single R-node has no children."""
|
||||
self.assertEqual(len(self.root.children), 0)
|
||||
|
||||
def test_skeleton_has_nine_edges(self) -> None:
|
||||
"""Test that the R-node skeleton contains 9 edges."""
|
||||
self.assertEqual(self.root.skeleton.num_edges(), 9)
|
||||
@@ -546,52 +452,6 @@ def _assert_no_ss_pp(
|
||||
)
|
||||
|
||||
|
||||
class TestSPQRNoSSPPInvariants(unittest.TestCase):
|
||||
"""Tests that no S-S or P-P adjacency occurs for all graphs."""
|
||||
|
||||
def _check_tree(self, g: MultiGraph) -> None:
|
||||
"""Build SPQR-tree and check S-S and P-P invariants.
|
||||
|
||||
:param g: The input multigraph.
|
||||
:return: None
|
||||
"""
|
||||
root: SPQRNode = build_spqr_tree(g)
|
||||
_assert_no_ss_pp(self, root, NodeType.S)
|
||||
_assert_no_ss_pp(self, root, NodeType.P)
|
||||
|
||||
def test_k3_no_ss_pp(self) -> None:
|
||||
"""Test no S-S or P-P adjacency for K3."""
|
||||
self._check_tree(_make_k3())
|
||||
|
||||
def test_c4_no_ss_pp(self) -> None:
|
||||
"""Test no S-S or P-P adjacency for C4."""
|
||||
self._check_tree(_make_c4())
|
||||
|
||||
def test_k4_no_ss_pp(self) -> None:
|
||||
"""Test no S-S or P-P adjacency for K4."""
|
||||
self._check_tree(_make_k4())
|
||||
|
||||
def test_two_parallel_no_ss_pp(self) -> None:
|
||||
"""Test no S-S or P-P adjacency for 2 parallel edges."""
|
||||
self._check_tree(_make_two_parallel())
|
||||
|
||||
def test_three_parallel_no_ss_pp(self) -> None:
|
||||
"""Test no S-S or P-P adjacency for 3 parallel edges."""
|
||||
self._check_tree(_make_three_parallel())
|
||||
|
||||
def test_diamond_no_ss_pp(self) -> None:
|
||||
"""Test no S-S or P-P adjacency for the diamond graph."""
|
||||
self._check_tree(_make_diamond())
|
||||
|
||||
def test_theta_no_ss_pp(self) -> None:
|
||||
"""Test no S-S or P-P adjacency for the theta graph."""
|
||||
self._check_tree(_make_theta())
|
||||
|
||||
def test_prism_no_ss_pp(self) -> None:
|
||||
"""Test no S-S or P-P adjacency for the triangular prism."""
|
||||
self._check_tree(_make_prism())
|
||||
|
||||
|
||||
def _count_real_edges_in_tree(root: SPQRNode) -> int:
|
||||
"""Count real (non-virtual) edges across all SPQR-tree skeletons.
|
||||
|
||||
@@ -820,28 +680,6 @@ class TestSPQRMultiEdgeComplex(unittest.TestCase):
|
||||
"""Test all SPQR-tree invariants for the multi-edge graph."""
|
||||
_check_spqr_invariants(self, self.g, self.root)
|
||||
|
||||
def test_has_p_node(self) -> None:
|
||||
"""Test that multi-edges produce P-nodes in the tree."""
|
||||
p_nodes: list[SPQRNode] = [
|
||||
n for n in self.all_nodes
|
||||
if n.type == NodeType.P
|
||||
]
|
||||
self.assertGreaterEqual(
|
||||
len(p_nodes), 1,
|
||||
"Multi-edge graph should have at least one P-node",
|
||||
)
|
||||
|
||||
def test_has_s_node(self) -> None:
|
||||
"""Test that the cycle backbone produces an S-node."""
|
||||
s_nodes: list[SPQRNode] = [
|
||||
n for n in self.all_nodes
|
||||
if n.type == NodeType.S
|
||||
]
|
||||
self.assertGreaterEqual(
|
||||
len(s_nodes), 1,
|
||||
"Multi-edge graph should have at least one S-node",
|
||||
)
|
||||
|
||||
def test_exact_node_structure(self) -> None:
|
||||
"""Test exact SPQR-tree node counts: 2 P-nodes, 1 S-node.
|
||||
|
||||
@@ -1045,10 +883,6 @@ class TestSPQRSingleEdge(unittest.TestCase):
|
||||
"""Test that there is exactly 1 node in the tree."""
|
||||
self.assertEqual(len(self.all_nodes), 1)
|
||||
|
||||
def test_no_children(self) -> None:
|
||||
"""Test that the Q-node has no children."""
|
||||
self.assertEqual(len(self.root.children), 0)
|
||||
|
||||
def test_skeleton_has_one_edge(self) -> None:
|
||||
"""Test that the Q-node skeleton has exactly 1 edge."""
|
||||
self.assertEqual(self.root.skeleton.num_edges(), 1)
|
||||
@@ -1104,10 +938,6 @@ class TestSPQRC6(unittest.TestCase):
|
||||
"""Test that C6 yields exactly 1 SPQR node."""
|
||||
self.assertEqual(len(self.all_nodes), 1)
|
||||
|
||||
def test_no_children(self) -> None:
|
||||
"""Test that the root S-node has no children."""
|
||||
self.assertEqual(len(self.root.children), 0)
|
||||
|
||||
def test_skeleton_has_six_edges(self) -> None:
|
||||
"""Test that the S-node skeleton has 6 edges."""
|
||||
self.assertEqual(self.root.skeleton.num_edges(), 6)
|
||||
@@ -1225,10 +1055,6 @@ class TestSPQRPetersen(unittest.TestCase):
|
||||
"""Test that there is exactly 1 node in the tree."""
|
||||
self.assertEqual(len(self.all_nodes), 1)
|
||||
|
||||
def test_no_children(self) -> None:
|
||||
"""Test that the R-node has no children."""
|
||||
self.assertEqual(len(self.root.children), 0)
|
||||
|
||||
def test_skeleton_has_fifteen_edges(self) -> None:
|
||||
"""Test that the R-node skeleton has 15 edges."""
|
||||
self.assertEqual(self.root.skeleton.num_edges(), 15)
|
||||
@@ -2120,19 +1946,6 @@ class TestSPQRLadder(unittest.TestCase):
|
||||
self.assertEqual(len(s), 3, "Expected 3 S-nodes")
|
||||
self.assertEqual(len(p), 2, "Expected 2 P-nodes")
|
||||
|
||||
def test_no_r_or_q_nodes(self) -> None:
|
||||
"""Test that the ladder has no R-nodes or Q-nodes."""
|
||||
r: list[SPQRNode] = [
|
||||
n for n in self.all_nodes
|
||||
if n.type == NodeType.R
|
||||
]
|
||||
q: list[SPQRNode] = [
|
||||
n for n in self.all_nodes
|
||||
if n.type == NodeType.Q
|
||||
]
|
||||
self.assertEqual(len(r), 0, "Expected no R-nodes")
|
||||
self.assertEqual(len(q), 0, "Expected no Q-nodes")
|
||||
|
||||
|
||||
def _make_c7() -> MultiGraph:
|
||||
"""Build the 7-cycle C7 (vertices 0-6).
|
||||
|
||||
@@ -143,10 +143,6 @@ class TestTriconnectedK3(unittest.TestCase):
|
||||
find_triconnected_components(self.g)
|
||||
"""The triconnected split components."""
|
||||
|
||||
def test_returns_list(self) -> None:
|
||||
"""Test that find_triconnected_components returns a list."""
|
||||
self.assertIsInstance(self.comps, list)
|
||||
|
||||
def test_single_component(self) -> None:
|
||||
"""Test that K3 produces exactly 1 triconnected component."""
|
||||
self.assertEqual(len(self.comps), 1)
|
||||
@@ -306,16 +302,6 @@ class TestTriconnectedThreeParallel(unittest.TestCase):
|
||||
class TestTriconnectedInvariants(unittest.TestCase):
|
||||
"""Tests for global invariants across all graphs."""
|
||||
|
||||
def _check_real_edge_count(self, g: MultiGraph) -> None:
|
||||
"""Check that real edge count is preserved across decomposition.
|
||||
|
||||
:param g: The input graph.
|
||||
:return: None
|
||||
"""
|
||||
comps: list[TriconnectedComponent] = \
|
||||
find_triconnected_components(g)
|
||||
self.assertEqual(_count_real_edges(comps), g.num_edges())
|
||||
|
||||
def _check_virtual_edges_in_two_comps(
|
||||
self, comps: list[TriconnectedComponent]
|
||||
) -> None:
|
||||
@@ -334,26 +320,6 @@ class TestTriconnectedInvariants(unittest.TestCase):
|
||||
f"(expected 2)",
|
||||
)
|
||||
|
||||
def test_k3_real_edge_count(self) -> None:
|
||||
"""Test real edge count invariant for K3."""
|
||||
self._check_real_edge_count(_make_k3())
|
||||
|
||||
def test_c4_real_edge_count(self) -> None:
|
||||
"""Test real edge count invariant for C4."""
|
||||
self._check_real_edge_count(_make_c4())
|
||||
|
||||
def test_k4_real_edge_count(self) -> None:
|
||||
"""Test real edge count invariant for K4."""
|
||||
self._check_real_edge_count(_make_k4())
|
||||
|
||||
def test_two_parallel_real_edge_count(self) -> None:
|
||||
"""Test real edge count invariant for 2 parallel edges."""
|
||||
self._check_real_edge_count(_make_two_parallel())
|
||||
|
||||
def test_three_parallel_real_edge_count(self) -> None:
|
||||
"""Test real edge count invariant for 3 parallel edges."""
|
||||
self._check_real_edge_count(_make_three_parallel())
|
||||
|
||||
def test_k3_virtual_edges_in_two_comps(self) -> None:
|
||||
"""Test virtual edge invariant for K3."""
|
||||
comps: list[TriconnectedComponent] = \
|
||||
@@ -384,40 +350,6 @@ class TestTriconnectedInvariants(unittest.TestCase):
|
||||
find_triconnected_components(_make_three_parallel())
|
||||
self._check_virtual_edges_in_two_comps(comps)
|
||||
|
||||
def test_component_types_are_valid(self) -> None:
|
||||
"""Test that all component types are valid ComponentType values."""
|
||||
for g in [
|
||||
_make_k3(), _make_c4(), _make_k4(),
|
||||
_make_two_parallel(), _make_three_parallel(),
|
||||
]:
|
||||
comps: list[TriconnectedComponent] = \
|
||||
find_triconnected_components(g)
|
||||
for comp in comps:
|
||||
self.assertIsInstance(comp.type, ComponentType)
|
||||
self.assertIn(
|
||||
comp.type,
|
||||
[
|
||||
ComponentType.BOND,
|
||||
ComponentType.POLYGON,
|
||||
ComponentType.TRICONNECTED,
|
||||
],
|
||||
)
|
||||
|
||||
def test_each_component_has_edges(self) -> None:
|
||||
"""Test that every component has at least 2 edges."""
|
||||
for g in [
|
||||
_make_k3(), _make_c4(), _make_k4(),
|
||||
_make_two_parallel(), _make_three_parallel(),
|
||||
]:
|
||||
comps: list[TriconnectedComponent] = \
|
||||
find_triconnected_components(g)
|
||||
for comp in comps:
|
||||
self.assertGreaterEqual(
|
||||
len(comp.edges),
|
||||
2,
|
||||
f"Component {comp.type} has fewer than 2 edges",
|
||||
)
|
||||
|
||||
|
||||
def _make_diamond() -> MultiGraph:
|
||||
"""Build the diamond graph (K4 minus one edge).
|
||||
@@ -489,14 +421,6 @@ class TestTriconnectedDiamond(unittest.TestCase):
|
||||
find_triconnected_components(self.g)
|
||||
"""The triconnected split components."""
|
||||
|
||||
def test_at_least_two_components(self) -> None:
|
||||
"""Test that the diamond produces at least 2 components."""
|
||||
self.assertGreaterEqual(
|
||||
len(self.comps),
|
||||
2,
|
||||
"Diamond has separation pair {2,3}, expect >=2 components",
|
||||
)
|
||||
|
||||
def test_total_real_edges(self) -> None:
|
||||
"""Test that total real edge count equals input edge count (5)."""
|
||||
self.assertEqual(
|
||||
@@ -515,18 +439,6 @@ class TestTriconnectedDiamond(unittest.TestCase):
|
||||
f"(expected 2)",
|
||||
)
|
||||
|
||||
def test_no_ss_adjacency(self) -> None:
|
||||
"""Test that no two S-type components share a virtual edge."""
|
||||
_assert_no_same_type_adjacency(
|
||||
self, self.comps, ComponentType.POLYGON
|
||||
)
|
||||
|
||||
def test_no_pp_adjacency(self) -> None:
|
||||
"""Test that no two P-type components share a virtual edge."""
|
||||
_assert_no_same_type_adjacency(
|
||||
self, self.comps, ComponentType.BOND
|
||||
)
|
||||
|
||||
def test_each_component_has_at_least_two_edges(self) -> None:
|
||||
"""Test that every component has at least 2 edges."""
|
||||
for comp in self.comps:
|
||||
@@ -566,18 +478,6 @@ class TestTriconnectedTheta(unittest.TestCase):
|
||||
f"(expected 2)",
|
||||
)
|
||||
|
||||
def test_no_ss_adjacency(self) -> None:
|
||||
"""Test that no two S-type components share a virtual edge."""
|
||||
_assert_no_same_type_adjacency(
|
||||
self, self.comps, ComponentType.POLYGON
|
||||
)
|
||||
|
||||
def test_no_pp_adjacency(self) -> None:
|
||||
"""Test that no two P-type components share a virtual edge."""
|
||||
_assert_no_same_type_adjacency(
|
||||
self, self.comps, ComponentType.BOND
|
||||
)
|
||||
|
||||
def test_each_component_has_at_least_two_edges(self) -> None:
|
||||
"""Test that every component has at least 2 edges."""
|
||||
for comp in self.comps:
|
||||
@@ -963,29 +863,6 @@ class TestTriconnectedMultiEdgeComplex(unittest.TestCase):
|
||||
"""Test all decomposition invariants for the multi-edge graph."""
|
||||
_check_all_invariants(self, self.g, self.comps)
|
||||
|
||||
def test_has_bond_components(self) -> None:
|
||||
"""Test that multi-edges produce BOND components."""
|
||||
bond_types: list[TriconnectedComponent] = [
|
||||
c for c in self.comps
|
||||
if c.type == ComponentType.BOND
|
||||
]
|
||||
self.assertGreaterEqual(
|
||||
len(bond_types), 1,
|
||||
"Multi-edge graph should have at least one BOND "
|
||||
"component",
|
||||
)
|
||||
|
||||
def test_has_polygon_component(self) -> None:
|
||||
"""Test that the backbone cycle produces a POLYGON component."""
|
||||
poly_types: list[TriconnectedComponent] = [
|
||||
c for c in self.comps
|
||||
if c.type == ComponentType.POLYGON
|
||||
]
|
||||
self.assertGreaterEqual(
|
||||
len(poly_types), 1,
|
||||
"Multi-edge graph should have at least one POLYGON",
|
||||
)
|
||||
|
||||
def test_exact_component_structure(self) -> None:
|
||||
"""Test exact component counts: 2 BONDs and 1 POLYGON.
|
||||
|
||||
@@ -1601,12 +1478,6 @@ class TestTriconnectedK33(unittest.TestCase):
|
||||
]
|
||||
self.assertEqual(len(real), 9)
|
||||
|
||||
def test_total_real_edges(self) -> None:
|
||||
"""Test that total real edge count equals input edge count."""
|
||||
self.assertEqual(
|
||||
_count_real_edges(self.comps), self.g.num_edges()
|
||||
)
|
||||
|
||||
|
||||
class TestTriconnectedW4(unittest.TestCase):
|
||||
"""Tests for triconnected decomposition of the wheel graph W4.
|
||||
@@ -1642,12 +1513,6 @@ class TestTriconnectedW4(unittest.TestCase):
|
||||
]
|
||||
self.assertEqual(len(real), 8)
|
||||
|
||||
def test_total_real_edges(self) -> None:
|
||||
"""Test that total real edge count equals input edge count."""
|
||||
self.assertEqual(
|
||||
_count_real_edges(self.comps), self.g.num_edges()
|
||||
)
|
||||
|
||||
|
||||
class TestTriconnectedK3Doubled(unittest.TestCase):
|
||||
"""Tests for triconnected decomposition of K3 with doubled edges.
|
||||
@@ -1850,18 +1715,6 @@ class TestTriconnectedPetersenAugmentedTwice(unittest.TestCase):
|
||||
"""Test all decomposition invariants for doubly-aug. Petersen."""
|
||||
_check_all_invariants(self, self.g, self.comps)
|
||||
|
||||
def test_136_total_components(self) -> None:
|
||||
"""Test that the doubly-augmented Petersen yields 136 comps.
|
||||
|
||||
Expected: 60 BOND + 75 POLYGON + 1 TRICONNECTED = 136 total.
|
||||
"""
|
||||
self.assertEqual(
|
||||
len(self.comps),
|
||||
136,
|
||||
f"Doubly-augmented Petersen should have 136 components, "
|
||||
f"got {len(self.comps)}",
|
||||
)
|
||||
|
||||
def test_one_triconnected(self) -> None:
|
||||
"""Test that there is exactly 1 TRICONNECTED component."""
|
||||
tc: list[TriconnectedComponent] = [
|
||||
@@ -2002,13 +1855,6 @@ class TestTriconnectedLadder(unittest.TestCase):
|
||||
self.assertEqual(len(poly), 3, "Expected 3 POLYGON")
|
||||
self.assertEqual(len(bond), 2, "Expected 2 BOND")
|
||||
|
||||
def test_real_edge_count(self) -> None:
|
||||
"""Test that total real edge count equals 10."""
|
||||
self.assertEqual(
|
||||
_count_real_edges(self.comps),
|
||||
self.g.num_edges(),
|
||||
)
|
||||
|
||||
|
||||
def _make_c7() -> MultiGraph:
|
||||
"""Build the 7-cycle C7 (vertices 0-6).
|
||||
@@ -2164,13 +2010,6 @@ class TestTriconnectedK23(unittest.TestCase):
|
||||
self.assertEqual(len(poly), 3, "Expected 3 POLYGON")
|
||||
self.assertEqual(len(bond), 1, "Expected 1 BOND")
|
||||
|
||||
def test_real_edge_count(self) -> None:
|
||||
"""Test that total real edge count equals 6."""
|
||||
self.assertEqual(
|
||||
_count_real_edges(self.comps),
|
||||
self.g.num_edges(),
|
||||
)
|
||||
|
||||
|
||||
def _make_w5() -> MultiGraph:
|
||||
"""Build the wheel graph W5 (hub + 5-cycle, 6 vertices).
|
||||
@@ -2484,13 +2323,6 @@ class TestTriconnectedK4OneDoubled(unittest.TestCase):
|
||||
len(tri), 1, "Expected 1 TRICONNECTED",
|
||||
)
|
||||
|
||||
def test_real_edge_count(self) -> None:
|
||||
"""Test that total real edge count equals 7."""
|
||||
self.assertEqual(
|
||||
_count_real_edges(self.comps),
|
||||
self.g.num_edges(),
|
||||
)
|
||||
|
||||
|
||||
def _make_mobius_kantor() -> MultiGraph:
|
||||
"""Build the Mobius-Kantor graph GP(8,3) (16 verts, 24 edges).
|
||||
@@ -2611,13 +2443,6 @@ class TestTriconnectedPetersenAugmented(unittest.TestCase):
|
||||
"""Test all decomposition invariants for aug. Petersen."""
|
||||
_check_all_invariants(self, self.g, self.comps)
|
||||
|
||||
def test_thirty_one_components(self) -> None:
|
||||
"""Test that augmented Petersen has 31 components."""
|
||||
self.assertEqual(
|
||||
len(self.comps), 31,
|
||||
f"Expected 31 components, got {len(self.comps)}",
|
||||
)
|
||||
|
||||
def test_one_triconnected(self) -> None:
|
||||
"""Test that there is exactly 1 TRICONNECTED component."""
|
||||
tri: list[TriconnectedComponent] = [
|
||||
@@ -2651,13 +2476,6 @@ class TestTriconnectedPetersenAugmented(unittest.TestCase):
|
||||
f"Expected 15 POLYGON, got {len(polys)}",
|
||||
)
|
||||
|
||||
def test_real_edge_count(self) -> None:
|
||||
"""Test that total real edge count equals 60."""
|
||||
self.assertEqual(
|
||||
_count_real_edges(self.comps),
|
||||
self.g.num_edges(),
|
||||
)
|
||||
|
||||
|
||||
def _make_wikimedia_spqr() -> MultiGraph:
|
||||
"""Build the Wikimedia Commons SPQR-tree example graph.
|
||||
@@ -2716,10 +2534,6 @@ class TestTriconnectedWikimediaSpqr(unittest.TestCase):
|
||||
self.comps: list[TriconnectedComponent] = \
|
||||
find_triconnected_components(self.g)
|
||||
|
||||
def test_component_count(self) -> None:
|
||||
"""Test that there are exactly 5 components."""
|
||||
self.assertEqual(len(self.comps), 5)
|
||||
|
||||
def test_triconnected_count(self) -> None:
|
||||
"""Test that there are exactly 3 TRICONNECTED."""
|
||||
n: int = sum(
|
||||
@@ -2801,10 +2615,6 @@ class TestTriconnectedRpstFig1a(unittest.TestCase):
|
||||
self.comps: list[TriconnectedComponent] = \
|
||||
find_triconnected_components(self.g)
|
||||
|
||||
def test_component_count(self) -> None:
|
||||
"""Test that there are exactly 10 components."""
|
||||
self.assertEqual(len(self.comps), 10)
|
||||
|
||||
def test_triconnected_count(self) -> None:
|
||||
"""Test that there is exactly 1 TRICONNECTED."""
|
||||
n: int = sum(
|
||||
@@ -2911,13 +2721,6 @@ class TestBiconnectivityCheck(unittest.TestCase):
|
||||
find_triconnected_components(g)
|
||||
self.assertEqual(comps, [])
|
||||
|
||||
def test_biconnected_graph_ok(self) -> None:
|
||||
"""Test that a biconnected graph does not raise."""
|
||||
g: MultiGraph = _make_k3()
|
||||
comps: list[TriconnectedComponent] = \
|
||||
find_triconnected_components(g)
|
||||
self.assertEqual(len(comps), 1)
|
||||
|
||||
|
||||
# Script used by TestTriconnectedDeterminism to run decomposition
|
||||
# in a subprocess with a specific PYTHONHASHSEED.
|
||||
|
||||
Reference in New Issue
Block a user