diff --git a/docs/working_with_kml.rst b/docs/working_with_kml.rst index 0150953c..8cda819a 100644 --- a/docs/working_with_kml.rst +++ b/docs/working_with_kml.rst @@ -230,8 +230,8 @@ CascadingStyle element into a supported Style element. 13.96486261382906 0.0 0.0 - absolute 632.584179697442 + absolute #__managed_style_0D301BCC0014827EFCCB diff --git a/fastkml/times.py b/fastkml/times.py index 62b06fd9..e33c71b3 100644 --- a/fastkml/times.py +++ b/fastkml/times.py @@ -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.""" @@ -142,7 +157,7 @@ def parse(cls, datestr: str) -> Optional["KmlDateTime"]: year = int(year_month_day_match.group("year")) 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 diff --git a/fastkml/views.py b/fastkml/views.py index 5e56d545..97b3ea66 100644 --- a/fastkml/views.py +++ b/fastkml/views.py @@ -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 @@ -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__) @@ -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: """ @@ -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. @@ -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.""" @@ -167,7 +162,6 @@ def __repr__(self) -> str: f"heading={self.heading!r}, " f"tilt={self.tilt!r}, " f"altitude_mode={self.altitude_mode}, " - f"time_primitive={self.times!r}, " f"**{self._get_splat()!r}," ")" ) @@ -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): @@ -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: """ @@ -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: @@ -339,7 +307,6 @@ def __init__( heading=heading, tilt=tilt, altitude_mode=altitude_mode, - time_primitive=time_primitive, **kwargs, ) self.roll = roll @@ -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}," ")" ) @@ -377,6 +343,18 @@ def __repr__(self) -> str: default=0.0, ), ) +registry.register( + 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): @@ -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: """ @@ -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: @@ -446,7 +422,6 @@ def __init__( heading=heading, tilt=tilt, altitude_mode=altitude_mode, - time_primitive=time_primitive, **kwargs, ) self.range = range @@ -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}," ")" ) @@ -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): @@ -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,), @@ -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: @@ -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, ), ) @@ -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, ), ) @@ -902,10 +888,3 @@ def __bool__(self) -> bool: set_element=xml_subelement, ), ) - - -__all__ = [ - "Camera", - "LookAt", - "Region", -] diff --git a/tests/hypothesis/geometry_test.py b/tests/hypothesis/geometry_test.py index 0e85323d..22dd8d65 100644 --- a/tests/hypothesis/geometry_test.py +++ b/tests/hypothesis/geometry_test.py @@ -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, ), ), ) diff --git a/tests/hypothesis/gx_test.py b/tests/hypothesis/gx_test.py index a9c184cd..823aae4e 100644 --- a/tests/hypothesis/gx_test.py +++ b/tests/hypothesis/gx_test.py @@ -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 @@ -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 from tests.hypothesis.strategies import nc_name track_items = st.builds( @@ -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(), ) diff --git a/tests/hypothesis/multi_geometry_test.py b/tests/hypothesis/multi_geometry_test.py index f54a56f6..e20950f3 100644 --- a/tests/hypothesis/multi_geometry_test.py +++ b/tests/hypothesis/multi_geometry_test.py @@ -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), ), ) diff --git a/tests/hypothesis/strategies.py b/tests/hypothesis/strategies.py index 3e154cea..8f6cd19c 100644 --- a/tests/hypothesis/strategies.py +++ b/tests/hypothesis/strategies.py @@ -15,6 +15,7 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """Custom hypothesis strategies for testing.""" +import datetime import re import string from functools import partial @@ -22,6 +23,10 @@ from urllib.parse import urlencode from hypothesis import strategies as st +from hypothesis.extra.dateutil import timezones + +import fastkml.enums +from fastkml.times import KmlDateTime ID_TEXT: Final = string.ascii_letters + string.digits + ".-_" nc_name = partial( @@ -48,6 +53,27 @@ alphabet=st.characters(min_codepoint=1, blacklist_categories=("Cc", "Cs")), ) +kml_datetimes = partial( + st.builds, + KmlDateTime, + dt=st.one_of( + st.dates( + min_value=datetime.date(2000, 1, 1), + max_value=datetime.date(2050, 1, 1), + ), + 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 + ), + ), + resolution=st.one_of( + st.none(), + st.one_of(st.none(), st.sampled_from(fastkml.enums.DateTimeResolution)), + ), +) + @st.composite def query_strings(draw: st.DrawFn) -> str: diff --git a/tests/hypothesis/times_test.py b/tests/hypothesis/times_test.py new file mode 100644 index 00000000..b7b1ab71 --- /dev/null +++ b/tests/hypothesis/times_test.py @@ -0,0 +1,76 @@ +# Copyright (C) 2024 Christian Ledermann +# +# This library is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +""" +Property-based tests for the times module. + +These tests use the hypothesis library to generate random input for the +functions under test. The tests are run with pytest. +""" +import typing + +from hypothesis import given +from hypothesis import strategies as st + +import fastkml +import fastkml.enums +import fastkml.times +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 +from tests.hypothesis.strategies import nc_name + + +class TestTimes(Lxml): + @given( + id=st.one_of(st.none(), nc_name()), + target_id=st.one_of(st.none(), nc_name()), + timestamp=st.one_of(st.none(), kml_datetimes()), + ) + def test_fuzz_time_stamp( + self, + id: typing.Optional[str], + target_id: typing.Optional[str], + timestamp: typing.Optional[fastkml.times.KmlDateTime], + ) -> None: + time_stamp = fastkml.TimeStamp(id=id, target_id=target_id, timestamp=timestamp) + + assert_repr_roundtrip(time_stamp) + assert_str_roundtrip(time_stamp) + assert_str_roundtrip_terse(time_stamp) + assert_str_roundtrip_verbose(time_stamp) + + @given( + id=st.one_of(st.none(), nc_name()), + target_id=st.one_of(st.none(), nc_name()), + begin=st.one_of(st.none(), kml_datetimes()), + end=st.one_of(st.none(), kml_datetimes()), + ) + def test_fuzz_time_span( + self, + id: typing.Optional[str], + target_id: typing.Optional[str], + begin: typing.Optional[fastkml.times.KmlDateTime], + end: typing.Optional[fastkml.times.KmlDateTime], + ) -> None: + time_span = fastkml.TimeSpan(id=id, target_id=target_id, begin=begin, end=end) + + assert_repr_roundtrip(time_span) + assert_str_roundtrip(time_span) + assert_str_roundtrip_terse(time_span) + assert_str_roundtrip_verbose(time_span) diff --git a/tests/hypothesis/views_test.py b/tests/hypothesis/views_test.py new file mode 100644 index 00000000..bfdc8f8a --- /dev/null +++ b/tests/hypothesis/views_test.py @@ -0,0 +1,285 @@ +# Copyright (C) 2024 Christian Ledermann +# +# This library is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +"""Property-based tests for the views module.""" +import typing +from functools import partial + +from hypothesis import given +from hypothesis import strategies as st + +import fastkml +import fastkml.enums +import fastkml.views +from fastkml.views import LatLonAltBox +from fastkml.views import Lod +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 nc_name + +lods = partial( + st.builds, + Lod, + min_lod_pixels=st.integers(), + max_lod_pixels=st.integers(), + min_fade_extent=st.integers(), + max_fade_extent=st.integers(), +) + +lat_lon_alt_boxes = partial( + st.builds, + LatLonAltBox, + north=st.floats(allow_nan=False, allow_infinity=False, min_value=0, max_value=90), + south=st.floats(allow_nan=False, allow_infinity=False, min_value=0, max_value=90), + east=st.floats(allow_nan=False, allow_infinity=False, min_value=0, max_value=180), + west=st.floats(allow_nan=False, allow_infinity=False, min_value=0, max_value=180), + min_altitude=st.floats(allow_nan=False, allow_infinity=False).filter( + lambda x: x != 0, + ), + max_altitude=st.floats(allow_nan=False, allow_infinity=False).filter( + lambda x: x != 0, + ), + altitude_mode=st.sampled_from(fastkml.enums.AltitudeMode), +) + +common_view = partial( + given, + id=st.one_of(st.none(), nc_name()), + target_id=st.one_of(st.none(), nc_name()), + longitude=st.one_of( + st.none(), + st.floats( + allow_nan=False, + allow_infinity=False, + min_value=-180, + max_value=180, + ).filter(lambda x: x != 0), + ), + latitude=st.one_of( + st.none(), + st.floats( + allow_nan=False, + allow_infinity=False, + min_value=-90, + max_value=90, + ).filter(lambda x: x != 0), + ), + altitude=st.one_of( + st.none(), + st.floats(allow_nan=False, allow_infinity=False).filter(lambda x: x != 0), + ), + heading=st.one_of( + st.none(), + st.floats(allow_nan=False, allow_infinity=False, min_value=0, max_value=360), + ), + tilt=st.one_of( + st.none(), + st.floats(allow_nan=False, allow_infinity=False, min_value=0, max_value=180), + ), + altitude_mode=st.one_of(st.none(), st.sampled_from(fastkml.enums.AltitudeMode)), +) + + +class TestLxml(Lxml): + + @given( + min_lod_pixels=st.one_of(st.none(), st.integers()), + max_lod_pixels=st.one_of(st.none(), st.integers()), + min_fade_extent=st.one_of(st.none(), st.integers()), + max_fade_extent=st.one_of(st.none(), st.integers()), + ) + def test_fuzz_lod( + self, + min_lod_pixels: typing.Optional[int], + max_lod_pixels: typing.Optional[int], + min_fade_extent: typing.Optional[int], + max_fade_extent: typing.Optional[int], + ) -> None: + lod = fastkml.views.Lod( + min_lod_pixels=min_lod_pixels, + max_lod_pixels=max_lod_pixels, + min_fade_extent=min_fade_extent, + max_fade_extent=max_fade_extent, + ) + + assert_repr_roundtrip(lod) + assert_str_roundtrip(lod) + assert_str_roundtrip_terse(lod) + assert_str_roundtrip_verbose(lod) + + @given( + north=st.one_of( + st.none(), + st.floats(allow_nan=False, allow_infinity=False, min_value=0, max_value=90), + ), + south=st.one_of( + st.none(), + st.floats(allow_nan=False, allow_infinity=False, min_value=0, max_value=90), + ), + east=st.one_of( + st.none(), + st.floats( + allow_nan=False, + allow_infinity=False, + min_value=0, + max_value=180, + ), + ), + west=st.one_of( + st.none(), + st.floats( + allow_nan=False, + allow_infinity=False, + min_value=0, + max_value=180, + ), + ), + min_altitude=st.one_of( + st.none(), + st.floats(allow_nan=False, allow_infinity=False).filter(lambda x: x != 0), + ), + max_altitude=st.one_of( + st.none(), + st.floats(allow_nan=False, allow_infinity=False).filter(lambda x: x != 0), + ), + altitude_mode=st.one_of(st.none(), st.sampled_from(fastkml.enums.AltitudeMode)), + ) + def test_fuzz_lat_lon_alt_box( + self, + north: typing.Optional[float], + south: typing.Optional[float], + east: typing.Optional[float], + west: typing.Optional[float], + min_altitude: typing.Optional[float], + max_altitude: typing.Optional[float], + altitude_mode: typing.Optional[fastkml.enums.AltitudeMode], + ) -> None: + lat_lon_alt_box = fastkml.views.LatLonAltBox( + north=north, + south=south, + east=east, + west=west, + min_altitude=min_altitude, + max_altitude=max_altitude, + altitude_mode=altitude_mode, + ) + + assert_repr_roundtrip(lat_lon_alt_box) + assert_str_roundtrip(lat_lon_alt_box) + assert_str_roundtrip_terse(lat_lon_alt_box) + assert_str_roundtrip_verbose(lat_lon_alt_box) + + @given( + id=st.one_of(st.none(), nc_name()), + target_id=st.one_of(st.none(), nc_name()), + lat_lon_alt_box=st.one_of(st.none(), lat_lon_alt_boxes()), + lod=st.one_of(st.none(), lods()), + ) + def test_fuzz_region( + self, + id: typing.Optional[str], + target_id: typing.Optional[str], + lat_lon_alt_box: typing.Optional[fastkml.views.LatLonAltBox], + lod: typing.Optional[fastkml.views.Lod], + ) -> None: + region = fastkml.views.Region( + id=id, + target_id=target_id, + lat_lon_alt_box=lat_lon_alt_box, + lod=lod, + ) + + assert_repr_roundtrip(region) + assert_str_roundtrip(region) + assert_str_roundtrip_terse(region) + assert_str_roundtrip_verbose(region) + + @common_view( + roll=st.one_of( + st.none(), + st.floats( + allow_nan=False, + allow_infinity=False, + min_value=-180, + max_value=180, + ).filter(lambda x: x != 0), + ), + ) + def test_fuzz_camera( + self, + id: typing.Optional[str], + target_id: typing.Optional[str], + longitude: typing.Optional[float], + latitude: typing.Optional[float], + altitude: typing.Optional[float], + heading: typing.Optional[float], + tilt: typing.Optional[float], + altitude_mode: typing.Optional[fastkml.enums.AltitudeMode], + roll: typing.Optional[float], + ) -> None: + camera = fastkml.Camera( + id=id, + target_id=target_id, + longitude=longitude, + latitude=latitude, + altitude=altitude, + heading=heading, + tilt=tilt, + roll=roll, + altitude_mode=altitude_mode, + ) + + assert_repr_roundtrip(camera) + assert_str_roundtrip(camera) + assert_str_roundtrip_terse(camera) + assert_str_roundtrip_verbose(camera) + + @common_view( + range=st.one_of( + st.none(), + st.floats(allow_nan=False, allow_infinity=False).filter(lambda x: x != 0), + ), + ) + def test_fuzz_look_at( + self, + id: typing.Optional[str], + target_id: typing.Optional[str], + longitude: typing.Optional[float], + latitude: typing.Optional[float], + altitude: typing.Optional[float], + heading: typing.Optional[float], + tilt: typing.Optional[float], + altitude_mode: typing.Optional[fastkml.enums.AltitudeMode], + range: typing.Optional[float], + ) -> None: + look_at = fastkml.LookAt( + id=id, + target_id=target_id, + longitude=longitude, + latitude=latitude, + altitude=altitude, + heading=heading, + tilt=tilt, + range=range, + altitude_mode=altitude_mode, + ) + + assert_repr_roundtrip(look_at) + assert_str_roundtrip(look_at) + assert_str_roundtrip_terse(look_at) + assert_str_roundtrip_verbose(look_at) diff --git a/tests/repr_eq_test.py b/tests/repr_eq_test.py index 65d827bf..2023f4d7 100644 --- a/tests/repr_eq_test.py +++ b/tests/repr_eq_test.py @@ -340,7 +340,6 @@ class TestRepr(StdLibrary): tilt=61.61116895973212, range=359.3753895394523, altitude_mode=AltitudeMode.relative_to_ground, - time_primitive=None, ), times=None, style_url=None, @@ -1485,7 +1484,6 @@ class TestRepr(StdLibrary): tilt=51.96, range=301.9568, altitude_mode=AltitudeMode.relative_to_ground, - time_primitive=None, ), times=None, style_url=None, diff --git a/tests/times_test.py b/tests/times_test.py index 1876a003..201ba02d 100644 --- a/tests/times_test.py +++ b/tests/times_test.py @@ -102,7 +102,7 @@ def test_parse_year(self) -> None: assert dt assert dt.resolution == DateTimeResolution.year - assert dt.dt == datetime.datetime(2000, 1, 1, tzinfo=tzutc()) + assert dt.dt == datetime.date(2000, 1, 1) def test_parse_year_0(self) -> None: with pytest.raises( @@ -116,14 +116,14 @@ def test_parse_year_month(self) -> None: assert dt assert dt.resolution == DateTimeResolution.year_month - assert dt.dt == datetime.datetime(2000, 3, 1, tzinfo=tzutc()) + assert dt.dt == datetime.date(2000, 3, 1) def test_parse_year_month_no_dash(self) -> None: dt = KmlDateTime.parse("200004") assert dt assert dt.resolution == DateTimeResolution.year_month - assert dt.dt == datetime.datetime(2000, 4, 1, tzinfo=tzutc()) + assert dt.dt == datetime.date(2000, 4, 1) def test_parse_year_month_0(self) -> None: with pytest.raises(ValueError, match="month must be in 1..12"): @@ -138,14 +138,14 @@ def test_parse_year_month_day(self) -> None: assert dt assert dt.resolution == DateTimeResolution.date - assert dt.dt == datetime.datetime(2000, 3, 1, tzinfo=tzutc()) + assert dt.dt == datetime.date(2000, 3, 1) def test_parse_year_month_day_no_dash(self) -> None: dt = KmlDateTime.parse("20000401") assert dt assert dt.resolution == DateTimeResolution.date - assert dt.dt == datetime.datetime(2000, 4, 1, tzinfo=tzutc()) + assert dt.dt == datetime.date(2000, 4, 1) def test_parse_year_month_day_0(self) -> None: with pytest.raises( @@ -295,7 +295,7 @@ def test_read_timestamp_year(self) -> None: assert ts.timestamp assert ts.timestamp.resolution == DateTimeResolution.year - assert ts.timestamp.dt == datetime.datetime(1997, 1, 1, 0, 0, tzinfo=tzutc()) + assert ts.timestamp.dt == datetime.date(1997, 1, 1) def test_read_timestamp_year_month(self) -> None: doc = """ @@ -308,7 +308,7 @@ def test_read_timestamp_year_month(self) -> None: assert ts.timestamp assert ts.timestamp.resolution == DateTimeResolution.year_month - assert ts.timestamp.dt == datetime.datetime(1997, 7, 1, 0, 0, tzinfo=tzutc()) + assert ts.timestamp.dt == datetime.date(1997, 7, 1) def test_read_timestamp_ym_no_hyphen(self) -> None: doc = """ @@ -321,7 +321,7 @@ def test_read_timestamp_ym_no_hyphen(self) -> None: assert ts.timestamp assert ts.timestamp.resolution == DateTimeResolution.year_month - assert ts.timestamp.dt == datetime.datetime(1998, 8, 1, 0, 0, tzinfo=tzutc()) + assert ts.timestamp.dt == datetime.date(1998, 8, 1) def test_read_timestamp_ymd(self) -> None: doc = """ @@ -334,7 +334,7 @@ def test_read_timestamp_ymd(self) -> None: assert ts.timestamp assert ts.timestamp.resolution == DateTimeResolution.date - assert ts.timestamp.dt == datetime.datetime(1997, 7, 16, 0, 0, tzinfo=tzutc()) + assert ts.timestamp.dt == datetime.date(1997, 7, 16) def test_read_timestamp_utc(self) -> None: # dateTime (YYYY-MM-DDThh:mm:ssZ) @@ -393,7 +393,7 @@ def test_read_timespan(self) -> None: assert ts.begin assert ts.begin.resolution == DateTimeResolution.date - assert ts.begin.dt == datetime.datetime(1876, 8, 1, 0, 0, tzinfo=tzutc()) + assert ts.begin.dt == datetime.date(1876, 8, 1) assert ts.end assert ts.end.resolution == DateTimeResolution.datetime assert ts.end.dt == datetime.datetime(1997, 7, 16, 7, 30, 15, tzinfo=tzutc()) @@ -415,7 +415,7 @@ def test_feature_fromstring(self) -> None: assert d.time_stamp is None assert d.begin - assert d.begin.dt == datetime.datetime(1876, 8, 1, 0, 0, tzinfo=tzutc()) + assert d.begin.dt == datetime.date(1876, 8, 1) assert d.end assert d.end.dt == datetime.datetime(1997, 7, 16, 7, 30, 15, tzinfo=tzutc()) diff --git a/tests/views_test.py b/tests/views_test.py index 097dfaae..68231f15 100644 --- a/tests/views_test.py +++ b/tests/views_test.py @@ -16,11 +16,7 @@ """Test the (Abstract)Views classes.""" -import datetime -from dateutil.tz import tzutc - -from fastkml import times from fastkml import views from fastkml.enums import AltitudeMode from tests.base import Lxml @@ -32,12 +28,6 @@ class TestStdLibrary(StdLibrary): def test_create_camera(self) -> None: """Test the creation of a camera.""" - time_span = times.TimeSpan( - id="time-span-id", - begin=times.KmlDateTime(datetime.datetime(2019, 1, 1, tzinfo=tzutc())), - end=times.KmlDateTime(datetime.datetime(2019, 1, 2, tzinfo=tzutc())), - ) - camera = views.Camera( id="cam-id", target_id="target-cam-id", @@ -48,7 +38,6 @@ def test_create_camera(self) -> None: altitude_mode=AltitudeMode("relativeToGround"), latitude=50, longitude=60, - time_primitive=time_span, ) assert camera.heading == 10 @@ -60,12 +49,6 @@ def test_create_camera(self) -> None: assert camera.longitude == 60 assert camera.id == "cam-id" assert camera.target_id == "target-cam-id" - assert camera.begin == times.KmlDateTime( - datetime.datetime(2019, 1, 1, tzinfo=tzutc()), - ) - assert camera.end == times.KmlDateTime( - datetime.datetime(2019, 1, 2, tzinfo=tzutc()), - ) assert camera.to_string() def test_camera_read(self) -> None: @@ -73,10 +56,6 @@ def test_camera_read(self) -> None: camera_xml = ( '' - '' - "2019-01-01T00:00:00" - "2019-01-02T00:00:00" - "" "60" "50" "40" @@ -98,18 +77,8 @@ def test_camera_read(self) -> None: assert camera.longitude == 60 assert camera.id == "cam-id" assert camera.target_id == "target-cam-id" - assert camera.begin == times.KmlDateTime( - datetime.datetime(2019, 1, 1, tzinfo=tzutc()), - ) - assert camera.end == times.KmlDateTime( - datetime.datetime(2019, 1, 2, tzinfo=tzutc()), - ) def test_create_look_at(self) -> None: - time_stamp = times.TimeStamp( - id="time-span-id", - timestamp=times.KmlDateTime(datetime.datetime(2019, 1, 1, tzinfo=tzutc())), - ) look_at = views.LookAt( id="look-at-id", @@ -120,7 +89,6 @@ def test_create_look_at(self) -> None: altitude_mode=AltitudeMode("relativeToGround"), latitude=50, longitude=60, - time_primitive=time_stamp, ) assert look_at.heading == 10 @@ -131,14 +99,6 @@ def test_create_look_at(self) -> None: assert look_at.longitude == 60 assert look_at.id == "look-at-id" assert look_at.target_id == "target-look-at-id" - assert look_at.times.timestamp.dt == datetime.datetime( - 2019, - 1, - 1, - tzinfo=tzutc(), - ) - assert look_at.begin is None - assert look_at.end is None assert look_at.to_string() def test_look_at_read(self) -> None: @@ -150,9 +110,6 @@ def test_look_at_read(self) -> None: "10" "20" "relativeToGround" - '' - "2019-01-01T00:00:00" - "" "30" "" ) @@ -166,14 +123,6 @@ def test_look_at_read(self) -> None: assert look_at.longitude == 60 assert look_at.id == "look-at-id" assert look_at.target_id == "target-look-at-id" - assert look_at.times.timestamp.dt == datetime.datetime( - 2019, - 1, - 1, - tzinfo=tzutc(), - ) - assert look_at.begin is None - assert look_at.end is None def test_region_with_all_optional_parameters(self) -> None: """Region object can be initialized with all optional parameters."""