|
| 1 | +""" |
| 2 | +Pytest configuration and shared fixtures for WeatherRoutingTool tests. |
| 3 | +
|
| 4 | +This module provides reusable fixtures for test setup and teardown, |
| 5 | +replacing ad-hoc helper patterns with proper pytest fixtures. |
| 6 | +""" |
| 7 | + |
| 8 | +import pytest |
| 9 | +import geopandas as gpd |
| 10 | +import sqlalchemy as db |
| 11 | +from shapely.geometry import LineString, Point, box |
| 12 | + |
| 13 | + |
| 14 | +@pytest.fixture(scope="class") |
| 15 | +def test_nodes_gdf(): |
| 16 | + """ |
| 17 | + Fixture providing test nodes GeoDataFrame for seamark/constraint tests. |
| 18 | +
|
| 19 | + :return: GeoDataFrame with test node data |
| 20 | + :rtype: gpd.GeoDataFrame |
| 21 | + """ |
| 22 | + return gpd.GeoDataFrame( |
| 23 | + columns=["tags", "geometry"], |
| 24 | + data=[ |
| 25 | + [{"waterway": "lock_gate", "seamark:type": "gate"}, Point(5, 15)], |
| 26 | + [{"seamark:type": "harbour"}, Point(9.91950, 57.06081)], |
| 27 | + [{"seamark:type": "buoy_cardinal"}, Point(12.01631, 48.92595)], |
| 28 | + [{"seamark:type": "separation_boundary"}, Point(12.01631, 48.92595)], |
| 29 | + [{"seamark:type": "separation_crossing"}, Point(12.01631, 48.92595)], |
| 30 | + [{"seamark:type": "separation_lane"}, Point(12.01631, 48.92595)], |
| 31 | + [{"seamark:type": "separation_roundabout"}, Point(12.01631, 48.92595)], |
| 32 | + [{"seamark:type": "separation_zone"}, Point(12.01631, 48.92595)], |
| 33 | + [{"seamark:type": "restricted_area"}, Point(12.01631, 48.92595)], |
| 34 | + ], |
| 35 | + ) |
| 36 | + |
| 37 | + |
| 38 | +@pytest.fixture(scope="class") |
| 39 | +def test_ways_gdf(): |
| 40 | + """ |
| 41 | + Fixture providing test ways GeoDataFrame for seamark/constraint tests. |
| 42 | +
|
| 43 | + :return: GeoDataFrame with test ways data |
| 44 | + :rtype: gpd.GeoDataFrame |
| 45 | + """ |
| 46 | + return gpd.GeoDataFrame( |
| 47 | + columns=["tags", "geometry"], |
| 48 | + data=[ |
| 49 | + [ |
| 50 | + {"seamark:type": "separation_boundary"}, |
| 51 | + LineString([(3.2738333, 51.8765), (3.154833, 51.853667)]), |
| 52 | + ], |
| 53 | + [ |
| 54 | + {"seamark:type": "separation_crossing"}, |
| 55 | + LineString( |
| 56 | + [ |
| 57 | + (6.3732417, 54.192091), |
| 58 | + (6.3593333, 54.1919199), |
| 59 | + (6.3310833, 54.1905871), |
| 60 | + (6.3182992, 54.189601), |
| 61 | + ] |
| 62 | + ), |
| 63 | + ], |
| 64 | + [ |
| 65 | + {"seamark:type": "separation_lane"}, |
| 66 | + LineString([(24.6575999, 59.6085725), (24.7026512, 59.5505585)]), |
| 67 | + ], |
| 68 | + [ |
| 69 | + {"seamark:type": "separation_roundabout"}, |
| 70 | + LineString( |
| 71 | + [ |
| 72 | + (27.9974563, 43.186327), |
| 73 | + (27.998524, 43.1864565), |
| 74 | + (27.9995173, 43.186412), |
| 75 | + (28.0012373, 43.1859232), |
| 76 | + (28.0020059, 43.1854689), |
| 77 | + (28.0025203, 43.1850186), |
| 78 | + (28.0029253, 43.1845006), |
| 79 | + (28.0032216, 43.1838693), |
| 80 | + (27.9947856, 43.1813859), |
| 81 | + (27.9944414, 43.1819034), |
| 82 | + (27.9941705, 43.1826993), |
| 83 | + (27.9941723, 43.1835194), |
| 84 | + (27.9944142, 43.1842511), |
| 85 | + (27.9947709, 43.1848037), |
| 86 | + (27.9953623, 43.1853841), |
| 87 | + (27.9961109, 43.1858589), |
| 88 | + (27.9974563, 43.186327), |
| 89 | + ] |
| 90 | + ), |
| 91 | + ], |
| 92 | + [ |
| 93 | + {"seamark:type": "separation_zone"}, |
| 94 | + LineString( |
| 95 | + [ |
| 96 | + (-1.9830398, 49.2927514), |
| 97 | + (-1.9830233, 49.2925889), |
| 98 | + (-1.9828257, 49.2924815), |
| 99 | + (-1.9827145, 49.2925089), |
| 100 | + (-1.9828543, 49.2927771), |
| 101 | + (-1.9830398, 49.2927514), |
| 102 | + ] |
| 103 | + ), |
| 104 | + ], |
| 105 | + [ |
| 106 | + {"seamark:type": "restricted_area"}, |
| 107 | + LineString( |
| 108 | + [ |
| 109 | + (12.3569916, 47.9186626), |
| 110 | + (12.3567217, 47.9188108), |
| 111 | + (12.3564934, 47.9189565), |
| 112 | + (12.3564734, 47.9191199), |
| 113 | + (12.3565413, 47.9192803), |
| 114 | + (12.3568636, 47.919524), |
| 115 | + (12.3571719, 47.9196858), |
| 116 | + (12.3575482, 47.9197593), |
| 117 | + (12.3579399, 47.9198024), |
| 118 | + (12.3587152, 47.9200541), |
| 119 | + (12.3594448, 47.9203064), |
| 120 | + (12.3596907, 47.9203917), |
| 121 | + (12.3599993, 47.9204654), |
| 122 | + (12.3604107, 47.9205391), |
| 123 | + (12.3608174, 47.9205554), |
| 124 | + (12.3610279, 47.9205224), |
| 125 | + (12.3612053, 47.9204511), |
| 126 | + (12.3614394, 47.9201326), |
| 127 | + (12.3616484, 47.9198195), |
| 128 | + (12.3616249, 47.9196335), |
| 129 | + (12.361631, 47.9194503), |
| 130 | + (12.3616174, 47.9193071), |
| 131 | + (12.36156, 47.9192435), |
| 132 | + (12.3614394, 47.9191936), |
| 133 | + (12.3611173, 47.9191633), |
| 134 | + (12.3609535, 47.9190676), |
| 135 | + (12.3607335, 47.9189749), |
| 136 | + (12.3604259, 47.918891), |
| 137 | + (12.3595763, 47.9187613), |
| 138 | + (12.3587674, 47.9185358), |
| 139 | + (12.3584371, 47.9183784), |
| 140 | + (12.3582044, 47.9182997), |
| 141 | + (12.3579056, 47.918306), |
| 142 | + (12.3576587, 47.9183381), |
| 143 | + (12.3573105, 47.9184692), |
| 144 | + (12.3569916, 47.9186626), |
| 145 | + ] |
| 146 | + ), |
| 147 | + ], |
| 148 | + ], |
| 149 | + ) |
| 150 | + |
| 151 | + |
| 152 | +@pytest.fixture(scope="class") |
| 153 | +def test_land_polygons_gdf(): |
| 154 | + """ |
| 155 | + Fixture providing test land polygons GeoDataFrame for constraint tests. |
| 156 | +
|
| 157 | + :return: GeoDataFrame with test land polygon data |
| 158 | + :rtype: gpd.GeoDataFrame |
| 159 | + """ |
| 160 | + return gpd.GeoDataFrame( |
| 161 | + geometry=[box(4.056342603541809, 49.06378892560051, 8.748316591073674, 51.19862259935186)] |
| 162 | + ) |
| 163 | + |
| 164 | + |
| 165 | +@pytest.fixture(scope="class") |
| 166 | +def continuous_check_database(tmp_path_factory, test_nodes_gdf, test_ways_gdf, test_land_polygons_gdf): |
| 167 | + """ |
| 168 | + Fixture providing a temporary SQLite database for ContinuousCheck tests. |
| 169 | +
|
| 170 | + Creates database in temporary directory with nodes, ways, and land_polygons layers. |
| 171 | + Database is automatically cleaned up after test class completes. |
| 172 | +
|
| 173 | + :param tmp_path_factory: pytest fixture for creating temporary directories |
| 174 | + :param test_nodes_gdf: GeoDataFrame with test nodes |
| 175 | + :type test_nodes_gdf: gpd.GeoDataFrame |
| 176 | + :param test_ways_gdf: GeoDataFrame with test ways |
| 177 | + :type test_ways_gdf: gpd.GeoDataFrame |
| 178 | + :param test_land_polygons_gdf: GeoDataFrame with test land polygons |
| 179 | + :type test_land_polygons_gdf: gpd.GeoDataFrame |
| 180 | + :yields: Database engine connected to temporary database |
| 181 | + :rtype: sqlalchemy.engine.Engine |
| 182 | + """ |
| 183 | + # Create temporary directory for this test class |
| 184 | + tmp_dir = tmp_path_factory.mktemp("continuous_check_db") |
| 185 | + db_path = tmp_dir / "gdfDB.sqlite" |
| 186 | + |
| 187 | + # Create database engine |
| 188 | + engine = db.create_engine(f"sqlite:///{db_path}") |
| 189 | + |
| 190 | + # Write GeoDataFrames to database |
| 191 | + test_nodes_gdf.to_file(str(db_path), driver="SQLite", layer="nodes", overwrite=True) |
| 192 | + test_ways_gdf.to_file(str(db_path), driver="SQLite", layer="ways", overwrite=True) |
| 193 | + test_land_polygons_gdf.to_file(str(db_path), driver="SQLite", layer="land_polygons", overwrite=True) |
| 194 | + |
| 195 | + yield engine |
| 196 | + |
| 197 | + # Cleanup (engine disposal) |
| 198 | + engine.dispose() |
| 199 | + # tmp_path_factory automatically cleans up the temporary directory |
| 200 | + |
| 201 | + |
| 202 | +@pytest.fixture(scope="class") |
| 203 | +def test_seamark_gdf(): |
| 204 | + """ |
| 205 | + Fixture providing test seamark GeoDataFrame for route postprocessing tests. |
| 206 | +
|
| 207 | + :return: GeoDataFrame with test seamark data |
| 208 | + :rtype: gpd.GeoDataFrame |
| 209 | + """ |
| 210 | + return gpd.GeoDataFrame( |
| 211 | + columns=["tags", "geometry"], |
| 212 | + data=[ |
| 213 | + [ |
| 214 | + {"seamark:type": "separation_boundary"}, |
| 215 | + LineString([(12, 0), (11, 3)]), |
| 216 | + ], |
| 217 | + [ |
| 218 | + {"seamark:type": "separation_boundary"}, |
| 219 | + LineString([(11, 3), (5, 9)]), |
| 220 | + ], |
| 221 | + [ |
| 222 | + {"seamark:type": "separation_boundary"}, |
| 223 | + LineString([(5, 9), (0, 6)]), |
| 224 | + ], |
| 225 | + [ |
| 226 | + {"seamark:type": "separation_line"}, |
| 227 | + LineString([(11, 3), (8, 6), (5, 9)]), |
| 228 | + ], |
| 229 | + [ |
| 230 | + {"seamark:type": "separation_line"}, |
| 231 | + LineString([(17, 7), (14, 10), (10, 14)]), |
| 232 | + ], |
| 233 | + [ |
| 234 | + {"seamark:type": "separation_zone"}, |
| 235 | + LineString([(15, 6), (9, 12), (7, 10), (13, 4), (15, 6)]), |
| 236 | + ], |
| 237 | + [ |
| 238 | + {"seamark:type": "separation_lane"}, |
| 239 | + LineString([(6, 10), (9, 7), (12, 4)]), |
| 240 | + ], |
| 241 | + [ |
| 242 | + {"seamark:type": "separation_lane"}, |
| 243 | + LineString([(16, 6), (13, 9), (9, 13)]), |
| 244 | + ], |
| 245 | + ], |
| 246 | + ) |
| 247 | + |
| 248 | + |
| 249 | +@pytest.fixture(scope="class") |
| 250 | +def route_postprocessing_database(tmp_path_factory, test_seamark_gdf): |
| 251 | + """ |
| 252 | + Fixture providing a temporary SQLite database for RoutePostprocessing tests. |
| 253 | +
|
| 254 | + Creates database in temporary directory with seamark layer. |
| 255 | + Database is automatically cleaned up after test class completes. |
| 256 | +
|
| 257 | + :param tmp_path_factory: pytest fixture for creating temporary directories |
| 258 | + :param test_seamark_gdf: GeoDataFrame with test seamark data |
| 259 | + :type test_seamark_gdf: gpd.GeoDataFrame |
| 260 | + :yields: Database engine connected to temporary database |
| 261 | + :rtype: sqlalchemy.engine.Engine |
| 262 | + """ |
| 263 | + # Create temporary directory for this test class |
| 264 | + tmp_dir = tmp_path_factory.mktemp("route_postprocessing_db") |
| 265 | + db_path = tmp_dir / "gdfDB.sqlite" |
| 266 | + |
| 267 | + # Create database engine |
| 268 | + engine = db.create_engine(f"sqlite:///{db_path}") |
| 269 | + |
| 270 | + # Write GeoDataFrame to database |
| 271 | + test_seamark_gdf.to_file(str(db_path), driver="SQLite", layer="seamark", overwrite=True) |
| 272 | + |
| 273 | + yield engine |
| 274 | + |
| 275 | + # Cleanup (engine disposal) |
| 276 | + engine.dispose() |
| 277 | + # tmp_path_factory automatically cleans up the temporary directory |
0 commit comments