[CalendarServer-changes] [14018] CalDAVTester/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Mon Sep 29 08:51:07 PDT 2014
Revision: 14018
http://trac.calendarserver.org//changeset/14018
Author: cdaboo at apple.com
Date: 2014-09-29 08:51:07 -0700 (Mon, 29 Sep 2014)
Log Message:
-----------
Add more sophisticated xpath testing to allow tests for sibling elements that match specific values.
Modified Paths:
--------------
CalDAVTester/trunk/README.txt
CalDAVTester/trunk/verifiers/xmlElementMatch.py
Modified: CalDAVTester/trunk/README.txt
===================================================================
--- CalDAVTester/trunk/README.txt 2014-09-29 09:29:56 UTC (rev 14017)
+++ CalDAVTester/trunk/README.txt 2014-09-29 15:51:07 UTC (rev 14018)
@@ -830,6 +830,12 @@
[json] - node contains valid JSON data.
[icalendar] - node contains valid iCalendare data.
+ Each path segment can now have its own test and "../" can be used to move up to
+ the parent. This allows testing for an element matching specific content plus
+ its sibling matching other specific content. e.g., "/{D}A/{D}B[=b]/../{D}C[=c]
+ which checks for an element {D}A with two child elements {D}B and {D}C each
+ with a specific value.
+
Argument: 'parent'
ElementTree style path for an XML element to use as the root for any
subsequent "exists" or "notexists" tests. This is useful for targeting
Modified: CalDAVTester/trunk/verifiers/xmlElementMatch.py
===================================================================
--- CalDAVTester/trunk/verifiers/xmlElementMatch.py 2014-09-29 09:29:56 UTC (rev 14017)
+++ CalDAVTester/trunk/verifiers/xmlElementMatch.py 2014-09-29 15:51:07 UTC (rev 14018)
@@ -67,12 +67,12 @@
resulttxt = ""
for path in exists:
- matched, txt = self.matchPath(root, path)
+ matched, txt = self.matchNode(root, path)
result &= matched
resulttxt += txt
for path in notexists:
- matched, _ignore_txt = self.matchPath(root, path)
+ matched, _ignore_txt = self.matchNode(root, path)
if matched:
resulttxt += " Items returned in XML for %s\n" % (path,)
result = False
@@ -153,26 +153,93 @@
return results
- def matchPath(self, root, path):
+ @classmethod
+ def testNode(cls, node, node_path, test):
+ result = None
+ if test[0] == '@':
+ if '=' in test:
+ attr, value = test[1:].split('=')
+ value = value[1:-1]
+ else:
+ attr = test[1:]
+ value = None
+ if attr not in node.keys():
+ result = " Missing attribute returned in XML for %s\n" % (node_path,)
+ if value is not None and node.get(attr) != value:
+ result = " Incorrect attribute value returned in XML for %s\n" % (node_path,)
+ elif test[0] == '=':
+ if node.text != test[1:]:
+ result = " Incorrect value returned in XML for %s\n" % (node_path,)
+ elif test[0] == '!':
+ if node.text == test[1:]:
+ result = " Incorrect value returned in XML for %s\n" % (node_path,)
+ elif test[0] == '*':
+ if node.text is None or node.text.find(test[1:]) == -1:
+ result = " Incorrect value returned in XML for %s\n" % (node_path,)
+ elif test[0] == '$':
+ if node.text is None or node.text.find(test[1:]) != -1:
+ result = " Incorrect value returned in XML for %s\n" % (node_path,)
+ elif test[0] == '+':
+ if node.text is None or not node.text.startswith(test[1:]):
+ result = " Incorrect value returned in XML for %s\n" % (node_path,)
+ elif test[0] == '^':
+ if "=" in test:
+ element, value = test[1:].split("=", 1)
+ else:
+ element = test[1:]
+ value = None
+ for child in node.getchildren():
+ if child.tag == element and (value is None or child.text == value):
+ break
+ else:
+ result = " Missing child returned in XML for %s\n" % (node_path,)
+ # Try to parse as iCalendar
+ elif test == 'icalendar':
+ try:
+ Calendar.parseText(node.text)
+ except:
+ result = " Incorrect value returned in iCalendar for %s\n" % (node_path,)
+
+ # Try to parse as JSON
+ elif test == 'json':
+ try:
+ json.loads(node.text)
+ except:
+ result = " Incorrect value returned in XML for %s\n" % (node_path,)
+ return result
+
+
+ @classmethod
+ def matchNode(cls, root, xpath, parent_map=None, title=None):
+
+ if title is None:
+ title = xpath
result = True
resulttxt = ""
- if '[' in path:
- actual_path, tests = path.split('[', 1)
+ # Find the first test in the xpath
+ if '[' in xpath:
+ actual_xpath, tests = xpath.split('[', 1)
else:
- actual_path = path
+ actual_xpath = xpath
tests = None
- # Handle absolute root element
- if actual_path[0] == '/':
- actual_path = actual_path[1:]
- r = re.search("(\{[^\}]+\}[^/]+)(.*)", actual_path)
+ if parent_map is None:
+ parent_map = dict((c, p) for p in root.getiterator() for c in p)
+
+ # Handle parents
+ if actual_xpath.startswith("../"):
+ root = parent_map[root]
+ actual_xpath = "./" + actual_xpath[3:]
+
+ # Handle absolute root element and find all matching nodes
+ r = re.search("(/\{[^\}]+\}[^/]+|\.)(.*)", actual_xpath)
if r.group(2):
root_path = r.group(1)
child_path = r.group(2)[1:]
- if root.tag != root_path:
- resulttxt += " Items not returned in XML for %s\n" % (path,)
+ if root_path != "." and root.tag != root_path[1:]:
+ resulttxt += " Items not returned in XML for %s\n" % (title,)
result = False
return result, resulttxt
nodes = root.findall(child_path)
@@ -180,76 +247,58 @@
nodes = (root,)
if len(nodes) == 0:
- resulttxt += " Items not returned in XML for %s\n" % (path,)
+ resulttxt += " Items not returned in XML for %s\n" % (title,)
result = False
return result, resulttxt
+
if tests:
- tests = [item[:-1] for item in tests.split('[')]
- for test in tests:
+ # Split the tests into tests plus additional path
+ pos = tests.find("]/")
+ if pos != -1:
+ node_tests = tests[:pos + 1]
+ next_path = tests[pos + 1:]
+ else:
+ node_tests = tests
+ next_path = None
+
+ node_tests = [item[:-1] for item in node_tests.split('[')]
+ for test in node_tests:
for node in nodes:
+ testresult = cls.testNode(node, title, test)
+ if testresult is None:
+ if next_path:
+ next_result, testresult = cls.matchNode(node, next_path[1:], parent_map, title)
+ if next_result:
+ break
+ else:
+ break
- def _doTest():
- result = None
- if test[0] == '@':
- if '=' in test:
- attr, value = test[1:].split('=')
- value = value[1:-1]
- else:
- attr = test[1:]
- value = None
- if attr not in node.keys():
- result = " Missing attribute returned in XML for %s\n" % (path,)
- if value is not None and node.get(attr) != value:
- result = " Incorrect attribute value returned in XML for %s\n" % (path,)
- elif test[0] == '=':
- if node.text != test[1:]:
- result = " Incorrect value returned in XML for %s\n" % (path,)
- elif test[0] == '!':
- if node.text == test[1:]:
- result = " Incorrect value returned in XML for %s\n" % (path,)
- elif test[0] == '*':
- if node.text is None or node.text.find(test[1:]) == -1:
- result = " Incorrect value returned in XML for %s\n" % (path,)
- elif test[0] == '$':
- if node.text is None or node.text.find(test[1:]) != -1:
- result = " Incorrect value returned in XML for %s\n" % (path,)
- elif test[0] == '+':
- if node.text is None or not node.text.startswith(test[1:]):
- result = " Incorrect value returned in XML for %s\n" % (path,)
- elif test[0] == '^':
- if "=" in test:
- element, value = test[1:].split("=", 1)
- else:
- element = test[1:]
- value = None
- for child in node.getchildren():
- if child.tag == element and (value is None or child.text == value):
- break
- else:
- result = " Missing child returned in XML for %s\n" % (path,)
-
- # Try to parse as iCalendar
- elif test == 'icalendar':
- try:
- Calendar.parseText(node.text)
- except:
- result = " Incorrect value returned in iCalendar for %s\n" % (path,)
-
- # Try to parse as JSON
- elif test == 'json':
- try:
- json.loads(node.text)
- except:
- result = " Incorrect value returned in XML for %s\n" % (path,)
- return result
-
- testresult = _doTest()
- if testresult is None:
- break
- if testresult is not None:
+ if testresult:
resulttxt += testresult
result = False
break
return result, resulttxt
+
+
+# Tests
+if __name__ == '__main__':
+ xmldata = """
+<D:test xmlns:D="DAV:">
+ <D:a>A</D:a>
+ <D:b>
+ <D:c>C</D:c>
+ <D:d>D</D:d>
+ </D:b>
+ <D:b>
+ <D:c>C</D:c>
+ <D:d>F</D:d>
+ </D:b>
+</D:test>
+"""
+
+ node = ElementTree(file=StringIO.StringIO(xmldata)).getroot()
+
+ assert Verifier.matchNode(node, "/{DAV:}test/{DAV:}b/{DAV:}c[=C]/../{DAV:}d[=D]")[0]
+ assert not Verifier.matchNode(node, "/{DAV:}test/{DAV:}b/{DAV:}c[=C]/../{DAV:}d[=E]")[0]
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20140929/063ed9c7/attachment-0001.html>
More information about the calendarserver-changes
mailing list