Skip to content

Commit

Permalink
100% coverage for schema.py
Browse files Browse the repository at this point in the history
  • Loading branch information
jamadden committed Feb 11, 2017
1 parent 0e06387 commit f06f052
Show file tree
Hide file tree
Showing 5 changed files with 207 additions and 13 deletions.
37 changes: 25 additions & 12 deletions ZConfig/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
from ZConfig import info
from ZConfig import url

BLANK = u''
from ZConfig._compat import reraise

BLANK = u''

def parseResource(resource, loader):
parser = SchemaParser(loader, resource.url)
Expand Down Expand Up @@ -95,19 +96,24 @@ def startElement(self, name, attrs):
% (_srepr(name), _srepr(parent)))
elif name != self._top_level:
self.error("Unknown document type " + name)

self._elem_stack.append(name)

# self._schema is assigned to in self.start_<_top_level>, so
# most of the checks for it being None are just extra precaution.
if name == self._top_level:
if self._schema is not None:
if self._schema is not None: # pragma: no cover
self.error("schema element improperly nested")
getattr(self, "start_" + name)(attrs)
elif name in self._handled_tags:
if self._schema is None:
if self._schema is None: # pragma: no cover
self.error(name + " element outside of schema")
getattr(self, "start_" + name)(attrs)
elif name in self._cdata_tags:
if self._schema is None:
if self._schema is None: # pragma: no cover
self.error(name + " element outside of schema")
if self._cdata is not None:
if self._cdata is not None: # pragma: no cover
# this should be handled by the earliel nesting check
self.error(name + " element improperly nested")
self._cdata = []
self._position = None
Expand All @@ -132,7 +138,8 @@ def endElement(self, name):
getattr(self, "characters_" + name)(data)

def endDocument(self):
if self._schema is None:
if self._schema is None: # pragma: no cover
# this would have to be a broken subclass
self.error("no %s found" % self._top_level)

# helper methods
Expand All @@ -142,7 +149,7 @@ def get_position(self):
return (self._locator.getLineNumber(),
self._locator.getColumnNumber(),
(self._locator.getSystemId() or self._url))
else:
else: # pragma: no cover
return None, None, self._url

def get_handler(self, attrs):
Expand Down Expand Up @@ -232,7 +239,8 @@ def get_key_info(self, attrs, element):
any, name, attribute = self.get_name_info(attrs, element)
if any == '*':
self.error(element + " may not specify '*' for name")
if not name and any != '+':
if not name and any != '+': # pragma: no cover
# Con we even get here?
self.error(element + " name may not be omitted or empty")
datatype = self.get_datatype(attrs, "datatype", "string")
handler = self.get_handler(attrs)
Expand Down Expand Up @@ -300,7 +308,7 @@ def start_import(self, attrs):
src = url.urljoin(self._url, src)
src, fragment = url.urldefrag(src)
if fragment:
self.error("import src many not include"
self.error("import src may not include"
" a fragment identifier")
schema = self._loader.loadURL(src)
for n in schema.gettypenames():
Expand Down Expand Up @@ -361,7 +369,8 @@ def start_section(self, attrs):
handler = self.get_handler(attrs)
min = self.get_required(attrs) and 1 or 0
any, name, attribute = self.get_name_info(attrs, "section", "*")
if any and not attribute:
if any and not attribute: # pragma: no cover
# It seems like this is handled by get_name_info.
self.error(
"attribute must be specified if section name is '*' or '+'")
section = info.SectionInfo(any or name, sectiontype,
Expand Down Expand Up @@ -460,7 +469,8 @@ def initerror(self, e):
return e

def error(self, message):
raise self.initerror(ZConfig.SchemaError(message))
e = self.initerror(ZConfig.SchemaError(message))
reraise(type(e), e, sys.exc_info()[2])


class SchemaParser(BaseParser):
Expand Down Expand Up @@ -593,6 +603,9 @@ def end_component(self):
self.pop_prefix()

def _check_not_toplevel(self, what):
if not self._stack:
if not self._stack: # pragma: no cover
# we can't get here because the elements that call
# this function have specified _allowed_parents that are
# checked first
self.error("cannot define top-level %s in a schema %s"
% (what, self._top_level))
5 changes: 5 additions & 0 deletions ZConfig/tests/bad-component.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<component>

<section />

</component>
7 changes: 7 additions & 0 deletions ZConfig/tests/bad-component2.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<component>

<sectiontype name='foo'>
<section type='foo' attribute='bar' />
</sectiontype>

</component>
2 changes: 1 addition & 1 deletion ZConfig/tests/test_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class BaseKeyInfoTestCase(InfoMixin, unittest.TestCase):

class Class(BaseKeyInfo):
def add_valueinfo(self, vi, key):
pass
"This wont actually be called"

def test_cant_instantiate(self):
self.Class = BaseKeyInfo
Expand Down
169 changes: 169 additions & 0 deletions ZConfig/tests/test_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -1150,6 +1150,175 @@ def test_multisection_example_is_error(self):
</schema>
""")

def checkErrorText(self, schema, error_text):
self.assertRaisesRegexp(ZConfig.SchemaError, error_text,
self.load_schema_text, schema)

def test_error_bad_parent(self):
self.checkErrorText(
"<schema><schema>",
"Unknown tag")

def test_error_unknown_doc(self):
self.checkErrorText("<bad>", "Unknown document type")

def test_error_extra_cdata(self):
self.checkErrorText("<schema>text",
"non-blank character data")


def test_error_subclass(self):
import ZConfig.schema
import ZConfig.datatypes
class MockLoader(object):
registry = ZConfig.datatypes.Registry()
parser = ZConfig.schema.SchemaParser(MockLoader(), 'url')
parser.push_prefix({'prefix': __name__})
parser.push_prefix({'prefix': '.' + __name__})

def cv(n):
raise ValueError()
MockLoader.registry._stock['dotted-suffix'] = cv

self.assertRaises(ZConfig.SchemaError,
parser.push_prefix,
{'prefix': __name__})

self.assertRaises(ZConfig.SchemaError,
parser.basic_key,
"not a basic key")

self.assertRaises(ZConfig.SchemaError,
parser.identifier,
"not an identifier")

def test_error_required_value(self):
self.checkErrorText(
"""
<schema>
<key name='+' required='maybe' attribute='keymap' />
</schema>
""",
"value for 'required' must be")

def test_error_section(self):
self.checkErrorText(
"""
<schema>
<section />
""",
"section must specify type")

def test_error_multisection(self):
self.checkErrorText(
"""
<schema>
<sectiontype name="abc" />
<multisection type="abc" name="doreme"/>
""",
"multisection must specify .* for the name")

def test_error_multikey(self):

self.checkErrorText(
"""
<schema>
<multikey name='foo' required="yes" default="1" attribute='keymap'/>
</schema>
""",
"default values for multikey must be given")

def test_error_key_info(self):

self.checkErrorText(
"""
<schema>
<key name='foo' required="yes" default="1" attribute='keymap'/>
</schema>
""",
"required key cannot have a default")

self.checkErrorText(
"""
<schema>
<key name='*' attribute='keymap'/>
</schema>
""",
r"key may not specify '\*' for name")

self.checkErrorText(
"""
<schema>
<key name='' attribute='keymap'/>
</schema>
""",
"name must be specified and non-empty")

self.checkErrorText(
"""
<schema>
<key name='*' />
</schema>
""",
"container attribute must be specified")

self.checkErrorText(
"""
<schema>
<key name='invalid key name' attribute='keymap'/>
</schema>
""",
"could not convert key name to keytype")

def test_error_import_fragment(self):
self.checkErrorText(
"""
<schema>
<import src='http://path/with#fragment' />
""",
"may not include a fragment identifier")

def test_error_sectiontype(self):
self.checkErrorText(
"""
<schema>
<sectiontype>
""",
"sectiontype name must not be omitted or empty")

def test_error_abstracttype(self):
self.checkErrorText(
"""
<schema>
<abstracttype>
""",
"abstracttype name must not be omitted or empty")

def test_metadefault(self):
self.load_schema_text(
"""
<schema>
<key name="name">
<metadefault>a default</metadefault>
</key>
</schema>
""")

def test_error_component_section(self):
self.checkErrorText(
"""
<schema>
<import package="ZConfig.tests" file="bad-component.xml" />
""",
"elements may not be nested")

self.load_schema_text(
"""
<schema>
<import package="ZConfig.tests" file="bad-component2.xml" />
</schema>
""")


def test_suite():
return unittest.makeSuite(SchemaTestCase)
Expand Down

0 comments on commit f06f052

Please sign in to comment.