From 9e1ecd7124af55e84d113a63d189262faafa5ad0 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 27 Dec 2015 09:39:22 -0800 Subject: [PATCH] Fix flaky history test --- homeassistant/components/recorder.py | 29 +++++----- tests/components/test_history.py | 86 +++++++++++++++------------- 2 files changed, 61 insertions(+), 54 deletions(-) diff --git a/homeassistant/components/recorder.py b/homeassistant/components/recorder.py index 126d8c9f40e..802634715e9 100644 --- a/homeassistant/components/recorder.py +++ b/homeassistant/components/recorder.py @@ -16,7 +16,7 @@ import json import atexit from homeassistant.core import Event, EventOrigin, State -import homeassistant.util.dt as date_util +import homeassistant.util.dt as dt_util from homeassistant.remote import JSONEncoder from homeassistant.const import ( MATCH_ALL, EVENT_TIME_CHANGED, EVENT_STATE_CHANGED, @@ -62,8 +62,8 @@ def row_to_state(row): try: return State( row[1], row[2], json.loads(row[3]), - date_util.utc_from_timestamp(row[4]), - date_util.utc_from_timestamp(row[5])) + dt_util.utc_from_timestamp(row[4]), + dt_util.utc_from_timestamp(row[5])) except ValueError: # When json.loads fails _LOGGER.exception("Error converting row to state: %s", row) @@ -74,7 +74,7 @@ def row_to_event(row): """ Convert a databse row to an event. """ try: return Event(row[1], json.loads(row[2]), EventOrigin(row[3]), - date_util.utc_from_timestamp(row[5])) + dt_util.utc_from_timestamp(row[5])) except ValueError: # When json.loads fails _LOGGER.exception("Error converting row to event: %s", row) @@ -116,10 +116,10 @@ class RecorderRun(object): self.start = _INSTANCE.recording_start self.closed_incorrect = False else: - self.start = date_util.utc_from_timestamp(row[1]) + self.start = dt_util.utc_from_timestamp(row[1]) if row[2] is not None: - self.end = date_util.utc_from_timestamp(row[2]) + self.end = dt_util.utc_from_timestamp(row[2]) self.closed_incorrect = bool(row[3]) @@ -169,8 +169,8 @@ class Recorder(threading.Thread): self.queue = queue.Queue() self.quit_object = object() self.lock = threading.Lock() - self.recording_start = date_util.utcnow() - self.utc_offset = date_util.now().utcoffset().total_seconds() + self.recording_start = dt_util.utcnow() + self.utc_offset = dt_util.now().utcoffset().total_seconds() def start_recording(event): """ Start recording. """ @@ -217,10 +217,11 @@ class Recorder(threading.Thread): def shutdown(self, event): """ Tells the recorder to shut down. """ self.queue.put(self.quit_object) + self.block_till_done() def record_state(self, entity_id, state, event_id): """ Save a state to the database. """ - now = date_util.utcnow() + now = dt_util.utcnow() # State got deleted if state is None: @@ -247,7 +248,7 @@ class Recorder(threading.Thread): """ Save an event to the database. """ info = ( event.event_type, json.dumps(event.data, cls=JSONEncoder), - str(event.origin), date_util.utcnow(), event.time_fired, + str(event.origin), dt_util.utcnow(), event.time_fired, self.utc_offset ) @@ -307,7 +308,7 @@ class Recorder(threading.Thread): def save_migration(migration_id): """ Save and commit a migration to the database. """ cur.execute('INSERT INTO schema_version VALUES (?, ?)', - (migration_id, date_util.utcnow())) + (migration_id, dt_util.utcnow())) self.conn.commit() _LOGGER.info("Database migrated to version %d", migration_id) @@ -420,18 +421,18 @@ class Recorder(threading.Thread): self.query( """INSERT INTO recorder_runs (start, created, utc_offset) VALUES (?, ?, ?)""", - (self.recording_start, date_util.utcnow(), self.utc_offset)) + (self.recording_start, dt_util.utcnow(), self.utc_offset)) def _close_run(self): """ Save end time for current run. """ self.query( "UPDATE recorder_runs SET end=? WHERE start=?", - (date_util.utcnow(), self.recording_start)) + (dt_util.utcnow(), self.recording_start)) def _adapt_datetime(datetimestamp): """ Turn a datetime into an integer for in the DB. """ - return date_util.as_utc(datetimestamp.replace(microsecond=0)).timestamp() + return dt_util.as_utc(datetimestamp.replace(microsecond=0)).timestamp() def _verify_instance(): diff --git a/tests/components/test_history.py b/tests/components/test_history.py index fdd8270a661..f9e773c499a 100644 --- a/tests/components/test_history.py +++ b/tests/components/test_history.py @@ -5,11 +5,10 @@ tests.test_component_history Tests the history component. """ # pylint: disable=protected-access,too-many-public-methods -import time +from datetime import timedelta import os import unittest from unittest.mock import patch -from datetime import timedelta import homeassistant.core as ha import homeassistant.util.dt as dt_util @@ -25,21 +24,24 @@ class TestComponentHistory(unittest.TestCase): def setUp(self): # pylint: disable=invalid-name """ Init needed objects. """ self.hass = get_test_home_assistant(1) - self.init_rec = False def tearDown(self): # pylint: disable=invalid-name """ Stop down stuff we started. """ self.hass.stop() - if self.init_rec: - recorder._INSTANCE.block_till_done() - os.remove(self.hass.config.path(recorder.DB_FILE)) + db_path = self.hass.config.path(recorder.DB_FILE) + if os.path.isfile(db_path): + os.remove(db_path) def init_recorder(self): recorder.setup(self.hass, {}) self.hass.start() + self.wait_recording_done() + + def wait_recording_done(self): + """ Block till recording is done. """ + self.hass.pool.block_till_done() recorder._INSTANCE.block_till_done() - self.init_rec = True def test_setup(self): """ Test setup method of history. """ @@ -56,12 +58,11 @@ class TestComponentHistory(unittest.TestCase): for i in range(7): self.hass.states.set(entity_id, "State {}".format(i)) + self.wait_recording_done() + if i > 1: states.append(self.hass.states.get(entity_id)) - self.hass.pool.block_till_done() - recorder._INSTANCE.block_till_done() - self.assertEqual( list(reversed(states)), history.last_5_states(entity_id)) @@ -70,22 +71,9 @@ class TestComponentHistory(unittest.TestCase): self.init_recorder() states = [] - for i in range(5): - state = ha.State( - 'test.point_in_time_{}'.format(i % 5), - "State {}".format(i), - {'attribute_test': i}) - - mock_state_change_event(self.hass, state) - self.hass.pool.block_till_done() - - states.append(state) - - recorder._INSTANCE.block_till_done() - - point = dt_util.utcnow() + timedelta(seconds=1) - - with patch('homeassistant.util.dt.utcnow', return_value=point): + now = dt_util.utcnow() + with patch('homeassistant.components.recorder.dt_util.utcnow', + return_value=now): for i in range(5): state = ha.State( 'test.point_in_time_{}'.format(i % 5), @@ -93,16 +81,32 @@ class TestComponentHistory(unittest.TestCase): {'attribute_test': i}) mock_state_change_event(self.hass, state) - self.hass.pool.block_till_done() + + states.append(state) + + self.wait_recording_done() + + future = now + timedelta(seconds=1) + with patch('homeassistant.components.recorder.dt_util.utcnow', + return_value=future): + for i in range(5): + state = ha.State( + 'test.point_in_time_{}'.format(i % 5), + "State {}".format(i), + {'attribute_test': i}) + + mock_state_change_event(self.hass, state) + + self.wait_recording_done() # Get states returns everything before POINT self.assertEqual(states, - sorted(history.get_states(point), + sorted(history.get_states(future), key=lambda state: state.entity_id)) # Test get_state here because we have a DB setup self.assertEqual( - states[0], history.get_state(point, states[0].entity_id)) + states[0], history.get_state(future, states[0].entity_id)) def test_state_changes_during_period(self): self.init_recorder() @@ -110,19 +114,20 @@ class TestComponentHistory(unittest.TestCase): def set_state(state): self.hass.states.set(entity_id, state) - self.hass.pool.block_till_done() - recorder._INSTANCE.block_till_done() - + self.wait_recording_done() return self.hass.states.get(entity_id) - set_state('idle') - set_state('YouTube') - start = dt_util.utcnow() point = start + timedelta(seconds=1) end = point + timedelta(seconds=1) - with patch('homeassistant.util.dt.utcnow', return_value=point): + with patch('homeassistant.components.recorder.dt_util.utcnow', + return_value=start): + set_state('idle') + set_state('YouTube') + + with patch('homeassistant.components.recorder.dt_util.utcnow', + return_value=point): states = [ set_state('idle'), set_state('Netflix'), @@ -130,10 +135,11 @@ class TestComponentHistory(unittest.TestCase): set_state('YouTube'), ] - with patch('homeassistant.util.dt.utcnow', return_value=end): + with patch('homeassistant.components.recorder.dt_util.utcnow', + return_value=end): set_state('Netflix') set_state('Plex') - self.assertEqual( - {entity_id: states}, - history.state_changes_during_period(start, end, entity_id)) + hist = history.state_changes_during_period(start, end, entity_id) + + self.assertEqual(states, hist[entity_id])