diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index ff7e73a00f1d0..8b3d3ee6f239d 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -3,7 +3,6 @@ import logging import logging.handlers import os -import shutil import sys from collections import defaultdict from threading import RLock @@ -12,21 +11,15 @@ import homeassistant.components as core_components from homeassistant.components import group, persistent_notification -import homeassistant.config as config_util +import homeassistant.config as conf_util import homeassistant.core as core import homeassistant.helpers.config_validation as cv import homeassistant.loader as loader -import homeassistant.util.dt as date_util -import homeassistant.util.location as loc_util import homeassistant.util.package as pkg_util -from homeassistant.const import ( - CONF_CUSTOMIZE, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, - CONF_TEMPERATURE_UNIT, CONF_TIME_ZONE, EVENT_COMPONENT_LOADED, - TEMP_CELSIUS, TEMP_FAHRENHEIT, PLATFORM_FORMAT, __version__) +from homeassistant.const import EVENT_COMPONENT_LOADED, PLATFORM_FORMAT from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import ( - event_decorators, service, config_per_platform, extract_domain_configs, - entity) + event_decorators, service, config_per_platform, extract_domain_configs) _LOGGER = logging.getLogger(__name__) _SETUP_LOCK = RLock() @@ -208,11 +201,6 @@ def prepare_setup_platform(hass, config, domain, platform_name): return platform -def mount_local_lib_path(config_dir): - """Add local library to Python Path.""" - sys.path.insert(0, os.path.join(config_dir, 'deps')) - - # pylint: disable=too-many-branches, too-many-statements, too-many-arguments def from_config_dict(config, hass=None, config_dir=None, enable_log=True, verbose=False, skip_pip=False, @@ -226,18 +214,17 @@ def from_config_dict(config, hass=None, config_dir=None, enable_log=True, if config_dir is not None: config_dir = os.path.abspath(config_dir) hass.config.config_dir = config_dir - mount_local_lib_path(config_dir) + _mount_local_lib_path(config_dir) core_config = config.get(core.DOMAIN, {}) try: - process_ha_core_config(hass, config_util.CORE_CONFIG_SCHEMA( - core_config)) - except vol.MultipleInvalid as ex: + conf_util.process_ha_core_config(hass, core_config) + except vol.Invalid as ex: cv.log_exception(_LOGGER, ex, 'homeassistant', core_config) return None - process_ha_config_upgrade(hass) + conf_util.process_ha_config_upgrade(hass) if enable_log: enable_logging(hass, verbose, log_rotate_days) @@ -292,12 +279,12 @@ def from_config_file(config_path, hass=None, verbose=False, skip_pip=True, # Set config dir to directory holding config file config_dir = os.path.abspath(os.path.dirname(config_path)) hass.config.config_dir = config_dir - mount_local_lib_path(config_dir) + _mount_local_lib_path(config_dir) enable_logging(hass, verbose, log_rotate_days) try: - config_dict = config_util.load_yaml_config_file(config_path) + config_dict = conf_util.load_yaml_config_file(config_path) except HomeAssistantError: return None @@ -356,100 +343,12 @@ def enable_logging(hass, verbose=False, log_rotate_days=None): 'Unable to setup error log %s (access denied)', err_log_path) -def process_ha_config_upgrade(hass): - """Upgrade config if necessary.""" - version_path = hass.config.path('.HA_VERSION') - - try: - with open(version_path, 'rt') as inp: - conf_version = inp.readline().strip() - except FileNotFoundError: - # Last version to not have this file - conf_version = '0.7.7' - - if conf_version == __version__: - return - - _LOGGER.info('Upgrading config directory from %s to %s', conf_version, - __version__) - - # This was where dependencies were installed before v0.18 - # Probably should keep this around until ~v0.20. - lib_path = hass.config.path('lib') - if os.path.isdir(lib_path): - shutil.rmtree(lib_path) - - lib_path = hass.config.path('deps') - if os.path.isdir(lib_path): - shutil.rmtree(lib_path) - - with open(version_path, 'wt') as outp: - outp.write(__version__) - - -def process_ha_core_config(hass, config): - """Process the [homeassistant] section from the config.""" - hac = hass.config - - def set_time_zone(time_zone_str): - """Helper method to set time zone.""" - if time_zone_str is None: - return - - time_zone = date_util.get_time_zone(time_zone_str) - - if time_zone: - hac.time_zone = time_zone - date_util.set_default_time_zone(time_zone) - else: - _LOGGER.error('Received invalid time zone %s', time_zone_str) - - for key, attr in ((CONF_LATITUDE, 'latitude'), - (CONF_LONGITUDE, 'longitude'), - (CONF_NAME, 'location_name')): - if key in config: - setattr(hac, attr, config[key]) - - if CONF_TIME_ZONE in config: - set_time_zone(config.get(CONF_TIME_ZONE)) - - entity.set_customize(config.get(CONF_CUSTOMIZE)) - - if CONF_TEMPERATURE_UNIT in config: - hac.temperature_unit = config[CONF_TEMPERATURE_UNIT] - - # If we miss some of the needed values, auto detect them - if None not in ( - hac.latitude, hac.longitude, hac.temperature_unit, hac.time_zone): - return - - _LOGGER.warning('Incomplete core config. Auto detecting location and ' - 'temperature unit') - - info = loc_util.detect_location_info() - - if info is None: - _LOGGER.error('Could not detect location information') - return - - if hac.latitude is None and hac.longitude is None: - hac.latitude = info.latitude - hac.longitude = info.longitude - - if hac.temperature_unit is None: - if info.use_fahrenheit: - hac.temperature_unit = TEMP_FAHRENHEIT - else: - hac.temperature_unit = TEMP_CELSIUS - - if hac.location_name is None: - hac.location_name = info.city - - if hac.time_zone is None: - set_time_zone(info.time_zone) - - def _ensure_loader_prepared(hass): """Ensure Home Assistant loader is prepared.""" if not loader.PREPARED: loader.prepare(hass) + + +def _mount_local_lib_path(config_dir): + """Add local library to Python Path.""" + sys.path.insert(0, os.path.join(config_dir, 'deps')) diff --git a/homeassistant/components/__init__.py b/homeassistant/components/__init__.py index d625f9cd3cdf4..38780ed9b28a0 100644 --- a/homeassistant/components/__init__.py +++ b/homeassistant/components/__init__.py @@ -121,16 +121,16 @@ def handle_turn_service(service): def handle_reload_config(call): """Service handler for reloading core config.""" from homeassistant.exceptions import HomeAssistantError - from homeassistant import config, bootstrap + from homeassistant import config as conf_util try: - path = config.find_config_file(hass.config.config_dir) - conf = config.load_yaml_config_file(path) + path = conf_util.find_config_file(hass.config.config_dir) + conf = conf_util.load_yaml_config_file(path) except HomeAssistantError as err: _LOGGER.error(err) return - bootstrap.process_ha_core_config(hass, conf.get(ha.DOMAIN) or {}) + conf_util.process_ha_core_config(hass, conf.get(ha.DOMAIN) or {}) hass.services.register(ha.DOMAIN, SERVICE_RELOAD_CORE_CONFIG, handle_reload_config) diff --git a/homeassistant/components/media_player/cmus.py b/homeassistant/components/media_player/cmus.py index 43ddee3ba027a..308d659e11166 100644 --- a/homeassistant/components/media_player/cmus.py +++ b/homeassistant/components/media_player/cmus.py @@ -17,7 +17,7 @@ CONF_PORT) _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['pycmus>=0.1.0'] +REQUIREMENTS = ['pycmus==0.1.0'] SUPPORT_CMUS = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_TURN_OFF | \ SUPPORT_TURN_ON | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | \ diff --git a/homeassistant/components/sensor/yr.py b/homeassistant/components/sensor/yr.py index 725043c4da838..ddfbc68d974ab 100644 --- a/homeassistant/components/sensor/yr.py +++ b/homeassistant/components/sensor/yr.py @@ -15,7 +15,6 @@ ) from homeassistant.helpers.entity import Entity from homeassistant.util import dt as dt_util -from homeassistant.util import location _LOGGER = logging.getLogger(__name__) @@ -54,16 +53,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the Yr.no sensor.""" latitude = config.get(CONF_LATITUDE, hass.config.latitude) longitude = config.get(CONF_LONGITUDE, hass.config.longitude) - elevation = config.get(CONF_ELEVATION) + elevation = config.get(CONF_ELEVATION, hass.config.elevation or 0) if None in (latitude, longitude): _LOGGER.error("Latitude or longitude not set in Home Assistant config") return False - if elevation is None: - elevation = location.elevation(latitude, - longitude) - coordinates = dict(lat=latitude, lon=longitude, msl=elevation) diff --git a/homeassistant/components/sun.py b/homeassistant/components/sun.py index 791fec791f895..4b2cd10b78178 100644 --- a/homeassistant/components/sun.py +++ b/homeassistant/components/sun.py @@ -12,7 +12,6 @@ from homeassistant.helpers.event import ( track_point_in_utc_time, track_utc_time_change) from homeassistant.util import dt as dt_util -from homeassistant.util import location as location_util from homeassistant.const import CONF_ELEVATION REQUIREMENTS = ['astral==1.2'] @@ -108,7 +107,7 @@ def setup(hass, config): elevation = platform_config.get(CONF_ELEVATION) if elevation is None: - elevation = location_util.elevation(latitude, longitude) + elevation = hass.config.elevation or 0 from astral import Location diff --git a/homeassistant/components/thermostat/eq3btsmart.py b/homeassistant/components/thermostat/eq3btsmart.py index c9bbdaeb0a4a8..a32a64e81a37a 100644 --- a/homeassistant/components/thermostat/eq3btsmart.py +++ b/homeassistant/components/thermostat/eq3btsmart.py @@ -10,7 +10,7 @@ from homeassistant.const import TEMP_CELCIUS from homeassistant.helpers.temperature import convert -REQUIREMENTS = ['bluepy_devices>=0.2.0'] +REQUIREMENTS = ['bluepy_devices==0.2.0'] CONF_MAC = 'mac' CONF_DEVICES = 'devices' diff --git a/homeassistant/config.py b/homeassistant/config.py index e8981e520c8d8..55e97f67c7e43 100644 --- a/homeassistant/config.py +++ b/homeassistant/config.py @@ -1,31 +1,35 @@ """Module to help with parsing and generating configuration files.""" import logging import os +import shutil from types import MappingProxyType import voluptuous as vol -import homeassistant.util.location as loc_util from homeassistant.const import ( CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, CONF_TEMPERATURE_UNIT, - CONF_TIME_ZONE, CONF_CUSTOMIZE) + CONF_TIME_ZONE, CONF_CUSTOMIZE, CONF_ELEVATION, TEMP_FAHRENHEIT, + TEMP_CELSIUS, __version__) from homeassistant.exceptions import HomeAssistantError from homeassistant.util.yaml import load_yaml import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.entity import valid_entity_id +from homeassistant.helpers.entity import valid_entity_id, set_customize +from homeassistant.util import dt as date_util, location as loc_util _LOGGER = logging.getLogger(__name__) YAML_CONFIG_FILE = 'configuration.yaml' +VERSION_FILE = '.HA_VERSION' CONFIG_DIR_NAME = '.homeassistant' DEFAULT_CONFIG = ( # Tuples (attribute, default, auto detect property, description) (CONF_NAME, 'Home', None, 'Name of the location where Home Assistant is ' 'running'), - (CONF_LATITUDE, None, 'latitude', 'Location required to calculate the time' + (CONF_LATITUDE, 0, 'latitude', 'Location required to calculate the time' ' the sun rises and sets'), - (CONF_LONGITUDE, None, 'longitude', None), + (CONF_LONGITUDE, 0, 'longitude', None), + (CONF_ELEVATION, 0, None, 'Impacts weather/sunrise data'), (CONF_TEMPERATURE_UNIT, 'C', None, 'C for Celsius, F for Fahrenheit'), (CONF_TIME_ZONE, 'UTC', 'time_zone', 'Pick yours from here: http://en.wiki' 'pedia.org/wiki/List_of_tz_database_time_zones'), @@ -39,7 +43,7 @@ 'history:': 'Enables support for tracking state changes over time.', 'logbook:': 'View all events in a logbook', 'sun:': 'Track the sun', - 'sensor:\n platform: yr': 'Prediction of weather', + 'sensor:\n platform: yr': 'Weather Prediction', } @@ -61,6 +65,7 @@ def _valid_customize(value): CONF_NAME: vol.Coerce(str), CONF_LATITUDE: cv.latitude, CONF_LONGITUDE: cv.longitude, + CONF_ELEVATION: vol.Coerce(float), CONF_TEMPERATURE_UNIT: cv.temperature_unit, CONF_TIME_ZONE: cv.time_zone, vol.Required(CONF_CUSTOMIZE, @@ -97,6 +102,7 @@ def create_default_config(config_dir, detect_location=True): Return path to new config file if success, None if failed. """ config_path = os.path.join(config_dir, YAML_CONFIG_FILE) + version_path = os.path.join(config_dir, VERSION_FILE) info = {attr: default for attr, default, _, _ in DEFAULT_CONFIG} @@ -111,6 +117,10 @@ def create_default_config(config_dir, detect_location=True): continue info[attr] = getattr(location_info, prop) or default + if location_info.latitude and location_info.longitude: + info[CONF_ELEVATION] = loc_util.elevation(location_info.latitude, + location_info.longitude) + # Writing files with YAML does not create the most human readable results # So we're hard coding a YAML template. try: @@ -130,6 +140,9 @@ def create_default_config(config_dir, detect_location=True): config_file.write("# {}\n".format(description)) config_file.write("{}\n\n".format(component)) + with open(version_path, 'wt') as version_file: + version_file.write(__version__) + return config_path except IOError: @@ -155,3 +168,112 @@ def load_yaml_config_file(config_path): raise HomeAssistantError(msg) return conf_dict + + +def process_ha_config_upgrade(hass): + """Upgrade config if necessary.""" + version_path = hass.config.path(VERSION_FILE) + + try: + with open(version_path, 'rt') as inp: + conf_version = inp.readline().strip() + except FileNotFoundError: + # Last version to not have this file + conf_version = '0.7.7' + + if conf_version == __version__: + return + + _LOGGER.info('Upgrading config directory from %s to %s', conf_version, + __version__) + + lib_path = hass.config.path('deps') + if os.path.isdir(lib_path): + shutil.rmtree(lib_path) + + with open(version_path, 'wt') as outp: + outp.write(__version__) + + +def process_ha_core_config(hass, config): + """Process the [homeassistant] section from the config.""" + # pylint: disable=too-many-branches + config = CORE_CONFIG_SCHEMA(config) + hac = hass.config + + def set_time_zone(time_zone_str): + """Helper method to set time zone.""" + if time_zone_str is None: + return + + time_zone = date_util.get_time_zone(time_zone_str) + + if time_zone: + hac.time_zone = time_zone + date_util.set_default_time_zone(time_zone) + else: + _LOGGER.error('Received invalid time zone %s', time_zone_str) + + for key, attr in ((CONF_LATITUDE, 'latitude'), + (CONF_LONGITUDE, 'longitude'), + (CONF_NAME, 'location_name'), + (CONF_ELEVATION, 'elevation')): + if key in config: + setattr(hac, attr, config[key]) + + if CONF_TIME_ZONE in config: + set_time_zone(config.get(CONF_TIME_ZONE)) + + set_customize(config.get(CONF_CUSTOMIZE) or {}) + + if CONF_TEMPERATURE_UNIT in config: + hac.temperature_unit = config[CONF_TEMPERATURE_UNIT] + + # Shortcut if no auto-detection necessary + if None not in (hac.latitude, hac.longitude, hac.temperature_unit, + hac.time_zone, hac.elevation): + return + + discovered = [] + + # If we miss some of the needed values, auto detect them + if None in (hac.latitude, hac.longitude, hac.temperature_unit, + hac.time_zone): + info = loc_util.detect_location_info() + + if info is None: + _LOGGER.error('Could not detect location information') + return + + if hac.latitude is None and hac.longitude is None: + hac.latitude = info.latitude + hac.longitude = info.longitude + discovered.append(('latitude', hac.latitude)) + discovered.append(('longitude', hac.longitude)) + + if hac.temperature_unit is None: + if info.use_fahrenheit: + hac.temperature_unit = TEMP_FAHRENHEIT + discovered.append(('temperature_unit', 'F')) + else: + hac.temperature_unit = TEMP_CELSIUS + discovered.append(('temperature_unit', 'C')) + + if hac.location_name is None: + hac.location_name = info.city + discovered.append(('name', info.city)) + + if hac.time_zone is None: + set_time_zone(info.time_zone) + discovered.append(('time_zone', info.time_zone)) + + if hac.elevation is None and hac.latitude is not None and \ + hac.longitude is not None: + elevation = loc_util.elevation(hac.latitude, hac.longitude) + hac.elevation = elevation + discovered.append(('elevation', elevation)) + + if discovered: + _LOGGER.warning( + 'Incomplete core config. Auto detected %s', + ', '.join('{}: {}'.format(key, val) for key, val in discovered)) diff --git a/homeassistant/core.py b/homeassistant/core.py index d3eed6ce5e03c..cbf02ea587f7e 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -681,6 +681,7 @@ def __init__(self): """Initialize a new config object.""" self.latitude = None self.longitude = None + self.elevation = None self.temperature_unit = None self.location_name = None self.time_zone = None diff --git a/homeassistant/util/location.py b/homeassistant/util/location.py index a596d9bc47635..a9b980bc87172 100644 --- a/homeassistant/util/location.py +++ b/homeassistant/util/location.py @@ -8,7 +8,8 @@ import requests ELEVATION_URL = 'http://maps.googleapis.com/maps/api/elevation/json' -DATA_SOURCE = ['https://freegeoip.io/json/', 'http://ip-api.com/json'] +FREEGEO_API = 'https://freegeoip.io/json/' +IP_API = 'http://ip-api.com/json' # Constants from https://github.com/maurycyp/vincenty # Earth ellipsoid according to WGS 84 @@ -32,30 +33,13 @@ def detect_location_info(): """Detect location information.""" - success = None + data = _get_freegeoip() - for source in DATA_SOURCE: - try: - raw_info = requests.get(source, timeout=5).json() - success = source - break - except (requests.RequestException, ValueError): - success = False + if data is None: + data = _get_ip_api() - if success is False: + if data is None: return None - else: - data = {key: raw_info.get(key) for key in LocationInfo._fields} - if success is DATA_SOURCE[1]: - data['ip'] = raw_info.get('query') - data['country_code'] = raw_info.get('countryCode') - data['country_name'] = raw_info.get('country') - data['region_code'] = raw_info.get('region') - data['region_name'] = raw_info.get('regionName') - data['zip_code'] = raw_info.get('zip') - data['time_zone'] = raw_info.get('timezone') - data['latitude'] = raw_info.get('lat') - data['longitude'] = raw_info.get('lon') # From Wikipedia: Fahrenheit is used in the Bahamas, Belize, # the Cayman Islands, Palau, and the United States and associated @@ -73,11 +57,16 @@ def distance(lat1, lon1, lat2, lon2): def elevation(latitude, longitude): """Return elevation for given latitude and longitude.""" - req = requests.get(ELEVATION_URL, - params={'locations': '{},{}'.format(latitude, - longitude), - 'sensor': 'false'}, - timeout=10) + try: + req = requests.get( + ELEVATION_URL, + params={ + 'locations': '{},{}'.format(latitude, longitude), + 'sensor': 'false', + }, + timeout=10) + except requests.RequestException: + return 0 if req.status_code != 200: return 0 @@ -157,3 +146,45 @@ def vincenty(point1, point2, miles=False): s *= MILES_PER_KILOMETER # kilometers to miles return round(s, 6) + + +def _get_freegeoip(): + """Query freegeoip.io for location data.""" + try: + raw_info = requests.get(FREEGEO_API, timeout=5).json() + except (requests.RequestException, ValueError): + return None + + return { + 'ip': raw_info.get('ip'), + 'country_code': raw_info.get('country_code'), + 'country_name': raw_info.get('country_name'), + 'region_code': raw_info.get('region_code'), + 'region_name': raw_info.get('region_name'), + 'city': raw_info.get('city'), + 'zip_code': raw_info.get('zip_code'), + 'time_zone': raw_info.get('time_zone'), + 'latitude': raw_info.get('latitude'), + 'longitude': raw_info.get('longitude'), + } + + +def _get_ip_api(): + """Query ip-api.com for location data.""" + try: + raw_info = requests.get(IP_API, timeout=5).json() + except (requests.RequestException, ValueError): + return None + + return { + 'ip': raw_info.get('query'), + 'country_code': raw_info.get('countryCode'), + 'country_name': raw_info.get('country'), + 'region_code': raw_info.get('region'), + 'region_name': raw_info.get('regionName'), + 'city': raw_info.get('city'), + 'zip_code': raw_info.get('zip'), + 'time_zone': raw_info.get('timezone'), + 'latitude': raw_info.get('lat'), + 'longitude': raw_info.get('lon'), + } diff --git a/requirements_all.txt b/requirements_all.txt index a131813edbd00..42795291eb1f8 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -41,7 +41,7 @@ blinkstick==1.1.7 blockchain==1.3.3 # homeassistant.components.thermostat.eq3btsmart -# bluepy_devices>=0.2.0 +# bluepy_devices==0.2.0 # homeassistant.components.notify.aws_lambda # homeassistant.components.notify.aws_sns @@ -245,7 +245,7 @@ pyasn1==0.1.9 pychromecast==0.7.2 # homeassistant.components.media_player.cmus -pycmus>=0.1.0 +pycmus==0.1.0 # homeassistant.components.envisalink # homeassistant.components.zwave diff --git a/requirements_test.txt b/requirements_test.txt index 5ec8619b37f7a..649859f250600 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,10 +1,9 @@ -flake8>=2.5.4 -pylint>=1.5.5 +flake8>=2.6.0 +pylint>=1.5.6 coveralls>=1.1 -pytest>=2.9.1 -pytest-cov>=2.2.0 +pytest>=2.9.2 +pytest-cov>=2.2.1 pytest-timeout>=1.0.0 pytest-capturelog>=0.7 -betamax==0.7.0 pydocstyle>=1.0.0 -httpretty==0.8.14 +requests_mock>=1.0 diff --git a/tests/__init__.py b/tests/__init__.py index c1f50d86dfb20..a931604fdce05 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,27 +1,25 @@ -"""Test the initialization.""" -import betamax +"""Setup some common test helper things.""" +import functools from homeassistant import util from homeassistant.util import location -with betamax.Betamax.configure() as config: - config.cassette_library_dir = 'tests/cassettes' -# Automatically called during different setups. Too often forgotten -# so mocked by default. -location.detect_location_info = lambda: location.LocationInfo( - ip='1.1.1.1', - country_code='US', - country_name='United States', - region_code='CA', - region_name='California', - city='San Diego', - zip_code='92122', - time_zone='America/Los_Angeles', - latitude='2.0', - longitude='1.0', - use_fahrenheit=True, -) +def test_real(func): + """Force a function to require a keyword _test_real to be passed in.""" + @functools.wraps(func) + def guard_func(*args, **kwargs): + real = kwargs.pop('_test_real', None) -location.elevation = lambda latitude, longitude: 0 + if not real: + raise Exception('Forgot to mock or pass "_test_real=True" to %s', + func.__name__) + + return func(*args, **kwargs) + + return guard_func + +# Guard a few functions that would make network connections +location.detect_location_info = test_real(location.detect_location_info) +location.elevation = test_real(location.elevation) util.get_local_ip = lambda: '127.0.0.1' diff --git a/tests/cassettes/tests.components.sensor.test_yr.TestSensorYr.test_custom_setup.json b/tests/cassettes/tests.components.sensor.test_yr.TestSensorYr.test_custom_setup.json deleted file mode 100644 index c647c4ae01769..0000000000000 --- a/tests/cassettes/tests.components.sensor.test_yr.TestSensorYr.test_custom_setup.json +++ /dev/null @@ -1 +0,0 @@ -{"http_interactions": [{"request": {"uri": "http://api.yr.no/weatherapi/locationforecast/1.9/?lat=32.87336;lon=117.22743;msl=0", "method": "GET", "headers": {"Accept": ["*/*"], "User-Agent": ["python-requests/2.9.1"], "Accept-Encoding": ["gzip, deflate"], "Connection": ["keep-alive"]}, "body": {"encoding": "utf-8", "string": ""}}, "recorded_at": "2016-06-09T04:02:23", "response": {"url": "http://api.yr.no/weatherapi/locationforecast/1.9/?lat=32.87336;lon=117.22743;msl=0", "headers": {"Location": ["http://api.met.no/weatherapi/locationforecast/1.9/?lat=32.87336;lon=117.22743;msl=0"], "Age": ["0"], "X-Varnish": ["4249781791"], "Via": ["1.1 varnish"], "Server": ["Varnish"], "Date": ["Thu, 09 Jun 2016 04:02:21 GMT"], "Connection": ["close"], "Accept-Ranges": ["bytes"]}, "body": {"encoding": null, "string": ""}, "status": {"message": "Moved permanently", "code": 301}}}, {"request": {"uri": "http://api.met.no/weatherapi/locationforecast/1.9/?lat=32.87336;lon=117.22743;msl=0", "method": "GET", "headers": {"Accept": ["*/*"], "User-Agent": ["python-requests/2.9.1"], "Accept-Encoding": ["gzip, deflate"], "Connection": ["keep-alive"]}, "body": {"encoding": "utf-8", "string": ""}}, "recorded_at": "2016-06-09T04:02:23", "response": {"url": "http://api.met.no/weatherapi/locationforecast/1.9/?lat=32.87336;lon=117.22743;msl=0", "headers": {"Content-Length": ["3637"], "X-Backend-Host": ["ravn_loc"], "X-Varnish": ["4249782320 4249780502"], "Via": ["1.1 varnish"], "Last-Modified": ["Thu, 09 Jun 2016 04:02:21 GMT"], "X-forecast-models": ["proff,ecdet"], "Accept-Ranges": ["bytes"], "Expires": ["Thu, 09 Jun 2016 05:01:15 GMT"], "Content-Encoding": ["gzip"], "Age": ["1"], "Content-Type": ["text/xml; charset=utf-8"], "Server": ["Apache"], "Vary": ["Accept-Encoding"], "Date": ["Thu, 09 Jun 2016 04:02:22 GMT"], "X-slicenumber": ["83"], "Connection": ["keep-alive"]}, "body": {"encoding": "utf-8", "base64_string": "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"}, "status": {"message": "OK", "code": 200}}}], "recorded_with": "betamax/0.7.0"} \ No newline at end of file diff --git a/tests/cassettes/tests.components.sensor.test_yr.TestSensorYr.test_default_setup.json b/tests/cassettes/tests.components.sensor.test_yr.TestSensorYr.test_default_setup.json deleted file mode 100644 index 8226cbbf96e20..0000000000000 --- a/tests/cassettes/tests.components.sensor.test_yr.TestSensorYr.test_default_setup.json +++ /dev/null @@ -1 +0,0 @@ -{"http_interactions": [{"request": {"uri": "http://api.yr.no/weatherapi/locationforecast/1.9/?lat=32.87336;lon=117.22743;msl=0", "method": "GET", "headers": {"Accept": ["*/*"], "User-Agent": ["python-requests/2.9.1"], "Accept-Encoding": ["gzip, deflate"], "Connection": ["keep-alive"]}, "body": {"encoding": "utf-8", "string": ""}}, "recorded_at": "2016-06-09T04:02:22", "response": {"url": "http://api.yr.no/weatherapi/locationforecast/1.9/?lat=32.87336;lon=117.22743;msl=0", "headers": {"Location": ["http://api.met.no/weatherapi/locationforecast/1.9/?lat=32.87336;lon=117.22743;msl=0"], "Age": ["0"], "X-Varnish": ["4249779869"], "Via": ["1.1 varnish"], "Server": ["Varnish"], "Date": ["Thu, 09 Jun 2016 04:02:20 GMT"], "Connection": ["close"], "Accept-Ranges": ["bytes"]}, "body": {"encoding": null, "string": ""}, "status": {"message": "Moved permanently", "code": 301}}}, {"request": {"uri": "http://api.met.no/weatherapi/locationforecast/1.9/?lat=32.87336;lon=117.22743;msl=0", "method": "GET", "headers": {"Accept": ["*/*"], "User-Agent": ["python-requests/2.9.1"], "Accept-Encoding": ["gzip, deflate"], "Connection": ["keep-alive"]}, "body": {"encoding": "utf-8", "string": ""}}, "recorded_at": "2016-06-09T04:02:22", "response": {"url": "http://api.met.no/weatherapi/locationforecast/1.9/?lat=32.87336;lon=117.22743;msl=0", "headers": {"Content-Length": ["3637"], "X-Backend-Host": ["ravn_loc"], "X-Varnish": ["4249780502"], "Via": ["1.1 varnish"], "Last-Modified": ["Thu, 09 Jun 2016 04:02:21 GMT"], "X-forecast-models": ["proff,ecdet"], "Accept-Ranges": ["bytes"], "Expires": ["Thu, 09 Jun 2016 05:01:15 GMT"], "Content-Encoding": ["gzip"], "Age": ["0"], "Content-Type": ["text/xml; charset=utf-8"], "Server": ["Apache"], "Vary": ["Accept-Encoding"], "Date": ["Thu, 09 Jun 2016 04:02:21 GMT"], "X-slicenumber": ["83"], "Connection": ["keep-alive"]}, "body": {"encoding": "utf-8", "base64_string": "H4sIAAAAAAAAA+1dTY/bthbd91cI3tcWSUmUBpNumjQtkEyKl3noQ3eKrWSE5y/Ycibpry/lsWxZImlfiqQoOEAWSfRhizLPPTw899775ywtnrLNLC1S79tivtzefdvmr0ZPRbG+m0yen5/Hz2S82nyZYN9Hk/+9f/dx+pQt0p/z5bZIl9Ns5LHz75arh3SRbdfpNHs5/m41TYt8tTzeKV3n40VWjJeryeEj2f9M5ofTPq822TTdFhM0Tibb/R1G3nTDTsxmr0bYR9HPPvuTPPrBnY/vMPp79MtPnufds1um+7/t/7GaZXNvyb7Jq9GbX8dv33wY+wiHI6/INot8WbtR/Ijwne+zP3+PvM1umS1nZx/Ejid3JLgjlB1fZt8Kds7594iPl3/erBbnx6LjsWJ1OoLOPnRy/NaT4zPcrzer2W5aeNN5ut2+Gq1X+bJ4zd7M6HhykS8yr3xZxfc1e8pq3K7/FmdHqtuWd65ehZfOi7zYzdjd/ZE3T6t/EDyOKQnYf62WXw7/hxAdY0yD+o323zJbrNkLLnabzMvZuD4+Po683TIvXo2m2Xyb77Yj72s637Fb4HjsjyaN65/z5ex1zh5t/4XKO8xmI2+WfWEf6ZNxNKpeMvfKj+ssm+2v+vx55C3WbCTJmP0KPmXpjg0Y+xK4usHHr+n/va/smtaNnnaLfJYX36vvGYblLV6egT3bNFsWrWvWm2y7rZ55valOf/ozPT4u8v1kTFpXTuer3Sxfssv31z48jLzDh+wv4QzR59WX/am/fXhbO5d35nz1/Gt5+5dbv/vwV+38mI1l8/xFNst3i9ol79+8/uO/7y98ylP+5al2ze9/vP394jPMsuf9T/yx+XN5Lfq1oJi9hfpt7o8QcppP5RxRmTCkvwnDfjnTfJ0XLzd8efbF4vjYh8G7335ffFrN92P0Z7op5t/3I85+zLvFp2zDPtLY2PjOjw0D+NbvSAI7eIxeLku/QS6Le38TifBNJC7BOh0nIFhHIbugQuWrcR2PaVdcD9gb1YLrdByAcD2JOEOkB9YDNiw2YJ37CAqo3vyx6JwvYhpkfL44j+riiHfzY1Oj6o2xOR1xAWcJh03KcJYBM1bB2aAjzlJa0nYdONukgN4lnCXj2BDOEg78GcFZooc9J40QZYWXmJ8vrmOJOAY5MzZA9nxAHTh7Jj2jeih8E6FLqO5zIOsCqhM4qiP2KTVUR9Udfpunu+tQPUElAdejirSfWIbqccThuHpQnXe+CVTnPoISqkc9sCDj88V5VBdHvJsfGxQLxyZ2C2cxDGdRSYTh7Lmr+pwQfey5rfheUCmQIZzFnKWLIZWi/QhusWcJLzE+X5zHEnEMcmVsgOz5gDpQ9kxefn/9vQmMRG/idMQBVGcTtc2rpNpzeYECquOuqB7pYs8xB+Gk7DkxhurW2DPvERxDdSELMj9fnEd1YcS78bFBvmi39eyIAziLA86Olwxng1hlj29Pfbtqz4EunIV5N2hozLoRBRz8MwG0mBdO4UDLWI8x64aAmFiZME6DiTgIuTQ2MPpc0TsofQ56pc/lePO3W8+OuADrUOtGVK6vK1C2x57DpIwmelC9jYlSVOfRbT2oHtoSRVgsbgdvh8RnOQ0yPl9cR3VJxLvtsSlHQOC/9t1yPhMfSJ/joLbJdz3O+h33+EK/hHcdOBtyiKRUew45YKjJ+Iw5+43X4Gz7KjnO0ogjuCvhLOqBl7hi7q1hSQNFzIg38ujjyqgAZeeKAMN4cwVTvbwD/hbr2RE3kBxmwmOMOQYjOTrXQVSQHOnKYQmAjDnhsc1eGXPE2fkd9jainPk4aOi1hiLi+HbDoyLYVj074gC24hDIkqM9yIGxNeyIrTTRpTGHQIeGyfxAHkpexlYeItvJD8TIUCaJlIw44+J1cMntzNgAyXKoSpbD3gCdv6N6dsQFQCccGiUD9NBXyVgpgaAbosdEF1vmSQA9ee54g28C0LnSjUObhnICdNv+XXmsu/mxEW+ouuVtRsAckqieGWhPXk60wSyUOJPhm+C4j6BEm/0eaIkr9t3+oEQcglwZGyBtPoAO1JsR9u3NEO+nOmVtZnwI5s3A0alakk3ynES6Elagm4YhL8NlWKjOfQQl8mzMmyEmQT/su+KId+tjg4S7qsgxa3MEVJ19fEoMBMAs7qpR+Lo8cBSYQZIkHGm4X5iFWjO4j6AAs6RxGwu0xMZ0cRtKxCHIobEB5wUqkeeoX/KMhBusyC1jM9hwR4NaXuD1oB50BPVoHxf0CM/ulBq1ZWzWtpWITfk0pCzo5t27kpB342ODhNusyDFnM+KsXyVAW1Y3O6Dkw7Uw63d3w2mrqcErtynNvkbGuDNVsjVjXpriBV8z0qU890FLXHHwurgOd2VsgOQ5UjNsHKCqxzch2GpFjnmcefNdgulheEoKfLAoPIdUVz1nsMfZnCISckqeXoHqAdzjTPXkehuz4Uk50M26eaWx7oZHRbS/ihzzOFNYGbrYP+kSAGjtSphpokuXcMnizNONL2Mrz0dnSZcgpnb1ZFzEGRuvg2tvZ8YGyJipUhk6Ulk8+sBzoeTvlvWOV5pHguc0UWhp1T1lJQnKLEQ9VBlocDaXDqi2eUhjjq/GDlUmphzOUvozUHtZoNa1qUI665CFhUILdszwQGEpzC8l1YCQ1b3mzwvz1UNBYV1EEK9IUJ8MVGFnjGdqUUCspjBiIcDbmC2mOmUkaojV6pTxjr3cogFbhownWMh2sWM7TUA/bH1HH1CAsnOpslgX08LwVDJTTebUeoe2pEHvMtPSU3gBNx0IdqL8QLdTqFKHH4IOtRl7YFoitosdE/uA1lKKlcS+rovDGOtyljZ9NN4lyDLXrw3zMnHNbKQ0vfeeY2KfLMQPVdBS3gLuqcAVlvBdtwStpsXZk2NW2aSdKIBWV+dkQvWBVvuBpcvDhLOe7JNnIcz5QhcKfetJ2W9ujdgJ8QMVtIhaKxiO5dvi8pAI+S5xTNWKgDnw+/ZYcFkLdZe1iC7cgjm+SWhsfchubad4lLZ9Veth3sZ0MYNbWK0Gf9TeO7SLWwLOSxyTtZr1GLxLuBWcOqtYdISEsZbS0EnSrCLvXYAtzHMj6oEtxOsJeQXf4l12yRHCwV4lXcsQ4ZLG+YHqWpFSjl01IXsDLhHzJc6JW7D6FIxwYQXC1bXo0H6/Xw9wtZ9XKm6Zq4Os5BGGl0HWxrYMifGyID9cYUtlE5Ggtu3h4255QitjYUPMd92StQJo3faaUQtQIi3p2icUa1keJkkMbcDBk8GGVUuH+whu4ZUktg9U1QqUNg+PMNeTxzcQ0t3AMVmLwvJrESUKnYNI5671sZ7OQSXLArbdNLaDaAu2qB6W1dy4tBDjbcwVU1q8yvZhNRlts6xAyHMDx8QsDGvHjkm94Pdf19Osrm2CQ6qJZkVQmsUzC+iq991en14lZkFNWprasZuzw0uj+0DFrAp6gOtC3DZp2eVZIsYbOKZm8cpiyJDLj0+u+OuRSwPR0pOZWS4Q3SkYpUa12FBA6wBqzMw0tIMoi/W9a1qR6hpRSYg/uEvq2PWfNK/TLUPJCYGE8bqlajVDqCdHLRLgUzfCq+svaSj8H2qpv1SCFkyDNwlaXNvVNenkUL6lEbSMLVAkYb5/YStWWyKGKkvEsJ3N83qT//PPPKt5TA0ZfUMh7w0dU7XAjaFOZOtq2Nrn/5xg66iKvcuKwvu0ybeXYQvpaofNqwLXG25x722kjarrJYbEgd7GhLmIW4maXUspC5G0u9nZxC0B5w3d0rfAanygQLf213TCrZDqwy1YQk+MjcnxpNkfzjOlx/OkAIXmz00zv50wP1B5SxG1OIK8TXkrFPLe0DF5C2h/CMKT+wFS0rGryzSKdZV05KUxSW2mPmeEhrWRyH0EJeAytJMoi/O9a1vKwKXUyIH26o4PJbzXLYGLNwtksnxCThuKD4ANxc4KV6CrJzwPqaVpPeayepQ2FOG4pUfdQk3niJ0o37+6pQJbKFHjW0HbtmXDABEJGW/knLAFMsUHca0fgsXs6SjQ13emveDrB66sNeLVksqDmukTFoK7jcliBq5itRxE0htcCXhu5JielcD8WuHJW3q1nIXOU6YVwIrE+sAK1vmQ19xmWGDFewIFsGpWubUT2QeqZRG1JWHStmrZASsRu42c07BglWlIrS8JpCF23DXjEOkqAQjt1Mot7donYPEyFS+IWDwLsdJqsIfgPlQRK1ArTZP0KmJFEp7rlojF20WTAVfs17yl12tYXU3xLxVT9eAWrOIyryzysBpMa9KwjC0KpVF+mBpWNa3gGlYvqdJUyHSpYxpWBLXC1yrSXG+F797/LQp1lQDktXOT45VbTvgeNXdDNnhxcLcxW0z1fFdMlW7Vo7GEVwKeS90SsaCF4fF+RMF45XdeFwZEF15B+RWKHSNY3C9kBbGoKcSSxveBKlnHfB9Y4o7flt0trgupkOpSxwQtXm9E2bpw7+JSELRoV0GL6HJl8Qzhch+8we1ClVY8CunSmpJ3/MbQWQn0QxW0QrVs6Z6qaFEJ2XVNyYIhVhSfOlpcrWR1XxnGsZZ8wx8rw8MVSitDQ0qWNLwPVslSgasjylmGq1jIdGPHlCze/pk0XQfVytFYLAofEU1F4VGTJXg/8OpavIptB3cbs8WUkqVkx6Ltgg6W8EpAcGPHlCxeQwSpw4HWlCxAk8OuCYYB0ZVgmHBm74+dwquSdAzpWNLoPlAdiyr1kq6mo328EjHc2DEBi+enlApYUW2nEMCvujob9KUVwvHqB7/iJnJZie5Dla8iVbxqtQpTxav7yXqzmu2mBbvd5DlLi6dsU76MX7yf/gVzmt+KSPAAAA=="}, "status": {"message": "OK", "code": 200}}}], "recorded_with": "betamax/0.7.0"} \ No newline at end of file diff --git a/tests/common.py b/tests/common.py index 98c61dfc16e89..26d466bc4b8b3 100644 --- a/tests/common.py +++ b/tests/common.py @@ -35,6 +35,7 @@ def get_test_home_assistant(num_threads=None): hass.config.config_dir = get_test_config_dir() hass.config.latitude = 32.87336 hass.config.longitude = -117.22743 + hass.config.elevation = 0 hass.config.time_zone = date_util.get_time_zone('US/Pacific') hass.config.temperature_unit = TEMP_CELSIUS @@ -105,6 +106,13 @@ def ensure_sun_set(hass): fire_time_changed(hass, sun.next_setting_utc(hass) + timedelta(seconds=10)) +def load_fixture(filename): + """Helper to load a fixture.""" + path = os.path.join(os.path.dirname(__file__), 'fixtures', filename) + with open(path) as fp: + return fp.read() + + def mock_state_change_event(hass, new_state, old_state=None): """Mock state change envent.""" event_data = { diff --git a/tests/components/test_forecast.py b/tests/components/sensor/test_forecast.py similarity index 68% rename from tests/components/test_forecast.py rename to tests/components/sensor/test_forecast.py index bfda22596c2c5..55bdec20a3511 100644 --- a/tests/components/test_forecast.py +++ b/tests/components/sensor/test_forecast.py @@ -1,17 +1,17 @@ """The tests for the forecast.io platform.""" -import json import re -import os import unittest from unittest.mock import MagicMock, patch import forecastio -import httpretty from requests.exceptions import HTTPError +import requests_mock from homeassistant.components.sensor import forecast from homeassistant import core as ha +from tests.common import load_fixture + class TestForecastSetup(unittest.TestCase): """Test the forecast.io platform.""" @@ -48,29 +48,14 @@ def test_setup_bad_api_key(self, mock_get_forecast): response = forecast.setup_platform(self.hass, self.config, MagicMock()) self.assertFalse(response) - @httpretty.activate + @requests_mock.Mocker() @patch('forecastio.api.get_forecast', wraps=forecastio.api.get_forecast) - def test_setup(self, mock_get_forecast): + def test_setup(self, m, mock_get_forecast): """Test for successfully setting up the forecast.io platform.""" - def load_fixture_from_json(): - cwd = os.path.dirname(__file__) - fixture_path = os.path.join(cwd, '..', 'fixtures', 'forecast.json') - with open(fixture_path) as file: - content = json.load(file) - return json.dumps(content) - - # Mock out any calls to the actual API and - # return the fixture json instead - uri = 'api.forecast.io\/forecast\/(\w+)\/(-?\d+\.?\d*),(-?\d+\.?\d*)' - httpretty.register_uri( - httpretty.GET, - re.compile(uri), - body=load_fixture_from_json(), - ) - # The following will raise an error if the regex for the mock was - # incorrect and we actually try to go out to the internet. - httpretty.HTTPretty.allow_net_connect = False - + uri = ('https://api.forecast.io\/forecast\/(\w+)\/' + '(-?\d+\.?\d*),(-?\d+\.?\d*)') + m.get(re.compile(uri), + text=load_fixture('forecast.json')) forecast.setup_platform(self.hass, self.config, MagicMock()) self.assertTrue(mock_get_forecast.called) self.assertEqual(mock_get_forecast.call_count, 1) diff --git a/tests/components/sensor/test_yr.py b/tests/components/sensor/test_yr.py index 43a1457869044..3ea94938f0d92 100644 --- a/tests/components/sensor/test_yr.py +++ b/tests/components/sensor/test_yr.py @@ -1,39 +1,40 @@ """The tests for the Yr sensor platform.""" from datetime import datetime +from unittest import TestCase from unittest.mock import patch -import pytest +import requests_mock from homeassistant.bootstrap import _setup_component import homeassistant.util.dt as dt_util -from tests.common import get_test_home_assistant +from tests.common import get_test_home_assistant, load_fixture -@pytest.mark.usefixtures('betamax_session') -class TestSensorYr: +class TestSensorYr(TestCase): """Test the Yr sensor.""" - def setup_method(self, method): + def setUp(self): """Setup things to be run when tests are started.""" self.hass = get_test_home_assistant() self.hass.config.latitude = 32.87336 self.hass.config.longitude = 117.22743 - def teardown_method(self, method): + def tearDown(self): """Stop everything that was started.""" self.hass.stop() - def test_default_setup(self, betamax_session): + @requests_mock.Mocker() + def test_default_setup(self, m): """Test the default setup.""" + m.get('http://api.yr.no/weatherapi/locationforecast/1.9/', + text=load_fixture('yr.no.json')) now = datetime(2016, 6, 9, 1, tzinfo=dt_util.UTC) - with patch('homeassistant.components.sensor.yr.requests.Session', - return_value=betamax_session): - with patch('homeassistant.components.sensor.yr.dt_util.utcnow', - return_value=now): - assert _setup_component(self.hass, 'sensor', { - 'sensor': {'platform': 'yr', - 'elevation': 0}}) + with patch('homeassistant.components.sensor.yr.dt_util.utcnow', + return_value=now): + assert _setup_component(self.hass, 'sensor', { + 'sensor': {'platform': 'yr', + 'elevation': 0}}) state = self.hass.states.get('sensor.yr_symbol') @@ -41,23 +42,24 @@ def test_default_setup(self, betamax_session): assert state.state.isnumeric() assert state.attributes.get('unit_of_measurement') is None - def test_custom_setup(self, betamax_session): + @requests_mock.Mocker() + def test_custom_setup(self, m): """Test a custom setup.""" + m.get('http://api.yr.no/weatherapi/locationforecast/1.9/', + text=load_fixture('yr.no.json')) now = datetime(2016, 6, 9, 1, tzinfo=dt_util.UTC) - with patch('homeassistant.components.sensor.yr.requests.Session', - return_value=betamax_session): - with patch('homeassistant.components.sensor.yr.dt_util.utcnow', - return_value=now): - assert _setup_component(self.hass, 'sensor', { - 'sensor': {'platform': 'yr', - 'elevation': 0, - 'monitored_conditions': [ - 'pressure', - 'windDirection', - 'humidity', - 'fog', - 'windSpeed']}}) + with patch('homeassistant.components.sensor.yr.dt_util.utcnow', + return_value=now): + assert _setup_component(self.hass, 'sensor', { + 'sensor': {'platform': 'yr', + 'elevation': 0, + 'monitored_conditions': [ + 'pressure', + 'windDirection', + 'humidity', + 'fog', + 'windSpeed']}}) state = self.hass.states.get('sensor.yr_pressure') assert 'hPa' == state.attributes.get('unit_of_measurement') diff --git a/tests/components/test_init.py b/tests/components/test_init.py index 68b0ca3be35d7..7abaf63b40770 100644 --- a/tests/components/test_init.py +++ b/tests/components/test_init.py @@ -131,7 +131,7 @@ def test_reload_core_conf(self): assert state.attributes.get('hello') == 'world' @patch('homeassistant.components._LOGGER.error') - @patch('homeassistant.bootstrap.process_ha_core_config') + @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: diff --git a/tests/fixtures/freegeoip.io.json b/tests/fixtures/freegeoip.io.json new file mode 100644 index 0000000000000..8afdaba070e99 --- /dev/null +++ b/tests/fixtures/freegeoip.io.json @@ -0,0 +1,13 @@ +{ + "ip": "1.2.3.4", + "country_code": "US", + "country_name": "United States", + "region_code": "CA", + "region_name": "California", + "city": "San Diego", + "zip_code": "92122", + "time_zone": "America\/Los_Angeles", + "latitude": 32.8594, + "longitude": -117.2073, + "metro_code": 825 +} diff --git a/tests/fixtures/google_maps_elevation.json b/tests/fixtures/google_maps_elevation.json new file mode 100644 index 0000000000000..95eeb0fe239ad --- /dev/null +++ b/tests/fixtures/google_maps_elevation.json @@ -0,0 +1,13 @@ +{ + "results" : [ + { + "elevation" : 101.5, + "location" : { + "lat" : 32.54321, + "lng" : -117.12345 + }, + "resolution" : 4.8 + } + ], + "status" : "OK" +} diff --git a/tests/fixtures/ip-api.com.json b/tests/fixtures/ip-api.com.json new file mode 100644 index 0000000000000..d31d4560589b3 --- /dev/null +++ b/tests/fixtures/ip-api.com.json @@ -0,0 +1,16 @@ +{ + "as": "AS20001 Time Warner Cable Internet LLC", + "city": "San Diego", + "country": "United States", + "countryCode": "US", + "isp": "Time Warner Cable", + "lat": 32.8594, + "lon": -117.2073, + "org": "Time Warner Cable", + "query": "1.2.3.4", + "region": "CA", + "regionName": "California", + "status": "success", + "timezone": "America\/Los_Angeles", + "zip": "92122" +} diff --git a/tests/fixtures/yr.no.json b/tests/fixtures/yr.no.json new file mode 100644 index 0000000000000..b181fdfd85b22 --- /dev/null +++ b/tests/fixtures/yr.no.json @@ -0,0 +1,1184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/test_bootstrap.py b/tests/test_bootstrap.py index 152818d02e470..34aaa1b83eda0 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 os import tempfile from unittest import mock import threading @@ -8,10 +7,7 @@ import voluptuous as vol from homeassistant import bootstrap, loader -from homeassistant.const import (__version__, CONF_LATITUDE, CONF_LONGITUDE, - CONF_NAME, CONF_CUSTOMIZE) import homeassistant.util.dt as dt_util -from homeassistant.helpers.entity import Entity from homeassistant.helpers.config_validation import PLATFORM_SCHEMA from tests.common import get_test_home_assistant, MockModule, MockPlatform @@ -24,23 +20,22 @@ class TestBootstrap: def setup_method(self, method): """Setup the test.""" + self.backup_cache = loader._COMPONENT_CACHE + if method == self.test_from_config_file: return self.hass = get_test_home_assistant() - self.backup_cache = loader._COMPONENT_CACHE def teardown_method(self, method): """Clean up.""" dt_util.DEFAULT_TIME_ZONE = ORIG_TIMEZONE - - if method == self.test_from_config_file: - return - self.hass.stop() loader._COMPONENT_CACHE = self.backup_cache - def test_from_config_file(self): + @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: @@ -48,71 +43,10 @@ def test_from_config_file(self): fp.write('{}:\n'.format(comp).encode('utf-8')) fp.flush() - hass = bootstrap.from_config_file(fp.name) - - components.append('group') - - assert sorted(components) == sorted(hass.config.components) - - def test_remove_lib_on_upgrade(self): - """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') - - with open(version_path, 'wt') as outp: - outp.write('0.7.0') - - os.mkdir(lib_dir) - - with open(check_file, 'w'): - pass - - self.hass.config.config_dir = config_dir - - assert os.path.isfile(check_file) - bootstrap.process_ha_config_upgrade(self.hass) - assert not os.path.isfile(check_file) - - def test_not_remove_lib_if_not_upgrade(self): - """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') - - with open(version_path, 'wt') as outp: - outp.write(__version__) - - os.mkdir(lib_dir) - - with open(check_file, 'w'): - pass - - self.hass.config.config_dir = config_dir - - bootstrap.process_ha_config_upgrade(self.hass) - - assert os.path.isfile(check_file) - - def test_entity_customization(self): - """Test entity customization through configuration.""" - config = {CONF_LATITUDE: 50, - CONF_LONGITUDE: 50, - CONF_NAME: 'Test', - CONF_CUSTOMIZE: {'test.test': {'hidden': True}}} - - bootstrap.process_ha_core_config(self.hass, config) - - entity = Entity() - entity.entity_id = 'test.test' - entity.hass = self.hass - entity.update_ha_state() - - state = self.hass.states.get('test.test') + self.hass = bootstrap.from_config_file(fp.name) - assert state.attributes['hidden'] + components.append('group') + assert sorted(components) == sorted(self.hass.config.components) def test_handle_setup_circular_dependency(self): """Test the setup of circular dependencies.""" @@ -302,8 +236,7 @@ def exception_setup(hass, config): assert not bootstrap._setup_component(self.hass, 'comp', None) assert 'comp' not in self.hass.config.components - @mock.patch('homeassistant.bootstrap.process_ha_core_config') - def test_home_assistant_core_config_validation(self, mock_process): + def test_home_assistant_core_config_validation(self): """Test if we pass in wrong information for HA conf.""" # Extensive HA conf validation testing is done in test_config.py assert None is bootstrap.from_config_dict({ @@ -311,7 +244,6 @@ def test_home_assistant_core_config_validation(self, mock_process): 'latitude': 'some string' } }) - assert not mock_process.called def test_component_setup_with_validation_and_dependency(self): """Test all config is passed to dependencies.""" diff --git a/tests/test_config.py b/tests/test_config.py index 8a5ec306b3bed..6be3f585967c2 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,22 +1,28 @@ """Test config utils.""" # pylint: disable=too-many-public-methods,protected-access +import os +import tempfile import unittest import unittest.mock as mock -import os import pytest from voluptuous import MultipleInvalid -from homeassistant.core import DOMAIN, HomeAssistantError +from homeassistant.core import DOMAIN, HomeAssistantError, Config import homeassistant.config as config_util from homeassistant.const import ( CONF_LATITUDE, CONF_LONGITUDE, CONF_TEMPERATURE_UNIT, CONF_NAME, - CONF_TIME_ZONE) + CONF_TIME_ZONE, CONF_ELEVATION, CONF_CUSTOMIZE, __version__, + TEMP_FAHRENHEIT) +from homeassistant.util import location as location_util, dt as dt_util +from homeassistant.helpers.entity import Entity -from tests.common import get_test_config_dir +from tests.common import ( + get_test_config_dir, get_test_home_assistant) CONFIG_DIR = get_test_config_dir() YAML_PATH = os.path.join(CONFIG_DIR, config_util.YAML_CONFIG_FILE) +ORIG_TIMEZONE = dt_util.DEFAULT_TIME_ZONE def create_file(path): @@ -30,9 +36,14 @@ class TestConfig(unittest.TestCase): def tearDown(self): # pylint: disable=invalid-name """Clean up.""" + dt_util.DEFAULT_TIME_ZONE = ORIG_TIMEZONE + if os.path.isfile(YAML_PATH): os.remove(YAML_PATH) + if hasattr(self, 'hass'): + self.hass.stop() + def test_create_default_config(self): """Test creation of default config.""" config_util.create_default_config(CONFIG_DIR, False) @@ -108,8 +119,15 @@ def test_load_yaml_config_preserves_key_order(self): [('hello', 0), ('world', 1)], list(config_util.load_yaml_config_file(YAML_PATH).items())) + @mock.patch('homeassistant.util.location.detect_location_info', + return_value=location_util.LocationInfo( + '0.0.0.0', 'US', 'United States', 'CA', 'California', + 'San Diego', '92122', 'America/Los_Angeles', 32.8594, + -117.2073, True)) + @mock.patch('homeassistant.util.location.elevation', return_value=101) @mock.patch('builtins.print') - def test_create_default_config_detect_location(self, mock_print): + def test_create_default_config_detect_location(self, mock_detect, + mock_elev, mock_print): """Test that detect location sets the correct config keys.""" config_util.ensure_config_exists(CONFIG_DIR) @@ -120,15 +138,16 @@ def test_create_default_config_detect_location(self, mock_print): ha_conf = config[DOMAIN] expected_values = { - CONF_LATITUDE: 2.0, - CONF_LONGITUDE: 1.0, + CONF_LATITUDE: 32.8594, + CONF_LONGITUDE: -117.2073, + CONF_ELEVATION: 101, CONF_TEMPERATURE_UNIT: 'F', CONF_NAME: 'Home', CONF_TIME_ZONE: 'America/Los_Angeles' } - self.assertEqual(expected_values, ha_conf) - self.assertTrue(mock_print.called) + assert expected_values == ha_conf + assert mock_print.called @mock.patch('builtins.print') def test_create_default_config_returns_none_if_write_error(self, @@ -166,3 +185,127 @@ def test_core_config_schema(self): }, }, }) + + def test_entity_customization(self): + """Test entity customization through configuration.""" + self.hass = get_test_home_assistant() + + config = {CONF_LATITUDE: 50, + CONF_LONGITUDE: 50, + CONF_NAME: 'Test', + CONF_CUSTOMIZE: {'test.test': {'hidden': True}}} + + config_util.process_ha_core_config(self.hass, config) + + entity = Entity() + entity.entity_id = 'test.test' + entity.hass = self.hass + entity.update_ha_state() + + state = self.hass.states.get('test.test') + + assert state.attributes['hidden'] + + def test_remove_lib_on_upgrade(self): + """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') + + with open(version_path, 'wt') as outp: + outp.write('0.7.0') + + os.mkdir(lib_dir) + + with open(check_file, 'w'): + pass + + self.hass = get_test_home_assistant() + self.hass.config.config_dir = config_dir + + 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): + """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') + + with open(version_path, 'wt') as outp: + outp.write(__version__) + + os.mkdir(lib_dir) + + with open(check_file, 'w'): + pass + + self.hass = get_test_home_assistant() + self.hass.config.config_dir = config_dir + + config_util.process_ha_config_upgrade(self.hass) + + assert os.path.isfile(check_file) + + def test_loading_configuration(self): + """Test loading core config onto hass object.""" + config = Config() + hass = mock.Mock(config=config) + + config_util.process_ha_core_config(hass, { + 'latitude': 60, + 'longitude': 50, + 'elevation': 25, + 'name': 'Huis', + 'temperature_unit': 'F', + 'time_zone': 'America/New_York', + }) + + assert config.latitude == 60 + assert config.longitude == 50 + assert config.elevation == 25 + assert config.location_name == 'Huis' + assert config.temperature_unit == TEMP_FAHRENHEIT + assert config.time_zone.zone == 'America/New_York' + + @mock.patch('homeassistant.util.location.detect_location_info', + return_value=location_util.LocationInfo( + '0.0.0.0', 'US', 'United States', 'CA', 'California', + 'San Diego', '92122', 'America/Los_Angeles', 32.8594, + -117.2073, True)) + @mock.patch('homeassistant.util.location.elevation', return_value=101) + def test_discovering_configuration(self, mock_detect, mock_elevation): + """Test auto discovery for missing core configs.""" + config = Config() + hass = mock.Mock(config=config) + + config_util.process_ha_core_config(hass, {}) + + assert config.latitude == 32.8594 + assert config.longitude == -117.2073 + assert config.elevation == 101 + assert config.location_name == 'San Diego' + assert config.temperature_unit == TEMP_FAHRENHEIT + assert config.time_zone.zone == 'America/Los_Angeles' + + @mock.patch('homeassistant.util.location.detect_location_info', + return_value=None) + @mock.patch('homeassistant.util.location.elevation', return_value=0) + def test_discovering_configuration_auto_detect_fails(self, mock_detect, + mock_elevation): + """Test config remains unchanged if discovery fails.""" + config = Config() + hass = mock.Mock(config=config) + + config_util.process_ha_core_config(hass, {}) + + blankConfig = Config() + assert config.latitude == blankConfig.latitude + assert config.longitude == blankConfig.longitude + assert config.elevation == blankConfig.elevation + assert config.location_name == blankConfig.location_name + assert config.temperature_unit == blankConfig.temperature_unit + assert config.time_zone == blankConfig.time_zone diff --git a/tests/util/test_location.py b/tests/util/test_location.py index 7d0052fe62c2a..1dfb71a87bf5a 100644 --- a/tests/util/test_location.py +++ b/tests/util/test_location.py @@ -1,9 +1,15 @@ """Test Home Assistant location util methods.""" # pylint: disable=too-many-public-methods -import unittest +from unittest import TestCase +from unittest.mock import patch + +import requests +import requests_mock import homeassistant.util.location as location_util +from tests.common import load_fixture + # Paris COORDINATES_PARIS = (48.864716, 2.349014) # New York @@ -20,26 +26,124 @@ DISTANCE_MILES = 3632.78 -class TestLocationUtil(unittest.TestCase): +class TestLocationUtil(TestCase): """Test util location methods.""" + def test_get_distance_to_same_place(self): + """Test getting the distance.""" + meters = location_util.distance(COORDINATES_PARIS[0], + COORDINATES_PARIS[1], + COORDINATES_PARIS[0], + COORDINATES_PARIS[1]) + + assert meters == 0 + def test_get_distance(self): """Test getting the distance.""" meters = location_util.distance(COORDINATES_PARIS[0], COORDINATES_PARIS[1], COORDINATES_NEW_YORK[0], COORDINATES_NEW_YORK[1]) - self.assertAlmostEqual(meters / 1000, DISTANCE_KM, places=2) + + assert meters/1000 - DISTANCE_KM < 0.01 def test_get_kilometers(self): """Test getting the distance between given coordinates in km.""" kilometers = location_util.vincenty(COORDINATES_PARIS, COORDINATES_NEW_YORK) - self.assertEqual(round(kilometers, 2), DISTANCE_KM) + assert round(kilometers, 2) == DISTANCE_KM def test_get_miles(self): """Test getting the distance between given coordinates in miles.""" miles = location_util.vincenty(COORDINATES_PARIS, COORDINATES_NEW_YORK, miles=True) - self.assertEqual(round(miles, 2), DISTANCE_MILES) + assert round(miles, 2) == DISTANCE_MILES + + @requests_mock.Mocker() + def test_detect_location_info_freegeoip(self, m): + """Test detect location info using freegeoip.""" + m.get(location_util.FREEGEO_API, + text=load_fixture('freegeoip.io.json')) + + info = location_util.detect_location_info(_test_real=True) + + assert info is not None + assert info.ip == '1.2.3.4' + assert info.country_code == 'US' + assert info.country_name == 'United States' + assert info.region_code == 'CA' + assert info.region_name == 'California' + assert info.city == 'San Diego' + assert info.zip_code == '92122' + assert info.time_zone == 'America/Los_Angeles' + assert info.latitude == 32.8594 + assert info.longitude == -117.2073 + assert info.use_fahrenheit + + @requests_mock.Mocker() + @patch('homeassistant.util.location._get_freegeoip', return_value=None) + def test_detect_location_info_ipapi(self, mock_req, mock_freegeoip): + """Test detect location info using freegeoip.""" + mock_req.get(location_util.IP_API, + text=load_fixture('ip-api.com.json')) + + info = location_util.detect_location_info(_test_real=True) + + assert info is not None + assert info.ip == '1.2.3.4' + assert info.country_code == 'US' + assert info.country_name == 'United States' + assert info.region_code == 'CA' + assert info.region_name == 'California' + assert info.city == 'San Diego' + assert info.zip_code == '92122' + assert info.time_zone == 'America/Los_Angeles' + assert info.latitude == 32.8594 + assert info.longitude == -117.2073 + assert info.use_fahrenheit + + @patch('homeassistant.util.location.elevation', return_value=0) + @patch('homeassistant.util.location._get_freegeoip', return_value=None) + @patch('homeassistant.util.location._get_ip_api', return_value=None) + def test_detect_location_info_both_queries_fail(self, mock_ipapi, + mock_freegeoip, + mock_elevation): + """Ensure we return None if both queries fail.""" + info = location_util.detect_location_info(_test_real=True) + assert info is None + + @patch('homeassistant.util.location.requests.get', + side_effect=requests.RequestException) + def test_freegeoip_query_raises(self, mock_get): + """Test freegeoip query when the request to API fails.""" + info = location_util._get_freegeoip() + assert info is None + + @patch('homeassistant.util.location.requests.get', + side_effect=requests.RequestException) + def test_ip_api_query_raises(self, mock_get): + """Test ip api query when the request to API fails.""" + info = location_util._get_ip_api() + assert info is None + + @patch('homeassistant.util.location.requests.get', + side_effect=requests.RequestException) + def test_elevation_query_raises(self, mock_get): + """Test elevation when the request to API fails.""" + elevation = location_util.elevation(10, 10, _test_real=True) + assert elevation == 0 + + @requests_mock.Mocker() + def test_elevation_query_fails(self, mock_req): + """Test elevation when the request to API fails.""" + mock_req.get(location_util.ELEVATION_URL, text='{}', status_code=401) + elevation = location_util.elevation(10, 10, _test_real=True) + assert elevation == 0 + + @requests_mock.Mocker() + def test_elevation_query_nonjson(self, mock_req): + """Test if elevation API returns a non JSON value.""" + mock_req.get(location_util.ELEVATION_URL, text='{ I am not JSON }') + elevation = location_util.elevation(10, 10, _test_real=True) + assert elevation == 0 diff --git a/tests/util/test_package.py b/tests/util/test_package.py index ea9a8f23dfae7..a4e0019695902 100644 --- a/tests/util/test_package.py +++ b/tests/util/test_package.py @@ -49,7 +49,7 @@ def test_install_package_zip(self): self.assertTrue(package.check_package_exists( TEST_NEW_REQ, self.lib_dir)) - bootstrap.mount_local_lib_path(self.tmp_dir.name) + bootstrap._mount_local_lib_path(self.tmp_dir.name) try: import pyhelloworld3