debian-xmlschema/xmlschema/tests/test_factory/schema_tests.py

149 lines
6.1 KiB
Python

#!/usr/bin/env 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>
#
from __future__ import print_function, unicode_literals
import pdb
import os
import pickle
import time
import logging
import warnings
from xmlschema import XMLSchemaBase
from xmlschema.compat import PY3, unicode_type
from xmlschema.etree import lxml_etree, py_etree_element
from xmlschema.xpath import XMLSchemaContext
from xmlschema.validators import XsdValidator
from xmlschema.tests import XsdValidatorTestCase
from .observers import SchemaObserver
def make_schema_test_class(test_file, test_args, test_num, schema_class, check_with_lxml):
"""
Creates a schema test class.
:param test_file: the schema test file path.
:param test_args: line arguments for test case.
:param test_num: a positive integer number associated with the test case.
:param schema_class: the schema class to use.
:param check_with_lxml: if `True` compare with lxml XMLSchema class, reporting anomalies. \
Works only for XSD 1.0 tests.
"""
xsd_file = os.path.relpath(test_file)
# Extract schema test arguments
expected_errors = test_args.errors
expected_warnings = test_args.warnings
inspect = test_args.inspect
locations = test_args.locations
defuse = test_args.defuse
debug_mode = test_args.debug
loglevel = logging.DEBUG if debug_mode else None
class TestSchema(XsdValidatorTestCase):
@classmethod
def setUpClass(cls):
cls.schema_class = schema_class
cls.errors = []
cls.longMessage = True
if debug_mode:
print("\n##\n## Testing %r schema in debug mode.\n##" % xsd_file)
pdb.set_trace()
def check_xsd_file(self):
if expected_errors > 0:
xs = schema_class(xsd_file, validation='lax', locations=locations,
defuse=defuse, loglevel=loglevel)
else:
xs = schema_class(xsd_file, locations=locations, defuse=defuse, loglevel=loglevel)
self.errors.extend(xs.maps.all_errors)
if inspect:
components_ids = set([id(c) for c in xs.maps.iter_components()])
missing = [c for c in SchemaObserver.components if id(c) not in components_ids]
if any(c for c in missing):
raise ValueError("schema missing %d components: %r" % (len(missing), missing))
# Pickling test (only for Python 3, skip inspected schema classes test)
if not inspect and PY3:
try:
obj = pickle.dumps(xs)
deserialized_schema = pickle.loads(obj)
except pickle.PicklingError:
# Don't raise if some schema parts (eg. a schema loaded from remote)
# are built with the SafeXMLParser that uses pure Python elements.
for e in xs.maps.iter_components():
elem = getattr(e, 'elem', getattr(e, 'root', None))
if isinstance(elem, py_etree_element):
break
else:
raise
else:
self.assertTrue(isinstance(deserialized_schema, XMLSchemaBase))
self.assertEqual(xs.built, deserialized_schema.built)
# XPath API tests
if not inspect and not self.errors:
context = XMLSchemaContext(xs)
elements = [x for x in xs.iter()]
context_elements = [x for x in context.iter() if isinstance(x, XsdValidator)]
self.assertEqual(context_elements, [x for x in context.iter_descendants()])
self.assertEqual(context_elements, elements)
def check_xsd_file_with_lxml(self, xmlschema_time):
start_time = time.time()
lxs = lxml_etree.parse(xsd_file)
try:
lxml_etree.XMLSchema(lxs.getroot())
except lxml_etree.XMLSchemaParseError as err:
if not self.errors:
print("\nSchema error with lxml.etree.XMLSchema for file {!r} ({}): {}".format(
xsd_file, self.__class__.__name__, unicode_type(err)
))
else:
if self.errors:
print("\nUnrecognized errors with lxml.etree.XMLSchema for file {!r} ({}): {}".format(
xsd_file, self.__class__.__name__,
'\n++++++\n'.join([unicode_type(e) for e in self.errors])
))
lxml_schema_time = time.time() - start_time
if lxml_schema_time >= xmlschema_time:
print(
"\nSlower lxml.etree.XMLSchema ({:.3f}s VS {:.3f}s) with file {!r} ({})".format(
lxml_schema_time, xmlschema_time, xsd_file, self.__class__.__name__
))
def test_xsd_file(self):
if inspect:
SchemaObserver.clear()
del self.errors[:]
start_time = time.time()
if expected_warnings > 0:
with warnings.catch_warnings(record=True) as ctx:
warnings.simplefilter("always")
self.check_xsd_file()
self.assertEqual(len(ctx), expected_warnings,
"%r: Wrong number of include/import warnings" % xsd_file)
else:
self.check_xsd_file()
# Check with lxml.etree.XMLSchema class
if check_with_lxml and lxml_etree is not None:
self.check_xsd_file_with_lxml(xmlschema_time=time.time() - start_time)
self.check_errors(xsd_file, expected_errors)
TestSchema.__name__ = TestSchema.__qualname__ = str('TestSchema{0:03}'.format(test_num))
return TestSchema