|
| 1 | +""" |
| 2 | +A vertex fills its attributes directly in ``__init__``, and ``Graph.add_vertex`` |
| 3 | +sets its name and graph directly, because the ``__setattr__`` of a vertex only |
| 4 | +exists to tell the graph that a coordinate moved, and a vertex that is not in |
| 5 | +a graph yet has no graph to tell. These tests check that nothing else changed: |
| 6 | +the attributes, the name table, and that moving a vertex that is in a graph is |
| 7 | +still seen. |
| 8 | +""" |
| 9 | + |
| 10 | +import contextlib |
| 11 | +import io |
| 12 | +import unittest |
| 13 | + |
| 14 | +import numpy as np |
| 15 | + |
| 16 | +from pgraph import DGraph, UGraph, Vertex |
| 17 | + |
| 18 | + |
| 19 | +class TestConstructor(unittest.TestCase): |
| 20 | + def test_attributes_of_a_new_vertex(self): |
| 21 | + v = Vertex(coord=[1, 2], name="a") |
| 22 | + np.testing.assert_array_equal(v.coord, [1, 2]) |
| 23 | + self.assertEqual(v.name, "a") |
| 24 | + self.assertIsNone(v.label) |
| 25 | + self.assertEqual(v._edgelist, []) |
| 26 | + self.assertEqual(v._inlist, []) |
| 27 | + self.assertIsNone(v._graph) |
| 28 | + self.assertTrue(v._connectivitychange) |
| 29 | + |
| 30 | + def test_no_coordinate(self): |
| 31 | + v = Vertex() |
| 32 | + self.assertIsNone(v.coord) |
| 33 | + self.assertIsNone(v.name) |
| 34 | + |
| 35 | + def test_the_coordinate_is_a_float_array_copy(self): |
| 36 | + c = np.array([1.0, 2.0]) |
| 37 | + v = Vertex(coord=c) |
| 38 | + c[0] = 99.0 |
| 39 | + self.assertEqual(v.coord[0], 1.0) # not the caller's array |
| 40 | + self.assertIsInstance(v.coord, np.ndarray) |
| 41 | + |
| 42 | + def test_the_lists_are_not_shared_between_vertices(self): |
| 43 | + a, b = Vertex(), Vertex() |
| 44 | + self.assertIsNot(a._edgelist, b._edgelist) |
| 45 | + self.assertIsNot(a._inlist, b._inlist) |
| 46 | + self.assertIsNot(a._edgelist, a._inlist) |
| 47 | + |
| 48 | + def test_subclass_with_its_own_init(self): |
| 49 | + class Landmark(Vertex): |
| 50 | + def __init__(self, coord=None, name=None, color=None): |
| 51 | + super().__init__(coord, name) |
| 52 | + self.color = color # an attribute of its own, set after |
| 53 | + |
| 54 | + v = Landmark([0.0, 0.0], name="l", color="red") |
| 55 | + self.assertEqual((v.name, v.color), ("l", "red")) |
| 56 | + g = UGraph(vertex_class=Landmark) |
| 57 | + g.add_vertex(v) |
| 58 | + self.assertEqual(v.color, "red") |
| 59 | + self.assertIs(g["l"], v) |
| 60 | + |
| 61 | + |
| 62 | +class TestInAGraph(unittest.TestCase): |
| 63 | + def test_name_table_and_back_pointer(self): |
| 64 | + for cls in (UGraph, DGraph): |
| 65 | + with self.subTest(cls=cls.__name__): |
| 66 | + g = cls() |
| 67 | + v = g.add_vertex([0.0, 0.0], name="v") |
| 68 | + w = g.add_vertex([1.0, 0.0]) # default name |
| 69 | + self.assertIs(v._graph, g) |
| 70 | + self.assertIs(g["v"], v) |
| 71 | + self.assertEqual(w.name, "#1") |
| 72 | + self.assertIs(g["#1"], w) |
| 73 | + self.assertEqual(g.validate(), []) |
| 74 | + |
| 75 | + def test_name_from_the_vertex_is_kept(self): |
| 76 | + g = UGraph() |
| 77 | + v = Vertex(name="given") |
| 78 | + g.add_vertex(v) |
| 79 | + self.assertEqual(v.name, "given") |
| 80 | + self.assertIs(g["given"], v) |
| 81 | + |
| 82 | + def test_renaming_still_updates_the_table(self): |
| 83 | + g = UGraph() |
| 84 | + v = g.add_vertex(name="old") |
| 85 | + v.name = "new" |
| 86 | + self.assertIs(g["new"], v) |
| 87 | + self.assertNotIn("old", g) |
| 88 | + |
| 89 | + def test_duplicate_name_is_refused_and_the_vertex_stays_free(self): |
| 90 | + g = UGraph() |
| 91 | + g.add_vertex(name="a") |
| 92 | + v = Vertex(name="b") |
| 93 | + with self.assertRaises(ValueError): |
| 94 | + g.add_vertex(v, name="a") |
| 95 | + self.assertIsNone(v._graph) |
| 96 | + self.assertEqual(v.name, "b") |
| 97 | + |
| 98 | + def test_a_vertex_in_a_graph_cannot_be_added_to_another(self): |
| 99 | + g, h = UGraph(), UGraph() |
| 100 | + v = g.add_vertex() |
| 101 | + with self.assertRaises(ValueError): |
| 102 | + h.add_vertex(v) |
| 103 | + |
| 104 | + def test_a_verbose_graph_says_what_it_adds(self): |
| 105 | + g = UGraph(dim=2, verbose=True) |
| 106 | + buf = io.StringIO() |
| 107 | + with contextlib.redirect_stdout(buf): |
| 108 | + g.add_vertex([1.0, 2.0], name="a") |
| 109 | + g.add_vertex([3.0, 4.0]) |
| 110 | + lines = buf.getvalue().splitlines() |
| 111 | + self.assertEqual(len(lines), 2) |
| 112 | + self.assertTrue(lines[0].startswith("New vertex a: ")) |
| 113 | + self.assertTrue(lines[1].startswith("New vertex #1: ")) |
| 114 | + |
| 115 | + def test_assigning_a_coordinate_is_still_seen(self): |
| 116 | + g = UGraph(dim=2) |
| 117 | + v = g.add_vertex([0.0, 0.0]) |
| 118 | + g._spatialchange = False |
| 119 | + v.coord = np.r_[1.0, 1.0] |
| 120 | + self.assertTrue(g._spatialchange) |
| 121 | + g._spatialchange = False |
| 122 | + v.coord += np.r_[1.0, 0.0] |
| 123 | + self.assertTrue(g._spatialchange) |
| 124 | + |
| 125 | + def test_assigning_other_attributes_is_not_a_change(self): |
| 126 | + g = UGraph(dim=2) |
| 127 | + v = g.add_vertex([0.0, 0.0]) |
| 128 | + g._spatialchange = False |
| 129 | + v.data = "anything" |
| 130 | + self.assertFalse(g._spatialchange) |
| 131 | + |
| 132 | + |
| 133 | +if __name__ == "__main__": |
| 134 | + unittest.main() |
0 commit comments