diff --git a/.gitignore b/.gitignore index 97717c04..87d5c97f 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ venv/ __pycache__/ *.stderr* docs/_* +.hypothesis/ diff --git a/pygeoif/geometry.py b/pygeoif/geometry.py index 58ed051a..fc86441a 100644 --- a/pygeoif/geometry.py +++ b/pygeoif/geometry.py @@ -117,7 +117,7 @@ def convex_hull(self) -> Optional[Union["Point", "LineString", "Polygon"]]: Returns a representation of the smallest convex Polygon containing all the points in the object unless the number of points in the object - is less than three. + is fewer than three. For two points, the convex hull collapses to a LineString; for 1, to a Point. """ @@ -271,7 +271,7 @@ def is_empty(self) -> bool: """ Return if this geometry is empty. - A Point is considered empty when it has less than 2 coordinates. + A Point is considered empty when it has fewer than 2 coordinates. """ return len(self._geoms) < 2 # noqa: PLR2004 @@ -382,7 +382,7 @@ def is_empty(self) -> bool: """ Return if this geometry is empty. - A Linestring is considered empty when it has less than 2 points. + A Linestring is considered empty when it has fewer than 2 points. """ return len(self._geoms) < 2 # noqa: PLR2004 diff --git a/pyproject.toml b/pyproject.toml index 78a35d36..fe2d5327 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -79,6 +79,7 @@ linting = [ "yamllint", ] tests = [ + "hypothesis", "pytest", "pytest-cov", ] diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..a6365803 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,52 @@ +"""Data-generating strategies for property-based testing.""" +import hypothesis.strategies as st + +# Incomplete list of allowed spatial reference systems to generate data +# - EPSG 4326 +# - EPSG 3857 +# ...? + + +# EPSG:4326 primitives +latitudes = st.floats( + min_value=-90.0, + max_value=90.0, + allow_nan=False, + allow_infinity=False, +) +longitudes = st.floats( + min_value=-180.0, + max_value=180.0, + allow_nan=False, + allow_infinity=False, +) +elevations = st.floats(allow_nan=False, allow_infinity=False) + + +# Point2D +@st.composite +def points_2d(draw, srs="EPSG:4326"): + if srs == "EPSG:4326": + return draw(st.tuples(latitudes, longitudes)) + raise NotImplementedError + + +# Point3D +@st.composite +def points_3d(draw, srs="EPSG:4326"): + if srs == "EPSG:4326": + return draw(st.tuples(latitudes, longitudes, elevations)) + raise NotImplementedError + + +# PointType +@st.composite +def points(draw, srs="EPSG:4326"): + if srs == "EPSG:4326": + return draw(st.one_of(points_2d(), points_3d())) + raise NotImplementedError + + +# LineType + +# Geometries diff --git a/tests/test_point.py b/tests/test_point.py index 37ad8803..c728feb9 100644 --- a/tests/test_point.py +++ b/tests/test_point.py @@ -3,9 +3,11 @@ from unittest import mock import pytest +from hypothesis import given from pygeoif import geometry from pygeoif.exceptions import DimensionError +from tests.conftest import points def test_empty() -> None: @@ -250,3 +252,10 @@ def test_hash_empty() -> None: point = geometry.Point(None, None) assert hash(point) == hash(()) + + +@given(points("EPSG:4326")) +def test_repr_eval_hypothesis_epsg_4326(point) -> None: + point = geometry.Point(*point) + + assert eval(repr(point), {}, {"Point": geometry.Point}) == point