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

View File

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

View File

@ -25,6 +25,7 @@ class GEOSIOTest(SimpleTestCase):
g2 = wkt_r.read(wkt) g2 = wkt_r.read(wkt)
for geom in (g1, g2): for geom in (g1, g2):
with self.subTest(geom=geom):
self.assertEqual(ref, geom) self.assertEqual(ref, geom)
# Should only accept string objects. # Should only accept string objects.
@ -67,6 +68,7 @@ class GEOSIOTest(SimpleTestCase):
g2 = wkb_r.read(hex_bin) g2 = wkb_r.read(hex_bin)
g3 = wkb_r.read(hex_str) g3 = wkb_r.read(hex_str)
for geom in (g1, g2, g3): for geom in (g1, g2, g3):
with self.subTest(geom=geom):
self.assertEqual(ref, geom) self.assertEqual(ref, geom)
bad_input = (1, 5.23, None, False) bad_input = (1, 5.23, None, False)
@ -89,9 +91,13 @@ class GEOSIOTest(SimpleTestCase):
self.assertEqual(wkb1, wkb_w.write(g)) self.assertEqual(wkb1, wkb_w.write(g))
# Ensuring bad byteorders are not accepted. # 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): for bad_byteorder in (-1, 2, 523, "foo", None):
# Equivalent of `wkb_w.byteorder = bad_byteorder` # 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) wkb_w._set_byteorder(bad_byteorder)
# Setting the byteorder to 0 (for Big Endian) # Setting the byteorder to 0 (for Big Endian)
@ -114,9 +120,11 @@ class GEOSIOTest(SimpleTestCase):
wkb3d_srid = memoryview(binascii.a2b_hex(hex3d_srid)) wkb3d_srid = memoryview(binascii.a2b_hex(hex3d_srid))
# Ensuring bad output dimensions are not accepted # 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): for bad_outdim in (-1, 0, 1, 4, 423, "foo", None):
with self.assertRaisesMessage( with (
ValueError, "WKB output dimension must be 2 or 3" self.subTest(bad_outdim=bad_outdim),
self.assertRaisesMessage(ValueError, msg),
): ):
wkb_w.outdim = bad_outdim wkb_w.outdim = bad_outdim
@ -221,9 +229,12 @@ class GEOSIOTest(SimpleTestCase):
wkb_w.byteorder = byteorder wkb_w.byteorder = byteorder
for srid, hex in enumerate(hexes): for srid, hex in enumerate(hexes):
wkb_w.srid = srid wkb_w.srid = srid
with self.subTest(byteorder=byteorder, hexes=hexes):
self.assertEqual(wkb_w.write_hex(p), hex) self.assertEqual(wkb_w.write_hex(p), hex)
self.assertEqual( self.assertEqual(
GEOSGeometry(wkb_w.write_hex(p)), p if srid else p_no_srid 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(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 # Modified from original contribution by Aryeh Leib Taurog, which was
# released under the New BSD license. # released under the New BSD license.
import unittest
from django.contrib.gis.geos.mutable_list import ListMixin from django.contrib.gis.geos.mutable_list import ListMixin
from django.test import SimpleTestCase
class UserListA(ListMixin): class UserListA(ListMixin):
@ -54,7 +53,7 @@ def nextRange(length):
nextRange.start = 0 nextRange.start = 0
class ListMixinTest(unittest.TestCase): class ListMixinTest(SimpleTestCase):
""" """
Tests base class ListMixin by comparing a list clone which is Tests base class ListMixin by comparing a list clone which is
a ListMixin subclass with a real Python list. a ListMixin subclass with a real Python list.
@ -79,6 +78,7 @@ class ListMixinTest(unittest.TestCase):
"Slice retrieval" "Slice retrieval"
pl, ul = self.lists_of_len() pl, ul = self.lists_of_len()
for i in self.limits_plus(1): 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))
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)) self.assertEqual(pl[:i:k], ul[:i:k], "slice [:%d:%d]" % (i, k))
for k in self.step_range(): for k in self.step_range():
with self.subTest(k=k):
self.assertEqual(pl[::k], ul[::k], "slice [::%d]" % (k)) self.assertEqual(pl[::k], ul[::k], "slice [::%d]" % (k))
def test02_setslice(self): def test02_setslice(self):
@ -105,6 +106,7 @@ class ListMixinTest(unittest.TestCase):
pl, ul = self.lists_of_len() pl, ul = self.lists_of_len()
for slen in range(self.limit + 1): for slen in range(self.limit + 1):
ssl = nextRange(slen) ssl = nextRange(slen)
with self.subTest(slen=slen):
ul[:] = ssl ul[:] = ssl
pl[:] = ssl pl[:] = ssl
self.assertEqual(pl, ul[:], "set slice [:]") self.assertEqual(pl, ul[:], "set slice [:]")
@ -130,13 +132,23 @@ class ListMixinTest(unittest.TestCase):
ssl = nextRange(len(ul[i:j:k])) ssl = nextRange(len(ul[i:j:k]))
ul[i:j:k] = ssl ul[i:j:k] = ssl
pl[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]) 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) setfcn(ul, i, j, k, sliceLen + 1)
if sliceLen > 2: 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) setfcn(ul, i, j, k, sliceLen - 1)
for k in self.step_range(): for k in self.step_range():
@ -160,6 +172,7 @@ class ListMixinTest(unittest.TestCase):
"Delete slice" "Delete slice"
for Len in range(self.limit): for Len in range(self.limit):
pl, ul = self.lists_of_len(Len) pl, ul = self.lists_of_len(Len)
with self.subTest(Len=Len):
del pl[:] del pl[:]
del ul[:] del ul[:]
self.assertEqual(pl[:], ul[:], "del slice [:]") self.assertEqual(pl[:], ul[:], "del slice [:]")
@ -206,18 +219,21 @@ class ListMixinTest(unittest.TestCase):
"Get/set/delete single item" "Get/set/delete single item"
pl, ul = self.lists_of_len() pl, ul = self.lists_of_len()
for i in self.limits_plus(0): for i in self.limits_plus(0):
with self.subTest(i=i):
self.assertEqual(pl[i], ul[i], "get single item [%d]" % i) self.assertEqual(pl[i], ul[i], "get single item [%d]" % i)
for i in self.limits_plus(0): for i in self.limits_plus(0):
pl, ul = self.lists_of_len() pl, ul = self.lists_of_len()
pl[i] = 100 pl[i] = 100
ul[i] = 100 ul[i] = 100
with self.subTest(i=i):
self.assertEqual(pl[:], ul[:], "set single item [%d]" % i) self.assertEqual(pl[:], ul[:], "set single item [%d]" % i)
for i in self.limits_plus(0): for i in self.limits_plus(0):
pl, ul = self.lists_of_len() pl, ul = self.lists_of_len()
del pl[i] del pl[i]
del ul[i] del ul[i]
with self.subTest(i=i):
self.assertEqual(pl[:], ul[:], "del single item [%d]" % i) self.assertEqual(pl[:], ul[:], "del single item [%d]" % i)
def test05_out_of_range_exceptions(self): def test05_out_of_range_exceptions(self):
@ -234,11 +250,13 @@ class ListMixinTest(unittest.TestCase):
pl, ul = self.lists_of_len() pl, ul = self.lists_of_len()
for i in (-1 - self.limit, self.limit): 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) setfcn(ul, i)
with self.assertRaises(IndexError): # 'get index %d' % i) with self.assertRaisesMessage(IndexError, msg):
getfcn(ul, i) getfcn(ul, i)
with self.assertRaises(IndexError): # 'del index %d' % i) with self.assertRaisesMessage(IndexError, msg):
delfcn(ul, i) delfcn(ul, i)
def test06_list_methods(self): def test06_list_methods(self):
@ -260,10 +278,12 @@ class ListMixinTest(unittest.TestCase):
pl, ul = self.lists_of_len() pl, ul = self.lists_of_len()
pl.insert(i, 50) pl.insert(i, 50)
ul.insert(i, 50) ul.insert(i, 50)
with self.subTest(i=i):
self.assertEqual(pl[:], ul[:], "insert at %d" % i) self.assertEqual(pl[:], ul[:], "insert at %d" % i)
for i in self.limits_plus(0): for i in self.limits_plus(0):
pl, ul = self.lists_of_len() 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.pop(i), ul.pop(i), "popped value at %d" % i)
self.assertEqual(pl[:], ul[:], "after pop at %d" % i) self.assertEqual(pl[:], ul[:], "after pop at %d" % i)
@ -276,22 +296,25 @@ class ListMixinTest(unittest.TestCase):
def popfcn(x, i): def popfcn(x, i):
x.pop(i) x.pop(i)
with self.assertRaises(IndexError): with self.assertRaisesMessage(IndexError, "invalid index: 3"):
popfcn(ul, self.limit) popfcn(ul, self.limit)
with self.assertRaises(IndexError): with self.assertRaisesMessage(IndexError, "invalid index: -4"):
popfcn(ul, -1 - self.limit) popfcn(ul, -1 - self.limit)
pl, ul = self.lists_of_len() pl, ul = self.lists_of_len()
for val in range(self.limit): for val in range(self.limit):
with self.subTest(val=val):
self.assertEqual(pl.index(val), ul.index(val), "index of %d" % val) self.assertEqual(pl.index(val), ul.index(val), "index of %d" % val)
for val in self.limits_plus(2): for val in self.limits_plus(2):
with self.subTest(val=val):
self.assertEqual(pl.count(val), ul.count(val), "count %d" % val) self.assertEqual(pl.count(val), ul.count(val), "count %d" % val)
for val in range(self.limit): for val in range(self.limit):
pl, ul = self.lists_of_len() pl, ul = self.lists_of_len()
pl.remove(val) pl.remove(val)
ul.remove(val) ul.remove(val)
with self.subTest(val=val):
self.assertEqual(pl[:], ul[:], "after remove val %d" % val) self.assertEqual(pl[:], ul[:], "after remove val %d" % val)
def indexfcn(x, v): def indexfcn(x, v):
@ -300,9 +323,10 @@ class ListMixinTest(unittest.TestCase):
def removefcn(x, v): def removefcn(x, v):
return x.remove(v) return x.remove(v)
with self.assertRaises(ValueError): msg = "40 not found in object"
with self.assertRaisesMessage(ValueError, msg):
indexfcn(ul, 40) indexfcn(ul, 40)
with self.assertRaises(ValueError): with self.assertRaisesMessage(ValueError, msg):
removefcn(ul, 40) removefcn(ul, 40)
def test07_allowed_types(self): def test07_allowed_types(self):
@ -315,9 +339,10 @@ class ListMixinTest(unittest.TestCase):
def setfcn(x, i, v): def setfcn(x, i, v):
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") setfcn(ul, 2, "hello")
with self.assertRaises(TypeError): with self.assertRaisesMessage(TypeError, msg):
setfcn(ul, slice(0, 3, 2), ("hello", "goodbye")) setfcn(ul, slice(0, 3, 2), ("hello", "goodbye"))
def test08_min_length(self): def test08_min_length(self):
@ -331,17 +356,20 @@ class ListMixinTest(unittest.TestCase):
def setfcn(x, i): def setfcn(x, i):
x[:i] = [] x[:i] = []
msg = "Must have at least 3 items"
for i in range(len(ul) - ul._minlength + 1, len(ul)): 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) delfcn(ul, i)
with self.assertRaises(ValueError): with self.assertRaisesMessage(ValueError, msg):
setfcn(ul, i) setfcn(ul, i)
del ul[: len(ul) - ul._minlength] del ul[: len(ul) - ul._minlength]
ul._maxlength = 4 ul._maxlength = 4
for i in range(0, ul._maxlength - len(ul)): for i in range(0, ul._maxlength - len(ul)):
with self.subTest(i=i):
ul.append(i) ul.append(i)
with self.assertRaises(ValueError): with self.assertRaisesMessage(ValueError, "Cannot have more than 4 items"):
ul.append(10) ul.append(10)
def test09_iterable_check(self): def test09_iterable_check(self):
@ -351,13 +379,16 @@ class ListMixinTest(unittest.TestCase):
def setfcn(x, i, v): def setfcn(x, i, v):
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) setfcn(ul, slice(0, 3, 2), 2)
def test10_checkindex(self): def test10_checkindex(self):
"Index check" "Index check"
pl, ul = self.lists_of_len() pl, ul = self.lists_of_len()
for i in self.limits_plus(0): for i in self.limits_plus(0):
with self.subTest(i=i):
if i < 0: if i < 0:
self.assertEqual( self.assertEqual(
ul._checkindex(i), i + self.limit, "_checkindex(neg index)" 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)") self.assertEqual(ul._checkindex(i), i, "_checkindex(pos index)")
for i in (-self.limit - 1, self.limit): 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) ul._checkindex(i)
def test_11_sorting(self): def test_11_sorting(self):