Add tests for new SVG camera and remove old ones

This commit is contained in:
Jules 2024-01-02 21:45:37 +01:00
parent c9ec30b8b2
commit b7d39bf753
Signed by: jdejaegh
GPG key ID: 99D6D184CA66933A
5 changed files with 279 additions and 76 deletions

View file

@ -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(

View file

@ -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

View file

@ -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')
@ -330,4 +328,4 @@ class RainGraph:
self._dwg.add(image)
def get_dwg(self):
return copy.deepcopy(self._dwg)
return copy.deepcopy(self._dwg)

View file

@ -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
View 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