class TestGraphCopy(UnittestPythonCompatibility): """ Test Graph copy and deepcopy methods """ def setUp(self): """ Build default graph with nodes, edges and attributes """ self.graph = Graph() self.graph.add_nodes([('g', { 'weight': 1.0 }), ('r', { 'weight': 1.5 }), ('a', { 'weight': 2.0 }), ('p', { 'weight': 2.5 }), ('h', { 'weight': 3.0 })]) self.graph.add_edges([(1, 2), (2, 3), (3, 4), (3, 5), (4, 5)], isedge=True) def tearDown(self): """ Test copied state Testing equality in node, edge and adjacency data stores is based on the internal '_storage' object and not so much the storage object itself which is often just a wrapper. """ # Main Graph object is new self.assertTrue(id(self.copied) != id(self.graph)) if self.shallow: # Internal node and edge stores point to parent. self.assertEqual(id(self.copied.nodes._storage), id(self.graph.nodes._storage)) self.assertEqual(id(self.copied.edges._storage), id(self.graph.edges._storage)) # ORM and origin objects point to parent self.assertEqual(id(self.copied.orm), id(self.graph.orm)) self.assertEqual(id(self.copied.origin), id(self.graph.origin)) else: # Internal node and edge stores point to parent. self.assertNotEqual(id(self.copied.nodes._storage), id(self.graph.nodes._storage)) self.assertNotEqual(id(self.copied.edges._storage), id(self.graph.edges._storage)) # ORM and origin objects point to parent self.assertNotEqual(id(self.copied.orm), id(self.graph.orm)) self.assertNotEqual(id(self.copied.origin), id(self.graph.origin)) def test_graph_copy_shallow(self): """ Test making a shallow copy of a graph. This essentially copies the Graph object while linking tot the data store in the parent Graph """ self.shallow = True self.copied = self.graph.copy(deep=False) def test_graph_copy_deep(self): """ Test making a deep copy of a graph (default) copying everything """ self.shallow = False self.copied = self.graph.copy() def test_graph_buildin_copy_shallow(self): """ Test making a shallow copy of a graph using the 'copy' method of the copy class. This calls the Graph.copy method """ self.shallow = True self.copied = copy.copy(self.graph) def test_graph_buildin_copy_deep(self): """ Test making a deep copy of a graph using the 'deepcopy' method of the copy class. This calls the Graph.copy method """ self.shallow = False self.copied = copy.deepcopy(self.graph) def test_graph_buildin_copy_deep_view(self): """ Test copying subgraphs either with the set 'view' only or the full origin graph (full graph) """ # Regular copy self.shallow = False self.copied = copy.deepcopy(self.graph) # Build subgraph, same origin view = self.graph.getnodes([3, 4, 5]) self.assertEqual(id(view.origin), id(self.graph.origin)) # Deep copy with or without view, different origin copy_view = view.copy(deep=True, copy_view=False) copy_full = view.copy(deep=True, copy_view=True) self.assertNotEqual(id(copy_view.origin), id(self.graph.origin)) self.assertNotEqual(id(copy_full.origin), id(self.graph.origin)) # Subgraph 'view' should be identical to the original # regardless the copy mode self.assertEqual(copy_view.nodes.keys(), view.nodes.keys()) self.assertEqual(copy_view.edges.keys(), view.edges.keys()) self.assertEqual(copy_view.adjacency.keys(), view.adjacency.keys()) self.assertEqual(copy_full.nodes.keys(), view.nodes.keys()) self.assertEqual(copy_full.edges.keys(), view.edges.keys()) self.assertEqual(copy_full.adjacency.keys(), view.adjacency.keys()) # The view copy origin should either be identical to the view # (copy_view = True) or to the full graph (copy_view = False) self.assertEqual(list(copy_view.nodes._storage.keys()), list(view.nodes.keys())) self.assertEqual(list(copy_full.nodes._storage.keys()), list(view.origin.nodes.keys())) # The copy_full has its origin equals self and thus copy_full.origin.nodes # equals copy_full.nodes. However, the view is also set which means that # by default the full graph is not accessible without resetting it copy_full.nodes.reset_view() self.assertEqual(copy_full.nodes.keys(), self.graph.nodes.keys())
class TestGraphIteration(UnittestPythonCompatibility): """ Test methods for iteration over nodes and edges in a graph """ def setUp(self): """ Build default Graph with node and edge attributes """ self.graph = Graph() self.graph.add_nodes([('g', {'weight': 1.0, 'value': 'gr'}), ('r', {'weight': 1.5, 'value': 'ra'}), ('a', {'weight': 2.0, 'value': 'ap'}), ('p', {'weight': 2.5, 'value': 'ph'}), ('h', {'weight': 3.0})]) self.graph.add_edges([(1, 2), (2, 3), (3, 4), (3, 5), (4, 5)], value=True, weight=43.2, key='edge') def test_magic_method_eq(self): """ Test Graph equality __eq__ (==) test """ self.assertTrue(self.graph == self.graph) self.assertTrue(self.graph.getnodes([1, 3, 4]) == self.graph.getnodes([1, 3, 4])) self.assertTrue(self.graph == self.graph.copy(deep=True)) self.assertFalse(self.graph.getnodes([1, 2]) == self.graph.getnodes([2, 3])) self.assertFalse(self.graph.getedges([(1, 2), (2, 3)]) == self.graph.getnodes([2, 3])) def test_magic_method_add(self): """ Test Graph addition __add__ (+) support """ # Adding self to self does not change anything self.assertEqual(self.graph + self.graph, self.graph) # Adding sub graphs together to yield the full graph only works if # there is an overlap in the graphs connecting them together. Without # the overlap the connecting edges are lost sub1 = self.graph.getnodes([1, 2, 3]) sub2 = self.graph.getnodes([3, 4, 5]) self.assertEqual(sub1 + sub2, self.graph) sub1 = self.graph.getnodes([1, 2, 3]) sub2 = self.graph.getnodes([4, 5]) self.assertNotEqual(sub1 + sub2, self.graph) # Sub graphs are still views on the origin combined = sub1 + sub2 self.assertTrue(combined.nodes.is_view) self.assertTrue(combined.edges.is_view) self.assertEqual(id(combined.origin), id(self.graph.origin)) # Adding graphs together that do not share a common origin sub1_copy = sub1.copy() combined = sub1_copy + sub2 self.assertFalse(combined.nodes.is_view) self.assertFalse(combined.edges.is_view) self.assertNotEqual(id(combined.origin), id(self.graph.origin)) def test_magic_method_iadd(self): """ Test Graph in place addition __iadd__ (+=) support """ # Adding sub graphs together to yield the full graph only works if # there is an overlap in the graphs connecting them together. Without # the overlap the connecting edges are lost sub1 = self.graph.getnodes([1, 2, 3]) sub2 = self.graph.getnodes([3, 4, 5]) sub1 += sub2 self.assertEqual(sub1, self.graph) sub1 = self.graph.getnodes([1, 2, 3]) sub2 = self.graph.getnodes([4, 5]) sub1 += sub2 self.assertNotEqual(sub1, self.graph) # Sub graphs are still views on the origin self.assertTrue(sub1.nodes.is_view) self.assertTrue(sub1.edges.is_view) self.assertEqual(id(sub1.origin), id(self.graph.origin)) # Adding graphs together that do not share a common origin sub1_copy = sub1.copy() sub1_copy += sub2 self.assertFalse(sub1_copy.nodes.is_view) self.assertFalse(sub1_copy.edges.is_view) self.assertNotEqual(id(sub1_copy.origin), id(self.graph.origin)) def test_magic_method_contains(self): """ Test Graph contains __contains__ test """ # Equal graphs also contain each other self.assertTrue(self.graph in self.graph) sub1 = self.graph.getnodes([1, 2, 3]) sub2 = self.graph.getnodes([4, 5]) self.assertTrue(sub1 in self.graph) self.assertFalse(sub2 in sub1) def test_magic_method_getitem(self): """ Test Graph dictionary style __getitem__ item lookup """ self.assertEqual(self.graph[2], self.graph.getnodes(2)) self.assertEqual(self.graph[(2, 3)], self.graph.getedges((2, 3))) # Support for slicing self.assertEqual(self.graph[2:], self.graph.getnodes([2, 3, 4, 5])) self.assertEqual(self.graph[2:4], self.graph.getnodes([2, 3])) self.assertEqual(self.graph[1:5:2], self.graph.getnodes([1, 3])) self.assertTrue(self.graph[1:-1].empty()) def test_magic_method_ge(self): """ Test Graph greater-then or equal __ge__ (>=) to support """ sub = self.graph.getnodes([2, 3, 4]) self.assertTrue(self.graph >= sub) self.assertFalse(sub >= self.graph) self.assertTrue(sub >= sub) def test_magic_method_gt(self): """ Test Graph greater-then __gt__ (>) support """ sub = self.graph.getnodes([2, 3, 4]) self.assertTrue(self.graph > sub) self.assertFalse(sub > self.graph) def test_magic_method_len(self): """ Test Graph length __len__ support """ self.assertEqual(len(self.graph), 5) def test_magic_method_le(self): """ Test Graph less-then or equal __le__ (<=) to support """ sub = self.graph.getnodes([2, 3, 4]) self.assertTrue(sub <= self.graph) self.assertFalse(self.graph <= sub) self.assertTrue(sub <= sub) def test_magic_method_lt(self): """ Test Graph greater-then __lt__ (<) support """ sub = self.graph.getnodes([2, 3, 4]) self.assertTrue(sub < self.graph) self.assertFalse(self.graph < sub) def test_magic_method_ne(self): """ Test Graph equality __ne__ (!=) test """ self.assertFalse(self.graph != self.graph) self.assertFalse(self.graph.getnodes([1, 3, 4]) != self.graph.getnodes([1, 3, 4])) self.assertFalse(self.graph != self.graph.copy(deep=True)) self.assertTrue(self.graph.getnodes([1, 2]) != self.graph.getnodes([2, 3])) self.assertTrue(self.graph.getedges([(1, 2), (2, 3)]) != self.graph.getnodes([2, 3])) def test_magic_method_sub(self): """ Test Graph subtract __sub__ (-) support """ sub = self.graph.getnodes([2, 3, 4]) self.assertEqual(self.graph - sub, self.graph.getnodes([1,5])) self.assertTrue(len(sub - self.graph) == 0) def test_magic_method_isub(self): """ Test Graph inplace subtract __isub__ (-=) support """ cp = self.graph.copy() self.graph -= self.graph.getnodes([2, 3, 4]) self.assertEqual(self.graph, cp.getnodes([1,5]))