mirror of
https://github.com/jdejaegh/irm-kmi-ha.git
synced 2025-06-27 03:35:56 +02:00
Add tests for new SVG camera and remove old ones
This commit is contained in:
parent
c9ec30b8b2
commit
b7d39bf753
5 changed files with 279 additions and 76 deletions
|
@ -54,7 +54,6 @@ class IrmKmiRadar(CoordinatorEntity, Camera):
|
|||
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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -83,8 +83,6 @@ class RainGraph:
|
|||
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')
|
||||
|
|
|
@ -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"
|
||||
|
|
278
tests/test_rain_graph.py
Normal file
278
tests/test_rain_graph.py
Normal file
|
@ -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 "<line" in svg_str
|
||||
assert 'stroke="white"' in svg_str
|
||||
|
||||
|
||||
def test_draw_chances_path():
|
||||
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_chances_path()
|
||||
|
||||
svg_str = rain_graph.get_dwg().tostring()
|
||||
|
||||
assert 'fill="#63c8fa"' in svg_str
|
||||
assert 'opacity="0.3"' in svg_str
|
||||
assert 'stroke="none"' in svg_str
|
||||
assert '<path ' in svg_str
|
||||
|
||||
|
||||
def test_draw_data_line():
|
||||
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_data_line()
|
||||
|
||||
svg_str = rain_graph.get_dwg().tostring()
|
||||
|
||||
assert 'fill="none"' in svg_str
|
||||
assert 'stroke-width="2"' in svg_str
|
||||
assert 'stroke="#63c8fa"' in svg_str
|
||||
assert '<path ' in svg_str
|
||||
|
||||
|
||||
def test_insert_background():
|
||||
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.insert_background()
|
||||
|
||||
with open("custom_components/irm_kmi/resources/be_white.png", "rb") as file:
|
||||
png_b64 = base64.b64encode(file.read()).decode('utf-8')
|
||||
|
||||
svg_str = rain_graph.get_dwg().tostring()
|
||||
|
||||
assert png_b64 in svg_str
|
||||
assert "<image " in svg_str
|
||||
assert 'height="490"' in svg_str
|
||||
assert 'width="640"' in svg_str
|
||||
assert 'x="0"' in svg_str
|
||||
assert 'y="0"' in svg_str
|
||||
|
||||
|
||||
def test_draw_current_frame_line_moving():
|
||||
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_current_fame_line()
|
||||
|
||||
str_svg = rain_graph.get_dwg().tostring()
|
||||
|
||||
assert '<line' in str_svg
|
||||
assert 'id="now"' in str_svg
|
||||
assert 'opacity="1"' in str_svg
|
||||
assert 'stroke="white"' in str_svg
|
||||
assert 'stroke-width="2"' in str_svg
|
||||
assert 'x1="50' in str_svg
|
||||
assert 'x2="50' in str_svg
|
||||
assert 'y1="520' in str_svg
|
||||
assert 'y2="670' in str_svg
|
||||
|
||||
assert 'animateTransform' in str_svg
|
||||
assert 'attributeName="transform"' in str_svg
|
||||
assert 'repeatCount="indefinite"' in str_svg
|
||||
assert 'type="translate"' in str_svg
|
||||
|
||||
|
||||
def test_draw_current_frame_line_index():
|
||||
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_current_fame_line(0)
|
||||
|
||||
str_svg = rain_graph.get_dwg().tostring()
|
||||
|
||||
assert '<line' in str_svg
|
||||
assert 'id="now"' in str_svg
|
||||
assert 'opacity="1"' in str_svg
|
||||
assert 'stroke="white"' in str_svg
|
||||
assert 'stroke-width="2"' in str_svg
|
||||
assert 'x1="50' in str_svg
|
||||
assert 'x2="50' in str_svg
|
||||
assert 'y1="520' in str_svg
|
||||
assert 'y2="670' in str_svg
|
||||
|
||||
assert 'animateTransform' not in str_svg
|
||||
assert 'attributeName="transform"' not in str_svg
|
||||
assert 'repeatCount="indefinite"' not in str_svg
|
||||
assert 'type="translate"' not in str_svg
|
||||
|
||||
|
||||
def test_draw_description_text():
|
||||
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_description_text()
|
||||
|
||||
str_svg = rain_graph.get_dwg().tostring()
|
||||
|
||||
assert "18:30" in str_svg
|
||||
assert "18:40" in str_svg
|
||||
assert "18:50" in str_svg
|
||||
assert "19:00" in str_svg
|
||||
assert "19:10" in str_svg
|
||||
assert "19:20" in str_svg
|
||||
assert "19:30" in str_svg
|
||||
assert "19:40" in str_svg
|
||||
assert "19:50" in str_svg
|
||||
assert "20:00" in str_svg
|
||||
|
||||
assert str_svg.count("2mm/10") == 10
|
||||
assert 'class="roboto"' in str_svg
|
||||
|
||||
|
||||
def test_draw_cloud_layer():
|
||||
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.insert_cloud_layer()
|
||||
|
||||
str_svg = rain_graph.get_dwg().tostring()
|
||||
|
||||
with open("tests/fixtures/clouds_be.png", "rb") as file:
|
||||
png_b64 = base64.b64encode(file.read()).decode('utf-8')
|
||||
|
||||
assert str_svg.count(png_b64) == 10
|
||||
assert str_svg.count('height="490"') == 10
|
||||
assert str_svg.count('width="640"') == 11 # Is also the width of the SVG itself
|
||||
|
||||
|
||||
def test_draw_location_layer():
|
||||
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_location()
|
||||
|
||||
str_svg = rain_graph.get_dwg().tostring()
|
||||
|
||||
with open("tests/fixtures/loc_layer_be_n.png", "rb") as file:
|
||||
png_b64 = base64.b64encode(file.read()).decode('utf-8')
|
||||
|
||||
assert png_b64 in str_svg
|
Loading…
Add table
Reference in a new issue