806 lines
33 KiB
Python
806 lines
33 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>
|
|
#
|
|
"""
|
|
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) # <cars>
|
|
self.check_advance_true(model) # <bikes>
|
|
self.assertIsNone(model.element)
|
|
|
|
model = ModelVisitor(group)
|
|
self.check_advance_true(model) # <cars>
|
|
self.check_advance_true(model) # <bikes>
|
|
self.check_advance_true(model, ValueError) # <bikes>
|
|
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) # <car>
|
|
self.check_advance_true(model) # <car>
|
|
self.check_advance_true(model) # <car>
|
|
self.check_advance_false(model) # (end)
|
|
self.assertIsNone(model.element)
|
|
|
|
model = ModelVisitor(group)
|
|
self.check_advance_false(model) # <not-a-car>
|
|
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) # <car>
|
|
self.check_advance_true(model) # <car>
|
|
self.check_advance_true(model) # <car>
|
|
self.check_advance_true(model) # <car>
|
|
self.check_advance_false(model) # (end)
|
|
self.assertIsNone(model.element)
|
|
|
|
model = ModelVisitor(group)
|
|
self.check_advance_false(model, [(group[0], 0, [group[0]])]) # <not-a-car>
|
|
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) # <name>
|
|
self.check_advance_true(model) # <born>
|
|
self.check_advance_true(model) # <dead>
|
|
self.check_advance_true(model) # <qualification>
|
|
self.assertIsNone(model.element)
|
|
|
|
model = ModelVisitor(group)
|
|
self.check_advance_true(model) # <name>
|
|
self.check_advance_true(model) # <born>
|
|
self.check_stop(model)
|
|
self.assertIsNone(model.element)
|
|
|
|
model = ModelVisitor(group)
|
|
self.check_advance_true(model) # <name> match
|
|
self.check_advance_false(model, [(group[1], 0, [group[1]])]) # <born> missing!
|
|
self.check_advance_true(model) # <dead> match
|
|
self.check_stop(model) # <qualification> is optional
|
|
self.assertIsNone(model.element)
|
|
|
|
# --- XSD 1.0/1.1 meta-schema models ---
|
|
|
|
def test_meta_simple_derivation_model(self):
|
|
"""
|
|
<xs:group name="simpleDerivation">
|
|
<xs:choice>
|
|
<xs:element ref="xs:restriction"/>
|
|
<xs:element ref="xs:list"/>
|
|
<xs:element ref="xs:union"/>
|
|
</xs:choice>
|
|
</xs:group>
|
|
"""
|
|
group = XMLSchema10.meta_schema.groups['simpleDerivation']
|
|
|
|
model = ModelVisitor(group)
|
|
self.check_advance_true(model) # <restriction> match
|
|
self.assertIsNone(model.element)
|
|
|
|
model = ModelVisitor(group)
|
|
self.check_advance_false(model) # <list> not match with <restriction>
|
|
self.check_advance_true(model) # <list> match
|
|
self.assertIsNone(model.element)
|
|
|
|
model = ModelVisitor(group)
|
|
self.check_advance_false(model) # <union> not match with <restriction>
|
|
self.check_advance_false(model) # <union> not match with <list>
|
|
self.check_advance_true(model) # <union> match
|
|
self.assertIsNone(model.element)
|
|
|
|
model = ModelVisitor(group)
|
|
self.check_advance_false(model) # <other> not match with <restriction>
|
|
self.check_advance_false(model) # <other> not match with <list>
|
|
self.check_advance_false(model, [(group, 0, group[:])]) # <other> not match with <union>
|
|
self.assertIsNone(model.element)
|
|
|
|
def test_meta_simple_restriction_model(self):
|
|
"""
|
|
<!-- XSD 1.0 -->
|
|
<xs:group name="facets">
|
|
<xs:choice>
|
|
<xs:element ref="xs:minExclusive"/>
|
|
<xs:element ref="xs:minInclusive"/>
|
|
<xs:element ref="xs:maxExclusive"/>
|
|
<xs:element ref="xs:maxInclusive"/>
|
|
<xs:element ref="xs:totalDigits"/>
|
|
<xs:element ref="xs:fractionDigits"/>
|
|
<xs:element ref="xs:length"/>
|
|
<xs:element ref="xs:minLength"/>
|
|
<xs:element ref="xs:maxLength"/>
|
|
<xs:element ref="xs:enumeration"/>
|
|
<xs:element ref="xs:whiteSpace"/>
|
|
<xs:element ref="xs:pattern"/>
|
|
</xs:choice>
|
|
</xs:group>
|
|
|
|
<xs:group name="simpleRestrictionModel">
|
|
<xs:sequence>
|
|
<xs:element name="simpleType" type="xs:localSimpleType" minOccurs="0"/>
|
|
<xs:group ref="xs:facets" minOccurs="0" maxOccurs="unbounded"/>
|
|
</xs:sequence>
|
|
</xs:group>
|
|
|
|
<!-- XSD 1.1 -->
|
|
<xs:group name="simpleRestrictionModel">
|
|
<xs:sequence>
|
|
<xs:element name="simpleType" type="xs:localSimpleType" minOccurs="0"/>
|
|
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
|
<xs:element ref="xs:facet"/> <!-- Use a substitution group -->
|
|
<xs:any processContents="lax" namespace="##other"/>
|
|
</xs:choice>
|
|
</xs:sequence>
|
|
</xs:group>
|
|
"""
|
|
# 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) # <simpleType> match
|
|
self.assertEqual(model.element, group[1][0][0])
|
|
self.check_advance_false(model) # <maxExclusive> do not match
|
|
self.assertEqual(model.element, group[1][0][1])
|
|
self.check_advance_false(model) # <maxExclusive> do not match
|
|
self.assertEqual(model.element, group[1][0][2])
|
|
self.check_advance_true(model) # <maxExclusive> 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):
|
|
"""
|
|
<xs:group name="schemaTop">
|
|
<xs:choice>
|
|
<xs:group ref="xs:redefinable"/>
|
|
<xs:element ref="xs:element"/>
|
|
<xs:element ref="xs:attribute"/>
|
|
<xs:element ref="xs:notation"/>
|
|
</xs:choice>
|
|
</xs:group>
|
|
|
|
<xs:group name="redefinable">
|
|
<xs:choice>
|
|
<xs:element ref="xs:simpleType"/>
|
|
<xs:element ref="xs:complexType"/>
|
|
<xs:element ref="xs:group"/>
|
|
<xs:element ref="xs:attributeGroup"/>
|
|
</xs:choice>
|
|
</xs:group>
|
|
"""
|
|
group = self.schema_class.meta_schema.groups['schemaTop']
|
|
|
|
model = ModelVisitor(group)
|
|
self.assertEqual(model.element, group[0][0][0])
|
|
self.check_advance_false(model) # <simpleType> don't match
|
|
self.assertEqual(model.element, group[0][0][1])
|
|
self.check_advance_true(model) # <complexType> match
|
|
self.assertIsNone(model.element)
|
|
|
|
model.restart()
|
|
self.assertEqual(model.element, group[0][0][0])
|
|
self.check_advance_false(model) # <simpleType> don't match
|
|
self.assertEqual(model.element, group[0][0][1])
|
|
self.check_advance_false(model) # <complexType> don't match
|
|
self.assertEqual(model.element, group[0][0][2])
|
|
self.check_advance_false(model) # <group> don't match
|
|
self.assertEqual(model.element, group[0][0][3])
|
|
self.check_advance_false(model) # <attributeGroup> don't match
|
|
self.assertEqual(model.element, group[1])
|
|
self.check_advance_false(model) # <element> don't match
|
|
self.assertEqual(model.element, group[2])
|
|
self.check_advance_false(model) # <attribute> don't match
|
|
self.assertEqual(model.element, group[3])
|
|
self.check_advance_false(model, [(group, 0, group[0][0][:] + group[1:])]) # <notation> don't match
|
|
|
|
model.restart()
|
|
self.assertEqual(model.element, group[0][0][0])
|
|
self.check_advance_false(model) # <simpleType> don't match
|
|
self.assertEqual(model.element, group[0][0][1])
|
|
self.check_advance_false(model) # <complexType> don't match
|
|
self.assertEqual(model.element, group[0][0][2])
|
|
self.check_advance_false(model) # <group> don't match
|
|
self.assertEqual(model.element, group[0][0][3])
|
|
self.check_advance_false(model) # <attributeGroup> don't match
|
|
self.assertEqual(model.element, group[1])
|
|
self.check_advance_false(model) # <element> don't match
|
|
self.assertEqual(model.element, group[2])
|
|
self.check_advance_true(model) # <attribute> match
|
|
self.assertIsNone(model.element)
|
|
|
|
def test_meta_attr_declarations_group(self):
|
|
"""
|
|
<xs:group name="attrDecls">
|
|
<xs:sequence>
|
|
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
|
<xs:element name="attribute" type="xs:attribute"/>
|
|
<xs:element name="attributeGroup" type="xs:attributeGroupRef"/>
|
|
</xs:choice>
|
|
<xs:element ref="xs:anyAttribute" minOccurs="0"/>
|
|
</xs:sequence>
|
|
</xs:group>
|
|
"""
|
|
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):
|
|
"""
|
|
<xs:group name="complexTypeModel">
|
|
<xs:choice>
|
|
<xs:element ref="xs:simpleContent"/>
|
|
<xs:element ref="xs:complexContent"/>
|
|
<xs:sequence>
|
|
<xs:group ref="xs:typeDefParticle" minOccurs="0"/>
|
|
<xs:group ref="xs:attrDecls"/>
|
|
</xs:sequence>
|
|
</xs:choice>
|
|
</xs:group>
|
|
|
|
<xs:group name="typeDefParticle">
|
|
<xs:choice>
|
|
<xs:element name="group" type="xs:groupRef"/>
|
|
<xs:element ref="xs:all"/>
|
|
<xs:element ref="xs:choice"/>
|
|
<xs:element ref="xs:sequence"/>
|
|
</xs:choice>
|
|
</xs:group>
|
|
|
|
<xs:group name="complexTypeModel">
|
|
<xs:choice>
|
|
<xs:element ref="xs:simpleContent"/>
|
|
<xs:element ref="xs:complexContent"/>
|
|
<xs:sequence>
|
|
<xs:element ref="xs:openContent" minOccurs="0"/>
|
|
<xs:group ref="xs:typeDefParticle" minOccurs="0"/>
|
|
<xs:group ref="xs:attrDecls"/>
|
|
<xs:group ref="xs:assertions"/>
|
|
</xs:sequence>
|
|
</xs:choice>
|
|
</xs:group>
|
|
|
|
"""
|
|
group = self.schema_class.meta_schema.groups['complexTypeModel']
|
|
|
|
model = ModelVisitor(group)
|
|
self.assertEqual(model.element, group[0])
|
|
self.check_advance_true(model) # <simpleContent> match
|
|
self.assertIsNone(model.element)
|
|
|
|
model.restart()
|
|
self.assertEqual(model.element, group[0])
|
|
self.check_advance_false(model)
|
|
self.check_advance_true(model) # <complexContent> 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) # <all> 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) # <all> match, <attributeGroup> 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]) # <elem3> of group1
|
|
for _ in range(8):
|
|
self.check_advance_false(model)
|
|
self.assertEqual(model.element, group[2]) # <elem12>
|
|
self.check_advance_false(model)
|
|
self.assertEqual(model.element, group[3]) # <elem13>
|
|
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 [<elem1> .. <elem5>]
|
|
self.check_advance_true(model)
|
|
self.assertEqual(model.element.name, 'elem6')
|
|
self.check_advance_true(model) # match choice with <elem6>
|
|
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 <elem1>
|
|
self.check_advance_true(model) # match choice with <elem2>
|
|
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 <elem1>
|
|
self.check_advance_false(model)
|
|
self.assertEqual(model.element, group[0][1])
|
|
self.check_advance_true(model) # match choice with <elem2>
|
|
self.assertEqual(model.element, group[0][2])
|
|
self.check_advance_true(model) # match choice with <elem3>
|
|
self.assertEqual(model.element, group[0][3])
|
|
self.check_advance_true(model) # match choice with <elem4>
|
|
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) # <header> matching
|
|
self.assertEqual(model.element, group[1][0][0]) # 'a' element
|
|
self.check_advance_true(model) # <a> matching
|
|
self.assertEqual(model.element, group[1][0][0]) # 'a' element
|
|
self.check_advance_true(model) # <a> 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) # <b> matching
|
|
self.assertEqual(model.element, group[1][1][0]) # 'b' element
|
|
self.check_advance_true(model) # <b> 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) # <c> matching
|
|
self.assertEqual(model.element, group[1][2][0]) # 'c' element
|
|
self.check_advance_true(model) # <c> matching
|
|
self.check_stop(model)
|
|
|
|
# issue_086-2.xml sequence simulation
|
|
model = ModelVisitor(group)
|
|
self.check_advance_true(model) # <header> 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) # <b> matching
|
|
self.assertEqual(model.element, group[1][1][0]) # 'b' element
|
|
self.check_advance_true(model) # <b> 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) # <c> matching
|
|
self.assertEqual(model.element, group[1][2][0]) # 'c' element
|
|
self.check_advance_true(model) # <c> matching
|
|
self.check_advance_false(model)
|
|
self.assertEqual(model.element, group[1][0][0]) # 'a' element
|
|
self.check_advance_true(model) # <a> matching
|
|
self.assertEqual(model.element, group[1][0][0]) # 'a' element
|
|
self.check_advance_true(model) # <a> 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("""
|
|
<xs:element name="A" type="A_type" />
|
|
<xs:complexType name="A_type">
|
|
<xs:sequence>
|
|
<xs:element name="B1" type="xs:string"/>
|
|
<xs:element name="B2" type="xs:integer"/>
|
|
<xs:element name="B3" type="xs:boolean"/>
|
|
</xs:sequence>
|
|
</xs:complexType>
|
|
""")
|
|
|
|
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("""
|
|
<xs:element name="A" type="A_type" />
|
|
<xs:complexType name="A_type">
|
|
<xs:sequence>
|
|
<xs:element name="B1" minOccurs="0" />
|
|
<xs:element name="B2" minOccurs="0" />
|
|
<xs:element name="B3" />
|
|
<xs:element name="B4" />
|
|
<xs:element name="B5" />
|
|
<xs:element name="B6" minOccurs="0" />
|
|
<xs:element name="B7" />
|
|
</xs:sequence>
|
|
</xs:complexType>
|
|
""")
|
|
|
|
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("""
|
|
<xs:element name="A" type="A_type" />
|
|
<xs:complexType name="A_type">
|
|
<xs:sequence>
|
|
<xs:element name="B1" minOccurs="0" />
|
|
<xs:element name="B2" minOccurs="0" maxOccurs="unbounded" />
|
|
<xs:element name="B3" maxOccurs="unbounded" />
|
|
<xs:element name="B4" />
|
|
<xs:element name="B5" maxOccurs="unbounded" />
|
|
<xs:element name="B6" minOccurs="0" />
|
|
<xs:element name="B7" maxOccurs="unbounded" />
|
|
</xs:sequence>
|
|
</xs:complexType>
|
|
""")
|
|
|
|
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("""
|
|
<xs:element name="A" type="A_type" />
|
|
<xs:complexType name="A_type">
|
|
<xs:sequence minOccurs="1" maxOccurs="2">
|
|
<xs:element name="B1" minOccurs="0" />
|
|
<xs:element name="B2" minOccurs="0" />
|
|
</xs:sequence>
|
|
</xs:complexType>
|
|
""")
|
|
|
|
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("""
|
|
<xs:element name="A" type="A_type" />
|
|
<xs:complexType name="A_type">
|
|
<xs:sequence>
|
|
<xs:element name="B1" />
|
|
<xs:element name="B2" />
|
|
<xs:element name="B3" />
|
|
</xs:sequence>
|
|
</xs:complexType>
|
|
""")
|
|
|
|
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()
|