debian-xmlschema/xmlschema/tests/__init__.py

209 lines
8.4 KiB
Python

# -*- coding: utf-8 -*-
#
# Copyright (c), 2016-2019, SISSA (International School for Advanced Studies).
# All rights reserved.
# This file is distributed under the terms of the MIT License.
# See the file 'LICENSE' in the root directory of the present
# distribution, or http://opensource.org/licenses/MIT.
#
# @author Davide Brunato <brunato@sissa.it>
#
"""
Tests subpackage module: common definitions for unittest scripts of the 'xmlschema' package.
"""
import unittest
import platform
import re
import os
import xmlschema
from xmlschema import XMLSchema
from xmlschema.compat import urlopen, URLError, unicode_type
from xmlschema.exceptions import XMLSchemaValueError
from xmlschema.qnames import XSD_SCHEMA
from xmlschema.namespaces import XSD_NAMESPACE, get_namespace
from xmlschema.etree import etree_element, etree_register_namespace, etree_elements_assert_equal
from xmlschema.resources import fetch_namespaces
from xmlschema.helpers import is_etree_element
def has_network_access(*locations):
for url in locations:
try:
urlopen(url, timeout=5)
except (URLError, OSError):
pass
else:
return True
return False
SKIP_REMOTE_TESTS = not has_network_access('http://www.sissa.it', 'http://www.w3.org/', 'http://dublincore.org/')
PROTECTED_PREFIX_PATTERN = re.compile(r'\bns\d:')
TEST_CASES_DIR = os.path.join(os.path.dirname(__file__), 'test_cases/')
SCHEMA_TEMPLATE = """<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" version="{0}">
{1}
</xs:schema>"""
def casepath(relative_path):
"""
Returns the absolute path from a relative path specified from the `xmlschema/tests/test_cases/` dir.
"""
return os.path.join(TEST_CASES_DIR, relative_path)
def print_test_header():
"""Print an header thar displays Python version and platform used for test session."""
header1 = "Test %r" % xmlschema
header2 = "with Python {} on platform {}".format(platform.python_version(), platform.platform())
print('{0}\n{1}\n{2}\n{0}'.format("*" * max(len(header1), len(header2)), header1, header2))
class XsdValidatorTestCase(unittest.TestCase):
"""
TestCase class for XSD validators.
"""
@classmethod
def casepath(cls, relative_path):
return casepath(relative_path)
etree_register_namespace(prefix='xs', uri=XSD_NAMESPACE)
etree_register_namespace(prefix='ns', uri="ns")
schema_class = XMLSchema
@classmethod
def setUpClass(cls):
cls.errors = []
cls.xsd_types = cls.schema_class.builtin_types()
cls.content_pattern = re.compile(r'(<|<xs:)(sequence|choice|all)')
cls.default_namespaces = {
'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
'tns': 'http://xmlschema.test/ns',
'ns': 'ns',
}
cls.vh_dir = casepath('examples/vehicles')
cls.vh_xsd_file = casepath('examples/vehicles/vehicles.xsd')
cls.vh_xml_file = casepath('examples/vehicles/vehicles.xml')
cls.vh_json_file = casepath('examples/vehicles/vehicles.json')
cls.vh_schema = cls.schema_class(cls.vh_xsd_file)
cls.vh_namespaces = fetch_namespaces(cls.vh_xml_file)
cls.col_dir = casepath('examples/collection')
cls.col_xsd_file = casepath('examples/collection/collection.xsd')
cls.col_xml_file = casepath('examples/collection/collection.xml')
cls.col_json_file = casepath('examples/collection/collection.json')
cls.col_schema = cls.schema_class(cls.col_xsd_file)
cls.col_namespaces = fetch_namespaces(cls.col_xml_file)
cls.st_xsd_file = casepath('features/decoder/simple-types.xsd')
cls.st_schema = cls.schema_class(cls.st_xsd_file)
cls.models_xsd_file = casepath('features/models/models.xsd')
cls.models_schema = cls.schema_class(cls.models_xsd_file)
def get_schema_source(self, source):
"""
Returns a schema source that can be used to create an XMLSchema instance.
:param source: A string or an ElementTree's Element.
:return: An schema source string, an ElementTree's Element or a full pathname.
"""
if is_etree_element(source):
if source.tag in (XSD_SCHEMA, 'schema'):
return source
elif get_namespace(source.tag):
raise XMLSchemaValueError("source %r namespace has to be empty." % source)
elif source.tag not in {'element', 'attribute', 'simpleType', 'complexType',
'group', 'attributeGroup', 'notation'}:
raise XMLSchemaValueError("% is not an XSD global definition/declaration." % source)
root = etree_element('schema', attrib={
'xmlns:xs': "http://www.w3.org/2001/XMLSchema",
'elementFormDefault': "qualified",
'version': self.schema_class.XSD_VERSION,
})
root.append(source)
return root
else:
source = source.strip()
if not source.startswith('<'):
return casepath(source)
elif source.startswith('<?xml ') or source.startswith('<xs:schema '):
return source
else:
return SCHEMA_TEMPLATE.format(self.schema_class.XSD_VERSION, source)
def get_schema(self, source):
return self.schema_class(self.get_schema_source(source))
def get_element(self, name, **attrib):
source = '<xs:element name="{}" {}/>'.format(
name, ' '.join('%s="%s"' % (k, v) for k, v in attrib.items())
)
schema = self.schema_class(self.get_schema_source(source))
return schema.elements[name]
def check_etree_elements(self, elem, other):
"""Checks if two ElementTree elements are equal."""
try:
self.assertIsNone(etree_elements_assert_equal(elem, other, strict=False, skip_comments=True))
except AssertionError as err:
self.assertIsNone(err, None)
def check_namespace_prefixes(self, s):
"""Checks that a string doesn't contain protected prefixes (ns0, ns1 ...)."""
match = PROTECTED_PREFIX_PATTERN.search(s)
if match:
msg = "Protected prefix {!r} found:\n {}".format(match.group(0), s)
self.assertIsNone(match, msg)
def check_schema(self, source, expected=None, **kwargs):
"""
Create a schema for a test case.
:param source: A relative path or a root Element or a portion of schema for a template.
:param expected: If it's an Exception class test the schema for raise an error. \
Otherwise build the schema and test a condition if expected is a callable, or make \
a substring test if it's not `None` (maybe a string). Then returns the schema instance.
"""
if isinstance(expected, type) and issubclass(expected, Exception):
self.assertRaises(expected, self.schema_class, self.get_schema_source(source), **kwargs)
else:
schema = self.schema_class(self.get_schema_source(source), **kwargs)
if callable(expected):
self.assertTrue(expected(schema))
return schema
def check_errors(self, path, expected):
"""
Checks schema or validation errors, checking information completeness of the
instances and those number against expected.
:param path: the path of the test case.
:param expected: the number of expected errors.
"""
for e in self.errors:
error_string = unicode_type(e)
self.assertTrue(e.path, "Missing path for: %s" % error_string)
self.assertTrue(e.namespaces, "Missing namespaces for: %s" % error_string)
self.check_namespace_prefixes(error_string)
if not self.errors and expected:
raise ValueError("{!r}: found no errors when {} expected.".format(path, expected))
elif len(self.errors) != expected:
num_errors = len(self.errors)
if num_errors == 1:
msg = "{!r}: n.{} errors expected, found {}:\n\n{}"
elif num_errors <= 5:
msg = "{!r}: n.{} errors expected, found {}. Errors follow:\n\n{}"
else:
msg = "{!r}: n.{} errors expected, found {}. First five errors follow:\n\n{}"
error_string = '\n++++++++++\n\n'.join([unicode_type(e) for e in self.errors[:5]])
raise ValueError(msg.format(path, expected, len(self.errors), error_string))