def test_initialize(self): classes = [ ClassFactory.create(qname="{xsdata}foo", tag=Tag.ELEMENT), ClassFactory.create(qname="{xsdata}foo", tag=Tag.COMPLEX_TYPE), ClassFactory.create(qname="{xsdata}foobar", tag=Tag.COMPLEX_TYPE), ] container = ClassContainer() container.extend(classes) expected = { "{xsdata}foo": classes[:2], "{xsdata}foobar": classes[2:], } self.assertEqual(2, len(container.data)) self.assertEqual(expected, container.data) self.assertEqual( [ "AttributeGroupHandler", "ClassExtensionHandler", "ClassEnumerationHandler", "AttributeSubstitutionHandler", "AttributeTypeHandler", "AttributeMergeHandler", "AttributeMixedContentHandler", "AttributeSanitizerHandler", ], [x.__class__.__name__ for x in container.processors], )
def analyze_classes(self, classes: List[Class]) -> List[Class]: """Analyzer the given class list and simplify attributes and extensions.""" container = ClassContainer(config=self.config) container.extend(classes) return ClassAnalyzer.process(container)
class ClassContainerTests(FactoryTestCase): def setUp(self): super().setUp() self.container = ClassContainer() def test_from_list(self): classes = [ ClassFactory.create(qname="foo", type=Element), ClassFactory.create(qname="foo", type=ComplexType), ClassFactory.create(qname="foobar", type=ComplexType), ] container = ClassContainer.from_list(classes) expected = { "{xsdata}foo": classes[:2], "{xsdata}foobar": classes[2:], } self.assertEqual(2, len(container)) self.assertEqual(expected, container) @mock.patch.object(ClassContainer, "process_class") def test_find(self, mock_process_class): class_a = ClassFactory.create(qname="a") class_b = ClassFactory.create(qname="b", status=Status.PROCESSED) class_c = ClassFactory.enumeration(2, qname="b", status=Status.PROCESSING) self.container.extend([class_a, class_b, class_c]) self.assertIsNone(self.container.find(QName("nope"))) self.assertEqual(class_a, self.container.find(class_a.qname)) self.assertEqual(class_b, self.container.find(class_b.qname)) self.assertEqual( class_c, self.container.find(class_b.qname, lambda x: x.is_enumeration), ) mock_process_class.assert_called_once_with(class_a) @mock.patch.object(ClassContainer, "process_class") def test_find_repeat_on_condition_and_not_processed( self, mock_process_class): first = ClassFactory.elements(2, qname="a") second = ClassFactory.elements(2, qname="a") self.container.extend([first, second]) def process_class(x: Class): x.status = Status.PROCESSED if x is first: first.attrs.clear() mock_process_class.side_effect = process_class self.assertEqual( second, self.container.find(first.qname, lambda x: len(x.attrs) == 2), )
def test_filter_classes_with_only_simple_types(self, mock_class_should_generate): mock_class_should_generate.return_value = False classes = [ClassFactory.enumeration(2), ClassFactory.create(type=SimpleType)] container = ClassContainer.from_list(classes) container.filter_classes() self.assertEqual(classes, container.class_list)
def test_from_list(self): classes = [ ClassFactory.create(qname="{xsdata}foo", type=Element), ClassFactory.create(qname="{xsdata}foo", type=ComplexType), ClassFactory.create(qname="{xsdata}foobar", type=ComplexType), ] container = ClassContainer.from_list(classes) expected = { "{xsdata}foo": classes[:2], "{xsdata}foobar": classes[2:], } self.assertEqual(2, len(container)) self.assertEqual(expected, container) self.assertEqual( [ "AttributeGroupHandler", "ClassExtensionHandler", "AttributeEnumUnionHandler", "AttributeSubstitutionHandler", "AttributeTypeHandler", "AttributeMergeHandler", "AttributeMixedContentHandler", "AttributeMismatchHandler", ], [x.__class__.__name__ for x in container.processors], )
def process(cls, container: ClassContainer, config: GeneratorConfig): """Iterate through all classes and run the sanitizer procedure.""" sanitizer = cls(container, config) collections.apply(container.iterate(), sanitizer.process_class) sanitizer.resolve_conflicts()
def test_select_classes_when_no_complex_class_available(self): classes = [ ClassFactory.enumeration(2), ClassFactory.create(type=SimpleType) ] container = ClassContainer.from_list(classes) self.assertEqual(classes, ClassAnalyzer.select_classes(container))
def process(cls, container: ClassContainer) -> List[Class]: """Run all the processes.""" # Run validation checks for duplicate, invalid and redefined types. ClassValidator(container).process() # Run analyzer handlers container.process() # Filter classes that should be generated. container.filter_classes() # Sanitize class attributes after merging and flattening types and extensions. ClassSanitizer(container).process() classes = container.class_list cls.validate_references(classes) return classes
def test_filter_classes(self, mock_class_should_generate): mock_class_should_generate.side_effect = [True, False, False, True, False] classes = ClassFactory.list(5) container = ClassContainer.from_list(classes) expected = [ classes[0], classes[3], ] container.filter_classes() self.assertEqual(expected, container.class_list)
def test_process( self, mock_validator_process, mock_container_process, mock_container_filter_classes, mock_sanitizer_process, mock_validate_references, ): config = GeneratorConfig() classes = ClassFactory.list(2) container = ClassContainer(config=config) container.extend(classes) result = ClassAnalyzer.process(container) self.assertEqual(classes, result) mock_validator_process.assert_called_once_with() mock_container_process.assert_called_once_with() mock_container_filter_classes.assert_called_once_with() mock_sanitizer_process.assert_called_once_with() mock_validate_references.assert_called_once_with(classes)
def setUp(self): super().setUp() self.root_enum = ClassFactory.enumeration(2) self.inner_enum = ClassFactory.enumeration(2) self.target = ClassFactory.create(attrs=[ AttrFactory.create( name="value", tag=Tag.UNION, types=[ AttrTypeFactory.create(qname=self.root_enum.qname), AttrTypeFactory.create(qname=self.inner_enum.qname, forward=True), ], ), ]) self.target.inner.append(self.inner_enum) self.container = ClassContainer() self.container.add(self.target) self.container.add(self.root_enum) self.processor = ClassEnumerationHandler(container=self.container)
def test_from_list(self): classes = [ ClassFactory.create(qname="foo", type=Element), ClassFactory.create(qname="foo", type=ComplexType), ClassFactory.create(qname="foobar", type=ComplexType), ] container = ClassContainer.from_list(classes) expected = { "{xsdata}foo": classes[:2], "{xsdata}foobar": classes[2:], } self.assertEqual(2, len(container)) self.assertEqual(expected, container)
def process(cls, classes: List[Class]) -> List[Class]: """Run all the processes.""" # Wrap classes with container for easy access. container = ClassContainer.from_list(classes) # Run validation checks for duplicate, invalid and redefined types. ClassValidator.process(container) # Run analyzer handlers container.process() # Sanitize class attributes after merging and flattening types and extensions. ClassSanitizer.process(container) # Select final list of classes to be generated. return cls.select_classes(container)
def test_select_classes(self, mock_validate_references): classes = [ ClassFactory.create(strict_type=True, type=ComplexType), ClassFactory.create(type=Element), ClassFactory.create(type=ComplexType), ClassFactory.create(type=SimpleType), ClassFactory.enumeration(2), ] container = ClassContainer.from_list(classes) expected = [ classes[1], classes[2], classes[4], ] self.assertEqual(expected, ClassAnalyzer.select_classes(container)) mock_validate_references.assert_called_once_with(expected)
def setUp(self): super().setUp() self.root_enum = ClassFactory.enumeration(2) self.inner_enum = ClassFactory.enumeration(2) self.target = ClassFactory.create(attrs=[ AttrFactory.create( name="value", tag=Tag.UNION, types=[ AttrTypeFactory.create(name=self.root_enum.name), AttrTypeFactory.create(name=self.inner_enum.name, forward=True), ], ), ], ) self.target.inner.append(self.inner_enum) container = ClassContainer.from_list([self.target, self.root_enum]) self.processor = AttributeEnumUnionHandler(container=container)
def select_classes(cls, container: ClassContainer) -> List[Class]: """ Return the qualified classes for code generation. Return all if no classes are derived from xs:element or xs:complexType. """ classes = list(container.iterate()) if any(item.is_complex for item in classes): classes = list( filter( lambda x: x.is_enumeration or (x.is_complex and not x.strict_type), classes, )) cls.validate_references(classes) return classes
def test_process( self, mock_validator_process, mock_container_process, mock_sanitizer_process, mock_select_classes, ): original_classes = ClassFactory.list(2) container = ClassContainer.from_list(original_classes) result_classes = ClassFactory.list(1) mock_select_classes.return_value = result_classes result = ClassAnalyzer.process(original_classes) self.assertEqual(result_classes, result) mock_validator_process.assert_called_once_with(container) mock_container_process.assert_called_once_with() mock_sanitizer_process.assert_called_once_with(container) mock_select_classes.assert_called_once_with(container)
def process(cls, classes: List[Class], config: GeneratorConfig) -> List[Class]: """Run all the processes.""" # Wrap classes with container for easy access. container = ClassContainer.from_list(classes) # Run validation checks for duplicate, invalid and redefined types. ClassValidator.process(container) # Run analyzer handlers container.process() # Filter classes that should be generated. container.filter_classes() # Sanitize class attributes after merging and flattening types and extensions. ClassSanitizer.process(container, config) classes = container.class_list cls.validate_references(classes) return classes
def setUp(self): super().setUp() container = ClassContainer() self.processor = AttributeTypeHandler(container=container)
class ClassValidatorTests(FactoryTestCase): def setUp(self): super().setUp() self.container = ClassContainer() self.validator = ClassValidator(container=self.container) @mock.patch.object(ClassValidator, "mark_strict_types") @mock.patch.object(ClassValidator, "handle_duplicate_types") @mock.patch.object(ClassValidator, "remove_invalid_classes") def test_process( self, mock_remove_invalid_classes, mock_handle_duplicate_types, mock_mark_strict_types, ): first = ClassFactory.create() second = first.clone() third = ClassFactory.create() self.container.extend([first, second, third]) ClassValidator.process(self.container) mock_remove_invalid_classes.assert_called_once_with([first, second]) mock_handle_duplicate_types.assert_called_once_with([first, second]) mock_mark_strict_types.assert_called_once_with([first, second]) def test_remove_invalid_classes(self): first = ClassFactory.create( extensions=[ ExtensionFactory.create(type=AttrTypeFactory.xs_bool()), ExtensionFactory.create(type=AttrTypeFactory.create(qname="foo")), ] ) second = ClassFactory.create( extensions=[ExtensionFactory.create(type=AttrTypeFactory.xs_bool()),] ) third = ClassFactory.create() self.validator.container.extend([first, second, third]) classes = [first, second, third] self.validator.remove_invalid_classes(classes) self.assertEqual([second, third], classes) @mock.patch.object(ClassValidator, "select_winner") def test_handle_duplicate_types(self, mock_select_winner): one = ClassFactory.create() two = one.clone() three = one.clone() four = ClassFactory.create() mock_select_winner.return_value = 0 classes = [one, two, three, four] self.validator.handle_duplicate_types(classes) self.assertEqual([one, four], classes) mock_select_winner.assert_called_once_with([one, two, three]) @mock.patch.object(ClassValidator, "merge_redefined_type") @mock.patch.object(ClassValidator, "select_winner") def test_handle_duplicate_types_with_redefined_type( self, mock_select_winner, mock_merge_redefined_type ): one = ClassFactory.create() two = one.clone() three = one.clone() four = ClassFactory.create() mock_select_winner.return_value = 0 one.container = Tag.REDEFINE classes = [one, two, three, four] self.validator.handle_duplicate_types(classes) self.assertEqual([one, four], classes) mock_select_winner.assert_called_once_with([one, two, three]) mock_merge_redefined_type.assert_has_calls( [mock.call(two, one), mock.call(three, one),] ) def test_mark_strict_types(self): one = ClassFactory.create(qname="foo", type=Element) two = ClassFactory.create(qname="foo", type=ComplexType) three = ClassFactory.create(qname="foo", type=SimpleType) self.validator.mark_strict_types([one, two, three]) self.assertFalse(one.strict_type) # Is an element self.assertTrue(two.strict_type) # Marked as abstract self.assertFalse(three.strict_type) # Is common four = ClassFactory.create(qname="bar", type=Attribute) five = ClassFactory.create(qname="bar", type=AttributeGroup) self.validator.mark_strict_types([four, five]) self.assertFalse(four.strict_type) # No element in group self.assertFalse(five.strict_type) # No element in group @mock.patch.object(ClassUtils, "copy_extensions") @mock.patch.object(ClassUtils, "copy_attributes") def test_merge_redefined_type_with_circular_extension( self, mock_copy_attributes, mock_copy_extensions ): source = ClassFactory.create() target = source.clone() ext_a = ExtensionFactory.create(type=AttrTypeFactory.create(qname=source.name)) ext_str = ExtensionFactory.create(type=AttrTypeFactory.create(qname="foo")) target.extensions.append(ext_str) target.extensions.append(ext_a) self.validator.merge_redefined_type(source, target) mock_copy_attributes.assert_called_once_with(source, target, ext_a) mock_copy_extensions.assert_called_once_with(source, target, ext_a) @mock.patch.object(ClassUtils, "copy_group_attributes") def test_merge_redefined_type_with_circular_group(self, mock_copy_group_attributes): source = ClassFactory.create() target = source.clone() target.container = Tag.REDEFINE first_attr = AttrFactory.create() second_attr = AttrFactory.create(name=source.name) target.attrs.extend((first_attr, second_attr)) self.validator.merge_redefined_type(source, target) mock_copy_group_attributes.assert_called_once_with(source, target, second_attr) def test_select_winner(self): classes = ClassFactory.list(2) self.assertEqual(-1, self.validator.select_winner(classes)) classes[0].container = Tag.OVERRIDE self.assertEqual(0, self.validator.select_winner(classes)) classes[0].container = Tag.SCHEMA classes[1].container = Tag.REDEFINE self.assertEqual(1, self.validator.select_winner(classes))
def setUp(self): super().setUp() self.config = GeneratorConfig() self.container = ClassContainer() self.sanitizer = ClassSanitizer(container=self.container, config=self.config)
class ClassSanitizerTest(FactoryTestCase): def setUp(self): super().setUp() self.config = GeneratorConfig() self.container = ClassContainer() self.sanitizer = ClassSanitizer(container=self.container, config=self.config) @mock.patch.object(ClassSanitizer, "resolve_conflicts") @mock.patch.object(ClassSanitizer, "process_class") def test_process(self, mock_process_class, mock_resolve_conflicts): classes = ClassFactory.list(2) self.sanitizer.container.extend(classes) ClassSanitizer.process(self.container, self.config) mock_process_class.assert_has_calls(list(map(mock.call, classes))) mock_resolve_conflicts.assert_called_once_with() @mock.patch.object(ClassSanitizer, "process_duplicate_attribute_names") @mock.patch.object(ClassSanitizer, "process_attribute_sequence") @mock.patch.object(ClassSanitizer, "process_attribute_restrictions") @mock.patch.object(ClassSanitizer, "process_attribute_default") def test_process_class( self, mock_process_attribute_default, mock_process_attribute_restrictions, mock_process_attribute_sequence, mock_process_duplicate_attribute_names, ): target = ClassFactory.elements(2) inner = ClassFactory.elements(1) target.inner.append(inner) self.sanitizer.process_class(target) calls_with_target = [ mock.call(target.inner[0], target.inner[0].attrs[0]), mock.call(target, target.attrs[0]), mock.call(target, target.attrs[1]), ] calls_without_target = [ mock.call(target.inner[0].attrs[0]), mock.call(target.attrs[0]), mock.call(target.attrs[1]), ] mock_process_attribute_default.assert_has_calls(calls_with_target) mock_process_attribute_restrictions.assert_has_calls(calls_without_target) mock_process_attribute_sequence.assert_has_calls(calls_with_target) mock_process_duplicate_attribute_names.assert_has_calls( [mock.call(target.inner[0].attrs), mock.call(target.attrs)] ) @mock.patch.object(ClassSanitizer, "group_compound_fields") def test_process_class_group_compound_fields(self, mock_group_compound_fields): target = ClassFactory.create() inner = ClassFactory.create() target.inner.append(inner) self.config.output.compound_fields = True self.sanitizer.process_class(target) mock_group_compound_fields.assert_has_calls( [ mock.call(inner), mock.call(target), ] ) def test_process_attribute_default_with_enumeration(self): target = ClassFactory.create() attr = AttrFactory.enumeration() attr.restrictions.max_occurs = 2 attr.fixed = True self.sanitizer.process_attribute_default(target, attr) self.assertTrue(attr.fixed) def test_process_attribute_default_with_list_field(self): target = ClassFactory.create() attr = AttrFactory.create(fixed=True) attr.restrictions.max_occurs = 2 self.sanitizer.process_attribute_default(target, attr) self.assertFalse(attr.fixed) def test_process_attribute_default_with_optional_field(self): target = ClassFactory.create() attr = AttrFactory.create(fixed=True, default=2) attr.restrictions.min_occurs = 0 self.sanitizer.process_attribute_default(target, attr) self.assertFalse(attr.fixed) self.assertIsNone(attr.default) def test_process_attribute_default_with_xsi_type(self): target = ClassFactory.create() attr = AttrFactory.create( fixed=True, default=2, name="type", namespace=Namespace.XSI.uri ) self.sanitizer.process_attribute_default(target, attr) self.assertFalse(attr.fixed) self.assertIsNone(attr.default) def test_process_attribute_default_with_valid_case(self): target = ClassFactory.create() attr = AttrFactory.create(fixed=True, default=2) self.sanitizer.process_attribute_default(target, attr) self.assertTrue(attr.fixed) self.assertEqual(2, attr.default) @mock.patch("xsdata.codegen.sanitizer.logger.warning") @mock.patch.object(ClassSanitizer, "promote_inner_class") @mock.patch.object(ClassSanitizer, "find_enum") def test_process_attribute_default_enum( self, mock_find_enum, mock_promote_inner_class, mock_logger_warning ): enum_one = ClassFactory.enumeration(1, qname="root") enum_one.attrs[0].default = "1" enum_one.attrs[0].name = "one" enum_two = ClassFactory.enumeration(1, qname="inner") enum_two.attrs[0].default = "2" enum_two.attrs[0].name = "two" enum_three = ClassFactory.enumeration(1, qname="missing_member") mock_find_enum.side_effect = [ None, enum_one, None, enum_two, enum_three, ] target = ClassFactory.create( qname="target", attrs=[ AttrFactory.create( types=[ AttrTypeFactory.create(), AttrTypeFactory.create(qname="foo"), ], default="1", ), AttrFactory.create( types=[ AttrTypeFactory.create(), AttrTypeFactory.create(qname="bar", forward=True), ], default="2", ), AttrFactory.create(default="3"), ], ) actual = [] for attr in target.attrs: self.sanitizer.process_attribute_default(target, attr) actual.append(attr.default) self.assertEqual(["@enum@root::one", "@enum@inner::two", None], actual) mock_promote_inner_class.assert_called_once_with(target, enum_two) mock_logger_warning.assert_called_once_with( "No enumeration member matched %s.%s default value `%s`", target.name, target.attrs[2].local_name, "3", ) def test_promote_inner_class(self): target = ClassFactory.elements(2, qname="parent") inner = ClassFactory.create(qname="{foo}bar") target.inner.append(inner) target.attrs[1].types.append(AttrTypeFactory.create(forward=True, qname="bar")) clone_target = target.clone() self.container.add(target) self.sanitizer.promote_inner_class(target, inner) self.assertNotIn(inner, target.inner) self.assertEqual("{foo}parent_bar", target.attrs[1].types[1].qname) self.assertFalse(target.attrs[1].types[1].forward) self.assertEqual("{foo}parent_bar", inner.qname) self.assertEqual(2, len(self.container.data)) self.assertIn(inner, self.container["{foo}parent_bar"]) self.assertEqual(clone_target.attrs[0], target.attrs[0]) self.assertEqual(clone_target.attrs[1].types[0], target.attrs[1].types[0]) def test_find_enum(self): native_type = AttrTypeFactory.create() matching_external = AttrTypeFactory.create("foo") missing_external = AttrTypeFactory.create("bar") matching_inner = AttrTypeFactory.create("foobar", forward=True) missing_inner = AttrTypeFactory.create("barfoo", forward=True) enumeration = ClassFactory.enumeration(1, qname="foo") inner = ClassFactory.enumeration(1, qname="foobar") target = ClassFactory.create( attrs=[ AttrFactory.create( types=[ native_type, matching_external, missing_external, matching_inner, missing_inner, ] ) ], inner=[inner], ) self.sanitizer.container.extend([target, enumeration]) actual = self.sanitizer.find_enum(target, native_type) self.assertIsNone(actual) actual = self.sanitizer.find_enum(target, matching_external) self.assertEqual(enumeration, actual) actual = self.sanitizer.find_enum(target, missing_external) self.assertIsNone(actual) actual = self.sanitizer.find_enum(target, matching_inner) self.assertEqual(inner, actual) actual = self.sanitizer.find_enum(target, missing_inner) self.assertIsNone(actual) def test_process_attribute_restrictions(self): restrictions = [ Restrictions(min_occurs=0, max_occurs=0, required=True), Restrictions(min_occurs=0, max_occurs=1, required=True), Restrictions(min_occurs=1, max_occurs=1, required=False), Restrictions(max_occurs=2, required=True), Restrictions(min_occurs=2, max_occurs=2, required=True), ] expected = [ {}, {}, {"required": True}, {"max_occurs": 2}, {"max_occurs": 2, "min_occurs": 2}, ] for idx, res in enumerate(restrictions): attr = AttrFactory.create(restrictions=res) self.sanitizer.process_attribute_restrictions(attr) self.assertEqual(expected[idx], res.asdict()) def test_sanitize_duplicate_attribute_names(self): attrs = [ AttrFactory.create(name="a", tag=Tag.ELEMENT), AttrFactory.create(name="a", tag=Tag.ATTRIBUTE), AttrFactory.create(name="b", tag=Tag.ATTRIBUTE), AttrFactory.create(name="c", tag=Tag.ATTRIBUTE), AttrFactory.create(name="c", tag=Tag.ELEMENT), AttrFactory.create(name="d", tag=Tag.ELEMENT), AttrFactory.create(name="d", tag=Tag.ELEMENT), AttrFactory.create(name="e", tag=Tag.ELEMENT, namespace="b"), AttrFactory.create(name="e", tag=Tag.ELEMENT), AttrFactory.create(name="f", tag=Tag.ELEMENT), AttrFactory.create(name="f", tag=Tag.ELEMENT, namespace="a"), AttrFactory.create(name="gA", tag=Tag.ENUMERATION), AttrFactory.create(name="g[A]", tag=Tag.ENUMERATION), AttrFactory.create(name="g_a", tag=Tag.ENUMERATION), AttrFactory.create(name="g_a_1", tag=Tag.ENUMERATION), ] self.sanitizer.process_duplicate_attribute_names(attrs) expected = [ "a", "a_Attribute", "b", "c_Attribute", "c", "d_Element", "d", "b_e", "e", "f", "a_f", "gA", "g[A]_2", "g_a_3", "g_a_1", ] self.assertEqual(expected, [x.name for x in attrs]) def test_sanitize_attribute_sequence(self): def len_sequential(target: Class): return len([attr for attr in target.attrs if attr.restrictions.sequential]) restrictions = Restrictions(max_occurs=2, sequential=True) target = ClassFactory.create( attrs=[ AttrFactory.create(restrictions=restrictions.clone()), AttrFactory.create(restrictions=restrictions.clone()), ] ) attrs_clone = [attr.clone() for attr in target.attrs] self.sanitizer.process_attribute_sequence(target, target.attrs[0]) self.assertEqual(2, len_sequential(target)) target.attrs[0].restrictions.sequential = False self.sanitizer.process_attribute_sequence(target, target.attrs[0]) self.assertEqual(1, len_sequential(target)) self.sanitizer.process_attribute_sequence(target, target.attrs[1]) self.assertEqual(0, len_sequential(target)) target.attrs = attrs_clone target.attrs[1].restrictions.sequential = False self.sanitizer.process_attribute_sequence(target, target.attrs[0]) self.assertEqual(0, len_sequential(target)) target.attrs[0].restrictions.sequential = True target.attrs[0].restrictions.max_occurs = 0 target.attrs[1].restrictions.sequential = True self.sanitizer.process_attribute_sequence(target, target.attrs[0]) self.assertEqual(1, len_sequential(target)) @mock.patch.object(ClassSanitizer, "rename_classes") def test_resolve_conflicts(self, mock_rename_classes): classes = [ ClassFactory.create(qname="{foo}A"), ClassFactory.create(qname="{foo}a"), ClassFactory.create(qname="a"), ClassFactory.create(qname="b"), ClassFactory.create(qname="b"), ] self.sanitizer.container.extend(classes) self.sanitizer.resolve_conflicts() mock_rename_classes.assert_has_calls( [ mock.call(classes[:2]), mock.call(classes[3:]), ] ) @mock.patch.object(ClassSanitizer, "rename_class") def test_rename_classes(self, mock_rename_class): classes = [ ClassFactory.create(qname="a", type=Element), ClassFactory.create(qname="A", type=Element), ClassFactory.create(qname="a", type=ComplexType), ] self.sanitizer.rename_classes(classes) mock_rename_class.assert_has_calls( [ mock.call(classes[0]), mock.call(classes[1]), mock.call(classes[2]), ] ) @mock.patch.object(ClassSanitizer, "rename_class") def test_rename_classes_protects_single_element(self, mock_rename_class): classes = [ ClassFactory.create(qname="a", type=Element), ClassFactory.create(qname="a", type=ComplexType), ] self.sanitizer.rename_classes(classes) mock_rename_class.assert_called_once_with(classes[1]) @mock.patch.object(ClassSanitizer, "rename_dependency") def test_rename_class(self, mock_rename_dependency): target = ClassFactory.create(qname="{foo}a") self.sanitizer.container.add(target) self.sanitizer.container.add(ClassFactory.create()) self.sanitizer.container.add(ClassFactory.create(qname="{foo}a_1")) self.sanitizer.container.add(ClassFactory.create(qname="{foo}A_2")) self.sanitizer.rename_class(target) self.assertEqual("{foo}a_3", target.qname) self.assertEqual("a", target.meta_name) mock_rename_dependency.assert_has_calls( mock.call(item, "{foo}a", "{foo}a_3") for item in self.sanitizer.container.iterate() ) self.assertEqual([target], self.container.data["{foo}a_3"]) self.assertEqual([], self.container.data["{foo}a"]) def test_rename_dependency(self): attr_type = AttrTypeFactory.create("{foo}bar") target = ClassFactory.create( extensions=[ ExtensionFactory.create(), ExtensionFactory.create(type=attr_type.clone()), ], attrs=[ AttrFactory.create(), AttrFactory.create(types=[AttrTypeFactory.create(), attr_type.clone()]), ], inner=[ ClassFactory.create( extensions=[ExtensionFactory.create(type=attr_type.clone())], attrs=[ AttrFactory.create(), AttrFactory.create( types=[AttrTypeFactory.create(), attr_type.clone()] ), ], ) ], ) self.sanitizer.rename_dependency(target, "{foo}bar", "thug") dependencies = set(target.dependencies()) self.assertNotIn("{foo}bar", dependencies) self.assertIn("thug", dependencies) @mock.patch.object(ClassSanitizer, "group_fields") def test_group_compound_fields(self, mock_group_fields): target = ClassFactory.elements(8) # First group repeating target.attrs[0].restrictions.choice = "1" target.attrs[1].restrictions.choice = "1" target.attrs[1].restrictions.max_occurs = 2 # Second group repeating target.attrs[2].restrictions.choice = "2" target.attrs[3].restrictions.choice = "2" target.attrs[3].restrictions.max_occurs = 2 # Third group optional target.attrs[4].restrictions.choice = "3" target.attrs[5].restrictions.choice = "3" self.sanitizer.group_compound_fields(target) mock_group_fields.assert_has_calls( [ mock.call(target, target.attrs[0:2]), mock.call(target, target.attrs[2:4]), ] ) def test_group_fields(self): target = ClassFactory.create(attrs=AttrFactory.list(2)) target.attrs[0].restrictions.min_occurs = 10 target.attrs[0].restrictions.max_occurs = 15 target.attrs[1].restrictions.min_occurs = 5 target.attrs[1].restrictions.max_occurs = 20 expected = AttrFactory.create( name="attr_B_Or_attr_C", tag="Choice", index=0, types=[AttrTypeFactory.xs_any()], choices=[ AttrFactory.create( tag=target.attrs[0].tag, name="attr_B", types=target.attrs[0].types, ), AttrFactory.create( tag=target.attrs[1].tag, name="attr_C", types=target.attrs[1].types, ), ], ) expected_res = Restrictions(min_occurs=5, max_occurs=20) self.sanitizer.group_fields(target, list(target.attrs)) self.assertEqual(1, len(target.attrs)) self.assertEqual(expected, target.attrs[0]) self.assertEqual(expected_res, target.attrs[0].restrictions) def test_group_fields_limit_name(self): target = ClassFactory.create(attrs=AttrFactory.list(3)) self.sanitizer.group_fields(target, list(target.attrs)) self.assertEqual(1, len(target.attrs)) self.assertEqual("attr_B_Or_attr_C_Or_attr_D", target.attrs[0].name) target = ClassFactory.create(attrs=AttrFactory.list(4)) self.sanitizer.group_fields(target, list(target.attrs)) self.assertEqual("choice", target.attrs[0].name) def test_build_attr_choice(self): attr = AttrFactory.create( name="a", namespace="xsdata", default="123", help="help", fixed=True ) attr.local_name = "aaa" attr.restrictions = Restrictions( required=True, prohibited=None, min_occurs=1, max_occurs=1, min_exclusive="1.1", min_inclusive="1", min_length=1, max_exclusive="1", max_inclusive="1.1", max_length=10, total_digits=333, fraction_digits=2, length=5, white_space="collapse", pattern=r"[A-Z]", explicit_timezone="+1", nillable=True, choice="abc", sequential=True, ) expected_res = attr.restrictions.clone() expected_res.min_occurs = None expected_res.max_occurs = None expected_res.sequential = None actual = self.sanitizer.build_attr_choice(attr) self.assertEqual(attr.local_name, actual.name) self.assertEqual(attr.namespace, actual.namespace) self.assertEqual(attr.default, actual.default) self.assertEqual(attr.tag, actual.tag) self.assertEqual(attr.types, actual.types) self.assertEqual(expected_res, actual.restrictions) self.assertEqual(attr.help, actual.help) self.assertFalse(actual.fixed)
def process(cls, container: ClassContainer): """Iterate through all classes and run the sanitizer procedure.""" sanitizer = cls(container) collections.apply(container.iterate(), sanitizer.process_class)
class ClassContainerTests(FactoryTestCase): def setUp(self): super().setUp() self.container = ClassContainer() def test_initialize(self): classes = [ ClassFactory.create(qname="{xsdata}foo", tag=Tag.ELEMENT), ClassFactory.create(qname="{xsdata}foo", tag=Tag.COMPLEX_TYPE), ClassFactory.create(qname="{xsdata}foobar", tag=Tag.COMPLEX_TYPE), ] container = ClassContainer() container.extend(classes) expected = { "{xsdata}foo": classes[:2], "{xsdata}foobar": classes[2:], } self.assertEqual(2, len(container.data)) self.assertEqual(expected, container.data) self.assertEqual( [ "AttributeGroupHandler", "ClassExtensionHandler", "ClassEnumerationHandler", "AttributeSubstitutionHandler", "AttributeTypeHandler", "AttributeMergeHandler", "AttributeMixedContentHandler", "AttributeSanitizerHandler", ], [x.__class__.__name__ for x in container.processors], ) @mock.patch.object(ClassContainer, "process_class") def test_find(self, mock_process_class): def process_class(x: Class): x.status = Status.PROCESSED class_a = ClassFactory.create(qname="a") class_b = ClassFactory.create(qname="b", status=Status.PROCESSED) class_c = ClassFactory.enumeration(2, qname="b", status=Status.PROCESSING) mock_process_class.side_effect = process_class self.container.extend([class_a, class_b, class_c]) self.assertIsNone(self.container.find("nope")) self.assertEqual(class_a, self.container.find(class_a.qname)) self.assertEqual(class_b, self.container.find(class_b.qname)) self.assertEqual( class_c, self.container.find(class_b.qname, lambda x: x.is_enumeration)) mock_process_class.assert_called_once_with(class_a) @mock.patch.object(ClassContainer, "process_class") def test_find_inner(self, mock_process_class): obj = ClassFactory.create() first = ClassFactory.create(qname="{a}a") second = ClassFactory.create(qname="{a}b", status=Status.PROCESSED) obj.inner.extend((first, second)) def process_class(x: Class): x.status = Status.PROCESSED mock_process_class.side_effect = process_class self.assertEqual(first, self.container.find_inner(obj, "{a}a")) self.assertEqual(second, self.container.find_inner(obj, "{a}b")) mock_process_class.assert_called_once_with(first) def test_process(self): target = ClassFactory.create(inner=ClassFactory.list(2)) self.container.add(target) self.container.process_class(target) self.assertEqual(Status.PROCESSED, target.status) self.assertEqual(Status.PROCESSED, target.inner[0].status) self.assertEqual(Status.PROCESSED, target.inner[1].status) @mock.patch.object(Class, "should_generate", new_callable=mock.PropertyMock) def test_filter_classes(self, mock_class_should_generate): mock_class_should_generate.side_effect = [ True, False, False, True, False ] classes = ClassFactory.list(5) container = ClassContainer() container.extend(classes) expected = [ classes[0], classes[3], ] container.filter_classes() self.assertEqual(expected, container.class_list) @mock.patch.object(Class, "should_generate", new_callable=mock.PropertyMock) def test_filter_classes_with_only_simple_types(self, mock_class_should_generate): mock_class_should_generate.return_value = False classes = [ClassFactory.enumeration(2), ClassFactory.simple_type()] container = ClassContainer() container.extend(classes) container.filter_classes() self.assertEqual(classes, container.class_list)
def setUp(self): super().setUp() self.container = ClassContainer()
class ClassContainerTests(FactoryTestCase): def setUp(self): super().setUp() self.container = ClassContainer() def test_from_list(self): classes = [ ClassFactory.create(qname="{xsdata}foo", type=Element), ClassFactory.create(qname="{xsdata}foo", type=ComplexType), ClassFactory.create(qname="{xsdata}foobar", type=ComplexType), ] container = ClassContainer.from_list(classes) expected = { "{xsdata}foo": classes[:2], "{xsdata}foobar": classes[2:], } self.assertEqual(2, len(container)) self.assertEqual(expected, container) self.assertEqual( [ "AttributeGroupHandler", "ClassExtensionHandler", "AttributeEnumUnionHandler", "AttributeSubstitutionHandler", "AttributeTypeHandler", "AttributeMergeHandler", "AttributeMixedContentHandler", "AttributeMismatchHandler", ], [x.__class__.__name__ for x in container.processors], ) @mock.patch.object(ClassContainer, "process_class") def test_find(self, mock_process_class): def process_class(x: Class): x.status = Status.PROCESSED class_a = ClassFactory.create(qname="a") class_b = ClassFactory.create(qname="b", status=Status.PROCESSED) class_c = ClassFactory.enumeration(2, qname="b", status=Status.PROCESSING) mock_process_class.side_effect = process_class self.container.extend([class_a, class_b, class_c]) self.assertIsNone(self.container.find("nope")) self.assertEqual(class_a, self.container.find(class_a.qname)) self.assertEqual(class_b, self.container.find(class_b.qname)) self.assertEqual( class_c, self.container.find(class_b.qname, lambda x: x.is_enumeration) ) mock_process_class.assert_called_once_with(class_a) @mock.patch.object(ClassContainer, "process_class") def test_find_inner(self, mock_process_class): obj = ClassFactory.create() first = ClassFactory.create(qname="{a}a") second = ClassFactory.enumeration(2, qname="{a}a") third = ClassFactory.create(qname="{c}c", status=Status.PROCESSED) fourth = ClassFactory.enumeration(2, qname="{d}d", status=Status.PROCESSING) obj.inner.extend((first, second, third, fourth)) def process_class(x: Class): x.status = Status.PROCESSED def is_enum(x: Class): return x.is_enumeration mock_process_class.side_effect = process_class self.assertIsNone(self.container.find_inner(obj, "nope")) self.assertEqual(first, self.container.find_inner(obj, "a")) self.assertEqual(second, self.container.find_inner(obj, "a", is_enum)) self.assertEqual(third, self.container.find_inner(obj, "c")) self.assertEqual(fourth, self.container.find_inner(obj, "d", is_enum)) mock_process_class.assert_has_calls([mock.call(first), mock.call(second)]) def test_process(self): target = ClassFactory.create(inner=ClassFactory.list(2)) self.container.add(target) self.container.process_class(target) self.assertEqual(Status.PROCESSED, target.status) self.assertEqual(Status.PROCESSED, target.inner[0].status) self.assertEqual(Status.PROCESSED, target.inner[1].status) @mock.patch.object(Class, "should_generate", new_callable=mock.PropertyMock) def test_filter_classes(self, mock_class_should_generate): mock_class_should_generate.side_effect = [True, False, False, True, False] classes = ClassFactory.list(5) container = ClassContainer.from_list(classes) expected = [ classes[0], classes[3], ] container.filter_classes() self.assertEqual(expected, container.class_list) @mock.patch.object(Class, "should_generate", new_callable=mock.PropertyMock) def test_filter_classes_with_only_simple_types(self, mock_class_should_generate): mock_class_should_generate.return_value = False classes = [ClassFactory.enumeration(2), ClassFactory.create(type=SimpleType)] container = ClassContainer.from_list(classes) container.filter_classes() self.assertEqual(classes, container.class_list)
def setUp(self): super().setUp() container = ClassContainer() self.processor = ClassExtensionHandler(container=container)
def setUp(self): super().setUp() container = ClassContainer() self.processor = AttributeSubstitutionHandler(container=container)
def setUp(self): super().setUp() self.container = ClassContainer() self.sanitizer = ClassSanitizer(container=self.container)
def setUp(self): super().setUp() self.container = ClassContainer() self.validator = ClassValidator(container=self.container)