##############################################################################
#
# Copyright (c) 2002, 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Tests of ZConfig schemas."""
import unittest
import ZConfig
from ZConfig.tests.support import TestBase, CONFIG_BASE
def uppercase(value):
return str(value).upper()
def appsection(value):
return MySection(value)
def get_foo(section):
return section.foo
class MySection:
def __init__(self, value):
self.conf = value
def get_section_attributes(section):
L = list(section.getSectionAttributes())
L.sort()
return L
class SchemaTestCase(TestBase):
"""Tests of the basic schema support itself."""
def test_minimal_schema(self):
schema = self.load_schema_text("")
self.assertEqual(len(schema), 0)
self.assertRaises(IndexError,
lambda schema=schema: schema[0])
self.assertRaises(ZConfig.ConfigurationError,
schema.getinfo, "foo")
def test_simple(self):
schema, conf = self.load_both("simple.xml", "simple.conf")
self._verifySimpleConf(conf)
def _verifySimpleConf(self,conf):
eq = self.assertEqual
eq(conf.var1, 'abc')
eq(conf.int_var, 12)
eq(conf.float_var, 12.02)
eq(conf.neg_int, -2)
check = self.assert_
check(conf.true_var_1)
check(conf.true_var_2)
check(conf.true_var_3)
check(not conf.false_var_1)
check(not conf.false_var_2)
check(not conf.false_var_3)
def test_app_datatype(self):
dtname = __name__ + ".uppercase"
schema = self.load_schema_text("""\
abc
abc
not
lower
case
""" % (dtname, dtname, dtname, dtname))
conf = self.load_config_text(schema, """\
a qwerty
c upp
c er
c case
""")
eq = self.assertEqual
eq(conf.a, 'QWERTY')
eq(conf.b, 'ABC')
eq(conf.c, ['UPP', 'ER', 'CASE'])
eq(conf.d, ['NOT', 'LOWER', 'CASE'])
eq(get_section_attributes(conf),
["a", "b", "c", "d"])
def test_app_sectiontype(self):
schema = self.load_schema_text("""\
""" % __name__)
conf = self.load_config_text(schema, """\
sample 42
""")
self.assert_(isinstance(conf, MySection))
o1 = conf.conf.sect
self.assert_(isinstance(o1, MySection))
self.assertEqual(o1.conf.sample, 42)
def test_empty_sections(self):
schema = self.load_schema_text("""\
""")
conf = self.load_config_text(schema, """\
""")
self.assert_(conf.s1 is not None)
self.assert_(conf.s2 is not None)
self.assertEqual(get_section_attributes(conf),
["s1", "s2"])
def test_deeply_nested_sections(self):
schema = self.load_schema_text("""\
""")
conf = self.load_config_text(schema, """\
key sect3-value
key sect2-value
""")
eq = self.assertEqual
eq(conf.sect.sect.sect.key, "type1-value")
eq(conf.sect.sect.key, "sect2-value")
eq(conf.sect.key, "sect3-value")
eq(get_section_attributes(conf),
["sect"])
eq(get_section_attributes(conf.sect),
["key", "sect"])
eq(get_section_attributes(conf.sect.sect),
["key", "sect"])
eq(get_section_attributes(conf.sect.sect.sect),
["key"])
def test_multivalued_keys(self):
schema = self.load_schema_text("""\
1
2
3
4
5
""")
conf = self.load_config_text(schema, """\
a foo
a bar
c 41
c 42
c 43
""", num_handlers=2)
L = []
self.handlers({'abc': L.append,
'DEF': L.append})
self.assertEqual(L, [['foo', 'bar'], conf])
L = []
self.handlers({'abc': None,
'DEF': L.append})
self.assertEqual(L, [conf])
self.assertEqual(conf.a, ['foo', 'bar'])
self.assertEqual(conf.b, [1, 2])
self.assertEqual(conf.c, [41, 42, 43])
self.assertEqual(conf.d, [])
self.assertEqual(get_section_attributes(conf),
["a", "b", "c", "d"])
def test_multikey_required(self):
schema = self.load_schema_text("""\
""")
self.assertRaises(ZConfig.ConfigurationError,
self.load_config_text, schema, "")
def test_multisection_required(self):
schema = self.load_schema_text("""\
""")
self.assertRaises(ZConfig.ConfigurationError,
self.load_config_text, schema, "")
def test_key_required_but_missing(self):
schema = self.load_schema_text("""\
""")
self.assertRaises(ZConfig.ConfigurationError,
self.load_config_text, schema, "")
def test_section_required_but_missing(self):
schema = self.load_schema_text("""\
""")
self.assertRaises(ZConfig.ConfigurationError,
self.load_config_text, schema, "")
def test_key_default_element(self):
self.assertRaises(
ZConfig.SchemaError, self.load_schema_text, """\
text
""")
def test_bad_handler_maps(self):
schema = self.load_schema_text("""\
""")
conf = self.load_config_text(schema, """\
a foo
b bar
""", num_handlers=2)
self.assertEqual(get_section_attributes(conf),
["a", "b"])
self.assertRaises(ZConfig.ConfigurationError,
self.handlers, {'abc': id, 'ABC': id, 'def': id})
self.assertRaises(ZConfig.ConfigurationError,
self.handlers, {})
def test_handler_ordering(self):
schema = self.load_schema_text("""\
""")
conf = self.load_config_text(schema, """\
""", num_handlers=3)
L = []
self.handlers({'a': L.append,
'b': L.append,
'c': L.append})
outer = conf.sect_outer
inner = outer.sect_inner
self.assertEqual(L, [inner, outer, conf])
def test_duplicate_section_names(self):
schema = self.load_schema_text("""\
""")
self.assertRaises(ZConfig.ConfigurationError, self.load_config_text,
schema, """\
""")
conf = self.load_config_text(schema, """\
""")
def test_disallowed_duplicate_attribute(self):
self.assertRaises(ZConfig.SchemaError, self.load_schema_text, """\
""")
def test_unknown_datatype_name(self):
self.assertRaises(ZConfig.SchemaError,
self.load_schema_text, "")
def test_load_abstracttype(self):
schema = self.load_schema_text("""\
This is an abstract section type.
""")
# check the types that get defined
t = schema.gettype("group")
self.assert_(t.isabstract())
t1 = schema.gettype("t1")
self.assert_(not t1.isabstract())
self.assert_(t.getsubtype("t1") is t1)
t2 = schema.gettype("t2")
self.assert_(not t2.isabstract())
self.assert_(t.getsubtype("t2") is t2)
self.assertRaises(ZConfig.ConfigurationError, t.getsubtype, "group")
self.assert_(t1 is not t2)
# try loading a config that relies on this schema
conf = self.load_config_text(schema, """\
k1 value1
k2 value2
""")
eq = self.assertEqual
eq(get_section_attributes(conf), ["g"])
eq(len(conf.g), 4)
eq(conf.g[0].k1, "default1")
eq(conf.g[1].k1, "value1")
eq(conf.g[2].k2, "default2")
eq(conf.g[3].k2, "value2")
# white box:
self.assert_(conf.g[0].getSectionDefinition() is t1)
self.assert_(conf.g[1].getSectionDefinition() is t1)
self.assert_(conf.g[2].getSectionDefinition() is t2)
self.assert_(conf.g[3].getSectionDefinition() is t2)
def test_abstracttype_extension(self):
schema = self.load_schema_text("""\
""")
abstype = schema.gettype("group")
self.assert_(schema.gettype("extra") is abstype.getsubtype("extra"))
# make sure we can use the extension in a config:
conf = self.load_config_text(schema, "")
self.assertEqual(conf.thing.getSectionType(), "extra")
self.assertEqual(get_section_attributes(conf), ["thing"])
self.assertEqual(get_section_attributes(conf.thing), [])
def test_abstracttype_extension_errors(self):
# specifying a non-existant abstracttype
self.assertRaises(ZConfig.SchemaError, self.load_schema_text, """\
""")
# specifying something that isn't an abstracttype
self.assertRaises(ZConfig.SchemaError, self.load_schema_text, """\
""")
def test_arbitrary_key(self):
schema = self.load_schema_text("""\
""")
conf = self.load_config_text(schema, "some-key 42")
self.assertEqual(conf.keymap, {'some-key': 42})
self.assertEqual(get_section_attributes(conf), ["keymap"])
def test_arbitrary_multikey_required(self):
schema = self.load_schema_text("""\
""")
conf = self.load_config_text(schema, """\
some-key 42
some-key 43
""")
self.assertEqual(conf.keymap, {'some-key': [42, 43]})
def test_arbitrary_multikey_optional(self):
schema = self.load_schema_text("""\
""")
conf = self.load_config_text(schema, """\
some-key 42
some-key 43
""")
self.assertEqual(conf.stuff.keymap, {'some-key': ['42', '43']})
self.assertEqual(get_section_attributes(conf), ["stuff"])
def test_arbitrary_multikey_optional_empty(self):
schema = self.load_schema_text("""\
""")
conf = self.load_config_text(schema, "")
self.assertEqual(conf.stuff.keymap, {})
def test_arbitrary_multikey_with_defaults(self):
schema = self.load_schema_text("""\
value-a1
value-a2
value-b
""")
conf = self.load_config_text(schema, "")
self.assertEqual(conf.keymap, {'a': ['value-a1', 'value-a2'],
'b': ['value-b']})
def test_arbitrary_multikey_with_unkeyed_default(self):
self.assertRaises(ZConfig.SchemaError,
self.load_schema_text, """\
value-a1
""")
def test_arbitrary_key_with_defaults(self):
schema = self.load_schema_text("""\
value-a
value-b
""")
conf = self.load_config_text(schema, "")
self.assertEqual(conf.keymap, {'a': 'value-a', 'b': 'value-b'})
def test_arbitrary_key_with_unkeyed_default(self):
self.assertRaises(ZConfig.SchemaError,
self.load_schema_text, """\
value-a1
""")
def test_arbitrary_keys_with_others(self):
schema = self.load_schema_text("""\
""")
conf = self.load_config_text(schema, """\
some-key 42
k2 3
""")
self.assertEqual(conf.k1, 'v1')
self.assertEqual(conf.k2, 3)
self.assertEqual(conf.keymap, {'some-key': 42})
self.assertEqual(get_section_attributes(conf),
["k1", "k2", "keymap"])
def test_arbitrary_key_missing(self):
schema = self.load_schema_text("""\
""")
self.assertRaises(ZConfig.ConfigurationError,
self.load_config_text, schema, "# empty config file")
def test_arbitrary_key_bad_schema(self):
self.assertRaises(ZConfig.SchemaError, self.load_schema_text, """\
""")
def test_getrequiredtypes(self):
schema = self.load_schema("library.xml")
self.assertEqual(schema.getrequiredtypes(), [])
schema = self.load_schema_text("""\
""")
L = schema.getrequiredtypes()
L.sort()
self.assertEqual(L, ["used"])
def test_getunusedtypes(self):
schema = self.load_schema("library.xml")
L = schema.getunusedtypes()
L.sort()
self.assertEqual(L, ["type-a", "type-b"])
schema = self.load_schema_text("""\
""")
self.assertEqual(schema.getunusedtypes(), ["unused"])
def test_section_value_mutation(self):
schema, conf = self.load_both("simple.xml", "simple.conf")
orig = conf.empty
new = []
conf.empty = new
self.assert_(conf.empty is new)
def test_simple_anonymous_section(self):
schema = self.load_schema_text("""\
""")
conf = self.load_config_text(schema, "")
self.assertEqual(conf.attr.key, "value")
def test_simple_anonymous_section_without_name(self):
# make sure we get the same behavior without name='*'
schema = self.load_schema_text("""\
""")
conf = self.load_config_text(schema, "")
self.assertEqual(conf.attr.key, "value")
def test_simple_anynamed_section(self):
schema = self.load_schema_text("""\
""")
conf = self.load_config_text(schema, "")
self.assertEqual(conf.attr.key, "value")
self.assertEqual(conf.attr.getSectionName(), "name")
# if we omit the name, it's an error
self.assertRaises(ZConfig.ConfigurationError,
self.load_config_text, schema, "")
def test_nested_abstract_sectiontype(self):
schema = self.load_schema_text("""\
""")
conf = self.load_config_text(schema, """\
""")
def test_nested_abstract_sectiontype_without_name(self):
# make sure we get the same behavior without name='*'
schema = self.load_schema_text("""\
""")
conf = self.load_config_text(schema, """\
""")
def test_reserved_attribute_prefix(self):
template = """\
%s
"""
def check(thing, self=self, template=template):
text = template % thing
self.assertRaises(ZConfig.SchemaError,
self.load_schema_text, text)
check("")
check("")
check("")
check("")
check("")
check("")
check("")
check("")
def test_sectiontype_as_schema(self):
schema = self.load_schema_text("""\
""")
t = schema.gettype("t")
conf = self.load_config_text(t, "")
self.assertEqual(conf.tkey, "tkey-default")
self.assertEqual(conf.section.skey, "skey-default")
self.assertEqual(get_section_attributes(conf), ["section", "tkey"])
self.assertEqual(get_section_attributes(conf.section), ["skey"])
def test_datatype_conversion_error(self):
schema_url = "file:///tmp/fake-url-1.xml"
config_url = "file:///tmp/fake-url-2.xml"
schema = self.load_schema_text("""\
""", url=schema_url)
e = self.get_data_conversion_error(
schema, "", config_url)
self.assertEqual(e.url, schema_url)
self.assertEqual(e.lineno, 2)
e = self.get_data_conversion_error(schema, """\
# comment
key splat
""", config_url)
self.assertEqual(e.url, config_url)
self.assertEqual(e.lineno, 3)
def get_data_conversion_error(self, schema, src, url):
try:
self.load_config_text(schema, src, url=url)
except ZConfig.DataConversionError, e:
return e
else:
self.fail("expected ZConfig.DataConversionError")
def test_numeric_section_name(self):
schema = self.load_schema_text("""\
""")
conf = self.load_config_text(schema, "")
self.assertEqual(len(conf.things), 1)
def test_sectiontype_extension(self):
schema = self.load_schema_text("""\
""")
conf = self.load_config_text(schema, """\
k1 k1-value
k2 k2-value
""")
eq = self.assertEqual
eq(conf.s.k1, "k1-value")
eq(conf.s.k2, "k2-value")
eq(get_section_attributes(conf), ["s"])
eq(get_section_attributes(conf.s), ["k1", "k2"])
def test_sectiontype_extension_errors(self):
# cannot override key from base
self.assertRaises(ZConfig.SchemaError, self.load_schema_text, """\
""")
# cannot extend non-existing section
self.assertRaises(ZConfig.SchemaError, self.load_schema_text, """\
""")
# cannot extend abstract type
self.assertRaises(ZConfig.SchemaError, self.load_schema_text, """\
""")
def test_sectiontype_derived_keytype(self):
# make sure that a derived section type inherits the keytype
# of its base
schema = self.load_schema_text("""\
""")
conf = self.load_config_text(schema, """\
foo bar
Foo BAR
""")
self.assertEqual(conf.foo.foo, "bar")
self.assertEqual(conf.foo.Foo, "BAR")
self.assertEqual(get_section_attributes(conf.foo), ["Foo", "foo"])
def test_sectiontype_override_keytype(self):
schema = self.load_schema_text("""\
""")
conf = self.load_config_text(schema, """\
ident1 foo
Ident2 bar
EXAMPLE.COM foo
""")
L = conf.base.map.items()
L.sort()
self.assertEqual(L, [("Ident2", "bar"), ("ident1", "foo")])
L = conf.derived.map.items()
L.sort()
self.assertEqual(L, [("example.com", "foo")])
self.assertEqual(get_section_attributes(conf), ["base", "derived"])
def test_keytype_applies_to_default_key(self):
schema = self.load_schema_text("""\
42
24
""")
conf = self.load_config_text(schema, "")
items = conf.sect.mapping.items()
items.sort()
self.assertEqual(items, [("bar", "24"), ("foo", "42")])
def test_duplicate_default_key_checked_in_schema(self):
self.assertRaises(ZConfig.SchemaError,
self.load_schema_text, """\
42
24
""")
def test_default_keys_rechecked_clash_in_derived_sectiontype(self):
# If the default values associated with a can't
# be supported by a new keytype for a derived sectiontype, an
# error should be indicated.
self.assertRaises(ZConfig.SchemaError,
self.load_schema_text, """\
42
42
""")
def test_default_keys_rechecked_dont_clash_in_derived_sectiontype(self):
# If the default values associated with a can't
# be supported by a new keytype for a derived sectiontype, an
# error should be indicated.
schema = self.load_schema_text("""\
42
42
""")
conf = self.load_config_text(schema, """\
""")
base = conf.base.mapping.items()
base.sort()
self.assertEqual(base, [("Foo", ["42"]), ("foo", ["42"])])
sect = conf.sect.mapping.items()
sect.sort()
self.assertEqual(sect, [("foo", ["42", "42"])])
def test_sectiontype_inherited_datatype(self):
schema = self.load_schema_text("""\
""")
conf = self.load_config_text(schema, """\
foo bar
""")
self.assertEqual(conf.splat, "bar")
def test_schema_keytype(self):
schema = self.load_schema_text("""\
""")
conf = self.load_config_text(schema,
"host.example.com 127.0.0.1\n"
"www.example.org 127.0.0.2\n")
table = conf.table
self.assertEqual(len(table), 2)
L = table.items()
L.sort()
self.assertEqual(L, [("host.example.com", "127.0.0.1"),
("www.example.org", "127.0.0.2")])
self.assertRaises(ZConfig.ConfigurationError,
self.load_config_text, schema, "abc. 127.0.0.1")
def test_keytype_identifier(self):
schema = self.load_schema_text("""\
""")
conf = self.load_config_text(schema,
"Foo Foo-value\n"
"foo foo-value\n")
self.assertEqual(conf.foo, "foo-value")
self.assertEqual(conf.Foo, "Foo-value")
self.assertEqual(get_section_attributes(conf), ["Foo", "foo"])
# key mis-match based on case:
self.assertRaises(ZConfig.ConfigurationError,
self.load_config_text, schema, "FOO frob\n")
# attribute names conflict, since the keytype isn't used to
# generate attribute names
self.assertRaises(ZConfig.SchemaError,
self.load_schema_text, """\
""")
def test_datatype_casesensitivity(self):
self.load_schema_text("")
def test_simple_extends(self):
schema = self.load_schema_text("""\
""" % (CONFIG_BASE, CONFIG_BASE))
self._verifySimpleConf(self.load_config(schema, "simple.conf"))
def test_extends_fragment_failure(self):
self.assertRaises(ZConfig.SchemaError,
self.load_schema_text,
"" % CONFIG_BASE)
def test_multi_extends_implicit_OK(self):
self.load_schema_text("""\
""" % (CONFIG_BASE, CONFIG_BASE))
def test_multi_extends_explicit_datatype_OK(self):
self.load_schema_text("""\
""" % (CONFIG_BASE, CONFIG_BASE))
def test_multi_extends_explicit_keytype_OK(self):
self.load_schema_text("""\
""" % (CONFIG_BASE, CONFIG_BASE, __name__))
def test_multi_extends_datatype_conflict(self):
self.assertRaises(ZConfig.SchemaError,
self.load_schema_text, """\
""" % (CONFIG_BASE, CONFIG_BASE))
def test_multi_extends_keytype_conflict(self):
self.assertRaises(ZConfig.SchemaError,
self.load_schema_text, """\
""" % (CONFIG_BASE, CONFIG_BASE))
def test_multiple_descriptions_is_error(self):
self.assertRaises(ZConfig.SchemaError,
self.load_schema_text, """\
foo
bar
""")
def test_suite():
return unittest.makeSuite(SchemaTestCase)
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')