1
0
mirror of https://github.com/django/django.git synced 2025-08-21 17:29:13 +00:00

Used assertRaisesMessage and subTest where appropriate in GEOS tests.

This commit is contained in:
David Smith 2024-11-07 20:40:56 +00:00 committed by nessita
parent 896fa85b02
commit ef42718a2b
5 changed files with 754 additions and 607 deletions

View File

@ -11,12 +11,12 @@
{"wkt": "MULTIPOLYGON (((180 60, 240 160, 300 60, 180 60)), ((80 80, 180 60, 160 140, 240 160, 360 140, 300 60, 420 100, 320 280, 120 260, 80 80)))", "valid": true, "num_geom": 2, "n_p": 14}
],
"errors": [
{"wkt": "GEOMETR##!@#%#............a32515", "bad": true, "hex": false},
{"wkt": "Foo.Bar", "bad": true, "hex": false},
{"wkt": "POINT (5, 23)", "bad": true, "hex": false},
{"wkt": "AAABBBDDDAAD##@#1113511111-098111111111111111533333333333333", "bad": true, "hex": true},
{"wkt": "FFFFFFFFFFFFFFFFF1355555555555555555565111", "bad": true, "hex": true},
{"wkt": "", "bad": true, "hex": false}
{"wkt": "GEOMETR##!@#%#............a32515", "bad": true, "hex": false, "msg": "String input unrecognized as WKT EWKT, and HEXEWKB."},
{"wkt": "Foo.Bar", "bad": true, "hex": false, "msg": "String input unrecognized as WKT EWKT, and HEXEWKB."},
{"wkt": "POINT (5, 23)", "bad": true, "hex": false, "msg": "Error encountered checking Geometry returned from GEOS C function \"GEOSWKTReader_read_r\"."},
{"wkt": "AAABBBDDDAAD##@#1113511111-098111111111111111533333333333333", "bad": true, "hex": true, "msg": "String input unrecognized as WKT EWKT, and HEXEWKB."},
{"wkt": "FFFFFFFFFFFFFFFFF1355555555555555555565111", "bad": true, "hex": true, "msg": "Error encountered checking Geometry returned from GEOS C function \"GEOSWKBReader_readHEX_r\"."},
{"wkt": "", "bad": true, "hex": false, "msg": "String input unrecognized as WKT EWKT, and HEXEWKB."}
],
"wkt_out": [
{"wkt": "POINT (110 130)", "ewkt": "POINT (110 130)", "kml": "<Point><coordinates>110.0,130.0,0</coordinates></Point>", "gml": "<gml:Point><gml:coordinates>110,130</gml:coordinates></gml:Point>"},

View File

@ -28,14 +28,31 @@ from django.contrib.gis.shortcuts import numpy
from django.template import Context
from django.template.engine import Engine
from django.test import SimpleTestCase
from django.utils.version import PY312
from ..test_data import TestDataMixin
class GEOSTest(SimpleTestCase, TestDataMixin):
error_checking_geom = (
'Error encountered checking Geometry returned from GEOS C function "{}".'
)
def assertArgumentTypeError(self, i, bad_type):
if PY312:
msg = (
f"argument {i}: TypeError: '{bad_type}' object cannot be interpreted "
"as an integer"
)
else:
msg = f"argument {i}: TypeError: wrong type"
return self.assertRaisesMessage(ctypes.ArgumentError, msg)
def test_wkt(self):
"Testing WKT output."
for g in self.geometries.wkt_out:
with self.subTest(g=g):
geom = fromstr(g.wkt)
if geom.hasz:
self.assertEqual(g.ewkt, geom.wkt)
@ -50,6 +67,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
def test_hex(self):
"Testing HEX output."
for g in self.geometries.hex_wkt:
with self.subTest(g=g):
geom = fromstr(g.wkt)
self.assertEqual(g.hex, geom.hex.decode())
@ -92,28 +110,35 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
geom = fromstr(tg.wkt)
kml = getattr(tg, "kml", False)
if kml:
with self.subTest(tg=tg):
self.assertEqual(kml, geom.kml)
def test_errors(self):
"Testing the Error handlers."
# string-based
for err in self.geometries.errors:
with self.assertRaises((GEOSException, ValueError)):
with (
self.subTest(err=err.wkt),
self.assertRaisesMessage((GEOSException, ValueError), err.msg),
):
fromstr(err.wkt)
# Bad WKB
with self.assertRaises(GEOSException):
with self.assertRaisesMessage(
GEOSException, self.error_checking_geom.format("GEOSWKBReader_read_r")
):
GEOSGeometry(memoryview(b"0"))
class NotAGeometry:
pass
# Some other object
with self.assertRaises(TypeError):
GEOSGeometry(NotAGeometry())
# None
with self.assertRaises(TypeError):
GEOSGeometry(None)
for geom in (NotAGeometry(), None):
msg = f"Improper geometry input type: {type(geom)}"
with (
self.subTest(geom=geom),
self.assertRaisesMessage(TypeError, msg),
):
GEOSGeometry(geom)
def test_wkb(self):
"Testing WKB output."
@ -128,6 +153,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
geom_h = GEOSGeometry(g.hex)
# we need to do this so decimal places get normalized
geom_t = fromstr(g.wkt)
with self.subTest(g=g):
self.assertEqual(geom_t.wkt, geom_h.wkt)
def test_create_wkb(self):
@ -137,6 +163,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
geom_h = GEOSGeometry(wkb)
# we need to do this so decimal places get normalized
geom_t = fromstr(g.wkt)
with self.subTest(g=g):
self.assertEqual(geom_t.wkt, geom_h.wkt)
def test_ewkt(self):
@ -146,6 +173,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
for p in self.geometries.polygons:
ewkt = "SRID=%d;%s" % (srid, p.wkt)
poly = fromstr(ewkt)
with self.subTest(p=p):
self.assertEqual(srid, poly.srid)
self.assertEqual(srid, poly.shell.srid)
self.assertEqual(srid, fromstr(poly.ewkt).srid) # Checking export
@ -154,6 +182,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
"Testing GeoJSON input/output (via GDAL)."
for g in self.geometries.json_geoms:
geom = GEOSGeometry(g.wkt)
with self.subTest(g=g):
if not hasattr(g, "not_equal"):
# Loading jsons to prevent decimal differences
self.assertEqual(json.loads(g.json), json.loads(geom.json))
@ -187,6 +216,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
for fh in (wkt_f, wkb_f):
fh.seek(0)
pnt = fromfile(fh)
with self.subTest(fh=fh):
self.assertEqual(ref_pnt, pnt)
def test_eq(self):
@ -201,6 +231,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
# Error shouldn't be raise on equivalence testing with
# an invalid type.
for g in (p, ls):
with self.subTest(g=g):
self.assertIsNotNone(g)
self.assertNotEqual(g, {"foo": "bar"})
self.assertIsNot(g, False)
@ -331,6 +362,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
for p in self.geometries.points:
# Creating the point from the WKT
pnt = fromstr(p.wkt)
with self.subTest(p=p):
self.assertEqual(pnt.geom_type, "Point")
self.assertEqual(pnt.geom_typeid, 0)
self.assertEqual(pnt.dims, 0)
@ -391,6 +423,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
"Testing MultiPoint objects."
for mp in self.geometries.multipoints:
mpnt = fromstr(mp.wkt)
with self.subTest(mp=mp):
self.assertEqual(mpnt.geom_type, "MultiPoint")
self.assertEqual(mpnt.geom_typeid, 4)
self.assertEqual(mpnt.dims, 0)
@ -398,8 +431,10 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
self.assertAlmostEqual(mp.centroid[0], mpnt.centroid.tuple[0], 9)
self.assertAlmostEqual(mp.centroid[1], mpnt.centroid.tuple[1], 9)
with self.assertRaises(IndexError):
mpnt.__getitem__(len(mpnt))
mpnt_len = len(mpnt)
msg = f"invalid index: {mpnt_len}"
with self.assertRaisesMessage(IndexError, msg):
mpnt.__getitem__(mpnt_len)
self.assertEqual(mp.centroid, mpnt.centroid.tuple)
self.assertEqual(mp.coords, tuple(m.tuple for m in mpnt))
for p in mpnt:
@ -413,6 +448,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
prev = fromstr("POINT(0 0)")
for line in self.geometries.linestrings:
ls = fromstr(line.wkt)
with self.subTest(line=line):
self.assertEqual(ls.geom_type, "LineString")
self.assertEqual(ls.geom_typeid, 1)
self.assertEqual(ls.dims, 1)
@ -425,14 +461,18 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
self.assertEqual(ls, fromstr(line.wkt))
self.assertIs(ls == prev, False) # Use assertIs() to test __eq__.
with self.assertRaises(IndexError):
ls.__getitem__(len(ls))
ls_len = len(ls)
msg = f"invalid index: {ls_len}"
with self.assertRaisesMessage(IndexError, msg):
ls.__getitem__(ls_len)
prev = ls
# Creating a LineString from a tuple, list, and numpy array
self.assertEqual(ls, LineString(ls.tuple)) # tuple
self.assertEqual(ls, LineString(*ls.tuple)) # as individual arguments
self.assertEqual(ls, LineString([list(tup) for tup in ls.tuple])) # as list
self.assertEqual(
ls, LineString([list(tup) for tup in ls.tuple])
) # as list
# Point individual arguments
self.assertEqual(
ls.wkt, LineString(*tuple(Point(tup) for tup in ls.tuple)).wkt
@ -499,6 +539,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
prev = fromstr("POINT(0 0)")
for line in self.geometries.multilinestrings:
ml = fromstr(line.wkt)
with self.subTest(line=line):
self.assertEqual(ml.geom_type, "MultiLineString")
self.assertEqual(ml.geom_typeid, 5)
self.assertEqual(ml.dims, 1)
@ -515,9 +556,13 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
self.assertEqual(ls.geom_typeid, 1)
self.assertIs(ls.empty, False)
with self.assertRaises(IndexError):
ml.__getitem__(len(ml))
self.assertEqual(ml.wkt, MultiLineString(*tuple(s.clone() for s in ml)).wkt)
ml_len = len(ml)
msg = f"invalid index: {ml_len}"
with self.assertRaisesMessage(IndexError, msg):
ml.__getitem__(ml_len)
self.assertEqual(
ml.wkt, MultiLineString(*tuple(s.clone() for s in ml)).wkt
)
self.assertEqual(
ml, MultiLineString(*tuple(LineString(s.tuple) for s in ml))
)
@ -526,6 +571,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
"Testing LinearRing objects."
for rr in self.geometries.linearrings:
lr = fromstr(rr.wkt)
with self.subTest(rr=rr):
self.assertEqual(lr.geom_type, "LinearRing")
self.assertEqual(lr.geom_typeid, 2)
self.assertEqual(lr.dims, 1)
@ -582,6 +628,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
for p in self.geometries.polygons:
# Creating the Polygon, testing its properties.
poly = fromstr(p.wkt)
with self.subTest(p=p):
self.assertEqual(poly.geom_type, "Polygon")
self.assertEqual(poly.geom_typeid, 3)
self.assertEqual(poly.dims, 2)
@ -608,15 +655,21 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
self.assertEqual(ring.geom_typeid, 2)
if p.ext_ring_cs:
self.assertEqual(p.ext_ring_cs, ring.tuple)
self.assertEqual(p.ext_ring_cs, poly[0].tuple) # Testing __getitem__
self.assertEqual(
p.ext_ring_cs, poly[0].tuple
) # Testing __getitem__
# Testing __getitem__ and __setitem__ on invalid indices
with self.assertRaises(IndexError):
poly.__getitem__(len(poly))
with self.assertRaises(IndexError):
poly.__setitem__(len(poly), False)
with self.assertRaises(IndexError):
poly.__getitem__(-1 * len(poly) - 1)
poly_len = len(poly)
msg = f"invalid index: {poly_len}"
with self.assertRaisesMessage(IndexError, msg):
poly.__getitem__(poly_len)
with self.assertRaisesMessage(IndexError, msg):
poly.__setitem__(poly_len, False)
negative_index = -1 * poly_len - 1
msg = f"invalid index: {negative_index}"
with self.assertRaisesMessage(IndexError, msg):
poly.__getitem__(negative_index)
# Testing __iter__
for r in poly:
@ -624,9 +677,13 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
self.assertEqual(r.geom_typeid, 2)
# Testing polygon construction.
with self.assertRaises(TypeError):
msg = (
"Parameter must be a sequence of LinearRings or "
"objects that can initialize to LinearRings"
)
with self.assertRaisesMessage(TypeError, msg):
Polygon(0, [1, 2, 3])
with self.assertRaises(TypeError):
with self.assertRaisesMessage(TypeError, msg):
Polygon("foo")
# Polygon(shell, (hole1, ... holeN))
@ -667,23 +724,27 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
fromstr("POINT (0 0)")
for mp in self.geometries.multipolygons:
mpoly = fromstr(mp.wkt)
with self.subTest(mp=mp):
self.assertEqual(mpoly.geom_type, "MultiPolygon")
self.assertEqual(mpoly.geom_typeid, 6)
self.assertEqual(mpoly.dims, 2)
self.assertEqual(mp.valid, mpoly.valid)
if mp.valid:
mpoly_len = len(mpoly)
self.assertEqual(mp.num_geom, mpoly.num_geom)
self.assertEqual(mp.n_p, mpoly.num_coords)
self.assertEqual(mp.num_geom, len(mpoly))
with self.assertRaises(IndexError):
mpoly.__getitem__(len(mpoly))
self.assertEqual(mp.num_geom, mpoly_len)
msg = f"invalid index: {mpoly_len}"
with self.assertRaisesMessage(IndexError, msg):
mpoly.__getitem__(mpoly_len)
for p in mpoly:
self.assertEqual(p.geom_type, "Polygon")
self.assertEqual(p.geom_typeid, 3)
self.assertIs(p.valid, True)
self.assertEqual(
mpoly.wkt, MultiPolygon(*tuple(poly.clone() for poly in mpoly)).wkt
mpoly.wkt,
MultiPolygon(*tuple(poly.clone() for poly in mpoly)).wkt,
)
def test_memory_hijinks(self):
@ -713,6 +774,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
def test_coord_seq(self):
"Testing Coordinate Sequence objects."
for p in self.geometries.polygons:
with self.subTest(p=p):
if p.ext_ring_cs:
# Constructing the polygon and getting the coordinate sequence
poly = fromstr(p.wkt)
@ -731,7 +793,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
c2 = cs[i] # Value from coordseq
self.assertEqual(c1, c2)
# Constructing the test value to set the coordinate sequence with
# Construct the test value to set the coordinate sequence with
if len(c1) == 2:
tset = (5, 23)
else:
@ -746,11 +808,13 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
def test_relate_pattern(self):
"Testing relate() and relate_pattern()."
g = fromstr("POINT (0 0)")
with self.assertRaises(GEOSException):
msg = "invalid intersection matrix pattern"
with self.assertRaisesMessage(GEOSException, msg):
g.relate_pattern(0, "invalid pattern, yo")
for rg in self.geometries.relate_geoms:
a = fromstr(rg.wkt_a)
b = fromstr(rg.wkt_b)
with self.subTest(rg=rg):
self.assertEqual(rg.result, a.relate_pattern(b, rg.pattern))
self.assertEqual(rg.pattern, a.relate(b))
@ -760,8 +824,9 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
a = fromstr(self.geometries.topology_geoms[i].wkt_a)
b = fromstr(self.geometries.topology_geoms[i].wkt_b)
i1 = fromstr(self.geometries.intersect_geoms[i].wkt)
self.assertIs(a.intersects(b), True)
i2 = a.intersection(b)
with self.subTest(i=i):
self.assertIs(a.intersects(b), True)
self.assertTrue(i1.equals(i2))
self.assertTrue(i1.equals(a & b)) # __and__ is intersection operator
a &= b # testing __iand__
@ -774,6 +839,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
b = fromstr(self.geometries.topology_geoms[i].wkt_b)
u1 = fromstr(self.geometries.union_geoms[i].wkt)
u2 = a.union(b)
with self.subTest(i=i):
self.assertTrue(u1.equals(u2))
self.assertTrue(u1.equals(a | b)) # __or__ is union operator
a |= b # testing __ior__
@ -786,6 +852,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
b = fromstr(self.geometries.topology_geoms[i].wkt_b)
u1 = fromstr(self.geometries.union_geoms[i].wkt)
u2 = GeometryCollection(a, b).unary_union
with self.subTest(i=i):
self.assertTrue(u1.equals(u2))
def test_difference(self):
@ -795,6 +862,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
b = fromstr(self.geometries.topology_geoms[i].wkt_b)
d1 = fromstr(self.geometries.diff_geoms[i].wkt)
d2 = a.difference(b)
with self.subTest(i=i):
self.assertTrue(d1.equals(d2))
self.assertTrue(d1.equals(a - b)) # __sub__ is difference operator
a -= b # testing __isub__
@ -807,6 +875,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
b = fromstr(self.geometries.topology_geoms[i].wkt_b)
d1 = fromstr(self.geometries.sdiff_geoms[i].wkt)
d2 = a.sym_difference(b)
with self.subTest(i=i):
self.assertTrue(d1.equals(d2))
self.assertTrue(
d1.equals(a ^ b)
@ -819,7 +888,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
g = fromstr(bg.wkt)
# Can't use a floating-point for the number of quadsegs.
with self.assertRaises(ctypes.ArgumentError):
with self.assertArgumentTypeError(4, "float"):
g.buffer(bg.width, quadsegs=1.1)
self._test_buffer(self.geometries.buffer_geoms, "buffer")
@ -829,21 +898,22 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
g = fromstr(bg.wkt)
# Can't use a floating-point for the number of quadsegs.
with self.assertRaises(ctypes.ArgumentError):
with self.assertArgumentTypeError(4, "float"):
g.buffer_with_style(bg.width, quadsegs=1.1)
# Can't use a floating-point for the end cap style.
with self.assertRaises(ctypes.ArgumentError):
with self.assertArgumentTypeError(5, "float"):
g.buffer_with_style(bg.width, end_cap_style=1.2)
# Can't use a end cap style that is not in the enum.
with self.assertRaises(GEOSException):
msg = self.error_checking_geom.format("GEOSBufferWithStyle_r")
with self.assertRaisesMessage(GEOSException, msg):
g.buffer_with_style(bg.width, end_cap_style=55)
# Can't use a floating-point for the join style.
with self.assertRaises(ctypes.ArgumentError):
with self.assertArgumentTypeError(6, "float"):
g.buffer_with_style(bg.width, join_style=1.3)
# Can't use a join style that is not in the enum.
with self.assertRaises(GEOSException):
with self.assertRaisesMessage(GEOSException, msg):
g.buffer_with_style(bg.width, join_style=66)
self._test_buffer(
@ -873,6 +943,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
if hasattr(bg, kwarg_name)
}
buf = getattr(g, buffer_method_name)(**buf_kwargs)
with self.subTest(bg=bg):
self.assertEqual(exp_buf.num_coords, buf.num_coords)
self.assertEqual(len(exp_buf), len(buf))
@ -905,13 +976,14 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
self.assertEqual(4326, pnt.srid)
pnt.srid = 3084
self.assertEqual(3084, pnt.srid)
with self.assertRaises(ctypes.ArgumentError):
with self.assertArgumentTypeError(3, "str"):
pnt.srid = "4326"
# Testing SRID keyword on fromstr(), and on Polygon rings.
poly = fromstr(self.geometries.polygons[1].wkt, srid=4269)
self.assertEqual(4269, poly.srid)
for ring in poly:
with self.subTest(ring=ring):
self.assertEqual(4269, ring.srid)
poly.srid = 4326
self.assertEqual(4326, poly.shell.srid)
@ -922,6 +994,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
)
self.assertEqual(32021, gc.srid)
for i in range(len(gc)):
with self.subTest(i=i):
self.assertEqual(32021, gc[i].srid)
# GEOS may get the SRID from HEXEWKB
@ -956,6 +1029,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
"""Test with a null srid and a srid unknown to GDAL."""
for srid in [None, 999999]:
pnt = Point(111200, 220900, srid=srid)
with self.subTest(srid=srid):
self.assertTrue(
pnt.ewkt.startswith(
("SRID=%s;" % srid if srid else "") + "POINT (111200"
@ -982,12 +1056,16 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
# ### Testing the mutability of Polygons ###
for p in self.geometries.polygons:
poly = fromstr(p.wkt)
msg = (
"Parameter must be a sequence of LinearRings or objects that can "
"initialize to LinearRings"
)
with self.subTest(p=p):
# Should only be able to use __setitem__ with LinearRing geometries.
with self.assertRaises(TypeError):
with self.assertRaisesMessage(TypeError, msg):
poly.__setitem__(0, LineString((1, 1), (2, 2)))
# Constructing the new shell by adding 500 to every point in the old shell.
# Construct the new shell by adding 500 to every point in the old shell.
shell_tup = poly.shell.tuple
new_coords = []
for point in shell_tup:
@ -1007,6 +1085,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
# Creating a random point.
pnt = mp[i]
new = Point(random.randint(21, 100), random.randint(21, 100))
with self.subTest(tg=tg, i=i):
# Testing the assignment
mp[i] = new
str(new) # what was used for the assignment is still accessible
@ -1028,6 +1107,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
r[k] = (r[k][0] + 500.0, r[k][1] + 500.0)
poly[j] = r
with self.subTest(tg=tg, i=i, j=j):
self.assertNotEqual(mpoly[i], poly)
# Testing the assignment
mpoly[i] = poly
@ -1056,9 +1136,9 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
p[:] = (1, 2)
self.assertEqual(p.wkt, Point(1, 2))
with self.assertRaises(ValueError):
with self.assertRaisesMessage(ValueError, "Must have at least 2 items"):
p[:] = (1,)
with self.assertRaises(ValueError):
with self.assertRaisesMessage(ValueError, "Cannot have more than 3 items"):
p[:] = (1, 2, 3, 4, 5)
def test_linestring_list_assignment(self):
@ -1070,7 +1150,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
ls[:] = ((0, 0), (1, 1), (2, 2))
self.assertEqual(ls, LineString((0, 0), (1, 1), (2, 2)))
with self.assertRaises(ValueError):
with self.assertRaisesMessage(ValueError, "Must have at least 2 items"):
ls[:] = (1,)
def test_linearring_list_assignment(self):
@ -1082,7 +1162,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
ls[:] = ((0, 0), (0, 1), (1, 1), (1, 0), (0, 0))
self.assertEqual(ls, LinearRing((0, 0), (0, 1), (1, 1), (1, 0), (0, 0)))
with self.assertRaises(ValueError):
with self.assertRaisesMessage(ValueError, "Must have at least 4 items"):
ls[:] = ((0, 0), (1, 1), (2, 2))
def test_polygon_list_assignment(self):
@ -1114,7 +1194,8 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
# Testing a 3D Point
pnt = Point(2, 3, 8)
self.assertEqual((2.0, 3.0, 8.0), pnt.coords)
with self.assertRaises(TypeError):
msg = "Dimension of value does not match."
with self.assertRaisesMessage(TypeError, msg):
pnt.tuple = (1.0, 2.0)
pnt.coords = (1.0, 2.0, 3.0)
self.assertEqual((1.0, 2.0, 3.0), pnt.coords)
@ -1122,7 +1203,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
# Testing a 3D LineString
ls = LineString((2.0, 3.0, 8.0), (50.0, 250.0, -117.0))
self.assertEqual(((2.0, 3.0, 8.0), (50.0, 250.0, -117.0)), ls.tuple)
with self.assertRaises(TypeError):
with self.assertRaisesMessage(TypeError, msg):
ls.__setitem__(0, (1.0, 2.0))
ls[0] = (1.0, 2.0, 3.0)
self.assertEqual((1.0, 2.0, 3.0), ls[0])
@ -1186,6 +1267,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
geoms.append(LineString(numpy.array([])))
for g in geoms:
with self.subTest(g=g):
self.assertIs(g.empty, True)
# Testing len() and num_geom.
@ -1204,17 +1286,20 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
if isinstance(g, Point):
# IndexError is not raised in GEOS 3.8.0.
if geos_version_tuple() != (3, 8, 0):
with self.assertRaises(IndexError):
msg = "invalid GEOS Geometry index:"
with self.assertRaisesMessage(IndexError, msg):
g.x
elif isinstance(g, Polygon):
lr = g.shell
self.assertEqual("LINEARRING EMPTY", lr.wkt)
self.assertEqual(0, len(lr))
self.assertIs(lr.empty, True)
with self.assertRaises(IndexError):
msg = "invalid index: 0"
with self.assertRaisesMessage(IndexError, msg):
lr.__getitem__(0)
else:
with self.assertRaises(IndexError):
msg = "invalid index: 0"
with self.assertRaisesMessage(IndexError, msg):
g.__getitem__(0)
def test_collection_dims(self):
@ -1306,6 +1391,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
# correct as having a 1 meter accuracy.
prec = -1
for p in (t1, t2, t3, k2):
with self.subTest(p=p):
self.assertAlmostEqual(trans.x, p.x, prec)
self.assertAlmostEqual(trans.y, p.y, prec)
@ -1332,22 +1418,14 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
def test_transform_nosrid(self):
"""Testing `transform` method (no SRID or negative SRID)"""
g = GEOSGeometry("POINT (-104.609 38.255)", srid=None)
with self.assertRaises(GEOSException):
g.transform(2774)
g = GEOSGeometry("POINT (-104.609 38.255)", srid=None)
with self.assertRaises(GEOSException):
g.transform(2774, clone=True)
g = GEOSGeometry("POINT (-104.609 38.255)", srid=-1)
with self.assertRaises(GEOSException):
g.transform(2774)
g = GEOSGeometry("POINT (-104.609 38.255)", srid=-1)
with self.assertRaises(GEOSException):
g.transform(2774, clone=True)
msg = "Calling transform() with no SRID set is not supported"
for srid, clone in itertools.product((None, -1), (True, False)):
g = GEOSGeometry("POINT (-104.609 38.255)", srid=srid)
with (
self.subTest(srid=srid, clone=clone),
self.assertRaisesMessage(GEOSException, msg),
):
g.transform(2774, clone=clone)
def test_extent(self):
"Testing `extent` method."
@ -1382,6 +1460,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
for geom in tgeoms:
s1 = pickle.dumps(geom)
g1 = pickle.loads(s1)
with self.subTest(geom=geom):
self.assertEqual(geom, g1)
self.assertEqual(geom.srid, g1.srid)
@ -1397,6 +1476,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
pnts = [Point(5, 5), Point(7.5, 7.5), Point(2.5, 7.5)]
for pnt in pnts:
# Results should be the same (but faster)
with self.subTest(pnt=pnt):
self.assertEqual(mpoly.contains(pnt), prep.contains(pnt))
self.assertEqual(mpoly.intersects(pnt), prep.intersects(pnt))
self.assertEqual(mpoly.covers(pnt), prep.covers(pnt))
@ -1643,5 +1723,5 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
# Step into CoordSeq iterator.
next(it)
ls[:] = []
with self.assertRaises(IndexError):
with self.assertRaisesMessage(IndexError, "invalid index: 1"):
next(it)

View File

@ -2,8 +2,6 @@
# Modified from original contribution by Aryeh Leib Taurog, which was
# released under the New BSD license.
import unittest
from django.contrib.gis.geos import (
LinearRing,
LineString,
@ -12,6 +10,7 @@ from django.contrib.gis.geos import (
Polygon,
fromstr,
)
from django.test import SimpleTestCase
def api_get_distance(x):
@ -77,7 +76,7 @@ geos_function_tests = [
]
class GEOSMutationTest(unittest.TestCase):
class GEOSMutationTest(SimpleTestCase):
"""
Tests Pythonic Mutability of Python GEOS geometry wrappers
get/set/delitem on a slice, normal list methods
@ -87,15 +86,20 @@ class GEOSMutationTest(unittest.TestCase):
"Testing Geometry IndexError"
p = Point(1, 2)
for i in range(-2, 2):
with self.subTest(i=i):
p._checkindex(i)
for i in (2, -3):
with (
self.subTest(i=i),
self.assertRaisesMessage(IndexError, f"invalid index: {i}"),
):
p._checkindex(i)
with self.assertRaises(IndexError):
p._checkindex(2)
with self.assertRaises(IndexError):
p._checkindex(-3)
def test01_PointMutations(self):
"Testing Point mutations"
for p in (Point(1, 2, 3), fromstr("POINT (1 2 3)")):
with self.subTest(p=p):
self.assertEqual(
p._get_single_external(1), 2.0, "Point _get_single_external"
)
@ -110,10 +114,13 @@ class GEOSMutationTest(unittest.TestCase):
def test02_PointExceptions(self):
"Testing Point exceptions"
with self.assertRaises(TypeError):
Point(range(1))
with self.assertRaises(TypeError):
Point(range(4))
msg = "Invalid parameters given for Point initialization."
for i in (range(1), range(4)):
with (
self.subTest(i=i),
self.assertRaisesMessage(TypeError, msg),
):
Point(i)
def test03_PointApi(self):
"Testing Point API"
@ -121,6 +128,7 @@ class GEOSMutationTest(unittest.TestCase):
for p in (Point(1, 2, 3), fromstr("POINT (1 2 3)")):
p[0:2] = [4, 5]
for f in geos_function_tests:
with self.subTest(p=p, f=f):
self.assertEqual(f(q), f(p), "Point " + f.__name__)
def test04_LineStringMutations(self):
@ -129,6 +137,7 @@ class GEOSMutationTest(unittest.TestCase):
LineString((1, 0), (4, 1), (6, -1)),
fromstr("LINESTRING (1 0,4 1,6 -1)"),
):
with self.subTest(ls=ls):
self.assertEqual(
ls._get_single_external(1),
(4.0, 1.0),
@ -151,6 +160,7 @@ class GEOSMutationTest(unittest.TestCase):
lsa = LineString(ls.coords)
for f in geos_function_tests:
with self.subTest(f=f):
self.assertEqual(f(lsa), f(ls), "LineString " + f.__name__)
def test05_Polygon(self):
@ -162,6 +172,7 @@ class GEOSMutationTest(unittest.TestCase):
),
fromstr("POLYGON ((1 0,4 1,6 -1,8 10,1 0),(5 4,6 4,6 3,5 4))"),
):
with self.subTest(pg=pg):
self.assertEqual(
pg._get_single_external(0),
LinearRing((1, 0), (4, 1), (6, -1), (8, 10), (1, 0)),
@ -184,7 +195,13 @@ class GEOSMutationTest(unittest.TestCase):
self.assertEqual(
pg.coords,
(
((1.0, 2.0), (10.0, 0.0), (12.0, 9.0), (-1.0, 15.0), (1.0, 2.0)),
(
(1.0, 2.0),
(10.0, 0.0),
(12.0, 9.0),
(-1.0, 15.0),
(1.0, 2.0),
),
((4.0, 2.0), (5.0, 2.0), (5.0, 3.0), (4.0, 2.0)),
),
"Polygon _set_list",
@ -192,6 +209,7 @@ class GEOSMutationTest(unittest.TestCase):
lsa = Polygon(*pg.coords)
for f in geos_function_tests:
with self.subTest(f=f):
self.assertEqual(f(lsa), f(pg), "Polygon " + f.__name__)
def test06_Collection(self):
@ -201,6 +219,7 @@ class GEOSMutationTest(unittest.TestCase):
fromstr("MULTIPOINT (3 4,-1 2,5 -4,2 8)"),
)
for mp in points:
with self.subTest(mp=mp):
self.assertEqual(
mp._get_single_external(2),
Point(5, -4),
@ -209,9 +228,12 @@ class GEOSMutationTest(unittest.TestCase):
mp._set_list(3, map(Point, ((5, 5), (3, -2), (8, 1))))
self.assertEqual(
mp.coords, ((5.0, 5.0), (3.0, -2.0), (8.0, 1.0)), "Collection _set_list"
mp.coords,
((5.0, 5.0), (3.0, -2.0), (8.0, 1.0)),
"Collection _set_list",
)
lsa = MultiPoint(*map(Point, ((5, 5), (3, -2), (8, 1))))
for f in geos_function_tests:
with self.subTest(f=f):
self.assertEqual(f(lsa), f(mp), "MultiPoint " + f.__name__)

View File

@ -25,6 +25,7 @@ class GEOSIOTest(SimpleTestCase):
g2 = wkt_r.read(wkt)
for geom in (g1, g2):
with self.subTest(geom=geom):
self.assertEqual(ref, geom)
# Should only accept string objects.
@ -67,6 +68,7 @@ class GEOSIOTest(SimpleTestCase):
g2 = wkb_r.read(hex_bin)
g3 = wkb_r.read(hex_str)
for geom in (g1, g2, g3):
with self.subTest(geom=geom):
self.assertEqual(ref, geom)
bad_input = (1, 5.23, None, False)
@ -89,9 +91,13 @@ class GEOSIOTest(SimpleTestCase):
self.assertEqual(wkb1, wkb_w.write(g))
# Ensuring bad byteorders are not accepted.
msg = "Byte order parameter must be 0 (Big Endian) or 1 (Little Endian)."
for bad_byteorder in (-1, 2, 523, "foo", None):
# Equivalent of `wkb_w.byteorder = bad_byteorder`
with self.assertRaises(ValueError):
with (
self.subTest(bad_byteorder=bad_byteorder),
self.assertRaisesMessage(ValueError, msg),
):
wkb_w._set_byteorder(bad_byteorder)
# Setting the byteorder to 0 (for Big Endian)
@ -114,9 +120,11 @@ class GEOSIOTest(SimpleTestCase):
wkb3d_srid = memoryview(binascii.a2b_hex(hex3d_srid))
# Ensuring bad output dimensions are not accepted
msg = "WKB output dimension must be 2 or 3"
for bad_outdim in (-1, 0, 1, 4, 423, "foo", None):
with self.assertRaisesMessage(
ValueError, "WKB output dimension must be 2 or 3"
with (
self.subTest(bad_outdim=bad_outdim),
self.assertRaisesMessage(ValueError, msg),
):
wkb_w.outdim = bad_outdim
@ -221,9 +229,12 @@ class GEOSIOTest(SimpleTestCase):
wkb_w.byteorder = byteorder
for srid, hex in enumerate(hexes):
wkb_w.srid = srid
with self.subTest(byteorder=byteorder, hexes=hexes):
self.assertEqual(wkb_w.write_hex(p), hex)
self.assertEqual(
GEOSGeometry(wkb_w.write_hex(p)), p if srid else p_no_srid
)
self.assertEqual(wkb_w.write(p), memoryview(binascii.a2b_hex(hex)))
self.assertEqual(GEOSGeometry(wkb_w.write(p)), p if srid else p_no_srid)
self.assertEqual(
GEOSGeometry(wkb_w.write(p)), p if srid else p_no_srid
)

View File

@ -4,9 +4,8 @@
# Modified from original contribution by Aryeh Leib Taurog, which was
# released under the New BSD license.
import unittest
from django.contrib.gis.geos.mutable_list import ListMixin
from django.test import SimpleTestCase
class UserListA(ListMixin):
@ -54,7 +53,7 @@ def nextRange(length):
nextRange.start = 0
class ListMixinTest(unittest.TestCase):
class ListMixinTest(SimpleTestCase):
"""
Tests base class ListMixin by comparing a list clone which is
a ListMixin subclass with a real Python list.
@ -79,6 +78,7 @@ class ListMixinTest(unittest.TestCase):
"Slice retrieval"
pl, ul = self.lists_of_len()
for i in self.limits_plus(1):
with self.subTest(i=i):
self.assertEqual(pl[i:], ul[i:], "slice [%d:]" % (i))
self.assertEqual(pl[:i], ul[:i], "slice [:%d]" % (i))
@ -94,6 +94,7 @@ class ListMixinTest(unittest.TestCase):
self.assertEqual(pl[:i:k], ul[:i:k], "slice [:%d:%d]" % (i, k))
for k in self.step_range():
with self.subTest(k=k):
self.assertEqual(pl[::k], ul[::k], "slice [::%d]" % (k))
def test02_setslice(self):
@ -105,6 +106,7 @@ class ListMixinTest(unittest.TestCase):
pl, ul = self.lists_of_len()
for slen in range(self.limit + 1):
ssl = nextRange(slen)
with self.subTest(slen=slen):
ul[:] = ssl
pl[:] = ssl
self.assertEqual(pl, ul[:], "set slice [:]")
@ -130,13 +132,23 @@ class ListMixinTest(unittest.TestCase):
ssl = nextRange(len(ul[i:j:k]))
ul[i:j:k] = ssl
pl[i:j:k] = ssl
self.assertEqual(pl, ul[:], "set slice [%d:%d:%d]" % (i, j, k))
self.assertEqual(
pl, ul[:], "set slice [%d:%d:%d]" % (i, j, k)
)
sliceLen = len(ul[i:j:k])
with self.assertRaises(ValueError):
msg = (
f"attempt to assign sequence of size {sliceLen + 1} "
f"to extended slice of size {sliceLen}"
)
with self.assertRaisesMessage(ValueError, msg):
setfcn(ul, i, j, k, sliceLen + 1)
if sliceLen > 2:
with self.assertRaises(ValueError):
msg = (
f"attempt to assign sequence of size {sliceLen - 1}"
f" to extended slice of size {sliceLen}"
)
with self.assertRaisesMessage(ValueError, msg):
setfcn(ul, i, j, k, sliceLen - 1)
for k in self.step_range():
@ -160,6 +172,7 @@ class ListMixinTest(unittest.TestCase):
"Delete slice"
for Len in range(self.limit):
pl, ul = self.lists_of_len(Len)
with self.subTest(Len=Len):
del pl[:]
del ul[:]
self.assertEqual(pl[:], ul[:], "del slice [:]")
@ -206,18 +219,21 @@ class ListMixinTest(unittest.TestCase):
"Get/set/delete single item"
pl, ul = self.lists_of_len()
for i in self.limits_plus(0):
with self.subTest(i=i):
self.assertEqual(pl[i], ul[i], "get single item [%d]" % i)
for i in self.limits_plus(0):
pl, ul = self.lists_of_len()
pl[i] = 100
ul[i] = 100
with self.subTest(i=i):
self.assertEqual(pl[:], ul[:], "set single item [%d]" % i)
for i in self.limits_plus(0):
pl, ul = self.lists_of_len()
del pl[i]
del ul[i]
with self.subTest(i=i):
self.assertEqual(pl[:], ul[:], "del single item [%d]" % i)
def test05_out_of_range_exceptions(self):
@ -234,11 +250,13 @@ class ListMixinTest(unittest.TestCase):
pl, ul = self.lists_of_len()
for i in (-1 - self.limit, self.limit):
with self.assertRaises(IndexError): # 'set index %d' % i)
msg = f"invalid index: {i}"
with self.subTest(i=i):
with self.assertRaisesMessage(IndexError, msg):
setfcn(ul, i)
with self.assertRaises(IndexError): # 'get index %d' % i)
with self.assertRaisesMessage(IndexError, msg):
getfcn(ul, i)
with self.assertRaises(IndexError): # 'del index %d' % i)
with self.assertRaisesMessage(IndexError, msg):
delfcn(ul, i)
def test06_list_methods(self):
@ -260,10 +278,12 @@ class ListMixinTest(unittest.TestCase):
pl, ul = self.lists_of_len()
pl.insert(i, 50)
ul.insert(i, 50)
with self.subTest(i=i):
self.assertEqual(pl[:], ul[:], "insert at %d" % i)
for i in self.limits_plus(0):
pl, ul = self.lists_of_len()
with self.subTest(i=i):
self.assertEqual(pl.pop(i), ul.pop(i), "popped value at %d" % i)
self.assertEqual(pl[:], ul[:], "after pop at %d" % i)
@ -276,22 +296,25 @@ class ListMixinTest(unittest.TestCase):
def popfcn(x, i):
x.pop(i)
with self.assertRaises(IndexError):
with self.assertRaisesMessage(IndexError, "invalid index: 3"):
popfcn(ul, self.limit)
with self.assertRaises(IndexError):
with self.assertRaisesMessage(IndexError, "invalid index: -4"):
popfcn(ul, -1 - self.limit)
pl, ul = self.lists_of_len()
for val in range(self.limit):
with self.subTest(val=val):
self.assertEqual(pl.index(val), ul.index(val), "index of %d" % val)
for val in self.limits_plus(2):
with self.subTest(val=val):
self.assertEqual(pl.count(val), ul.count(val), "count %d" % val)
for val in range(self.limit):
pl, ul = self.lists_of_len()
pl.remove(val)
ul.remove(val)
with self.subTest(val=val):
self.assertEqual(pl[:], ul[:], "after remove val %d" % val)
def indexfcn(x, v):
@ -300,9 +323,10 @@ class ListMixinTest(unittest.TestCase):
def removefcn(x, v):
return x.remove(v)
with self.assertRaises(ValueError):
msg = "40 not found in object"
with self.assertRaisesMessage(ValueError, msg):
indexfcn(ul, 40)
with self.assertRaises(ValueError):
with self.assertRaisesMessage(ValueError, msg):
removefcn(ul, 40)
def test07_allowed_types(self):
@ -315,9 +339,10 @@ class ListMixinTest(unittest.TestCase):
def setfcn(x, i, v):
x[i] = v
with self.assertRaises(TypeError):
msg = "Invalid type encountered in the arguments."
with self.assertRaisesMessage(TypeError, msg):
setfcn(ul, 2, "hello")
with self.assertRaises(TypeError):
with self.assertRaisesMessage(TypeError, msg):
setfcn(ul, slice(0, 3, 2), ("hello", "goodbye"))
def test08_min_length(self):
@ -331,17 +356,20 @@ class ListMixinTest(unittest.TestCase):
def setfcn(x, i):
x[:i] = []
msg = "Must have at least 3 items"
for i in range(len(ul) - ul._minlength + 1, len(ul)):
with self.assertRaises(ValueError):
with self.subTest(i=i):
with self.assertRaisesMessage(ValueError, msg):
delfcn(ul, i)
with self.assertRaises(ValueError):
with self.assertRaisesMessage(ValueError, msg):
setfcn(ul, i)
del ul[: len(ul) - ul._minlength]
ul._maxlength = 4
for i in range(0, ul._maxlength - len(ul)):
with self.subTest(i=i):
ul.append(i)
with self.assertRaises(ValueError):
with self.assertRaisesMessage(ValueError, "Cannot have more than 4 items"):
ul.append(10)
def test09_iterable_check(self):
@ -351,13 +379,16 @@ class ListMixinTest(unittest.TestCase):
def setfcn(x, i, v):
x[i] = v
with self.assertRaises(TypeError):
with self.assertRaisesMessage(
TypeError, "can only assign an iterable to a slice"
):
setfcn(ul, slice(0, 3, 2), 2)
def test10_checkindex(self):
"Index check"
pl, ul = self.lists_of_len()
for i in self.limits_plus(0):
with self.subTest(i=i):
if i < 0:
self.assertEqual(
ul._checkindex(i), i + self.limit, "_checkindex(neg index)"
@ -366,7 +397,10 @@ class ListMixinTest(unittest.TestCase):
self.assertEqual(ul._checkindex(i), i, "_checkindex(pos index)")
for i in (-self.limit - 1, self.limit):
with self.assertRaises(IndexError):
with (
self.subTest(i=i),
self.assertRaisesMessage(IndexError, f"invalid index: {i}"),
):
ul._checkindex(i)
def test_11_sorting(self):