diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index b9a3d89251a8..294645f693b0 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -131,9 +131,10 @@ def _async_setup_component(hass: core.HomeAssistant, return False component = loader.get_component(domain) + async_comp = hasattr(component, 'async_setup') try: - if hasattr(component, 'async_setup'): + if async_comp: result = yield from component.async_setup(hass, config) else: result = yield from hass.loop.run_in_executor( @@ -155,9 +156,12 @@ def _async_setup_component(hass: core.HomeAssistant, # Assumption: if a component does not depend on groups # it communicates with devices - if 'group' not in getattr(component, 'DEPENDENCIES', []) and \ - hass.pool.worker_count <= 10: - hass.pool.add_worker() + if (not async_comp and + 'group' not in getattr(component, 'DEPENDENCIES', [])): + if hass.pool is None: + hass.async_init_pool() + if hass.pool.worker_count <= 10: + hass.pool.add_worker() hass.bus.async_fire( EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: component.DOMAIN} diff --git a/homeassistant/components/sensor/zigbee.py b/homeassistant/components/sensor/zigbee.py index 6b455230aa6a..7d4ead138e36 100644 --- a/homeassistant/components/sensor/zigbee.py +++ b/homeassistant/components/sensor/zigbee.py @@ -12,7 +12,6 @@ import voluptuous as vol from homeassistant.components import zigbee from homeassistant.components.zigbee import PLATFORM_SCHEMA from homeassistant.const import TEMP_CELSIUS -from homeassistant.core import JobPriority from homeassistant.helpers.entity import Entity _LOGGER = logging.getLogger(__name__) @@ -56,8 +55,7 @@ class ZigBeeTemperatureSensor(Entity): self._config = config self._temp = None # Get initial state - hass.pool.add_job( - JobPriority.EVENT_STATE, (self.update_ha_state, True)) + hass.add_job(self.update_ha_state, True) @property def name(self): diff --git a/homeassistant/components/zigbee.py b/homeassistant/components/zigbee.py index 8a9271745c97..a428d03efc1b 100644 --- a/homeassistant/components/zigbee.py +++ b/homeassistant/components/zigbee.py @@ -13,7 +13,6 @@ import voluptuous as vol from homeassistant.const import ( EVENT_HOMEASSISTANT_STOP, CONF_DEVICE, CONF_NAME, CONF_PIN) -from homeassistant.core import JobPriority from homeassistant.helpers.entity import Entity from homeassistant.helpers import config_validation as cv @@ -308,8 +307,7 @@ class ZigBeeDigitalIn(Entity): subscribe(hass, handle_frame) # Get initial state - hass.pool.add_job( - JobPriority.EVENT_STATE, (self.update_ha_state, True)) + hass.add_job(self.update_ha_state, True) @property def name(self): @@ -435,8 +433,7 @@ class ZigBeeAnalogIn(Entity): subscribe(hass, handle_frame) # Get initial state - hass.pool.add_job( - JobPriority.EVENT_STATE, (self.update_ha_state, True)) + hass.add_job(self.update_ha_state, True) @property def name(self): diff --git a/homeassistant/core.py b/homeassistant/core.py index 4d61b33eb652..5fb7d2761ccd 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -102,29 +102,6 @@ class CoreState(enum.Enum): return self.value -class JobPriority(util.OrderedEnum): - """Provides job priorities for event bus jobs.""" - - EVENT_CALLBACK = 0 - EVENT_SERVICE = 1 - EVENT_STATE = 2 - EVENT_TIME = 3 - EVENT_DEFAULT = 4 - - @staticmethod - def from_event_type(event_type): - """Return a priority based on event type.""" - if event_type == EVENT_TIME_CHANGED: - return JobPriority.EVENT_TIME - elif event_type == EVENT_STATE_CHANGED: - return JobPriority.EVENT_STATE - elif event_type == EVENT_CALL_SERVICE: - return JobPriority.EVENT_SERVICE - elif event_type == EVENT_SERVICE_EXECUTED: - return JobPriority.EVENT_CALLBACK - return JobPriority.EVENT_DEFAULT - - class HomeAssistant(object): """Root object of the Home Assistant home automation.""" @@ -134,9 +111,10 @@ class HomeAssistant(object): self.executor = ThreadPoolExecutor(max_workers=5) self.loop.set_default_executor(self.executor) self.loop.set_exception_handler(self._async_exception_handler) - self.pool = create_worker_pool() + self.pool = None self.bus = EventBus(self) - self.services = ServiceRegistry(self.bus, self.add_job, self.loop) + self.services = ServiceRegistry(self.bus, self.async_add_job, + self.loop) self.states = StateMachine(self.bus, self.loop) self.config = Config() # type: Config # This is a dictionary that any component can store any data on. @@ -180,8 +158,7 @@ class HomeAssistant(object): This method is a coroutine. """ - _LOGGER.info( - "Starting Home Assistant (%d threads)", self.pool.worker_count) + _LOGGER.info("Starting Home Assistant") self.state = CoreState.starting @@ -208,24 +185,24 @@ class HomeAssistant(object): # pylint: disable=protected-access self.loop._thread_ident = threading.get_ident() _async_create_timer(self) - _async_monitor_worker_pool(self) self.bus.async_fire(EVENT_HOMEASSISTANT_START) - yield from self.loop.run_in_executor(None, self.pool.block_till_done) + if self.pool is not None: + yield from self.loop.run_in_executor( + None, self.pool.block_till_done) self.state = CoreState.running - def add_job(self, - target: Callable[..., None], - *args: Any, - priority: JobPriority=JobPriority.EVENT_DEFAULT) -> None: + def add_job(self, target: Callable[..., None], *args: Any) -> None: """Add job to the worker pool. target: target to call. args: parameters for method to call. """ - self.pool.add_job(priority, (target,) + args) + if self.pool is None: + run_callback_threadsafe(self.pool, self.async_init_pool).result() + self.pool.add_job((target,) + args) @callback - def async_add_job(self, target: Callable[..., None], *args: Any): + def async_add_job(self, target: Callable[..., None], *args: Any) -> None: """Add a job from within the eventloop. This method must be run in the event loop. @@ -238,10 +215,12 @@ class HomeAssistant(object): elif asyncio.iscoroutinefunction(target): self.loop.create_task(target(*args)) else: - self.add_job(target, *args) + if self.pool is None: + self.async_init_pool() + self.pool.add_job((target,) + args) @callback - def async_run_job(self, target: Callable[..., None], *args: Any): + def async_run_job(self, target: Callable[..., None], *args: Any) -> None: """Run a job from within the event loop. This method must be run in the event loop. @@ -254,7 +233,7 @@ class HomeAssistant(object): else: self.async_add_job(target, *args) - def _loop_empty(self): + def _loop_empty(self) -> bool: """Python 3.4.2 empty loop compatibility function.""" # pylint: disable=protected-access if sys.version_info < (3, 4, 3): @@ -264,7 +243,7 @@ class HomeAssistant(object): return self.loop._current_handle is None and \ len(self.loop._ready) == 0 - def block_till_done(self): + def block_till_done(self) -> None: """Block till all pending work is done.""" complete = threading.Event() @@ -278,7 +257,8 @@ class HomeAssistant(object): count = 0 while True: # Wait for the work queue to empty - self.pool.block_till_done() + if self.pool is not None: + self.pool.block_till_done() # Verify the loop is empty if self._loop_empty(): @@ -309,8 +289,10 @@ class HomeAssistant(object): """ self.state = CoreState.stopping self.bus.async_fire(EVENT_HOMEASSISTANT_STOP) - yield from self.loop.run_in_executor(None, self.pool.block_till_done) - yield from self.loop.run_in_executor(None, self.pool.stop) + if self.pool is not None: + yield from self.loop.run_in_executor( + None, self.pool.block_till_done) + yield from self.loop.run_in_executor(None, self.pool.stop) self.executor.shutdown() if self._websession is not None: yield from self._websession.close() @@ -337,6 +319,12 @@ class HomeAssistant(object): exc_info=exc_info ) + @callback + def async_init_pool(self): + """Initialize the worker pool.""" + self.pool = create_worker_pool() + _async_monitor_worker_pool(self) + @callback def _async_stop_handler(self, *args): """Stop Home Assistant.""" @@ -867,10 +855,10 @@ class ServiceCall(object): class ServiceRegistry(object): """Offers services over the eventbus.""" - def __init__(self, bus, add_job, loop): + def __init__(self, bus, async_add_job, loop): """Initialize a service registry.""" self._services = {} - self._add_job = add_job + self._async_add_job = async_add_job self._bus = bus self._loop = loop self._cur_id = 0 @@ -1073,7 +1061,7 @@ class ServiceRegistry(object): service_handler.func(service_call) fire_service_executed() - self._add_job(execute_service, priority=JobPriority.EVENT_SERVICE) + self._async_add_job(execute_service) def _generate_unique_id(self): """Generate a unique service call id.""" diff --git a/homeassistant/util/__init__.py b/homeassistant/util/__init__.py index 1f5a285a117d..69ff5d7a61f8 100644 --- a/homeassistant/util/__init__.py +++ b/homeassistant/util/__init__.py @@ -319,7 +319,7 @@ class ThreadPool(object): self._job_handler = job_handler self.worker_count = 0 - self._work_queue = queue.PriorityQueue() + self._work_queue = queue.Queue() self.current_jobs = [] self._quit_task = object() @@ -349,24 +349,24 @@ class ThreadPool(object): if not self.running: raise RuntimeError("ThreadPool not running") - self._work_queue.put(PriorityQueueItem(0, self._quit_task)) + self._work_queue.put(self._quit_task) self.worker_count -= 1 - def add_job(self, priority, job): + def add_job(self, job): """Add a job to the queue.""" if not self.running: raise RuntimeError("ThreadPool not running") - self._work_queue.put(PriorityQueueItem(priority, job)) + self._work_queue.put(job) def add_many_jobs(self, jobs): """Add a list of jobs to the queue.""" if not self.running: raise RuntimeError("ThreadPool not running") - for priority, job in jobs: - self._work_queue.put(PriorityQueueItem(priority, job)) + for job in jobs: + self._work_queue.put(job) def block_till_done(self): """Block till current work is done.""" @@ -392,7 +392,7 @@ class ThreadPool(object): """Handle jobs for the thread pool.""" while True: # Get new item from work_queue - job = self._work_queue.get().item + job = self._work_queue.get() if job is self._quit_task: self._work_queue.task_done() @@ -410,16 +410,3 @@ class ThreadPool(object): # Tell work_queue the task is done self._work_queue.task_done() - - -class PriorityQueueItem(object): - """Holds a priority and a value. Used within PriorityQueue.""" - - def __init__(self, priority, item): - """Initialize the queue.""" - self.priority = priority - self.item = item - - def __lt__(self, other): - """Return the ordering.""" - return self.priority < other.priority diff --git a/tests/common.py b/tests/common.py index 275beb6be94a..af65a93f2167 100644 --- a/tests/common.py +++ b/tests/common.py @@ -31,18 +31,12 @@ def get_test_config_dir(*add_path): return os.path.join(os.path.dirname(__file__), "testing_config", *add_path) -def get_test_home_assistant(num_threads=None): +def get_test_home_assistant(): """Return a Home Assistant object pointing at test config dir.""" loop = asyncio.new_event_loop() - if num_threads: - orig_num_threads = ha.MIN_WORKER_THREAD - ha.MIN_WORKER_THREAD = num_threads - hass = loop.run_until_complete(async_test_home_assistant(loop)) - - if num_threads: - ha.MIN_WORKER_THREAD = orig_num_threads + hass.allow_pool = True # FIXME should not be a daemon. Means hass.stop() not called in teardown stop_event = threading.Event() @@ -60,17 +54,10 @@ def get_test_home_assistant(num_threads=None): orig_start = hass.start orig_stop = hass.stop - @asyncio.coroutine - def fake_stop(): - """Fake stop.""" - yield None - - @patch.object(ha, '_async_create_timer') - @patch.object(ha, '_async_monitor_worker_pool') @patch.object(hass.loop, 'add_signal_handler') + @patch.object(ha, '_async_create_timer') @patch.object(hass.loop, 'run_forever') @patch.object(hass.loop, 'close') - @patch.object(hass, 'async_stop', return_value=fake_stop()) def start_hass(*mocks): """Helper to start hass.""" orig_start() @@ -108,6 +95,20 @@ def async_test_home_assistant(loop): hass.state = ha.CoreState.running + hass.allow_pool = False + orig_init = hass.async_init_pool + + @ha.callback + def mock_async_init_pool(): + """Prevent worker pool from being initialized.""" + if hass.allow_pool: + with patch('homeassistant.core._async_monitor_worker_pool'): + orig_init() + else: + assert False, 'Thread pool not allowed. Set hass.allow_pool = True' + + hass.async_init_pool = mock_async_init_pool + return hass @@ -225,7 +226,8 @@ class MockModule(object): # pylint: disable=invalid-name def __init__(self, domain=None, dependencies=None, setup=None, - requirements=None, config_schema=None, platform_schema=None): + requirements=None, config_schema=None, platform_schema=None, + async_setup=None): """Initialize the mock module.""" self.DOMAIN = domain self.DEPENDENCIES = dependencies or [] @@ -238,8 +240,15 @@ class MockModule(object): if platform_schema is not None: self.PLATFORM_SCHEMA = platform_schema + if async_setup is not None: + self.async_setup = async_setup + def setup(self, hass, config): - """Setup the component.""" + """Setup the component. + + We always define this mock because MagicMock setups will be seen by the + executor as a coroutine, raising an exception. + """ if self._setup is not None: return self._setup(hass, config) return True diff --git a/tests/components/camera/test_generic.py b/tests/components/camera/test_generic.py index e2ce9c159360..fde4bb2fbd40 100644 --- a/tests/components/camera/test_generic.py +++ b/tests/components/camera/test_generic.py @@ -8,6 +8,7 @@ from homeassistant.bootstrap import setup_component @asyncio.coroutine def test_fetching_url(aioclient_mock, hass, test_client): """Test that it fetches the given url.""" + hass.allow_pool = True aioclient_mock.get('http://example.com', text='hello world') def setup_platform(): @@ -39,6 +40,7 @@ def test_fetching_url(aioclient_mock, hass, test_client): @asyncio.coroutine def test_limit_refetch(aioclient_mock, hass, test_client): """Test that it fetches the given url.""" + hass.allow_pool = True aioclient_mock.get('http://example.com/5a', text='hello world') aioclient_mock.get('http://example.com/10a', text='hello world') aioclient_mock.get('http://example.com/15a', text='hello planet') diff --git a/tests/components/camera/test_local_file.py b/tests/components/camera/test_local_file.py index d43c138c5705..9a692b0a4ee4 100644 --- a/tests/components/camera/test_local_file.py +++ b/tests/components/camera/test_local_file.py @@ -14,6 +14,8 @@ from tests.common import assert_setup_component, mock_http_component @asyncio.coroutine def test_loading_file(hass, test_client): """Test that it loads image from disk.""" + hass.allow_pool = True + @mock.patch('os.path.isfile', mock.Mock(return_value=True)) @mock.patch('os.access', mock.Mock(return_value=True)) def setup_platform(): diff --git a/tests/components/climate/test_demo.py b/tests/components/climate/test_demo.py index aa94bdf63c92..04fc2e332477 100644 --- a/tests/components/climate/test_demo.py +++ b/tests/components/climate/test_demo.py @@ -86,7 +86,7 @@ class TestDemoClimate(unittest.TestCase): self.assertEqual(24.0, state.attributes.get('target_temp_high')) climate.set_temperature(self.hass, target_temp_high=25, target_temp_low=20, entity_id=ENTITY_ECOBEE) - self.hass.pool.block_till_done() + self.hass.block_till_done() state = self.hass.states.get(ENTITY_ECOBEE) self.assertEqual(None, state.attributes.get('temperature')) self.assertEqual(20.0, state.attributes.get('target_temp_low')) @@ -102,7 +102,7 @@ class TestDemoClimate(unittest.TestCase): climate.set_temperature(self.hass, temperature=None, entity_id=ENTITY_ECOBEE, target_temp_low=None, target_temp_high=None) - self.hass.pool.block_till_done() + self.hass.block_till_done() state = self.hass.states.get(ENTITY_ECOBEE) self.assertEqual(None, state.attributes.get('temperature')) self.assertEqual(21.0, state.attributes.get('target_temp_low')) diff --git a/tests/components/cover/test_rfxtrx.py b/tests/components/cover/test_rfxtrx.py index 85ff26145edf..5f6ecd01e4e7 100644 --- a/tests/components/cover/test_rfxtrx.py +++ b/tests/components/cover/test_rfxtrx.py @@ -15,7 +15,7 @@ class TestCoverRfxtrx(unittest.TestCase): def setUp(self): """Setup things to be run when tests are started.""" - self.hass = get_test_home_assistant(0) + self.hass = get_test_home_assistant() self.hass.config.components = ['rfxtrx'] def tearDown(self): diff --git a/tests/components/light/test_demo.py b/tests/components/light/test_demo.py index abb7cc2ac126..759127c75f93 100644 --- a/tests/components/light/test_demo.py +++ b/tests/components/light/test_demo.py @@ -30,7 +30,7 @@ class TestDemoClimate(unittest.TestCase): """Test light state attributes.""" light.turn_on( self.hass, ENTITY_LIGHT, xy_color=(.4, .6), brightness=25) - self.hass.pool.block_till_done() + self.hass.block_till_done() state = self.hass.states.get(ENTITY_LIGHT) self.assertTrue(light.is_on(self.hass, ENTITY_LIGHT)) self.assertEqual((.4, .6), state.attributes.get(light.ATTR_XY_COLOR)) @@ -40,21 +40,21 @@ class TestDemoClimate(unittest.TestCase): light.turn_on( self.hass, ENTITY_LIGHT, rgb_color=(251, 252, 253), white_value=254) - self.hass.pool.block_till_done() + self.hass.block_till_done() state = self.hass.states.get(ENTITY_LIGHT) self.assertEqual(254, state.attributes.get(light.ATTR_WHITE_VALUE)) self.assertEqual( (251, 252, 253), state.attributes.get(light.ATTR_RGB_COLOR)) light.turn_on(self.hass, ENTITY_LIGHT, color_temp=400) - self.hass.pool.block_till_done() + self.hass.block_till_done() state = self.hass.states.get(ENTITY_LIGHT) self.assertEqual(400, state.attributes.get(light.ATTR_COLOR_TEMP)) def test_turn_off(self): """Test light turn off method.""" light.turn_on(self.hass, ENTITY_LIGHT) - self.hass.pool.block_till_done() + self.hass.block_till_done() self.assertTrue(light.is_on(self.hass, ENTITY_LIGHT)) light.turn_off(self.hass, ENTITY_LIGHT) - self.hass.pool.block_till_done() + self.hass.block_till_done() self.assertFalse(light.is_on(self.hass, ENTITY_LIGHT)) diff --git a/tests/components/light/test_rfxtrx.py b/tests/components/light/test_rfxtrx.py index c87e562c4ffa..6a9311b7892b 100644 --- a/tests/components/light/test_rfxtrx.py +++ b/tests/components/light/test_rfxtrx.py @@ -15,7 +15,7 @@ class TestLightRfxtrx(unittest.TestCase): def setUp(self): """Setup things to be run when tests are started.""" - self.hass = get_test_home_assistant(0) + self.hass = get_test_home_assistant() self.hass.config.components = ['rfxtrx'] def tearDown(self): diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index 5b65df9e1daa..9626f1a878b2 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -21,7 +21,7 @@ class TestMQTT(unittest.TestCase): def setUp(self): # pylint: disable=invalid-name """Setup things to be run when tests are started.""" - self.hass = get_test_home_assistant(1) + self.hass = get_test_home_assistant() mock_mqtt_component(self.hass) self.calls = [] @@ -217,7 +217,7 @@ class TestMQTTCallbacks(unittest.TestCase): def setUp(self): # pylint: disable=invalid-name """Setup things to be run when tests are started.""" - self.hass = get_test_home_assistant(1) + self.hass = get_test_home_assistant() # mock_mqtt_component(self.hass) with mock.patch('paho.mqtt.client.Client'): diff --git a/tests/components/notify/test_demo.py b/tests/components/notify/test_demo.py index 3ec00a84bda7..61baabed69fb 100644 --- a/tests/components/notify/test_demo.py +++ b/tests/components/notify/test_demo.py @@ -111,7 +111,7 @@ class TestNotifyDemo(unittest.TestCase): } script.call_from_config(self.hass, conf) - self.hass.pool.block_till_done() + self.hass.block_till_done() self.assertTrue(len(self.events) == 1) assert { 'message': 'Test 123 4', diff --git a/tests/components/sensor/test_imap_email_content.py b/tests/components/sensor/test_imap_email_content.py index 1f0b81ce8eb2..17619f1efa67 100644 --- a/tests/components/sensor/test_imap_email_content.py +++ b/tests/components/sensor/test_imap_email_content.py @@ -178,7 +178,7 @@ class EmailContentSensor(unittest.TestCase): sensor.entity_id = "sensor.emailtest" sensor.update() - self.hass.pool.block_till_done() + self.hass.block_till_done() states_received.wait(5) self.assertEqual("Test Message", states[0].state) diff --git a/tests/components/sensor/test_random.py b/tests/components/sensor/test_random.py index 3e66d5003ce4..902edfc3ee4f 100644 --- a/tests/components/sensor/test_random.py +++ b/tests/components/sensor/test_random.py @@ -33,4 +33,4 @@ class TestRandomSensor(unittest.TestCase): state = self.hass.states.get('sensor.test') self.assertLessEqual(int(state.state), config['sensor']['maximum']) - self.assertGreater(int(state.state), config['sensor']['minimum']) + self.assertGreaterEqual(int(state.state), config['sensor']['minimum']) diff --git a/tests/components/sensor/test_rfxtrx.py b/tests/components/sensor/test_rfxtrx.py index 1de6cf194193..e70f8b5641d5 100644 --- a/tests/components/sensor/test_rfxtrx.py +++ b/tests/components/sensor/test_rfxtrx.py @@ -16,7 +16,7 @@ class TestSensorRfxtrx(unittest.TestCase): def setUp(self): """Setup things to be run when tests are started.""" - self.hass = get_test_home_assistant(0) + self.hass = get_test_home_assistant() self.hass.config.components = ['rfxtrx'] def tearDown(self): diff --git a/tests/components/switch/test_rfxtrx.py b/tests/components/switch/test_rfxtrx.py index b45342336e39..f0d38ca20c35 100644 --- a/tests/components/switch/test_rfxtrx.py +++ b/tests/components/switch/test_rfxtrx.py @@ -15,7 +15,7 @@ class TestSwitchRfxtrx(unittest.TestCase): def setUp(self): """Setup things to be run when tests are started.""" - self.hass = get_test_home_assistant(0) + self.hass = get_test_home_assistant() self.hass.config.components = ['rfxtrx'] def tearDown(self): diff --git a/tests/components/test_conversation.py b/tests/components/test_conversation.py index 454b088dc5a6..1172221f16ff 100644 --- a/tests/components/test_conversation.py +++ b/tests/components/test_conversation.py @@ -19,7 +19,7 @@ class TestConversation(unittest.TestCase): def setUp(self): """Setup things to be run when tests are started.""" self.ent_id = 'light.kitchen_lights' - self.hass = get_test_home_assistant(3) + self.hass = get_test_home_assistant() self.hass.states.set(self.ent_id, 'on') self.assertTrue(run_coroutine_threadsafe( core_components.async_setup(self.hass, {}), self.hass.loop diff --git a/tests/components/test_emulated_hue.py b/tests/components/test_emulated_hue.py index e280ba827ea0..ef4ea7f234ed 100755 --- a/tests/components/test_emulated_hue.py +++ b/tests/components/test_emulated_hue.py @@ -1,8 +1,6 @@ """The tests for the emulated Hue component.""" import time import json -import threading -import asyncio import unittest import requests @@ -372,58 +370,3 @@ class TestEmulatedHueExposedByDefault(unittest.TestCase): url, data=json.dumps(data), timeout=5, headers=req_headers) return result - - -class MQTTBroker(object): - """Encapsulates an embedded MQTT broker.""" - - def __init__(self, host, port): - """Initialize a new instance.""" - from hbmqtt.broker import Broker - - self._loop = asyncio.new_event_loop() - - hbmqtt_config = { - 'listeners': { - 'default': { - 'max-connections': 50000, - 'type': 'tcp', - 'bind': '{}:{}'.format(host, port) - } - }, - 'auth': { - 'plugins': ['auth.anonymous'], - 'allow-anonymous': True - } - } - - self._broker = Broker(config=hbmqtt_config, loop=self._loop) - - self._thread = threading.Thread(target=self._run_loop) - self._started_ev = threading.Event() - - def start(self): - """Start the broker.""" - self._thread.start() - self._started_ev.wait() - - def stop(self): - """Stop the broker.""" - self._loop.call_soon_threadsafe(asyncio.async, self._broker.shutdown()) - self._loop.call_soon_threadsafe(self._loop.stop) - self._thread.join() - - def _run_loop(self): - """Run the loop.""" - asyncio.set_event_loop(self._loop) - self._loop.run_until_complete(self._broker_coroutine()) - - self._started_ev.set() - - self._loop.run_forever() - self._loop.close() - - @asyncio.coroutine - def _broker_coroutine(self): - """The Broker coroutine.""" - yield from self._broker.start() diff --git a/tests/components/test_influxdb.py b/tests/components/test_influxdb.py index b0517ec2f531..de90e86c0bf7 100644 --- a/tests/components/test_influxdb.py +++ b/tests/components/test_influxdb.py @@ -17,7 +17,7 @@ class TestInfluxDB(unittest.TestCase): def setUp(self): """Setup things to be run when tests are started.""" - self.hass = get_test_home_assistant(2) + self.hass = get_test_home_assistant() self.handler_method = None self.hass.bus.listen = mock.Mock() diff --git a/tests/components/test_logentries.py b/tests/components/test_logentries.py index b7e40f7ebb68..5d3a9d79f97c 100644 --- a/tests/components/test_logentries.py +++ b/tests/components/test_logentries.py @@ -15,7 +15,7 @@ class TestLogentries(unittest.TestCase): def setUp(self): # pylint: disable=invalid-name """Setup things to be run when tests are started.""" - self.hass = get_test_home_assistant(2) + self.hass = get_test_home_assistant() def tearDown(self): # pylint: disable=invalid-name """Stop everything that was started.""" diff --git a/tests/components/test_logger.py b/tests/components/test_logger.py index 6b290eec6389..e4e8c75d1bdb 100644 --- a/tests/components/test_logger.py +++ b/tests/components/test_logger.py @@ -16,7 +16,7 @@ class TestUpdater(unittest.TestCase): def setUp(self): """Setup things to be run when tests are started.""" - self.hass = get_test_home_assistant(2) + self.hass = get_test_home_assistant() self.log_config = {'logger': {'default': 'warning', 'logs': {'test': 'info'}}} diff --git a/tests/components/test_splunk.py b/tests/components/test_splunk.py index 1f6648ce5820..787208503175 100644 --- a/tests/components/test_splunk.py +++ b/tests/components/test_splunk.py @@ -14,7 +14,7 @@ class TestSplunk(unittest.TestCase): def setUp(self): # pylint: disable=invalid-name """Setup things to be run when tests are started.""" - self.hass = get_test_home_assistant(2) + self.hass = get_test_home_assistant() def tearDown(self): # pylint: disable=invalid-name """Stop everything that was started.""" diff --git a/tests/components/test_statsd.py b/tests/components/test_statsd.py index eb8782b582c1..b0cba0e41f97 100644 --- a/tests/components/test_statsd.py +++ b/tests/components/test_statsd.py @@ -17,7 +17,7 @@ class TestStatsd(unittest.TestCase): def setUp(self): # pylint: disable=invalid-name """Setup things to be run when tests are started.""" - self.hass = get_test_home_assistant(2) + self.hass = get_test_home_assistant() def tearDown(self): # pylint: disable=invalid-name """Stop everything that was started.""" diff --git a/tests/test_core.py b/tests/test_core.py index 3c7cfd32ef77..8a9fb8f6d4a6 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -56,7 +56,7 @@ def test_async_add_job_add_threaded_job_to_pool(mock_iscoro): ha.HomeAssistant.async_add_job(hass, job) assert len(hass.loop.call_soon.mock_calls) == 0 assert len(hass.loop.create_task.mock_calls) == 0 - assert len(hass.add_job.mock_calls) == 1 + assert len(hass.pool.add_job.mock_calls) == 1 def test_async_run_job_calls_callback(): @@ -91,7 +91,7 @@ class TestHomeAssistant(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): """Setup things to be run when tests are started.""" - self.hass = get_test_home_assistant(0) + self.hass = get_test_home_assistant() # pylint: disable=invalid-name def tearDown(self): @@ -169,7 +169,6 @@ class TestEventBus(unittest.TestCase): """Setup things to be run when tests are started.""" self.hass = get_test_home_assistant() self.bus = self.hass.bus - self.bus.listen('test_event', lambda x: len) # pylint: disable=invalid-name def tearDown(self): @@ -178,6 +177,7 @@ class TestEventBus(unittest.TestCase): def test_add_remove_listener(self): """Test remove_listener method.""" + self.hass.allow_pool = False old_count = len(self.bus.listeners) def listener(_): pass @@ -195,8 +195,10 @@ class TestEventBus(unittest.TestCase): def test_unsubscribe_listener(self): """Test unsubscribe listener from returned function.""" + self.hass.allow_pool = False calls = [] + @ha.callback def listener(event): """Mock listener.""" calls.append(event) @@ -217,6 +219,7 @@ class TestEventBus(unittest.TestCase): def test_listen_once_event_with_callback(self): """Test listen_once_event method.""" + self.hass.allow_pool = False runs = [] @ha.callback @@ -234,6 +237,7 @@ class TestEventBus(unittest.TestCase): def test_listen_once_event_with_coroutine(self): """Test listen_once_event method.""" + self.hass.allow_pool = False runs = [] @asyncio.coroutine @@ -279,6 +283,7 @@ class TestEventBus(unittest.TestCase): def test_callback_event_listener(self): """Test a event listener listeners.""" + self.hass.allow_pool = False callback_calls = [] @ha.callback @@ -292,6 +297,7 @@ class TestEventBus(unittest.TestCase): def test_coroutine_event_listener(self): """Test a event listener listeners.""" + self.hass.allow_pool = False coroutine_calls = [] @asyncio.coroutine @@ -366,10 +372,11 @@ class TestStateMachine(unittest.TestCase): # pylint: disable=invalid-name def setUp(self): """Setup things to be run when tests are started.""" - self.hass = get_test_home_assistant(0) + self.hass = get_test_home_assistant() self.states = self.hass.states self.states.set("light.Bowl", "on") self.states.set("switch.AC", "off") + self.hass.allow_pool = False # pylint: disable=invalid-name def tearDown(self): @@ -413,8 +420,12 @@ class TestStateMachine(unittest.TestCase): def test_remove(self): """Test remove method.""" events = [] - self.hass.bus.listen(EVENT_STATE_CHANGED, - lambda event: events.append(event)) + + @ha.callback + def callback(event): + events.append(event) + + self.hass.bus.listen(EVENT_STATE_CHANGED, callback) self.assertIn('light.bowl', self.states.entity_ids()) self.assertTrue(self.states.remove('light.bowl')) @@ -436,8 +447,11 @@ class TestStateMachine(unittest.TestCase): """Test insensitivty.""" runs = [] - self.hass.bus.listen(EVENT_STATE_CHANGED, - lambda event: runs.append(event)) + @ha.callback + def callback(event): + runs.append(event) + + self.hass.bus.listen(EVENT_STATE_CHANGED, callback) self.states.set('light.BOWL', 'off') self.hass.block_till_done() @@ -462,7 +476,12 @@ class TestStateMachine(unittest.TestCase): def test_force_update(self): """Test force update option.""" events = [] - self.hass.bus.listen(EVENT_STATE_CHANGED, lambda ev: events.append(ev)) + + @ha.callback + def callback(event): + events.append(event) + + self.hass.bus.listen(EVENT_STATE_CHANGED, callback) self.states.set('light.bowl', 'on') self.hass.block_till_done() @@ -504,6 +523,7 @@ class TestServiceRegistry(unittest.TestCase): def test_has_service(self): """Test has_service method.""" + self.hass.allow_pool = False self.assertTrue( self.services.has_service("tesT_domaiN", "tesT_servicE")) self.assertFalse( @@ -513,6 +533,7 @@ class TestServiceRegistry(unittest.TestCase): def test_services(self): """Test services.""" + self.hass.allow_pool = False expected = { 'test_domain': {'test_service': {'description': '', 'fields': {}}} } @@ -535,6 +556,7 @@ class TestServiceRegistry(unittest.TestCase): def test_call_non_existing_with_blocking(self): """Test non-existing with blocking.""" + self.hass.allow_pool = False prior = ha.SERVICE_CALL_LIMIT try: ha.SERVICE_CALL_LIMIT = 0.01 @@ -545,6 +567,7 @@ class TestServiceRegistry(unittest.TestCase): def test_async_service(self): """Test registering and calling an async service.""" + self.hass.allow_pool = False calls = [] @asyncio.coroutine @@ -561,6 +584,7 @@ class TestServiceRegistry(unittest.TestCase): def test_callback_service(self): """Test registering and calling an async service.""" + self.hass.allow_pool = False calls = [] @ha.callback @@ -629,8 +653,9 @@ class TestWorkerPool(unittest.TestCase): def register_call(_): calls.append(1) - pool.add_job(ha.JobPriority.EVENT_DEFAULT, (malicious_job, None)) - pool.add_job(ha.JobPriority.EVENT_DEFAULT, (register_call, None)) + pool.add_job((malicious_job, None)) + pool.block_till_done() + pool.add_job((register_call, None)) pool.block_till_done() self.assertEqual(1, len(calls))