From 8d366a736731c10b4d6b56a9939e2b8299051ee0 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 14 Feb 2016 22:01:30 -0800 Subject: [PATCH 1/3] Tests: Mock get_local_ip by default too --- tests/__init__.py | 2 ++ tests/components/device_tracker/test_locative.py | 4 +--- tests/components/test_alexa.py | 5 +---- tests/components/test_api.py | 4 +--- tests/components/test_frontend.py | 5 +---- tests/test_remote.py | 11 +++-------- 6 files changed, 9 insertions(+), 22 deletions(-) diff --git a/tests/__init__.py b/tests/__init__.py index e582287f941..1f18116b24b 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -6,6 +6,7 @@ Tests initialization. """ import betamax +from homeassistant import util from homeassistant.util import location with betamax.Betamax.configure() as config: @@ -28,3 +29,4 @@ location.detect_location_info = lambda: location.LocationInfo( ) location.elevation = lambda latitude, longitude: 0 +util.get_local_ip = lambda: '127.0.0.1' diff --git a/tests/components/device_tracker/test_locative.py b/tests/components/device_tracker/test_locative.py index 32a63d0962f..5182a536034 100644 --- a/tests/components/device_tracker/test_locative.py +++ b/tests/components/device_tracker/test_locative.py @@ -28,9 +28,7 @@ def _url(data={}): return "{}{}locative?{}".format(HTTP_BASE_URL, const.URL_API, data) -@patch('homeassistant.components.http.util.get_local_ip', - return_value='127.0.0.1') -def setUpModule(mock_get_local_ip): # pylint: disable=invalid-name +def setUpModule(): # pylint: disable=invalid-name """ Initalizes a Home Assistant server. """ global hass diff --git a/tests/components/test_alexa.py b/tests/components/test_alexa.py index fa08f8e9129..f1de566a57e 100644 --- a/tests/components/test_alexa.py +++ b/tests/components/test_alexa.py @@ -7,7 +7,6 @@ Tests Home Assistant Alexa component does what it should do. # pylint: disable=protected-access,too-many-public-methods import unittest import json -from unittest.mock import patch import requests @@ -29,9 +28,7 @@ hass = None calls = [] -@patch('homeassistant.components.http.util.get_local_ip', - return_value='127.0.0.1') -def setUpModule(mock_get_local_ip): # pylint: disable=invalid-name +def setUpModule(): # pylint: disable=invalid-name """ Initalize a Home Assistant server for testing this module. """ global hass diff --git a/tests/components/test_api.py b/tests/components/test_api.py index 6e72666454f..3dbaedd8c1a 100644 --- a/tests/components/test_api.py +++ b/tests/components/test_api.py @@ -32,9 +32,7 @@ def _url(path=""): return HTTP_BASE_URL + path -@patch('homeassistant.components.http.util.get_local_ip', - return_value='127.0.0.1') -def setUpModule(mock_get_local_ip): # pylint: disable=invalid-name +def setUpModule(): # pylint: disable=invalid-name """ Initializes a Home Assistant server. """ global hass diff --git a/tests/components/test_frontend.py b/tests/components/test_frontend.py index a81cea899f6..8455bd5a8f9 100644 --- a/tests/components/test_frontend.py +++ b/tests/components/test_frontend.py @@ -7,7 +7,6 @@ Tests Home Assistant HTTP component does what it should do. # pylint: disable=protected-access,too-many-public-methods import re import unittest -from unittest.mock import patch import requests @@ -30,9 +29,7 @@ def _url(path=""): return HTTP_BASE_URL + path -@patch('homeassistant.components.http.util.get_local_ip', - return_value='127.0.0.1') -def setUpModule(mock_get_local_ip): # pylint: disable=invalid-name +def setUpModule(): # pylint: disable=invalid-name """ Initalizes a Home Assistant server. """ global hass diff --git a/tests/test_remote.py b/tests/test_remote.py index 725ba25ac80..435cbde79f9 100644 --- a/tests/test_remote.py +++ b/tests/test_remote.py @@ -6,7 +6,6 @@ Tests Home Assistant remote methods and classes. """ # pylint: disable=protected-access,too-many-public-methods import unittest -from unittest.mock import patch import homeassistant.core as ha import homeassistant.bootstrap as bootstrap @@ -24,7 +23,8 @@ HTTP_BASE_URL = "http://127.0.0.1:{}".format(MASTER_PORT) HA_HEADERS = {HTTP_HEADER_HA_AUTH: API_PASSWORD} -hass, slave, master_api, broken_api = None, None, None, None +broken_api = remote.API('127.0.0.1', BROKEN_PORT) +hass, slave, master_api = None, None, None def _url(path=""): @@ -32,9 +32,7 @@ def _url(path=""): return HTTP_BASE_URL + path -@patch('homeassistant.components.http.util.get_local_ip', - return_value='127.0.0.1') -def setUpModule(mock_get_local_ip): # pylint: disable=invalid-name +def setUpModule(): # pylint: disable=invalid-name """ Initalizes a Home Assistant server and Slave instance. """ global hass, slave, master_api, broken_api @@ -63,9 +61,6 @@ def setUpModule(mock_get_local_ip): # pylint: disable=invalid-name slave.start() - # Setup API pointing at nothing - broken_api = remote.API("127.0.0.1", "", BROKEN_PORT) - def tearDownModule(): # pylint: disable=invalid-name """ Stops the Home Assistant server and slave. """ From 68803a46b6d912ac21d21b01d17d8335c308a1f5 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 14 Feb 2016 23:01:49 -0800 Subject: [PATCH 2/3] Thread pool tweaks --- homeassistant/core.py | 5 --- homeassistant/remote.py | 7 +--- homeassistant/util/__init__.py | 19 ++++----- .../device_tracker/test_locative.py | 3 ++ tests/components/test_alexa.py | 3 ++ tests/components/test_api.py | 3 ++ tests/components/test_frontend.py | 3 ++ tests/test_remote.py | 40 ++++++++++--------- 8 files changed, 45 insertions(+), 38 deletions(-) diff --git a/homeassistant/core.py b/homeassistant/core.py index 25062952ed0..839058d25dd 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -103,12 +103,7 @@ class HomeAssistant(object): def stop(self): """Stop Home Assistant and shuts down all threads.""" _LOGGER.info("Stopping") - self.bus.fire(EVENT_HOMEASSISTANT_STOP) - - # Wait till all responses to homeassistant_stop are done - self.pool.block_till_done() - self.pool.stop() diff --git a/homeassistant/remote.py b/homeassistant/remote.py index 6b55622adce..29ac7eeb1db 100644 --- a/homeassistant/remote.py +++ b/homeassistant/remote.py @@ -148,14 +148,11 @@ class HomeAssistant(ha.HomeAssistant): self.bus.fire(ha.EVENT_HOMEASSISTANT_STOP, origin=ha.EventOrigin.remote) + self.pool.stop() + # Disconnect master event forwarding disconnect_remote_events(self.remote_api, self.config.api) - # Wait till all responses to homeassistant_stop are done - self.pool.block_till_done() - - self.pool.stop() - class EventBus(ha.EventBus): """ EventBus implementation that forwards fire_event to remote API. """ diff --git a/homeassistant/util/__init__.py b/homeassistant/util/__init__.py index 89b2ab0e1f3..16650fe549d 100644 --- a/homeassistant/util/__init__.py +++ b/homeassistant/util/__init__.py @@ -284,7 +284,7 @@ class Throttle(object): class ThreadPool(object): - """ A priority queue-based thread pool. """ + """A priority queue-based thread pool.""" # pylint: disable=too-many-instance-attributes def __init__(self, job_handler, worker_count=0, busy_callback=None): @@ -311,7 +311,7 @@ class ThreadPool(object): self.add_worker() def add_worker(self): - """ Adds a worker to the thread pool. Resets warning limit. """ + """Add worker to the thread pool and reset warning limit.""" with self._lock: if not self.running: raise RuntimeError("ThreadPool not running") @@ -324,7 +324,7 @@ class ThreadPool(object): self.busy_warning_limit = self.worker_count * 3 def remove_worker(self): - """ Removes a worker from the thread pool. Resets warning limit. """ + """Remove worker from the thread pool and reset warning limit.""" with self._lock: if not self.running: raise RuntimeError("ThreadPool not running") @@ -354,18 +354,19 @@ class ThreadPool(object): self._work_queue.qsize()) def block_till_done(self): - """ Blocks till all work is done. """ + """Block till current work is done.""" self._work_queue.join() + # import traceback + # traceback.print_stack() def stop(self): - """ Stops all the threads. """ + """Finish all the jobs and stops all the threads.""" + self.block_till_done() + with self._lock: if not self.running: return - # Ensure all current jobs finish - self.block_till_done() - # Tell the workers to quit for _ in range(self.worker_count): self.remove_worker() @@ -376,7 +377,7 @@ class ThreadPool(object): self.block_till_done() def _worker(self): - """ Handles jobs for the thread pool. """ + """Handle jobs for the thread pool.""" while True: # Get new item from work_queue job = self._work_queue.get().item diff --git a/tests/components/device_tracker/test_locative.py b/tests/components/device_tracker/test_locative.py index 5182a536034..5e8babca2d9 100644 --- a/tests/components/device_tracker/test_locative.py +++ b/tests/components/device_tracker/test_locative.py @@ -64,6 +64,9 @@ def tearDownModule(): # pylint: disable=invalid-name class TestLocative(unittest.TestCase): """ Test Locative """ + def tearDown(self): + hass.pool.block_till_done() + def test_missing_data(self, update_config): data = { 'latitude': 1.0, diff --git a/tests/components/test_alexa.py b/tests/components/test_alexa.py index f1de566a57e..fcdc4b09937 100644 --- a/tests/components/test_alexa.py +++ b/tests/components/test_alexa.py @@ -102,6 +102,9 @@ def _req(data={}): class TestAlexa(unittest.TestCase): """ Test Alexa. """ + def tearDown(self): + hass.pool.block_till_done() + def test_launch_request(self): data = { 'version': '1.0', diff --git a/tests/components/test_api.py b/tests/components/test_api.py index 3dbaedd8c1a..87f67414e0f 100644 --- a/tests/components/test_api.py +++ b/tests/components/test_api.py @@ -59,6 +59,9 @@ def tearDownModule(): # pylint: disable=invalid-name class TestAPI(unittest.TestCase): """ Test the API. """ + def tearDown(self): + hass.pool.block_till_done() + # TODO move back to http component and test with use_auth. def test_access_denied_without_password(self): req = requests.get(_url(const.URL_API)) diff --git a/tests/components/test_frontend.py b/tests/components/test_frontend.py index 8455bd5a8f9..a9fb6550a9f 100644 --- a/tests/components/test_frontend.py +++ b/tests/components/test_frontend.py @@ -56,6 +56,9 @@ def tearDownModule(): # pylint: disable=invalid-name class TestFrontend(unittest.TestCase): """ Test the frontend. """ + def tearDown(self): + hass.pool.block_till_done() + def test_frontend_and_static(self): """ Tests if we can get the frontend. """ req = requests.get(_url("")) diff --git a/tests/test_remote.py b/tests/test_remote.py index 435cbde79f9..b49aea25244 100644 --- a/tests/test_remote.py +++ b/tests/test_remote.py @@ -34,7 +34,7 @@ def _url(path=""): def setUpModule(): # pylint: disable=invalid-name """ Initalizes a Home Assistant server and Slave instance. """ - global hass, slave, master_api, broken_api + global hass, slave, master_api hass = get_test_home_assistant() @@ -64,8 +64,6 @@ def setUpModule(): # pylint: disable=invalid-name def tearDownModule(): # pylint: disable=invalid-name """ Stops the Home Assistant server and slave. """ - global hass, slave - slave.stop() hass.stop() @@ -73,6 +71,10 @@ def tearDownModule(): # pylint: disable=invalid-name class TestRemoteMethods(unittest.TestCase): """ Test the homeassistant.remote module. """ + def tearDown(self): + slave.pool.block_till_done() + hass.pool.block_till_done() + def test_validate_api(self): """ Test Python API validate_api. """ self.assertEqual(remote.APIStatus.OK, remote.validate_api(master_api)) @@ -193,10 +195,24 @@ class TestRemoteMethods(unittest.TestCase): # Should not raise an exception remote.call_service(broken_api, "test_domain", "test_service") + def test_json_encoder(self): + """ Test the JSON Encoder. """ + ha_json_enc = remote.JSONEncoder() + state = hass.states.get('test.test') + + self.assertEqual(state.as_dict(), ha_json_enc.default(state)) + + # Default method raises TypeError if non HA object + self.assertRaises(TypeError, ha_json_enc.default, 1) + class TestRemoteClasses(unittest.TestCase): """ Test the homeassistant.remote module. """ + def tearDown(self): + slave.pool.block_till_done() + hass.pool.block_till_done() + def test_home_assistant_init(self): """ Test HomeAssistant init. """ # Wrong password @@ -211,12 +227,8 @@ class TestRemoteClasses(unittest.TestCase): def test_statemachine_init(self): """ Tests if remote.StateMachine copies all states on init. """ - self.assertEqual(len(hass.states.all()), - len(slave.states.all())) - - for state in hass.states.all(): - self.assertEqual( - state, slave.states.get(state.entity_id)) + self.assertEqual(sorted(hass.states.all()), + sorted(slave.states.all())) def test_statemachine_set(self): """ Tests if setting the state on a slave is recorded. """ @@ -271,13 +283,3 @@ class TestRemoteClasses(unittest.TestCase): hass.pool.block_till_done() self.assertEqual(1, len(test_value)) - - def test_json_encoder(self): - """ Test the JSON Encoder. """ - ha_json_enc = remote.JSONEncoder() - state = hass.states.get('test.test') - - self.assertEqual(state.as_dict(), ha_json_enc.default(state)) - - # Default method raises TypeError if non HA object - self.assertRaises(TypeError, ha_json_enc.default, 1) From c287520432859a48d5aa6825216d3dcb214d1e9a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 14 Feb 2016 23:16:54 -0800 Subject: [PATCH 3/3] MQTT Light test - switch order --- tests/components/light/test_mqtt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/components/light/test_mqtt.py b/tests/components/light/test_mqtt.py index ff55c22f0e3..f08e84677a5 100644 --- a/tests/components/light/test_mqtt.py +++ b/tests/components/light/test_mqtt.py @@ -172,12 +172,12 @@ class TestLightMQTT(unittest.TestCase): self.assertIsNone(state.attributes.get('brightness')) self.assertIsNone(state.attributes.get('rgb_color')) + fire_mqtt_message(self.hass, 'test_light_rgb/rgb/status', + '{"hello": [1, 2, 3]}') fire_mqtt_message(self.hass, 'test_light_rgb/status', '{"hello": "ON"}') fire_mqtt_message(self.hass, 'test_light_rgb/brightness/status', '{"hello": "50"}') - fire_mqtt_message(self.hass, 'test_light_rgb/rgb/status', - '{"hello": [1, 2, 3]}') self.hass.pool.block_till_done() state = self.hass.states.get('light.test')