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

374 add hypothesis tests for times and views #379

Merged
merged 5 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
23 changes: 19 additions & 4 deletions fastkml/times.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,30 @@ def __init__(
resolution: Optional[DateTimeResolution] = None,
) -> None:
"""Initialize a KmlDateTime object."""
self.dt = dt
self.resolution = resolution
if resolution is None:
# sourcery skip: swap-if-expression
self.resolution = (
resolution = (
DateTimeResolution.date
if not isinstance(dt, datetime)
else DateTimeResolution.datetime
)
dt = (
dt.date()
if isinstance(dt, datetime) and resolution != DateTimeResolution.datetime
else dt
)
if resolution == DateTimeResolution.year:
self.dt = date(dt.year, 1, 1)
elif resolution == DateTimeResolution.year_month:
self.dt = date(dt.year, dt.month, 1)
else:
self.dt = dt
self.resolution = (
DateTimeResolution.date
if not isinstance(self.dt, datetime)
and resolution == DateTimeResolution.datetime
else resolution
)

def __repr__(self) -> str:
"""Create a string (c)representation for KmlDateTime."""
Expand Down Expand Up @@ -142,7 +157,7 @@ def parse(cls, datestr: str) -> Optional["KmlDateTime"]:
year = int(year_month_day_match.group("year"))
cleder marked this conversation as resolved.
Show resolved Hide resolved
month = int(year_month_day_match.group("month") or 1)
day = int(year_month_day_match.group("day") or 1)
dt = arrow.get(year, month, day).datetime
dt = date(year, month, day)
resolution = DateTimeResolution.date
if year_month_day_match.group("day") is None:
resolution = DateTimeResolution.year_month
Expand Down
91 changes: 35 additions & 56 deletions fastkml/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
from typing import Any
from typing import Dict
from typing import Optional
from typing import Union

from fastkml import config
from fastkml.base import _XMLObject
Expand All @@ -35,8 +34,8 @@
from fastkml.mixins import TimeMixin
from fastkml.registry import RegistryItem
from fastkml.registry import registry
from fastkml.times import TimeSpan
from fastkml.times import TimeStamp

__all__ = ["Camera", "LatLonAltBox", "Lod", "LookAt", "Region"]

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -100,7 +99,6 @@ def __init__(
heading: Optional[float] = None,
tilt: Optional[float] = None,
altitude_mode: Optional[AltitudeMode] = None,
time_primitive: Union[TimeSpan, TimeStamp, None] = None,
**kwargs: Any,
) -> None:
"""
Expand Down Expand Up @@ -128,8 +126,6 @@ def __init__(
The tilt angle of the view.
altitude_mode : Optional[AltitudeMode]
The altitude mode of the view.
time_primitive : Union[TimeSpan, TimeStamp, None]
The time primitive associated with the view.
kwargs : Any
Additional keyword arguments.

Expand All @@ -151,7 +147,6 @@ def __init__(
self.heading = heading
self.tilt = tilt
self.altitude_mode = altitude_mode
self.times = time_primitive

def __repr__(self) -> str:
"""Create a string (c)representation for _AbstractView."""
Expand All @@ -167,7 +162,6 @@ def __repr__(self) -> str:
f"heading={self.heading!r}, "
f"tilt={self.tilt!r}, "
f"altitude_mode={self.altitude_mode}, "
cleder marked this conversation as resolved.
Show resolved Hide resolved
f"time_primitive={self.times!r}, "
f"**{self._get_splat()!r},"
")"
)
Expand Down Expand Up @@ -233,29 +227,6 @@ def __repr__(self) -> str:
default=0.0,
),
)
registry.register(
_AbstractView,
RegistryItem(
ns_ids=("kml",),
attr_name="altitude_mode",
node_name="altitudeMode",
classes=(AltitudeMode,),
get_kwarg=subelement_enum_kwarg,
set_element=enum_subelement,
default=AltitudeMode.clamp_to_ground,
),
)
registry.register(
_AbstractView,
RegistryItem(
ns_ids=("kml",),
attr_name="time_primitive",
node_name="TimeStamp",
classes=(TimeSpan, TimeStamp),
get_kwarg=xml_subelement_kwarg,
set_element=xml_subelement,
),
)


class Camera(_AbstractView):
Expand Down Expand Up @@ -300,7 +271,6 @@ def __init__(
tilt: Optional[float] = None,
roll: Optional[float] = None,
altitude_mode: Optional[AltitudeMode] = None,
time_primitive: Union[TimeSpan, TimeStamp, None] = None,
**kwargs: Any,
) -> None:
"""
Expand All @@ -319,8 +289,6 @@ def __init__(
tilt (Optional[float]): The tilt of the view.
roll (Optional[float]): The roll of the view.
altitude_mode (AltitudeMode): The altitude mode of the view.
time_primitive (Union[TimeSpan, TimeStamp, None]): The time primitive of the
view.
**kwargs (Any): Additional keyword arguments.

Returns:
Expand All @@ -339,7 +307,6 @@ def __init__(
heading=heading,
tilt=tilt,
altitude_mode=altitude_mode,
time_primitive=time_primitive,
**kwargs,
)
self.roll = roll
Expand All @@ -359,7 +326,6 @@ def __repr__(self) -> str:
f"tilt={self.tilt!r}, "
f"roll={self.roll!r}, "
f"altitude_mode={self.altitude_mode}, "
f"time_primitive={self.times!r}, "
f"**{self._get_splat()!r},"
")"
)
Expand All @@ -377,6 +343,18 @@ def __repr__(self) -> str:
default=0.0,
),
)
registry.register(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider extracting the altitude_mode registration logic into a reusable helper function

The altitude_mode registration is duplicated across Camera, LookAt, and LatLonAltBox classes. Consider creating a helper function to reduce duplication while preserving namespace flexibility:

def register_altitude_mode(cls, ns_ids=("kml",)):
    """Helper to register altitude_mode with custom namespaces."""
    registry.register(
        cls,
        RegistryItem(
            ns_ids=ns_ids,
            attr_name="altitude_mode",
            node_name="altitudeMode", 
            classes=(AltitudeMode,),
            get_kwarg=subelement_enum_kwarg,
            set_element=enum_subelement,
            default=AltitudeMode.clamp_to_ground,
        ),
    )

# Usage:
register_altitude_mode(Camera, ns_ids=("kml", "gx", ""))
register_altitude_mode(LookAt, ns_ids=("kml", "gx", ""))
register_altitude_mode(LatLonAltBox, ns_ids=("kml", "gx", ""))

This maintains the namespace customization while eliminating code duplication.

Camera,
RegistryItem(
ns_ids=("kml", "gx", ""),
attr_name="altitude_mode",
node_name="altitudeMode",
classes=(AltitudeMode,),
get_kwarg=subelement_enum_kwarg,
set_element=enum_subelement,
default=AltitudeMode.clamp_to_ground,
),
)


class LookAt(_AbstractView):
Expand Down Expand Up @@ -407,7 +385,6 @@ def __init__(
tilt: Optional[float] = None,
range: Optional[float] = None,
altitude_mode: Optional[AltitudeMode] = None,
time_primitive: Union[TimeSpan, TimeStamp, None] = None,
**kwargs: Any,
) -> None:
"""
Expand All @@ -427,7 +404,6 @@ def __init__(
range (Optional[float]): The range value.
altitude_mode (AltitudeMode): The altitude mode. Defaults to
AltitudeMode.relative_to_ground.
time_primitive (Union[TimeSpan, TimeStamp, None]): The time primitive.
**kwargs (Any): Additional keyword arguments.

Returns:
Expand All @@ -446,7 +422,6 @@ def __init__(
heading=heading,
tilt=tilt,
altitude_mode=altitude_mode,
time_primitive=time_primitive,
**kwargs,
)
self.range = range
Expand All @@ -466,7 +441,6 @@ def __repr__(self) -> str:
f"tilt={self.tilt!r}, "
f"range={self.range!r}, "
f"altitude_mode={self.altitude_mode}, "
f"time_primitive={self.times!r}, "
f"**{self._get_splat()!r},"
")"
)
Expand All @@ -483,6 +457,18 @@ def __repr__(self) -> str:
set_element=float_subelement,
),
)
registry.register(
LookAt,
RegistryItem(
ns_ids=("kml", "gx", ""),
attr_name="altitude_mode",
node_name="altitudeMode",
classes=(AltitudeMode,),
get_kwarg=subelement_enum_kwarg,
set_element=enum_subelement,
default=AltitudeMode.clamp_to_ground,
),
)


class LatLonAltBox(_XMLObject):
Expand Down Expand Up @@ -653,7 +639,7 @@ def __bool__(self) -> bool:
registry.register(
LatLonAltBox,
RegistryItem(
ns_ids=("kml",),
ns_ids=("kml", "gx", ""),
attr_name="altitude_mode",
node_name="altitudeMode",
classes=(AltitudeMode,),
Expand Down Expand Up @@ -701,10 +687,10 @@ def __init__(
ns (Optional[str]): The namespace for the view.
name_spaces (Optional[Dict[str, str]]): The dictionary of namespace prefixes
and URIs.
min_lod_pixels (Optional[float]): The minimum level of detail in pixels.
max_lod_pixels (Optional[float]): The maximum level of detail in pixels.
min_fade_extent (Optional[float]): The minimum fade extent in pixels.
max_fade_extent (Optional[float]): The maximum fade extent in pixels.
min_lod_pixels (Optional[int]): The minimum level of detail in pixels.
max_lod_pixels (Optional[int]): The maximum level of detail in pixels.
min_fade_extent (Optional[int]): The minimum fade extent in pixels.
max_fade_extent (Optional[int]): The maximum fade extent in pixels.
**kwargs (Any): Additional keyword arguments.

Returns:
Expand Down Expand Up @@ -775,8 +761,8 @@ def __bool__(self) -> bool:
attr_name="min_fade_extent",
node_name="minFadeExtent",
classes=(float,),
get_kwarg=subelement_float_kwarg,
set_element=float_subelement,
get_kwarg=subelement_int_kwarg,
set_element=int_subelement,
default=0,
),
)
Expand All @@ -787,8 +773,8 @@ def __bool__(self) -> bool:
attr_name="max_fade_extent",
node_name="maxFadeExtent",
classes=(float,),
get_kwarg=subelement_float_kwarg,
set_element=float_subelement,
get_kwarg=subelement_int_kwarg,
set_element=int_subelement,
default=0,
),
)
Expand Down Expand Up @@ -902,10 +888,3 @@ def __bool__(self) -> bool:
set_element=xml_subelement,
),
)


__all__ = [
"Camera",
"LookAt",
"Region",
]
6 changes: 1 addition & 5 deletions tests/hypothesis/geometry_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,7 @@
altitude_mode=st.one_of(
st.none(),
st.sampled_from(
(
AltitudeMode.absolute,
AltitudeMode.clamp_to_ground,
AltitudeMode.relative_to_ground,
),
AltitudeMode,
),
),
)
Expand Down
14 changes: 2 additions & 12 deletions tests/hypothesis/gx_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,10 @@
# along with this library; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
"""Test gx Track and MultiTrack."""
import datetime
import typing

from hypothesis import given
from hypothesis import strategies as st
from hypothesis.extra.dateutil import timezones
from pygeoif.hypothesis.strategies import epsg4326
from pygeoif.hypothesis.strategies import points

Expand All @@ -29,12 +27,12 @@
import fastkml.types
from fastkml.gx import Angle
from fastkml.gx import TrackItem
from fastkml.times import KmlDateTime
from tests.base import Lxml
from tests.hypothesis.common import assert_repr_roundtrip
from tests.hypothesis.common import assert_str_roundtrip
from tests.hypothesis.common import assert_str_roundtrip_terse
from tests.hypothesis.common import assert_str_roundtrip_verbose
from tests.hypothesis.strategies import kml_datetimes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): Don't import test modules. (dont-import-test-modules)

ExplanationDon't import test modules.

Tests should be self-contained and don't depend on each other.

If a helper function is used by multiple tests,
define it in a helper module,
instead of importing one test from the other.

from tests.hypothesis.strategies import nc_name

track_items = st.builds(
Expand All @@ -51,15 +49,7 @@
),
),
coord=points(srs=epsg4326),
when=st.builds(
KmlDateTime,
dt=st.datetimes(
allow_imaginary=False,
timezones=timezones(),
min_value=datetime.datetime(2000, 1, 1), # noqa: DTZ001
max_value=datetime.datetime(2050, 1, 1), # noqa: DTZ001
),
),
when=kml_datetimes(),
)


Expand Down
8 changes: 1 addition & 7 deletions tests/hypothesis/multi_geometry_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,7 @@
tessellate=st.one_of(st.none(), st.booleans()),
altitude_mode=st.one_of(
st.none(),
st.sampled_from(
(
AltitudeMode.absolute,
AltitudeMode.clamp_to_ground,
AltitudeMode.relative_to_ground,
),
),
st.sampled_from(AltitudeMode),
),
)

Expand Down
Loading
Loading