#!/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 # import sys import unittest from xmlschema import XMLSchemaEncodeError, XMLSchemaValidationError from xmlschema.converters import UnorderedConverter from xmlschema.compat import unicode_type, ordered_dict_class from xmlschema.qnames import local_name from xmlschema.etree import etree_element, etree_tostring, ElementTree from xmlschema.validators.exceptions import XMLSchemaChildrenValidationError from xmlschema.helpers import is_etree_element from xmlschema.tests import XsdValidatorTestCase from xmlschema.validators import XMLSchema11 class TestEncoding(XsdValidatorTestCase): def check_encode(self, xsd_component, data, expected, **kwargs): if isinstance(expected, type) and issubclass(expected, Exception): self.assertRaises(expected, xsd_component.encode, data, **kwargs) elif is_etree_element(expected): elem = xsd_component.encode(data, **kwargs) self.check_etree_elements(expected, elem) else: obj = xsd_component.encode(data, **kwargs) if isinstance(obj, tuple) and len(obj) == 2 and isinstance(obj[1], list): self.assertEqual(expected, obj[0]) self.assertTrue(isinstance(obj[0], type(expected))) elif is_etree_element(obj): namespaces = kwargs.pop('namespaces', self.default_namespaces) self.assertEqual(expected, etree_tostring(obj, namespaces=namespaces).strip()) else: self.assertEqual(expected, obj) self.assertTrue(isinstance(obj, type(expected))) def test_decode_encode(self): """Test encode after a decode, checking the re-encoded tree.""" filename = self.casepath('examples/collection/collection.xml') xt = ElementTree.parse(filename) xd = self.col_schema.to_dict(filename, dict_class=ordered_dict_class) elem = self.col_schema.encode(xd, path='./col:collection', namespaces=self.col_namespaces) self.assertEqual( len([e for e in elem.iter()]), 20, msg="The encoded tree must have 20 elements as the origin." ) self.assertTrue(all( local_name(e1.tag) == local_name(e2.tag) for e1, e2 in zip(elem.iter(), xt.getroot().iter()) )) def test_string_based_builtin_types(self): self.check_encode(self.xsd_types['string'], 'sample string ', u'sample string ') self.check_encode(self.xsd_types['normalizedString'], ' sample string ', u' sample string ') self.check_encode(self.xsd_types['normalizedString'], '\n\r sample\tstring\n', u' sample string ') self.check_encode(self.xsd_types['token'], '\n\r sample\t\tstring\n ', u'sample string') self.check_encode(self.xsd_types['language'], 'sample string', XMLSchemaValidationError) self.check_encode(self.xsd_types['language'], ' en ', u'en') self.check_encode(self.xsd_types['Name'], 'first_name', u'first_name') self.check_encode(self.xsd_types['Name'], ' first_name ', u'first_name') self.check_encode(self.xsd_types['Name'], 'first name', XMLSchemaValidationError) self.check_encode(self.xsd_types['Name'], '1st_name', XMLSchemaValidationError) self.check_encode(self.xsd_types['Name'], 'first_name1', u'first_name1') self.check_encode(self.xsd_types['Name'], 'first:name', u'first:name') self.check_encode(self.xsd_types['NCName'], 'first_name', u'first_name') self.check_encode(self.xsd_types['NCName'], 'first:name', XMLSchemaValidationError) self.check_encode(self.xsd_types['ENTITY'], 'first:name', XMLSchemaValidationError) self.check_encode(self.xsd_types['ID'], 'first:name', XMLSchemaValidationError) self.check_encode(self.xsd_types['IDREF'], 'first:name', XMLSchemaValidationError) def test_decimal_based_builtin_types(self): self.check_encode(self.xsd_types['decimal'], -99.09, u'-99.09') self.check_encode(self.xsd_types['decimal'], '-99.09', u'-99.09') self.check_encode(self.xsd_types['integer'], 1000, u'1000') self.check_encode(self.xsd_types['integer'], 100.0, XMLSchemaEncodeError) self.check_encode(self.xsd_types['integer'], 100.0, u'100', validation='lax') self.check_encode(self.xsd_types['short'], 1999, u'1999') self.check_encode(self.xsd_types['short'], 10000000, XMLSchemaValidationError) self.check_encode(self.xsd_types['float'], 100.0, u'100.0') self.check_encode(self.xsd_types['float'], 'hello', XMLSchemaEncodeError) self.check_encode(self.xsd_types['double'], -4531.7, u'-4531.7') self.check_encode(self.xsd_types['positiveInteger'], -1, XMLSchemaValidationError) self.check_encode(self.xsd_types['positiveInteger'], 0, XMLSchemaValidationError) self.check_encode(self.xsd_types['nonNegativeInteger'], 0, u'0') self.check_encode(self.xsd_types['nonNegativeInteger'], -1, XMLSchemaValidationError) self.check_encode(self.xsd_types['negativeInteger'], -100, u'-100') self.check_encode(self.xsd_types['nonPositiveInteger'], 7, XMLSchemaValidationError) self.check_encode(self.xsd_types['unsignedLong'], 101, u'101') self.check_encode(self.xsd_types['unsignedLong'], -101, XMLSchemaValidationError) self.check_encode(self.xsd_types['nonPositiveInteger'], 7, XMLSchemaValidationError) def test_list_builtin_types(self): self.check_encode(self.xsd_types['IDREFS'], ['first_name'], u'first_name') self.check_encode(self.xsd_types['IDREFS'], 'first_name', u'first_name') # Transform data to list self.check_encode(self.xsd_types['IDREFS'], ['one', 'two', 'three'], u'one two three') self.check_encode(self.xsd_types['IDREFS'], [1, 'two', 'three'], XMLSchemaValidationError) self.check_encode(self.xsd_types['NMTOKENS'], ['one', 'two', 'three'], u'one two three') self.check_encode(self.xsd_types['ENTITIES'], ('mouse', 'cat', 'dog'), u'mouse cat dog') def test_datetime_builtin_type(self): xs = self.get_schema('') dt = xs.decode('
2019-01-01T13:40:00
', datetime_types=True) self.assertEqual(etree_tostring(xs.encode(dt)), '
2019-01-01T13:40:00
') def test_date_builtin_type(self): xs = self.get_schema('') date = xs.decode('
2001-04-15
', datetime_types=True) self.assertEqual(etree_tostring(xs.encode(date)), '
2001-04-15
') def test_duration_builtin_type(self): xs = self.get_schema('') duration = xs.decode('P5Y3MT60H30.001S', datetime_types=True) self.assertEqual(etree_tostring(xs.encode(duration)), 'P5Y3M2DT12H30.001S') def test_gregorian_year_builtin_type(self): xs = self.get_schema('') gyear = xs.decode('2000', datetime_types=True) self.assertEqual(etree_tostring(xs.encode(gyear)), '2000') def test_gregorian_yearmonth_builtin_type(self): xs = self.get_schema('') gyear_month = xs.decode('2000-12', datetime_types=True) self.assertEqual(etree_tostring(xs.encode(gyear_month)), '2000-12') def test_list_types(self): list_of_strings = self.st_schema.types['list_of_strings'] self.check_encode(list_of_strings, (10, 25, 40), u'', validation='lax') self.check_encode(list_of_strings, (10, 25, 40), u'10 25 40', validation='skip') self.check_encode(list_of_strings, ['a', 'b', 'c'], u'a b c', validation='skip') list_of_integers = self.st_schema.types['list_of_integers'] self.check_encode(list_of_integers, (10, 25, 40), u'10 25 40') self.check_encode(list_of_integers, (10, 25.0, 40), XMLSchemaValidationError) self.check_encode(list_of_integers, (10, 25.0, 40), u'10 25 40', validation='lax') list_of_floats = self.st_schema.types['list_of_floats'] self.check_encode(list_of_floats, [10.1, 25.0, 40.0], u'10.1 25.0 40.0') self.check_encode(list_of_floats, [10.1, 25, 40.0], u'10.1 25.0 40.0', validation='lax') self.check_encode(list_of_floats, [10.1, False, 40.0], u'10.1 0.0 40.0', validation='lax') list_of_booleans = self.st_schema.types['list_of_booleans'] self.check_encode(list_of_booleans, [True, False, True], u'true false true') self.check_encode(list_of_booleans, [10, False, True], XMLSchemaEncodeError) self.check_encode(list_of_booleans, [True, False, 40.0], u'true false', validation='lax') self.check_encode(list_of_booleans, [True, False, 40.0], u'true false 40.0', validation='skip') def test_union_types(self): integer_or_float = self.st_schema.types['integer_or_float'] self.check_encode(integer_or_float, -95, u'-95') self.check_encode(integer_or_float, -95.0, u'-95.0') self.check_encode(integer_or_float, True, XMLSchemaEncodeError) self.check_encode(integer_or_float, True, u'1', validation='lax') integer_or_string = self.st_schema.types['integer_or_string'] self.check_encode(integer_or_string, 89, u'89') self.check_encode(integer_or_string, 89.0, u'89', validation='lax') self.check_encode(integer_or_string, 89.0, XMLSchemaEncodeError) self.check_encode(integer_or_string, False, XMLSchemaEncodeError) self.check_encode(integer_or_string, "Venice ", u'Venice ') boolean_or_integer_or_string = self.st_schema.types['boolean_or_integer_or_string'] self.check_encode(boolean_or_integer_or_string, 89, u'89') self.check_encode(boolean_or_integer_or_string, 89.0, u'89', validation='lax') self.check_encode(boolean_or_integer_or_string, 89.0, XMLSchemaEncodeError) self.check_encode(boolean_or_integer_or_string, False, u'false') self.check_encode(boolean_or_integer_or_string, "Venice ", u'Venice ') def test_simple_elements(self): elem = etree_element('A') elem.text = '89' self.check_encode(self.get_element('A', type='xs:string'), '89', elem) self.check_encode(self.get_element('A', type='xs:integer'), 89, elem) elem.text = '-10.4' self.check_encode(self.get_element('A', type='xs:float'), -10.4, elem) elem.text = 'false' self.check_encode(self.get_element('A', type='xs:boolean'), False, elem) elem.text = 'true' self.check_encode(self.get_element('A', type='xs:boolean'), True, elem) self.check_encode(self.get_element('A', type='xs:short'), 128000, XMLSchemaValidationError) elem.text = '0' self.check_encode(self.get_element('A', type='xs:nonNegativeInteger'), 0, elem) self.check_encode(self.get_element('A', type='xs:nonNegativeInteger'), '0', XMLSchemaValidationError) self.check_encode(self.get_element('A', type='xs:positiveInteger'), 0, XMLSchemaValidationError) elem.text = '-1' self.check_encode(self.get_element('A', type='xs:negativeInteger'), -1, elem) self.check_encode(self.get_element('A', type='xs:nonNegativeInteger'), -1, XMLSchemaValidationError) def test_complex_elements(self): schema = self.get_schema(""" """) self.check_encode( schema.elements['A'], data={'@a1': 10, '@a2': -1, '$': 'simple '}, expected='simple ', ) self.check_encode( schema.elements['A'], {'@a1': 10, '@a2': -1, '$': 'simple '}, ElementTree.fromstring('simple '), ) self.check_encode( schema.elements['A'], {'@a1': 10, '@a2': -1}, ElementTree.fromstring('') ) self.check_encode( schema.elements['A'], {'@a1': 10, '$': 'simple '}, ElementTree.fromstring('simple ') ) self.check_encode(schema.elements['A'], {'@a2': -1, '$': 'simple '}, XMLSchemaValidationError) schema = self.get_schema(""" """) self.check_encode( xsd_component=schema.elements['A'], data=ordered_dict_class([('B1', 'abc'), ('B2', 10), ('B3', False)]), expected=u'\nabc\n10\nfalse\n', indent=0, ) self.check_encode(schema.elements['A'], {'B1': 'abc', 'B2': 10, 'B4': False}, XMLSchemaValidationError) def test_error_message(self): schema = self.schema_class(self.casepath('issues/issue_115/Rotation.xsd')) rotation_data = { "@roll": 0.0, "@pitch": 0.0, "@yaw": -1.0 # <----- invalid value, must be between 0 and 360 } message_lines = [] try: schema.encode(rotation_data) except Exception as err: message_lines = unicode_type(err).split('\n') self.assertTrue(message_lines, msg="Empty error message!") self.assertEqual(message_lines[-4], 'Instance:') if sys.version_info < (3, 8): text = '' else: text = '' self.assertEqual(message_lines[-2].strip(), text) def test_max_occurs_sequence(self): # Issue #119 schema = self.get_schema(""" """) # Check validity self.assertIsNone(schema.validate("1")) self.assertIsNone(schema.validate("12")) with self.assertRaises(XMLSchemaChildrenValidationError): schema.validate("123") self.assertTrue(is_etree_element(schema.to_etree({'A': 1}, path='foo'))) self.assertTrue(is_etree_element(schema.to_etree({'A': [1]}, path='foo'))) self.assertTrue(is_etree_element(schema.to_etree({'A': [1, 2]}, path='foo'))) with self.assertRaises(XMLSchemaChildrenValidationError): schema.to_etree({'A': [1, 2, 3]}, path='foo') schema = self.get_schema(""" """) self.assertTrue(is_etree_element(schema.to_etree({'A': [1, 2]}, path='foo'))) with self.assertRaises(XMLSchemaChildrenValidationError): schema.to_etree({'A': [1, 2, 3]}, path='foo') def test_encode_unordered_content(self): schema = self.get_schema(""" """) self.check_encode( xsd_component=schema.elements['A'], data=ordered_dict_class([('B2', 10), ('B1', 'abc'), ('B3', True)]), expected=XMLSchemaChildrenValidationError ) self.check_encode( xsd_component=schema.elements['A'], data=ordered_dict_class([('B2', 10), ('B1', 'abc'), ('B3', True)]), expected=u'\nabc\n10\ntrue\n', indent=0, cdata_prefix='#', converter=UnorderedConverter ) self.check_encode( xsd_component=schema.elements['A'], data=ordered_dict_class([('B1', 'abc'), ('B2', 10), ('#1', 'hello'), ('B3', True)]), expected='\nhelloabc\n10\ntrue\n', indent=0, cdata_prefix='#', converter=UnorderedConverter ) self.check_encode( xsd_component=schema.elements['A'], data=ordered_dict_class([('B1', 'abc'), ('B2', 10), ('#1', 'hello'), ('B3', True)]), expected=u'\nabc\n10\nhello\ntrue\n', indent=0, cdata_prefix='#' ) self.check_encode( xsd_component=schema.elements['A'], data=ordered_dict_class([('B1', 'abc'), ('B2', 10), ('#1', 'hello')]), expected=XMLSchemaValidationError, indent=0, cdata_prefix='#' ) def test_strict_trailing_content(self): """Too many elements for a group raises an exception.""" schema = self.get_schema(""" """) self.check_encode( schema.elements['foo'], data={"A": [1, 2, 3]}, expected=XMLSchemaChildrenValidationError, ) def test_unordered_converter_repeated_sequence_of_elements(self): schema = self.get_schema(""" """) root = schema.to_etree(ordered_dict_class([('A', [1, 2]), ('B', [3, 4])])) self.assertListEqual([e.text for e in root], ['1', '3', '2', '4']) root = schema.to_etree({"A": [1, 2], "B": [3, 4]}, converter=UnorderedConverter) self.assertListEqual([e.text for e in root], ['1', '3', '2', '4']) root = schema.to_etree({"A": [1, 2], "B": [3, 4]}, unordered=True) self.assertListEqual([e.text for e in root], ['1', '3', '2', '4']) class TestEncoding11(TestEncoding): schema_class = XMLSchema11 if __name__ == '__main__': from xmlschema.tests import print_test_header print_test_header() unittest.main()