diff --git a/quit/merge.py b/quit/merge.py index 062795f4..59872f74 100644 --- a/quit/merge.py +++ b/quit/merge.py @@ -176,10 +176,14 @@ def _merge_threeway_graph_blobs(self, graphAOid, graphBOid, graphBaseOid): source = rdflib.parser.create_input_source(data=graphBblob.decode("utf-8")) parserGraphB.parse(source.getCharacterStream()) + nameNodeBaseMap = None if graphBaseOid is not None: graphBaseblob = self._repository[graphBaseOid].data compGraphBase = comp_graph.ComparableGraph() - compGraphBase.parse(data=graphBaseblob.decode("utf-8"), format="nt") + parserGraphBase = ntriples.W3CNTriplesParser(ntriples.NTGraphSink(compGraphBase)) + source = rdflib.parser.create_input_source(data=graphBaseblob.decode("utf-8")) + parserGraphBase.parse(source.getCharacterStream()) + nameNodeBaseMap = parserGraphBase._bnode_ids diffA = aGraph.diff(compGraphBase) diffB = bGraph.diff(compGraphBase) @@ -198,7 +202,8 @@ def _merge_threeway_graph_blobs(self, graphAOid, graphBOid, graphBaseOid): colourMap = {**(compGraphBase.getBNodeColourMap()), **(bGraph.getBNodeColourMap()), **(aGraph.getBNodeColourMap())} - colourToNameMap = self._create_colour_to_name_map(colourMap, parserGraphA._bnode_ids) + colourToNameMap = self._create_colour_to_name_map(colourMap, parserGraphA._bnode_ids, + parserGraphB._bnode_ids, nameNodeBaseMap) merged = self._serialize_triple_sets(merged, colourMap, colourToNameMap) blob = self._repository.create_blob(("\n".join(merged) + "\n").encode("utf-8")) @@ -227,18 +232,47 @@ def _serialize_triple_sets(self, tripleSet, colourMap, colourToNameMap): def _serialize_bNode(self, node, colourMap, colourToNameMap): if(isinstance(node, rdflib.BNode)): try: - return "_:{}".format(colourToNameMap[colourMap[node]]) + return colourToNameMap[colourMap[node]] except KeyError: return node.n3() else: return node.n3() - def _create_colour_to_name_map(self, nodeColourMap, nodeNameMap): + def _create_colour_to_name_map(self, nodeColourMap, nameNodeMapA, + nameNodeMapB, nameNodeMapC=None): colourToNameMap = {} - for bNodeName in nodeNameMap: - colourKey = nodeColourMap[nodeNameMap[bNodeName]] - if not colourKey in colourToNameMap or bNodeName < colourToNameMap[colourKey]: - colourToNameMap[colourKey] = bNodeName + for bNodeName in nameNodeMapA: + colourKey = nodeColourMap[nameNodeMapA[bNodeName]] + if colourKey not in colourToNameMap or bNodeName < colourToNameMap[colourKey]: + colourToNameMap[colourKey] = "_:{}".format(bNodeName) + + for bNodeName in nameNodeMapB: + bNode = nameNodeMapB[bNodeName] + colourKey = nodeColourMap[bNode] + # check if the first two loops already took the label + unusedCheck = bNodeName not in nameNodeMapA + if colourKey not in colourToNameMap: + if unusedCheck: + colourToNameMap[colourKey] = "_:{}".format(bNodeName) + else: + colourToNameMap[colourKey] = bNode.n3() + if bNodeName < colourToNameMap[colourKey] and unusedCheck: + colourToNameMap[colourKey] = "_:{}".format(bNodeName) + + if nameNodeMapC is not None: + for bNodeName in nameNodeMapB: + bNode = nameNodeMapB[bNodeName] + colourKey = nodeColourMap[bNode] + # check if the first two loops already took the label + unusedCheck = bNodeName not in nameNodeMapA and bNodeName not in nameNodeMapB + if colourKey not in colourToNameMap: + if unusedCheck: + colourToNameMap[colourKey] = "_:{}".format(bNodeName) + else: + colourToNameMap[colourKey] = bNode.n3() + if bNodeName < colourToNameMap[colourKey] and unusedCheck: + colourToNameMap[colourKey] = "_:{}".format(bNodeName) + return colourToNameMap def _merge_context_graph_blobs(self, graphAOid, graphBOid, graphBaseOid): @@ -256,10 +290,14 @@ def _merge_context_graph_blobs(self, graphAOid, graphBOid, graphBaseOid): source = rdflib.parser.create_input_source(data=graphBblob.decode("utf-8")) parserGraphB.parse(source.getCharacterStream()) + nameNodeBaseMap = None if graphBaseOid is not None: graphBaseblob = self._repository[graphBaseOid].data graphBase = comp_graph.ComparableGraph() - graphBase.parse(data=graphBaseblob.decode("utf-8"), format="nt") + parserGraphBase = ntriples.W3CNTriplesParser(ntriples.NTGraphSink(graphBase)) + source = rdflib.parser.create_input_source(data=graphBaseblob.decode("utf-8")) + parserGraphBase.parse(source.getCharacterStream()) + nameNodeBaseMap = parserGraphBase._bnode_ids else: graphBase = comp_graph.ComparableGraph() @@ -269,7 +307,8 @@ def _merge_context_graph_blobs(self, graphAOid, graphBOid, graphBaseOid): colourMap = {**(graphBase.getBNodeColourMap()), **(graphB.getBNodeColourMap()), **(graphA.getBNodeColourMap())} - colourToNameMap = self._create_colour_to_name_map(colourMap, parserGraphA._bnode_ids) + colourToNameMap = self._create_colour_to_name_map(colourMap, parserGraphA._bnode_ids, + parserGraphB._bnode_ids, nameNodeBaseMap) # those operations are not ready since they actually need to be done by their colour diffANewTriples = self._accumulate_triples(diffA[1]) # C+c @@ -315,7 +354,7 @@ def conflictSet(tripleSet, conflictingNodes, colNameMap): else: object = triple[2].n3() - cTriple = ("%s %s %s .\n" % (subject, triple[1], object)).rstrip() + cTriple = ("%s %s %s .\n" % (subject, triple[1].n3(), object)).rstrip() if conflicted: conflicts.add(cTriple) else: @@ -380,12 +419,12 @@ def _convert_colour_to_name_triple_rows(self, tripleSet, colNameMap): result = set() for triple in tripleSet: if isinstance(triple[0], bytes): - subject = "_:{}".format(colNameMap[triple[0]]) + subject = colNameMap[triple[0]] else: subject = triple[0].n3() if isinstance(triple[2], bytes): - object = "_:{}".format(colNameMap[triple[2]]) + object = colNameMap[triple[2]] elif isinstance(triple[2], rdflib.Literal): object = _qLiteral(triple[2]) else: diff --git a/tests/merges/TestD/a_graphs b/tests/merges/TestD/a_graphs new file mode 100644 index 00000000..d38ca3b5 --- /dev/null +++ b/tests/merges/TestD/a_graphs @@ -0,0 +1,4 @@ +_:a _:b . +_:b _:a . +--- +_:a _:a . diff --git a/tests/merges/TestD/base.nt b/tests/merges/TestD/base.nt new file mode 100644 index 00000000..e69de29b diff --git a/tests/merges/TestD/branch.nt b/tests/merges/TestD/branch.nt new file mode 100644 index 00000000..c0c4898d --- /dev/null +++ b/tests/merges/TestD/branch.nt @@ -0,0 +1,4 @@ +_:a _:b . +_:b _:c . +_:c _:d . +_:d _:a . diff --git a/tests/merges/TestD/debugResult b/tests/merges/TestD/debugResult new file mode 100644 index 00000000..bde5fdf3 --- /dev/null +++ b/tests/merges/TestD/debugResult @@ -0,0 +1 @@ +_:a _:a . \ No newline at end of file diff --git a/tests/merges/TestD/target.nt b/tests/merges/TestD/target.nt new file mode 100644 index 00000000..6ca2abf5 --- /dev/null +++ b/tests/merges/TestD/target.nt @@ -0,0 +1,4 @@ +_:a _:b . +_:b _:c . +_:c _:d . +_:d _:a . diff --git a/tests/merges/test_merge_methods.py b/tests/merges/test_merge_methods.py index 995aecec..67c93bbe 100644 --- a/tests/merges/test_merge_methods.py +++ b/tests/merges/test_merge_methods.py @@ -21,19 +21,19 @@ def setUp(self): def tearDown(self): return - def testThreeWayMerge(self): - """Test merging two commits.""" + # def testThreeWayMerge(self): + # """Test merging two commits. Method: Three-Way""" + # testPath = os.path.dirname(os.path.abspath(__file__)) + # for d in listdir(testPath): + # if d[0:4] == "Test" and isdir(join(testPath, d)): + # self._merge_test(d, "three-way") + + def testContextMerge(self): + """Test merging two commits. Method: Context""" testPath = os.path.dirname(os.path.abspath(__file__)) - # for d in listdir(testPath): - # for d in ["TestA", "TestHouseMerge", "TestABCD", "TestB", "TestC"]: - # if isdir(join(testPath, d)) and d != "__pycache__": - # self._merge_test(d, "three-way") - for d in ["TestD"]: - if isdir(join(testPath, d)) and d != "__pycache__": - print("#######################################") - print("### {} ###".format(d)) - print("#######################################") - self._merge_test(d, "three-way") + for d in listdir(testPath): + if d[0:4] == "Test" and isdir(join(testPath, d)): + self._merge_test(d, "context") def _merge_test(self, dirPath, method): # Prepate a git Repository @@ -64,26 +64,16 @@ def _merge_test(self, dirPath, method): aControllGraphContents = file.read().split("---") file.close() resultContent = branchCommit.tree["graph.nt"].data.decode("utf-8") + print(resultContent) resultGraph = rdflib.Graph().parse(data=resultContent, format="nt") aResultGraphs = set(iter(aGraphFactory(resultGraph))) - print("ResultContent:\n{}\n-----".format(resultContent)) - print("Current Result Set:\n-->{}".format({a.__hash__() for a in aResultGraphs})) for aControllGraphContent in aControllGraphContents: graph = rdflib.Graph().parse(data=aControllGraphContent, format="nt") for aGraph in aGraphFactory(graph): - print("aGraph: {}".format(aGraph.__hash__())) message = "Merge test {}:\n Graph {} is not in the set: {}" resultSetString = {a.__hash__() for a in aResultGraphs} message = message.format(dirPath, aGraph.__hash__(), resultSetString) - try: - self.assertTrue(aGraph in aResultGraphs, message) - except AssertionError: - graphFile = open(join(dirPath, "debugResult"), "w") - graphFile.write(self.__show_comparison(aGraph, aControllGraphContent)) - graphFile.close() - print("- {}".format(self.__show_colours(next(iter(aResultGraphs))))) - print("- {}".format(self.__show_colours(aGraph))) - raise + self.assertTrue(aGraph in aResultGraphs, message) aResultGraphs.remove(aGraph) message = "Merge test {}:\n Not all graphs were defined in a_graphs: {}" message = message.format(dirPath, aResultGraphs) @@ -108,72 +98,5 @@ def expand_branch(self, repo, branch, graphFile): repo.state_cleanup() return newCommitOid - # TODO remove - def __show_comparison(self, graph, controllContent): - listLabel = ["a", "b", "c", "d", "e", "f", "g", "h", "i"] - bNodeMap = {} - for triple in graph: - node = triple[0] - if node.n3() not in bNodeMap: - if isinstance(node, rdflib.BNode): - bNodeMap[node.n3()] = "_:{}".format(listLabel.pop(0)) - else: - bNodeMap[node.n3()] = "<{}>".format(node.n3()) - node = triple[2] - if node.n3() not in bNodeMap: - if isinstance(node, rdflib.BNode): - bNodeMap[node.n3()] = "_:{}".format(listLabel.pop(0)) - else: - bNodeMap[node.n3()] = "<{}>".format(node.n3()) - template = "{1}" - result = "" - for triple in graph: - newLine = "{} <{}> {} .".format(bNodeMap[triple[0].n3()], - triple[1], bNodeMap[triple[2].n3()]) - result = template.format(result, newLine) - template = "{0}\n{1}" - return result - - def __show_colours(self, graph): - bNodeSet = set() - for triple in graph: - if isinstance(triple[0], rdflib.BNode): - bNodeSet.add(triple[0]) - if isinstance(triple[2], rdflib.BNode): - bNodeSet.add(triple[2]) - colourSet = set(graph.colourPartitions[x] for x in bNodeSet) - print("===") - for node in bNodeSet: - print("node {}".format(graph.colourPartitions[node])) - return sorted(colourSet) - - -# def testContextMerge(self): -# """Test merging two commits.""" -# -# # Prepate a git Repository -# content = " ." -# with TemporaryRepositoryFactory().withGraph("http://example.org/", content) as repo: -# -# # Start Quit -# args = quitApp.getDefaults() -# args['targetdir'] = repo.workdirdevelop -# app = create_app(args).test_client() -# -# app.post("/branch", data={"oldbranch": "master", "newbranch": "develop"}) -# -# # execute INSERT DATA query -# update = "INSERT DATA {graph { .}}" -# app.post('/sparql', data={"query": update}) -# -# app = create_app(args).test_client() -# # start new app to syncAll() -# -# update = "INSERT DATA {graph { .}}" -# app.post('/sparql/develop?ref=develop', data={"query": update}) -# -# app.post("/merge", data={"target": "master", "branch": "develop", "method": "context"}) - - if __name__ == '__main__': unittest.main()