"""Test system log component.""" import asyncio import logging import pytest from homeassistant.bootstrap import async_setup_component from homeassistant.components import system_log from unittest.mock import MagicMock, patch _LOGGER = logging.getLogger('test_logger') @pytest.fixture(autouse=True) @asyncio.coroutine def setup_test_case(hass): """Setup system_log component before test case.""" config = {'system_log': {'max_entries': 2}} yield from async_setup_component(hass, system_log.DOMAIN, config) @asyncio.coroutine def get_error_log(hass, test_client, expected_count): """Fetch all entries from system_log via the API.""" client = yield from test_client(hass.http.app) resp = yield from client.get('/api/error/all') assert resp.status == 200 data = yield from resp.json() assert len(data) == expected_count return data def _generate_and_log_exception(exception, log): try: raise Exception(exception) except: # pylint: disable=bare-except _LOGGER.exception(log) def assert_log(log, exception, message, level): """Assert that specified values are in a specific log entry.""" assert exception in log['exception'] assert message == log['message'] assert level == log['level'] assert 'timestamp' in log def get_frame(name): """Get log stack frame.""" return (name, None, None, None) @asyncio.coroutine def test_normal_logs(hass, test_client): """Test that debug and info are not logged.""" _LOGGER.debug('debug') _LOGGER.info('info') # Assert done by get_error_log yield from get_error_log(hass, test_client, 0) @asyncio.coroutine def test_exception(hass, test_client): """Test that exceptions are logged and retrieved correctly.""" _generate_and_log_exception('exception message', 'log message') log = (yield from get_error_log(hass, test_client, 1))[0] assert_log(log, 'exception message', 'log message', 'ERROR') @asyncio.coroutine def test_warning(hass, test_client): """Test that warning are logged and retrieved correctly.""" _LOGGER.warning('warning message') log = (yield from get_error_log(hass, test_client, 1))[0] assert_log(log, '', 'warning message', 'WARNING') @asyncio.coroutine def test_error(hass, test_client): """Test that errors are logged and retrieved correctly.""" _LOGGER.error('error message') log = (yield from get_error_log(hass, test_client, 1))[0] assert_log(log, '', 'error message', 'ERROR') @asyncio.coroutine def test_critical(hass, test_client): """Test that critical are logged and retrieved correctly.""" _LOGGER.critical('critical message') log = (yield from get_error_log(hass, test_client, 1))[0] assert_log(log, '', 'critical message', 'CRITICAL') @asyncio.coroutine def test_remove_older_logs(hass, test_client): """Test that older logs are rotated out.""" _LOGGER.error('error message 1') _LOGGER.error('error message 2') _LOGGER.error('error message 3') log = yield from get_error_log(hass, test_client, 2) assert_log(log[0], '', 'error message 3', 'ERROR') assert_log(log[1], '', 'error message 2', 'ERROR') @asyncio.coroutine def test_clear_logs(hass, test_client): """Test that the log can be cleared via a service call.""" _LOGGER.error('error message') hass.async_add_job( hass.services.async_call( system_log.DOMAIN, system_log.SERVICE_CLEAR, {})) yield from hass.async_block_till_done() # Assert done by get_error_log yield from get_error_log(hass, test_client, 0) @asyncio.coroutine def test_unknown_path(hass, test_client): """Test error logged from unknown path.""" _LOGGER.findCaller = MagicMock( return_value=('unknown_path', 0, None, None)) _LOGGER.error('error message') log = (yield from get_error_log(hass, test_client, 1))[0] assert log['source'] == 'unknown_path' def log_error_from_test_path(path): """Log error while mocking the path.""" call_path = 'internal_path.py' with patch.object( _LOGGER, 'findCaller', MagicMock(return_value=(call_path, 0, None, None))): with patch('traceback.extract_stack', MagicMock(return_value=[ get_frame('main_path/main.py'), get_frame(path), get_frame(call_path), get_frame('venv_path/logging/log.py')])): _LOGGER.error('error message') @asyncio.coroutine def test_homeassistant_path(hass, test_client): """Test error logged from homeassistant path.""" log_error_from_test_path('venv_path/homeassistant/component/component.py') with patch('homeassistant.components.system_log.HOMEASSISTANT_PATH', new=['venv_path/homeassistant']): log = (yield from get_error_log(hass, test_client, 1))[0] assert log['source'] == 'component/component.py' @asyncio.coroutine def test_config_path(hass, test_client): """Test error logged from config path.""" log_error_from_test_path('config/custom_component/test.py') with patch.object(hass.config, 'config_dir', new='config'): log = (yield from get_error_log(hass, test_client, 1))[0] assert log['source'] == 'custom_component/test.py' @asyncio.coroutine def test_netdisco_path(hass, test_client): """Test error logged from netdisco path.""" log_error_from_test_path('venv_path/netdisco/disco_component.py') with patch.dict('sys.modules', netdisco=MagicMock(__path__=['venv_path/netdisco'])): log = (yield from get_error_log(hass, test_client, 1))[0] assert log['source'] == 'disco_component.py'