diff --git a/tests/components/camera/test_local_file.py b/tests/components/camera/test_local_file.py index c29aa4b6da0..0c131b441b5 100644 --- a/tests/components/camera/test_local_file.py +++ b/tests/components/camera/test_local_file.py @@ -1,5 +1,4 @@ """The tests for local file camera component.""" -from tempfile import NamedTemporaryFile import unittest from unittest import mock @@ -26,45 +25,46 @@ class TestLocalCamera(unittest.TestCase): def test_loading_file(self): """Test that it loads image from disk.""" + test_string = 'hello' self.hass.wsgi = mock.MagicMock() - with NamedTemporaryFile() as fptr: - fptr.write('hello'.encode('utf-8')) - fptr.flush() - + with mock.patch('os.path.isfile', mock.Mock(return_value=True)), \ + mock.patch('os.access', mock.Mock(return_value=True)): assert setup_component(self.hass, 'camera', { 'camera': { 'name': 'config_test', 'platform': 'local_file', - 'file_path': fptr.name, + 'file_path': 'mock.file', }}) - image_view = self.hass.wsgi.mock_calls[0][1][0] + image_view = self.hass.wsgi.mock_calls[0][1][0] + m_open = mock.mock_open(read_data=test_string) + with mock.patch( + 'homeassistant.components.camera.local_file.open', + m_open, create=True + ): builder = EnvironBuilder(method='GET') Request = request_class() # pylint: disable=invalid-name request = Request(builder.get_environ()) request.authenticated = True resp = image_view.get(request, 'camera.config_test') - assert resp.status_code == 200, resp.response - assert resp.response[0].decode('utf-8') == 'hello' + assert resp.status_code == 200, resp.response + assert resp.response[0].decode('utf-8') == test_string def test_file_not_readable(self): """Test local file will not setup when file is not readable.""" self.hass.wsgi = mock.MagicMock() - with NamedTemporaryFile() as fptr: - fptr.write('hello'.encode('utf-8')) - fptr.flush() + with mock.patch('os.path.isfile', mock.Mock(return_value=True)), \ + mock.patch('os.access', return_value=False), \ + assert_setup_component(0): + assert setup_component(self.hass, 'camera', { + 'camera': { + 'name': 'config_test', + 'platform': 'local_file', + 'file_path': 'mock.file', + }}) - with mock.patch('os.access', return_value=False), \ - assert_setup_component(0): - assert setup_component(self.hass, 'camera', { - 'camera': { - 'name': 'config_test', - 'platform': 'local_file', - 'file_path': fptr.name, - }}) - - assert [] == self.hass.states.all() + assert [] == self.hass.states.all() diff --git a/tests/components/mqtt/test_server.py b/tests/components/mqtt/test_server.py index cf869082f4b..eb7dabb28b3 100644 --- a/tests/components/mqtt/test_server.py +++ b/tests/components/mqtt/test_server.py @@ -1,5 +1,5 @@ """The tests for the MQTT component embedded server.""" -from unittest.mock import MagicMock, patch +from unittest.mock import Mock, MagicMock, patch from homeassistant.bootstrap import _setup_component import homeassistant.components.mqtt as mqtt @@ -18,14 +18,12 @@ class TestMQTT: """Stop everything that was started.""" self.hass.stop() - @patch('passlib.apps.custom_app_context', return_value='') - @patch('tempfile.NamedTemporaryFile', return_value=MagicMock()) + @patch('passlib.apps.custom_app_context', Mock(return_value='')) + @patch('tempfile.NamedTemporaryFile', Mock(return_value=MagicMock())) + @patch('asyncio.new_event_loop', Mock()) @patch('homeassistant.components.mqtt.MQTT') @patch('asyncio.gather') - @patch('asyncio.new_event_loop') - def test_creating_config_with_http_pass(self, mock_new_loop, mock_gather, - mock_mqtt, mock_temp, - mock_context): + def test_creating_config_with_http_pass(self, mock_gather, mock_mqtt): """Test if the MQTT server gets started and subscribe/publish msg.""" self.hass.config.components.append('http') password = 'super_secret' @@ -45,10 +43,10 @@ class TestMQTT: assert mock_mqtt.mock_calls[0][1][5] is None assert mock_mqtt.mock_calls[0][1][6] is None - @patch('tempfile.NamedTemporaryFile', return_value=MagicMock()) + @patch('tempfile.NamedTemporaryFile', Mock(return_value=MagicMock())) + @patch('asyncio.new_event_loop', Mock()) @patch('asyncio.gather') - @patch('asyncio.new_event_loop') - def test_broker_config_fails(self, mock_new_loop, mock_gather, mock_temp): + def test_broker_config_fails(self, mock_gather): """Test if the MQTT component fails if server fails.""" self.hass.config.components.append('http') from hbmqtt.broker import BrokerException diff --git a/tests/components/notify/test_demo.py b/tests/components/notify/test_demo.py index a0d9f28fe1a..3ec00a84bda 100644 --- a/tests/components/notify/test_demo.py +++ b/tests/components/notify/test_demo.py @@ -1,12 +1,10 @@ """The tests for the notify demo platform.""" -import tempfile import unittest from homeassistant.bootstrap import setup_component import homeassistant.components.notify as notify from homeassistant.components.notify import demo from homeassistant.helpers import script -from homeassistant.util import yaml from tests.common import get_test_home_assistant @@ -70,21 +68,18 @@ class TestNotifyDemo(unittest.TestCase): def test_calling_notify_from_script_loaded_from_yaml_without_title(self): """Test if we can call a notify from a script.""" - yaml_conf = """ -service: notify.notify -data: - data: - push: - sound: US-EN-Morgan-Freeman-Roommate-Is-Arriving.wav -data_template: - message: > - Test 123 {{ 2 + 2 }} -""" - - with tempfile.NamedTemporaryFile() as fp: - fp.write(yaml_conf.encode('utf-8')) - fp.flush() - conf = yaml.load_yaml(fp.name) + conf = { + 'service': 'notify.notify', + 'data': { + 'data': { + 'push': { + 'sound': + 'US-EN-Morgan-Freeman-Roommate-Is-Arriving.wav' + } + } + }, + 'data_template': {'message': 'Test 123 {{ 2 + 2 }}\n'}, + } script.call_from_config(self.hass, conf) self.hass.block_till_done() @@ -99,22 +94,21 @@ data_template: def test_calling_notify_from_script_loaded_from_yaml_with_title(self): """Test if we can call a notify from a script.""" - yaml_conf = """ -service: notify.notify -data: - data: - push: - sound: US-EN-Morgan-Freeman-Roommate-Is-Arriving.wav -data_template: - title: Test - message: > - Test 123 {{ 2 + 2 }} -""" - - with tempfile.NamedTemporaryFile() as fp: - fp.write(yaml_conf.encode('utf-8')) - fp.flush() - conf = yaml.load_yaml(fp.name) + conf = { + 'service': 'notify.notify', + 'data': { + 'data': { + 'push': { + 'sound': + 'US-EN-Morgan-Freeman-Roommate-Is-Arriving.wav' + } + } + }, + 'data_template': { + 'message': 'Test 123 {{ 2 + 2 }}\n', + 'title': 'Test' + } + } script.call_from_config(self.hass, conf) self.hass.pool.block_till_done() diff --git a/tests/components/notify/test_file.py b/tests/components/notify/test_file.py index eaca5c3d962..f63d16a5711 100644 --- a/tests/components/notify/test_file.py +++ b/tests/components/notify/test_file.py @@ -1,8 +1,7 @@ """The tests for the notify file platform.""" import os import unittest -import tempfile -from unittest.mock import patch +from unittest.mock import call, mock_open, patch from homeassistant.bootstrap import setup_component import homeassistant.components.notify as notify @@ -34,13 +33,19 @@ class TestNotifyFile(unittest.TestCase): }, })) + @patch('homeassistant.components.notify.file.os.stat') @patch('homeassistant.util.dt.utcnow') - def test_notify_file(self, mock_utcnow): + def test_notify_file(self, mock_utcnow, mock_stat): """Test the notify file output.""" mock_utcnow.return_value = dt_util.as_utc(dt_util.now()) + mock_stat.return_value.st_size = 0 - with tempfile.TemporaryDirectory() as tempdirname: - filename = os.path.join(tempdirname, 'notify.txt') + m_open = mock_open() + with patch( + 'homeassistant.components.notify.file.open', + m_open, create=True + ): + filename = 'mock_file' message = 'one, two, testing, testing' self.assertTrue(setup_component(self.hass, notify.DOMAIN, { 'notify': { @@ -58,5 +63,12 @@ class TestNotifyFile(unittest.TestCase): self.hass.services.call('notify', 'test', {'message': message}, blocking=True) - result = open(filename).read() - self.assertEqual(result, "{}{}\n".format(title, message)) + full_filename = os.path.join(self.hass.config.path(), filename) + self.assertEqual(m_open.call_count, 1) + self.assertEqual(m_open.call_args, call(full_filename, 'a')) + + self.assertEqual(m_open.return_value.write.call_count, 2) + self.assertEqual( + m_open.return_value.write.call_args_list, + [call(title), call(message + '\n')] + ) diff --git a/tests/components/test_api.py b/tests/components/test_api.py index ca494305073..78affc70648 100644 --- a/tests/components/test_api.py +++ b/tests/components/test_api.py @@ -2,10 +2,9 @@ # pylint: disable=protected-access,too-many-public-methods from contextlib import closing import json -import tempfile import time import unittest -from unittest.mock import patch +from unittest.mock import Mock, patch import requests @@ -244,15 +243,20 @@ class TestAPI(unittest.TestCase): def test_api_get_error_log(self): """Test the return of the error log.""" - test_content = 'Test String°' - with tempfile.NamedTemporaryFile() as log: - log.write(test_content.encode('utf-8')) - log.flush() + test_string = 'Test String°'.encode('UTF-8') - with patch.object(hass.config, 'path', return_value=log.name): - req = requests.get(_url(const.URL_API_ERROR_LOG), - headers=HA_HEADERS) - self.assertEqual(test_content, req.text) + # Can't use read_data with wsgiserver in Python 3.4.2. Due to a + # bug in read_data, it can't handle byte types ('Type str doesn't + # support the buffer API'), but wsgiserver requires byte types + # ('WSGI Applications must yield bytes'). So just mock our own + # read method. + m_open = Mock(return_value=Mock( + read=Mock(side_effect=[test_string])) + ) + with patch('homeassistant.components.http.open', m_open, create=True): + req = requests.get(_url(const.URL_API_ERROR_LOG), + headers=HA_HEADERS) + self.assertEqual(test_string, req.text.encode('UTF-8')) self.assertIsNone(req.headers.get('expires')) def test_api_get_event_listeners(self): diff --git a/tests/components/test_init.py b/tests/components/test_init.py index 76878432ecd..44a60ee986f 100644 --- a/tests/components/test_init.py +++ b/tests/components/test_init.py @@ -1,8 +1,7 @@ """The testd for Core components.""" # pylint: disable=protected-access,too-many-public-methods import unittest -from unittest.mock import patch -from tempfile import TemporaryDirectory +from unittest.mock import patch, Mock import yaml @@ -13,7 +12,8 @@ from homeassistant.const import ( import homeassistant.components as comps from homeassistant.helpers import entity -from tests.common import get_test_home_assistant, mock_service +from tests.common import ( + get_test_home_assistant, mock_service, patch_yaml_files) class TestComponentsCore(unittest.TestCase): @@ -89,6 +89,7 @@ class TestComponentsCore(unittest.TestCase): ('sensor', 'turn_on', {'entity_id': ['sensor.bla']}, False), mock_call.call_args_list[1][0]) + @patch('homeassistant.config.os.path.isfile', Mock(return_value=True)) def test_reload_core_conf(self): """Test reload core conf service.""" ent = entity.Entity() @@ -101,23 +102,20 @@ class TestComponentsCore(unittest.TestCase): assert state.state == 'unknown' assert state.attributes == {} - with TemporaryDirectory() as conf_dir: - self.hass.config.config_dir = conf_dir - conf_yaml = self.hass.config.path(config.YAML_CONFIG_FILE) - - with open(conf_yaml, 'a') as fp: - fp.write(yaml.dump({ - ha.DOMAIN: { - 'latitude': 10, - 'longitude': 20, - 'customize': { - 'test.Entity': { - 'hello': 'world' - } + files = { + config.YAML_CONFIG_FILE: yaml.dump({ + ha.DOMAIN: { + 'latitude': 10, + 'longitude': 20, + 'customize': { + 'test.Entity': { + 'hello': 'world' } } - })) - + } + }) + } + with patch_yaml_files(files, True): comps.reload_core_config(self.hass) self.hass.block_till_done() @@ -131,17 +129,15 @@ class TestComponentsCore(unittest.TestCase): assert state.state == 'unknown' assert state.attributes.get('hello') == 'world' + @patch('homeassistant.config.os.path.isfile', Mock(return_value=True)) @patch('homeassistant.components._LOGGER.error') @patch('homeassistant.config.process_ha_core_config') def test_reload_core_with_wrong_conf(self, mock_process, mock_error): """Test reload core conf service.""" - with TemporaryDirectory() as conf_dir: - self.hass.config.config_dir = conf_dir - conf_yaml = self.hass.config.path(config.YAML_CONFIG_FILE) - - with open(conf_yaml, 'a') as fp: - fp.write(yaml.dump(['invalid', 'config'])) - + files = { + config.YAML_CONFIG_FILE: yaml.dump(['invalid', 'config']) + } + with patch_yaml_files(files, True): comps.reload_core_config(self.hass) self.hass.block_till_done() diff --git a/tests/components/test_panel_custom.py b/tests/components/test_panel_custom.py index 6a41706db98..1ef12161bcb 100644 --- a/tests/components/test_panel_custom.py +++ b/tests/components/test_panel_custom.py @@ -1,9 +1,8 @@ """The tests for the panel_custom component.""" import os import shutil -from tempfile import NamedTemporaryFile import unittest -from unittest.mock import patch +from unittest.mock import Mock, patch from homeassistant import bootstrap from homeassistant.components import panel_custom @@ -47,31 +46,40 @@ class TestPanelCustom(unittest.TestCase): @patch('homeassistant.components.panel_custom.register_panel') def test_webcomponent_custom_path(self, mock_register, _mock_setup): """Test if a web component is found in config panels dir.""" - with NamedTemporaryFile() as fp: - config = { - 'panel_custom': { - 'name': 'todomvc', - 'webcomponent_path': fp.name, - 'sidebar_title': 'Sidebar Title', - 'sidebar_icon': 'mdi:iconicon', - 'url_path': 'nice_url', - 'config': 5, - } - } + filename = 'mock.file' - with patch('os.path.isfile', return_value=False): - assert not bootstrap.setup_component(self.hass, 'panel_custom', - config) - assert not mock_register.called - - assert bootstrap.setup_component(self.hass, 'panel_custom', config) - assert mock_register.called - args = mock_register.mock_calls[0][1] - kwargs = mock_register.mock_calls[0][2] - assert args == (self.hass, 'todomvc', fp.name) - assert kwargs == { - 'config': 5, - 'url_path': 'nice_url', + config = { + 'panel_custom': { + 'name': 'todomvc', + 'webcomponent_path': filename, + 'sidebar_title': 'Sidebar Title', 'sidebar_icon': 'mdi:iconicon', - 'sidebar_title': 'Sidebar Title' + 'url_path': 'nice_url', + 'config': 5, } + } + + with patch('os.path.isfile', Mock(return_value=False)): + assert not bootstrap.setup_component( + self.hass, 'panel_custom', config + ) + assert not mock_register.called + + with patch('os.path.isfile', Mock(return_value=True)): + with patch('os.access', Mock(return_value=True)): + assert bootstrap.setup_component( + self.hass, 'panel_custom', config + ) + + assert mock_register.called + + args = mock_register.mock_calls[0][1] + assert args == (self.hass, 'todomvc', filename) + + kwargs = mock_register.mock_calls[0][2] + assert kwargs == { + 'config': 5, + 'url_path': 'nice_url', + 'sidebar_icon': 'mdi:iconicon', + 'sidebar_title': 'Sidebar Title' + } diff --git a/tests/helpers/test_config_validation.py b/tests/helpers/test_config_validation.py index 9f929244888..287219aa669 100644 --- a/tests/helpers/test_config_validation.py +++ b/tests/helpers/test_config_validation.py @@ -3,10 +3,10 @@ from collections import OrderedDict from datetime import timedelta import enum import os -import tempfile import pytest import voluptuous as vol +from unittest.mock import Mock, patch import homeassistant.helpers.config_validation as cv @@ -68,18 +68,18 @@ def test_isfile(): """Validate that the value is an existing file.""" schema = vol.Schema(cv.isfile) - with tempfile.NamedTemporaryFile() as fp: - pass + fake_file = 'this-file-does-not.exist' + assert not os.path.isfile(fake_file) - for value in ('invalid', None, -1, 0, 80000, fp.name): + for value in ('invalid', None, -1, 0, 80000, fake_file): with pytest.raises(vol.Invalid): schema(value) - with tempfile.TemporaryDirectory() as tmp_path: - tmp_file = os.path.join(tmp_path, "test.txt") - with open(tmp_file, "w") as tmp_handl: - tmp_handl.write("test file") - schema(tmp_file) + # patching methods that allow us to fake a file existing + # with write access + with patch('os.path.isfile', Mock(return_value=True)), \ + patch('os.access', Mock(return_value=True)): + schema('test.txt') def test_url(): diff --git a/tests/test_bootstrap.py b/tests/test_bootstrap.py index d3f3caf795c..c84c95f396c 100644 --- a/tests/test_bootstrap.py +++ b/tests/test_bootstrap.py @@ -1,6 +1,5 @@ """Test the bootstrapping.""" # pylint: disable=too-many-public-methods,protected-access -import tempfile from unittest import mock import threading import logging @@ -12,7 +11,8 @@ import homeassistant.util.dt as dt_util from homeassistant.helpers.config_validation import PLATFORM_SCHEMA from tests.common import \ - get_test_home_assistant, MockModule, MockPlatform, assert_setup_component + get_test_home_assistant, MockModule, MockPlatform, \ + assert_setup_component, patch_yaml_files ORIG_TIMEZONE = dt_util.DEFAULT_TIME_ZONE @@ -45,17 +45,27 @@ class TestBootstrap: self.hass.stop() loader._COMPONENT_CACHE = self.backup_cache + @mock.patch( + # prevent .HA_VERISON file from being written + 'homeassistant.bootstrap.conf_util.process_ha_config_upgrade', + mock.Mock() + ) @mock.patch('homeassistant.util.location.detect_location_info', return_value=None) def test_from_config_file(self, mock_detect): """Test with configuration file.""" components = ['browser', 'conversation', 'script'] - with tempfile.NamedTemporaryFile() as fp: - for comp in components: - fp.write('{}:\n'.format(comp).encode('utf-8')) - fp.flush() + files = { + 'config.yaml': ''.join( + '{}:\n'.format(comp) + for comp in components + ) + } - self.hass = bootstrap.from_config_file(fp.name) + with mock.patch('os.path.isfile', mock.Mock(return_value=True)), \ + mock.patch('os.access', mock.Mock(return_value=True)), \ + patch_yaml_files(files, True): + self.hass = bootstrap.from_config_file('config.yaml') components.append('group') assert sorted(components) == sorted(self.hass.config.components) diff --git a/tests/test_config.py b/tests/test_config.py index 1512a7688ea..4787da5bcde 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,7 +1,6 @@ """Test config utils.""" # pylint: disable=too-many-public-methods,protected-access import os -import tempfile import unittest import unittest.mock as mock @@ -212,49 +211,56 @@ class TestConfig(unittest.TestCase): assert state.attributes['hidden'] - def test_remove_lib_on_upgrade(self): + @mock.patch('homeassistant.config.shutil') + @mock.patch('homeassistant.config.os') + def test_remove_lib_on_upgrade(self, mock_os, mock_shutil): """Test removal of library on upgrade.""" - with tempfile.TemporaryDirectory() as config_dir: - version_path = os.path.join(config_dir, '.HA_VERSION') - lib_dir = os.path.join(config_dir, 'deps') - check_file = os.path.join(lib_dir, 'check') + ha_version = '0.7.0' - with open(version_path, 'wt') as outp: - outp.write('0.7.0') + mock_os.path.isdir = mock.Mock(return_value=True) - os.mkdir(lib_dir) - - with open(check_file, 'w'): - pass + mock_open = mock.mock_open() + with mock.patch('homeassistant.config.open', mock_open, create=True): + opened_file = mock_open.return_value + opened_file.readline.return_value = ha_version self.hass = get_test_home_assistant() - self.hass.config.config_dir = config_dir + self.hass.config.path = mock.Mock() - assert os.path.isfile(check_file) config_util.process_ha_config_upgrade(self.hass) - assert not os.path.isfile(check_file) - def test_not_remove_lib_if_not_upgrade(self): + hass_path = self.hass.config.path.return_value + + self.assertEqual(mock_os.path.isdir.call_count, 1) + self.assertEqual( + mock_os.path.isdir.call_args, mock.call(hass_path) + ) + + self.assertEqual(mock_shutil.rmtree.call_count, 1) + self.assertEqual( + mock_shutil.rmtree.call_args, mock.call(hass_path) + ) + + @mock.patch('homeassistant.config.shutil') + @mock.patch('homeassistant.config.os') + def test_not_remove_lib_if_not_upgrade(self, mock_os, mock_shutil): """Test removal of library with no upgrade.""" - with tempfile.TemporaryDirectory() as config_dir: - version_path = os.path.join(config_dir, '.HA_VERSION') - lib_dir = os.path.join(config_dir, 'deps') - check_file = os.path.join(lib_dir, 'check') + ha_version = __version__ - with open(version_path, 'wt') as outp: - outp.write(__version__) + mock_os.path.isdir = mock.Mock(return_value=True) - os.mkdir(lib_dir) - - with open(check_file, 'w'): - pass + mock_open = mock.mock_open() + with mock.patch('homeassistant.config.open', mock_open, create=True): + opened_file = mock_open.return_value + opened_file.readline.return_value = ha_version self.hass = get_test_home_assistant() - self.hass.config.config_dir = config_dir + self.hass.config.path = mock.Mock() config_util.process_ha_config_upgrade(self.hass) - assert os.path.isfile(check_file) + assert mock_os.path.isdir.call_count == 0 + assert mock_shutil.rmtree.call_count == 0 def test_loading_configuration(self): """Test loading core config onto hass object.""" diff --git a/tests/util/test_package.py b/tests/util/test_package.py index 3aa742516e4..d7af4f2d6a3 100644 --- a/tests/util/test_package.py +++ b/tests/util/test_package.py @@ -1,9 +1,12 @@ """Test Home Assistant package util methods.""" import os -import tempfile +import pkg_resources +import subprocess import unittest -from homeassistant.bootstrap import mount_local_lib_path +from distutils.sysconfig import get_python_lib +from unittest.mock import call, patch + import homeassistant.util.package as package RESOURCE_DIR = os.path.abspath( @@ -15,43 +18,114 @@ TEST_ZIP_REQ = 'file://{}#{}' \ .format(os.path.join(RESOURCE_DIR, 'pyhelloworld3.zip'), TEST_NEW_REQ) -class TestPackageUtil(unittest.TestCase): +@patch('homeassistant.util.package.subprocess.call') +@patch('homeassistant.util.package.check_package_exists') +class TestPackageUtilInstallPackage(unittest.TestCase): """Test for homeassistant.util.package module.""" - def setUp(self): - """Create local library for testing.""" - self.tmp_dir = tempfile.TemporaryDirectory() - self.lib_dir = mount_local_lib_path(self.tmp_dir.name) - - def tearDown(self): - """Stop everything that was started.""" - self.tmp_dir.cleanup() - - def test_install_existing_package(self): + def test_install_existing_package(self, mock_exists, mock_subprocess): """Test an install attempt on an existing package.""" - self.assertTrue(package.check_package_exists( - TEST_EXIST_REQ, self.lib_dir)) + mock_exists.return_value = True self.assertTrue(package.install_package(TEST_EXIST_REQ)) - def test_install_package_zip(self): - """Test an install attempt from a zip path.""" - self.assertFalse(package.check_package_exists( - TEST_ZIP_REQ, self.lib_dir)) - self.assertFalse(package.check_package_exists( - TEST_NEW_REQ, self.lib_dir)) + self.assertEqual(mock_exists.call_count, 1) + self.assertEqual(mock_exists.call_args, call(TEST_EXIST_REQ, None)) - self.assertTrue(package.install_package( - TEST_ZIP_REQ, True, self.lib_dir)) + self.assertEqual(mock_subprocess.call_count, 0) - self.assertTrue(package.check_package_exists( - TEST_ZIP_REQ, self.lib_dir)) - self.assertTrue(package.check_package_exists( - TEST_NEW_REQ, self.lib_dir)) + @patch('homeassistant.util.package.sys') + def test_install(self, mock_sys, mock_exists, mock_subprocess): + """Test an install attempt on a package that doesn't exist.""" + mock_exists.return_value = False + mock_subprocess.return_value = 0 - try: - import pyhelloworld3 - except ImportError: - self.fail('Unable to import pyhelloworld3 after installing it.') + self.assertTrue(package.install_package(TEST_NEW_REQ, False)) - self.assertEqual(pyhelloworld3.__version__, '1.0.0') + self.assertEqual(mock_exists.call_count, 1) + + self.assertEqual(mock_subprocess.call_count, 1) + self.assertEqual( + mock_subprocess.call_args, + call([ + mock_sys.executable, '-m', 'pip', + 'install', '--quiet', TEST_NEW_REQ + ]) + ) + + @patch('homeassistant.util.package.sys') + def test_install_upgrade(self, mock_sys, mock_exists, mock_subprocess): + """Test an upgrade attempt on a package.""" + mock_exists.return_value = False + mock_subprocess.return_value = 0 + + self.assertTrue(package.install_package(TEST_NEW_REQ)) + + self.assertEqual(mock_exists.call_count, 1) + + self.assertEqual(mock_subprocess.call_count, 1) + self.assertEqual( + mock_subprocess.call_args, + call([ + mock_sys.executable, '-m', 'pip', 'install', + '--quiet', TEST_NEW_REQ, '--upgrade' + ]) + ) + + @patch('homeassistant.util.package.sys') + def test_install_target(self, mock_sys, mock_exists, mock_subprocess): + """Test an install with a target.""" + target = 'target_folder' + mock_exists.return_value = False + mock_subprocess.return_value = 0 + + self.assertTrue( + package.install_package(TEST_NEW_REQ, False, target=target) + ) + + self.assertEqual(mock_exists.call_count, 1) + + self.assertEqual(mock_subprocess.call_count, 1) + self.assertEqual( + mock_subprocess.call_args, + call([ + mock_sys.executable, '-m', 'pip', 'install', '--quiet', + TEST_NEW_REQ, '--target', os.path.abspath(target) + ]) + ) + + @patch('homeassistant.util.package._LOGGER') + @patch('homeassistant.util.package.sys') + def test_install_error( + self, mock_sys, mock_logger, mock_exists, mock_subprocess + ): + """Test an install with a target.""" + mock_exists.return_value = False + mock_subprocess.side_effect = [subprocess.SubprocessError] + + self.assertFalse(package.install_package(TEST_NEW_REQ)) + + self.assertEqual(mock_logger.exception.call_count, 1) + + +class TestPackageUtilCheckPackageExists(unittest.TestCase): + """Test for homeassistant.util.package module.""" + + def test_check_package_global(self): + """Test for a globally-installed package""" + installed_package = list(pkg_resources.working_set)[0].project_name + + self.assertTrue(package.check_package_exists(installed_package, None)) + + def test_check_package_local(self): + """Test for a locally-installed package""" + lib_dir = get_python_lib() + installed_package = list(pkg_resources.working_set)[0].project_name + + self.assertTrue( + package.check_package_exists(installed_package, lib_dir) + ) + + def test_check_package_zip(self): + """Test for an installed zip package""" + self.assertFalse(package.check_package_exists(TEST_ZIP_REQ, None)) diff --git a/tests/util/test_yaml.py b/tests/util/test_yaml.py index b1214c2ff17..7c7bb0b9255 100644 --- a/tests/util/test_yaml.py +++ b/tests/util/test_yaml.py @@ -2,7 +2,6 @@ import io import unittest import os -import tempfile from unittest.mock import patch from homeassistant.exceptions import HomeAssistantError @@ -68,146 +67,116 @@ class TestYaml(unittest.TestCase): def test_include_yaml(self): """Test include yaml.""" - with tempfile.NamedTemporaryFile() as include_file: - include_file.write(b"value") - include_file.seek(0) - conf = "key: !include {}".format(include_file.name) + with patch_yaml_files({'test.yaml': 'value'}): + conf = 'key: !include test.yaml' with io.StringIO(conf) as file: doc = yaml.yaml.safe_load(file) assert doc["key"] == "value" - def test_include_dir_list(self): + @patch('homeassistant.util.yaml.os.walk') + def test_include_dir_list(self, mock_walk): """Test include dir list yaml.""" - with tempfile.TemporaryDirectory() as include_dir: - file_1 = tempfile.NamedTemporaryFile(dir=include_dir, - suffix=".yaml", delete=False) - file_1.write(b"one") - file_1.close() - file_2 = tempfile.NamedTemporaryFile(dir=include_dir, - suffix=".yaml", delete=False) - file_2.write(b"two") - file_2.close() - conf = "key: !include_dir_list {}".format(include_dir) + mock_walk.return_value = [['/tmp', [], ['one.yaml', 'two.yaml']]] + + with patch_yaml_files({ + '/tmp/one.yaml': 'one', '/tmp/two.yaml': 'two' + }): + conf = "key: !include_dir_list /tmp" with io.StringIO(conf) as file: doc = yaml.yaml.safe_load(file) assert sorted(doc["key"]) == sorted(["one", "two"]) - def test_include_dir_list_recursive(self): + @patch('homeassistant.util.yaml.os.walk') + def test_include_dir_list_recursive(self, mock_walk): """Test include dir recursive list yaml.""" - with tempfile.TemporaryDirectory() as include_dir: - file_0 = tempfile.NamedTemporaryFile(dir=include_dir, - suffix=".yaml", delete=False) - file_0.write(b"zero") - file_0.close() - temp_dir = tempfile.TemporaryDirectory(dir=include_dir) - file_1 = tempfile.NamedTemporaryFile(dir=temp_dir.name, - suffix=".yaml", delete=False) - file_1.write(b"one") - file_1.close() - file_2 = tempfile.NamedTemporaryFile(dir=temp_dir.name, - suffix=".yaml", delete=False) - file_2.write(b"two") - file_2.close() - conf = "key: !include_dir_list {}".format(include_dir) + mock_walk.return_value = [ + ['/tmp', ['tmp2'], ['zero.yaml']], + ['/tmp/tmp2', [], ['one.yaml', 'two.yaml']], + ] + + with patch_yaml_files({ + '/tmp/zero.yaml': 'zero', '/tmp/tmp2/one.yaml': 'one', + '/tmp/tmp2/two.yaml': 'two' + }): + conf = "key: !include_dir_list /tmp" with io.StringIO(conf) as file: doc = yaml.yaml.safe_load(file) assert sorted(doc["key"]) == sorted(["zero", "one", "two"]) - def test_include_dir_named(self): + @patch('homeassistant.util.yaml.os.walk') + def test_include_dir_named(self, mock_walk): """Test include dir named yaml.""" - with tempfile.TemporaryDirectory() as include_dir: - file_1 = tempfile.NamedTemporaryFile(dir=include_dir, - suffix=".yaml", delete=False) - file_1.write(b"one") - file_1.close() - file_2 = tempfile.NamedTemporaryFile(dir=include_dir, - suffix=".yaml", delete=False) - file_2.write(b"two") - file_2.close() - conf = "key: !include_dir_named {}".format(include_dir) - correct = {} - correct[os.path.splitext(os.path.basename(file_1.name))[0]] = "one" - correct[os.path.splitext(os.path.basename(file_2.name))[0]] = "two" + mock_walk.return_value = [['/tmp', [], ['first.yaml', 'second.yaml']]] + + with patch_yaml_files({ + '/tmp/first.yaml': 'one', '/tmp/second.yaml': 'two' + }): + conf = "key: !include_dir_named /tmp" + correct = {'first': 'one', 'second': 'two'} with io.StringIO(conf) as file: doc = yaml.yaml.safe_load(file) assert doc["key"] == correct - def test_include_dir_named_recursive(self): + @patch('homeassistant.util.yaml.os.walk') + def test_include_dir_named_recursive(self, mock_walk): """Test include dir named yaml.""" - with tempfile.TemporaryDirectory() as include_dir: - file_0 = tempfile.NamedTemporaryFile(dir=include_dir, - suffix=".yaml", delete=False) - file_0.write(b"zero") - file_0.close() - temp_dir = tempfile.TemporaryDirectory(dir=include_dir) - file_1 = tempfile.NamedTemporaryFile(dir=temp_dir.name, - suffix=".yaml", delete=False) - file_1.write(b"one") - file_1.close() - file_2 = tempfile.NamedTemporaryFile(dir=temp_dir.name, - suffix=".yaml", delete=False) - file_2.write(b"two") - file_2.close() - conf = "key: !include_dir_named {}".format(include_dir) - correct = {} - correct[os.path.splitext( - os.path.basename(file_0.name))[0]] = "zero" - correct[os.path.splitext(os.path.basename(file_1.name))[0]] = "one" - correct[os.path.splitext(os.path.basename(file_2.name))[0]] = "two" + mock_walk.return_value = [ + ['/tmp', ['tmp2'], ['first.yaml']], + ['/tmp/tmp2', [], ['second.yaml', 'third.yaml']], + ] + + with patch_yaml_files({ + '/tmp/first.yaml': 'one', '/tmp/tmp2/second.yaml': 'two', + '/tmp/tmp2/third.yaml': 'three' + }): + conf = "key: !include_dir_named /tmp" + correct = {'first': 'one', 'second': 'two', 'third': 'three'} with io.StringIO(conf) as file: doc = yaml.yaml.safe_load(file) assert doc["key"] == correct - def test_include_dir_merge_list(self): + @patch('homeassistant.util.yaml.os.walk') + def test_include_dir_merge_list(self, mock_walk): """Test include dir merge list yaml.""" - with tempfile.TemporaryDirectory() as include_dir: - file_1 = tempfile.NamedTemporaryFile(dir=include_dir, - suffix=".yaml", delete=False) - file_1.write(b"- one") - file_1.close() - file_2 = tempfile.NamedTemporaryFile(dir=include_dir, - suffix=".yaml", delete=False) - file_2.write(b"- two\n- three") - file_2.close() - conf = "key: !include_dir_merge_list {}".format(include_dir) + mock_walk.return_value = [['/tmp', [], ['first.yaml', 'second.yaml']]] + + with patch_yaml_files({ + '/tmp/first.yaml': '- one', + '/tmp/second.yaml': '- two\n- three' + }): + conf = "key: !include_dir_merge_list /tmp" with io.StringIO(conf) as file: doc = yaml.yaml.safe_load(file) assert sorted(doc["key"]) == sorted(["one", "two", "three"]) - def test_include_dir_merge_list_recursive(self): + @patch('homeassistant.util.yaml.os.walk') + def test_include_dir_merge_list_recursive(self, mock_walk): """Test include dir merge list yaml.""" - with tempfile.TemporaryDirectory() as include_dir: - file_0 = tempfile.NamedTemporaryFile(dir=include_dir, - suffix=".yaml", delete=False) - file_0.write(b"- zero") - file_0.close() - temp_dir = tempfile.TemporaryDirectory(dir=include_dir) - file_1 = tempfile.NamedTemporaryFile(dir=temp_dir.name, - suffix=".yaml", delete=False) - file_1.write(b"- one") - file_1.close() - file_2 = tempfile.NamedTemporaryFile(dir=temp_dir.name, - suffix=".yaml", delete=False) - file_2.write(b"- two\n- three") - file_2.close() - conf = "key: !include_dir_merge_list {}".format(include_dir) + mock_walk.return_value = [ + ['/tmp', ['tmp2'], ['first.yaml']], + ['/tmp/tmp2', [], ['second.yaml', 'third.yaml']], + ] + + with patch_yaml_files({ + '/tmp/first.yaml': '- one', '/tmp/tmp2/second.yaml': '- two', + '/tmp/tmp2/third.yaml': '- three\n- four' + }): + conf = "key: !include_dir_merge_list /tmp" with io.StringIO(conf) as file: doc = yaml.yaml.safe_load(file) - assert sorted(doc["key"]) == sorted(["zero", "one", "two", - "three"]) + assert sorted(doc["key"]) == sorted(["one", "two", + "three", "four"]) - def test_include_dir_merge_named(self): + @patch('homeassistant.util.yaml.os.walk') + def test_include_dir_merge_named(self, mock_walk): """Test include dir merge named yaml.""" - with tempfile.TemporaryDirectory() as include_dir: - file_1 = tempfile.NamedTemporaryFile(dir=include_dir, - suffix=".yaml", delete=False) - file_1.write(b"key1: one") - file_1.close() - file_2 = tempfile.NamedTemporaryFile(dir=include_dir, - suffix=".yaml", delete=False) - file_2.write(b"key2: two\nkey3: three") - file_2.close() - conf = "key: !include_dir_merge_named {}".format(include_dir) + mock_walk.return_value = [['/tmp', [], ['first.yaml', 'second.yaml']]] + + with patch_yaml_files({ + '/tmp/first.yaml': 'key1: one', + '/tmp/second.yaml': 'key2: two\nkey3: three' + }): + conf = "key: !include_dir_merge_named /tmp" with io.StringIO(conf) as file: doc = yaml.yaml.safe_load(file) assert doc["key"] == { @@ -216,30 +185,27 @@ class TestYaml(unittest.TestCase): "key3": "three" } - def test_include_dir_merge_named_recursive(self): + @patch('homeassistant.util.yaml.os.walk') + def test_include_dir_merge_named_recursive(self, mock_walk): """Test include dir merge named yaml.""" - with tempfile.TemporaryDirectory() as include_dir: - file_0 = tempfile.NamedTemporaryFile(dir=include_dir, - suffix=".yaml", delete=False) - file_0.write(b"key0: zero") - file_0.close() - temp_dir = tempfile.TemporaryDirectory(dir=include_dir) - file_1 = tempfile.NamedTemporaryFile(dir=temp_dir.name, - suffix=".yaml", delete=False) - file_1.write(b"key1: one") - file_1.close() - file_2 = tempfile.NamedTemporaryFile(dir=temp_dir.name, - suffix=".yaml", delete=False) - file_2.write(b"key2: two\nkey3: three") - file_2.close() - conf = "key: !include_dir_merge_named {}".format(include_dir) + mock_walk.return_value = [ + ['/tmp', ['tmp2'], ['first.yaml']], + ['/tmp/tmp2', [], ['second.yaml', 'third.yaml']], + ] + + with patch_yaml_files({ + '/tmp/first.yaml': 'key1: one', + '/tmp/tmp2/second.yaml': 'key2: two', + '/tmp/tmp2/third.yaml': 'key3: three\nkey4: four' + }): + conf = "key: !include_dir_merge_named /tmp" with io.StringIO(conf) as file: doc = yaml.yaml.safe_load(file) assert doc["key"] == { - "key0": "zero", "key1": "one", "key2": "two", - "key3": "three" + "key3": "three", + "key4": "four" } FILES = {}