# # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import doctest from unittest import mock from lxml import doctestcompare import six CHECKER = doctestcompare.LXMLOutputChecker() PARSE_XML = doctest.register_optionflag('PARSE_XML') class RequestSideEffect(object): def __init__(self): self.actions = [] self.started = False def append(self, resp=None, ex=None): if not self.started: self.actions.append((resp, ex)) def __call__(self, *args, **kwargs): if not self.started: self.started = True
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 get_set_nodes(e, setname, create=False): """Return the attributes set nodes (create one if requested) setname can for example be meta_attributes """ l = [c for c in e.iterchildren(setname)] if l: return l if create: import idmgmt elem = etree.SubElement(e, setname, id="") elem.set("id", idmgmt.new(elem, e.get("id"))) l.append(elem) return l _checker = doctestcompare.LXMLOutputChecker() def xml_equals_unordered(a, b): "used by xml_equals to compare xml trees without ordering" def fail(msg): common_debug("%s!=%s: %s" % (a.tag, b.tag, msg)) return False def tagflat(x): return isinstance(x.tag, basestring) and x.tag or x.text def sortby(v): if v.tag == 'primitive': return v.tag
def assertXMLEqual(s_act, s_exp, entity): """ 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" ?> * entity (string): A human readable identification for what is compared. """ # Make sure that None values are already excluded by the caller assert isinstance(s_act, (six.text_type, six.binary_type)) assert isinstance(s_exp, (six.text_type, six.binary_type)) # 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) try: # Note: lxml.etree.XML() has issues with unicode strings as input, # so we pass UTF-8 encoded byte strings. See lxml bug # https://bugs.launchpad.net/lxml/+bug/1902364 for a similar issue # with lxml.etree.fromstring(). x_act = etree.XML(_ensure_bytes(s_act), parser=parser) x_exp = etree.XML(_ensure_bytes(s_exp), parser=parser) except etree.XMLSyntaxError as exc: raise AssertionError("XML cannot be validated for %s: %s" % (entity, exc)) 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, p in enumerate(parent): # TODO 6/18 AM: Loop above should probably be on elems if p.tag == tag and first is None: first = i if p.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 # https://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))
def equals(self, rhs): checker = doctestcompare.LXMLOutputChecker() return checker.check_output(self._xml, rhs, 0)