Update MANIFEST.in template and tests
- Now MANIFEST.in includes package data inclusions and exclusions - Clean test subpackage - Test factory moved to a separated module - Add a factory argument for checking with lxml - SchemaObserver moved to a separated module - Test factory now can creates tests for XMLSchema11 class
This commit is contained in:
parent
310052c5f6
commit
ca47623dba
14
MANIFEST.in
14
MANIFEST.in
|
@ -1,2 +1,14 @@
|
|||
# Include the license file
|
||||
include MANIFEST.in
|
||||
include LICENSE
|
||||
include README.rst
|
||||
include CHANGELOG.rst
|
||||
include setup.py
|
||||
include setup.cfg
|
||||
include requirements-dev.txt
|
||||
include tox.ini
|
||||
include doc/*
|
||||
|
||||
recursive-include xmlschema *
|
||||
recursive-exclude xmlschema/tests/extra* *
|
||||
|
||||
global-exclude *.py[cod]
|
||||
|
|
3
setup.py
3
setup.py
|
@ -9,7 +9,7 @@
|
|||
#
|
||||
# @author Davide Brunato <brunato@sissa.it>
|
||||
#
|
||||
from setuptools import setup
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
with open("README.rst") as readme:
|
||||
long_description = readme.read()
|
||||
|
@ -20,7 +20,6 @@ setup(
|
|||
install_requires=['elementpath>=1.1.0', 'defusedxml>=0.5'],
|
||||
packages=['xmlschema'],
|
||||
include_package_data=True,
|
||||
exclude_package_date={'xmlschema': ['tests/extra-*']},
|
||||
author='Davide Brunato',
|
||||
author_email='brunato@sissa.it',
|
||||
url='https://github.com/brunato/xmlschema',
|
||||
|
|
|
@ -9,21 +9,14 @@
|
|||
# @author Davide Brunato <brunato@sissa.it>
|
||||
#
|
||||
"""
|
||||
Tests subpackage imports and methods for unittest scripts of the 'xmlschema' package.
|
||||
Tests subpackage module: common definitions for unittest scripts of the 'xmlschema' package.
|
||||
"""
|
||||
import unittest
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
import glob
|
||||
import fileinput
|
||||
import argparse
|
||||
import logging
|
||||
from functools import wraps
|
||||
|
||||
import xmlschema
|
||||
from xmlschema import XMLSchema, XMLSchema10
|
||||
from xmlschema.validators import XMLSchema11
|
||||
from xmlschema import XMLSchema
|
||||
from xmlschema.compat import urlopen, URLError
|
||||
from xmlschema.exceptions import XMLSchemaValueError
|
||||
from xmlschema.etree import (
|
||||
|
@ -34,7 +27,8 @@ from xmlschema.qnames import XSD_SCHEMA
|
|||
from xmlschema.helpers import get_namespace
|
||||
from xmlschema.namespaces import XSD_NAMESPACE
|
||||
|
||||
logger = logging.getLogger('xmlschema.tests')
|
||||
from .schema_observers import SchemaObserver
|
||||
from .test_factory import tests_factory
|
||||
|
||||
|
||||
def has_network_access(*locations):
|
||||
|
@ -49,8 +43,7 @@ def has_network_access(*locations):
|
|||
|
||||
|
||||
SKIP_REMOTE_TESTS = not has_network_access('http://www.sissa.it', 'http://www.w3.org/', 'http://dublincore.org/')
|
||||
|
||||
PROTECTED_PREFIX_PATTERN = re.compile("ns\d:")
|
||||
PROTECTED_PREFIX_PATTERN = re.compile(r'ns\d:')
|
||||
|
||||
|
||||
def print_test_header():
|
||||
|
@ -58,190 +51,6 @@ def print_test_header():
|
|||
print("*" * len(header) + '\n' + header + '\n' + "*" * len(header))
|
||||
|
||||
|
||||
def get_testfiles(test_dir):
|
||||
# Checks arguments and defines the testfiles lists to use
|
||||
if '-x' not in sys.argv and '--extra' not in sys.argv:
|
||||
testfiles = glob.glob(os.path.join(test_dir, 'cases/testfiles'))
|
||||
else:
|
||||
testfiles = glob.glob(os.path.join(test_dir, '*/testfiles'))
|
||||
try:
|
||||
sys.argv.remove('-x')
|
||||
except ValueError:
|
||||
sys.argv.remove('--extra')
|
||||
return testfiles
|
||||
|
||||
|
||||
class SchemaObserver(object):
|
||||
components = []
|
||||
|
||||
@classmethod
|
||||
def observe_builder(cls, builder):
|
||||
if isinstance(builder, type):
|
||||
class BuilderProxy(builder):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(BuilderProxy, self).__init__(*args, **kwargs)
|
||||
cls.components.append(self)
|
||||
BuilderProxy.__name__ = builder.__name__
|
||||
return BuilderProxy
|
||||
|
||||
elif callable(builder):
|
||||
@wraps(builder)
|
||||
def builder_proxy(*args, **kwargs):
|
||||
result = builder(*args, **kwargs)
|
||||
cls.components.append(result)
|
||||
return result
|
||||
return builder_proxy
|
||||
|
||||
@classmethod
|
||||
def clear(cls):
|
||||
del cls.components[:]
|
||||
|
||||
|
||||
class ObservedXMLSchema10(XMLSchema10):
|
||||
BUILDERS = {
|
||||
k: SchemaObserver.observe_builder(getattr(XMLSchema10.BUILDERS, k))
|
||||
for k in getattr(XMLSchema10.BUILDERS, '_fields')
|
||||
}
|
||||
|
||||
|
||||
def get_test_args(args_line):
|
||||
try:
|
||||
args_line, _ = args_line.split('#', 1)
|
||||
except ValueError:
|
||||
pass
|
||||
return re.split(r'(?<!\\) ', args_line.strip())
|
||||
|
||||
|
||||
def xsd_version_number(value):
|
||||
if value not in ('1.0', '1.1'):
|
||||
msg = "%r is not an XSD version." % value
|
||||
raise argparse.ArgumentTypeError(msg)
|
||||
return value
|
||||
|
||||
|
||||
def defuse_data(value):
|
||||
if value not in ('always', 'remote', 'never'):
|
||||
msg = "%r is not a valid value." % value
|
||||
raise argparse.ArgumentTypeError(msg)
|
||||
return value
|
||||
|
||||
|
||||
def get_args_parser():
|
||||
parser = argparse.ArgumentParser(add_help=True)
|
||||
parser.usage = "TEST_FILE [OPTIONS]\nTry 'TEST_FILE --help' for more information."
|
||||
parser.add_argument('filename', metavar='TEST_FILE', type=str, help="Test filename (relative path).")
|
||||
parser.add_argument(
|
||||
'-L', dest='locations', nargs=2, type=str, default=None, action='append',
|
||||
metavar="URI-URL", help="Schema location hint overrides."
|
||||
)
|
||||
parser.add_argument(
|
||||
'--version', dest='version', metavar='VERSION', type=xsd_version_number, default='1.0',
|
||||
help="XSD schema version to use for the test case (default is 1.0)."
|
||||
)
|
||||
parser.add_argument(
|
||||
'--errors', type=int, default=0, metavar='NUM', help="Number of errors expected (default=0)."
|
||||
)
|
||||
parser.add_argument(
|
||||
'--warnings', type=int, default=0, metavar='NUM', help="Number of warnings expected (default=0)."
|
||||
)
|
||||
parser.add_argument(
|
||||
'--inspect', action="store_true", default=False, help="Inspect using an observed custom schema class."
|
||||
)
|
||||
parser.add_argument(
|
||||
'--defuse', metavar='(always, remote, never)', type=defuse_data, default='remote',
|
||||
help="Define when to use the defused XML data loaders."
|
||||
)
|
||||
parser.add_argument(
|
||||
'--timeout', type=int, default=300, metavar='SEC', help="Timeout for fetching resources (default=300)."
|
||||
)
|
||||
parser.add_argument(
|
||||
'--defaults', action="store_true", default=False,
|
||||
help="Test data uses default or fixed values (skip strict encoding checks).",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--skip', action="store_true", default=False,
|
||||
help="Skip strict encoding checks (for cases where test data uses default or "
|
||||
"fixed values or some test data are skipped by wildcards processContents)."
|
||||
)
|
||||
parser.add_argument(
|
||||
'--debug', action="store_true", default=False,
|
||||
help="Activate the debug mode (only the cases with --debug are executed).",
|
||||
)
|
||||
return parser
|
||||
|
||||
|
||||
test_line_parser = get_args_parser()
|
||||
|
||||
|
||||
def tests_factory(test_class_builder, testfiles, suffix="xml"):
|
||||
test_classes = {}
|
||||
test_num = 0
|
||||
debug_mode = False
|
||||
line_buffer = []
|
||||
|
||||
for line in fileinput.input(testfiles):
|
||||
line = line.strip()
|
||||
if not line or line[0] == '#':
|
||||
if not line_buffer:
|
||||
continue
|
||||
else:
|
||||
raise SyntaxError("Empty continuation at line %d!" % fileinput.filelineno())
|
||||
elif '#' in line:
|
||||
line = line.split('#', 1)[0].rstrip()
|
||||
|
||||
# Process line continuations
|
||||
if line[-1] == '\\':
|
||||
line_buffer.append(line[:-1].strip())
|
||||
continue
|
||||
elif line_buffer:
|
||||
line_buffer.append(line)
|
||||
line = ' '.join(line_buffer)
|
||||
del line_buffer[:]
|
||||
|
||||
test_args = test_line_parser.parse_args(get_test_args(line))
|
||||
if test_args.locations is not None:
|
||||
test_args.locations = {k.strip('\'"'): v for k, v in test_args.locations}
|
||||
|
||||
test_file = os.path.join(os.path.dirname(fileinput.filename()), test_args.filename)
|
||||
if os.path.isdir(test_file):
|
||||
logger.debug("Skip %s: is a directory.", test_file)
|
||||
continue
|
||||
elif os.path.splitext(test_file)[1].lower() != '.%s' % suffix:
|
||||
logger.debug("Skip %s: wrong suffix.", test_file)
|
||||
continue
|
||||
elif not os.path.isfile(test_file):
|
||||
logger.error("Skip %s: is not a file.", test_file)
|
||||
continue
|
||||
|
||||
test_num += 1
|
||||
|
||||
# Debug mode activation
|
||||
if debug_mode:
|
||||
if not test_args.debug:
|
||||
continue
|
||||
elif test_args.debug:
|
||||
debug_mode = True
|
||||
logger.debug("Debug mode activated: discard previous %r test classes.", len(test_classes))
|
||||
test_classes.clear()
|
||||
|
||||
if test_args.inspect:
|
||||
test_class = test_class_builder(test_file, test_args, test_num, ObservedXMLSchema10)
|
||||
else:
|
||||
test_class = test_class_builder(test_file, test_args, test_num)
|
||||
|
||||
test_classes[test_class.__name__] = test_class
|
||||
logger.debug("Add XSD 1.0 test class %r.", test_class.__name__)
|
||||
|
||||
# test_class = test_class_builder(test_file, test_args, test_num, XMLSchema11)
|
||||
# test_classes[test_class.__name__] = test_class
|
||||
# logger.debug("Add XSD 1.1 test class for %r.", test_class.__name__)
|
||||
|
||||
if line_buffer:
|
||||
raise ValueError("Not completed line continuation at the end!")
|
||||
|
||||
return test_classes
|
||||
|
||||
|
||||
class XMLSchemaTestCase(unittest.TestCase):
|
||||
"""
|
||||
XMLSchema TestCase class.
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c), 2016-2018, 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>
|
||||
#
|
||||
"""
|
||||
Observers for XMLSchema classes.
|
||||
"""
|
||||
from functools import wraps
|
||||
|
||||
from xmlschema.validators import XMLSchema10, XMLSchema11
|
||||
|
||||
|
||||
class SchemaObserver(object):
|
||||
"""
|
||||
Observer that registers created components. Run the 'clear' method after each usage.
|
||||
"""
|
||||
components = []
|
||||
|
||||
@classmethod
|
||||
def observed_builder(cls, builder):
|
||||
if isinstance(builder, type):
|
||||
class BuilderProxy(builder):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(BuilderProxy, self).__init__(*args, **kwargs)
|
||||
cls.components.append(self)
|
||||
BuilderProxy.__name__ = builder.__name__
|
||||
return BuilderProxy
|
||||
|
||||
elif callable(builder):
|
||||
@wraps(builder)
|
||||
def builder_proxy(*args, **kwargs):
|
||||
result = builder(*args, **kwargs)
|
||||
cls.components.append(result)
|
||||
return result
|
||||
return builder_proxy
|
||||
|
||||
@classmethod
|
||||
def clear(cls):
|
||||
del cls.components[:]
|
||||
|
||||
|
||||
class ObservedXMLSchema10(XMLSchema10):
|
||||
BUILDERS = {
|
||||
k: SchemaObserver.observed_builder(getattr(XMLSchema10.BUILDERS, k))
|
||||
for k in getattr(XMLSchema10.BUILDERS, '_fields')
|
||||
}
|
||||
|
||||
|
||||
class ObservedXMLSchema11(XMLSchema11):
|
||||
BUILDERS = {
|
||||
k: SchemaObserver.observed_builder(getattr(XMLSchema11.BUILDERS, k))
|
||||
for k in getattr(XMLSchema11.BUILDERS, '_fields')
|
||||
}
|
|
@ -25,7 +25,7 @@ if __name__ == '__main__':
|
|||
sys.path.insert(0, pkg_base_dir)
|
||||
import xmlschema
|
||||
|
||||
from xmlschema.tests import tests_factory, print_test_header, get_testfiles
|
||||
from xmlschema.tests import print_test_header, tests_factory
|
||||
from xmlschema.tests.test_helpers import TestHelpers
|
||||
from xmlschema.tests.test_meta import TestXsd10BuiltinTypes, TestGlobalMaps
|
||||
from xmlschema.tests.test_regex import TestCodePoints, TestUnicodeSubset, TestUnicodeCategories, TestPatterns
|
||||
|
@ -39,8 +39,6 @@ if __name__ == '__main__':
|
|||
from xmlschema.tests.test_package import TestPackaging
|
||||
|
||||
print_test_header()
|
||||
|
||||
testfiles = get_testfiles(os.path.dirname(os.path.abspath(__file__)))
|
||||
globals().update(tests_factory(make_schema_test_class, testfiles, 'xsd'))
|
||||
globals().update(tests_factory(make_validator_test_class, testfiles, 'xml'))
|
||||
globals().update(tests_factory(make_schema_test_class, 'xsd'))
|
||||
globals().update(tests_factory(make_validator_test_class, 'xml'))
|
||||
unittest.main()
|
||||
|
|
|
@ -0,0 +1,190 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c), 2016-2018, 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>
|
||||
#
|
||||
"""
|
||||
Test factory for creating test cases from lists of paths to XSD or XML files.
|
||||
|
||||
The list of cases can be defined within files named "testfiles". These are text files
|
||||
that contain a list of relative paths to XSD or XML files, that are used to dinamically
|
||||
build a set of test classes. Each path is followed by a list of options that defines a
|
||||
custom setting for each test.
|
||||
"""
|
||||
import sys
|
||||
import re
|
||||
import os
|
||||
import glob
|
||||
import fileinput
|
||||
import argparse
|
||||
import logging
|
||||
|
||||
from xmlschema.validators import XMLSchema10, XMLSchema11
|
||||
from .schema_observers import ObservedXMLSchema10, ObservedXMLSchema11
|
||||
|
||||
logger = logging.getLogger(__file__)
|
||||
|
||||
|
||||
TEST_FACTORY_OPTIONS = {
|
||||
'extra_files': '-x' in sys.argv or '--extra' in sys.argv, # Include extra test cases
|
||||
'check_with_lxml': '-l' in sys.argv or '--lxml' in sys.argv, # Check with lxml.etree.XMLSchema (for XSD 1.0)
|
||||
}
|
||||
"""Command line options for test factory."""
|
||||
|
||||
sys.argv = [a for a in sys.argv if a not in {'-x', '--extra', '-l', '--lxml'}] # Clean sys.argv for unittest
|
||||
|
||||
|
||||
def get_test_args(args_line):
|
||||
"""Returns the list of arguments from provided text line."""
|
||||
try:
|
||||
args_line, _ = args_line.split('#', 1) # Strip optional ending comment
|
||||
except ValueError:
|
||||
pass
|
||||
return re.split(r'(?<!\\) ', args_line.strip())
|
||||
|
||||
|
||||
def create_test_line_args_parser():
|
||||
"""Creates an arguments parser for uncommented on not blank "testfiles" lines."""
|
||||
|
||||
def xsd_version_number(value):
|
||||
if value not in ('1.0', '1.1'):
|
||||
msg = "%r is not an XSD version." % value
|
||||
raise argparse.ArgumentTypeError(msg)
|
||||
return value
|
||||
|
||||
def defuse_data(value):
|
||||
if value not in ('always', 'remote', 'never'):
|
||||
msg = "%r is not a valid value." % value
|
||||
raise argparse.ArgumentTypeError(msg)
|
||||
return value
|
||||
|
||||
parser = argparse.ArgumentParser(add_help=True)
|
||||
parser.usage = "TEST_FILE [OPTIONS]\nTry 'TEST_FILE --help' for more information."
|
||||
parser.add_argument('filename', metavar='TEST_FILE', type=str, help="Test filename (relative path).")
|
||||
parser.add_argument(
|
||||
'-L', dest='locations', nargs=2, type=str, default=None, action='append',
|
||||
metavar="URI-URL", help="Schema location hint overrides."
|
||||
)
|
||||
parser.add_argument(
|
||||
'--version', dest='version', metavar='VERSION', type=xsd_version_number, default='1.0',
|
||||
help="XSD schema version to use for the test case (default is 1.0)."
|
||||
)
|
||||
parser.add_argument(
|
||||
'--errors', type=int, default=0, metavar='NUM', help="Number of errors expected (default=0)."
|
||||
)
|
||||
parser.add_argument(
|
||||
'--warnings', type=int, default=0, metavar='NUM', help="Number of warnings expected (default=0)."
|
||||
)
|
||||
parser.add_argument(
|
||||
'--inspect', action="store_true", default=False, help="Inspect using an observed custom schema class."
|
||||
)
|
||||
parser.add_argument(
|
||||
'--defuse', metavar='(always, remote, never)', type=defuse_data, default='remote',
|
||||
help="Define when to use the defused XML data loaders."
|
||||
)
|
||||
parser.add_argument(
|
||||
'--timeout', type=int, default=300, metavar='SEC', help="Timeout for fetching resources (default=300)."
|
||||
)
|
||||
parser.add_argument(
|
||||
'--defaults', action="store_true", default=False,
|
||||
help="Test data uses default or fixed values (skip strict encoding checks).",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--skip', action="store_true", default=False,
|
||||
help="Skip strict encoding checks (for cases where test data uses default or "
|
||||
"fixed values or some test data are skipped by wildcards processContents)."
|
||||
)
|
||||
parser.add_argument(
|
||||
'--debug', action="store_true", default=False,
|
||||
help="Activate the debug mode (only the cases with --debug are executed).",
|
||||
)
|
||||
return parser
|
||||
|
||||
|
||||
test_line_parser = create_test_line_args_parser()
|
||||
|
||||
|
||||
def tests_factory(test_class_builder, suffix='xml'):
|
||||
"""
|
||||
Factory function for file based schema/validation cases.
|
||||
|
||||
:param test_class_builder: the test class builder function.
|
||||
:param suffix: the suffix ('xml' or 'xsd') to consider for cases.
|
||||
:return: a list of test classes.
|
||||
"""
|
||||
test_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
test_classes = {}
|
||||
test_num = 0
|
||||
debug_mode = False
|
||||
line_buffer = []
|
||||
|
||||
if TEST_FACTORY_OPTIONS['extra_files']:
|
||||
testfiles = glob.glob(os.path.join(test_dir, '*/testfiles'))
|
||||
else:
|
||||
testfiles = glob.glob(os.path.join(test_dir, 'cases/testfiles'))
|
||||
|
||||
for line in fileinput.input(testfiles):
|
||||
line = line.strip()
|
||||
if not line or line[0] == '#':
|
||||
if not line_buffer:
|
||||
continue
|
||||
else:
|
||||
raise SyntaxError("Empty continuation at line %d!" % fileinput.filelineno())
|
||||
elif '#' in line:
|
||||
line = line.split('#', 1)[0].rstrip()
|
||||
|
||||
# Process line continuations
|
||||
if line[-1] == '\\':
|
||||
line_buffer.append(line[:-1].strip())
|
||||
continue
|
||||
elif line_buffer:
|
||||
line_buffer.append(line)
|
||||
line = ' '.join(line_buffer)
|
||||
del line_buffer[:]
|
||||
|
||||
test_args = test_line_parser.parse_args(get_test_args(line))
|
||||
if test_args.locations is not None:
|
||||
test_args.locations = {k.strip('\'"'): v for k, v in test_args.locations}
|
||||
|
||||
test_file = os.path.join(os.path.dirname(fileinput.filename()), test_args.filename)
|
||||
if os.path.isdir(test_file):
|
||||
logger.debug("Skip %s: is a directory.", test_file)
|
||||
continue
|
||||
elif os.path.splitext(test_file)[1].lower() != '.%s' % suffix:
|
||||
logger.debug("Skip %s: wrong suffix.", test_file)
|
||||
continue
|
||||
elif not os.path.isfile(test_file):
|
||||
logger.error("Skip %s: is not a file.", test_file)
|
||||
continue
|
||||
|
||||
test_num += 1
|
||||
|
||||
# Debug mode activation
|
||||
if debug_mode:
|
||||
if not test_args.debug:
|
||||
continue
|
||||
elif test_args.debug:
|
||||
debug_mode = True
|
||||
logger.debug("Debug mode activated: discard previous %r test classes.", len(test_classes))
|
||||
test_classes.clear()
|
||||
|
||||
if test_args.version == '1.0':
|
||||
schema_class = ObservedXMLSchema10 if test_args.inspect else XMLSchema10
|
||||
check_with_lxml = TEST_FACTORY_OPTIONS['check_with_lxml']
|
||||
else:
|
||||
schema_class = ObservedXMLSchema11 if test_args.inspect else XMLSchema11
|
||||
check_with_lxml = False
|
||||
|
||||
test_class = test_class_builder(test_file, test_args, test_num, schema_class, check_with_lxml)
|
||||
test_classes[test_class.__name__] = test_class
|
||||
logger.debug("Add XSD %s test class %r.", test_args.version, test_class.__name__)
|
||||
|
||||
if line_buffer:
|
||||
raise ValueError("Not completed line continuation at the end!")
|
||||
|
||||
return test_classes
|
|
@ -12,18 +12,14 @@
|
|||
"""
|
||||
This module runs tests concerning the building of XSD schemas with the 'xmlschema' package.
|
||||
"""
|
||||
from __future__ import print_function, unicode_literals
|
||||
import unittest
|
||||
import os
|
||||
import sys
|
||||
import pickle
|
||||
import time
|
||||
import warnings
|
||||
|
||||
try:
|
||||
# noinspection PyPackageRequirements
|
||||
import lxml.etree as _lxml_etree
|
||||
except ImportError:
|
||||
_lxml_etree = None
|
||||
|
||||
try:
|
||||
import xmlschema
|
||||
except ImportError:
|
||||
|
@ -37,7 +33,9 @@ from xmlschema import XMLSchemaBase, XMLSchema, XMLSchemaParseError, XMLSchemaVa
|
|||
from xmlschema.compat import PY3, unicode_type
|
||||
from xmlschema.qnames import XSD_LIST, XSD_UNION
|
||||
from xmlschema.tests import SKIP_REMOTE_TESTS, SchemaObserver, XMLSchemaTestCase
|
||||
from xmlschema.etree import lxml_etree
|
||||
from xmlschema.etree import defused_etree
|
||||
|
||||
from xmlschema.xpath import ElementPathContext
|
||||
from xmlschema.validators import XsdValidator, XMLSchema11
|
||||
|
||||
|
@ -475,8 +473,17 @@ class TestXMLSchema11(TestXMLSchema10):
|
|||
# self.assertTrue(schema.types['RestrictedDateTimeType'].is_valid('2000-01-01T12:00:00'))
|
||||
|
||||
|
||||
def make_schema_test_class(test_file, test_args, test_num=0, schema_class=None):
|
||||
def make_schema_test_class(test_file, test_args, test_num=0, schema_class=None, check_with_lxml=True):
|
||||
"""
|
||||
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 = test_file
|
||||
if schema_class is None:
|
||||
schema_class = XMLSchema
|
||||
|
@ -528,6 +535,7 @@ def make_schema_test_class(test_file, test_args, test_num=0, schema_class=None):
|
|||
|
||||
return errors_
|
||||
|
||||
start_time = time.time()
|
||||
if expected_warnings > 0:
|
||||
with warnings.catch_warnings(record=True) as ctx:
|
||||
warnings.simplefilter("always")
|
||||
|
@ -536,7 +544,31 @@ def make_schema_test_class(test_file, test_args, test_num=0, schema_class=None):
|
|||
else:
|
||||
errors = check_schema()
|
||||
|
||||
# Checks errors completeness
|
||||
# Check with lxml.etree.XMLSchema class
|
||||
if check_with_lxml and lxml_etree is not None:
|
||||
schema_time = time.time() - start_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 errors:
|
||||
print("\nSchema error with lxml.etree.XMLSchema for file {!r} ({}): {}".format(
|
||||
rel_path, class_name, unicode_type(err)
|
||||
))
|
||||
else:
|
||||
if errors:
|
||||
print("\nUnrecognized errors with lxml.etree.XMLSchema for file {!r} ({}): {}".format(
|
||||
rel_path, class_name, '\n++++++\n'.join([unicode_type(e) for e in errors])
|
||||
))
|
||||
lxml_schema_time = time.time() - start_time
|
||||
if lxml_schema_time >= schema_time:
|
||||
print(
|
||||
"\nSlower lxml.etree.XMLSchema ({:.3f}s VS {:.3f}s) with file {!r} ({})".format(
|
||||
lxml_schema_time, schema_time, rel_path, class_name
|
||||
))
|
||||
|
||||
# Check errors completeness
|
||||
for e in errors:
|
||||
error_string = unicode_type(e)
|
||||
self.assertTrue(e.path, "Missing path for: %s" % error_string)
|
||||
|
@ -558,16 +590,15 @@ def make_schema_test_class(test_file, test_args, test_num=0, schema_class=None):
|
|||
rel_path = os.path.relpath(test_file)
|
||||
class_name = 'Test{}_{:03}'.format(schema_class.__name__, test_num)
|
||||
return type(
|
||||
class_name, (XMLSchemaTestCase,),
|
||||
{'test_schema_{0:03}_{1}'.format(test_num, rel_path): test_schema}
|
||||
)
|
||||
class_name, (XMLSchemaTestCase,), {
|
||||
'test_schema_{0:03}_{1}'.format(test_num, rel_path): test_schema
|
||||
})
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from xmlschema.tests import print_test_header, get_testfiles, tests_factory
|
||||
from xmlschema.tests import print_test_header, tests_factory
|
||||
|
||||
print_test_header()
|
||||
testfiles = get_testfiles(os.path.dirname(os.path.abspath(__file__)))
|
||||
schema_tests = tests_factory(make_schema_test_class, testfiles, suffix='xsd')
|
||||
schema_tests = tests_factory(make_schema_test_class, 'xsd')
|
||||
globals().update(schema_tests)
|
||||
unittest.main()
|
||||
|
|
|
@ -40,7 +40,7 @@ from xmlschema.resources import fetch_namespaces
|
|||
from xmlschema.tests import XMLSchemaTestCase
|
||||
from xmlschema.etree import (
|
||||
etree_element, etree_tostring, is_etree_element, etree_fromstring, etree_parse,
|
||||
etree_elements_assert_equal, lxml_etree_parse, lxml_etree_element
|
||||
etree_elements_assert_equal, lxml_etree, lxml_etree_parse, lxml_etree_element
|
||||
)
|
||||
from xmlschema.qnames import XSI_TYPE
|
||||
from xmlschema.helpers import local_name
|
||||
|
@ -291,7 +291,19 @@ def iter_nested_items(items, dict_class=dict, list_class=list):
|
|||
yield items
|
||||
|
||||
|
||||
def make_validator_test_class(test_file, test_args, test_num=0, schema_class=XMLSchema):
|
||||
def make_validator_test_class(test_file, test_args, test_num=0, schema_class=None, check_with_lxml=False):
|
||||
"""
|
||||
Creates a validator test class.
|
||||
|
||||
:param test_file: the XML 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.
|
||||
"""
|
||||
if schema_class is None:
|
||||
schema_class = XMLSchema
|
||||
|
||||
# Extract schema test arguments
|
||||
expected_errors = test_args.errors
|
||||
|
@ -317,6 +329,8 @@ def make_validator_test_class(test_file, test_args, test_num=0, schema_class=XML
|
|||
# Builds schema instance using 'lax' validation mode to accepts also schemas with not crashing errors.
|
||||
source, _locations = xmlschema.fetch_schema_locations(xml_file, locations)
|
||||
cls.schema = schema_class(source, validation='lax', locations=_locations, defuse=defuse)
|
||||
if check_with_lxml and lxml_etree is not None:
|
||||
cls.lxml_schema = lxml_etree.parse(source)
|
||||
|
||||
cls.errors = []
|
||||
cls.chunks = []
|
||||
|
@ -543,6 +557,19 @@ def make_validator_test_class(test_file, test_args, test_num=0, schema_class=XML
|
|||
self.assertEqual(len(list(self.schema.iter_errors(xml_file))), expected_errors,
|
||||
msg_template % "wrong number of errors (%d expected)" % expected_errors)
|
||||
|
||||
def check_lxml_validation(self):
|
||||
try:
|
||||
schema = lxml_etree.XMLSchema(self.lxml_schema.getroot())
|
||||
except lxml_etree.XMLSchemaParseError:
|
||||
print("\nSkip lxml.etree.XMLSchema validation test for {!r} ({})".
|
||||
format(rel_path, TestValidator.__name__, ))
|
||||
else:
|
||||
xml_tree = lxml_etree_parse(xml_file)
|
||||
if self.errors:
|
||||
self.assertFalse(schema.validate(xml_tree))
|
||||
else:
|
||||
self.assertTrue(schema.validate(xml_tree))
|
||||
|
||||
def test_decoding_and_encoding(self):
|
||||
self.check_decoding_with_element_tree()
|
||||
|
||||
|
@ -557,6 +584,8 @@ def make_validator_test_class(test_file, test_args, test_num=0, schema_class=XML
|
|||
|
||||
self.check_iter_errors()
|
||||
self.check_validate_and_is_valid_api()
|
||||
if check_with_lxml and lxml_etree is not None:
|
||||
self.check_lxml_validation()
|
||||
|
||||
TestValidator.__name__ = TestValidator.__qualname__ = 'TestValidator{0:03}'.format(test_num)
|
||||
return TestValidator
|
||||
|
@ -1067,10 +1096,9 @@ class TestEncoding(XMLSchemaTestCase):
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from xmlschema.tests import print_test_header, tests_factory, get_testfiles
|
||||
from xmlschema.tests import print_test_header, tests_factory
|
||||
|
||||
print_test_header()
|
||||
testfiles = get_testfiles(os.path.dirname(os.path.abspath(__file__)))
|
||||
decoder_tests = tests_factory(make_validator_test_class, testfiles, 'xml')
|
||||
decoder_tests = tests_factory(make_validator_test_class, 'xml')
|
||||
globals().update(decoder_tests)
|
||||
unittest.main()
|
||||
|
|
Loading…
Reference in New Issue