virtinst: Add full test coverage for xml*.py files

This commit is contained in:
Cole Robinson 2019-06-09 18:16:51 -04:00
parent 361657ad15
commit ae5e9d9a2c
5 changed files with 70 additions and 28 deletions

7
.coveragerc Normal file
View File

@ -0,0 +1,7 @@
[report]
exclude_lines =
# Have to re-enable the standard pragma
pragma: no cover
# Don't complain if tests don't hit defensive assertion code:
raise NotImplementedError

View File

@ -1448,13 +1448,43 @@ class XMLParseTest(unittest.TestCase):
except ValueError: except ValueError:
pass pass
def testXMLRootValidate(self): def testXMLCoverage(self):
try: try:
# Ensure we validate root element
virtinst.DeviceDisk(self.conn, parsexml="<foo/>") virtinst.DeviceDisk(self.conn, parsexml="<foo/>")
raise AssertionError("Expected parse failure") raise AssertionError("Expected parse failure")
except RuntimeError as e: except RuntimeError as e:
self.assertTrue("'foo'" in str(e)) self.assertTrue("'foo'" in str(e))
try:
# Ensure we validate root element
virtinst.DeviceDisk(self.conn, parsexml=-1)
raise AssertionError("Expected parse failure")
except Exception as e:
self.assertTrue("xmlParseDoc" in str(e))
try:
from virtinst import xmlutil
xmlutil.raise_programming_error(True, "for coverage")
raise AssertionError("We shouldn't get here")
except RuntimeError:
pass
try:
virtinst.DeviceDisk.validate_generic_name("objtype", None)
raise AssertionError("We shouldn't get here")
except ValueError:
pass
try:
virtinst.DeviceDisk.validate_generic_name("objtype", "foo bar")
raise AssertionError("We shouldn't get here")
except ValueError:
pass
# Test property __repr__ for code coverage
assert str(virtinst.DeviceDisk.address)
assert str(virtinst.DeviceDisk.driver_cache)
def testReplaceChildParse(self): def testReplaceChildParse(self):
buildfile = "tests/xmlparse-xml/replace-child-build.xml" buildfile = "tests/xmlparse-xml/replace-child-build.xml"
parsefile = "tests/xmlparse-xml/replace-child-parse.xml" parsefile = "tests/xmlparse-xml/replace-child-parse.xml"

View File

@ -224,8 +224,7 @@ class _XMLBase(object):
xpathobj = _XPath(fullxpath) xpathobj = _XPath(fullxpath)
parentxpath = "." parentxpath = "."
parentnode = self._find(parentxpath) parentnode = self._find(parentxpath)
if parentnode is None: xmlutil.raise_programming_error(not parentnode,
raise RuntimeError("programming error: "
"Did not find XML root node for xpath=%s" % fullxpath) "Did not find XML root node for xpath=%s" % fullxpath)
for xpathseg in xpathobj.segments[1:]: for xpathseg in xpathobj.segments[1:]:
@ -287,15 +286,15 @@ class _Libxml2API(_XMLBase):
self._ctx.xpathRegisterNs(key, val) self._ctx.xpathRegisterNs(key, val)
def __del__(self): def __del__(self):
if not hasattr(self, "_doc"):
# Incase we error when parsing the doc
return
self._doc.freeDoc() self._doc.freeDoc()
self._doc = None self._doc = None
self._ctx.xpathFreeContext() self._ctx.xpathFreeContext()
self._ctx = None self._ctx = None
def _sanitize_xml(self, xml): def _sanitize_xml(self, xml):
# Strip starting <?...> line
if xml.startswith("<?"):
ignore, xml = xml.split("\n", 1)
if not xml.endswith("\n") and "\n" in xml: if not xml.endswith("\n") and "\n" in xml:
xml += "\n" xml += "\n"
return xml return xml

View File

@ -192,8 +192,8 @@ class XMLProperty(_XMLPropertyBase):
:param do_abspath: If True, run os.path.abspath on the passed value :param do_abspath: If True, run os.path.abspath on the passed value
""" """
self._xpath = xpath self._xpath = xpath
if not self._xpath: xmlutil.raise_programming_error(not self._xpath,
raise RuntimeError("XMLProperty: xpath must be passed.") "XMLProperty: xpath must be passed.")
self._is_bool = is_bool self._is_bool = is_bool
self._is_int = is_int self._is_int = is_int
@ -201,10 +201,11 @@ class XMLProperty(_XMLPropertyBase):
self._is_onoff = is_onoff self._is_onoff = is_onoff
self._do_abspath = do_abspath self._do_abspath = do_abspath
if sum([int(bool(i)) for i in conflicts = sum([int(bool(i)) for i in
[self._is_bool, self._is_int, [self._is_bool, self._is_int,
self._is_yesno, self._is_onoff]]) > 1: self._is_yesno, self._is_onoff]])
raise RuntimeError("Conflict property converter options.") xmlutil.raise_programming_error(conflicts > 1,
"Conflict property converter options.")
self._is_tracked = False self._is_tracked = False
if _trackprops: if _trackprops:
@ -405,8 +406,6 @@ class _XMLState(object):
self._parent_xpath = xpath or "" self._parent_xpath = xpath or ""
def _join_xpath(self, x1, x2): def _join_xpath(self, x1, x2):
if x1.endswith("/"):
x1 = x1[:-1]
if x2.startswith("."): if x2.startswith("."):
x2 = x2[1:] x2 = x2[1:]
return x1 + x2 return x1 + x2
@ -478,11 +477,7 @@ class XMLBuilder(object):
self.conn = conn self.conn = conn
if self._XML_SANITIZE: if self._XML_SANITIZE:
if hasattr(parsexml, 'decode'): parsexml = parsexml.encode("ascii", "ignore").decode("ascii")
parsexml = parsexml.decode("ascii", "ignore").encode("ascii")
else:
parsexml = parsexml.encode("ascii", "ignore").decode("ascii")
parsexml = "".join([c for c in parsexml if c in string.printable]) parsexml = "".join([c for c in parsexml if c in string.printable])
self._propstore = collections.OrderedDict() self._propstore = collections.OrderedDict()
@ -502,15 +497,16 @@ class XMLBuilder(object):
xmlprops = self._all_xml_props() xmlprops = self._all_xml_props()
childprops = self._all_child_props() childprops = self._all_child_props()
for key in self._XML_PROP_ORDER: for key in self._XML_PROP_ORDER:
if key not in xmlprops and key not in childprops: xmlutil.raise_programming_error(
raise RuntimeError("programming error: key '%s' must be " key not in xmlprops and key not in childprops,
"xml prop or child prop" % key) "key '%s' must be xml prop or child prop" % key)
childclasses = [] childclasses = []
for childprop in childprops.values(): for childprop in childprops.values():
if childprop.child_class in childclasses: xmlutil.raise_programming_error(
raise RuntimeError("programming error: can't register " childprop.child_class in childclasses,
"duplicate child_classs=%s" % childprop.child_class) "can't register duplicate child_class=%s" %
childprop.child_class)
childclasses.append(childprop.child_class) childclasses.append(childprop.child_class)
setattr(self.__class__, cachekey, True) setattr(self.__class__, cachekey, True)
@ -636,14 +632,16 @@ class XMLBuilder(object):
def _find_child_prop(self, child_class): def _find_child_prop(self, child_class):
xmlprops = self._all_child_props() xmlprops = self._all_child_props()
ret = None
for xmlprop in list(xmlprops.values()): for xmlprop in list(xmlprops.values()):
if xmlprop.is_single: if xmlprop.is_single:
continue continue
if child_class is xmlprop.child_class: if child_class is xmlprop.child_class:
return xmlprop ret = xmlprop
raise RuntimeError("programming error: " break
"Didn't find child property for child_class=%s" % xmlutil.raise_programming_error(not ret,
child_class) "Didn't find child property for child_class=%s" % child_class)
return ret
def _set_xpaths(self, parent_xpath, relative_object_xpath=-1): def _set_xpaths(self, parent_xpath, relative_object_xpath=-1):
""" """

View File

@ -57,3 +57,11 @@ def set_prop_path(obj, prop_path, value):
parent = getattr(parent, piece) parent = getattr(parent, piece)
return setattr(parent, pieces[-1], value) return setattr(parent, pieces[-1], value)
def raise_programming_error(cond, msg):
"""
Small helper to raise a consistent error message for coding issues
"""
if cond:
raise RuntimeError("programming error: %s" % msg)