Skip to content

Commit

Permalink
Coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
tefra committed May 14, 2020
1 parent 0491fd9 commit d5553d7
Show file tree
Hide file tree
Showing 12 changed files with 287 additions and 53 deletions.
46 changes: 46 additions & 0 deletions tests/formats/dataclass/filters/test_attribute_metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from unittest import TestCase

from tests.factories import AttrFactory
from tests.factories import FactoryTestCase
from xsdata.formats.dataclass.filters import attribute_metadata
from xsdata.models.enums import Tag


class AttributeMetadataTests(FactoryTestCase):
def test_attribute_metadata(self):
attr = AttrFactory.element()
expected = {"name": "attr_B", "type": "Element"}
self.assertEqual(expected, attribute_metadata(attr, None))
self.assertEqual(expected, attribute_metadata(attr, "foo"))

def test_attribute_metadata_namespace(self):
attr = AttrFactory.element(namespace="foo")
expected = {"name": "attr_B", "namespace": "foo", "type": "Element"}

self.assertEqual(expected, attribute_metadata(attr, None))
self.assertNotIn("namespace", attribute_metadata(attr, "foo"))

attr = AttrFactory.attribute(namespace="foo")
expected = {"name": "attr_C", "namespace": "foo", "type": "Attribute"}

self.assertEqual(expected, attribute_metadata(attr, None))
self.assertIn("namespace", attribute_metadata(attr, "foo"))

def test_attribute_metadata_name(self):
attr = AttrFactory.element(local_name="foo", name="bar")
actual = attribute_metadata(attr, None)
self.assertEqual("foo", actual["name"])

attr = AttrFactory.element(local_name="foo", name="Foo")
self.assertNotIn("name", attribute_metadata(attr, None))

attr = AttrFactory.create(tag=Tag.ANY, local_name="foo", name="bar")
self.assertNotIn("name", attribute_metadata(attr, None))

def test_attribute_metadata_restrictions(self):
attr = AttrFactory.create(tag=Tag.RESTRICTION)
attr.restrictions.max_occurs = 2
attr.restrictions.required = False

expected = {"max_occurs": 2}
self.assertEqual(expected, attribute_metadata(attr, None))
72 changes: 72 additions & 0 deletions tests/formats/dataclass/filters/test_default_imports.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from unittest import TestCase

from xsdata.formats.dataclass.filters import default_imports


class DefaultImportsTests(TestCase):
def test_default_imports_with_decimal(self):
output = " Decimal "

expected = "from decimal import Decimal"
self.assertIn(expected, default_imports(output))

def test_default_imports_with_enum(self):
output = " (Enum) "

expected = "from enum import Enum"
self.assertIn(expected, default_imports(output))

def test_default_imports_with_dataclasses(self):
output = " @dataclass "

expected = "from dataclasses import dataclass"
self.assertIn(expected, default_imports(output))

output = " field( "
expected = "from dataclasses import field"
self.assertIn(expected, default_imports(output))

output = " field( @dataclass "
expected = "from dataclasses import dataclass, field"
self.assertIn(expected, default_imports(output))

def test_default_imports_with_qname(self):
output = " QName "

expected = "from lxml.etree import QName"
self.assertIn(expected, default_imports(output))

def test_default_imports_with_typing(self):
output = " Dict[ "

expected = "from typing import Dict"
self.assertIn(expected, default_imports(output))

output = " List[ "

expected = "from typing import List"
self.assertIn(expected, default_imports(output))

output = " Optional[ "

expected = "from typing import Optional"
self.assertIn(expected, default_imports(output))

output = " Union[ "

expected = "from typing import Union"
self.assertIn(expected, default_imports(output))

output = " Optional[Union[Dict[ "

expected = "from typing import Dict, Optional, Union"
self.assertIn(expected, default_imports(output))

def test_default_imports_combo(self):
output = """@dataclass
class Foo:
field: Optional[str] = field(default=None)"""

expected = """from dataclasses import dataclass, field
from typing import Optional"""
self.assertEqual(expected, default_imports(output))
21 changes: 21 additions & 0 deletions tests/formats/dataclass/filters/test_format_arguments.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from unittest import TestCase

from tests.factories import AttrFactory
from tests.factories import FactoryTestCase
from xsdata.formats.dataclass.filters import attribute_metadata
from xsdata.formats.dataclass.filters import format_arguments
from xsdata.models.enums import Tag


class AttributeMetadataTests(TestCase):
def test_format_arguments(self):
data = dict(
num=1, text="foo", text_two="fo'o", text_three='fo"o', pattern="foo",
)

expected = '''num=1,
text="foo",
text_two="fo'o",
text_three="fo'o",
pattern=r"foo"'''
self.assertEqual(expected, format_arguments(data))
43 changes: 43 additions & 0 deletions tests/formats/dataclass/filters/test_format_docstring.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from tests.factories import AttrFactory
from tests.factories import ClassFactory
from tests.factories import FactoryTestCase
from xsdata.formats.dataclass.filters import class_docstring


class ClassDocstringTests(FactoryTestCase):
def test_class_docstring(self):
target = ClassFactory.create(
attrs=[
AttrFactory.element(help="help"),
AttrFactory.element(help="Foo\nBar"),
AttrFactory.element(),
]
)

expected = '''"""
:ivar attr_b: help
:ivar attr_c: Foo
Bar
:ivar attr_d:
"""'''
self.assertEqual(expected, class_docstring(target))

def test_class_docstring_with_class_help(self):
target = ClassFactory.elements(2, help="Help Me!")

expected = '''"""Help Me!
:ivar attr_b:
:ivar attr_c:
"""'''
self.assertEqual(expected, class_docstring(target))

def test_class_docstring_with_enumeration(self):
target = ClassFactory.enumeration(2, help="Help Me!")

expected = '''"""Help Me!
:cvar ATTR_B:
:cvar ATTR_C:
"""'''
self.assertEqual(expected, class_docstring(target, enum=True))
38 changes: 37 additions & 1 deletion tests/models/codegen/test_attr.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import sys
from unittest import mock

from tests.factories import AttrFactory
from tests.factories import FactoryTestCase
from xsdata.formats.dataclass.models.constants import XmlType
from xsdata.models.codegen import Attr
from xsdata.models.codegen import Restrictions
from xsdata.models.enums import Namespace
from xsdata.models.enums import Tag


class AttrTests(FactoryTestCase):
Expand Down Expand Up @@ -78,7 +82,6 @@ def test_property_is_wild_attr(self):
self.assertTrue(attr.is_wildcard)

def test_property_is_xsi_type(self):

attr = AttrFactory.create()
self.assertFalse(attr.is_xsi_type)

Expand All @@ -90,3 +93,36 @@ def test_property_is_xsi_type(self):

attr.name = "type"
self.assertTrue(attr.is_xsi_type)

@mock.patch.object(Attr, "xml_type", new_callable=mock.PropertyMock)
def test_property_is_nameless(self, mock_xml_type):
mock_xml_type.side_effect = [
XmlType.WILDCARD,
XmlType.ATTRIBUTES,
XmlType.ELEMENT,
XmlType.ATTRIBUTE,
XmlType.TEXT,
]
attr = AttrFactory.any_attribute()

self.assertTrue(attr.is_nameless)
self.assertTrue(attr.is_nameless)
self.assertFalse(attr.is_nameless)
self.assertFalse(attr.is_nameless)
self.assertFalse(attr.is_nameless)

def test_property_xml_type(self):
attr = AttrFactory.create(tag=Tag.ELEMENT)
self.assertEqual("Element", attr.xml_type)

attr = AttrFactory.create(tag=Tag.ATTRIBUTE)
self.assertEqual("Attribute", attr.xml_type)

attr = AttrFactory.create(tag=Tag.ANY_ATTRIBUTE)
self.assertEqual("Attributes", attr.xml_type)

attr = AttrFactory.create(tag=Tag.ANY)
self.assertEqual("Wildcard", attr.xml_type)

attr = AttrFactory.create(tag=Tag.RESTRICTION)
self.assertIsNone(attr.xml_type)
10 changes: 9 additions & 1 deletion tests/test_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -776,7 +776,6 @@ def test_sanitize_class(
mock_sanitize_attribute_sequence,
mock_sanitize_duplicate_attribute_names,
):

target = ClassFactory.elements(2)
inner = ClassFactory.elements(1)
target.inner.append(inner)
Expand Down Expand Up @@ -877,6 +876,15 @@ def test_sanitize_attribute_default_value_with_enumeration(self):

self.assertEqual([None, 2, None, "@[email protected]"], actual)

attr = AttrFactory.create(
default="missing", types=AttrTypeFactory.list(1, name="hit")
)

with self.assertRaises(AnalyzerError) as cm:
self.analyzer.sanitize_attribute_default_value(target, attr)

self.assertEqual("Unknown enumeration hit: missing", str(cm.exception))

@mock.patch.object(ClassAnalyzer, "flatten_class")
def test_class_depends_on_has_a_depth_limit(self, *args):
one = ClassFactory.create(extensions=ExtensionFactory.list(1))
Expand Down
47 changes: 28 additions & 19 deletions xsdata/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,25 +402,34 @@ def sanitize_attribute_default_value(self, target: Class, attr: Attr):
attr.default = None

if attr.default:
for attr_type in attr.types:
if attr_type.native:
continue

source = None
if attr_type.forward_ref:
attr.default = None
else:
source = self.find_class(
target.source_qname(attr_type.name),
condition=lambda x: x.is_enumeration,
)

if source:
enumeration = next(
(x.name for x in source.attrs if x.default == attr.default),
None,
)
attr.default = f"@enum@{source.name}.{enumeration}"
self.sanitize_attribute_default_enum(target, attr)

def sanitize_attribute_default_enum(self, target: Class, attr: Attr):
for attr_type in attr.types:
if attr_type.native:
continue
if attr_type.forward_ref:
attr.default = None
continue

source = self.find_class(
target.source_qname(attr_type.name),
condition=lambda x: x.is_enumeration,
)

if not source:
continue

enumeration = next(
(x.name for x in source.attrs if x.default == attr.default), None,
)

if not enumeration:
raise AnalyzerError(
f"Unknown enumeration {source.name}: {attr.default}"
)

attr.default = f"@enum@{source.name}.{enumeration}"

def class_depends_on(self, source: Class, target: Class, depth: int = 1) -> bool:
"""Check if any source dependencies recursively match the target
Expand Down
Loading

0 comments on commit d5553d7

Please sign in to comment.