def testParseEdgeList(self): # edgeList -> ID | ID '->' edgeList # Left off first required ID. p = Parser(Lexer('')) g = Graph() self.assertRaises(SyntaxError, p._parseEdgeList, g) # Using non-existing label should create a new vertex. g = Graph() p = Parser(Lexer('A')) p._parseEdgeList(g) self.assertEqual(g._vertices['v0'].label, 'A') # Using an existing label should not create a new vertex. g = Graph() g.addVertex(Vertex('u0', 'A')) p = Parser(Lexer('A')) p._parseEdgeList(g) self.assertEqual(g._vertices['u0'].label, 'A') # Left off second ID. p = Parser(Lexer('A ->')) g = Graph() self.assertRaises(SyntaxError, p._parseEdgeList, g) # Simple transition should create two vertices and connect them. g = Graph() p = Parser(Lexer('A -> B')) p._parseEdgeList(g) self.assertEquals(len(g._vertices), 2) self.assertEquals(g._vertices['v0'].label, 'A') self.assertEquals(g._vertices['v1'].label, 'B') self.assertEquals(g._edges['v0'][0].label, 'B')
def testFindMatchingProductions(self): # Providing no productions should result in no matches. gen = Generator() g = Graph() self.assertEquals( len(gen._findMatchingProductions(g, [])), 0) # We have a production, but the LHS can't be found in the graph. # No solutions. g = Graph() g.addEdge(Vertex('g0', 'A'), Vertex('g1', 'B')) lhs = Graph() lhs.addEdge(Vertex('g0', 'C'), Vertex('g1', 'D')) rhs = Graph() p1 = Production(lhs, rhs) gen = Generator() self.assertEquals( len(gen._findMatchingProductions(g, [p1])), 0) # One matching production, a simple vertex "A". g = Graph() g.addEdge(Vertex('g0', 'A'), Vertex('g1', 'B')) lhs = Graph() lhs.addVertex(Vertex('g0', 'A', '1')) rhs = Graph() p1 = Production(lhs, rhs) self.assertEquals( len(gen._findMatchingProductions(g, [p1])), 1) # Two matching productions. g = Graph() g.addEdge(Vertex('g0', 'A'), Vertex('g1', 'B')) lhs = Graph() lhs.addVertex(Vertex('g0', 'A', '2')) rhs = Graph() p1 = Production(lhs, rhs) p2 = Production(lhs, rhs) self.assertEquals( len(gen._findMatchingProductions(g, [p1, p2])), 2)
def testDeleteMissingVertices(self): # lhs has no vertices(!). Nothing done. g = Graph() lhs = Graph() rhs = Graph() p = Production(lhs, rhs) gen = Generator() gen._deleteMissingVertices(g, p, {}) self.assertEqual(len(g._vertices), 0) # lhs has vertices, but they all appear in the rhs. Nothing done. g.addVertex(Vertex('g0', 'A', 1)) lhs.addVertex(Vertex('l0', 'A', 1)) rhs.addVertex(Vertex('r0', 'A', 1)) p = Production(lhs, rhs) gen._deleteMissingVertices(g, p, {'l0':'g0'}) self.assertEqual(len(g._vertices), 1) # lhs has a vertex (A2) that don't appear in the rhs. It should be # deleted from g. g.addVertex(Vertex('g1', 'A', 2)) lhs.addVertex(Vertex('l1', 'A', 2)) p = Production(lhs, rhs) self.assertEqual(len(g._vertices), 2) gen._deleteMissingVertices(g, p, {'l0':'g0', 'l1':'g1'}) self.assertEqual(len(g._vertices), 1)
def testSubgraphSearchSolutionFound(self): # Test that when the length of query=>data vertex matches is the # same as the number of query vertices, then the solution is stored. g = Graph() q = Graph() q.addVertex(Vertex('u1', 'A')) # one query vertex matches = {'u1': 'v1'} # one match g._subgraphSearch(matches, q) self.assertEqual(len(g._solutions), 1)
def testSubgraphSearchSolutionNoCandidates(self): # Test when an umatched query vertex doesn't have any candidates, we # don't find any solutions. g = Graph() g.addVertex(Vertex('v1', 'A')) q = Graph() q.addVertex(Vertex('u1', 'B')) matches = {} self.assertEqual(len(g._solutions), 0) g._subgraphSearch(matches, q) self.assertEqual(len(g._solutions), 0)
def testSubgraphSearchSimpleSolution(self): # One simple solution. g = Graph() g.addVertex(Vertex('v1', 'A')) v1 = g._vertices['v1'] q = Graph() q.addVertex(Vertex('u1', 'A')) q._vertices['u1'].candidates = [v1] self.assertEqual(len(g._solutions), 0) g._subgraphSearch({}, q) self.assertEqual(len(g._solutions), 1) self.assertIn('u1', g._solutions[0]) self.assertEquals(g._solutions[0]['u1'], 'v1')
def testSubgraphSearchOneCandidateNotJoinable(self): # The query vertex has a candidate data vertex, but they aren't # "joinable" -- no solution. g = Graph() g.addVertex(Vertex('v1', 'A')) g.addVertex(Vertex('v2', 'B')) v1 = g._vertices['v1'] q = Graph() q.addEdge(Vertex('u1', 'A'), Vertex('u2', 'B')) q._vertices['u1'].candidates = [v1] # u2 and v2 are already matched. There's an edge between u1 and # u2, but no edge between v1 and v2 so u1 and v1 cannot be matched. matches = {'u2': 'v2'} self.assertEqual(len(g._solutions), 0) g._subgraphSearch(matches, q) self.assertEqual(len(g._solutions), 0)
def testAddNewVertices(self): # Production rhs has no vertices, so nothing done. g = Graph() lhs = Graph() rhs = Graph() p = Production(lhs, rhs) gen = Generator() self.assertEqual(len(g._vertices), 0) gen._addNewVertices(g, p, {}) self.assertEqual(len(g._vertices), 0) # Production rhs has vertices, but they all appear in the LHS. Hence # they aren't new and nothing is done. lhs.addVertex(Vertex('l1', 'A', 1)) rhs.addVertex(Vertex('r1', 'A', 1)) self.assertEqual(len(g._vertices), 0) gen._addNewVertices(g, p, {}) self.assertEqual(len(g._vertices), 0) # rhs has one new vertex not in the lhs. rhsMapping = {} rhs.addVertex(Vertex('r2', 'B', 2)) self.assertEqual(len(g._vertices), 0) gen._addNewVertices(g, p, rhsMapping) self.assertEqual(len(g._vertices), 1) self.assertIn('v0', g._vertices) # new vertex is v0 self.assertEqual(g._vertices['v0'].label, 'B') # with label B self.assertEqual(g._vertices['v0'].number, 2) # with number 2 self.assertIn('r2', rhsMapping) # now appears in rhsMapping self.assertEqual(rhsMapping['r2'], 'v0') # r2 mapped to v0 (the newly added vertex) in graph
def testAddNewEdges(self): # rhs has no edges, so nothing changes. g = Graph() lhs = Graph() rhs = Graph() p = Production(lhs,rhs) rhsMapping = {} gen = Generator() self.assertEqual(len(g._edges), 0) gen._addNewEdges(g, p, rhsMapping) self.assertEqual(len(g._edges), 0) # rhs has an edge, but it already exists in the graph. g = Graph() g.addEdge(Vertex('g0', 'A'), Vertex('g1', 'B')) lhs = Graph() rhs = Graph() rhs.addEdge(Vertex('r0', 'A', 1), Vertex('r1', 'B', 1)) p = Production(lhs,rhs) rhsMapping = {'r0':'g0', 'r1':'g1'} gen = Generator() self.assertEqual(len(g._edges['g0']), 1) gen._addNewEdges(g, p, rhsMapping) self.assertEqual(len(g._edges['g0']), 1) # rhs has a new edge not in graph. g = Graph() g.addVertex(Vertex('g0', 'A')) g.addVertex(Vertex('g1', 'B')) lhs = Graph() rhs = Graph() rhs.addEdge(Vertex('r0', 'A', 1), Vertex('r1', 'B', 1)) p = Production(lhs,rhs) rhsMapping = {'r0':'g0', 'r1':'g1'} gen = Generator() self.assertEqual(len(g._edges['g0']), 0) gen._addNewEdges(g, p, rhsMapping) self.assertEqual(len(g._edges['g0']), 1) self.assertEqual(g._edges['g0'][0].id, 'g1')
def testMapRHSToGraph(self): # No vertices in rhs. Mapping returned is empty. g = Graph() lhs = Graph() rhs = Graph() p = Production(lhs, rhs) gen = Generator() rhsMapping = gen._mapRHSToGraph(g, p, {}) self.assertEqual(len(rhsMapping), 0) # rhs has vertex r1, but it doesn't appear in the lhs. Mapping returned # is empty. rhs.addVertex(Vertex('r1', 'A', '1')) rhsMapping = gen._mapRHSToGraph(g, p, {}) self.assertEqual(len(rhsMapping), 0) # rhs vertex r1 also appears in lhs as l1, which is mapped to g1. # r1 should appear in rhsMapping mapped to g1. lhs.addVertex(Vertex('l1', 'A', '1')) rhsMapping = gen._mapRHSToGraph(g, p, {'l1':'g1'}) self.assertEqual(len(rhsMapping), 1) self.assertIn('r1', rhsMapping) self.assertEqual(rhsMapping['r1'], 'g1')
def testParseVertexID(self): p = Parser(Lexer('')) # No text label raises an error. g = Graph() t = Token(0, '123') self.assertRaises(AttributeError, p._parseVertexID, t, g) # Only a label - doesn't exist in the graph. g = Graph() t = Token(0, 'A') v = p._parseVertexID(t, g) self.assertEqual(v.label, 'A') self.assertIsNone(v.number) # Label and a number - not in the graph. g = Graph() t = Token(0, 'A1') v = p._parseVertexID(t, g) self.assertEqual(v.label, 'A') self.assertEqual(v.number, '1') # Only a label - already in the graph. g = Graph() u = Vertex('v1', 'A') g.addVertex(u) t = Token(0, 'A') v = p._parseVertexID(t, g) self.assertEqual(v, u) # Label and a number - already in the graph. g = Graph() u = Vertex('v1', 'A', '1') g.addVertex(u) t = Token(0, 'A1') v = p._parseVertexID(t, g) self.assertEqual(v, u)
def testApplyProduction_Blackbox(self): # Black-box test of _applyProduction. # Graph is A->C,D g = Graph() g.addEdge(Vertex('g0', 'A'), Vertex('g1', 'C')) g.addVertex(Vertex('g2', 'D')) # Production is A->C,D ==> A->B,C. # This adds vertex B, adds edge from A->B, deletes edge from A->C, # and deletes vertex D. lhs = Graph() lhs.addEdge(Vertex('l0', 'A'), Vertex('l1', 'C')) lhs.addVertex(Vertex('l2', 'D')) rhs = Graph() rhs.addEdge(Vertex('r0', 'A'), Vertex('r1', 'B')) rhs.addVertex(Vertex('r2', 'C')) p = Production(lhs, rhs) gen = Generator() gen._applyProduction(g, p, {'l0':'g0','l1':'g1','l2':'g2'}) self.assertEqual(len(g._vertices), 3) # <g0,A> is still in the graph. self.assertIn('g0', g._vertices) self.assertEqual(g._vertices['g0'].label, 'A') # <v3,B> has been added. self.assertIn('v2', g._vertices) self.assertEqual(g._vertices['v2'].label, 'B') # A->B self.assertIn(g._vertices['v2'], g._edges['g0']) # <g1,C> is still in the graph. self.assertIn('g1', g._vertices) self.assertEqual(g._vertices['g1'].label, 'C') # A->C has been removed. self.assertNotIn(g._vertices['g1'], g._edges['g0']) # <g2,D> has been removed. self.assertNotIn('g2', g._vertices) # Output looks fine. self.assertEqual(str(g), 'digraph {\nA_g0->B_v2;\n\n}')
class TestGraphClass(unittest.TestCase): def setUp(self): self.g = Graph() def testConstructor(self): # New graphs should be empty. self.assertTrue(len(self.g._vertices) == 0) self.assertTrue(len(self.g._edges) == 0) self.assertTrue(len(self.g._neighbors) == 0) def testAddVertex(self): # Correct add. v = self.g.addVertex(Vertex('u1')) self.assertTrue('u1' in self.g._vertices) # Adding an existing vertex should return the existing vertex. self.assertEquals(self.g.addVertex(v), v) def testAddEdge(self): # Add edge with two vid's a = self.g.addVertex(Vertex('u1', 'A')) b = self.g.addVertex(Vertex('u2', 'B')) self.g.addEdge('u1', 'u2') # Add edge with one vid and one Vertex. self.g.addEdge('u1', Vertex('u3', 'C')) c = self.g._vertices['u3'] # Add edge with one Vertex and one vid. self.g.addEdge(Vertex('u4', 'D'), 'u1') d = self.g._vertices['u4'] # Add edge with two Vertics. self.g.addEdge(Vertex('u5', 'E'), Vertex('u6', 'F')) e = self.g._vertices['u5'] f = self.g._vertices['u6'] # Make sure A points to B and C. self.assertTrue(b in self.g._edges['u1']) self.assertTrue(c in self.g._edges['u1']) self.assertTrue(a in self.g._edges['u4']) self.assertTrue(f in self.g._edges['u5']) # Make sure neighbors all reference each other. self.assertTrue(b in self.g._neighbors['u1']) self.assertTrue(c in self.g._neighbors['u1']) self.assertTrue(d in self.g._neighbors['u1']) self.assertTrue(a in self.g._neighbors['u2']) self.assertTrue(a in self.g._neighbors['u3']) self.assertTrue(a in self.g._neighbors['u4']) self.assertTrue(e in self.g._neighbors['u6']) self.assertTrue(f in self.g._neighbors['u5']) # Make sure the vertex degrees were updated. self.assertEquals(self.g._vertices['u1'].degree, 3) self.assertEquals(self.g._vertices['u2'].degree, 1) self.assertEquals(self.g._vertices['u3'].degree, 1) self.assertEquals(self.g._vertices['u4'].degree, 1) self.assertEquals(self.g._vertices['u5'].degree, 1) self.assertEquals(self.g._vertices['u6'].degree, 1) def testDeleteEdge(self): # Referencing non-existing vertices should return False self.assertFalse(self.g.deleteEdge('aaa', 'bbb')) # Build A->B self.g.addEdge(Vertex('u1', 'A'), Vertex('u2', 'B')) u1 = self.g._vertices['u1'] u2 = self.g._vertices['u2'] # Deleting a non-existant edge should return False. self.assertFalse(self.g.deleteEdge('u1', 'bbb')) # Deleting an existing edge should return True. self.assertTrue(self.g.deleteEdge('u1', 'u2')) # u2 should not appear in the list of edges from u1. self.assertTrue(u2 not in self.g._edges['u1']) # u2 should not appear in the list of neighbors of u1. self.assertTrue(u2 not in self.g._neighbors['u1']) # Both vertex degress should be decremented. self.assertEquals(u1.degree, 0) self.assertEquals(u2.degree, 0) def testDeleteVertex(self): # Deleting a non-existing vertex raises an exception. self.assertIsNone(self.g.deleteVertex('X')) # Build A->B, B->A self.g.addEdge(Vertex('u1', 'A'), Vertex('u2', 'B')) self.g.addEdge('u2', 'u1') u1 = self.g._vertices['u1'] u2 = self.g._vertices['u2'] self.g.deleteVertex('u1') # u1 shouldn't appear anywhere as an edge or neighbor. self.assertTrue('u1' not in self.g._edges) self.assertTrue('u1' not in self.g._neighbors) self.assertTrue('u1' not in self.g._edges['u2']) self.assertTrue('u1' not in self.g._neighbors['u2']) # u1 isn't a vertex anymore. self.assertTrue('u1' not in self.g._vertices) def testEdgesProperty(self): # Build A->B, B->C self.g.addEdge(Vertex('u1', 'A'), Vertex('u2', 'B')) self.g.addEdge('u2', Vertex('u3', 'C')) for e in self.g.edges: if e[0].id == 'u1': self.assertEquals(e[1].id, 'u2') elif e[0].id == 'u2': self.assertEquals(e[1].id, 'u3') else: self.assertFalse(True) def testFindVertex(self): u1 = self.g.addVertex(Vertex('u1', 'A', 1)) self.assertIsNone(self.g.findVertex('X')) self.assertIsNone(self.g.findVertex('A2')) self.assertEqual(self.g.findVertex('A1'), u1) def testHasEdgeBetweenVertices(self): self.g.addEdge(Vertex('u0', 'A'), Vertex('u1', 'B')) self.g.addEdge('u1', Vertex('u2', 'C')) self.assertTrue(self.g.hasEdgeBetweenVertices('u0', 'u1')) self.assertFalse(self.g.hasEdgeBetweenVertices('u0', 'u2')) self.assertFalse(self.g.hasEdgeBetweenVertices('XX', 'XX')) self.assertFalse(self.g.hasEdgeBetweenVertices('u0', 'XX')) def testLabelsProperty(self): self.g.addVertex(Vertex('u1', 'A')) self.g.addVertex(Vertex('u2', 'B')) self.g.addVertex(Vertex('u3', 'C')) labels = self.g.labels self.assertEquals(len(labels), 3) self.assertTrue('A' in labels) self.assertTrue('B' in labels) self.assertTrue('C' in labels) def testNamesProperty(self): self.g.addVertex(Vertex('u1', 'A', 1)) self.g.addVertex(Vertex('u2', 'B', 2)) self.g.addVertex(Vertex('u3', 'C', 3)) names = self.g.names self.assertEquals(len(names), 3) self.assertTrue('A1' in names) self.assertTrue('B2' in names) self.assertTrue('C3' in names) def testNumVertices(self): # Empty graph. self.assertEquals(self.g.numVertices, 0) # One vertex. a = self.g.addVertex(Vertex('u1')) self.assertEquals(self.g.numVertices, 1) def testRepr(self): # Test of the output function (__repr__). # No vertices. self.assertEquals(self.g.__repr__(), "digraph {\n\n}") # A self.g.addVertex(Vertex('u1', 'A')) self.assertEquals(self.g.__repr__(), "digraph {\nA_u1\n}") # A->B self.g.addEdge('u1', Vertex('u2', 'B')) self.assertEquals(self.g.__repr__(), "digraph {\nA_u1->B_u2;\n\n}") # A->B->C self.g.addEdge('u2', Vertex('u3', 'C')) self.assertEquals(self.g.__repr__(), "digraph {\nA_u1->B_u2;\nB_u2->C_u3;\n\n}") # A->B->C, A->D self.g.addEdge('u1', Vertex('u4', 'D')) self.assertEquals( self.g.__repr__(), "digraph {\nA_u1->B_u2;\nA_u1->D_u4;\nB_u2->C_u3;\n\n}")
def testDeleteMissingEdges(self): # If lhs has no edges, then there's nothing missing from the rhs. # Nothing is done to the graph. g = Graph() g.addEdge(Vertex('g0', 'A', 1), Vertex('g1', 'B', 1)) lhs = Graph() rhs = Graph() p = Production(lhs,rhs) lhsMapping = {} rhsMapping = {} gen = Generator() self.assertEqual(len(g._edges['g0']), 1) gen._deleteMissingEdges(g, p, lhsMapping, rhsMapping) self.assertEqual(len(g._edges['g0']), 1) # lhs has an edge, but it also appears on the rhs. Nothing done. g = Graph() g.addEdge(Vertex('g0', 'A', 1), Vertex('g1', 'B', 1)) lhs = Graph() lhs.addEdge(Vertex('l0', 'A', 1), Vertex('l1', 'B', 1)) rhs = Graph() rhs.addEdge(Vertex('r0', 'A', 1), Vertex('r1', 'B', 1)) p = Production(lhs,rhs) lhsMapping = {'l0':'g0', 'l1':'g1'} rhsMapping = {'r0':'g0', 'r1':'g1'} gen = Generator() self.assertEqual(len(g._edges['g0']), 1) gen._deleteMissingEdges(g, p, lhsMapping, rhsMapping) self.assertEqual(len(g._edges['g0']), 1) # lhs has an edge, but the starting vertex doesn't appear in the RHS. # The edge should be deleted from the graph. g = Graph() g.addEdge(Vertex('g0', 'A', 1), Vertex('g1', 'B', 1)) lhs = Graph() lhs.addEdge(Vertex('l0', 'A', 1), Vertex('l1', 'B', 1)) rhs = Graph() rhs.addVertex(Vertex('r0', 'A', 2)) rhs.addVertex(Vertex('r1', 'B', 1)) p = Production(lhs,rhs) lhsMapping = {'l0':'g0', 'l1':'g1'} rhsMapping = {'r1':'g1'} gen = Generator() self.assertEqual(len(g._edges['g0']), 1) gen._deleteMissingEdges(g, p, lhsMapping, rhsMapping) self.assertEqual(len(g._edges['g0']), 0) # lhs has an edge, but the ending vertex doesn't appear in the RHS. # The edge should be deleted from the graph. g = Graph() g.addEdge(Vertex('g0', 'A', 1), Vertex('g1', 'B', 1)) lhs = Graph() lhs.addEdge(Vertex('l0', 'A', 1), Vertex('l1', 'B', 1)) rhs = Graph() rhs.addVertex(Vertex('r0', 'A', 1)) rhs.addVertex(Vertex('r1', 'B', 2)) p = Production(lhs,rhs) lhsMapping = {'l0':'g0', 'l1':'g1'} rhsMapping = {'r0':'g0'} gen = Generator() self.assertEqual(len(g._edges['g0']), 1) gen._deleteMissingEdges(g, p, lhsMapping, rhsMapping) self.assertEqual(len(g._edges['g0']), 0) # lhs has an edge, but it's gone from the rhs. It should be deleted # from the graph. g = Graph() g.addEdge(Vertex('g0', 'A', 1), Vertex('g1', 'B', 1)) lhs = Graph() lhs.addEdge(Vertex('l0', 'A', 1), Vertex('l1', 'B', 1)) rhs = Graph() rhs.addVertex(Vertex('r0', 'A', 1)) rhs.addVertex(Vertex('r1', 'B', 1)) p = Production(lhs,rhs) lhsMapping = {'l0':'g0', 'l1':'g1'} rhsMapping = {'r0':'g0', 'r1':'g1'} gen = Generator() self.assertEqual(len(g._edges['g0']), 1) gen._deleteMissingEdges(g, p, lhsMapping, rhsMapping) self.assertEqual(len(g._edges['g0']), 0)