def assertXMLEqual(self, s1, s2, entity=None): """Assert that the two XML fragments are equal, tolerating the following variations: * whitespace outside of element content and attribute values. * order of attributes. * order of certain child elements (see `sort_elements` in this function). Parameters: * s1 and s2 are string representations of an XML fragment. The strings may be Unicode strings or UTF-8 encoded byte strings. The strings may contain an encoding declaration even when they are Unicode strings. Note: An encoding declaration is the `encoding` attribute in the XML declaration (aka XML processing statement), e.g.: <?xml version="1.0" encoding="utf-8" ?> """ # Ensure Unicode strings and remove encoding from XML declaration encoding_pattern = re.compile( r'^<\?xml +(([a-zA-Z0-9_]+=".*")?) +' + r'encoding="utf-8" +(([a-zA-Z0-9_]+=".*")?) *\?>') encoding_repl = r'<?xml \1 \3 ?>' s1 = re.sub(encoding_pattern, encoding_repl, _ensure_unicode(s1)) s2 = re.sub(encoding_pattern, encoding_repl, _ensure_unicode(s2)) parser = etree.XMLParser(remove_blank_text=True) x1 = etree.XML(s1, parser=parser) x2 = etree.XML(s2, parser=parser) # Sort certain elements def sort_children(root, sort_elements): for tag, attr in sort_elements: # elems is a list of elements with this tag name elems = root.xpath("//*[local-name() = $tag]", tag=tag) if len(elems) > 0: parent = elems[0].getparent() first = None after = None for i in range(0, len(parent)): if parent[i].tag == tag and first is None: first = i if parent[i].tag != tag and first is not None: after = i # The following changes the original XML tree: # The following pylint warning can safely be disabled, see # http://stackoverflow.com/a/25314665 # pylint: disable=cell-var-from-loop parent[first:after] = sorted(elems, key=lambda e: e.attrib[attr]) sort_elements = [ # Sort sibling elements with <first> tag by its <second> attribute ("IPARAMVALUE", "NAME"), ("PROPERTY", "NAME"), ("PARAMETER", "NAME"), ] sort_children(x1, sort_elements) sort_children(x2, sort_elements) ns1 = _ensure_unicode(etree.tostring(x1)) ns2 = _ensure_unicode(etree.tostring(x2)) checker = doctestcompare.LXMLOutputChecker() # This tolerates differences in whitespace and attribute order if not checker.check_output(ns1, ns2, 0): diff = checker.output_difference(doctest.Example("", ns1), ns2, 0) raise AssertionError("XML is not as expected in %s: %s"%\ (entity, diff))
def assertXMLEqual(s_act, s_exp, entity=None): """Assert that the two XML fragments are equal, tolerating the following variations: * whitespace outside of element content and attribute values. * order of attributes. * order of certain child elements (see `sort_elements` in this function). Parameters: * s_act and s_exp are string representations of an XML fragment. The strings may be Unicode strings or UTF-8 encoded byte strings. The strings may contain an encoding declaration even when they are Unicode strings. Note: An encoding declaration is the `encoding` attribute in the XML declaration (aka XML processing statement), e.g.: <?xml version="1.0" encoding="utf-8" ?> """ # Ensure Unicode strings and remove encoding from XML declaration encoding_pattern = re.compile( r'^<\?xml +(([a-zA-Z0-9_]+=".*")?) +' + r'encoding="utf-8" +(([a-zA-Z0-9_]+=".*")?) *\?>') encoding_repl = r'<?xml \1 \3 ?>' s_act = re.sub(encoding_pattern, encoding_repl, _ensure_unicode(s_act)) s_exp = re.sub(encoding_pattern, encoding_repl, _ensure_unicode(s_exp)) parser = etree.XMLParser(remove_blank_text=True) x_act = etree.XML(s_act, parser=parser) x_exp = etree.XML(s_exp, parser=parser) def sort_embedded(root, sort_elements): """ Helper function for `sort_children()`, in support of embedded objects. This function invokes sort_children() on each embedded object in `root`, after unembedding the embedded object. Parameters: root (etree.Element): XML tree of the CIM-XML representation of the CIM element that contains an embedded CIM object (e.g. the CIM element may be an INSTANCE XML element and one of its PROPERTY child elements has a value that is an embedded CIM instance). """ emb_elems = root.xpath("//*[@EmbeddedObject or @EMBEDDEDOBJECT]" "/*[local-name() = 'VALUE' or " "local-name() = 'VALUE.ARRAY']") for emb_elem in emb_elems: elem = xml_unembed(emb_elem.text) sort_children(elem, sort_elements) emb_elem.text = xml_embed(elem) def sort_children(root, sort_elements): """Sort certain elements in the `root` parameter to facilitate comparison of two XML documents. In addition, make sure this is also applied to any embedded objects (in their unembedded state). """ sort_embedded(root, sort_elements) for tag, attr in sort_elements: # elems is a list of elements with this tag name elems = root.xpath("//*[local-name() = $tag]", tag=tag) if elems: parent = elems[0].getparent() first = None after = None for i in range(0, len(parent)): if parent[i].tag == tag and first is None: first = i if parent[i].tag != tag and first is not None: after = i # The following changes the original XML tree: # The following pylint warning can safely be disabled, see # http://stackoverflow.com/a/25314665 # pylint: disable=cell-var-from-loop parent[first:after] = sorted(elems, key=lambda e: e.attrib[attr]) sort_elements = [ # Sort sibling elements with <first> tag by its <second> attribute ("IPARAMVALUE", "NAME"), ("PROPERTY", "NAME"), ("PROPERTY.ARRAY", "NAME"), ("PARAMETER", "NAME"), ("KEYBINDING", "NAME"), ] sort_children(x_act, sort_elements) sort_children(x_exp, sort_elements) ns_act = _ensure_unicode(etree.tostring(x_act)) ns_exp = _ensure_unicode(etree.tostring(x_exp)) checker = doctestcompare.LXMLOutputChecker() # This tolerates differences in whitespace and attribute order if not checker.check_output(ns_act, ns_exp, 0): diff = checker.output_difference(doctest.Example("", ns_exp), ns_act, 0) raise AssertionError("XML is not as expected in %s: %s" % (entity, diff))