11from __future__ import annotations
22
33import io
4+ import itertools
45
56import pytest
67from hypothesis import HealthCheck , given , settings
2728PointsLengths = integers (min_value = 1 , max_value = 8000 ) # length of points
2829oid = one_of (none (), integers (min_value = 0 ))
2930point_2D = builds (shp .Point , x = xs , y = ys , oid = oid )
30- pointM = builds (
31+ pointm = builds (
3132 shp .PointM ,
3233 x = xs ,
3334 y = ys ,
3435 m = ms ,
3536 oid = oid ,
3637)
37- pointZ = builds (
38+ pointz = builds (
3839 shp .PointZ ,
3940 x = xs ,
4041 y = ys ,
@@ -79,7 +80,7 @@ def test_Point_2D_roundtrips(
7980
8081
8182@pytest .mark .hypothesis
82- @given (expected = pointM , i = integers (min_value = 1 ))
83+ @given (expected = pointm , i = integers (min_value = 1 ))
8384def test_PointM_roundtrips (
8485 expected : shp .Point ,
8586 i : int ,
@@ -103,7 +104,7 @@ def test_PointM_roundtrips(
103104
104105
105106@pytest .mark .hypothesis
106- @given (expected = pointZ , i = integers (min_value = 1 ))
107+ @given (expected = pointz , i = integers (min_value = 1 ))
107108def test_PointZ_roundtrips (
108109 expected : shp .Point ,
109110 i : int ,
@@ -159,11 +160,11 @@ def multipointM_from_xyms(point_ms: tuple[float, float, float | None], oid_: int
159160 xy_vals = zip (x_vals , y_vals )
160161 return shp .MultiPointM (points = list (xy_vals ), m = list (m_vals ), oid = oid_ )
161162
162- multipointM = builds (multipointM_from_xyms , lists (tuples (xs , ys , ms ), min_size = 1 ), oid )
163+ multipointm = builds (multipointM_from_xyms , lists (tuples (xs , ys , ms ), min_size = 1 ), oid )
163164
164165@pytest .mark .hypothesis
165- @settings (suppress_health_check = [HealthCheck .too_slow , HealthCheck .data_too_large ])
166- @given (expected = multipointM , i = integers (min_value = 1 ))
166+ # @settings(suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large])
167+ @given (expected = multipointm , i = integers (min_value = 1 ))
167168def test_MultiPointM_roundtrips (
168169 expected : shp .MultiPointM ,
169170 i : int ,
@@ -195,7 +196,7 @@ def multipointZ_from_xyzms(pointz_ms: tuple[float, float, float, float | None],
195196
196197
197198@pytest .mark .hypothesis
198- @settings (suppress_health_check = [HealthCheck .too_slow , HealthCheck .data_too_large ])
199+ # @settings(suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large])
199200@given (expected = multipointz , i = integers (min_value = 1 ))
200201def test_MultiPointZ_roundtrips (
201202 expected : shp .MultiPointZ ,
@@ -247,7 +248,7 @@ def test_Polyline_roundtrips(
247248 assert actual .oid == expected .oid
248249
249250@pytest .mark .hypothesis
250- @settings (suppress_health_check = [HealthCheck .too_slow , HealthCheck .data_too_large ])
251+ # @settings(suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large])
251252@given (expected = polylinem , i = integers (min_value = 1 ))
252253def test_PolylineM_roundtrips (
253254 expected : shp .PolylineM ,
@@ -272,7 +273,7 @@ def test_PolylineM_roundtrips(
272273 assert actual .oid == expected .oid
273274
274275@pytest .mark .hypothesis
275- @settings (suppress_health_check = [HealthCheck .too_slow , HealthCheck .data_too_large ])
276+ # @settings(suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large])
276277@given (expected = polylinez , i = integers (min_value = 1 ))
277278def test_PolylineZ_roundtrips (
278279 expected : shp .PolylineZ ,
@@ -326,7 +327,7 @@ def test_Polygon_roundtrips(
326327 assert actual .oid == expected .oid
327328
328329@pytest .mark .hypothesis
329- @settings (suppress_health_check = [HealthCheck .too_slow , HealthCheck .data_too_large ])
330+ # @settings(suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large])
330331@given (expected = polygonm , i = integers (min_value = 1 ))
331332def test_PolygonM_roundtrips (
332333 expected : shp .PolygonM ,
@@ -351,7 +352,7 @@ def test_PolygonM_roundtrips(
351352 assert actual .oid == expected .oid
352353
353354@pytest .mark .hypothesis
354- @settings (suppress_health_check = [HealthCheck .too_slow , HealthCheck .data_too_large ])
355+ # @settings(suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large])
355356@given (expected = polygonz , i = integers (min_value = 1 ))
356357def test_PolygonZ_roundtrips (
357358 expected : shp .PolygonZ ,
@@ -385,20 +386,14 @@ def multipatch_from_xyzms_and_types(
385386 xyzm_vals , p_types = zip (* xyzms_and_types )
386387 return shp .MultiPatch (lines = xyzm_vals , partTypes = p_types , oid = oid )
387388
388- multipatches = builds (
389+ multipatch = builds (
389390 multipatch_from_xyzms_and_types ,
390391 lists (tuples (lists (tuples (xs , ys , zs , ms ), min_size = 1 ), part_types ), min_size = 1 ), oid )
391- # @composite
392- # def multipatches(draw):
393- # N = draw(PointsLengths)
394- # p_types = draw(lists(part_types, min_size=N, max_size=N))
395- # patches = draw(lists(lists(tuples(xs, ys, zs, ms), min_size=1), min_size=N, max_size=N))
396- # return shp.MultiPatch(lines = patches, partTypes = p_types, oid=oid)
397392
398393
399394@pytest .mark .hypothesis
400- @settings (suppress_health_check = [HealthCheck .too_slow , HealthCheck .data_too_large ])
401- @given (expected = multipatches , i = integers (min_value = 1 ))
395+ # @settings(suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large])
396+ @given (expected = multipatch , i = integers (min_value = 1 ))
402397def test_MultiPatch_roundtrips (
403398 expected : shp .MultiPatch ,
404399 i : int ,
@@ -421,4 +416,70 @@ def test_MultiPatch_roundtrips(
421416 assert actual .m == expected .m , f"{ type (actual .m )= } , { type (expected .m )= } "
422417 assert actual .z == expected .z , f"{ type (actual .z )= } , { type (expected .z )= } "
423418 assert actual .oid == expected .oid
424- assert actual .partTypes == expected .partTypes , f"{ type (actual .partTypes )= } , { type (expected .partTypes )= } "
419+ assert actual .partTypes == expected .partTypes , f"{ type (actual .partTypes )= } , { type (expected .partTypes )= } "
420+
421+
422+ shape_codes_names_and_strategies = [
423+ # (0, "Null Shape"),
424+ (1 , "Point" , point_2D ),
425+ (3 , "PolyLine" , polyline ),
426+ (5 , "Polygon" , polygon ),
427+ (8 , "MultiPoint" , multipoint ),
428+ (11 , "PointZ" , pointz ),
429+ (13 , "PolyLineZ" , polylinez ),
430+ (15 , "PolygonZ" , polygonz ),
431+ (18 , "MultiPointZ" , multipointz ),
432+ (21 , "PointM" , pointm ),
433+ (23 , "PolyLineM" , polylinem ),
434+ (25 , "PolygonM" , polygonm ),
435+ (28 , "MultiPointM" , multipointm ),
436+ (31 , "MultiPatch" , multipatch ),
437+ ]
438+
439+ def code_and_shape_strat_from_triple (t ):
440+ x , _name , shapes = t
441+ return tuples (just (x ), lists (shapes , min_size = 0 )) # Empty shp files are in the esri spec.
442+
443+ codes_and_shapes_strats = [
444+ code_and_shape_strat_from_triple (t )
445+ for t in shape_codes_names_and_strategies
446+ ]
447+
448+ codes_and_shapes = one_of (codes_and_shapes_strats )
449+
450+ @pytest .mark .hypothesis
451+ # @settings(suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large])
452+ @given (codes_and_shapes = codes_and_shapes )
453+ def test_shp_reader_writer_roundtrip (codes_and_shapes )-> None :
454+ code_ex , expected_shapes = codes_and_shapes
455+ stream = io .BytesIO ()
456+ with shp .ShpWriter (shp = stream , shapeType = code_ex ) as w :
457+ for shape in expected_shapes :
458+ w .shape (shape )
459+ stream .seek (0 )
460+ with shp .ShpReader (shp = stream ) as r :
461+ assert r .shapeType == code_ex
462+
463+ for actual , expected in itertools .zip_longest (r .shapes (), expected_shapes ):
464+
465+ assert isinstance (actual , shp .SHAPE_CLASS_FROM_SHAPETYPE [code_ex ])
466+ assert actual .points_3D == expected .points_3D
467+ # Don't assert actual.oid == expected.oid it's defined by
468+ # actual.oid indicates the order actual was written in, expected.oid
469+ # is not currently encoded (as we'd have to resort the entire Shapefile after each shape)
470+ assert actual .parts == expected .parts , f"{ type (actual .parts )= } , { type (expected .parts )= } "
471+
472+ if (m := getattr (actual , "m" , None )):
473+ assert m == expected .m , f"{ type (m )= } , { type (expected .m )= } "
474+ else :
475+ assert not hasattr (expected , "m" )
476+
477+ if (z := getattr (actual , "z" , None )):
478+ assert z == expected .z , f"{ type (z )= } , { type (expected .z )= } "
479+ else :
480+ assert not hasattr (expected , "z" )
481+
482+ if (partTypes := getattr (actual , "partTypes" , None )):
483+ assert actual .partTypes == expected .partTypes , f"{ type (actual .partTypes )= } , { type (expected .partTypes )= } "
484+ else :
485+ assert not hasattr (expected , "partTypes" )
0 commit comments