From d2bab83aee4541535211025585a13d3a01ebe22f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 20 Jul 2020 21:24:30 +0000 Subject: [PATCH] fix more tests --- tests/components/utility_meter/test_sensor.py | 18 +++--- tests/components/zwave/test_init.py | 2 +- tests/conftest.py | 64 +++++++++++++++++++ 3 files changed, 74 insertions(+), 10 deletions(-) diff --git a/tests/components/utility_meter/test_sensor.py b/tests/components/utility_meter/test_sensor.py index 09145fc4e4e..c1613c53a20 100644 --- a/tests/components/utility_meter/test_sensor.py +++ b/tests/components/utility_meter/test_sensor.py @@ -260,49 +260,49 @@ async def _test_self_reset(hass, config, start_time, expect_reset=True): assert state.state == "5" -async def test_self_reset_hourly(hass): +async def test_self_reset_hourly(hass, legacy_patchable_time): """Test hourly reset of meter.""" await _test_self_reset( hass, gen_config("hourly"), "2017-12-31T23:59:00.000000+00:00" ) -async def test_self_reset_daily(hass): +async def test_self_reset_daily(hass, legacy_patchable_time): """Test daily reset of meter.""" await _test_self_reset( hass, gen_config("daily"), "2017-12-31T23:59:00.000000+00:00" ) -async def test_self_reset_weekly(hass): +async def test_self_reset_weekly(hass, legacy_patchable_time): """Test weekly reset of meter.""" await _test_self_reset( hass, gen_config("weekly"), "2017-12-31T23:59:00.000000+00:00" ) -async def test_self_reset_monthly(hass): +async def test_self_reset_monthly(hass, legacy_patchable_time): """Test monthly reset of meter.""" await _test_self_reset( hass, gen_config("monthly"), "2017-12-31T23:59:00.000000+00:00" ) -async def test_self_reset_quarterly(hass): +async def test_self_reset_quarterly(hass, legacy_patchable_time): """Test quarterly reset of meter.""" await _test_self_reset( hass, gen_config("quarterly"), "2017-03-31T23:59:00.000000+00:00" ) -async def test_self_reset_yearly(hass): +async def test_self_reset_yearly(hass, legacy_patchable_time): """Test yearly reset of meter.""" await _test_self_reset( hass, gen_config("yearly"), "2017-12-31T23:59:00.000000+00:00" ) -async def test_self_no_reset_yearly(hass): +async def test_self_no_reset_yearly(hass, legacy_patchable_time): """Test yearly reset of meter does not occur after 1st January.""" await _test_self_reset( hass, @@ -312,7 +312,7 @@ async def test_self_no_reset_yearly(hass): ) -async def test_reset_yearly_offset(hass): +async def test_reset_yearly_offset(hass, legacy_patchable_time): """Test yearly reset of meter.""" await _test_self_reset( hass, @@ -321,7 +321,7 @@ async def test_reset_yearly_offset(hass): ) -async def test_no_reset_yearly_offset(hass): +async def test_no_reset_yearly_offset(hass, legacy_patchable_time): """Test yearly reset of meter.""" await _test_self_reset( hass, diff --git a/tests/components/zwave/test_init.py b/tests/components/zwave/test_init.py index d1f141582ca..12b2c59ca81 100644 --- a/tests/components/zwave/test_init.py +++ b/tests/components/zwave/test_init.py @@ -119,7 +119,7 @@ async def test_erronous_network_key_fails_validation(hass, mock_openzwave): zwave.CONFIG_SCHEMA({"zwave": {"network_key": value}}) -async def test_auto_heal_midnight(hass, mock_openzwave): +async def test_auto_heal_midnight(hass, mock_openzwave, legacy_patchable_time): """Test network auto-heal at midnight.""" await async_setup_component(hass, "zwave", {"zwave": {"autoheal": True}}) await hass.async_block_till_done() diff --git a/tests/conftest.py b/tests/conftest.py index c3f600f9693..5c90dcb063e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,6 @@ """Set up some common test helper things.""" import asyncio +import datetime import functools import logging import threading @@ -387,8 +388,71 @@ def legacy_patchable_time(): return async_unsub + @ha.callback + @loader.bind_hass + def async_track_utc_time_change( + hass, action, hour=None, minute=None, second=None, local=False + ): + """Add a listener that will fire if time matches a pattern.""" + # We do not have to wrap the function with time pattern matching logic + # if no pattern given + if all(val is None for val in (hour, minute, second)): + + @ha.callback + def time_change_listener(ev) -> None: + """Fire every time event that comes in.""" + hass.async_run_job(action, ev.data[ATTR_NOW]) + + return hass.bus.async_listen(EVENT_TIME_CHANGED, time_change_listener) + + matching_seconds = event.dt_util.parse_time_expression(second, 0, 59) + matching_minutes = event.dt_util.parse_time_expression(minute, 0, 59) + matching_hours = event.dt_util.parse_time_expression(hour, 0, 23) + + next_time = None + + def calculate_next(now) -> None: + """Calculate and set the next time the trigger should fire.""" + nonlocal next_time + + localized_now = event.dt_util.as_local(now) if local else now + next_time = event.dt_util.find_next_time_expression_time( + localized_now, matching_seconds, matching_minutes, matching_hours + ) + + # Make sure rolling back the clock doesn't prevent the timer from + # triggering. + last_now = None + + @ha.callback + def pattern_time_change_listener(ev) -> None: + """Listen for matching time_changed events.""" + nonlocal next_time, last_now + + now = ev.data[ATTR_NOW] + + if last_now is None or now < last_now: + # Time rolled back or next time not yet calculated + calculate_next(now) + + last_now = now + + if next_time <= now: + hass.async_run_job( + action, event.dt_util.as_local(now) if local else now + ) + calculate_next(now + datetime.timedelta(seconds=1)) + + # We can't use async_track_point_in_utc_time here because it would + # break in the case that the system time abruptly jumps backwards. + # Our custom last_now logic takes care of resolving that scenario. + return hass.bus.async_listen(EVENT_TIME_CHANGED, pattern_time_change_listener) + with patch( "homeassistant.helpers.event.async_track_point_in_utc_time", async_track_point_in_utc_time, + ), patch( + "homeassistant.helpers.event.async_track_utc_time_change", + async_track_utc_time_change, ): yield