Skip to content

Commit bd3ced3

Browse files
fix tests
1 parent 9762f4e commit bd3ced3

File tree

1 file changed

+102
-138
lines changed

1 file changed

+102
-138
lines changed

tests/unit/simulation/test_monte_carlo_plots_background.py

Lines changed: 102 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
# pylint: disable=unused-argument,assignment-from-no-return
22
import os
3-
import urllib.error # pylint: disable=unused-import
3+
import urllib.error
44
from unittest.mock import MagicMock, patch
55

66
import matplotlib.pyplot as plt
7+
import numpy as np
78
import pytest
8-
from PIL import UnidentifiedImageError # pylint: disable=unused-import
9+
from PIL import UnidentifiedImageError
910

1011
from rocketpy.plots.monte_carlo_plots import _MonteCarloPlots
1112
from rocketpy.simulation import MonteCarlo
13+
from rocketpy.tools import import_optional_dependency
1214

1315
plt.rcParams.update({"figure.max_open_warning": 0})
1416

@@ -66,100 +68,53 @@ def __init__(self, latitude=32.990254, longitude=-106.974998):
6668
self.longitude = longitude
6769

6870

71+
@pytest.mark.parametrize(
72+
"background_type",
73+
[None, "satellite", "street", "terrain", "CartoDB.Positron"],
74+
)
6975
@patch("matplotlib.pyplot.show")
70-
def test_ellipses_background_none(mock_show):
71-
"""Test default behavior when background=None (no background map displayed).
72-
73-
Parameters
74-
----------
75-
mock_show :
76-
Mocks the matplotlib.pyplot.show() function to avoid showing the plots.
77-
"""
78-
mock_monte_carlo = MockMonteCarlo(environment=SimpleEnvironment())
79-
# Test that background=None does not raise an error
80-
result = mock_monte_carlo.plots.ellipses(background=None)
81-
assert result is None
76+
def test_ellipses_background_types_display_successfully(mock_show, background_type):
77+
"""Test that different background map types display without errors.
8278
83-
84-
@patch("matplotlib.pyplot.show")
85-
def test_ellipses_background_satellite(mock_show):
86-
"""Test using satellite map when background="satellite".
79+
This parameterized test verifies that the ellipses method works with:
80+
- None (no background map)
81+
- "satellite" (Esri.WorldImagery)
82+
- "street" (OpenStreetMap.Mapnik)
83+
- "terrain" (Esri.WorldTopoMap)
84+
- Custom provider (e.g., CartoDB.Positron)
8785
8886
Parameters
8987
----------
90-
mock_show :
91-
Mocks the matplotlib.pyplot.show() function to avoid showing the plots.
88+
mock_show : unittest.mock.MagicMock
89+
Mocks the matplotlib.pyplot.show() function to avoid displaying plots.
90+
background_type : str or None
91+
The background map type to test.
9292
"""
9393
mock_monte_carlo = MockMonteCarlo(environment=SimpleEnvironment())
94-
# Test that background="satellite" does not raise an error
95-
result = mock_monte_carlo.plots.ellipses(background="satellite")
96-
assert result is None
97-
9894

99-
@patch("matplotlib.pyplot.show")
100-
def test_ellipses_background_street(mock_show):
101-
"""Test using street map when background="street".
95+
result = mock_monte_carlo.plots.ellipses(background=background_type)
10296

103-
Parameters
104-
----------
105-
mock_show :
106-
Mocks the matplotlib.pyplot.show() function to avoid showing the plots.
107-
"""
108-
mock_monte_carlo = MockMonteCarlo(environment=SimpleEnvironment())
109-
# Test that background="street" does not raise an error
110-
result = mock_monte_carlo.plots.ellipses(background="street")
11197
assert result is None
11298

11399

114100
@patch("matplotlib.pyplot.show")
115-
def test_ellipses_background_terrain(mock_show):
116-
"""Test using terrain map when background="terrain".
117-
118-
Parameters
119-
----------
120-
mock_show :
121-
Mocks the matplotlib.pyplot.show() function to avoid showing the plots.
122-
"""
123-
mock_monte_carlo = MockMonteCarlo(environment=SimpleEnvironment())
124-
# Test that background="terrain" does not raise an error
125-
result = mock_monte_carlo.plots.ellipses(background="terrain")
126-
assert result is None
127-
101+
def test_ellipses_image_takes_precedence_over_background(mock_show, tmp_path):
102+
"""Test that image parameter takes precedence over background parameter.
128103
129-
@patch("matplotlib.pyplot.show")
130-
def test_ellipses_background_custom_provider(mock_show):
131-
"""Test using custom contextily provider for background.
104+
When both image and background are provided, the image should be used
105+
and the background map should not be downloaded.
132106
133107
Parameters
134108
----------
135-
mock_show :
136-
Mocks the matplotlib.pyplot.show() function to avoid showing the plots.
109+
mock_show : unittest.mock.MagicMock
110+
Mocks the matplotlib.pyplot.show() function to avoid displaying plots.
111+
tmp_path : pathlib.Path
112+
Pytest fixture providing a temporary directory.
137113
"""
138-
mock_monte_carlo = MockMonteCarlo(environment=SimpleEnvironment())
139-
# Test that custom provider does not raise an error
140-
result = mock_monte_carlo.plots.ellipses(background="CartoDB.Positron")
141-
assert result is None
142-
143-
144-
@patch("matplotlib.pyplot.show")
145-
def test_ellipses_image_takes_precedence_over_background(mock_show, tmp_path):
146-
"""Test that image takes precedence when both image and background are provided.
147114

148-
Parameters
149-
----------
150-
mock_show :
151-
Mocks the matplotlib.pyplot.show() function to avoid showing the plots.
152-
tmp_path :
153-
pytest fixture providing a temporary directory.
154-
"""
155115
mock_monte_carlo = MockMonteCarlo(environment=SimpleEnvironment())
156116
dummy_image_path = tmp_path / "dummy_image.png"
157117
dummy_image_path.write_bytes(b"dummy")
158-
159-
# Test that when both image and background are provided, image takes precedence
160-
# This should not attempt to download background map
161-
import numpy as np # pylint: disable=import-outside-toplevel
162-
163118
mock_image = np.zeros((100, 100, 3), dtype=np.uint8) # RGB image
164119

165120
with patch("imageio.imread") as mock_imread:
@@ -172,64 +127,67 @@ def test_ellipses_image_takes_precedence_over_background(mock_show, tmp_path):
172127

173128

174129
@patch("matplotlib.pyplot.show")
175-
def test_ellipses_background_no_environment(mock_show):
176-
"""Test that ValueError is raised when MonteCarlo object has no environment attribute.
130+
def test_ellipses_background_raises_error_when_no_environment(mock_show):
131+
"""Test that ValueError is raised when environment attribute is missing.
177132
178-
This test creates a MonteCarlo object without an environment attribute.
179-
The function should raise ValueError when trying to fetch background map.
133+
Parameters
134+
----------
135+
mock_show : unittest.mock.MagicMock
136+
Mocks the matplotlib.pyplot.show() function to avoid displaying plots.
180137
"""
138+
181139
mock_monte_carlo = MockMonteCarlo(environment=None)
182140

183141
with pytest.raises(ValueError) as exc_info:
184142
mock_monte_carlo.plots.ellipses(background="satellite")
185-
assert "environment" in str(exc_info.value).lower()
186-
assert "automatically fetching the background map" in str(exc_info.value)
143+
144+
error_message = str(exc_info.value).lower()
145+
assert "environment" in error_message
146+
assert "automatically fetching the background map" in error_message
187147

188148

189149
@patch("matplotlib.pyplot.show")
190-
def test_ellipses_background_no_latitude_longitude(mock_show):
191-
"""Test that ValueError is raised when environment has no latitude or longitude attributes.
150+
def test_ellipses_background_raises_error_when_missing_coordinates(mock_show):
151+
"""Test that ValueError is raised when environment lacks latitude or longitude.
192152
193-
This test creates a mock environment without latitude and longitude attributes.
194-
The function should raise ValueError when trying to fetch background map.
153+
Parameters
154+
----------
155+
mock_show : unittest.mock.MagicMock
156+
Mocks the matplotlib.pyplot.show() function to avoid displaying plots.
195157
"""
196158

197-
# Create a simple environment object without latitude and longitude
198159
class EmptyEnvironment:
199160
"""Empty environment object without latitude and longitude attributes."""
200161

201-
def __init__(self):
202-
pass
203-
204162
mock_environment = EmptyEnvironment()
205163
mock_monte_carlo = MockMonteCarlo(environment=mock_environment)
206164

207165
with pytest.raises(ValueError) as exc_info:
208166
mock_monte_carlo.plots.ellipses(background="satellite")
209-
assert "latitude" in str(exc_info.value).lower()
210-
assert "longitude" in str(exc_info.value).lower()
211-
assert "automatically fetching the background map" in str(exc_info.value)
167+
168+
error_message = str(exc_info.value).lower()
169+
assert "latitude" in error_message
170+
assert "longitude" in error_message
171+
assert "automatically fetching the background map" in error_message
212172

213173

214174
@patch("matplotlib.pyplot.show")
215-
def test_ellipses_background_contextily_not_installed(mock_show):
175+
def test_ellipses_background_raises_error_when_contextily_not_installed(mock_show):
216176
"""Test that ImportError is raised when contextily is not installed.
217177
218178
Parameters
219179
----------
220-
mock_show :
221-
Mocks the matplotlib.pyplot.show() function to avoid showing the plots.
180+
mock_show : unittest.mock.MagicMock
181+
Mocks the matplotlib.pyplot.show() function to avoid displaying plots.
222182
"""
183+
223184
mock_monte_carlo = MockMonteCarlo(environment=SimpleEnvironment())
224-
from rocketpy.tools import (
225-
import_optional_dependency as original_import, # pylint: disable=import-outside-toplevel
226-
)
227185

228-
# Create a mock function that only raises exception when importing contextily
229186
def mock_import_optional_dependency(name):
187+
"""Mock function that raises ImportError for contextily."""
230188
if name == "contextily":
231189
raise ImportError("No module named 'contextily'")
232-
return original_import(name)
190+
return import_optional_dependency(name)
233191

234192
with patch(
235193
"rocketpy.plots.monte_carlo_plots.import_optional_dependency",
@@ -241,70 +199,73 @@ def mock_import_optional_dependency(name):
241199

242200

243201
@patch("matplotlib.pyplot.show")
244-
def test_ellipses_background_with_custom_xlim_ylim(mock_show):
245-
"""Test using background with custom xlim and ylim.
202+
def test_ellipses_background_works_with_custom_limits(mock_show):
203+
"""Test that background maps work with custom axis limits.
246204
247205
Parameters
248206
----------
249-
mock_show :
250-
Mocks the matplotlib.pyplot.show() function to avoid showing the plots.
207+
mock_show : unittest.mock.MagicMock
208+
Mocks the matplotlib.pyplot.show() function to avoid displaying plots.
251209
"""
210+
252211
mock_monte_carlo = MockMonteCarlo(environment=SimpleEnvironment())
253-
# Test using custom xlim and ylim
212+
254213
result = mock_monte_carlo.plots.ellipses(
255214
background="satellite",
256215
xlim=(-5000, 5000),
257216
ylim=(-5000, 5000),
258217
)
218+
259219
assert result is None
260220

261221

262222
@patch("matplotlib.pyplot.show")
263-
def test_ellipses_background_save(mock_show):
264-
"""Test using background with save=True.
223+
def test_ellipses_background_saves_file_successfully(mock_show):
224+
"""Test that plots with background maps can be saved to file.
265225
266226
Parameters
267227
----------
268-
mock_show :
269-
Mocks the matplotlib.pyplot.show() function to avoid showing the plots.
228+
mock_show : unittest.mock.MagicMock
229+
Mocks the matplotlib.pyplot.show() function to avoid displaying plots.
270230
"""
231+
271232
filename = "monte_carlo_test.png"
233+
mock_monte_carlo = MockMonteCarlo(
234+
environment=SimpleEnvironment(), filename="monte_carlo_test"
235+
)
236+
272237
try:
273-
mock_monte_carlo = MockMonteCarlo(
274-
environment=SimpleEnvironment(), filename="monte_carlo_test"
275-
)
276-
# Test save functionality
277238
result = mock_monte_carlo.plots.ellipses(background="satellite", save=True)
278239
assert result is None
279-
# Verify file was created
280240
assert os.path.exists(filename)
281241
finally:
282242
if os.path.exists(filename):
283243
os.remove(filename)
284244

285245

286246
@patch("matplotlib.pyplot.show")
287-
def test_ellipses_background_invalid_provider(mock_show):
288-
"""Test that ValueError is raised when an invalid map provider is specified.
247+
def test_ellipses_background_raises_error_for_invalid_provider(mock_show):
248+
"""Test that ValueError is raised for invalid map provider names.
289249
290250
Parameters
291251
----------
292-
mock_show :
293-
Mocks the matplotlib.pyplot.show() function to avoid showing the plots.
252+
mock_show : unittest.mock.MagicMock
253+
Mocks the matplotlib.pyplot.show() function to avoid displaying plots.
294254
"""
255+
295256
mock_monte_carlo = MockMonteCarlo(environment=SimpleEnvironment())
257+
invalid_provider = "Invalid.Provider.Name"
258+
296259
with pytest.raises(ValueError) as exc_info:
297-
mock_monte_carlo.plots.ellipses(background="Invalid.Provider.Name")
298-
assert "Invalid map provider" in str(exc_info.value)
299-
assert "Invalid.Provider.Name" in str(exc_info.value)
300-
assert (
301-
"satellite" in str(exc_info.value)
302-
or "street" in str(exc_info.value)
303-
or "terrain" in str(exc_info.value)
304-
)
260+
mock_monte_carlo.plots.ellipses(background=invalid_provider)
261+
262+
error_message = str(exc_info.value)
263+
assert "Invalid map provider" in error_message
264+
assert invalid_provider in error_message
265+
# Check that error message includes built-in options
266+
assert any(option in error_message for option in ["satellite", "street", "terrain"])
305267

306268

307-
@patch("matplotlib.pyplot.show")
308269
@pytest.mark.parametrize(
309270
"exception_factory,expected_exception,expected_messages",
310271
[
@@ -373,45 +334,48 @@ def test_ellipses_background_invalid_provider(mock_show):
373334
),
374335
],
375336
)
376-
def test_ellipses_background_bounds2img_failure(
337+
@patch("matplotlib.pyplot.show")
338+
def test_ellipses_background_handles_bounds2img_failures(
377339
mock_show, exception_factory, expected_exception, expected_messages
378340
):
379341
"""Test that appropriate exceptions are raised when bounds2img fails.
380342
381-
This is a parameterized test that covers all exception types handled in
382-
the _fetch_background_map method:
383-
- ValueError: invalid coordinates or zoom level
384-
- ConnectionError: network errors (URLError, HTTPError, TimeoutError)
385-
- RuntimeError: UnidentifiedImageError (invalid image data)
386-
- RuntimeError: other unexpected exceptions
343+
This parameterized test verifies error handling for all exception types
344+
that can occur during background map fetching:
345+
- ValueError: Invalid coordinates or zoom level
346+
- ConnectionError: Network errors (URLError, HTTPError, TimeoutError)
347+
- RuntimeError: Invalid image data (UnidentifiedImageError)
348+
- RuntimeError: Other unexpected exceptions
387349
388350
Parameters
389351
----------
390-
mock_show :
391-
Mocks the matplotlib.pyplot.show() function to avoid showing the plots.
352+
mock_show : unittest.mock.MagicMock
353+
Mocks the matplotlib.pyplot.show() function to avoid displaying plots.
392354
exception_factory : callable
393355
A function that returns the exception to raise in mock_bounds2img.
394356
expected_exception : type
395357
The expected exception type to be raised.
396-
expected_messages : list[str]
358+
expected_messages : list of str
397359
List of expected message substrings in the raised exception.
398360
"""
399-
mock_monte_carlo = MockMonteCarlo(environment=SimpleEnvironment())
400361

362+
mock_monte_carlo = MockMonteCarlo(environment=SimpleEnvironment())
401363
contextily = pytest.importorskip("contextily")
402364

403365
mock_contextily = MagicMock()
404366
mock_contextily.providers = contextily.providers
405367

406368
def mock_bounds2img(*args, **kwargs):
369+
"""Mock bounds2img that raises the specified exception."""
407370
raise exception_factory()
408371

409372
mock_contextily.bounds2img = mock_bounds2img
410373

411374
def mock_import_optional_dependency(name):
375+
"""Mock import function that returns mock contextily."""
412376
if name == "contextily":
413377
return mock_contextily
414-
return original_import(name)
378+
return import_optional_dependency(name)
415379

416380
with patch(
417381
"rocketpy.plots.monte_carlo_plots.import_optional_dependency",

0 commit comments

Comments
 (0)