diff --git a/tests/gis_tests/geoapp/models.py b/tests/gis_tests/geoapp/models.py index 2c13c827c6..9dd6e442fa 100644 --- a/tests/gis_tests/geoapp/models.py +++ b/tests/gis_tests/geoapp/models.py @@ -102,3 +102,15 @@ class ManyPointModel(NamedModel): point1 = models.PointField() point2 = models.PointField() point3 = models.PointField(srid=3857) + + +class Points(models.Model): + geom = models.MultiPointField() + + +class Lines(models.Model): + geom = models.MultiLineStringField() + + +class GeometryCollectionModel(models.Model): + geom = models.GeometryCollectionField() diff --git a/tests/gis_tests/geoapp/test_functions.py b/tests/gis_tests/geoapp/test_functions.py index a6b072542c..d945c60b51 100644 --- a/tests/gis_tests/geoapp/test_functions.py +++ b/tests/gis_tests/geoapp/test_functions.py @@ -14,13 +14,12 @@ from django.contrib.gis.geos import ( Polygon, fromstr, ) -from django.contrib.gis.geos.libgeos import geos_version_tuple from django.contrib.gis.measure import Area from django.db import NotSupportedError, connection from django.db.models import F, IntegerField, Sum, Value from django.test import TestCase, skipUnlessDBFeature -from ..utils import FuncTestMixin +from ..utils import FuncTestMixin, can_save_multipoint from .models import ( City, Country, @@ -965,12 +964,7 @@ class GISFunctionsTests(FuncTestMixin, TestCase): ("MULTILINESTRING", MultiLineString), ("MULTIPOLYGON", MultiPolygon), ] - # GEOSWKTWriter_write() behavior was changed in GEOS 3.12+ to include - # parentheses for sub-members. MariaDB doesn't accept WKT - # representations with additional parentheses for MultiPoint. This is - # an accepted bug (MDEV-36166) in MariaDB that should be fixed in the - # future. - if not connection.ops.mariadb or geos_version_tuple() < (3, 12): + if can_save_multipoint: test_features.append( Feature(name="MultiPoint", geom=MultiPoint(Point(0, 0), Point(1, 1))) ) diff --git a/tests/gis_tests/geoapp/tests.py b/tests/gis_tests/geoapp/tests.py index 84138eb431..060ca723f8 100644 --- a/tests/gis_tests/geoapp/tests.py +++ b/tests/gis_tests/geoapp/tests.py @@ -1,4 +1,5 @@ from io import StringIO +from unittest import skipIf from django.contrib.gis import gdal from django.contrib.gis.db.models import Extent, MakeLine, Union, functions @@ -21,15 +22,18 @@ from django.db.models import F, OuterRef, Subquery from django.test import TestCase, skipUnlessDBFeature from django.test.utils import CaptureQueriesContext -from ..utils import skipUnlessGISLookup +from ..utils import cannot_save_multipoint, skipUnlessGISLookup from .models import ( City, Country, Feature, + GeometryCollectionModel, + Lines, MinusOneSRID, MultiFields, NonConcreteModel, PennsylvaniaCity, + Points, State, ThreeDimensionalFeature, Track, @@ -269,6 +273,48 @@ class GeoModelTest(TestCase): self.assertEqual(feature.geom.srid, g.srid) +class SaveLoadTests(TestCase): + def test_multilinestringfield(self): + geom = MultiLineString( + LineString((0, 0), (1, 1), (5, 5)), + LineString((0, 0), (0, 5), (5, 5), (5, 0), (0, 0)), + ) + obj = Lines.objects.create(geom=geom) + obj.refresh_from_db() + self.assertEqual(obj.geom.tuple, geom.tuple) + + def test_multilinestring_with_linearring(self): + geom = MultiLineString( + LineString((0, 0), (1, 1), (5, 5)), + LinearRing((0, 0), (0, 5), (5, 5), (5, 0), (0, 0)), + ) + obj = Lines.objects.create(geom=geom) + obj.refresh_from_db() + self.assertEqual(obj.geom.tuple, geom.tuple) + self.assertEqual(obj.geom[1].__class__.__name__, "LineString") + self.assertEqual(obj.geom[0].tuple, geom[0].tuple) + # LinearRings are transformed to LineString. + self.assertEqual(obj.geom[1].__class__.__name__, "LineString") + self.assertEqual(obj.geom[1].tuple, geom[1].tuple) + + @skipIf(cannot_save_multipoint, "MariaDB cannot save MultiPoint due to a bug.") + def test_multipointfield(self): + geom = MultiPoint(Point(1, 1), Point(0, 0)) + obj = Points.objects.create(geom=geom) + obj.refresh_from_db() + self.assertEqual(obj.geom, geom) + + def test_geometrycollectionfield(self): + geom = GeometryCollection( + Point(2, 2), + LineString((0, 0), (2, 2)), + Polygon(LinearRing((0, 0), (0, 5), (5, 5), (5, 0), (0, 0))), + ) + obj = GeometryCollectionModel.objects.create(geom=geom) + obj.refresh_from_db() + self.assertIs(obj.geom.equals(geom), True) + + class GeoLookupTest(TestCase): fixtures = ["initial"] diff --git a/tests/gis_tests/utils.py b/tests/gis_tests/utils.py index 2431ba4cec..c0c7d40322 100644 --- a/tests/gis_tests/utils.py +++ b/tests/gis_tests/utils.py @@ -4,6 +4,7 @@ from functools import wraps from unittest import mock from django.conf import settings +from django.contrib.gis.geos.libgeos import geos_version_tuple from django.db import DEFAULT_DB_ALIAS, connection from django.db.models import Func @@ -31,6 +32,12 @@ def skipUnlessGISLookup(*gis_lookups): _default_db = settings.DATABASES[DEFAULT_DB_ALIAS]["ENGINE"].rsplit(".")[-1] # MySQL spatial indices can't handle NULL geometries. gisfield_may_be_null = _default_db != "mysql" +# GEOSWKTWriter_write() behavior was changed in GEOS 3.12+ to include +# parentheses for sub-members. MariaDB doesn't accept WKT representations with +# additional parentheses for MultiPoint. This is an accepted bug (MDEV-36166) +# in MariaDB that should be fixed in the future. +cannot_save_multipoint = connection.ops.mariadb and geos_version_tuple() >= (3, 12) +can_save_multipoint = not cannot_save_multipoint class FuncTestMixin: