From b8f94d8d122fa48e2bf0d1293b40968669c931f8 Mon Sep 17 00:00:00 2001 From: Erik Hendrix Date: Mon, 19 Nov 2018 17:18:04 -0700 Subject: [PATCH 01/20] Create test for platform Created test for platform. Added media_stop to common.py test --- tests/components/media_player/common.py | 12 +- tests/components/media_player/test_directv.py | 422 ++++++++++++++++++ 2 files changed, 431 insertions(+), 3 deletions(-) create mode 100644 tests/components/media_player/test_directv.py diff --git a/tests/components/media_player/common.py b/tests/components/media_player/common.py index 3f4d4cb9f241e..effcecfd6476f 100644 --- a/tests/components/media_player/common.py +++ b/tests/components/media_player/common.py @@ -11,9 +11,9 @@ from homeassistant.const import ( ATTR_ENTITY_ID, SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_PLAY, SERVICE_MEDIA_PLAY_PAUSE, SERVICE_MEDIA_PREVIOUS_TRACK, - SERVICE_MEDIA_SEEK, SERVICE_TOGGLE, SERVICE_TURN_OFF, SERVICE_TURN_ON, - SERVICE_VOLUME_DOWN, SERVICE_VOLUME_MUTE, SERVICE_VOLUME_SET, - SERVICE_VOLUME_UP) + SERVICE_MEDIA_SEEK, SERVICE_MEDIA_STOP, SERVICE_TOGGLE, SERVICE_TURN_OFF, + SERVICE_TURN_ON, SERVICE_VOLUME_DOWN, SERVICE_VOLUME_MUTE, + SERVICE_VOLUME_SET, SERVICE_VOLUME_UP) from homeassistant.loader import bind_hass @@ -94,6 +94,12 @@ def media_pause(hass, entity_id=None): data = {ATTR_ENTITY_ID: entity_id} if entity_id else {} hass.services.call(DOMAIN, SERVICE_MEDIA_PAUSE, data) +@bind_hass +def media_stop(hass, entity_id=None): + """Send the media player the command for stop.""" + data = {ATTR_ENTITY_ID: entity_id} if entity_id else {} + hass.services.call(DOMAIN, SERVICE_MEDIA_STOP, data) + @bind_hass def media_next_track(hass, entity_id=None): diff --git a/tests/components/media_player/test_directv.py b/tests/components/media_player/test_directv.py new file mode 100644 index 0000000000000..6fed185eac1f5 --- /dev/null +++ b/tests/components/media_player/test_directv.py @@ -0,0 +1,422 @@ +"""The tests for the DirecTV Media player platform.""" +import unittest +from unittest.mock import patch, Mock, MagicMock + + +from datetime import datetime, timedelta +import logging +import sys + +from homeassistant.setup import setup_component +import homeassistant.components.media_player as mp +from homeassistant.components.media_player.directv import ( + ATTR_MEDIA_CURRENTLY_RECORDING, ATTR_MEDIA_RATING, ATTR_MEDIA_RECORDED, + ATTR_MEDIA_START_TIME, DATA_DIRECTV, DEFAULT_DEVICE, DEFAULT_PORT, + setup_platform) +from homeassistant.const import ( + CONF_DEVICE, CONF_HOST, CONF_NAME, CONF_PORT, STATE_OFF, STATE_PAUSED, + STATE_PLAYING) +import homeassistant.util.dt as dt_util + +from tests.common import get_test_home_assistant +from tests.components.media_player import common + + +IP_ADDRESS = '127.0.0.1' + +WORKING_CONFIG = { + CONF_HOST: IP_ADDRESS, + CONF_NAME: 'Main DVR', + CONF_PORT: DEFAULT_PORT, + CONF_DEVICE: DEFAULT_DEVICE +} + +DISCOVERY_INFO = { + 'host': IP_ADDRESS, + 'serial': 1234 +} + +LOCATIONS = [ + { + 'locationName': 'Main DVR', + 'clientAddr': DEFAULT_DEVICE + } +] + +LIVE = { + "callsign": "CNNHD", + "date": "20181110", + "duration": 3600, + "isOffAir": False, + "isPclocked": 1, + "isPpv": False, + "isRecording": False, + "isVod": False, + "major": 202, + "minor": 65535, + "offset": 1, + "programId": "102454523", + "rating": "No Rating", + "startTime": 1541876400, + "stationId": 3900947, + "title": "CNN Newsroom With Fredricka Whitfield" +} + +RECORDING = { + "callsign": "CNNHD", + "date": "20181110", + "duration": 3600, + "isOffAir": False, + "isPclocked": 1, + "isPpv": False, + "isRecording": True, + "isVod": False, + "major": 202, + "minor": 65535, + "offset": 1, + "programId": "102454523", + "rating": "No Rating", + "startTime": 1541876400, + "stationId": 3900947, + "title": "CNN Newsroom With Fredricka Whitfield", + 'uniqueId': '12345', + 'episodeTitle': 'CNN Recorded' +} + +_LOGGER = logging.getLogger(__name__) + + +class mockDIRECTVClass: + """A fake DirecTV DVR device.""" + + def __init__(self, ip, port=8080, clientAddr='0'): + """Initialize the fake DirecTV device.""" + self._host = ip + self._port = port + self._device = clientAddr + self._standby = True + self._play = False + + _LOGGER.debug("INIT with host: %s, port: %s, device: %s", + self._host, self._port, self._device) + + self._locations = LOCATIONS + + self.attributes = LIVE + + def get_locations(self): + """Mock for get_locations method.""" + _LOGGER.debug("get_locations called") + test_locations = { + 'locations': self._locations, + 'status': { + 'code': 200, + 'commandResult': 0, + 'msg': 'OK.', + 'query': '/info/getLocations' + } + } + + return test_locations + + def get_standby(self): + """Mock for get_standby method.""" + _LOGGER.debug("STANDBY is: %s", self._standby) + return self._standby + + def get_tuned(self): + """Mock for get_tuned method.""" + _LOGGER.debug("get_tuned called") + if self._play: + self.attributes['offset'] = self.attributes['offset']+1 + + test_attributes = self.attributes + test_attributes['status'] = { + "code": 200, + "commandResult": 0, + "msg": "OK.", + "query": "/tv/getTuned" + } + return test_attributes + + def key_press(self, keypress): + """Mock for key_press method.""" + _LOGGER.debug("Key Press: %s", keypress) + if keypress == 'poweron': + self._standby = False + self._play = True + elif keypress == 'poweroff': + self._standby = True + self._play = False + elif keypress == 'play': + self._play = True + elif keypress == 'pause' or keypress == 'stop': + self._play = False + + def tune_channel(self, source): + """Mock for tune_channel method.""" + _LOGGER.debug("Change channel %s", source) + self.attributes['major'] = int(source) + + +class TestSetupDirectvMediaPlayer(unittest.TestCase): + """Test the different possibilities for setting up DirecTV media player.""" + + def setUp(self): + """Set up things to be run when tests are started.""" + self.hass = get_test_home_assistant() + + # Mocking DIRECTV class in DirectPy with our own. + self.current_directpy = sys.modules.get('DirectPy') + sys.modules['DirectPy'] = MagicMock() + import DirectPy + DirectPy.DIRECTV = mockDIRECTVClass + + self.addCleanup(self.tearDown) + + def tearDown(self): + """Stop everything that was started.""" + sys.modules['DirectPy'] = self.current_directpy + self.hass.stop() + + def test_setup_platform_config(self): + """Test setting up the platform from configuration.""" + + add_entities = Mock() + setup_platform( + self.hass, WORKING_CONFIG, add_entities) + assert add_entities.call_count == 1 + assert len(self.hass.data[DATA_DIRECTV]) == 1 + assert (WORKING_CONFIG[CONF_HOST], WORKING_CONFIG[CONF_DEVICE]) in\ + self.hass.data[DATA_DIRECTV] + + def test_setup_platform_discover(self): + """Test setting up the platform from discovery.""" + add_entities = Mock() + setup_platform(self.hass, {}, add_entities, + discovery_info=DISCOVERY_INFO) + assert add_entities.call_count == 1 + + def test_setup_platform_discover_duplicate(self): + """Test no duplicate entities are created.""" + add_entities = Mock() + setup_platform( + self.hass, WORKING_CONFIG, add_entities) + setup_platform(self.hass, {}, add_entities, + discovery_info=DISCOVERY_INFO) + assert add_entities.call_count == 2 + assert len(self.hass.data[DATA_DIRECTV]) == 1 + + def test_setup_platform_discover_client(self): + """Test additional devices are discovered.""" + add_entities = Mock() + + LOCATIONS.append({ + 'locationName': 'Client 1', + 'clientAddr': '1' + }) + LOCATIONS.append({ + 'locationName': 'Client 2', + 'clientAddr': '2' + }) + + setup_platform( + self.hass, WORKING_CONFIG, add_entities) + setup_platform(self.hass, {}, add_entities, + discovery_info=DISCOVERY_INFO) + del LOCATIONS[-1] + del LOCATIONS[-1] + assert add_entities.call_count == 2 + assert len(self.hass.data[DATA_DIRECTV]) == 3 + assert (IP_ADDRESS, '1') in self.hass.data[DATA_DIRECTV] + assert (IP_ADDRESS, '2') in self.hass.data[DATA_DIRECTV] + + +class TestDirectvMediaPlayer(unittest.TestCase): + """Test the DirecTV media player.""" + + def setUp(self): + """Set up things to be run when tests are started.""" + self.hass = get_test_home_assistant() + self.hass.start() + + # Mocking DIRECTV class in DirectPy with our own. + self.current_directpy = sys.modules.get('DirectPy') + sys.modules['DirectPy'] = MagicMock() + import DirectPy + DirectPy.DIRECTV = mockDIRECTVClass + + self.addCleanup(self.tearDown) + + self.main_entity_id = 'media_player.main_dvr' + self.client_entity_id = 'media_player.client_dvr' + self.config = { + 'media_player': [{ + 'platform': 'directv', + 'name': 'Main DVR', + 'host': IP_ADDRESS, + 'port': DEFAULT_PORT, + 'device': DEFAULT_DEVICE + }, { + 'platform': 'directv', + 'name': 'Client DVR', + 'host': IP_ADDRESS, + 'port': DEFAULT_PORT, + 'device': '1' + } + ] + } + + setup_component(self.hass, mp.DOMAIN, self.config) + self.hass.block_till_done() + + self.main_media_entity = self.hass.data['media_player'].get_entity( + self.main_entity_id) + + self.client_media_entity = self.hass.data['media_player'].get_entity( + self.client_entity_id) + + # Set the client so it seems a recording is being watched. + self.client_media_entity.dtv.attributes = RECORDING + # Clients do not support turning on, setting it as client is on here. + self.client_media_entity.dtv._standby = False + + self.main_media_entity.schedule_update_ha_state(True) + self.client_media_entity.schedule_update_ha_state(True) + self.hass.block_till_done() + + self.now = datetime(2018, 11, 19, 20, 0, 0, tzinfo=dt_util.UTC) + + def tearDown(self): + """Stop everything that was started.""" + sys.modules['DirectPy'] = self.current_directpy + self.hass.stop() + + def test_supported_features(self): + """Test supported features.""" + + # Features supported for main DVR + assert mp.SUPPORT_PAUSE | mp.SUPPORT_TURN_ON | mp.SUPPORT_TURN_OFF |\ + mp.SUPPORT_PLAY_MEDIA | mp.SUPPORT_SELECT_SOURCE |\ + mp.SUPPORT_STOP | mp.SUPPORT_NEXT_TRACK |\ + mp.SUPPORT_PREVIOUS_TRACK | mp.SUPPORT_PLAY ==\ + self.main_media_entity.supported_features + + # Feature supported for clients. + assert mp.SUPPORT_PAUSE |\ + mp.SUPPORT_PLAY_MEDIA | mp.SUPPORT_SELECT_SOURCE |\ + mp.SUPPORT_STOP | mp.SUPPORT_NEXT_TRACK |\ + mp.SUPPORT_PREVIOUS_TRACK | mp.SUPPORT_PLAY ==\ + self.client_media_entity.supported_features + + def test_check_attributes(self): + """Test attributes""" + + # Start playing TV + with patch('homeassistant.helpers.condition.dt_util.now', + return_value=self.now): + common.media_play(self.hass, self.client_entity_id) + self.hass.block_till_done() + + state = self.hass.states.get(self.client_entity_id) + assert state.state == STATE_PLAYING + + assert state.attributes.get(mp.ATTR_MEDIA_CONTENT_ID) == \ + RECORDING['programId'] + assert state.attributes.get(mp.ATTR_MEDIA_CONTENT_TYPE) == \ + mp.MEDIA_TYPE_TVSHOW + assert state.attributes.get(mp.ATTR_MEDIA_DURATION) == \ + RECORDING['duration'] + assert state.attributes.get(mp.ATTR_MEDIA_POSITION) == 2 + assert state.attributes.get(mp.ATTR_MEDIA_POSITION_UPDATED_AT) == \ + self.now + assert state.attributes.get(mp.ATTR_MEDIA_TITLE) == \ + RECORDING['title'] + assert state.attributes.get(mp.ATTR_MEDIA_SERIES_TITLE) == \ + RECORDING['episodeTitle'] + assert state.attributes.get(mp.ATTR_MEDIA_CHANNEL) == \ + "{} ({})".format(RECORDING['callsign'], RECORDING['major']) + assert state.attributes.get(mp.ATTR_INPUT_SOURCE) == \ + RECORDING['major'] + assert state.attributes.get(ATTR_MEDIA_CURRENTLY_RECORDING) == \ + RECORDING['isRecording'] + assert state.attributes.get(ATTR_MEDIA_RATING) == \ + RECORDING['rating'] + assert state.attributes.get(ATTR_MEDIA_RECORDED) + assert state.attributes.get(ATTR_MEDIA_START_TIME) == \ + datetime(2018, 11, 10, 19, 0, tzinfo=dt_util.UTC) + + # Test to make sure that ATTR_MEDIA_POSITION_UPDATED_AT is not + # updated if TV is paused. + with patch('homeassistant.helpers.condition.dt_util.now', + return_value=self.now + timedelta(minutes=5)): + common.media_pause(self.hass, self.client_entity_id) + self.hass.block_till_done() + + state = self.hass.states.get(self.client_entity_id) + assert state.state == STATE_PAUSED + assert state.attributes.get(mp.ATTR_MEDIA_POSITION_UPDATED_AT) == \ + self.now + + def test_main_services(self): + """Test the different services.""" + + # DVR starts in turned off state. + state = self.hass.states.get(self.main_entity_id) + assert state.state == STATE_OFF + + # Turn main DVR on. When turning on DVR is playing. + common.turn_on(self.hass, self.main_entity_id) + self.hass.block_till_done() + state = self.hass.states.get(self.main_entity_id) + assert state.state == STATE_PLAYING + + # Pause live TV. + common.media_pause(self.hass, self.main_entity_id) + self.hass.block_till_done() + state = self.hass.states.get(self.main_entity_id) + assert state.state == STATE_PAUSED + + # Start play again for live TV. + common.media_play(self.hass, self.main_entity_id) + self.hass.block_till_done() + state = self.hass.states.get(self.main_entity_id) + assert state.state == STATE_PLAYING + + # Change channel, currently it should be 202 + assert state.attributes.get('source') == 202 + common.select_source(self.hass, 7, self.main_entity_id) + self.hass.block_till_done() + state = self.hass.states.get(self.main_entity_id) + assert state.attributes.get('source') == 7 + + # Stop live TV. + common.media_stop(self.hass, self.main_entity_id) + self.hass.block_till_done() + state = self.hass.states.get(self.main_entity_id) + assert state.state == STATE_PAUSED + + # Turn main DVR off. + common.turn_off(self.hass, self.main_entity_id) + self.hass.block_till_done() + state = self.hass.states.get(self.main_entity_id) + assert state.state == STATE_OFF + + def test_available(self): + """Test available status.""" + + # Confirm service is currently set to available. + assert self.main_media_entity.available + + # Make update fail (i.e. DVR offline) + dtv = self.main_media_entity.dtv + self.main_media_entity.dtv = None + self.main_media_entity.schedule_update_ha_state(True) + self.hass.block_till_done() + assert not self.main_media_entity.available + + # Make update work again (i.e. DVR back online) + self.main_media_entity.dtv = dtv + self.main_media_entity.schedule_update_ha_state(True) + self.hass.block_till_done() + assert self.main_media_entity.available From a838e5cfeb670592f3b53eb9745e359d8f69d48d Mon Sep 17 00:00:00 2001 From: Erik Hendrix Date: Mon, 19 Nov 2018 18:40:10 -0700 Subject: [PATCH 02/20] Multiple improvements Fixed lint issue in common.py Fixed lint issues in test_directv.py Improved patching import using modile_patcher.start() and stop() Added asserts for service calls. --- tests/components/media_player/common.py | 1 + tests/components/media_player/test_directv.py | 101 +++++++++++------- 2 files changed, 63 insertions(+), 39 deletions(-) diff --git a/tests/components/media_player/common.py b/tests/components/media_player/common.py index effcecfd6476f..2174967eae53f 100644 --- a/tests/components/media_player/common.py +++ b/tests/components/media_player/common.py @@ -94,6 +94,7 @@ def media_pause(hass, entity_id=None): data = {ATTR_ENTITY_ID: entity_id} if entity_id else {} hass.services.call(DOMAIN, SERVICE_MEDIA_PAUSE, data) + @bind_hass def media_stop(hass, entity_id=None): """Send the media player the command for stop.""" diff --git a/tests/components/media_player/test_directv.py b/tests/components/media_player/test_directv.py index 6fed185eac1f5..ffc192f35b5f5 100644 --- a/tests/components/media_player/test_directv.py +++ b/tests/components/media_player/test_directv.py @@ -167,8 +167,12 @@ def setUp(self): self.hass = get_test_home_assistant() # Mocking DIRECTV class in DirectPy with our own. - self.current_directpy = sys.modules.get('DirectPy') - sys.modules['DirectPy'] = MagicMock() + self.directpy_mock = MagicMock() + modules = { + 'DirectPy': self.directpy_mock + } + self.module_patcher = patch.dict('sys.modules', modules) + self.module_patcher.start() import DirectPy DirectPy.DIRECTV = mockDIRECTVClass @@ -176,12 +180,11 @@ def setUp(self): def tearDown(self): """Stop everything that was started.""" - sys.modules['DirectPy'] = self.current_directpy self.hass.stop() + self.module_patcher.stop() def test_setup_platform_config(self): """Test setting up the platform from configuration.""" - add_entities = Mock() setup_platform( self.hass, WORKING_CONFIG, add_entities) @@ -294,7 +297,6 @@ def tearDown(self): def test_supported_features(self): """Test supported features.""" - # Features supported for main DVR assert mp.SUPPORT_PAUSE | mp.SUPPORT_TURN_ON | mp.SUPPORT_TURN_OFF |\ mp.SUPPORT_PLAY_MEDIA | mp.SUPPORT_SELECT_SOURCE |\ @@ -310,8 +312,7 @@ def test_supported_features(self): self.client_media_entity.supported_features def test_check_attributes(self): - """Test attributes""" - + """Test attributes.""" # Start playing TV with patch('homeassistant.helpers.condition.dt_util.now', return_value=self.now): @@ -360,51 +361,73 @@ def test_check_attributes(self): def test_main_services(self): """Test the different services.""" + dtv_inst = self.main_media_entity.dtv # DVR starts in turned off state. state = self.hass.states.get(self.main_entity_id) assert state.state == STATE_OFF - # Turn main DVR on. When turning on DVR is playing. - common.turn_on(self.hass, self.main_entity_id) - self.hass.block_till_done() - state = self.hass.states.get(self.main_entity_id) - assert state.state == STATE_PLAYING + # All these should call key_press in our class. + with patch.object(dtv_inst, 'key_press', + wraps=dtv_inst.key_press) as mock_key_press, \ + patch.object(dtv_inst, 'tune_channel', + wraps=dtv_inst.tune_channel) as mock_tune_channel, \ + patch.object(dtv_inst, 'get_tuned', + wraps=dtv_inst.get_tuned) as mock_get_tuned, \ + patch.object(dtv_inst, 'get_standby', + wraps=dtv_inst.get_standby) as mock_get_standby: + + # Turn main DVR on. When turning on DVR is playing. + common.turn_on(self.hass, self.main_entity_id) + self.hass.block_till_done() + mock_key_press.assert_called_with('poweron') + state = self.hass.states.get(self.main_entity_id) + assert state.state == STATE_PLAYING - # Pause live TV. - common.media_pause(self.hass, self.main_entity_id) - self.hass.block_till_done() - state = self.hass.states.get(self.main_entity_id) - assert state.state == STATE_PAUSED + # Pause live TV. + common.media_pause(self.hass, self.main_entity_id) + self.hass.block_till_done() + mock_key_press.assert_called_with('pause') + state = self.hass.states.get(self.main_entity_id) + assert state.state == STATE_PAUSED - # Start play again for live TV. - common.media_play(self.hass, self.main_entity_id) - self.hass.block_till_done() - state = self.hass.states.get(self.main_entity_id) - assert state.state == STATE_PLAYING + # Start play again for live TV. + common.media_play(self.hass, self.main_entity_id) + self.hass.block_till_done() + mock_key_press.assert_called_with('play') + state = self.hass.states.get(self.main_entity_id) + assert state.state == STATE_PLAYING - # Change channel, currently it should be 202 - assert state.attributes.get('source') == 202 - common.select_source(self.hass, 7, self.main_entity_id) - self.hass.block_till_done() - state = self.hass.states.get(self.main_entity_id) - assert state.attributes.get('source') == 7 + # Change channel, currently it should be 202 + assert state.attributes.get('source') == 202 + common.select_source(self.hass, 7, self.main_entity_id) + self.hass.block_till_done() + mock_tune_channel.assert_called_with('7') + state = self.hass.states.get(self.main_entity_id) + assert state.attributes.get('source') == 7 - # Stop live TV. - common.media_stop(self.hass, self.main_entity_id) - self.hass.block_till_done() - state = self.hass.states.get(self.main_entity_id) - assert state.state == STATE_PAUSED + # Stop live TV. + common.media_stop(self.hass, self.main_entity_id) + self.hass.block_till_done() + mock_key_press.assert_called_with('stop') + state = self.hass.states.get(self.main_entity_id) + assert state.state == STATE_PAUSED - # Turn main DVR off. - common.turn_off(self.hass, self.main_entity_id) - self.hass.block_till_done() - state = self.hass.states.get(self.main_entity_id) - assert state.state == STATE_OFF + # Turn main DVR off. + common.turn_off(self.hass, self.main_entity_id) + self.hass.block_till_done() + mock_key_press.assert_called_with('poweroff') + state = self.hass.states.get(self.main_entity_id) + assert state.state == STATE_OFF + + # There should have been 6 calls to check if DVR is in standby + assert 6 == mock_get_standby.call_count + # There should be 5 calls to get current info (only 1 time it will + # not be called as DVR is in standby.) + assert 5 == mock_get_tuned.call_count def test_available(self): """Test available status.""" - # Confirm service is currently set to available. assert self.main_media_entity.available From 4028a2f9a41ebc18f8585fea22f865f5af8e9330 Mon Sep 17 00:00:00 2001 From: Erik Hendrix Date: Tue, 20 Nov 2018 12:18:28 -0700 Subject: [PATCH 03/20] Updates based on Martin's review Updates based on Martin's review. --- tests/components/media_player/test_directv.py | 647 ++++++++++-------- 1 file changed, 354 insertions(+), 293 deletions(-) diff --git a/tests/components/media_player/test_directv.py b/tests/components/media_player/test_directv.py index ffc192f35b5f5..00c59767dde1e 100644 --- a/tests/components/media_player/test_directv.py +++ b/tests/components/media_player/test_directv.py @@ -1,48 +1,38 @@ """The tests for the DirecTV Media player platform.""" -import unittest -from unittest.mock import patch, Mock, MagicMock - +from unittest.mock import patch, Mock from datetime import datetime, timedelta import logging -import sys +import pytest -from homeassistant.setup import setup_component import homeassistant.components.media_player as mp +from homeassistant.components.media_player import ( + ATTR_INPUT_SOURCE, ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_CONTENT_TYPE, + ATTR_MEDIA_ENQUEUE, DOMAIN, SERVICE_PLAY_MEDIA, SERVICE_SELECT_SOURCE) + from homeassistant.components.media_player.directv import ( ATTR_MEDIA_CURRENTLY_RECORDING, ATTR_MEDIA_RATING, ATTR_MEDIA_RECORDED, ATTR_MEDIA_START_TIME, DATA_DIRECTV, DEFAULT_DEVICE, DEFAULT_PORT, setup_platform) from homeassistant.const import ( - CONF_DEVICE, CONF_HOST, CONF_NAME, CONF_PORT, STATE_OFF, STATE_PAUSED, - STATE_PLAYING) + ATTR_ENTITY_ID, CONF_DEVICE, CONF_HOST, CONF_NAME, CONF_PORT, + SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_PLAY, + SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_MEDIA_STOP, SERVICE_TURN_OFF, + SERVICE_TURN_ON, STATE_OFF, STATE_PAUSED, STATE_PLAYING) +from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util -from tests.common import get_test_home_assistant -from tests.components.media_player import common - +from tests.common import MockDependency +CLIENT_ENTITY_ID = 'media_player.client_dvr' +MAIN_ENTITY_ID = 'media_player.main_dvr' IP_ADDRESS = '127.0.0.1' -WORKING_CONFIG = { - CONF_HOST: IP_ADDRESS, - CONF_NAME: 'Main DVR', - CONF_PORT: DEFAULT_PORT, - CONF_DEVICE: DEFAULT_DEVICE -} - DISCOVERY_INFO = { 'host': IP_ADDRESS, 'serial': 1234 } -LOCATIONS = [ - { - 'locationName': 'Main DVR', - 'clientAddr': DEFAULT_DEVICE - } -] - LIVE = { "callsign": "CNNHD", "date": "20181110", @@ -62,6 +52,13 @@ "title": "CNN Newsroom With Fredricka Whitfield" } +LOCATIONS = [ + { + 'locationName': 'Main DVR', + 'clientAddr': DEFAULT_DEVICE + } +] + RECORDING = { "callsign": "CNNHD", "date": "20181110", @@ -83,9 +80,131 @@ 'episodeTitle': 'CNN Recorded' } +WORKING_CONFIG = { + 'media_player': { + 'platform': 'directv', + CONF_HOST: IP_ADDRESS, + CONF_NAME: 'Main DVR', + CONF_PORT: DEFAULT_PORT, + CONF_DEVICE: DEFAULT_DEVICE + } +} + _LOGGER = logging.getLogger(__name__) +@pytest.fixture +def platforms(): + """Fixture for setting up test platforms.""" + config = { + 'media_player': [{ + 'platform': 'directv', + 'name': 'Main DVR', + 'host': IP_ADDRESS, + 'port': DEFAULT_PORT, + 'device': DEFAULT_DEVICE + }, { + 'platform': 'directv', + 'name': 'Client DVR', + 'host': IP_ADDRESS, + 'port': DEFAULT_PORT, + 'device': '1' + }] + } + + async def setup_test_platforms(hass): + with MockDependency('DirectPy'), \ + patch('DirectPy.DIRECTV', new=mockDIRECTVClass): + await async_setup_component(hass, mp.DOMAIN, config) + await hass.async_block_till_done() + + main_media_entity = hass.data['media_player'].get_entity( + MAIN_ENTITY_ID) + + client_media_entity = hass.data['media_player'].get_entity( + CLIENT_ENTITY_ID) + + # Set the client so it seems a recording is being watched. + client_media_entity.dtv.attributes = RECORDING + # Clients do not support turning on, setting it as client is on here. + client_media_entity.dtv._standby = False + + main_media_entity.schedule_update_ha_state(True) + client_media_entity.schedule_update_ha_state(True) + await hass.async_block_till_done() + + return (main_media_entity, client_media_entity) + + return setup_test_platforms + + +async def async_turn_on(hass, entity_id=None): + """Turn on specified media player or all.""" + data = {ATTR_ENTITY_ID: entity_id} if entity_id else {} + await hass.services.async_call(DOMAIN, SERVICE_TURN_ON, data) + + +async def async_turn_off(hass, entity_id=None): + """Turn off specified media player or all.""" + data = {ATTR_ENTITY_ID: entity_id} if entity_id else {} + await hass.services.async_call(DOMAIN, SERVICE_TURN_OFF, data) + + +async def async_media_pause(hass, entity_id=None): + """Send the media player the command for pause.""" + data = {ATTR_ENTITY_ID: entity_id} if entity_id else {} + await hass.services.async_call(DOMAIN, SERVICE_MEDIA_PAUSE, data) + + +async def async_media_play(hass, entity_id=None): + """Send the media player the command for play/pause.""" + data = {ATTR_ENTITY_ID: entity_id} if entity_id else {} + await hass.services.async_call(DOMAIN, SERVICE_MEDIA_PLAY, data) + + +async def async_media_stop(hass, entity_id=None): + """Send the media player the command for stop.""" + data = {ATTR_ENTITY_ID: entity_id} if entity_id else {} + await hass.services.async_call(DOMAIN, SERVICE_MEDIA_STOP, data) + + +async def async_media_next_track(hass, entity_id=None): + """Send the media player the command for next track.""" + data = {ATTR_ENTITY_ID: entity_id} if entity_id else {} + await hass.services.async_call(DOMAIN, SERVICE_MEDIA_NEXT_TRACK, data) + + +async def async_media_previous_track(hass, entity_id=None): + """Send the media player the command for prev track.""" + data = {ATTR_ENTITY_ID: entity_id} if entity_id else {} + await hass.services.async_call(DOMAIN, SERVICE_MEDIA_PREVIOUS_TRACK, data) + + +async def async_play_media(hass, media_type, media_id, entity_id=None, + enqueue=None): + """Send the media player the command for playing media.""" + data = {ATTR_MEDIA_CONTENT_TYPE: media_type, + ATTR_MEDIA_CONTENT_ID: media_id} + + if entity_id: + data[ATTR_ENTITY_ID] = entity_id + + if enqueue: + data[ATTR_MEDIA_ENQUEUE] = enqueue + + await hass.services.async_call(DOMAIN, SERVICE_PLAY_MEDIA, data) + + +async def async_select_source(hass, source, entity_id=None): + """Send the media player the command to select input source.""" + data = {ATTR_INPUT_SOURCE: source} + + if entity_id: + data[ATTR_ENTITY_ID] = entity_id + + await hass.services.async_call(DOMAIN, SERVICE_SELECT_SOURCE, data) + + class mockDIRECTVClass: """A fake DirecTV DVR device.""" @@ -159,287 +278,229 @@ def tune_channel(self, source): self.attributes['major'] = int(source) -class TestSetupDirectvMediaPlayer(unittest.TestCase): - """Test the different possibilities for setting up DirecTV media player.""" +async def test_setup_platform_config(hass): + """Test setting up the platform from configuration.""" + with MockDependency('DirectPy'), \ + patch('DirectPy.DIRECTV', new=mockDIRECTVClass): - def setUp(self): - """Set up things to be run when tests are started.""" - self.hass = get_test_home_assistant() + await async_setup_component(hass, mp.DOMAIN, WORKING_CONFIG) + await hass.async_block_till_done() - # Mocking DIRECTV class in DirectPy with our own. - self.directpy_mock = MagicMock() - modules = { - 'DirectPy': self.directpy_mock - } - self.module_patcher = patch.dict('sys.modules', modules) - self.module_patcher.start() - import DirectPy - DirectPy.DIRECTV = mockDIRECTVClass - - self.addCleanup(self.tearDown) - - def tearDown(self): - """Stop everything that was started.""" - self.hass.stop() - self.module_patcher.stop() - - def test_setup_platform_config(self): - """Test setting up the platform from configuration.""" - add_entities = Mock() - setup_platform( - self.hass, WORKING_CONFIG, add_entities) - assert add_entities.call_count == 1 - assert len(self.hass.data[DATA_DIRECTV]) == 1 - assert (WORKING_CONFIG[CONF_HOST], WORKING_CONFIG[CONF_DEVICE]) in\ - self.hass.data[DATA_DIRECTV] - - def test_setup_platform_discover(self): - """Test setting up the platform from discovery.""" - add_entities = Mock() - setup_platform(self.hass, {}, add_entities, - discovery_info=DISCOVERY_INFO) - assert add_entities.call_count == 1 - - def test_setup_platform_discover_duplicate(self): - """Test no duplicate entities are created.""" - add_entities = Mock() - setup_platform( - self.hass, WORKING_CONFIG, add_entities) - setup_platform(self.hass, {}, add_entities, + assert len(hass.data[DATA_DIRECTV]) == 1 + assert (IP_ADDRESS, DEFAULT_DEVICE) in hass.data[DATA_DIRECTV] + + +async def test_setup_platform_discover(hass): + """Test setting up the platform from discovery.""" + add_entities = Mock() + with MockDependency('DirectPy'), \ + patch('DirectPy.DIRECTV', new=mockDIRECTVClass): + + setup_platform(hass, {}, add_entities, discovery_info=DISCOVERY_INFO) - assert add_entities.call_count == 2 - assert len(self.hass.data[DATA_DIRECTV]) == 1 - - def test_setup_platform_discover_client(self): - """Test additional devices are discovered.""" - add_entities = Mock() - - LOCATIONS.append({ - 'locationName': 'Client 1', - 'clientAddr': '1' - }) - LOCATIONS.append({ - 'locationName': 'Client 2', - 'clientAddr': '2' - }) - - setup_platform( - self.hass, WORKING_CONFIG, add_entities) - setup_platform(self.hass, {}, add_entities, + + assert len(hass.data[DATA_DIRECTV]) == 1 + assert add_entities.call_count == 1 + + +async def test_setup_platform_discover_duplicate(hass): + """Test setting up the platform from discovery.""" + add_entities = Mock() + + with MockDependency('DirectPy'), \ + patch('DirectPy.DIRECTV', new=mockDIRECTVClass): + + await async_setup_component(hass, mp.DOMAIN, WORKING_CONFIG) + await hass.async_block_till_done() + + setup_platform(hass, {}, add_entities, discovery_info=DISCOVERY_INFO) - del LOCATIONS[-1] - del LOCATIONS[-1] - assert add_entities.call_count == 2 - assert len(self.hass.data[DATA_DIRECTV]) == 3 - assert (IP_ADDRESS, '1') in self.hass.data[DATA_DIRECTV] - assert (IP_ADDRESS, '2') in self.hass.data[DATA_DIRECTV] - - -class TestDirectvMediaPlayer(unittest.TestCase): - """Test the DirecTV media player.""" - - def setUp(self): - """Set up things to be run when tests are started.""" - self.hass = get_test_home_assistant() - self.hass.start() - - # Mocking DIRECTV class in DirectPy with our own. - self.current_directpy = sys.modules.get('DirectPy') - sys.modules['DirectPy'] = MagicMock() - import DirectPy - DirectPy.DIRECTV = mockDIRECTVClass - - self.addCleanup(self.tearDown) - - self.main_entity_id = 'media_player.main_dvr' - self.client_entity_id = 'media_player.client_dvr' - self.config = { - 'media_player': [{ - 'platform': 'directv', - 'name': 'Main DVR', - 'host': IP_ADDRESS, - 'port': DEFAULT_PORT, - 'device': DEFAULT_DEVICE - }, { - 'platform': 'directv', - 'name': 'Client DVR', - 'host': IP_ADDRESS, - 'port': DEFAULT_PORT, - 'device': '1' - } - ] - } - setup_component(self.hass, mp.DOMAIN, self.config) - self.hass.block_till_done() + assert len(hass.data[DATA_DIRECTV]) == 1 + assert add_entities.call_count == 1 - self.main_media_entity = self.hass.data['media_player'].get_entity( - self.main_entity_id) - self.client_media_entity = self.hass.data['media_player'].get_entity( - self.client_entity_id) +async def test_setup_platform_discover_client(hass): + """Test setting up the platform from discovery.""" + add_entities = Mock() - # Set the client so it seems a recording is being watched. - self.client_media_entity.dtv.attributes = RECORDING - # Clients do not support turning on, setting it as client is on here. - self.client_media_entity.dtv._standby = False - - self.main_media_entity.schedule_update_ha_state(True) - self.client_media_entity.schedule_update_ha_state(True) - self.hass.block_till_done() - - self.now = datetime(2018, 11, 19, 20, 0, 0, tzinfo=dt_util.UTC) - - def tearDown(self): - """Stop everything that was started.""" - sys.modules['DirectPy'] = self.current_directpy - self.hass.stop() - - def test_supported_features(self): - """Test supported features.""" - # Features supported for main DVR - assert mp.SUPPORT_PAUSE | mp.SUPPORT_TURN_ON | mp.SUPPORT_TURN_OFF |\ - mp.SUPPORT_PLAY_MEDIA | mp.SUPPORT_SELECT_SOURCE |\ - mp.SUPPORT_STOP | mp.SUPPORT_NEXT_TRACK |\ - mp.SUPPORT_PREVIOUS_TRACK | mp.SUPPORT_PLAY ==\ - self.main_media_entity.supported_features - - # Feature supported for clients. - assert mp.SUPPORT_PAUSE |\ - mp.SUPPORT_PLAY_MEDIA | mp.SUPPORT_SELECT_SOURCE |\ - mp.SUPPORT_STOP | mp.SUPPORT_NEXT_TRACK |\ - mp.SUPPORT_PREVIOUS_TRACK | mp.SUPPORT_PLAY ==\ - self.client_media_entity.supported_features - - def test_check_attributes(self): - """Test attributes.""" - # Start playing TV - with patch('homeassistant.helpers.condition.dt_util.now', - return_value=self.now): - common.media_play(self.hass, self.client_entity_id) - self.hass.block_till_done() - - state = self.hass.states.get(self.client_entity_id) + LOCATIONS.append({ + 'locationName': 'Client 1', + 'clientAddr': '1' + }) + LOCATIONS.append({ + 'locationName': 'Client 2', + 'clientAddr': '2' + }) + + with MockDependency('DirectPy'), \ + patch('DirectPy.DIRECTV', new=mockDIRECTVClass): + + await async_setup_component(hass, mp.DOMAIN, WORKING_CONFIG) + await hass.async_block_till_done() + + setup_platform(hass, {}, add_entities, + discovery_info=DISCOVERY_INFO) + + del LOCATIONS[-1] + del LOCATIONS[-1] + assert len(hass.data[DATA_DIRECTV]) == 3 + assert (IP_ADDRESS, '1') in hass.data[DATA_DIRECTV] + assert (IP_ADDRESS, '2') in hass.data[DATA_DIRECTV] + + +async def test_supported_features(hass, platforms): + """Test supported features.""" + main_media_entity, client_media_entity = await platforms(hass) + + # Features supported for main DVR + assert mp.SUPPORT_PAUSE | mp.SUPPORT_TURN_ON | mp.SUPPORT_TURN_OFF |\ + mp.SUPPORT_PLAY_MEDIA | mp.SUPPORT_SELECT_SOURCE |\ + mp.SUPPORT_STOP | mp.SUPPORT_NEXT_TRACK |\ + mp.SUPPORT_PREVIOUS_TRACK | mp.SUPPORT_PLAY ==\ + main_media_entity.supported_features + + # Feature supported for clients. + assert mp.SUPPORT_PAUSE |\ + mp.SUPPORT_PLAY_MEDIA | mp.SUPPORT_SELECT_SOURCE |\ + mp.SUPPORT_STOP | mp.SUPPORT_NEXT_TRACK |\ + mp.SUPPORT_PREVIOUS_TRACK | mp.SUPPORT_PLAY ==\ + client_media_entity.supported_features + + +async def test_check_attributes(hass, platforms): + """Test attributes.""" + main_media_entity, client_media_entity = await platforms(hass) + now = datetime(2018, 11, 19, 20, 0, 0, tzinfo=dt_util.UTC) + + # Start playing TV + with patch('homeassistant.helpers.condition.dt_util.now', + return_value=now): + await async_media_play(hass, CLIENT_ENTITY_ID) + await hass.async_block_till_done() + + state = hass.states.get(CLIENT_ENTITY_ID) + assert state.state == STATE_PLAYING + + assert state.attributes.get(mp.ATTR_MEDIA_CONTENT_ID) == \ + RECORDING['programId'] + assert state.attributes.get(mp.ATTR_MEDIA_CONTENT_TYPE) == \ + mp.MEDIA_TYPE_TVSHOW + assert state.attributes.get(mp.ATTR_MEDIA_DURATION) == \ + RECORDING['duration'] + assert state.attributes.get(mp.ATTR_MEDIA_POSITION) == 2 + assert state.attributes.get(mp.ATTR_MEDIA_POSITION_UPDATED_AT) == now + assert state.attributes.get(mp.ATTR_MEDIA_TITLE) == RECORDING['title'] + assert state.attributes.get(mp.ATTR_MEDIA_SERIES_TITLE) == \ + RECORDING['episodeTitle'] + assert state.attributes.get(mp.ATTR_MEDIA_CHANNEL) == \ + "{} ({})".format(RECORDING['callsign'], RECORDING['major']) + assert state.attributes.get(mp.ATTR_INPUT_SOURCE) == RECORDING['major'] + assert state.attributes.get(ATTR_MEDIA_CURRENTLY_RECORDING) == \ + RECORDING['isRecording'] + assert state.attributes.get(ATTR_MEDIA_RATING) == RECORDING['rating'] + assert state.attributes.get(ATTR_MEDIA_RECORDED) + assert state.attributes.get(ATTR_MEDIA_START_TIME) == \ + datetime(2018, 11, 10, 19, 0, tzinfo=dt_util.UTC) + + # Test to make sure that ATTR_MEDIA_POSITION_UPDATED_AT is not + # updated if TV is paused. + with patch('homeassistant.helpers.condition.dt_util.now', + return_value=now + timedelta(minutes=5)): + await async_media_pause(hass, CLIENT_ENTITY_ID) + await hass.async_block_till_done() + + state = hass.states.get(CLIENT_ENTITY_ID) + assert state.state == STATE_PAUSED + assert state.attributes.get(mp.ATTR_MEDIA_POSITION_UPDATED_AT) == now + + +async def test_main_services(hass, platforms): + """Test the different services.""" + main_media_entity, client_media_entity = await platforms(hass) + + dtv_inst = main_media_entity.dtv + + # DVR starts in turned off state. + state = hass.states.get(MAIN_ENTITY_ID) + assert state.state == STATE_OFF + + # All these should call key_press in our class. + with patch.object(dtv_inst, 'key_press', + wraps=dtv_inst.key_press) as mock_key_press, \ + patch.object(dtv_inst, 'tune_channel', + wraps=dtv_inst.tune_channel) as mock_tune_channel, \ + patch.object(dtv_inst, 'get_tuned', + wraps=dtv_inst.get_tuned) as mock_get_tuned, \ + patch.object(dtv_inst, 'get_standby', + wraps=dtv_inst.get_standby) as mock_get_standby: + + # Turn main DVR on. When turning on DVR is playing. + await async_turn_on(hass, MAIN_ENTITY_ID) + await hass.async_block_till_done() + mock_key_press.assert_called_with('poweron') + state = hass.states.get(MAIN_ENTITY_ID) assert state.state == STATE_PLAYING - assert state.attributes.get(mp.ATTR_MEDIA_CONTENT_ID) == \ - RECORDING['programId'] - assert state.attributes.get(mp.ATTR_MEDIA_CONTENT_TYPE) == \ - mp.MEDIA_TYPE_TVSHOW - assert state.attributes.get(mp.ATTR_MEDIA_DURATION) == \ - RECORDING['duration'] - assert state.attributes.get(mp.ATTR_MEDIA_POSITION) == 2 - assert state.attributes.get(mp.ATTR_MEDIA_POSITION_UPDATED_AT) == \ - self.now - assert state.attributes.get(mp.ATTR_MEDIA_TITLE) == \ - RECORDING['title'] - assert state.attributes.get(mp.ATTR_MEDIA_SERIES_TITLE) == \ - RECORDING['episodeTitle'] - assert state.attributes.get(mp.ATTR_MEDIA_CHANNEL) == \ - "{} ({})".format(RECORDING['callsign'], RECORDING['major']) - assert state.attributes.get(mp.ATTR_INPUT_SOURCE) == \ - RECORDING['major'] - assert state.attributes.get(ATTR_MEDIA_CURRENTLY_RECORDING) == \ - RECORDING['isRecording'] - assert state.attributes.get(ATTR_MEDIA_RATING) == \ - RECORDING['rating'] - assert state.attributes.get(ATTR_MEDIA_RECORDED) - assert state.attributes.get(ATTR_MEDIA_START_TIME) == \ - datetime(2018, 11, 10, 19, 0, tzinfo=dt_util.UTC) - - # Test to make sure that ATTR_MEDIA_POSITION_UPDATED_AT is not - # updated if TV is paused. - with patch('homeassistant.helpers.condition.dt_util.now', - return_value=self.now + timedelta(minutes=5)): - common.media_pause(self.hass, self.client_entity_id) - self.hass.block_till_done() - - state = self.hass.states.get(self.client_entity_id) + # Pause live TV. + await async_media_pause(hass, MAIN_ENTITY_ID) + await hass.async_block_till_done() + mock_key_press.assert_called_with('pause') + state = hass.states.get(MAIN_ENTITY_ID) assert state.state == STATE_PAUSED - assert state.attributes.get(mp.ATTR_MEDIA_POSITION_UPDATED_AT) == \ - self.now - def test_main_services(self): - """Test the different services.""" - dtv_inst = self.main_media_entity.dtv + # Start play again for live TV. + await async_media_play(hass, MAIN_ENTITY_ID) + await hass.async_block_till_done() + mock_key_press.assert_called_with('play') + state = hass.states.get(MAIN_ENTITY_ID) + assert state.state == STATE_PLAYING + + # Change channel, currently it should be 202 + assert state.attributes.get('source') == 202 + await async_select_source(hass, 7, MAIN_ENTITY_ID) + await hass.async_block_till_done() + mock_tune_channel.assert_called_with('7') + state = hass.states.get(MAIN_ENTITY_ID) + assert state.attributes.get('source') == 7 + + # Stop live TV. + await async_media_stop(hass, MAIN_ENTITY_ID) + await hass.async_block_till_done() + mock_key_press.assert_called_with('stop') + state = hass.states.get(MAIN_ENTITY_ID) + assert state.state == STATE_PAUSED - # DVR starts in turned off state. - state = self.hass.states.get(self.main_entity_id) + # Turn main DVR off. + await async_turn_off(hass, MAIN_ENTITY_ID) + await hass.async_block_till_done() + mock_key_press.assert_called_with('poweroff') + state = hass.states.get(MAIN_ENTITY_ID) assert state.state == STATE_OFF - # All these should call key_press in our class. - with patch.object(dtv_inst, 'key_press', - wraps=dtv_inst.key_press) as mock_key_press, \ - patch.object(dtv_inst, 'tune_channel', - wraps=dtv_inst.tune_channel) as mock_tune_channel, \ - patch.object(dtv_inst, 'get_tuned', - wraps=dtv_inst.get_tuned) as mock_get_tuned, \ - patch.object(dtv_inst, 'get_standby', - wraps=dtv_inst.get_standby) as mock_get_standby: - - # Turn main DVR on. When turning on DVR is playing. - common.turn_on(self.hass, self.main_entity_id) - self.hass.block_till_done() - mock_key_press.assert_called_with('poweron') - state = self.hass.states.get(self.main_entity_id) - assert state.state == STATE_PLAYING - - # Pause live TV. - common.media_pause(self.hass, self.main_entity_id) - self.hass.block_till_done() - mock_key_press.assert_called_with('pause') - state = self.hass.states.get(self.main_entity_id) - assert state.state == STATE_PAUSED - - # Start play again for live TV. - common.media_play(self.hass, self.main_entity_id) - self.hass.block_till_done() - mock_key_press.assert_called_with('play') - state = self.hass.states.get(self.main_entity_id) - assert state.state == STATE_PLAYING - - # Change channel, currently it should be 202 - assert state.attributes.get('source') == 202 - common.select_source(self.hass, 7, self.main_entity_id) - self.hass.block_till_done() - mock_tune_channel.assert_called_with('7') - state = self.hass.states.get(self.main_entity_id) - assert state.attributes.get('source') == 7 - - # Stop live TV. - common.media_stop(self.hass, self.main_entity_id) - self.hass.block_till_done() - mock_key_press.assert_called_with('stop') - state = self.hass.states.get(self.main_entity_id) - assert state.state == STATE_PAUSED - - # Turn main DVR off. - common.turn_off(self.hass, self.main_entity_id) - self.hass.block_till_done() - mock_key_press.assert_called_with('poweroff') - state = self.hass.states.get(self.main_entity_id) - assert state.state == STATE_OFF - - # There should have been 6 calls to check if DVR is in standby - assert 6 == mock_get_standby.call_count - # There should be 5 calls to get current info (only 1 time it will - # not be called as DVR is in standby.) - assert 5 == mock_get_tuned.call_count - - def test_available(self): - """Test available status.""" - # Confirm service is currently set to available. - assert self.main_media_entity.available - - # Make update fail (i.e. DVR offline) - dtv = self.main_media_entity.dtv - self.main_media_entity.dtv = None - self.main_media_entity.schedule_update_ha_state(True) - self.hass.block_till_done() - assert not self.main_media_entity.available - - # Make update work again (i.e. DVR back online) - self.main_media_entity.dtv = dtv - self.main_media_entity.schedule_update_ha_state(True) - self.hass.block_till_done() - assert self.main_media_entity.available + # There should have been 6 calls to check if DVR is in standby + assert 6 == mock_get_standby.call_count + # There should be 5 calls to get current info (only 1 time it will + # not be called as DVR is in standby.) + assert 5 == mock_get_tuned.call_count + + +async def test_available(hass, platforms): + """Test available status.""" + main_media_entity, client_media_entity = await platforms(hass) + + # Confirm service is currently set to available. + assert main_media_entity.available + + # Make update fail (i.e. DVR offline) + dtv = main_media_entity.dtv + main_media_entity.dtv = None + main_media_entity.schedule_update_ha_state(True) + await hass.async_block_till_done() + assert not main_media_entity.available + + # Make update work again (i.e. DVR back online) + main_media_entity.dtv = dtv + main_media_entity.schedule_update_ha_state(True) + await hass.async_block_till_done() + assert main_media_entity.available From ec23ded1ed8d3c24e8130685545df44f583ebf3f Mon Sep 17 00:00:00 2001 From: Erik Hendrix Date: Tue, 20 Nov 2018 13:01:00 -0700 Subject: [PATCH 04/20] Updated test based on PR#18474 Updated test to use service play_media instead of select_source based on change from PR18474 --- tests/components/media_player/test_directv.py | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/tests/components/media_player/test_directv.py b/tests/components/media_player/test_directv.py index 00c59767dde1e..b83b5d56d21a8 100644 --- a/tests/components/media_player/test_directv.py +++ b/tests/components/media_player/test_directv.py @@ -8,7 +8,7 @@ import homeassistant.components.media_player as mp from homeassistant.components.media_player import ( ATTR_INPUT_SOURCE, ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_CONTENT_TYPE, - ATTR_MEDIA_ENQUEUE, DOMAIN, SERVICE_PLAY_MEDIA, SERVICE_SELECT_SOURCE) + ATTR_MEDIA_ENQUEUE, DOMAIN, SERVICE_PLAY_MEDIA) from homeassistant.components.media_player.directv import ( ATTR_MEDIA_CURRENTLY_RECORDING, ATTR_MEDIA_RATING, ATTR_MEDIA_RECORDED, @@ -195,16 +195,6 @@ async def async_play_media(hass, media_type, media_id, entity_id=None, await hass.services.async_call(DOMAIN, SERVICE_PLAY_MEDIA, data) -async def async_select_source(hass, source, entity_id=None): - """Send the media player the command to select input source.""" - data = {ATTR_INPUT_SOURCE: source} - - if entity_id: - data[ATTR_ENTITY_ID] = entity_id - - await hass.services.async_call(DOMAIN, SERVICE_SELECT_SOURCE, data) - - class mockDIRECTVClass: """A fake DirecTV DVR device.""" @@ -355,15 +345,13 @@ async def test_supported_features(hass, platforms): # Features supported for main DVR assert mp.SUPPORT_PAUSE | mp.SUPPORT_TURN_ON | mp.SUPPORT_TURN_OFF |\ - mp.SUPPORT_PLAY_MEDIA | mp.SUPPORT_SELECT_SOURCE |\ - mp.SUPPORT_STOP | mp.SUPPORT_NEXT_TRACK |\ + mp.SUPPORT_PLAY_MEDIA | mp.SUPPORT_STOP | mp.SUPPORT_NEXT_TRACK |\ mp.SUPPORT_PREVIOUS_TRACK | mp.SUPPORT_PLAY ==\ main_media_entity.supported_features # Feature supported for clients. assert mp.SUPPORT_PAUSE |\ - mp.SUPPORT_PLAY_MEDIA | mp.SUPPORT_SELECT_SOURCE |\ - mp.SUPPORT_STOP | mp.SUPPORT_NEXT_TRACK |\ + mp.SUPPORT_PLAY_MEDIA | mp.SUPPORT_STOP | mp.SUPPORT_NEXT_TRACK |\ mp.SUPPORT_PREVIOUS_TRACK | mp.SUPPORT_PLAY ==\ client_media_entity.supported_features @@ -458,7 +446,7 @@ async def test_main_services(hass, platforms): # Change channel, currently it should be 202 assert state.attributes.get('source') == 202 - await async_select_source(hass, 7, MAIN_ENTITY_ID) + await async_play_media(hass, 'channel', 7, MAIN_ENTITY_ID) await hass.async_block_till_done() mock_tune_channel.assert_called_with('7') state = hass.states.get(MAIN_ENTITY_ID) From 555c2103d53ca4c872f4254c977f042782e87012 Mon Sep 17 00:00:00 2001 From: Erik Hendrix Date: Tue, 20 Nov 2018 13:04:52 -0700 Subject: [PATCH 05/20] Lint issues Lint issues --- tests/components/media_player/test_directv.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/components/media_player/test_directv.py b/tests/components/media_player/test_directv.py index b83b5d56d21a8..4680638ede6b2 100644 --- a/tests/components/media_player/test_directv.py +++ b/tests/components/media_player/test_directv.py @@ -7,8 +7,8 @@ import homeassistant.components.media_player as mp from homeassistant.components.media_player import ( - ATTR_INPUT_SOURCE, ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_CONTENT_TYPE, - ATTR_MEDIA_ENQUEUE, DOMAIN, SERVICE_PLAY_MEDIA) + ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_ENQUEUE, DOMAIN, + SERVICE_PLAY_MEDIA) from homeassistant.components.media_player.directv import ( ATTR_MEDIA_CURRENTLY_RECORDING, ATTR_MEDIA_RATING, ATTR_MEDIA_RECORDED, @@ -345,7 +345,7 @@ async def test_supported_features(hass, platforms): # Features supported for main DVR assert mp.SUPPORT_PAUSE | mp.SUPPORT_TURN_ON | mp.SUPPORT_TURN_OFF |\ - mp.SUPPORT_PLAY_MEDIA | mp.SUPPORT_STOP | mp.SUPPORT_NEXT_TRACK |\ + mp.SUPPORT_PLAY_MEDIA | mp.SUPPORT_STOP | mp.SUPPORT_NEXT_TRACK |\ mp.SUPPORT_PREVIOUS_TRACK | mp.SUPPORT_PLAY ==\ main_media_entity.supported_features From ed3f7a451720b0fdcba37136eea55bb8f0b2be5a Mon Sep 17 00:00:00 2001 From: Erik Hendrix Date: Tue, 20 Nov 2018 16:28:35 -0700 Subject: [PATCH 06/20] Further updates based on feedback Updates based on feedback provided. --- tests/components/media_player/test_directv.py | 140 +++++++++--------- 1 file changed, 72 insertions(+), 68 deletions(-) diff --git a/tests/components/media_player/test_directv.py b/tests/components/media_player/test_directv.py index 4680638ede6b2..afe5ca752d6df 100644 --- a/tests/components/media_player/test_directv.py +++ b/tests/components/media_player/test_directv.py @@ -1,15 +1,13 @@ """The tests for the DirecTV Media player platform.""" -from unittest.mock import patch, Mock +from unittest.mock import call, Mock, patch from datetime import datetime, timedelta -import logging import pytest import homeassistant.components.media_player as mp from homeassistant.components.media_player import ( ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_ENQUEUE, DOMAIN, SERVICE_PLAY_MEDIA) - from homeassistant.components.media_player.directv import ( ATTR_MEDIA_CURRENTLY_RECORDING, ATTR_MEDIA_RATING, ATTR_MEDIA_RECORDED, ATTR_MEDIA_START_TIME, DATA_DIRECTV, DEFAULT_DEVICE, DEFAULT_PORT, @@ -18,7 +16,9 @@ ATTR_ENTITY_ID, CONF_DEVICE, CONF_HOST, CONF_NAME, CONF_PORT, SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_PLAY, SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_MEDIA_STOP, SERVICE_TURN_OFF, - SERVICE_TURN_ON, STATE_OFF, STATE_PAUSED, STATE_PLAYING) + SERVICE_TURN_ON, STATE_OFF, STATE_PAUSED, STATE_PLAYING, STATE_UNAVAILABLE) +import homeassistant.core as ha +from homeassistant.helpers.discovery import async_load_platform from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util @@ -34,7 +34,7 @@ } LIVE = { - "callsign": "CNNHD", + "callsign": "HASSTV", "date": "20181110", "duration": 3600, "isOffAir": False, @@ -49,7 +49,7 @@ "rating": "No Rating", "startTime": 1541876400, "stationId": 3900947, - "title": "CNN Newsroom With Fredricka Whitfield" + "title": "Using Home Assistant to automate your home" } LOCATIONS = [ @@ -60,7 +60,7 @@ ] RECORDING = { - "callsign": "CNNHD", + "callsign": "HASSTV", "date": "20181110", "duration": 3600, "isOffAir": False, @@ -75,9 +75,9 @@ "rating": "No Rating", "startTime": 1541876400, "stationId": 3900947, - "title": "CNN Newsroom With Fredricka Whitfield", + "title": "Using Home Assistant to automate your home", 'uniqueId': '12345', - 'episodeTitle': 'CNN Recorded' + 'episodeTitle': 'Configure DirecTV platform.' } WORKING_CONFIG = { @@ -90,11 +90,9 @@ } } -_LOGGER = logging.getLogger(__name__) - @pytest.fixture -def platforms(): +async def platforms(hass): """Fixture for setting up test platforms.""" config = { 'media_player': [{ @@ -112,30 +110,25 @@ def platforms(): }] } - async def setup_test_platforms(hass): - with MockDependency('DirectPy'), \ - patch('DirectPy.DIRECTV', new=mockDIRECTVClass): - await async_setup_component(hass, mp.DOMAIN, config) - await hass.async_block_till_done() - - main_media_entity = hass.data['media_player'].get_entity( - MAIN_ENTITY_ID) + with MockDependency('DirectPy'), \ + patch('DirectPy.DIRECTV', new=MockDirectvClass): + await async_setup_component(hass, mp.DOMAIN, config) + await hass.async_block_till_done() - client_media_entity = hass.data['media_player'].get_entity( - CLIENT_ENTITY_ID) + main_media_entity = hass.data['media_player'].get_entity(MAIN_ENTITY_ID) + client_media_entity = hass.data['media_player'].get_entity( + CLIENT_ENTITY_ID) - # Set the client so it seems a recording is being watched. - client_media_entity.dtv.attributes = RECORDING - # Clients do not support turning on, setting it as client is on here. - client_media_entity.dtv._standby = False + # Set the client so it seems a recording is being watched. + client_media_entity.dtv.attributes = RECORDING + # Clients do not support turning on, setting it as client is on here. + client_media_entity.dtv._standby = False - main_media_entity.schedule_update_ha_state(True) - client_media_entity.schedule_update_ha_state(True) - await hass.async_block_till_done() - - return (main_media_entity, client_media_entity) + main_media_entity.schedule_update_ha_state(True) + client_media_entity.schedule_update_ha_state(True) + await hass.async_block_till_done() - return setup_test_platforms + return platforms async def async_turn_on(hass, entity_id=None): @@ -195,7 +188,7 @@ async def async_play_media(hass, media_type, media_id, entity_id=None, await hass.services.async_call(DOMAIN, SERVICE_PLAY_MEDIA, data) -class mockDIRECTVClass: +class MockDirectvClass: """A fake DirecTV DVR device.""" def __init__(self, ip, port=8080, clientAddr='0'): @@ -206,16 +199,12 @@ def __init__(self, ip, port=8080, clientAddr='0'): self._standby = True self._play = False - _LOGGER.debug("INIT with host: %s, port: %s, device: %s", - self._host, self._port, self._device) - self._locations = LOCATIONS self.attributes = LIVE def get_locations(self): """Mock for get_locations method.""" - _LOGGER.debug("get_locations called") test_locations = { 'locations': self._locations, 'status': { @@ -230,12 +219,10 @@ def get_locations(self): def get_standby(self): """Mock for get_standby method.""" - _LOGGER.debug("STANDBY is: %s", self._standby) return self._standby def get_tuned(self): """Mock for get_tuned method.""" - _LOGGER.debug("get_tuned called") if self._play: self.attributes['offset'] = self.attributes['offset']+1 @@ -250,7 +237,6 @@ def get_tuned(self): def key_press(self, keypress): """Mock for key_press method.""" - _LOGGER.debug("Key Press: %s", keypress) if keypress == 'poweron': self._standby = False self._play = True @@ -264,14 +250,13 @@ def key_press(self, keypress): def tune_channel(self, source): """Mock for tune_channel method.""" - _LOGGER.debug("Change channel %s", source) self.attributes['major'] = int(source) async def test_setup_platform_config(hass): """Test setting up the platform from configuration.""" with MockDependency('DirectPy'), \ - patch('DirectPy.DIRECTV', new=mockDIRECTVClass): + patch('DirectPy.DIRECTV', new=MockDirectvClass): await async_setup_component(hass, mp.DOMAIN, WORKING_CONFIG) await hass.async_block_till_done() @@ -284,10 +269,15 @@ async def test_setup_platform_discover(hass): """Test setting up the platform from discovery.""" add_entities = Mock() with MockDependency('DirectPy'), \ - patch('DirectPy.DIRECTV', new=mockDIRECTVClass): + patch('DirectPy.DIRECTV', new=MockDirectvClass): - setup_platform(hass, {}, add_entities, - discovery_info=DISCOVERY_INFO) + hass.async_create_task( + async_load_platform(hass, mp.DOMAIN, 'directv', DISCOVERY_INFO, + ha.Config()) + ) + await hass.async_block_till_done() +# setup_platform(hass, {}, add_entities, +# discovery_info=DISCOVERY_INFO) assert len(hass.data[DATA_DIRECTV]) == 1 assert add_entities.call_count == 1 @@ -298,7 +288,7 @@ async def test_setup_platform_discover_duplicate(hass): add_entities = Mock() with MockDependency('DirectPy'), \ - patch('DirectPy.DIRECTV', new=mockDIRECTVClass): + patch('DirectPy.DIRECTV', new=MockDirectvClass): await async_setup_component(hass, mp.DOMAIN, WORKING_CONFIG) await hass.async_block_till_done() @@ -324,7 +314,7 @@ async def test_setup_platform_discover_client(hass): }) with MockDependency('DirectPy'), \ - patch('DirectPy.DIRECTV', new=mockDIRECTVClass): + patch('DirectPy.DIRECTV', new=MockDirectvClass): await async_setup_component(hass, mp.DOMAIN, WORKING_CONFIG) await hass.async_block_till_done() @@ -341,24 +331,26 @@ async def test_setup_platform_discover_client(hass): async def test_supported_features(hass, platforms): """Test supported features.""" - main_media_entity, client_media_entity = await platforms(hass) + await platforms(hass) # Features supported for main DVR + state = hass.states.get(MAIN_ENTITY_ID) assert mp.SUPPORT_PAUSE | mp.SUPPORT_TURN_ON | mp.SUPPORT_TURN_OFF |\ mp.SUPPORT_PLAY_MEDIA | mp.SUPPORT_STOP | mp.SUPPORT_NEXT_TRACK |\ mp.SUPPORT_PREVIOUS_TRACK | mp.SUPPORT_PLAY ==\ - main_media_entity.supported_features + state.attributes.get('supported_features') # Feature supported for clients. + state = hass.states.get(CLIENT_ENTITY_ID) assert mp.SUPPORT_PAUSE |\ mp.SUPPORT_PLAY_MEDIA | mp.SUPPORT_STOP | mp.SUPPORT_NEXT_TRACK |\ mp.SUPPORT_PREVIOUS_TRACK | mp.SUPPORT_PLAY ==\ - client_media_entity.supported_features + state.attributes.get('supported_features') async def test_check_attributes(hass, platforms): """Test attributes.""" - main_media_entity, client_media_entity = await platforms(hass) + await platforms(hass) now = datetime(2018, 11, 19, 20, 0, 0, tzinfo=dt_util.UTC) # Start playing TV @@ -405,8 +397,9 @@ async def test_check_attributes(hass, platforms): async def test_main_services(hass, platforms): """Test the different services.""" - main_media_entity, client_media_entity = await platforms(hass) + await platforms(hass) + main_media_entity = hass.data['media_player'].get_entity(MAIN_ENTITY_ID) dtv_inst = main_media_entity.dtv # DVR starts in turned off state. @@ -426,21 +419,24 @@ async def test_main_services(hass, platforms): # Turn main DVR on. When turning on DVR is playing. await async_turn_on(hass, MAIN_ENTITY_ID) await hass.async_block_till_done() - mock_key_press.assert_called_with('poweron') + assert mock_key_press.called + assert mock_key_press.call_args == call('poweron') state = hass.states.get(MAIN_ENTITY_ID) assert state.state == STATE_PLAYING # Pause live TV. await async_media_pause(hass, MAIN_ENTITY_ID) await hass.async_block_till_done() - mock_key_press.assert_called_with('pause') + assert mock_key_press.called + assert mock_key_press.call_args == call('pause') state = hass.states.get(MAIN_ENTITY_ID) assert state.state == STATE_PAUSED # Start play again for live TV. await async_media_play(hass, MAIN_ENTITY_ID) await hass.async_block_till_done() - mock_key_press.assert_called_with('play') + assert mock_key_press.called + assert mock_key_press.call_args == call('play') state = hass.states.get(MAIN_ENTITY_ID) assert state.state == STATE_PLAYING @@ -448,21 +444,24 @@ async def test_main_services(hass, platforms): assert state.attributes.get('source') == 202 await async_play_media(hass, 'channel', 7, MAIN_ENTITY_ID) await hass.async_block_till_done() - mock_tune_channel.assert_called_with('7') + assert mock_tune_channel.called + assert mock_tune_channel.call_args == call('7') state = hass.states.get(MAIN_ENTITY_ID) assert state.attributes.get('source') == 7 # Stop live TV. await async_media_stop(hass, MAIN_ENTITY_ID) await hass.async_block_till_done() - mock_key_press.assert_called_with('stop') + assert mock_key_press.called + assert mock_key_press.call_args == call('stop') state = hass.states.get(MAIN_ENTITY_ID) assert state.state == STATE_PAUSED # Turn main DVR off. await async_turn_off(hass, MAIN_ENTITY_ID) await hass.async_block_till_done() - mock_key_press.assert_called_with('poweroff') + assert mock_key_press.called + assert mock_key_press.call_args == call('poweroff') state = hass.states.get(MAIN_ENTITY_ID) assert state.state == STATE_OFF @@ -475,20 +474,25 @@ async def test_main_services(hass, platforms): async def test_available(hass, platforms): """Test available status.""" - main_media_entity, client_media_entity = await platforms(hass) + await platforms(hass) # Confirm service is currently set to available. - assert main_media_entity.available + state = hass.states.get(MAIN_ENTITY_ID) + assert state.state is not STATE_UNAVAILABLE + main_media_entity = hass.data['media_player'].get_entity(MAIN_ENTITY_ID) # Make update fail (i.e. DVR offline) - dtv = main_media_entity.dtv - main_media_entity.dtv = None - main_media_entity.schedule_update_ha_state(True) - await hass.async_block_till_done() - assert not main_media_entity.available + with patch.object(main_media_entity, 'dtv', None): + main_media_entity.schedule_update_ha_state(True) + await hass.async_block_till_done() + main_media_entity.schedule_update_ha_state(False) + await hass.async_block_till_done() - # Make update work again (i.e. DVR back online) - main_media_entity.dtv = dtv + state = hass.states.get(MAIN_ENTITY_ID) + assert state.state is STATE_UNAVAILABLE + + # Recheck state, update should work again. main_media_entity.schedule_update_ha_state(True) await hass.async_block_till_done() - assert main_media_entity.available + state = hass.states.get(MAIN_ENTITY_ID) + assert state.state is not STATE_UNAVAILABLE From 4a16d9f853a27cf080723c8991116fa74c22c818 Mon Sep 17 00:00:00 2001 From: Erik Hendrix Date: Tue, 20 Nov 2018 17:11:59 -0700 Subject: [PATCH 07/20] Using async_load_platform for discovery test Using async_load_platform for discovery tests. Added asserts to ensure entities are created with correct names. --- tests/components/media_player/test_directv.py | 49 +++++++++++-------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/tests/components/media_player/test_directv.py b/tests/components/media_player/test_directv.py index afe5ca752d6df..6088a66a3128f 100644 --- a/tests/components/media_player/test_directv.py +++ b/tests/components/media_player/test_directv.py @@ -1,5 +1,5 @@ """The tests for the DirecTV Media player platform.""" -from unittest.mock import call, Mock, patch +from unittest.mock import call, patch from datetime import datetime, timedelta import pytest @@ -10,14 +10,12 @@ SERVICE_PLAY_MEDIA) from homeassistant.components.media_player.directv import ( ATTR_MEDIA_CURRENTLY_RECORDING, ATTR_MEDIA_RATING, ATTR_MEDIA_RECORDED, - ATTR_MEDIA_START_TIME, DATA_DIRECTV, DEFAULT_DEVICE, DEFAULT_PORT, - setup_platform) + ATTR_MEDIA_START_TIME, DATA_DIRECTV, DEFAULT_DEVICE, DEFAULT_PORT) from homeassistant.const import ( ATTR_ENTITY_ID, CONF_DEVICE, CONF_HOST, CONF_NAME, CONF_PORT, SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_PLAY, SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_MEDIA_STOP, SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_OFF, STATE_PAUSED, STATE_PLAYING, STATE_UNAVAILABLE) -import homeassistant.core as ha from homeassistant.helpers.discovery import async_load_platform from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util @@ -111,7 +109,7 @@ async def platforms(hass): } with MockDependency('DirectPy'), \ - patch('DirectPy.DIRECTV', new=MockDirectvClass): + patch('DirectPy.DIRECTV', new=MockDirectvClass): await async_setup_component(hass, mp.DOMAIN, config) await hass.async_block_till_done() @@ -261,49 +259,50 @@ async def test_setup_platform_config(hass): await async_setup_component(hass, mp.DOMAIN, WORKING_CONFIG) await hass.async_block_till_done() + assert sum(1 for item in hass.data['media_player'].entities) == 1 + assert hass.data['media_player'].get_entity(MAIN_ENTITY_ID) is not None assert len(hass.data[DATA_DIRECTV]) == 1 assert (IP_ADDRESS, DEFAULT_DEVICE) in hass.data[DATA_DIRECTV] async def test_setup_platform_discover(hass): """Test setting up the platform from discovery.""" - add_entities = Mock() with MockDependency('DirectPy'), \ patch('DirectPy.DIRECTV', new=MockDirectvClass): hass.async_create_task( async_load_platform(hass, mp.DOMAIN, 'directv', DISCOVERY_INFO, - ha.Config()) + {'media_player': {}}) ) await hass.async_block_till_done() -# setup_platform(hass, {}, add_entities, -# discovery_info=DISCOVERY_INFO) + assert sum(1 for item in hass.data['media_player'].entities) == 1 + assert hass.data['media_player'].get_entity(MAIN_ENTITY_ID) is not None assert len(hass.data[DATA_DIRECTV]) == 1 - assert add_entities.call_count == 1 + assert (IP_ADDRESS, DEFAULT_DEVICE) in hass.data[DATA_DIRECTV] async def test_setup_platform_discover_duplicate(hass): """Test setting up the platform from discovery.""" - add_entities = Mock() - with MockDependency('DirectPy'), \ patch('DirectPy.DIRECTV', new=MockDirectvClass): await async_setup_component(hass, mp.DOMAIN, WORKING_CONFIG) await hass.async_block_till_done() + hass.async_create_task( + async_load_platform(hass, mp.DOMAIN, 'directv', DISCOVERY_INFO, + {'media_player': {}}) + ) + await hass.async_block_till_done() - setup_platform(hass, {}, add_entities, - discovery_info=DISCOVERY_INFO) - + assert sum(1 for item in hass.data['media_player'].entities) == 1 + assert hass.data['media_player'].get_entity(MAIN_ENTITY_ID) is not None assert len(hass.data[DATA_DIRECTV]) == 1 - assert add_entities.call_count == 1 + assert (IP_ADDRESS, DEFAULT_DEVICE) in hass.data[DATA_DIRECTV] async def test_setup_platform_discover_client(hass): """Test setting up the platform from discovery.""" - add_entities = Mock() - LOCATIONS.append({ 'locationName': 'Client 1', 'clientAddr': '1' @@ -319,12 +318,22 @@ async def test_setup_platform_discover_client(hass): await async_setup_component(hass, mp.DOMAIN, WORKING_CONFIG) await hass.async_block_till_done() - setup_platform(hass, {}, add_entities, - discovery_info=DISCOVERY_INFO) + hass.async_create_task( + async_load_platform(hass, mp.DOMAIN, 'directv', DISCOVERY_INFO, + {'media_player': {}}) + ) + await hass.async_block_till_done() del LOCATIONS[-1] del LOCATIONS[-1] + assert sum(1 for item in hass.data['media_player'].entities) == 3 + assert hass.data['media_player'].get_entity(MAIN_ENTITY_ID) is not None + assert hass.data['media_player'].get_entity('media_player.client_1') \ + is not None + assert hass.data['media_player'].get_entity('media_player.client_2') \ + is not None assert len(hass.data[DATA_DIRECTV]) == 3 + assert (IP_ADDRESS, DEFAULT_DEVICE) in hass.data[DATA_DIRECTV] assert (IP_ADDRESS, '1') in hass.data[DATA_DIRECTV] assert (IP_ADDRESS, '2') in hass.data[DATA_DIRECTV] From 923f32ab52a81b73c6e7b552bb3f47dbb7b00504 Mon Sep 17 00:00:00 2001 From: Erik Hendrix Date: Tue, 20 Nov 2018 17:52:56 -0700 Subject: [PATCH 08/20] Used HASS event_loop to setup component Use HASS event_loop to setup the component async. --- tests/components/media_player/test_directv.py | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/tests/components/media_player/test_directv.py b/tests/components/media_player/test_directv.py index 6088a66a3128f..4106fa1e064eb 100644 --- a/tests/components/media_player/test_directv.py +++ b/tests/components/media_player/test_directv.py @@ -3,6 +3,7 @@ from datetime import datetime, timedelta import pytest +import asyncio import homeassistant.components.media_player as mp from homeassistant.components.media_player import ( @@ -90,7 +91,7 @@ @pytest.fixture -async def platforms(hass): +def platforms(hass): """Fixture for setting up test platforms.""" config = { 'media_player': [{ @@ -108,10 +109,13 @@ async def platforms(hass): }] } + hass_loop = asyncio.get_event_loop() + with MockDependency('DirectPy'), \ patch('DirectPy.DIRECTV', new=MockDirectvClass): - await async_setup_component(hass, mp.DOMAIN, config) - await hass.async_block_till_done() + hass_loop.run_until_complete(async_setup_component( + hass, mp.DOMAIN, config)) + hass_loop.run_until_complete(hass.async_block_till_done()) main_media_entity = hass.data['media_player'].get_entity(MAIN_ENTITY_ID) client_media_entity = hass.data['media_player'].get_entity( @@ -124,9 +128,9 @@ async def platforms(hass): main_media_entity.schedule_update_ha_state(True) client_media_entity.schedule_update_ha_state(True) - await hass.async_block_till_done() + hass_loop.run_until_complete(hass.async_block_till_done()) - return platforms + return async def async_turn_on(hass, entity_id=None): @@ -340,8 +344,6 @@ async def test_setup_platform_discover_client(hass): async def test_supported_features(hass, platforms): """Test supported features.""" - await platforms(hass) - # Features supported for main DVR state = hass.states.get(MAIN_ENTITY_ID) assert mp.SUPPORT_PAUSE | mp.SUPPORT_TURN_ON | mp.SUPPORT_TURN_OFF |\ @@ -359,7 +361,6 @@ async def test_supported_features(hass, platforms): async def test_check_attributes(hass, platforms): """Test attributes.""" - await platforms(hass) now = datetime(2018, 11, 19, 20, 0, 0, tzinfo=dt_util.UTC) # Start playing TV @@ -406,8 +407,6 @@ async def test_check_attributes(hass, platforms): async def test_main_services(hass, platforms): """Test the different services.""" - await platforms(hass) - main_media_entity = hass.data['media_player'].get_entity(MAIN_ENTITY_ID) dtv_inst = main_media_entity.dtv @@ -483,8 +482,6 @@ async def test_main_services(hass, platforms): async def test_available(hass, platforms): """Test available status.""" - await platforms(hass) - # Confirm service is currently set to available. state = hass.states.get(MAIN_ENTITY_ID) assert state.state is not STATE_UNAVAILABLE From e4a2647c0b7ad61e5a3f205cba4c9eeb33092b24 Mon Sep 17 00:00:00 2001 From: Erik Hendrix Date: Tue, 20 Nov 2018 18:34:21 -0700 Subject: [PATCH 09/20] Updated to use state machine for # entities Updated to use state machine to count # entities instead of entities. --- tests/components/media_player/test_directv.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/components/media_player/test_directv.py b/tests/components/media_player/test_directv.py index 4106fa1e064eb..78bf9e7a25149 100644 --- a/tests/components/media_player/test_directv.py +++ b/tests/components/media_player/test_directv.py @@ -263,7 +263,7 @@ async def test_setup_platform_config(hass): await async_setup_component(hass, mp.DOMAIN, WORKING_CONFIG) await hass.async_block_till_done() - assert sum(1 for item in hass.data['media_player'].entities) == 1 + assert len(hass.states.async_entity_ids('media_player')) == 1 assert hass.data['media_player'].get_entity(MAIN_ENTITY_ID) is not None assert len(hass.data[DATA_DIRECTV]) == 1 assert (IP_ADDRESS, DEFAULT_DEVICE) in hass.data[DATA_DIRECTV] @@ -280,7 +280,7 @@ async def test_setup_platform_discover(hass): ) await hass.async_block_till_done() - assert sum(1 for item in hass.data['media_player'].entities) == 1 + assert len(hass.states.async_entity_ids('media_player')) == 1 assert hass.data['media_player'].get_entity(MAIN_ENTITY_ID) is not None assert len(hass.data[DATA_DIRECTV]) == 1 assert (IP_ADDRESS, DEFAULT_DEVICE) in hass.data[DATA_DIRECTV] @@ -299,7 +299,7 @@ async def test_setup_platform_discover_duplicate(hass): ) await hass.async_block_till_done() - assert sum(1 for item in hass.data['media_player'].entities) == 1 + assert len(hass.states.async_entity_ids('media_player')) == 1 assert hass.data['media_player'].get_entity(MAIN_ENTITY_ID) is not None assert len(hass.data[DATA_DIRECTV]) == 1 assert (IP_ADDRESS, DEFAULT_DEVICE) in hass.data[DATA_DIRECTV] @@ -330,7 +330,7 @@ async def test_setup_platform_discover_client(hass): del LOCATIONS[-1] del LOCATIONS[-1] - assert sum(1 for item in hass.data['media_player'].entities) == 3 + assert len(hass.states.async_entity_ids('media_player')) == 3 assert hass.data['media_player'].get_entity(MAIN_ENTITY_ID) is not None assert hass.data['media_player'].get_entity('media_player.client_1') \ is not None From 32e11150090119439e3cada4d6fafc988bf2c868 Mon Sep 17 00:00:00 2001 From: Erik Hendrix Date: Tue, 20 Nov 2018 18:40:15 -0700 Subject: [PATCH 10/20] Use hass.loop instead of getting current loop Small update to use hass.loop instead, thanks Martin! --- tests/components/media_player/test_directv.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/components/media_player/test_directv.py b/tests/components/media_player/test_directv.py index 78bf9e7a25149..d2be8752c793c 100644 --- a/tests/components/media_player/test_directv.py +++ b/tests/components/media_player/test_directv.py @@ -109,13 +109,11 @@ def platforms(hass): }] } - hass_loop = asyncio.get_event_loop() - with MockDependency('DirectPy'), \ patch('DirectPy.DIRECTV', new=MockDirectvClass): - hass_loop.run_until_complete(async_setup_component( + hass.loop.run_until_complete(async_setup_component( hass, mp.DOMAIN, config)) - hass_loop.run_until_complete(hass.async_block_till_done()) + hass.loop.run_until_complete(hass.async_block_till_done()) main_media_entity = hass.data['media_player'].get_entity(MAIN_ENTITY_ID) client_media_entity = hass.data['media_player'].get_entity( @@ -128,7 +126,7 @@ def platforms(hass): main_media_entity.schedule_update_ha_state(True) client_media_entity.schedule_update_ha_state(True) - hass_loop.run_until_complete(hass.async_block_till_done()) + hass.loop.run_until_complete(hass.async_block_till_done()) return From babfdaa9dc474d63c06b78de68285e6d7a99a257 Mon Sep 17 00:00:00 2001 From: Erik Hendrix Date: Tue, 20 Nov 2018 20:21:08 -0700 Subject: [PATCH 11/20] Forgot to remove asyncio Removed asyncio import. --- tests/components/media_player/test_directv.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/components/media_player/test_directv.py b/tests/components/media_player/test_directv.py index d2be8752c793c..ab5c20ee82336 100644 --- a/tests/components/media_player/test_directv.py +++ b/tests/components/media_player/test_directv.py @@ -3,7 +3,6 @@ from datetime import datetime, timedelta import pytest -import asyncio import homeassistant.components.media_player as mp from homeassistant.components.media_player import ( From 63f9d86315a93e8897c50b4c7311a561a8a048b7 Mon Sep 17 00:00:00 2001 From: Erik Hendrix Date: Wed, 21 Nov 2018 12:37:23 -0700 Subject: [PATCH 12/20] Added fixtures Added fixtures. --- tests/components/media_player/test_directv.py | 83 ++++++++++++++----- 1 file changed, 62 insertions(+), 21 deletions(-) diff --git a/tests/components/media_player/test_directv.py b/tests/components/media_player/test_directv.py index ab5c20ee82336..d31cf2e5f0e1e 100644 --- a/tests/components/media_player/test_directv.py +++ b/tests/components/media_player/test_directv.py @@ -90,7 +90,33 @@ @pytest.fixture -def platforms(hass): +def client_dtv(): + """Fixture for a client device.""" + mocked_dtv = MockDirectvClass('mock_ip') + mocked_dtv.attributes = RECORDING + mocked_dtv._standby = False + return mocked_dtv + + +@pytest.fixture +def main_dtv(): + """Fixture for main DVR.""" + return MockDirectvClass('mock_ip') + + +@pytest.fixture +def dtv_side_effect(client_dtv, main_dtv): + """Fixture to create DIRECTV instance for main and client.""" + def mock_dtv(ip, port, client_addr): + mocked_dtv = next(iter([main_dtv, client_dtv])) + mocked_dtv._host = ip + mocked_dtv._port = port + mocked_dtv._device = client_addr + return mock_dtv + + +@pytest.fixture +def platforms(hass, dtv_side_effect): """Fixture for setting up test platforms.""" config = { 'media_player': [{ @@ -109,23 +135,31 @@ def platforms(hass): } with MockDependency('DirectPy'), \ - patch('DirectPy.DIRECTV', new=MockDirectvClass): + patch('DirectPy.DIRECTV', side_effect=dtv_side_effect): hass.loop.run_until_complete(async_setup_component( hass, mp.DOMAIN, config)) hass.loop.run_until_complete(hass.async_block_till_done()) + main_media_entity = hass.data['media_player'].get_entity( + MAIN_ENTITY_ID) + main_media_entity.schedule_update_ha_state(True) + client_media_entity = hass.data['media_player'].get_entity( + CLIENT_ENTITY_ID) + client_media_entity.schedule_update_ha_state(True) + hass.loop.run_until_complete(hass.async_block_till_done()) + yield - main_media_entity = hass.data['media_player'].get_entity(MAIN_ENTITY_ID) - client_media_entity = hass.data['media_player'].get_entity( - CLIENT_ENTITY_ID) +# main_media_entity = hass.data['media_player'].get_entity(MAIN_ENTITY_ID) +# client_media_entity = hass.data['media_player'].get_entity( +# CLIENT_ENTITY_ID) - # Set the client so it seems a recording is being watched. - client_media_entity.dtv.attributes = RECORDING - # Clients do not support turning on, setting it as client is on here. - client_media_entity.dtv._standby = False +# # Set the client so it seems a recording is being watched. +# client_media_entity.dtv.attributes = RECORDING +# # Clients do not support turning on, setting it as client is on here. +# client_media_entity.dtv._standby = False - main_media_entity.schedule_update_ha_state(True) - client_media_entity.schedule_update_ha_state(True) - hass.loop.run_until_complete(hass.async_block_till_done()) +# main_media_entity.schedule_update_ha_state(True) +# client_media_entity.schedule_update_ha_state(True) +# hass.loop.run_until_complete(hass.async_block_till_done()) return @@ -260,8 +294,9 @@ async def test_setup_platform_config(hass): await async_setup_component(hass, mp.DOMAIN, WORKING_CONFIG) await hass.async_block_till_done() + state = hass.states.get(MAIN_ENTITY_ID) + assert state assert len(hass.states.async_entity_ids('media_player')) == 1 - assert hass.data['media_player'].get_entity(MAIN_ENTITY_ID) is not None assert len(hass.data[DATA_DIRECTV]) == 1 assert (IP_ADDRESS, DEFAULT_DEVICE) in hass.data[DATA_DIRECTV] @@ -277,8 +312,9 @@ async def test_setup_platform_discover(hass): ) await hass.async_block_till_done() + state = hass.states.get(MAIN_ENTITY_ID) + assert state assert len(hass.states.async_entity_ids('media_player')) == 1 - assert hass.data['media_player'].get_entity(MAIN_ENTITY_ID) is not None assert len(hass.data[DATA_DIRECTV]) == 1 assert (IP_ADDRESS, DEFAULT_DEVICE) in hass.data[DATA_DIRECTV] @@ -296,8 +332,9 @@ async def test_setup_platform_discover_duplicate(hass): ) await hass.async_block_till_done() + state = hass.states.get(MAIN_ENTITY_ID) + assert state assert len(hass.states.async_entity_ids('media_player')) == 1 - assert hass.data['media_player'].get_entity(MAIN_ENTITY_ID) is not None assert len(hass.data[DATA_DIRECTV]) == 1 assert (IP_ADDRESS, DEFAULT_DEVICE) in hass.data[DATA_DIRECTV] @@ -327,12 +364,14 @@ async def test_setup_platform_discover_client(hass): del LOCATIONS[-1] del LOCATIONS[-1] + state = hass.states.get(MAIN_ENTITY_ID) + assert state + state = hass.states.get('media_player.client_1') + assert state + state = hass.states.get('media_player.client_2') + assert state + assert len(hass.states.async_entity_ids('media_player')) == 3 - assert hass.data['media_player'].get_entity(MAIN_ENTITY_ID) is not None - assert hass.data['media_player'].get_entity('media_player.client_1') \ - is not None - assert hass.data['media_player'].get_entity('media_player.client_2') \ - is not None assert len(hass.data[DATA_DIRECTV]) == 3 assert (IP_ADDRESS, DEFAULT_DEVICE) in hass.data[DATA_DIRECTV] assert (IP_ADDRESS, '1') in hass.data[DATA_DIRECTV] @@ -402,7 +441,7 @@ async def test_check_attributes(hass, platforms): assert state.attributes.get(mp.ATTR_MEDIA_POSITION_UPDATED_AT) == now -async def test_main_services(hass, platforms): +async def test_main_services(hass, platforms, main_dtv): """Test the different services.""" main_media_entity = hass.data['media_player'].get_entity(MAIN_ENTITY_ID) dtv_inst = main_media_entity.dtv @@ -471,9 +510,11 @@ async def test_main_services(hass, platforms): assert state.state == STATE_OFF # There should have been 6 calls to check if DVR is in standby + assert main_dtv.get_standby.call_count == 6 assert 6 == mock_get_standby.call_count # There should be 5 calls to get current info (only 1 time it will # not be called as DVR is in standby.) + assert 5 == main_dtv.get_tuned.call_count == 5 assert 5 == mock_get_tuned.call_count From 897fecb93027dff417c9c4e152ed00b2f47cd3aa Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Wed, 21 Nov 2018 21:12:11 +0100 Subject: [PATCH 13/20] Remove not needed updates and assertions --- tests/components/media_player/test_directv.py | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/tests/components/media_player/test_directv.py b/tests/components/media_player/test_directv.py index d31cf2e5f0e1e..afa0341f49ff1 100644 --- a/tests/components/media_player/test_directv.py +++ b/tests/components/media_player/test_directv.py @@ -139,30 +139,8 @@ def platforms(hass, dtv_side_effect): hass.loop.run_until_complete(async_setup_component( hass, mp.DOMAIN, config)) hass.loop.run_until_complete(hass.async_block_till_done()) - main_media_entity = hass.data['media_player'].get_entity( - MAIN_ENTITY_ID) - main_media_entity.schedule_update_ha_state(True) - client_media_entity = hass.data['media_player'].get_entity( - CLIENT_ENTITY_ID) - client_media_entity.schedule_update_ha_state(True) - hass.loop.run_until_complete(hass.async_block_till_done()) yield -# main_media_entity = hass.data['media_player'].get_entity(MAIN_ENTITY_ID) -# client_media_entity = hass.data['media_player'].get_entity( -# CLIENT_ENTITY_ID) - -# # Set the client so it seems a recording is being watched. -# client_media_entity.dtv.attributes = RECORDING -# # Clients do not support turning on, setting it as client is on here. -# client_media_entity.dtv._standby = False - -# main_media_entity.schedule_update_ha_state(True) -# client_media_entity.schedule_update_ha_state(True) -# hass.loop.run_until_complete(hass.async_block_till_done()) - - return - async def async_turn_on(hass, entity_id=None): """Turn on specified media player or all.""" @@ -297,8 +275,6 @@ async def test_setup_platform_config(hass): state = hass.states.get(MAIN_ENTITY_ID) assert state assert len(hass.states.async_entity_ids('media_player')) == 1 - assert len(hass.data[DATA_DIRECTV]) == 1 - assert (IP_ADDRESS, DEFAULT_DEVICE) in hass.data[DATA_DIRECTV] async def test_setup_platform_discover(hass): @@ -315,8 +291,6 @@ async def test_setup_platform_discover(hass): state = hass.states.get(MAIN_ENTITY_ID) assert state assert len(hass.states.async_entity_ids('media_player')) == 1 - assert len(hass.data[DATA_DIRECTV]) == 1 - assert (IP_ADDRESS, DEFAULT_DEVICE) in hass.data[DATA_DIRECTV] async def test_setup_platform_discover_duplicate(hass): @@ -335,8 +309,6 @@ async def test_setup_platform_discover_duplicate(hass): state = hass.states.get(MAIN_ENTITY_ID) assert state assert len(hass.states.async_entity_ids('media_player')) == 1 - assert len(hass.data[DATA_DIRECTV]) == 1 - assert (IP_ADDRESS, DEFAULT_DEVICE) in hass.data[DATA_DIRECTV] async def test_setup_platform_discover_client(hass): @@ -372,10 +344,6 @@ async def test_setup_platform_discover_client(hass): assert state assert len(hass.states.async_entity_ids('media_player')) == 3 - assert len(hass.data[DATA_DIRECTV]) == 3 - assert (IP_ADDRESS, DEFAULT_DEVICE) in hass.data[DATA_DIRECTV] - assert (IP_ADDRESS, '1') in hass.data[DATA_DIRECTV] - assert (IP_ADDRESS, '2') in hass.data[DATA_DIRECTV] async def test_supported_features(hass, platforms): From 0ac79bd26fe92ab78b8449de044be7ed98e1e504 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Wed, 21 Nov 2018 21:39:12 +0100 Subject: [PATCH 14/20] Return mocked dtv instance from side_effect --- tests/components/media_player/test_directv.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/components/media_player/test_directv.py b/tests/components/media_player/test_directv.py index afa0341f49ff1..d7d54883eef65 100644 --- a/tests/components/media_player/test_directv.py +++ b/tests/components/media_player/test_directv.py @@ -112,6 +112,7 @@ def mock_dtv(ip, port, client_addr): mocked_dtv._host = ip mocked_dtv._port = port mocked_dtv._device = client_addr + return mocked_dtv return mock_dtv From 5e8211842b078f1c4963049363e2c15552636c7e Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Wed, 21 Nov 2018 22:36:09 +0100 Subject: [PATCH 15/20] Fix return correct fixture instance --- tests/components/media_player/test_directv.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/components/media_player/test_directv.py b/tests/components/media_player/test_directv.py index d7d54883eef65..ccf2069fccbdd 100644 --- a/tests/components/media_player/test_directv.py +++ b/tests/components/media_player/test_directv.py @@ -108,7 +108,10 @@ def main_dtv(): def dtv_side_effect(client_dtv, main_dtv): """Fixture to create DIRECTV instance for main and client.""" def mock_dtv(ip, port, client_addr): - mocked_dtv = next(iter([main_dtv, client_dtv])) + if client_addr != '0': + mocked_dtv = client_dtv + else: + mocked_dtv = main_dtv mocked_dtv._host = ip mocked_dtv._port = port mocked_dtv._device = client_addr From 4d10f264fa0076a9094b7360948a9a5b72e80e24 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Wed, 21 Nov 2018 22:37:40 +0100 Subject: [PATCH 16/20] Clean up assertions --- tests/components/media_player/test_directv.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/components/media_player/test_directv.py b/tests/components/media_player/test_directv.py index ccf2069fccbdd..64dec77c68f6e 100644 --- a/tests/components/media_player/test_directv.py +++ b/tests/components/media_player/test_directv.py @@ -483,11 +483,11 @@ async def test_main_services(hass, platforms, main_dtv): # There should have been 6 calls to check if DVR is in standby assert main_dtv.get_standby.call_count == 6 - assert 6 == mock_get_standby.call_count + assert mock_get_standby.call_count == 6 # There should be 5 calls to get current info (only 1 time it will # not be called as DVR is in standby.) - assert 5 == main_dtv.get_tuned.call_count == 5 - assert 5 == mock_get_tuned.call_count + assert main_dtv.get_tuned.call_count == 5 + assert mock_get_tuned.call_count == 5 async def test_available(hass, platforms): From c93858a3f3b5e41b3acccdf7c616b84a245c6230 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Wed, 21 Nov 2018 23:27:43 +0100 Subject: [PATCH 17/20] Fix remaining patches --- tests/components/media_player/test_directv.py | 56 +++++++++++-------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/tests/components/media_player/test_directv.py b/tests/components/media_player/test_directv.py index 64dec77c68f6e..18bd4e723c44e 100644 --- a/tests/components/media_player/test_directv.py +++ b/tests/components/media_player/test_directv.py @@ -2,6 +2,7 @@ from unittest.mock import call, patch from datetime import datetime, timedelta +import requests import pytest import homeassistant.components.media_player as mp @@ -10,7 +11,7 @@ SERVICE_PLAY_MEDIA) from homeassistant.components.media_player.directv import ( ATTR_MEDIA_CURRENTLY_RECORDING, ATTR_MEDIA_RATING, ATTR_MEDIA_RECORDED, - ATTR_MEDIA_START_TIME, DATA_DIRECTV, DEFAULT_DEVICE, DEFAULT_PORT) + ATTR_MEDIA_START_TIME, DEFAULT_DEVICE, DEFAULT_PORT) from homeassistant.const import ( ATTR_ENTITY_ID, CONF_DEVICE, CONF_HOST, CONF_NAME, CONF_PORT, SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_PLAY, @@ -20,7 +21,7 @@ from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util -from tests.common import MockDependency +from tests.common import MockDependency, async_fire_time_changed CLIENT_ENTITY_ID = 'media_player.client_dvr' MAIN_ENTITY_ID = 'media_player.main_dvr' @@ -415,22 +416,19 @@ async def test_check_attributes(hass, platforms): async def test_main_services(hass, platforms, main_dtv): """Test the different services.""" - main_media_entity = hass.data['media_player'].get_entity(MAIN_ENTITY_ID) - dtv_inst = main_media_entity.dtv - # DVR starts in turned off state. state = hass.states.get(MAIN_ENTITY_ID) assert state.state == STATE_OFF # All these should call key_press in our class. - with patch.object(dtv_inst, 'key_press', - wraps=dtv_inst.key_press) as mock_key_press, \ - patch.object(dtv_inst, 'tune_channel', - wraps=dtv_inst.tune_channel) as mock_tune_channel, \ - patch.object(dtv_inst, 'get_tuned', - wraps=dtv_inst.get_tuned) as mock_get_tuned, \ - patch.object(dtv_inst, 'get_standby', - wraps=dtv_inst.get_standby) as mock_get_standby: + with patch.object(main_dtv, 'key_press', + wraps=main_dtv.key_press) as mock_key_press, \ + patch.object(main_dtv, 'tune_channel', + wraps=main_dtv.tune_channel) as mock_tune_channel, \ + patch.object(main_dtv, 'get_tuned', + wraps=main_dtv.get_tuned) as mock_get_tuned, \ + patch.object(main_dtv, 'get_standby', + wraps=main_dtv.get_standby) as mock_get_standby: # Turn main DVR on. When turning on DVR is playing. await async_turn_on(hass, MAIN_ENTITY_ID) @@ -490,25 +488,35 @@ async def test_main_services(hass, platforms, main_dtv): assert mock_get_tuned.call_count == 5 -async def test_available(hass, platforms): +async def test_available(hass, main_dtv): """Test available status.""" + now = dt_util.utcnow() + + with MockDependency('DirectPy'), \ + patch('DirectPy.DIRECTV', return_value=main_dtv), \ + patch('homeassistant.util.dt.utcnow', return_value=now): + await async_setup_component(hass, mp.DOMAIN, WORKING_CONFIG) + await hass.async_block_till_done() + # Confirm service is currently set to available. state = hass.states.get(MAIN_ENTITY_ID) - assert state.state is not STATE_UNAVAILABLE + assert state.state != STATE_UNAVAILABLE - main_media_entity = hass.data['media_player'].get_entity(MAIN_ENTITY_ID) # Make update fail (i.e. DVR offline) - with patch.object(main_media_entity, 'dtv', None): - main_media_entity.schedule_update_ha_state(True) - await hass.async_block_till_done() - main_media_entity.schedule_update_ha_state(False) + next_update = now + timedelta(minutes=5) + with patch.object( + main_dtv, 'get_standby', side_effect=requests.RequestException), \ + patch('homeassistant.util.dt.utcnow', return_value=next_update): + async_fire_time_changed(hass, next_update) await hass.async_block_till_done() state = hass.states.get(MAIN_ENTITY_ID) - assert state.state is STATE_UNAVAILABLE + assert state.state == STATE_UNAVAILABLE # Recheck state, update should work again. - main_media_entity.schedule_update_ha_state(True) - await hass.async_block_till_done() + next_update = next_update + timedelta(minutes=5) + with patch('homeassistant.util.dt.utcnow', return_value=next_update): + async_fire_time_changed(hass, next_update) + await hass.async_block_till_done() state = hass.states.get(MAIN_ENTITY_ID) - assert state.state is not STATE_UNAVAILABLE + assert state.state != STATE_UNAVAILABLE From 3fc84b00f177b7501e1223575ab6e01ae4305a25 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Wed, 21 Nov 2018 23:52:02 +0100 Subject: [PATCH 18/20] Mock time when setting up component in fixture --- tests/components/media_player/test_directv.py | 38 ++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/tests/components/media_player/test_directv.py b/tests/components/media_player/test_directv.py index 18bd4e723c44e..ee06cd6a81e34 100644 --- a/tests/components/media_player/test_directv.py +++ b/tests/components/media_player/test_directv.py @@ -121,7 +121,13 @@ def mock_dtv(ip, port, client_addr): @pytest.fixture -def platforms(hass, dtv_side_effect): +def mock_now(): + """Fixture for dtutil.now.""" + return dt_util.utcnow() + + +@pytest.fixture +def platforms(hass, dtv_side_effect, mock_now): """Fixture for setting up test platforms.""" config = { 'media_player': [{ @@ -140,7 +146,8 @@ def platforms(hass, dtv_side_effect): } with MockDependency('DirectPy'), \ - patch('DirectPy.DIRECTV', side_effect=dtv_side_effect): + patch('DirectPy.DIRECTV', side_effect=dtv_side_effect), \ + patch('homeassistant.util.dt.utcnow', return_value=mock_now): hass.loop.run_until_complete(async_setup_component( hass, mp.DOMAIN, config)) hass.loop.run_until_complete(hass.async_block_till_done()) @@ -368,8 +375,12 @@ async def test_supported_features(hass, platforms): state.attributes.get('supported_features') -async def test_check_attributes(hass, platforms): +async def test_check_attributes(hass, platforms, mock_now): """Test attributes.""" + next_update = mock_now + timedelta(minutes=5) + with patch('homeassistant.util.dt.utcnow', return_value=next_update): + async_fire_time_changed(hass, next_update) + await hass.async_block_till_done() now = datetime(2018, 11, 19, 20, 0, 0, tzinfo=dt_util.UTC) # Start playing TV @@ -414,9 +425,13 @@ async def test_check_attributes(hass, platforms): assert state.attributes.get(mp.ATTR_MEDIA_POSITION_UPDATED_AT) == now -async def test_main_services(hass, platforms, main_dtv): +async def test_main_services(hass, platforms, main_dtv, mock_now): """Test the different services.""" - # DVR starts in turned off state. + next_update = mock_now + timedelta(minutes=5) + with patch('homeassistant.util.dt.utcnow', return_value=next_update): + async_fire_time_changed(hass, next_update) + await hass.async_block_till_done() + # DVR starts in off state. state = hass.states.get(MAIN_ENTITY_ID) assert state.state == STATE_OFF @@ -488,14 +503,11 @@ async def test_main_services(hass, platforms, main_dtv): assert mock_get_tuned.call_count == 5 -async def test_available(hass, main_dtv): +async def test_available(hass, platforms, main_dtv, mock_now): """Test available status.""" - now = dt_util.utcnow() - - with MockDependency('DirectPy'), \ - patch('DirectPy.DIRECTV', return_value=main_dtv), \ - patch('homeassistant.util.dt.utcnow', return_value=now): - await async_setup_component(hass, mp.DOMAIN, WORKING_CONFIG) + next_update = mock_now + timedelta(minutes=5) + with patch('homeassistant.util.dt.utcnow', return_value=next_update): + async_fire_time_changed(hass, next_update) await hass.async_block_till_done() # Confirm service is currently set to available. @@ -503,7 +515,7 @@ async def test_available(hass, main_dtv): assert state.state != STATE_UNAVAILABLE # Make update fail (i.e. DVR offline) - next_update = now + timedelta(minutes=5) + next_update = next_update + timedelta(minutes=5) with patch.object( main_dtv, 'get_standby', side_effect=requests.RequestException), \ patch('homeassistant.util.dt.utcnow', return_value=next_update): From beb92a66e75a05c0e6202e791f5e1c04e1632eb9 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Thu, 22 Nov 2018 00:00:56 +0100 Subject: [PATCH 19/20] Patch time correctly --- tests/components/media_player/test_directv.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/components/media_player/test_directv.py b/tests/components/media_player/test_directv.py index ee06cd6a81e34..951f1319cc027 100644 --- a/tests/components/media_player/test_directv.py +++ b/tests/components/media_player/test_directv.py @@ -381,11 +381,10 @@ async def test_check_attributes(hass, platforms, mock_now): with patch('homeassistant.util.dt.utcnow', return_value=next_update): async_fire_time_changed(hass, next_update) await hass.async_block_till_done() - now = datetime(2018, 11, 19, 20, 0, 0, tzinfo=dt_util.UTC) # Start playing TV - with patch('homeassistant.helpers.condition.dt_util.now', - return_value=now): + with patch('homeassistant.util.dt.utcnow', + return_value=next_update): await async_media_play(hass, CLIENT_ENTITY_ID) await hass.async_block_till_done() @@ -399,7 +398,8 @@ async def test_check_attributes(hass, platforms, mock_now): assert state.attributes.get(mp.ATTR_MEDIA_DURATION) == \ RECORDING['duration'] assert state.attributes.get(mp.ATTR_MEDIA_POSITION) == 2 - assert state.attributes.get(mp.ATTR_MEDIA_POSITION_UPDATED_AT) == now + assert state.attributes.get( + mp.ATTR_MEDIA_POSITION_UPDATED_AT) == next_update assert state.attributes.get(mp.ATTR_MEDIA_TITLE) == RECORDING['title'] assert state.attributes.get(mp.ATTR_MEDIA_SERIES_TITLE) == \ RECORDING['episodeTitle'] @@ -415,14 +415,15 @@ async def test_check_attributes(hass, platforms, mock_now): # Test to make sure that ATTR_MEDIA_POSITION_UPDATED_AT is not # updated if TV is paused. - with patch('homeassistant.helpers.condition.dt_util.now', - return_value=now + timedelta(minutes=5)): + with patch('homeassistant.util.dt.utcnow', + return_value=next_update + timedelta(minutes=5)): await async_media_pause(hass, CLIENT_ENTITY_ID) await hass.async_block_till_done() state = hass.states.get(CLIENT_ENTITY_ID) assert state.state == STATE_PAUSED - assert state.attributes.get(mp.ATTR_MEDIA_POSITION_UPDATED_AT) == now + assert state.attributes.get( + mp.ATTR_MEDIA_POSITION_UPDATED_AT) == next_update async def test_main_services(hass, platforms, main_dtv, mock_now): From 532ea5f00c24f48655155441b8e25f4a0fd899e9 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Thu, 22 Nov 2018 00:02:08 +0100 Subject: [PATCH 20/20] Attribute _last_update should return utcnow --- homeassistant/components/media_player/directv.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/media_player/directv.py b/homeassistant/components/media_player/directv.py index 7a1e240d82e1d..d8c67e372b2fb 100644 --- a/homeassistant/components/media_player/directv.py +++ b/homeassistant/components/media_player/directv.py @@ -162,8 +162,8 @@ def update(self): self._current['offset'] self._assumed_state = self._is_recorded self._last_position = self._current['offset'] - self._last_update = dt_util.now() if not self._paused or\ - self._last_update is None else self._last_update + self._last_update = dt_util.utcnow() if not self._paused \ + or self._last_update is None else self._last_update else: self._available = False except requests.RequestException as ex: