#!/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 # """ This module runs tests concerning model groups validation. """ import unittest from xmlschema import XMLSchema10, XMLSchema11 from xmlschema.validators import ModelVisitor from xmlschema.compat import ordered_dict_class from xmlschema.tests import casepath, XsdValidatorTestCase class TestModelValidation(XsdValidatorTestCase): # --- Test helper functions --- def check_advance_true(self, model, expected=None): """ Advances a model with a match condition and checks the expected error list or exception. :param model: an ModelGroupVisitor instance. :param expected: can be an exception class or a list. Leaving `None` means that an empty \ list is expected. """ if isinstance(expected, type) and issubclass(expected, Exception): self.assertRaises(expected, lambda x: list(model.advance(x)), True) else: self.assertEqual([e for e in model.advance(True)], expected or []) def check_advance_false(self, model, expected=None): """ Advances a model with a no-match condition and checks the expected error list or or exception. :param model: an ModelGroupVisitor instance. :param expected: can be an exception class or a list. Leaving `None` means that an empty \ list is expected. """ if isinstance(expected, type) and issubclass(expected, Exception): self.assertRaises(expected, lambda x: list(model.advance(x)), False) else: self.assertEqual([e for e in model.advance(False)], expected or []) def check_advance(self, model, match, expected=None): """ Advances a model with an argument match condition and checks the expected error list. :param model: an ModelGroupVisitor instance. :param match: the matching boolean condition. :param expected: can be an exception class or a list. Leaving `None` means that an empty \ list is expected. """ if isinstance(expected, type) and issubclass(expected, Exception): self.assertRaises(expected, lambda x: list(model.advance(x)), match) else: self.assertEqual([e for e in model.advance(match)], expected or []) def check_stop(self, model, expected=None): """ Stops a model and checks the expected errors list. :param model: an ModelGroupVisitor instance. :param expected: can be an exception class or a list. Leaving `None` means that an empty \ list is expected. """ if isinstance(expected, type) and issubclass(expected, Exception): self.assertRaises(expected, lambda: list(model.stop())) else: self.assertEqual([e for e in model.stop()], expected or []) # --- Vehicles schema --- def test_vehicles_model(self): # Sequence with two not-emptiable single-occurs elements group = self.vh_schema.elements['vehicles'].type.content_type model = ModelVisitor(group) self.check_advance_true(model) # self.check_advance_true(model) # self.assertIsNone(model.element) model = ModelVisitor(group) self.check_advance_true(model) # self.check_advance_true(model) # self.check_advance_true(model, ValueError) # self.assertIsNone(model.element) def test_cars_model(self): # Emptiable 1:1 sequence with one emptiable and unlimited element. group = self.vh_schema.elements['cars'].type.content_type model = ModelVisitor(group) self.check_advance_true(model) # self.check_advance_true(model) # self.check_advance_true(model) # self.check_advance_false(model) # (end) self.assertIsNone(model.element) model = ModelVisitor(group) self.check_advance_false(model) # self.assertIsNone(model.element) # --- Collection schema --- def test_collection_model(self): # Sequence with one not-emptiable and unlimited element. group = self.col_schema.elements['collection'].type.content_type model = ModelVisitor(group) self.check_advance_true(model) # self.check_advance_true(model) # self.check_advance_true(model) # self.check_advance_true(model) # self.check_advance_false(model) # (end) self.assertIsNone(model.element) model = ModelVisitor(group) self.check_advance_false(model, [(group[0], 0, [group[0]])]) # self.assertIsNone(model.element) def test_person_type_model(self): # Sequence with four single elements, last two are also emptiable. group = self.col_schema.types['personType'].content_type model = ModelVisitor(group) self.check_advance_true(model) # self.check_advance_true(model) # self.check_advance_true(model) # self.check_advance_true(model) # self.assertIsNone(model.element) model = ModelVisitor(group) self.check_advance_true(model) # self.check_advance_true(model) # self.check_stop(model) self.assertIsNone(model.element) model = ModelVisitor(group) self.check_advance_true(model) # match self.check_advance_false(model, [(group[1], 0, [group[1]])]) # missing! self.check_advance_true(model) # match self.check_stop(model) # is optional self.assertIsNone(model.element) # --- XSD 1.0/1.1 meta-schema models --- def test_meta_simple_derivation_model(self): """ """ group = XMLSchema10.meta_schema.groups['simpleDerivation'] model = ModelVisitor(group) self.check_advance_true(model) # match self.assertIsNone(model.element) model = ModelVisitor(group) self.check_advance_false(model) # not match with self.check_advance_true(model) # match self.assertIsNone(model.element) model = ModelVisitor(group) self.check_advance_false(model) # not match with self.check_advance_false(model) # not match with self.check_advance_true(model) # match self.assertIsNone(model.element) model = ModelVisitor(group) self.check_advance_false(model) # not match with self.check_advance_false(model) # not match with self.check_advance_false(model, [(group, 0, group[:])]) # not match with self.assertIsNone(model.element) def test_meta_simple_restriction_model(self): """ """ # Sequence with an optional single element and an optional unlimited choice. group = self.schema_class.meta_schema.groups['simpleRestrictionModel'] model = ModelVisitor(group) if self.schema_class.XSD_VERSION == '1.0': self.assertEqual(model.element, group[0]) self.check_advance_true(model) # match self.assertEqual(model.element, group[1][0][0]) self.check_advance_false(model) # do not match self.assertEqual(model.element, group[1][0][1]) self.check_advance_false(model) # do not match self.assertEqual(model.element, group[1][0][2]) self.check_advance_true(model) # match self.assertEqual(model.element, group[1][0][0]) for _ in range(12): self.check_advance_false(model) # no match for all the inner choice group "xs:facets" self.assertIsNone(model.element) def test_meta_schema_top_model(self): """ """ group = self.schema_class.meta_schema.groups['schemaTop'] model = ModelVisitor(group) self.assertEqual(model.element, group[0][0][0]) self.check_advance_false(model) # don't match self.assertEqual(model.element, group[0][0][1]) self.check_advance_true(model) # match self.assertIsNone(model.element) model.restart() self.assertEqual(model.element, group[0][0][0]) self.check_advance_false(model) # don't match self.assertEqual(model.element, group[0][0][1]) self.check_advance_false(model) # don't match self.assertEqual(model.element, group[0][0][2]) self.check_advance_false(model) # don't match self.assertEqual(model.element, group[0][0][3]) self.check_advance_false(model) # don't match self.assertEqual(model.element, group[1]) self.check_advance_false(model) # don't match self.assertEqual(model.element, group[2]) self.check_advance_false(model) # don't match self.assertEqual(model.element, group[3]) self.check_advance_false(model, [(group, 0, group[0][0][:] + group[1:])]) # don't match model.restart() self.assertEqual(model.element, group[0][0][0]) self.check_advance_false(model) # don't match self.assertEqual(model.element, group[0][0][1]) self.check_advance_false(model) # don't match self.assertEqual(model.element, group[0][0][2]) self.check_advance_false(model) # don't match self.assertEqual(model.element, group[0][0][3]) self.check_advance_false(model) # don't match self.assertEqual(model.element, group[1]) self.check_advance_false(model) # don't match self.assertEqual(model.element, group[2]) self.check_advance_true(model) # match self.assertIsNone(model.element) def test_meta_attr_declarations_group(self): """ """ group = self.schema_class.meta_schema.groups['attrDecls'] model = ModelVisitor(group) for match in [False, False, True]: self.check_advance(model, match) self.assertIsNone(model.element) model = ModelVisitor(group) self.check_advance_false(model) self.check_advance_true(model) self.assertEqual(model.element, group[0][0]) model = ModelVisitor(group) for match in [False, True, False, False]: self.check_advance(model, match) self.assertEqual(model.element, group[1]) model = ModelVisitor(group) for match in [False, True, True, False, True, False, False]: self.check_advance(model, match) self.assertEqual(model.element, group[1]) def test_meta_complex_type_model(self): """ """ group = self.schema_class.meta_schema.groups['complexTypeModel'] model = ModelVisitor(group) self.assertEqual(model.element, group[0]) self.check_advance_true(model) # match self.assertIsNone(model.element) model.restart() self.assertEqual(model.element, group[0]) self.check_advance_false(model) self.check_advance_true(model) # match self.assertIsNone(model.element) if self.schema_class.XSD_VERSION == '1.0': model.restart() self.assertEqual(model.element, group[0]) for match in [False, False, False, False, True]: self.check_advance(model, match) # match self.check_stop(model) self.assertIsNone(model.element) model.restart() self.assertEqual(model.element, group[0]) for match in [False, False, False, False, True, False, True, False, False, False]: self.check_advance(model, match) # match, match self.assertIsNone(model.element) def test_meta_schema_document_model(self): group = self.schema_class.meta_schema.elements['schema'].type.content_type # A schema model with a wrong tag model = ModelVisitor(group) if self.schema_class.XSD_VERSION == '1.0': self.assertEqual(model.element, group[0][0]) self.check_advance_false(model) # eg. anyAttribute self.check_stop(model) else: self.assertEqual(model.element, group[0][0][0]) # # Tests on schema test_cases/features/models/models.xsd def test_model_group1(self): group = self.models_schema.groups['group1'] model = ModelVisitor(group) self.assertEqual(model.element, group[0]) self.check_stop(model) model.restart() self.assertEqual(model.element, group[0]) for _ in range(3): self.check_advance_false(model) self.assertIsNone(model.element) model.restart() for match in [False, True, False]: self.check_advance(model, match) self.assertIsNone(model.element) def test_model_group2(self): group = self.models_schema.groups['group2'] model = ModelVisitor(group) self.assertEqual(model.element, group[0]) for _ in range(3): self.check_advance_false(model) # group1 do not match self.assertEqual(model.element, group[1][0][0][2]) # of group1 for _ in range(8): self.check_advance_false(model) self.assertEqual(model.element, group[2]) # self.check_advance_false(model) self.assertEqual(model.element, group[3]) # self.check_advance_false(model) self.assertIsNone(model.element) def test_model_group3(self): group = self.models_schema.groups['group3'] model = ModelVisitor(group) self.assertEqual(model.element, group[0]) for match in [True, False, True]: self.check_advance(model, match) self.check_stop(model) def test_model_group4(self): group = self.models_schema.groups['group4'] model = ModelVisitor(group) self.assertEqual(model.element, group[0]) for match in [True, False, True]: self.check_advance(model, match) self.check_stop(model) def test_model_group5(self): group = self.models_schema.groups['group5'] model = ModelVisitor(group) self.assertEqual(model.element, group[0][0]) for _ in range(5): # match [ .. ] self.check_advance_true(model) self.assertEqual(model.element.name, 'elem6') self.check_advance_true(model) # match choice with self.check_stop(model) def test_model_group6(self): group = self.models_schema.groups['group6'] model = ModelVisitor(group) self.assertEqual(model.element, group[0][0]) self.check_advance_true(model) # match choice with self.check_advance_true(model) # match choice with self.assertIsNone(model.element) def test_model_group7(self): group = self.models_schema.types['complexType7'].content_type model = ModelVisitor(group) self.assertEqual(model.element, group[0][0]) self.check_stop(model, [(group[0][0], 0, [group[0][0]])]) group = self.models_schema.types['complexType7_emptiable'].content_type model = ModelVisitor(group) self.assertEqual(model.element, group[0][0]) self.check_stop(model) def test_model_group8(self): group = self.models_schema.groups['group8'] model = ModelVisitor(group) self.assertEqual(model.element, group[0][0]) self.check_advance_true(model) # match choice with self.check_advance_false(model) self.assertEqual(model.element, group[0][1]) self.check_advance_true(model) # match choice with self.assertEqual(model.element, group[0][2]) self.check_advance_true(model) # match choice with self.assertEqual(model.element, group[0][3]) self.check_advance_true(model) # match choice with self.assertIsNone(model.element) # # Tests on issues def test_issue_086(self): issue_086_xsd = casepath('issues/issue_086/issue_086.xsd') schema = self.schema_class(issue_086_xsd) group = schema.types['Foo'].content_type # issue_086-1.xml sequence simulation model = ModelVisitor(group) self.assertEqual(model.element, group[0]) self.check_advance_true(model) #
matching self.assertEqual(model.element, group[1][0][0]) # 'a' element self.check_advance_true(model) # matching self.assertEqual(model.element, group[1][0][0]) # 'a' element self.check_advance_true(model) # matching self.assertEqual(model.element, group[1][0][0]) # 'a' element self.check_advance_false(model) self.assertEqual(model.element, group[1][0][0]) # 'a' element self.check_advance_false(model) self.assertEqual(model.element, group[1][1][0]) # 'b' element self.check_advance_true(model) # matching self.assertEqual(model.element, group[1][1][0]) # 'b' element self.check_advance_true(model) # matching self.check_advance_false(model) self.assertEqual(model.element, group[1][0][0]) # 'a' element (choice group restarted) self.check_advance_false(model) self.check_advance_false(model) self.assertEqual(model.element, group[1][2][0]) # 'c' element self.check_advance_true(model) # matching self.assertEqual(model.element, group[1][2][0]) # 'c' element self.check_advance_true(model) # matching self.check_stop(model) # issue_086-2.xml sequence simulation model = ModelVisitor(group) self.check_advance_true(model) #
matching self.assertEqual(model.element, group[1][0][0]) # 'a' element self.check_advance_false(model) self.assertEqual(model.element, group[1][1][0]) # 'b' element self.check_advance_true(model) # matching self.assertEqual(model.element, group[1][1][0]) # 'b' element self.check_advance_true(model) # matching self.check_advance_false(model) self.assertEqual(model.element, group[1][0][0]) # 'a' element (choice group restarted) self.check_advance_false(model) self.check_advance_false(model) self.assertEqual(model.element, group[1][2][0]) # 'c' element self.check_advance_true(model) # matching self.assertEqual(model.element, group[1][2][0]) # 'c' element self.check_advance_true(model) # matching self.check_advance_false(model) self.assertEqual(model.element, group[1][0][0]) # 'a' element self.check_advance_true(model) # matching self.assertEqual(model.element, group[1][0][0]) # 'a' element self.check_advance_true(model) # matching self.assertEqual(model.element, group[1][0][0]) # 'a' element self.check_stop(model) class TestModelValidation11(TestModelValidation): schema_class = XMLSchema11 class TestModelBasedSorting(XsdValidatorTestCase): def test_sort_content(self): # test of ModelVisitor's sort_content/iter_unordered_content schema = self.get_schema(""" """) model = ModelVisitor(schema.types['A_type'].content_type) self.assertListEqual( model.sort_content([('B2', 10), ('B1', 'abc'), ('B3', True)]), [('B1', 'abc'), ('B2', 10), ('B3', True)] ) self.assertListEqual( model.sort_content([('B3', True), ('B2', 10), ('B1', 'abc')]), [('B1', 'abc'), ('B2', 10), ('B3', True)] ) self.assertListEqual( model.sort_content([('B2', 10), ('B4', None), ('B1', 'abc'), ('B3', True)]), [('B1', 'abc'), ('B2', 10), ('B3', True), ('B4', None)] ) content = [('B2', 10), ('B4', None), ('B1', 'abc'), (1, 'hello'), ('B3', True)] self.assertListEqual( model.sort_content(content), [(1, 'hello'), ('B1', 'abc'), ('B2', 10), ('B3', True), ('B4', None)] ) content = [(2, 'world!'), ('B2', 10), ('B4', None), ('B1', 'abc'), (1, 'hello'), ('B3', True)] self.assertListEqual( model.sort_content(content), [(1, 'hello'), ('B1', 'abc'), (2, 'world!'), ('B2', 10), ('B3', True), ('B4', None)] ) # With a dict-type argument content = ordered_dict_class([('B2', [10]), ('B1', ['abc']), ('B3', [True])]) self.assertListEqual( model.sort_content(content), [('B1', 'abc'), ('B2', 10), ('B3', True)] ) content = ordered_dict_class([('B2', [10]), ('B1', ['abc']), ('B3', [True]), (1, 'hello')]) self.assertListEqual( model.sort_content(content), [(1, 'hello'), ('B1', 'abc'), ('B2', 10), ('B3', True)] ) # With partial content self.assertListEqual(model.sort_content([]), []) self.assertListEqual(model.sort_content([('B1', 'abc')]), [('B1', 'abc')]) self.assertListEqual(model.sort_content([('B2', 10)]), [('B2', 10)]) self.assertListEqual(model.sort_content([('B3', True)]), [('B3', True)]) self.assertListEqual( model.sort_content([('B3', True), ('B1', 'abc')]), [('B1', 'abc'), ('B3', True)] ) self.assertListEqual( model.sort_content([('B2', 10), ('B1', 'abc')]), [('B1', 'abc'), ('B2', 10)] ) self.assertListEqual( model.sort_content([('B3', True), ('B2', 10)]), [('B2', 10), ('B3', True)] ) def test_iter_collapsed_content_with_optional_elements(self): schema = self.get_schema(""" """) model = ModelVisitor(schema.types['A_type'].content_type) content = [('B3', 10), ('B4', None), ('B5', True), ('B6', 'alpha'), ('B7', 20)] model.restart() self.assertListEqual( list(model.iter_collapsed_content(content)), content ) content = [('B3', 10), ('B5', True), ('B6', 'alpha'), ('B7', 20)] # Missing B4 model.restart() self.assertListEqual( list(model.iter_collapsed_content(content)), content ) def test_iter_collapsed_content_with_repeated_elements(self): schema = self.get_schema(""" """) model = ModelVisitor(schema.types['A_type'].content_type) content = [ ('B3', 10), ('B4', None), ('B5', True), ('B5', False), ('B6', 'alpha'), ('B7', 20) ] self.assertListEqual( list(model.iter_collapsed_content(content)), content ) content = [('B3', 10), ('B3', 11), ('B3', 12), ('B4', None), ('B5', True), ('B5', False), ('B6', 'alpha'), ('B7', 20), ('B7', 30)] model.restart() self.assertListEqual( list(model.iter_collapsed_content(content)), content ) content = [('B3', 10), ('B3', 11), ('B3', 12), ('B4', None), ('B5', True), ('B5', False)] model.restart() self.assertListEqual( list(model.iter_collapsed_content(content)), content ) def test_iter_collapsed_content_with_repeated_groups(self): schema = self.get_schema(""" """) model = ModelVisitor(schema.types['A_type'].content_type) content = [('B1', 1), ('B1', 2), ('B2', 3), ('B2', 4)] self.assertListEqual( list(model.iter_collapsed_content(content)), [('B1', 1), ('B2', 3), ('B1', 2), ('B2', 4)] ) # Model broken by unknown element at start content = [('X', None), ('B1', 1), ('B1', 2), ('B2', 3), ('B2', 4)] model.restart() self.assertListEqual(list(model.iter_collapsed_content(content)), content) content = [('B1', 1), ('X', None), ('B1', 2), ('B2', 3), ('B2', 4)] model.restart() self.assertListEqual(list(model.iter_collapsed_content(content)), content) content = [('B1', 1), ('B1', 2), ('X', None), ('B2', 3), ('B2', 4)] model.restart() self.assertListEqual(list(model.iter_collapsed_content(content)), content) content = [('B1', 1), ('B1', 2), ('B2', 3), ('X', None), ('B2', 4)] model.restart() self.assertListEqual( list(model.iter_collapsed_content(content)), [('B1', 1), ('B2', 3), ('B1', 2), ('X', None), ('B2', 4)] ) content = [('B1', 1), ('B1', 2), ('B2', 3), ('B2', 4), ('X', None)] model.restart() self.assertListEqual( list(model.iter_collapsed_content(content)), [('B1', 1), ('B2', 3), ('B1', 2), ('B2', 4), ('X', None)] ) def test_iter_collapsed_content_with_single_elements(self): schema = self.get_schema(""" """) model = ModelVisitor(schema.types['A_type'].content_type) content = [('B1', 'abc'), ('B2', 10), ('B3', False)] model.restart() self.assertListEqual(list(model.iter_collapsed_content(content)), content) content = [('B3', False), ('B1', 'abc'), ('B2', 10)] model.restart() self.assertListEqual(list(model.iter_collapsed_content(content)), content) content = [('B1', 'abc'), ('B3', False), ('B2', 10)] model.restart() self.assertListEqual(list(model.iter_collapsed_content(content)), content) content = [('B1', 'abc'), ('B1', 'def'), ('B2', 10), ('B3', False)] model.restart() self.assertListEqual( list(model.iter_collapsed_content(content)), [('B1', 'abc'), ('B2', 10), ('B3', False), ('B1', 'def')] ) content = [('B1', 'abc'), ('B2', 10), ('X', None)] model.restart() self.assertListEqual(list(model.iter_collapsed_content(content)), content) content = [('X', None), ('B1', 'abc'), ('B2', 10), ('B3', False)] model.restart() self.assertListEqual(list(model.iter_collapsed_content(content)), content) if __name__ == '__main__': from xmlschema.tests import print_test_header print_test_header() unittest.main()