162 lines
4.5 KiB
Python
162 lines
4.5 KiB
Python
try:
|
|
import doctest
|
|
doctest.OutputChecker
|
|
except (AttributeError, ImportError): # Python < 2.4
|
|
import util.doctest24 as doctest
|
|
try:
|
|
import xml.etree.ElementTree as ET
|
|
except ImportError:
|
|
import elementtree.ElementTree as ET
|
|
from xml.parsers.expat import ExpatError as XMLParseError
|
|
|
|
RealOutputChecker = doctest.OutputChecker
|
|
|
|
|
|
def debug(*msg):
|
|
import sys
|
|
print >> sys.stderr, ' '.join(map(str, msg))
|
|
|
|
|
|
class HTMLOutputChecker(RealOutputChecker):
|
|
|
|
def check_output(self, want, got, optionflags):
|
|
normal = RealOutputChecker.check_output(self, want, got, optionflags)
|
|
if normal or not got:
|
|
return normal
|
|
try:
|
|
want_xml = make_xml(want)
|
|
except XMLParseError:
|
|
pass
|
|
else:
|
|
try:
|
|
got_xml = make_xml(got)
|
|
except XMLParseError:
|
|
pass
|
|
else:
|
|
if xml_compare(want_xml, got_xml):
|
|
return True
|
|
return False
|
|
|
|
def output_difference(self, example, got, optionflags):
|
|
actual = RealOutputChecker.output_difference(
|
|
self, example, got, optionflags)
|
|
want_xml = got_xml = None
|
|
try:
|
|
want_xml = make_xml(example.want)
|
|
want_norm = make_string(want_xml)
|
|
except XMLParseError as e:
|
|
if example.want.startswith('<'):
|
|
want_norm = '(bad XML: %s)' % e
|
|
# '<xml>%s</xml>' % example.want
|
|
else:
|
|
return actual
|
|
try:
|
|
got_xml = make_xml(got)
|
|
got_norm = make_string(got_xml)
|
|
except XMLParseError as e:
|
|
if example.want.startswith('<'):
|
|
got_norm = '(bad XML: %s)' % e
|
|
else:
|
|
return actual
|
|
s = '%s\nXML Wanted: %s\nXML Got : %s\n' % (
|
|
actual, want_norm, got_norm)
|
|
if got_xml and want_xml:
|
|
result = []
|
|
xml_compare(want_xml, got_xml, result.append)
|
|
s += 'Difference report:\n%s\n' % '\n'.join(result)
|
|
return s
|
|
|
|
|
|
def xml_sort(children):
|
|
tcl1 = {}
|
|
#idx = 0
|
|
|
|
for child in children:
|
|
if 'name' in child.attrib:
|
|
key = child.attrib['name']
|
|
else:
|
|
key = child.tag
|
|
if key not in tcl1:
|
|
tcl1[key] = []
|
|
tcl1[key].append(child)
|
|
cl1_keys = list(tcl1.keys())
|
|
cl1_keys.sort()
|
|
cl1 = []
|
|
for key in cl1_keys:
|
|
cl1.extend(tcl1[key])
|
|
return cl1
|
|
|
|
def xml_compare(x1, x2):
|
|
if x1.tag != x2.tag:
|
|
print ('Tags do not match: %s and %s' % (x1.tag, x2.tag))
|
|
return False
|
|
for name, value in x1.attrib.items():
|
|
if x2.attrib.get(name) != value:
|
|
print ('Attributes do not match: %s=%r, %s=%r'
|
|
% (name, value, name, x2.attrib.get(name)))
|
|
return False
|
|
for name in x2.attrib:
|
|
if name not in x1.attrib:
|
|
print ('x2 has an attribute x1 is missing: %s'
|
|
% name)
|
|
return False
|
|
if not text_compare(x1.text, x2.text):
|
|
print ('text: %r != %r' % (x1.text, x2.text))
|
|
return False
|
|
if not text_compare(x1.tail, x2.tail):
|
|
print ('tail: %r != %r' % (x1.tail, x2.tail))
|
|
return False
|
|
|
|
cl1 = xml_sort(x1.getchildren())
|
|
cl2 = xml_sort(x2.getchildren())
|
|
|
|
if len(cl1) != len(cl2):
|
|
cl1_tags = []
|
|
for c in cl1:
|
|
cl1_tags.append(c.tag)
|
|
cl2_tags = []
|
|
for c in cl2:
|
|
cl2_tags.append(c.tag)
|
|
print ('children length differs, %i != %i (%s != %s)'
|
|
% (len(cl1), len(cl2), cl1_tags, cl2_tags))
|
|
return False
|
|
i = 0
|
|
for c1, c2 in zip(cl1, cl2):
|
|
i += 1
|
|
if not xml_compare(c1, c2):
|
|
if 'name' in c1.attrib:
|
|
name = c1.attrib['name']
|
|
else:
|
|
name = i
|
|
print ('in tag "%s" with name "%s"'
|
|
% (c1.tag, name))
|
|
return False
|
|
return True
|
|
|
|
|
|
def text_compare(t1, t2):
|
|
if not t1 and not t2:
|
|
return True
|
|
if t1 == '*' or t2 == '*':
|
|
return True
|
|
return (t1 or '').strip() == (t2 or '').strip()
|
|
|
|
|
|
def make_xml(s):
|
|
return ET.XML('<xml>%s</xml>' % s)
|
|
|
|
|
|
def make_string(xml):
|
|
if isinstance(xml, (str, unicode)):
|
|
xml = make_xml(xml)
|
|
s = ET.tostring(xml)
|
|
if s == '<xml />':
|
|
return ''
|
|
assert s.startswith('<xml>') and s.endswith('</xml>'), repr(s)
|
|
return s[5:-6]
|
|
|
|
|
|
def install():
|
|
doctest.OutputChecker = HTMLOutputChecker
|
|
|