From b7d39bf753169cff5eebdd52a6322ad878d52095 Mon Sep 17 00:00:00 2001 From: Jules Dejaeghere Date: Tue, 2 Jan 2024 21:45:37 +0100 Subject: [PATCH] Add tests for new SVG camera and remove old ones --- custom_components/irm_kmi/camera.py | 1 - custom_components/irm_kmi/data.py | 1 - custom_components/irm_kmi/rain_graph.py | 4 +- tests/test_coordinator.py | 71 ------ tests/test_rain_graph.py | 278 ++++++++++++++++++++++++ 5 files changed, 279 insertions(+), 76 deletions(-) create mode 100644 tests/test_rain_graph.py diff --git a/custom_components/irm_kmi/camera.py b/custom_components/irm_kmi/camera.py index de8bc7f..09e26c7 100644 --- a/custom_components/irm_kmi/camera.py +++ b/custom_components/irm_kmi/camera.py @@ -54,7 +54,6 @@ def camera_image(self, width: int | None = None, height: int | None = None) -> bytes | None: """Return still image to be used as thumbnail.""" - # TODO make it a still image to avoid cuts in playback on the dashboard return self.coordinator.data.get('animation', {}).get('svg_still') async def async_camera_image( diff --git a/custom_components/irm_kmi/data.py b/custom_components/irm_kmi/data.py index 0ef88ca..0763e54 100644 --- a/custom_components/irm_kmi/data.py +++ b/custom_components/irm_kmi/data.py @@ -24,7 +24,6 @@ class CurrentWeatherData(TypedDict, total=False): pressure: float | None -# TODO cleanup useless fields class AnimationFrameData(TypedDict, total=False): """Holds one single frame of the radar camera, along with the timestamp of the frame""" time: datetime | None diff --git a/custom_components/irm_kmi/rain_graph.py b/custom_components/irm_kmi/rain_graph.py index 5d187da..28fac81 100644 --- a/custom_components/irm_kmi/rain_graph.py +++ b/custom_components/irm_kmi/rain_graph.py @@ -83,8 +83,6 @@ def __init__(self, self.draw_location() self._dwg_still = self._dwg - self._dwg_animated.saveas("animated_rain.svg") - def draw_svg_frame(self): """Create the global area to draw the other items""" self._dwg.embed_font(name="Roboto Medium", filename='custom_components/irm_kmi/resources/roboto_medium.ttf') @@ -330,4 +328,4 @@ def draw_location(self): self._dwg.add(image) def get_dwg(self): - return copy.deepcopy(self._dwg) \ No newline at end of file + return copy.deepcopy(self._dwg) diff --git a/tests/test_coordinator.py b/tests/test_coordinator.py index d185366..ed4b582 100644 --- a/tests/test_coordinator.py +++ b/tests/test_coordinator.py @@ -107,74 +107,3 @@ def test_hourly_forecast() -> None: ) assert result[8] == expected - - -@freeze_time(datetime.fromisoformat("2023-12-28T15:30:00+01:00")) -@pytest.mark.skip(reason="Outdated test, cannot be tested this way with the new camera features") -async def test_get_image_nl( - hass: HomeAssistant, - mock_image_irm_kmi_api: AsyncMock, - mock_config_entry: MockConfigEntry) -> None: - api_data = get_api_data("forecast_nl.json") - coordinator = IrmKmiCoordinator(hass, mock_config_entry) - - result = await coordinator._async_animation_data(api_data) - - # Construct the expected image for the most recent one - tz = pytz.timezone(hass.config.time_zone) - background = Image.open("custom_components/irm_kmi/resources/nl.png").convert('RGBA') - layer = Image.open("tests/fixtures/clouds_nl.png").convert('RGBA') - localisation = Image.open("tests/fixtures/loc_layer_nl.png").convert('RGBA') - temp = Image.alpha_composite(background, layer) - expected = Image.alpha_composite(temp, localisation) - draw = ImageDraw.Draw(expected) - font = ImageFont.truetype("custom_components/irm_kmi/resources/roboto_medium.ttf", 16) - time_image = (datetime.fromisoformat("2023-12-28T14:25:00+00:00") - .astimezone(tz=tz)) - time_str = time_image.isoformat(sep=' ', timespec='minutes') - draw.text((4, 4), time_str, (0, 0, 0), font=font) - - result_image = Image.open(BytesIO(result['sequence'][-1]['image'])).convert('RGBA') - - assert list(result_image.getdata()) == list(expected.getdata()) - most_recent_image = result['sequence'][result['most_recent_image_idx']]['image'] - thumb_image = Image.open(BytesIO(most_recent_image)).convert('RGBA') - assert list(thumb_image.getdata()) == list(expected.getdata()) - - assert result['hint'] == "No rain forecasted shortly" - - -@freeze_time(datetime.fromisoformat("2023-12-26T18:31:00+01:00")) -@pytest.mark.skip(reason="Outdated test, cannot be tested this way with the new camera features") -async def test_get_image_be( - hass: HomeAssistant, - mock_image_irm_kmi_api: AsyncMock, - mock_config_entry: MockConfigEntry -) -> None: - api_data = get_api_data("forecast.json") - coordinator = IrmKmiCoordinator(hass, mock_config_entry) - - result = await coordinator._async_animation_data(api_data) - - # Construct the expected image for the most recent one - tz = pytz.timezone(hass.config.time_zone) - background = Image.open("custom_components/irm_kmi/resources/be_black.png").convert('RGBA') - layer = Image.open("tests/fixtures/clouds_be.png").convert('RGBA') - localisation = Image.open("tests/fixtures/loc_layer_be_n.png").convert('RGBA') - temp = Image.alpha_composite(background, layer) - expected = Image.alpha_composite(temp, localisation) - draw = ImageDraw.Draw(expected) - font = ImageFont.truetype("custom_components/irm_kmi/resources/roboto_medium.ttf", 16) - time_image = (datetime.fromisoformat("2023-12-26T18:30:00+01:00") - .astimezone(tz=tz)) - time_str = time_image.isoformat(sep=' ', timespec='minutes') - draw.text((4, 4), time_str, (255, 255, 255), font=font) - - result_image = Image.open(BytesIO(result['sequence'][9]['image'])).convert('RGBA') - - assert list(result_image.getdata()) == list(expected.getdata()) - most_recent_image = result['sequence'][result['most_recent_image_idx']]['image'] - thumb_image = Image.open(BytesIO(most_recent_image)).convert('RGBA') - assert list(thumb_image.getdata()) == list(expected.getdata()) - - assert result['hint'] == "No rain forecasted shortly" diff --git a/tests/test_rain_graph.py b/tests/test_rain_graph.py new file mode 100644 index 0000000..21c024a --- /dev/null +++ b/tests/test_rain_graph.py @@ -0,0 +1,278 @@ +import base64 +from datetime import datetime, timedelta + +from custom_components.irm_kmi.data import (AnimationFrameData, + RadarAnimationData) +from custom_components.irm_kmi.rain_graph import RainGraph + + +def get_radar_animation_data() -> RadarAnimationData: + with open("tests/fixtures/clouds_be.png", "rb") as file: + image_data = file.read() + with open("tests/fixtures/loc_layer_be_n.png", "rb") as file: + location = file.read() + + sequence = [ + AnimationFrameData( + time=datetime.fromisoformat("2023-12-26T18:30:00+00:00") + timedelta(minutes=10 * i), + image=image_data, + value=2, + position=.5, + position_lower=.4, + position_higher=.6 + ) + for i in range(10) + ] + + return RadarAnimationData( + sequence=sequence, + most_recent_image_idx=2, + hint="Testing SVG camera", + unit="mm/10min", + location=location + ) + + +def test_svg_frame_setup(): + data = get_radar_animation_data() + rain_graph = RainGraph( + animation_data=data, + background_image_path="custom_components/irm_kmi/resources/be_white.png", + background_size=(640, 490), + auto=False + ) + + rain_graph.draw_svg_frame() + + svg_str = rain_graph.get_dwg().tostring() + + with open("custom_components/irm_kmi/resources/roboto_medium.ttf", "rb") as file: + font_b64 = base64.b64encode(file.read()).decode('utf-8') + + assert '#385E95' in svg_str + assert 'font-family: "Roboto Medium";' in svg_str + assert font_b64 in svg_str + + +def test_svg_hint(): + data = get_radar_animation_data() + rain_graph = RainGraph( + animation_data=data, + background_image_path="custom_components/irm_kmi/resources/be_white.png", + background_size=(640, 490), + auto=False + ) + + rain_graph.write_hint() + + svg_str = rain_graph.get_dwg().tostring() + + assert "Testing SVG camera" in svg_str + + +def test_svg_time_bars(): + data = get_radar_animation_data() + rain_graph = RainGraph( + animation_data=data, + background_image_path="custom_components/irm_kmi/resources/be_white.png", + background_size=(640, 490), + auto=False + ) + + rain_graph.draw_hour_bars() + + svg_str = rain_graph.get_dwg().tostring() + + assert "19h" in svg_str + assert "20h" in svg_str + + assert "