From 4ad9381e0310010a94a03ea1b6cc3f383ba50ab1 Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Sat, 17 Nov 2007 12:37:13 +0000 Subject: [PATCH] More tests for merging. Don't merge groups if their requirements differ. Use the closest acceptable group, not just any acceptable one. git-svn-id: file:///home/talex/Backups/sf.net/Subversion/zero-install/trunk/0publish@2082 9f8c893c-44ee-0310-b757-c8ca8341c71e --- merge.py | 52 +++++++++++++++++++++++++++++++++-------- tests/testlocal.py | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 106 insertions(+), 14 deletions(-) diff --git a/merge.py b/merge.py index 7ec132f..40d1532 100644 --- a/merge.py +++ b/merge.py @@ -5,10 +5,10 @@ from zeroinstall.injector import model, reader from logging import info import xmltools -def childNodes(parent, namespaceURI, localName = None): +def childNodes(parent, namespaceURI = None, localName = None): for x in parent.childNodes: if x.nodeType != Node.ELEMENT_NODE: continue - if x.namespaceURI != namespaceURI: continue + if namespaceURI is not None and x.namespaceURI != namespaceURI: continue if localName is None or x.localName == localName: yield x @@ -47,18 +47,49 @@ def find_groups(parent): yield x for y in find_groups(x): yield y + +def nodesEqual(a, b): + assert a.nodeType == Node.ELEMENT_NODE + assert b.nodeType == Node.ELEMENT_NODE + + if a.namespaceURI != b.namespaceURI: + return False + + if a.nodeName != b.nodeName: + return False -def is_subset(group, impl): + a_attrs = set(["%s %s" % (name, value) for name, value in a.attributes.itemsNS()]) + b_attrs = set(["%s %s" % (name, value) for name, value in b.attributes.itemsNS()]) + + if a_attrs != b_attrs: + #print "%s != %s" % (a_attrs, b_attrs) + return False + + a_children = list(childNodes(a)) + b_children = list(childNodes(b)) + + if len(a_children) != len(b_children): + return False + + for a_child, b_child in zip(a_children, b_children): + if not nodesEqual(a_child, b_child): + return False + + return True + +def score_subset(group, impl): + """Returns (is_subset, badness)""" for key in group.attribs: if key not in impl.attribs.keys(): - print "BAD", key - return False # Group sets an attribute the impl doesn't want + #print "BAD", key + return (0,) # Group sets an attribute the impl doesn't want for g_req in group.requires: for i_req in impl.requires: if nodesEqual(g_req, i_req): break else: - return False # Group adds a requires that the impl doesn't want - return True + return (0,) # Group adds a requires that the impl doesn't want + # Score result so we get groups that have all the same requires first, then ones with all the same attribs + return (1, len(group.requires), len(group.attribs)) def merge(data, local): local_doc = minidom.parse(local) @@ -76,12 +107,13 @@ def merge(data, local): # - A subset of the new implementation's attributes (names, not values) # Choose the most compatible (the root counts as a minimally compatible group) - best_group = (0, master_doc.documentElement) + best_group = ((1, 0, 0), master_doc.documentElement) # (score, element) for group in find_groups(master_doc.documentElement): group_context = Context(group) - if is_subset(group_context, new_impl_context): - best_group = (0, group) + score = score_subset(group_context, new_impl_context) + if score > best_group[0]: + best_group = (score, group) group = best_group[1] group_context = Context(group) diff --git a/tests/testlocal.py b/tests/testlocal.py index cbae914..171b5de 100755 --- a/tests/testlocal.py +++ b/tests/testlocal.py @@ -16,7 +16,7 @@ header = """ test for testing This is for testing. -""" + """ footer = """ """ @@ -60,18 +60,78 @@ class TestValidator(unittest.TestCase): assert len(master.implementations) == 2 def testMergeGroup(self): - master = parse(tap(merge.merge(header + "" + footer, local_file))) + master = parse(tap(merge.merge(header + "\n \n " + footer, local_file))) assert master.uri == 'http://test/hello.xml', master assert len(master.implementations) == 2 assert master.implementations['sha1=002'].requires == [] def testMergeLocalReq(self): - master = parse(tap(merge.merge(header + "" + footer, local_file_req))) + master = parse(tap(merge.merge(header + "\n \n " + footer, local_file_req))) + assert master.uri == 'http://test/hello.xml', master + assert len(master.implementations) == 2 + deps = master.implementations['sha1=003'].requires + assert len(deps) == 1 + assert deps[0].interface == 'http://foo', deps[0] + + def testNotSubset(self): + master = parse(tap(merge.merge(header + "\n \n " + footer, local_file))) assert master.uri == 'http://test/hello.xml', master assert len(master.implementations) == 2 + assert master.implementations['sha1=123'].metadata.get('a', None) == 'a' + assert master.implementations['sha1=002'].metadata.get('a', None) == None + + master = parse(tap(merge.merge(header + """\n + + + + + + + + + + """ + footer, local_file_req))) + assert len(master.implementations['sha1=001'].requires[0].restrictions) == 1 + assert len(master.implementations['sha1=003'].requires[0].restrictions) == 0 + + assert master.implementations['sha1=004'].requires[0].metadata.get('meta', None) == 'foo' + assert master.implementations['sha1=003'].requires[0].metadata.get('meta', None) == None + + def testMergeBest(self): + master_xml = tap(merge.merge(header + """\n + + + + + + + """ + footer, local_file_req)) + master = parse(master_xml) + assert master.uri == 'http://test/hello.xml', master + assert len(master.implementations) == 3 + deps = master.implementations['sha1=003'].requires + assert len(deps) == 1 + assert deps[0].interface == 'http://foo', deps[0] + + assert len(minidom.parseString(master_xml).documentElement.getElementsByTagNameNS(XMLNS_IFACE, 'group')) == 2 + + # Again, but with the groups the other way around + master_xml = tap(merge.merge(header + """\n + + + + + + + """ + footer, local_file_req)) + master = parse(master_xml) + assert master.uri == 'http://test/hello.xml', master + assert len(master.implementations) == 3 deps = master.implementations['sha1=003'].requires assert len(deps) == 1 - #assert deps[0].interface == 'http://foo', deps[0] + assert deps[0].interface == 'http://foo', deps[0] + + assert len(minidom.parseString(master_xml).documentElement.getElementsByTagNameNS(XMLNS_IFACE, 'group')) == 2 def testLocalContext(self): def get_context(xml_frag): -- 2.11.4.GIT