Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolve compound fields issues with multi-inheritance types #533

Merged
merged 7 commits into from
Jun 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 60 additions & 21 deletions tests/codegen/handlers/test_attribute_compound_choice.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
from unittest import mock

from xsdata.codegen.container import ClassContainer
from xsdata.codegen.handlers import AttributeCompoundChoiceHandler
from xsdata.codegen.models import Class
from xsdata.codegen.models import Restrictions
from xsdata.models.config import GeneratorConfig
from xsdata.models.enums import DataType
from xsdata.utils.testing import AttrFactory
from xsdata.utils.testing import AttrTypeFactory
from xsdata.utils.testing import ClassFactory
from xsdata.utils.testing import ExtensionFactory
from xsdata.utils.testing import FactoryTestCase


class AttributeCompoundChoiceHandlerTests(FactoryTestCase):
def setUp(self):
super().setUp()

self.processor = AttributeCompoundChoiceHandler()
self.config = GeneratorConfig()
self.config.output.compound_fields = True
self.container = ClassContainer(config=self.config)
self.processor = AttributeCompoundChoiceHandler(container=self.container)

@mock.patch.object(AttributeCompoundChoiceHandler, "group_fields")
def test_process(self, mock_group_fields):
Expand Down Expand Up @@ -87,38 +94,34 @@ def test_group_fields_with_effective_choices_sums_occurs(self):
self.assertEqual(1, len(target.attrs))
self.assertEqual(expected_res, target.attrs[0].restrictions)

def test_group_fields_limit_name(self):
target = ClassFactory.create(attrs=AttrFactory.list(3))
for attr in target.attrs:
attr.restrictions.choice = "1"
def test_choose_name(self):
target = ClassFactory.create()

self.processor.group_fields(target, list(target.attrs))
actual = self.processor.choose_name(target, ["a", "b", "c"])
self.assertEqual("a_Or_b_Or_c", actual)

self.assertEqual(1, len(target.attrs))
self.assertEqual("attr_B_Or_attr_C_Or_attr_D", target.attrs[0].name)
actual = self.processor.choose_name(target, ["a", "b", "c", "d"])
self.assertEqual("choice", actual)

target = ClassFactory.create(attrs=AttrFactory.list(4))
for attr in target.attrs:
attr.restrictions.choice = "1"
target.attrs.append(AttrFactory.create(name="choice"))
actual = self.processor.choose_name(target, ["a", "b", "c", "d"])
self.assertEqual("choice_1", actual)

self.processor.group_fields(target, list(target.attrs))
self.assertEqual("choice", target.attrs[0].name)
base = ClassFactory.create()
base.attrs.append(AttrFactory.create(name="Choice!"))
target.extensions.append(ExtensionFactory.reference(base.qname))
self.container.extend((target, base))

target = ClassFactory.create()
attr = AttrFactory.element(restrictions=Restrictions(choice="1"))
target.attrs.append(attr)
target.attrs.append(attr.clone())
self.processor.group_fields(target, list(target.attrs))
self.assertEqual("choice", target.attrs[0].name)
target.attrs.clear()
actual = self.processor.choose_name(target, ["a", "b", "c", "d"])
self.assertEqual("choice_1", actual)

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",
Expand Down Expand Up @@ -152,3 +155,39 @@ def test_build_attr_choice(self):
self.assertEqual(expected_res, actual.restrictions)
self.assertEqual(attr.help, actual.help)
self.assertFalse(actual.fixed)

def test_reset_sequential(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.processor.compound_fields = False
self.processor.reset_sequential(target, 0)
self.assertEqual(2, len_sequential(target))

target.attrs[0].restrictions.sequential = False
self.processor.reset_sequential(target, 0)
self.assertEqual(1, len_sequential(target))

self.processor.reset_sequential(target, 1)
self.assertEqual(0, len_sequential(target))

target.attrs = attrs_clone
target.attrs[1].restrictions.sequential = False
self.processor.reset_sequential(target, 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.processor.reset_sequential(target, 0)
self.assertEqual(1, len_sequential(target))
37 changes: 37 additions & 0 deletions tests/codegen/handlers/test_attribute_default_value.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from xsdata.codegen.container import ClassContainer
from xsdata.codegen.handlers import AttributeDefaultValueHandler
from xsdata.models.config import GeneratorConfig
from xsdata.models.enums import DataType
from xsdata.models.enums import Namespace
from xsdata.utils.testing import AttrFactory
from xsdata.utils.testing import AttrTypeFactory
Expand All @@ -26,6 +27,34 @@ def test_process_attribute_with_enumeration(self):
self.processor.process_attribute(target, attr)
self.assertTrue(attr.fixed)

@mock.patch.object(AttributeDefaultValueHandler, "process_attribute")
def test_process_with_attr_choices(self, mock_process_attribute):
choice = AttrFactory.create(
name="attr_B_Or_attr_C",
tag="Choice",
index=0,
types=[AttrTypeFactory.native(DataType.ANY_TYPE)],
choices=[
AttrFactory.reference("one"),
AttrFactory.reference("two"),
AttrFactory.reference("three"),
],
)
target = ClassFactory.create()
target.attrs.append(choice)

self.processor.process(target)

self.assertEqual(4, mock_process_attribute.call_count)
mock_process_attribute.assert_has_calls(
[
mock.call(target, target.attrs[0]),
mock.call(target, target.attrs[0].choices[0]),
mock.call(target, target.attrs[0].choices[1]),
mock.call(target, target.attrs[0].choices[2]),
]
)

def test_process_attribute_with_optional_field(self):
target = ClassFactory.create()
attr = AttrFactory.create(fixed=True, default=2)
Expand All @@ -34,6 +63,14 @@ def test_process_attribute_with_optional_field(self):
self.assertFalse(attr.fixed)
self.assertIsNone(attr.default)

def test_process_attribute_with_list_field(self):
target = ClassFactory.create()
attr = AttrFactory.create(fixed=True, default=2)
attr.restrictions.max_occurs = 5
self.processor.process_attribute(target, attr)
self.assertFalse(attr.fixed)
self.assertIsNone(attr.default)

def test_process_attribute_with_xsi_type(self):
target = ClassFactory.create()
attr = AttrFactory.create(
Expand Down
2 changes: 1 addition & 1 deletion tests/codegen/handlers/test_attribute_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def test_process_attribute_with_circular_reference(self):
target = ClassFactory.create(qname="bar", tag=Tag.GROUP)
target.attrs.append(group_attr)

target.status = Status.PROCESSING
target.status = Status.FLATTENING
self.processor.container.add(target)

self.processor.process_attribute(target, group_attr)
Expand Down
14 changes: 8 additions & 6 deletions tests/codegen/handlers/test_attribute_overrides.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ def setUp(self):
@mock.patch.object(AttributeOverridesHandler, "validate_override")
def test_process(self, mock_validate_override, mock_resolve_conflict):
class_a = ClassFactory.create(
status=Status.PROCESSING,
status=Status.FLATTENING,
attrs=[
AttrFactory.create(name="el", tag=Tag.ELEMENT),
AttrFactory.create(name="at", tag=Tag.ATTRIBUTE),
],
)

class_b = ClassFactory.elements(2, status=Status.PROCESSED)
class_c = ClassFactory.create(status=Status.PROCESSED)
class_b = ClassFactory.elements(2, status=Status.FLATTENED)
class_c = ClassFactory.create(status=Status.FLATTENED)

class_b.extensions.append(ExtensionFactory.reference(class_c.qname))
class_a.extensions.append(ExtensionFactory.reference(class_b.qname))
Expand Down Expand Up @@ -78,14 +78,16 @@ def test_validate_override(self):
self.processor.validate_override(target, attr_a, attr_b)
self.assertEqual(1, len(target.attrs))

# restrictions except choice, min/max occurs don't match
# Restrictions don't match
attr_b.fixed = attr_a.fixed
attr_a.restrictions.required = not attr_b.restrictions.required
attr_a.restrictions.tokens = not attr_b.restrictions.tokens
attr_a.restrictions.nillable = not attr_b.restrictions.nillable
self.processor.validate_override(target, attr_a, attr_b)
self.assertEqual(1, len(target.attrs))

# Restrictions are compatible again
attr_a.restrictions.required = attr_b.restrictions.required
attr_a.restrictions.tokens = attr_b.restrictions.tokens
attr_a.restrictions.nillable = attr_b.restrictions.nillable
self.processor.validate_override(target, attr_a, attr_b)
self.assertEqual(0, len(target.attrs))

Expand Down
112 changes: 0 additions & 112 deletions tests/codegen/handlers/test_attribute_restrictions.py

This file was deleted.

20 changes: 17 additions & 3 deletions tests/codegen/handlers/test_attribute_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ def test_process_inner_type_with_simple_type(
self, mock_copy_attribute_properties, mock_update_restrictions
):
attr = AttrFactory.create(types=[AttrTypeFactory.create(qname="{bar}a")])
inner = ClassFactory.simple_type(qname="{bar}a", status=Status.PROCESSED)
inner = ClassFactory.simple_type(qname="{bar}a", status=Status.FLATTENED)
target = ClassFactory.create(inner=[inner])

self.processor.process_inner_type(target, attr, attr.types[0])
Expand All @@ -199,7 +199,7 @@ def test_process_inner_type_with_complex_type(
self, mock_copy_attribute_properties, mock_update_restrictions
):
target = ClassFactory.create()
inner = ClassFactory.elements(2, qname="a", status=Status.PROCESSED)
inner = ClassFactory.elements(2, qname="a", status=Status.FLATTENED)
attr = AttrFactory.create(types=[AttrTypeFactory.create(qname="a")])

target.inner.append(inner)
Expand Down Expand Up @@ -260,6 +260,20 @@ def test_copy_attribute_properties_from_nillable_source(self):
self.processor.copy_attribute_properties(source, target, attr, attr.types[0])
self.assertTrue(attr.restrictions.nillable)

def test_copy_attribute_properties_to_attribute_target(self):
source = ClassFactory.elements(1, nillable=True)
target = ClassFactory.create(attrs=AttrFactory.list(1, tag=Tag.ATTRIBUTE))
attr = target.attrs[0]
attr.restrictions.min_occurs = 1
attr.restrictions.max_occurs = 1

source.attrs[0].restrictions.min_occurs = 0
source.attrs[0].restrictions.max_occurs = 1

self.assertFalse(attr.is_optional)
self.processor.copy_attribute_properties(source, target, attr, attr.types[0])
self.assertFalse(attr.is_optional)

@mock.patch.object(AttributeTypeHandler, "is_circular_dependency")
def test_set_circular_flag(self, mock_is_circular_dependency):
source = ClassFactory.create()
Expand All @@ -281,7 +295,7 @@ def test_is_circular_dependency(self, mock_dependencies, mock_container_find):
source = ClassFactory.create()
target = ClassFactory.create()
another = ClassFactory.create()
processing = ClassFactory.create(status=Status.PROCESSING)
processing = ClassFactory.create(status=Status.FLATTENING)

find_classes = {"a": another, "b": target}

Expand Down
Loading