mirror of
https://github.com/openSUSE/osc.git
synced 2025-01-13 17:16:23 +01:00
tests: Ignore the ordering of attributes in XML documents
Old xml.etree.cElementTree versions (python2) reorder the attributes while recent xml.etree.cElementTree versions (python3) keep the document order. Example: python3: >>> ET.tostring(ET.fromstring('<foo y="foo" x="bar"/>')) b'<foo y="foo" x="bar" />' >>> python2: >>> ET.tostring(ET.fromstring('<foo y="foo" x="bar"/>')) '<foo x="bar" y="foo" />' >>> So far, the testsuite compared two serialized XML documents via a simple string comparison. For instance via, self.assertEqual(actual_serialized_xml, expected_serialized_xml) where the expected_serialized_xml is, for instance, a hardcoded str. Obviously, this would only work for python2 or python3. In order to support both python versions, we first parse both XML documents and then compare the corresponding trees (this is OK because we do not care about comments etc.). A related issue is the way how the testsuite compares data that is "send" to the API. So far, this was a plain bytes comparison. Again, this won't work in case of XML documents (see above). Moreover, we have currently no notion to "indicate" that the transmitted data is an XML document. As a workaround, we keep the plain bytes comparison and in case it fails, we try an xml comparison (see above) as a last resort. Strictly speaking, this is "wrong" (there might be cases (in the future) where we want to ensure that the transmitted XML data is bit identical to a fixture file) but a reasonable comprise for now. Fixes: #751 ("[python3.8] Testsuite fails")
This commit is contained in:
parent
27ef627f1d
commit
443e3ef90b
@ -42,6 +42,30 @@ def urlcompare(url, *args):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def xml_equal(actual, exp):
|
||||||
|
try:
|
||||||
|
actual_xml = ET.fromstring(actual)
|
||||||
|
exp_xml = ET.fromstring(exp)
|
||||||
|
except ET.ParseError:
|
||||||
|
return False
|
||||||
|
todo = [(actual_xml, exp_xml)]
|
||||||
|
while todo:
|
||||||
|
actual_xml, exp_xml = todo.pop(0)
|
||||||
|
if actual_xml.tag != exp_xml.tag:
|
||||||
|
return False
|
||||||
|
if actual_xml.attrib != exp_xml.attrib:
|
||||||
|
return False
|
||||||
|
if actual_xml.text != exp_xml.text:
|
||||||
|
return False
|
||||||
|
if actual_xml.tail != exp_xml.tail:
|
||||||
|
return False
|
||||||
|
if len(actual_xml) != len(exp_xml):
|
||||||
|
return False
|
||||||
|
todo.extend(list(zip(actual_xml, exp_xml)))
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class RequestWrongOrder(Exception):
|
class RequestWrongOrder(Exception):
|
||||||
"""raised if an unexpected request is issued to urllib2"""
|
"""raised if an unexpected request is issued to urllib2"""
|
||||||
def __init__(self, url, exp_url, method, exp_method):
|
def __init__(self, url, exp_url, method, exp_method):
|
||||||
@ -98,7 +122,14 @@ class MyHTTPHandler(HTTPHandler):
|
|||||||
if hasattr(data, 'read'):
|
if hasattr(data, 'read'):
|
||||||
data = data.read()
|
data = data.read()
|
||||||
if data != exp:
|
if data != exp:
|
||||||
raise RequestDataMismatch(req.get_full_url(), repr(data), repr(exp))
|
# We do not have a notion to explicitly mark xml content. In case
|
||||||
|
# of xml, we do not care about the exact xml representation (for
|
||||||
|
# now). Hence, if both, data and exp, are xml and are "equal",
|
||||||
|
# everything is fine (for now); otherwise, error out
|
||||||
|
# (of course, this is problematic if we want to ensure that XML
|
||||||
|
# documents are bit identical...)
|
||||||
|
if not xml_equal(data, exp):
|
||||||
|
raise RequestDataMismatch(req.get_full_url(), repr(data), repr(exp))
|
||||||
return self.__get_response(req.get_full_url(), **kwargs)
|
return self.__get_response(req.get_full_url(), **kwargs)
|
||||||
|
|
||||||
def __get_response(self, url, **kwargs):
|
def __get_response(self, url, **kwargs):
|
||||||
@ -192,14 +223,27 @@ class OscTestCase(unittest.TestCase):
|
|||||||
|
|
||||||
def _check_digests(self, fname, *skipfiles):
|
def _check_digests(self, fname, *skipfiles):
|
||||||
fname = os.path.join(self._get_fixtures_dir(), fname)
|
fname = os.path.join(self._get_fixtures_dir(), fname)
|
||||||
self.assertEqual(open(os.path.join('.osc', '_files'), 'r').read(), open(fname, 'r').read())
|
with open(os.path.join('.osc', '_files'), 'r') as f:
|
||||||
root = ET.parse(fname).getroot()
|
files_act = f.read()
|
||||||
|
with open(fname, 'r') as f:
|
||||||
|
files_exp = f.read()
|
||||||
|
self.assertXMLEqual(files_act, files_exp)
|
||||||
|
root = ET.fromstring(files_act)
|
||||||
for i in root.findall('entry'):
|
for i in root.findall('entry'):
|
||||||
if i.get('name') in skipfiles:
|
if i.get('name') in skipfiles:
|
||||||
continue
|
continue
|
||||||
self.assertTrue(os.path.exists(os.path.join('.osc', i.get('name'))))
|
self.assertTrue(os.path.exists(os.path.join('.osc', i.get('name'))))
|
||||||
self.assertEqual(osc.core.dgst(os.path.join('.osc', i.get('name'))), i.get('md5'))
|
self.assertEqual(osc.core.dgst(os.path.join('.osc', i.get('name'))), i.get('md5'))
|
||||||
|
|
||||||
|
def assertXMLEqual(self, act, exp):
|
||||||
|
if xml_equal(act, exp):
|
||||||
|
return
|
||||||
|
# ok, xmls are different, hence, assertEqual is expected to fail
|
||||||
|
# (we just use it in order to get a "nice" error message)
|
||||||
|
self.assertEqual(act, exp)
|
||||||
|
# not reached (unless assertEqual is overridden in an incompatible way)
|
||||||
|
raise RuntimeError('assertEqual assumptions violated')
|
||||||
|
|
||||||
def assertEqualMultiline(self, got, exp):
|
def assertEqualMultiline(self, got, exp):
|
||||||
if (got + exp).find('\n') == -1:
|
if (got + exp).find('\n') == -1:
|
||||||
self.assertEqual(got, exp)
|
self.assertEqual(got, exp)
|
||||||
|
@ -38,7 +38,7 @@ class TestRequest(OscTestCase):
|
|||||||
<target package="bar" project="foobar" />
|
<target package="bar" project="foobar" />
|
||||||
</action>
|
</action>
|
||||||
</request>"""
|
</request>"""
|
||||||
self.assertEqual(exp, r.to_str())
|
self.assertXMLEqual(exp, r.to_str())
|
||||||
|
|
||||||
def test_createsr_with_option(self):
|
def test_createsr_with_option(self):
|
||||||
"""create a submitrequest with option"""
|
"""create a submitrequest with option"""
|
||||||
@ -67,7 +67,7 @@ class TestRequest(OscTestCase):
|
|||||||
</options>
|
</options>
|
||||||
</action>
|
</action>
|
||||||
</request>"""
|
</request>"""
|
||||||
self.assertEqual(exp, r.to_str())
|
self.assertXMLEqual(exp, r.to_str())
|
||||||
|
|
||||||
def test_createsr_missing_tgt_package(self):
|
def test_createsr_missing_tgt_package(self):
|
||||||
"""create a submitrequest with missing target package"""
|
"""create a submitrequest with missing target package"""
|
||||||
@ -88,7 +88,7 @@ class TestRequest(OscTestCase):
|
|||||||
<target project="foobar" />
|
<target project="foobar" />
|
||||||
</action>
|
</action>
|
||||||
</request>"""
|
</request>"""
|
||||||
self.assertEqual(exp, r.to_str())
|
self.assertXMLEqual(exp, r.to_str())
|
||||||
|
|
||||||
def test_createsr_invalid_argument(self):
|
def test_createsr_invalid_argument(self):
|
||||||
"""create a submitrequest with invalid action argument"""
|
"""create a submitrequest with invalid action argument"""
|
||||||
@ -117,7 +117,7 @@ class TestRequest(OscTestCase):
|
|||||||
<person name="user" role="reader" />
|
<person name="user" role="reader" />
|
||||||
</action>
|
</action>
|
||||||
</request>"""
|
</request>"""
|
||||||
self.assertEqual(exp, r.to_str())
|
self.assertXMLEqual(exp, r.to_str())
|
||||||
|
|
||||||
def test_create_add_role_group(self):
|
def test_create_add_role_group(self):
|
||||||
"""create an add_role request (group element)"""
|
"""create an add_role request (group element)"""
|
||||||
@ -138,7 +138,7 @@ class TestRequest(OscTestCase):
|
|||||||
<group name="group" role="reviewer" />
|
<group name="group" role="reviewer" />
|
||||||
</action>
|
</action>
|
||||||
</request>"""
|
</request>"""
|
||||||
self.assertEqual(exp, r.to_str())
|
self.assertXMLEqual(exp, r.to_str())
|
||||||
|
|
||||||
def test_create_add_role_person_group(self):
|
def test_create_add_role_person_group(self):
|
||||||
"""create an add_role request (person+group element)"""
|
"""create an add_role request (person+group element)"""
|
||||||
@ -161,7 +161,7 @@ class TestRequest(OscTestCase):
|
|||||||
<group name="group" role="reviewer" />
|
<group name="group" role="reviewer" />
|
||||||
</action>
|
</action>
|
||||||
</request>"""
|
</request>"""
|
||||||
self.assertEqual(exp, r.to_str())
|
self.assertXMLEqual(exp, r.to_str())
|
||||||
|
|
||||||
def test_create_set_bugowner_project(self):
|
def test_create_set_bugowner_project(self):
|
||||||
"""create a set_bugowner request for a project"""
|
"""create a set_bugowner request for a project"""
|
||||||
@ -179,7 +179,7 @@ class TestRequest(OscTestCase):
|
|||||||
<person name="buguser" />
|
<person name="buguser" />
|
||||||
</action>
|
</action>
|
||||||
</request>"""
|
</request>"""
|
||||||
self.assertEqual(exp, r.to_str())
|
self.assertXMLEqual(exp, r.to_str())
|
||||||
|
|
||||||
def test_create_set_bugowner_package(self):
|
def test_create_set_bugowner_package(self):
|
||||||
"""create a set_bugowner request for a package"""
|
"""create a set_bugowner request for a package"""
|
||||||
@ -197,7 +197,7 @@ class TestRequest(OscTestCase):
|
|||||||
<person name="buguser" />
|
<person name="buguser" />
|
||||||
</action>
|
</action>
|
||||||
</request>"""
|
</request>"""
|
||||||
self.assertEqual(exp, r.to_str())
|
self.assertXMLEqual(exp, r.to_str())
|
||||||
|
|
||||||
def test_create_delete_project(self):
|
def test_create_delete_project(self):
|
||||||
"""create a delete request for a project"""
|
"""create a delete request for a project"""
|
||||||
@ -213,7 +213,7 @@ class TestRequest(OscTestCase):
|
|||||||
<target project="foo" />
|
<target project="foo" />
|
||||||
</action>
|
</action>
|
||||||
</request>"""
|
</request>"""
|
||||||
self.assertEqual(exp, r.to_str())
|
self.assertXMLEqual(exp, r.to_str())
|
||||||
|
|
||||||
def test_create_delete_package(self):
|
def test_create_delete_package(self):
|
||||||
"""create a delete request for a package"""
|
"""create a delete request for a package"""
|
||||||
@ -229,7 +229,7 @@ class TestRequest(OscTestCase):
|
|||||||
<target package="deleteme" project="foo" />
|
<target package="deleteme" project="foo" />
|
||||||
</action>
|
</action>
|
||||||
</request>"""
|
</request>"""
|
||||||
self.assertEqual(exp, r.to_str())
|
self.assertXMLEqual(exp, r.to_str())
|
||||||
|
|
||||||
def test_create_change_devel(self):
|
def test_create_change_devel(self):
|
||||||
"""create a change devel request"""
|
"""create a change devel request"""
|
||||||
@ -248,7 +248,7 @@ class TestRequest(OscTestCase):
|
|||||||
<target package="devpkg" project="devprj" />
|
<target package="devpkg" project="devprj" />
|
||||||
</action>
|
</action>
|
||||||
</request>"""
|
</request>"""
|
||||||
self.assertEqual(exp, r.to_str())
|
self.assertXMLEqual(exp, r.to_str())
|
||||||
|
|
||||||
def test_action_from_xml1(self):
|
def test_action_from_xml1(self):
|
||||||
"""create action from xml"""
|
"""create action from xml"""
|
||||||
@ -266,7 +266,7 @@ class TestRequest(OscTestCase):
|
|||||||
self.assertEqual(action.person_role, 'reader')
|
self.assertEqual(action.person_role, 'reader')
|
||||||
self.assertEqual(action.group_name, 'group')
|
self.assertEqual(action.group_name, 'group')
|
||||||
self.assertEqual(action.group_role, 'reviewer')
|
self.assertEqual(action.group_role, 'reviewer')
|
||||||
self.assertEqual(xml, action.to_str())
|
self.assertXMLEqual(xml, action.to_str())
|
||||||
|
|
||||||
def test_action_from_xml2(self):
|
def test_action_from_xml2(self):
|
||||||
"""create action from xml"""
|
"""create action from xml"""
|
||||||
@ -288,7 +288,7 @@ class TestRequest(OscTestCase):
|
|||||||
self.assertEqual(action.opt_sourceupdate, 'cleanup')
|
self.assertEqual(action.opt_sourceupdate, 'cleanup')
|
||||||
self.assertEqual(action.opt_updatelink, '1')
|
self.assertEqual(action.opt_updatelink, '1')
|
||||||
self.assertTrue(action.src_rev is None)
|
self.assertTrue(action.src_rev is None)
|
||||||
self.assertEqual(xml, action.to_str())
|
self.assertXMLEqual(xml, action.to_str())
|
||||||
|
|
||||||
def test_action_from_xml3(self):
|
def test_action_from_xml3(self):
|
||||||
"""create action from xml (with acceptinfo element)"""
|
"""create action from xml (with acceptinfo element)"""
|
||||||
@ -312,7 +312,7 @@ class TestRequest(OscTestCase):
|
|||||||
self.assertEqual(action.acceptinfo_xsrcmd5, 'ffffffffffffffffffffffffffffffff')
|
self.assertEqual(action.acceptinfo_xsrcmd5, 'ffffffffffffffffffffffffffffffff')
|
||||||
self.assertTrue(action.acceptinfo_osrcmd5 is None)
|
self.assertTrue(action.acceptinfo_osrcmd5 is None)
|
||||||
self.assertTrue(action.acceptinfo_oxsrcmd5 is None)
|
self.assertTrue(action.acceptinfo_oxsrcmd5 is None)
|
||||||
self.assertEqual(xml, action.to_str())
|
self.assertXMLEqual(xml, action.to_str())
|
||||||
|
|
||||||
def test_action_from_xml_unknown_type(self):
|
def test_action_from_xml_unknown_type(self):
|
||||||
"""try to create action from xml with unknown type"""
|
"""try to create action from xml with unknown type"""
|
||||||
@ -350,7 +350,7 @@ class TestRequest(OscTestCase):
|
|||||||
self.assertEqual(r.description, 'this is a\nvery long\ndescription')
|
self.assertEqual(r.description, 'this is a\nvery long\ndescription')
|
||||||
self.assertTrue(len(r.statehistory) == 1)
|
self.assertTrue(len(r.statehistory) == 1)
|
||||||
self.assertTrue(len(r.reviews) == 0)
|
self.assertTrue(len(r.reviews) == 0)
|
||||||
self.assertEqual(xml, r.to_str())
|
self.assertXMLEqual(xml, r.to_str())
|
||||||
|
|
||||||
def test_read_request2(self):
|
def test_read_request2(self):
|
||||||
"""read in a request (with reviews)"""
|
"""read in a request (with reviews)"""
|
||||||
@ -389,7 +389,7 @@ class TestRequest(OscTestCase):
|
|||||||
self.assertEqual(r.creator, 'creator')
|
self.assertEqual(r.creator, 'creator')
|
||||||
self.assertTrue(len(r.statehistory) == 1)
|
self.assertTrue(len(r.statehistory) == 1)
|
||||||
self.assertTrue(len(r.reviews) == 1)
|
self.assertTrue(len(r.reviews) == 1)
|
||||||
self.assertEqual(xml, r.to_str())
|
self.assertXMLEqual(xml, r.to_str())
|
||||||
|
|
||||||
def test_read_request3(self):
|
def test_read_request3(self):
|
||||||
"""read in a request (with an "empty" comment+description)"""
|
"""read in a request (with an "empty" comment+description)"""
|
||||||
@ -426,7 +426,7 @@ class TestRequest(OscTestCase):
|
|||||||
<state name="new" when="2010-12-28T12:36:29" who="xyz" />
|
<state name="new" when="2010-12-28T12:36:29" who="xyz" />
|
||||||
</request>"""
|
</request>"""
|
||||||
|
|
||||||
self.assertEqual(exp, r.to_str())
|
self.assertXMLEqual(exp, r.to_str())
|
||||||
|
|
||||||
def test_request_list_view1(self):
|
def test_request_list_view1(self):
|
||||||
"""test the list_view method"""
|
"""test the list_view method"""
|
||||||
@ -559,7 +559,7 @@ Comment: <no comment>"""
|
|||||||
</action>
|
</action>
|
||||||
<state name="new" when="2010-12-30T02:11:22" who="olduser" />
|
<state name="new" when="2010-12-30T02:11:22" who="olduser" />
|
||||||
</request>"""
|
</request>"""
|
||||||
self.assertEqual(exp, r.to_str())
|
self.assertXMLEqual(exp, r.to_str())
|
||||||
|
|
||||||
def test_get_actions(self):
|
def test_get_actions(self):
|
||||||
"""test get_actions method"""
|
"""test get_actions method"""
|
||||||
|
Loading…
Reference in New Issue
Block a user